Blame view

drivers/cpufreq/qcom-cpufreq-nvmem.c 12.3 KB
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
1
2
3
4
5
6
7
8
9
10
11
  // SPDX-License-Identifier: GPL-2.0
  /*
   * Copyright (c) 2018, The Linux Foundation. All rights reserved.
   */
  
  /*
   * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
   * the CPU frequency subset and voltage value of each OPP varies
   * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
   * defines the voltage and frequency value based on the msm-id in SMEM
   * and speedbin blown in the efuse combination.
7d1270954   Sricharan R   cpufreq: qcom: Re...
12
   * The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
13
14
15
16
17
18
19
20
21
22
23
24
   * to provide the OPP framework with required information.
   * This is used to determine the voltage and frequency value for each OPP of
   * operating-points-v2 table when it is parsed by the OPP framework.
   */
  
  #include <linux/cpu.h>
  #include <linux/err.h>
  #include <linux/init.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/nvmem-consumer.h>
  #include <linux/of.h>
7d1270954   Sricharan R   cpufreq: qcom: Re...
25
  #include <linux/of_device.h>
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
26
  #include <linux/platform_device.h>
1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
27
  #include <linux/pm_domain.h>
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  #include <linux/pm_opp.h>
  #include <linux/slab.h>
  #include <linux/soc/qcom/smem.h>
  
  #define MSM_ID_SMEM	137
  
  enum _msm_id {
  	MSM8996V3 = 0xF6ul,
  	APQ8096V3 = 0x123ul,
  	MSM8996SG = 0x131ul,
  	APQ8096SG = 0x138ul,
  };
  
  enum _msm8996_version {
  	MSM8996_V3,
  	MSM8996_SG,
  	NUM_OF_MSM8996_VERSIONS,
  };
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
46
47
48
49
50
  struct qcom_cpufreq_drv;
  
  struct qcom_cpufreq_match_data {
  	int (*get_version)(struct device *cpu_dev,
  			   struct nvmem_cell *speedbin_nvmem,
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
51
  			   char **pvs_name,
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
52
  			   struct qcom_cpufreq_drv *drv);
1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
53
  	const char **genpd_names;
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
54
55
56
  };
  
  struct qcom_cpufreq_drv {
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
57
58
  	struct opp_table **names_opp_tables;
  	struct opp_table **hw_opp_tables;
1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
59
  	struct opp_table **genpd_opp_tables;
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
60
61
62
  	u32 versions;
  	const struct qcom_cpufreq_match_data *data;
  };
7d1270954   Sricharan R   cpufreq: qcom: Re...
63
  static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev;
5ad7346b4   Ilia Lin   cpufreq: kryo: Ad...
64

a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  static void get_krait_bin_format_a(struct device *cpu_dev,
  					  int *speed, int *pvs, int *pvs_ver,
  					  struct nvmem_cell *pvs_nvmem, u8 *buf)
  {
  	u32 pte_efuse;
  
  	pte_efuse = *((u32 *)buf);
  
  	*speed = pte_efuse & 0xf;
  	if (*speed == 0xf)
  		*speed = (pte_efuse >> 4) & 0xf;
  
  	if (*speed == 0xf) {
  		*speed = 0;
  		dev_warn(cpu_dev, "Speed bin: Defaulting to %d
  ", *speed);
  	} else {
  		dev_dbg(cpu_dev, "Speed bin: %d
  ", *speed);
  	}
  
  	*pvs = (pte_efuse >> 10) & 0x7;
  	if (*pvs == 0x7)
  		*pvs = (pte_efuse >> 13) & 0x7;
  
  	if (*pvs == 0x7) {
  		*pvs = 0;
  		dev_warn(cpu_dev, "PVS bin: Defaulting to %d
  ", *pvs);
  	} else {
  		dev_dbg(cpu_dev, "PVS bin: %d
  ", *pvs);
  	}
  }
  
  static void get_krait_bin_format_b(struct device *cpu_dev,
  					  int *speed, int *pvs, int *pvs_ver,
  					  struct nvmem_cell *pvs_nvmem, u8 *buf)
  {
  	u32 pte_efuse, redundant_sel;
  
  	pte_efuse = *((u32 *)buf);
  	redundant_sel = (pte_efuse >> 24) & 0x7;
  
  	*pvs_ver = (pte_efuse >> 4) & 0x3;
  
  	switch (redundant_sel) {
  	case 1:
  		*pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
  		*speed = (pte_efuse >> 27) & 0xf;
  		break;
  	case 2:
  		*pvs = (pte_efuse >> 27) & 0xf;
  		*speed = pte_efuse & 0x7;
  		break;
  	default:
  		/* 4 bits of PVS are in efuse register bits 31, 8-6. */
  		*pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7);
  		*speed = pte_efuse & 0x7;
  	}
  
  	/* Check SPEED_BIN_BLOW_STATUS */
  	if (pte_efuse & BIT(3)) {
  		dev_dbg(cpu_dev, "Speed bin: %d
  ", *speed);
  	} else {
  		dev_warn(cpu_dev, "Speed bin not set. Defaulting to 0!
  ");
  		*speed = 0;
  	}
  
  	/* Check PVS_BLOW_STATUS */
  	pte_efuse = *(((u32 *)buf) + 4);
  	pte_efuse &= BIT(21);
  	if (pte_efuse) {
  		dev_dbg(cpu_dev, "PVS bin: %d
  ", *pvs);
  	} else {
  		dev_warn(cpu_dev, "PVS bin not set. Defaulting to 0!
  ");
  		*pvs = 0;
  	}
  
  	dev_dbg(cpu_dev, "PVS version: %d
  ", *pvs_ver);
  }
7d1270954   Sricharan R   cpufreq: qcom: Re...
151
  static enum _msm8996_version qcom_cpufreq_get_msm_id(void)
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  {
  	size_t len;
  	u32 *msm_id;
  	enum _msm8996_version version;
  
  	msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
  	if (IS_ERR(msm_id))
  		return NUM_OF_MSM8996_VERSIONS;
  
  	/* The first 4 bytes are format, next to them is the actual msm-id */
  	msm_id++;
  
  	switch ((enum _msm_id)*msm_id) {
  	case MSM8996V3:
  	case APQ8096V3:
  		version = MSM8996_V3;
  		break;
  	case MSM8996SG:
  	case APQ8096SG:
  		version = MSM8996_SG;
  		break;
  	default:
  		version = NUM_OF_MSM8996_VERSIONS;
  	}
  
  	return version;
  }
7d1270954   Sricharan R   cpufreq: qcom: Re...
179
180
  static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
  					  struct nvmem_cell *speedbin_nvmem,
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
181
  					  char **pvs_name,
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
182
  					  struct qcom_cpufreq_drv *drv)
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
183
  {
7d1270954   Sricharan R   cpufreq: qcom: Re...
184
185
  	size_t len;
  	u8 *speedbin;
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
186
  	enum _msm8996_version msm8996_version;
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
187
  	*pvs_name = NULL;
7d1270954   Sricharan R   cpufreq: qcom: Re...
188
189
190
191
192
193
194
195
196
197
198
199
200
  
  	msm8996_version = qcom_cpufreq_get_msm_id();
  	if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
  		dev_err(cpu_dev, "Not Snapdragon 820/821!");
  		return -ENODEV;
  	}
  
  	speedbin = nvmem_cell_read(speedbin_nvmem, &len);
  	if (IS_ERR(speedbin))
  		return PTR_ERR(speedbin);
  
  	switch (msm8996_version) {
  	case MSM8996_V3:
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
201
  		drv->versions = 1 << (unsigned int)(*speedbin);
7d1270954   Sricharan R   cpufreq: qcom: Re...
202
203
  		break;
  	case MSM8996_SG:
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
204
  		drv->versions = 1 << ((unsigned int)(*speedbin) + 4);
7d1270954   Sricharan R   cpufreq: qcom: Re...
205
206
207
208
209
210
211
212
213
  		break;
  	default:
  		BUG();
  		break;
  	}
  
  	kfree(speedbin);
  	return 0;
  }
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  static int qcom_cpufreq_krait_name_version(struct device *cpu_dev,
  					   struct nvmem_cell *speedbin_nvmem,
  					   char **pvs_name,
  					   struct qcom_cpufreq_drv *drv)
  {
  	int speed = 0, pvs = 0, pvs_ver = 0;
  	u8 *speedbin;
  	size_t len;
  
  	speedbin = nvmem_cell_read(speedbin_nvmem, &len);
  
  	if (IS_ERR(speedbin))
  		return PTR_ERR(speedbin);
  
  	switch (len) {
  	case 4:
  		get_krait_bin_format_a(cpu_dev, &speed, &pvs, &pvs_ver,
  				       speedbin_nvmem, speedbin);
  		break;
  	case 8:
  		get_krait_bin_format_b(cpu_dev, &speed, &pvs, &pvs_ver,
  				       speedbin_nvmem, speedbin);
  		break;
  	default:
  		dev_err(cpu_dev, "Unable to read nvmem data. Defaulting to 0!
  ");
  		return -ENODEV;
  	}
  
  	snprintf(*pvs_name, sizeof("speedXX-pvsXX-vXX"), "speed%d-pvs%d-v%d",
  		 speed, pvs, pvs_ver);
  
  	drv->versions = (1 << speed);
  
  	kfree(speedbin);
  	return 0;
  }
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
251
252
253
  static const struct qcom_cpufreq_match_data match_data_kryo = {
  	.get_version = qcom_cpufreq_kryo_name_version,
  };
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
254
255
256
  static const struct qcom_cpufreq_match_data match_data_krait = {
  	.get_version = qcom_cpufreq_krait_name_version,
  };
1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
257
258
259
260
261
  static const char *qcs404_genpd_names[] = { "cpr", NULL };
  
  static const struct qcom_cpufreq_match_data match_data_qcs404 = {
  	.genpd_names = qcs404_genpd_names,
  };
7d1270954   Sricharan R   cpufreq: qcom: Re...
262
263
  static int qcom_cpufreq_probe(struct platform_device *pdev)
  {
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
264
  	struct qcom_cpufreq_drv *drv;
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
265
266
267
  	struct nvmem_cell *speedbin_nvmem;
  	struct device_node *np;
  	struct device *cpu_dev;
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
268
  	char *pvs_name = "speedXX-pvsXX-vXX";
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
269
  	unsigned cpu;
7d1270954   Sricharan R   cpufreq: qcom: Re...
270
  	const struct of_device_id *match;
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
271
272
273
  	int ret;
  
  	cpu_dev = get_cpu_device(0);
1dd2058f9   Dan Carpenter   cpufreq: qcom-kry...
274
275
  	if (!cpu_dev)
  		return -ENODEV;
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
276

46e2856b8   Ilia Lin   cpufreq: Add Kryo...
277
  	np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
1dd2058f9   Dan Carpenter   cpufreq: qcom-kry...
278
279
  	if (!np)
  		return -ENOENT;
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
280

2dea65168   Ansuel Smith   cpufreq: qcom: fi...
281
  	ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
282
283
284
285
  	if (!ret) {
  		of_node_put(np);
  		return -ENOENT;
  	}
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
286
287
288
289
290
291
292
293
294
  	drv = kzalloc(sizeof(*drv), GFP_KERNEL);
  	if (!drv)
  		return -ENOMEM;
  
  	match = pdev->dev.platform_data;
  	drv->data = match->data;
  	if (!drv->data) {
  		ret = -ENODEV;
  		goto free_drv;
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
295
  	}
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
296
297
298
299
300
301
302
303
304
305
306
  	if (drv->data->get_version) {
  		speedbin_nvmem = of_nvmem_cell_get(np, NULL);
  		if (IS_ERR(speedbin_nvmem)) {
  			if (PTR_ERR(speedbin_nvmem) != -EPROBE_DEFER)
  				dev_err(cpu_dev,
  					"Could not get nvmem cell: %ld
  ",
  					PTR_ERR(speedbin_nvmem));
  			ret = PTR_ERR(speedbin_nvmem);
  			goto free_drv;
  		}
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
307

a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
308
309
  		ret = drv->data->get_version(cpu_dev,
  							speedbin_nvmem, &pvs_name, drv);
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
310
311
312
313
314
315
316
  		if (ret) {
  			nvmem_cell_put(speedbin_nvmem);
  			goto free_drv;
  		}
  		nvmem_cell_put(speedbin_nvmem);
  	}
  	of_node_put(np);
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
317
318
  	drv->names_opp_tables = kcalloc(num_possible_cpus(),
  				  sizeof(*drv->names_opp_tables),
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
319
  				  GFP_KERNEL);
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
320
  	if (!drv->names_opp_tables) {
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
321
322
323
  		ret = -ENOMEM;
  		goto free_drv;
  	}
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
324
325
326
327
328
329
330
  	drv->hw_opp_tables = kcalloc(num_possible_cpus(),
  				  sizeof(*drv->hw_opp_tables),
  				  GFP_KERNEL);
  	if (!drv->hw_opp_tables) {
  		ret = -ENOMEM;
  		goto free_opp_names;
  	}
0334906c0   Viresh Kumar   cpufreq: kryo: Re...
331

1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
332
333
334
335
336
337
338
  	drv->genpd_opp_tables = kcalloc(num_possible_cpus(),
  					sizeof(*drv->genpd_opp_tables),
  					GFP_KERNEL);
  	if (!drv->genpd_opp_tables) {
  		ret = -ENOMEM;
  		goto free_opp;
  	}
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
339
340
341
342
  	for_each_possible_cpu(cpu) {
  		cpu_dev = get_cpu_device(cpu);
  		if (NULL == cpu_dev) {
  			ret = -ENODEV;
1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
343
  			goto free_genpd_opp;
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
344
  		}
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
345
  		if (drv->data->get_version) {
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
  
  			if (pvs_name) {
  				drv->names_opp_tables[cpu] = dev_pm_opp_set_prop_name(
  								     cpu_dev,
  								     pvs_name);
  				if (IS_ERR(drv->names_opp_tables[cpu])) {
  					ret = PTR_ERR(drv->names_opp_tables[cpu]);
  					dev_err(cpu_dev, "Failed to add OPP name %s
  ",
  						pvs_name);
  					goto free_opp;
  				}
  			}
  
  			drv->hw_opp_tables[cpu] = dev_pm_opp_set_supported_hw(
  									 cpu_dev, &drv->versions, 1);
  			if (IS_ERR(drv->hw_opp_tables[cpu])) {
  				ret = PTR_ERR(drv->hw_opp_tables[cpu]);
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
364
365
366
  				dev_err(cpu_dev,
  					"Failed to set supported hardware
  ");
1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
  				goto free_genpd_opp;
  			}
  		}
  
  		if (drv->data->genpd_names) {
  			drv->genpd_opp_tables[cpu] =
  				dev_pm_opp_attach_genpd(cpu_dev,
  							drv->data->genpd_names,
  							NULL);
  			if (IS_ERR(drv->genpd_opp_tables[cpu])) {
  				ret = PTR_ERR(drv->genpd_opp_tables[cpu]);
  				if (ret != -EPROBE_DEFER)
  					dev_err(cpu_dev,
  						"Could not attach to pm_domain: %d
  ",
  						ret);
  				goto free_genpd_opp;
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
384
  			}
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
385
386
387
388
389
  		}
  	}
  
  	cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
  							  NULL, 0);
0334906c0   Viresh Kumar   cpufreq: kryo: Re...
390
  	if (!IS_ERR(cpufreq_dt_pdev)) {
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
391
  		platform_set_drvdata(pdev, drv);
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
392
  		return 0;
0334906c0   Viresh Kumar   cpufreq: kryo: Re...
393
  	}
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
394
395
396
397
  
  	ret = PTR_ERR(cpufreq_dt_pdev);
  	dev_err(cpu_dev, "Failed to register platform device
  ");
1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
398
399
400
401
402
403
404
  free_genpd_opp:
  	for_each_possible_cpu(cpu) {
  		if (IS_ERR_OR_NULL(drv->genpd_opp_tables[cpu]))
  			break;
  		dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
  	}
  	kfree(drv->genpd_opp_tables);
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
405
406
  free_opp:
  	for_each_possible_cpu(cpu) {
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
407
408
409
410
411
412
  		if (IS_ERR_OR_NULL(drv->names_opp_tables[cpu]))
  			break;
  		dev_pm_opp_put_prop_name(drv->names_opp_tables[cpu]);
  	}
  	for_each_possible_cpu(cpu) {
  		if (IS_ERR_OR_NULL(drv->hw_opp_tables[cpu]))
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
413
  			break;
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
414
  		dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
415
  	}
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
416
417
418
  	kfree(drv->hw_opp_tables);
  free_opp_names:
  	kfree(drv->names_opp_tables);
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
419
420
  free_drv:
  	kfree(drv);
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
421
422
423
  
  	return ret;
  }
7d1270954   Sricharan R   cpufreq: qcom: Re...
424
  static int qcom_cpufreq_remove(struct platform_device *pdev)
5ad7346b4   Ilia Lin   cpufreq: kryo: Ad...
425
  {
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
426
  	struct qcom_cpufreq_drv *drv = platform_get_drvdata(pdev);
0334906c0   Viresh Kumar   cpufreq: kryo: Re...
427
  	unsigned int cpu;
5ad7346b4   Ilia Lin   cpufreq: kryo: Ad...
428
  	platform_device_unregister(cpufreq_dt_pdev);
0334906c0   Viresh Kumar   cpufreq: kryo: Re...
429

1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
430
  	for_each_possible_cpu(cpu) {
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
431
432
433
434
  		if (drv->names_opp_tables[cpu])
  			dev_pm_opp_put_supported_hw(drv->names_opp_tables[cpu]);
  		if (drv->hw_opp_tables[cpu])
  			dev_pm_opp_put_supported_hw(drv->hw_opp_tables[cpu]);
1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
435
436
437
  		if (drv->genpd_opp_tables[cpu])
  			dev_pm_opp_detach_genpd(drv->genpd_opp_tables[cpu]);
  	}
0334906c0   Viresh Kumar   cpufreq: kryo: Re...
438

a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
439
440
  	kfree(drv->names_opp_tables);
  	kfree(drv->hw_opp_tables);
1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
441
  	kfree(drv->genpd_opp_tables);
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
442
  	kfree(drv);
0334906c0   Viresh Kumar   cpufreq: kryo: Re...
443

5ad7346b4   Ilia Lin   cpufreq: kryo: Ad...
444
445
  	return 0;
  }
7d1270954   Sricharan R   cpufreq: qcom: Re...
446
447
448
  static struct platform_driver qcom_cpufreq_driver = {
  	.probe = qcom_cpufreq_probe,
  	.remove = qcom_cpufreq_remove,
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
449
  	.driver = {
7d1270954   Sricharan R   cpufreq: qcom: Re...
450
  		.name = "qcom-cpufreq-nvmem",
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
451
452
  	},
  };
7d1270954   Sricharan R   cpufreq: qcom: Re...
453
  static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
57f2f8b4a   Niklas Cassel   cpufreq: qcom: Re...
454
455
  	{ .compatible = "qcom,apq8096", .data = &match_data_kryo },
  	{ .compatible = "qcom,msm8996", .data = &match_data_kryo },
1cb8339ca   Niklas Cassel   cpufreq: qcom: Ad...
456
  	{ .compatible = "qcom,qcs404", .data = &match_data_qcs404 },
a8811ec76   Ansuel Smith   cpufreq: qcom: Ad...
457
458
459
460
  	{ .compatible = "qcom,ipq8064", .data = &match_data_krait },
  	{ .compatible = "qcom,apq8064", .data = &match_data_krait },
  	{ .compatible = "qcom,msm8974", .data = &match_data_krait },
  	{ .compatible = "qcom,msm8960", .data = &match_data_krait },
7d1270954   Sricharan R   cpufreq: qcom: Re...
461
  	{},
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
462
  };
757ee8737   Pali Rohár   cpufreq: qcom: Ad...
463
  MODULE_DEVICE_TABLE(of, qcom_cpufreq_match_list);
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
464
465
466
467
468
469
470
  
  /*
   * Since the driver depends on smem and nvmem drivers, which may
   * return EPROBE_DEFER, all the real activity is done in the probe,
   * which may be defered as well. The init here is only registering
   * the driver and the platform device.
   */
7d1270954   Sricharan R   cpufreq: qcom: Re...
471
  static int __init qcom_cpufreq_init(void)
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
472
473
474
475
476
477
478
  {
  	struct device_node *np = of_find_node_by_path("/");
  	const struct of_device_id *match;
  	int ret;
  
  	if (!np)
  		return -ENODEV;
7d1270954   Sricharan R   cpufreq: qcom: Re...
479
  	match = of_match_node(qcom_cpufreq_match_list, np);
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
480
481
482
  	of_node_put(np);
  	if (!match)
  		return -ENODEV;
7d1270954   Sricharan R   cpufreq: qcom: Re...
483
  	ret = platform_driver_register(&qcom_cpufreq_driver);
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
484
485
  	if (unlikely(ret < 0))
  		return ret;
7d1270954   Sricharan R   cpufreq: qcom: Re...
486
487
488
  	cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem",
  						     -1, match, sizeof(*match));
  	ret = PTR_ERR_OR_ZERO(cpufreq_pdev);
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
489
490
  	if (0 == ret)
  		return 0;
7d1270954   Sricharan R   cpufreq: qcom: Re...
491
  	platform_driver_unregister(&qcom_cpufreq_driver);
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
492
493
  	return ret;
  }
7d1270954   Sricharan R   cpufreq: qcom: Re...
494
  module_init(qcom_cpufreq_init);
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
495

7d1270954   Sricharan R   cpufreq: qcom: Re...
496
  static void __exit qcom_cpufreq_exit(void)
5ad7346b4   Ilia Lin   cpufreq: kryo: Ad...
497
  {
7d1270954   Sricharan R   cpufreq: qcom: Re...
498
499
  	platform_device_unregister(cpufreq_pdev);
  	platform_driver_unregister(&qcom_cpufreq_driver);
5ad7346b4   Ilia Lin   cpufreq: kryo: Ad...
500
  }
7d1270954   Sricharan R   cpufreq: qcom: Re...
501
  module_exit(qcom_cpufreq_exit);
5ad7346b4   Ilia Lin   cpufreq: kryo: Ad...
502

7d1270954   Sricharan R   cpufreq: qcom: Re...
503
  MODULE_DESCRIPTION("Qualcomm Technologies, Inc. CPUfreq driver");
46e2856b8   Ilia Lin   cpufreq: Add Kryo...
504
  MODULE_LICENSE("GPL v2");