Commit | Line | Data |
---|---|---|
46e2856b IL |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (c) 2018, The Linux Foundation. All rights reserved. | |
4 | */ | |
5 | ||
6 | /* | |
7 | * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors, | |
8 | * the CPU frequency subset and voltage value of each OPP varies | |
9 | * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables | |
10 | * defines the voltage and frequency value based on the msm-id in SMEM | |
11 | * and speedbin blown in the efuse combination. | |
7d127095 | 12 | * The qcom-cpufreq-nvmem driver reads the msm-id and efuse value from the SoC |
46e2856b IL |
13 | * to provide the OPP framework with required information. |
14 | * This is used to determine the voltage and frequency value for each OPP of | |
15 | * operating-points-v2 table when it is parsed by the OPP framework. | |
16 | */ | |
17 | ||
18 | #include <linux/cpu.h> | |
19 | #include <linux/err.h> | |
20 | #include <linux/init.h> | |
21 | #include <linux/kernel.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/nvmem-consumer.h> | |
24 | #include <linux/of.h> | |
25 | #include <linux/platform_device.h> | |
1cb8339c | 26 | #include <linux/pm_domain.h> |
46e2856b IL |
27 | #include <linux/pm_opp.h> |
28 | #include <linux/slab.h> | |
29 | #include <linux/soc/qcom/smem.h> | |
30 | ||
865d7e71 | 31 | #include <dt-bindings/arm/qcom,ids.h> |
46e2856b | 32 | |
4a3754f7 CM |
33 | enum ipq806x_versions { |
34 | IPQ8062_VERSION = 0, | |
35 | IPQ8064_VERSION, | |
36 | IPQ8065_VERSION, | |
37 | }; | |
38 | ||
47e161a7 RM |
39 | #define IPQ6000_VERSION BIT(2) |
40 | ||
0b9cd949 RM |
41 | enum ipq8074_versions { |
42 | IPQ8074_HAWKEYE_VERSION = 0, | |
43 | IPQ8074_ACORN_VERSION, | |
44 | }; | |
45 | ||
57f2f8b4 NC |
46 | struct qcom_cpufreq_drv; |
47 | ||
48 | struct qcom_cpufreq_match_data { | |
49 | int (*get_version)(struct device *cpu_dev, | |
50 | struct nvmem_cell *speedbin_nvmem, | |
a8811ec7 | 51 | char **pvs_name, |
57f2f8b4 | 52 | struct qcom_cpufreq_drv *drv); |
1cb8339c | 53 | const char **genpd_names; |
57f2f8b4 NC |
54 | }; |
55 | ||
2a5d46c3 SG |
56 | struct qcom_cpufreq_drv_cpu { |
57 | int opp_token; | |
58 | }; | |
59 | ||
57f2f8b4 | 60 | struct qcom_cpufreq_drv { |
57f2f8b4 NC |
61 | u32 versions; |
62 | const struct qcom_cpufreq_match_data *data; | |
2a5d46c3 | 63 | struct qcom_cpufreq_drv_cpu cpus[]; |
57f2f8b4 NC |
64 | }; |
65 | ||
7d127095 | 66 | static struct platform_device *cpufreq_dt_pdev, *cpufreq_pdev; |
5ad7346b | 67 | |
f0d64f4a SG |
68 | static int qcom_cpufreq_simple_get_version(struct device *cpu_dev, |
69 | struct nvmem_cell *speedbin_nvmem, | |
70 | char **pvs_name, | |
71 | struct qcom_cpufreq_drv *drv) | |
72 | { | |
73 | u8 *speedbin; | |
74 | ||
75 | *pvs_name = NULL; | |
76 | speedbin = nvmem_cell_read(speedbin_nvmem, NULL); | |
77 | if (IS_ERR(speedbin)) | |
78 | return PTR_ERR(speedbin); | |
79 | ||
80 | dev_dbg(cpu_dev, "speedbin: %d\n", *speedbin); | |
81 | drv->versions = 1 << *speedbin; | |
82 | kfree(speedbin); | |
83 | return 0; | |
84 | } | |
85 | ||
a8811ec7 | 86 | static void get_krait_bin_format_a(struct device *cpu_dev, |
a243a1ed | 87 | int *speed, int *pvs, |
a05887f0 | 88 | u8 *buf) |
a8811ec7 AS |
89 | { |
90 | u32 pte_efuse; | |
91 | ||
92 | pte_efuse = *((u32 *)buf); | |
93 | ||
94 | *speed = pte_efuse & 0xf; | |
95 | if (*speed == 0xf) | |
96 | *speed = (pte_efuse >> 4) & 0xf; | |
97 | ||
98 | if (*speed == 0xf) { | |
99 | *speed = 0; | |
100 | dev_warn(cpu_dev, "Speed bin: Defaulting to %d\n", *speed); | |
101 | } else { | |
102 | dev_dbg(cpu_dev, "Speed bin: %d\n", *speed); | |
103 | } | |
104 | ||
105 | *pvs = (pte_efuse >> 10) & 0x7; | |
106 | if (*pvs == 0x7) | |
107 | *pvs = (pte_efuse >> 13) & 0x7; | |
108 | ||
109 | if (*pvs == 0x7) { | |
110 | *pvs = 0; | |
111 | dev_warn(cpu_dev, "PVS bin: Defaulting to %d\n", *pvs); | |
112 | } else { | |
113 | dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs); | |
114 | } | |
115 | } | |
116 | ||
117 | static void get_krait_bin_format_b(struct device *cpu_dev, | |
118 | int *speed, int *pvs, int *pvs_ver, | |
a05887f0 | 119 | u8 *buf) |
a8811ec7 AS |
120 | { |
121 | u32 pte_efuse, redundant_sel; | |
122 | ||
123 | pte_efuse = *((u32 *)buf); | |
124 | redundant_sel = (pte_efuse >> 24) & 0x7; | |
125 | ||
126 | *pvs_ver = (pte_efuse >> 4) & 0x3; | |
127 | ||
128 | switch (redundant_sel) { | |
129 | case 1: | |
130 | *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); | |
131 | *speed = (pte_efuse >> 27) & 0xf; | |
132 | break; | |
133 | case 2: | |
134 | *pvs = (pte_efuse >> 27) & 0xf; | |
135 | *speed = pte_efuse & 0x7; | |
136 | break; | |
137 | default: | |
138 | /* 4 bits of PVS are in efuse register bits 31, 8-6. */ | |
139 | *pvs = ((pte_efuse >> 28) & 0x8) | ((pte_efuse >> 6) & 0x7); | |
140 | *speed = pte_efuse & 0x7; | |
141 | } | |
142 | ||
143 | /* Check SPEED_BIN_BLOW_STATUS */ | |
144 | if (pte_efuse & BIT(3)) { | |
145 | dev_dbg(cpu_dev, "Speed bin: %d\n", *speed); | |
146 | } else { | |
147 | dev_warn(cpu_dev, "Speed bin not set. Defaulting to 0!\n"); | |
148 | *speed = 0; | |
149 | } | |
150 | ||
151 | /* Check PVS_BLOW_STATUS */ | |
4a8a77ab | 152 | pte_efuse = *(((u32 *)buf) + 1); |
a8811ec7 AS |
153 | pte_efuse &= BIT(21); |
154 | if (pte_efuse) { | |
155 | dev_dbg(cpu_dev, "PVS bin: %d\n", *pvs); | |
156 | } else { | |
157 | dev_warn(cpu_dev, "PVS bin not set. Defaulting to 0!\n"); | |
158 | *pvs = 0; | |
159 | } | |
160 | ||
161 | dev_dbg(cpu_dev, "PVS version: %d\n", *pvs_ver); | |
162 | } | |
163 | ||
7d127095 S |
164 | static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, |
165 | struct nvmem_cell *speedbin_nvmem, | |
a8811ec7 | 166 | char **pvs_name, |
57f2f8b4 | 167 | struct qcom_cpufreq_drv *drv) |
46e2856b | 168 | { |
7d127095 | 169 | size_t len; |
7d0f03d1 | 170 | u32 msm_id; |
7d127095 | 171 | u8 *speedbin; |
7d0f03d1 | 172 | int ret; |
a8811ec7 | 173 | *pvs_name = NULL; |
7d127095 | 174 | |
7d0f03d1 RM |
175 | ret = qcom_smem_get_soc_id(&msm_id); |
176 | if (ret) | |
177 | return ret; | |
7d127095 S |
178 | |
179 | speedbin = nvmem_cell_read(speedbin_nvmem, &len); | |
180 | if (IS_ERR(speedbin)) | |
181 | return PTR_ERR(speedbin); | |
182 | ||
7d0f03d1 RM |
183 | switch (msm_id) { |
184 | case QCOM_ID_MSM8996: | |
185 | case QCOM_ID_APQ8096: | |
ba5a61a0 VN |
186 | case QCOM_ID_IPQ5332: |
187 | case QCOM_ID_IPQ5322: | |
188 | case QCOM_ID_IPQ5312: | |
189 | case QCOM_ID_IPQ5302: | |
190 | case QCOM_ID_IPQ5300: | |
5b5b5806 VN |
191 | case QCOM_ID_IPQ9514: |
192 | case QCOM_ID_IPQ9550: | |
193 | case QCOM_ID_IPQ9554: | |
194 | case QCOM_ID_IPQ9570: | |
195 | case QCOM_ID_IPQ9574: | |
57f2f8b4 | 196 | drv->versions = 1 << (unsigned int)(*speedbin); |
7d127095 | 197 | break; |
7d0f03d1 RM |
198 | case QCOM_ID_MSM8996SG: |
199 | case QCOM_ID_APQ8096SG: | |
57f2f8b4 | 200 | drv->versions = 1 << ((unsigned int)(*speedbin) + 4); |
7d127095 S |
201 | break; |
202 | default: | |
203 | BUG(); | |
204 | break; | |
205 | } | |
206 | ||
207 | kfree(speedbin); | |
208 | return 0; | |
209 | } | |
210 | ||
a8811ec7 AS |
211 | static int qcom_cpufreq_krait_name_version(struct device *cpu_dev, |
212 | struct nvmem_cell *speedbin_nvmem, | |
213 | char **pvs_name, | |
214 | struct qcom_cpufreq_drv *drv) | |
215 | { | |
216 | int speed = 0, pvs = 0, pvs_ver = 0; | |
217 | u8 *speedbin; | |
218 | size_t len; | |
9f42cf54 | 219 | int ret = 0; |
a8811ec7 AS |
220 | |
221 | speedbin = nvmem_cell_read(speedbin_nvmem, &len); | |
222 | ||
223 | if (IS_ERR(speedbin)) | |
224 | return PTR_ERR(speedbin); | |
225 | ||
226 | switch (len) { | |
227 | case 4: | |
a243a1ed | 228 | get_krait_bin_format_a(cpu_dev, &speed, &pvs, speedbin); |
a8811ec7 AS |
229 | break; |
230 | case 8: | |
231 | get_krait_bin_format_b(cpu_dev, &speed, &pvs, &pvs_ver, | |
a05887f0 | 232 | speedbin); |
a8811ec7 AS |
233 | break; |
234 | default: | |
235 | dev_err(cpu_dev, "Unable to read nvmem data. Defaulting to 0!\n"); | |
9f42cf54 FP |
236 | ret = -ENODEV; |
237 | goto len_error; | |
a8811ec7 AS |
238 | } |
239 | ||
240 | snprintf(*pvs_name, sizeof("speedXX-pvsXX-vXX"), "speed%d-pvs%d-v%d", | |
241 | speed, pvs, pvs_ver); | |
242 | ||
243 | drv->versions = (1 << speed); | |
244 | ||
9f42cf54 | 245 | len_error: |
a8811ec7 | 246 | kfree(speedbin); |
9f42cf54 | 247 | return ret; |
a8811ec7 AS |
248 | } |
249 | ||
4a3754f7 CM |
250 | static int qcom_cpufreq_ipq8064_name_version(struct device *cpu_dev, |
251 | struct nvmem_cell *speedbin_nvmem, | |
252 | char **pvs_name, | |
253 | struct qcom_cpufreq_drv *drv) | |
254 | { | |
255 | int speed = 0, pvs = 0; | |
256 | int msm_id, ret = 0; | |
257 | u8 *speedbin; | |
258 | size_t len; | |
259 | ||
260 | speedbin = nvmem_cell_read(speedbin_nvmem, &len); | |
261 | if (IS_ERR(speedbin)) | |
262 | return PTR_ERR(speedbin); | |
263 | ||
264 | if (len != 4) { | |
265 | dev_err(cpu_dev, "Unable to read nvmem data. Defaulting to 0!\n"); | |
266 | ret = -ENODEV; | |
267 | goto exit; | |
268 | } | |
269 | ||
270 | get_krait_bin_format_a(cpu_dev, &speed, &pvs, speedbin); | |
271 | ||
272 | ret = qcom_smem_get_soc_id(&msm_id); | |
273 | if (ret) | |
274 | goto exit; | |
275 | ||
276 | switch (msm_id) { | |
277 | case QCOM_ID_IPQ8062: | |
278 | drv->versions = BIT(IPQ8062_VERSION); | |
279 | break; | |
280 | case QCOM_ID_IPQ8064: | |
281 | case QCOM_ID_IPQ8066: | |
282 | case QCOM_ID_IPQ8068: | |
283 | drv->versions = BIT(IPQ8064_VERSION); | |
284 | break; | |
285 | case QCOM_ID_IPQ8065: | |
286 | case QCOM_ID_IPQ8069: | |
287 | drv->versions = BIT(IPQ8065_VERSION); | |
288 | break; | |
289 | default: | |
290 | dev_err(cpu_dev, | |
291 | "SoC ID %u is not part of IPQ8064 family, limiting to 1.0GHz!\n", | |
292 | msm_id); | |
293 | drv->versions = BIT(IPQ8062_VERSION); | |
294 | break; | |
295 | } | |
296 | ||
297 | /* IPQ8064 speed is never fused. Only pvs values are fused. */ | |
298 | snprintf(*pvs_name, sizeof("speed0-pvsXX"), "speed0-pvs%d", pvs); | |
299 | ||
300 | exit: | |
301 | kfree(speedbin); | |
302 | return ret; | |
303 | } | |
304 | ||
47e161a7 RM |
305 | static int qcom_cpufreq_ipq6018_name_version(struct device *cpu_dev, |
306 | struct nvmem_cell *speedbin_nvmem, | |
307 | char **pvs_name, | |
308 | struct qcom_cpufreq_drv *drv) | |
309 | { | |
310 | u32 msm_id; | |
311 | int ret; | |
312 | u8 *speedbin; | |
313 | *pvs_name = NULL; | |
314 | ||
315 | ret = qcom_smem_get_soc_id(&msm_id); | |
316 | if (ret) | |
317 | return ret; | |
318 | ||
319 | speedbin = nvmem_cell_read(speedbin_nvmem, NULL); | |
320 | if (IS_ERR(speedbin)) | |
321 | return PTR_ERR(speedbin); | |
322 | ||
323 | switch (msm_id) { | |
324 | case QCOM_ID_IPQ6005: | |
325 | case QCOM_ID_IPQ6010: | |
326 | case QCOM_ID_IPQ6018: | |
327 | case QCOM_ID_IPQ6028: | |
328 | /* Fuse Value Freq BIT to set | |
329 | * --------------------------------- | |
330 | * 2’b0 No Limit BIT(0) | |
331 | * 2’b1 1.5 GHz BIT(1) | |
332 | */ | |
333 | drv->versions = 1 << (unsigned int)(*speedbin); | |
334 | break; | |
335 | case QCOM_ID_IPQ6000: | |
336 | /* | |
337 | * IPQ6018 family only has one bit to advertise the CPU | |
338 | * speed-bin, but that is not enough for IPQ6000 which | |
339 | * is only rated up to 1.2GHz. | |
340 | * So for IPQ6000 manually set BIT(2) based on SMEM ID. | |
341 | */ | |
342 | drv->versions = IPQ6000_VERSION; | |
343 | break; | |
344 | default: | |
345 | dev_err(cpu_dev, | |
346 | "SoC ID %u is not part of IPQ6018 family, limiting to 1.2GHz!\n", | |
347 | msm_id); | |
348 | drv->versions = IPQ6000_VERSION; | |
349 | break; | |
350 | } | |
351 | ||
352 | kfree(speedbin); | |
353 | return 0; | |
354 | } | |
355 | ||
0b9cd949 RM |
356 | static int qcom_cpufreq_ipq8074_name_version(struct device *cpu_dev, |
357 | struct nvmem_cell *speedbin_nvmem, | |
358 | char **pvs_name, | |
359 | struct qcom_cpufreq_drv *drv) | |
360 | { | |
361 | u32 msm_id; | |
362 | int ret; | |
363 | *pvs_name = NULL; | |
364 | ||
365 | ret = qcom_smem_get_soc_id(&msm_id); | |
366 | if (ret) | |
367 | return ret; | |
368 | ||
369 | switch (msm_id) { | |
370 | case QCOM_ID_IPQ8070A: | |
371 | case QCOM_ID_IPQ8071A: | |
372 | case QCOM_ID_IPQ8172: | |
373 | case QCOM_ID_IPQ8173: | |
374 | case QCOM_ID_IPQ8174: | |
375 | drv->versions = BIT(IPQ8074_ACORN_VERSION); | |
376 | break; | |
377 | case QCOM_ID_IPQ8072A: | |
378 | case QCOM_ID_IPQ8074A: | |
379 | case QCOM_ID_IPQ8076A: | |
380 | case QCOM_ID_IPQ8078A: | |
381 | drv->versions = BIT(IPQ8074_HAWKEYE_VERSION); | |
382 | break; | |
383 | default: | |
384 | dev_err(cpu_dev, | |
385 | "SoC ID %u is not part of IPQ8074 family, limiting to 1.4GHz!\n", | |
386 | msm_id); | |
387 | drv->versions = BIT(IPQ8074_ACORN_VERSION); | |
388 | break; | |
389 | } | |
390 | ||
391 | return 0; | |
392 | } | |
393 | ||
f0d64f4a SG |
394 | static const char *generic_genpd_names[] = { "perf", NULL }; |
395 | ||
57f2f8b4 NC |
396 | static const struct qcom_cpufreq_match_data match_data_kryo = { |
397 | .get_version = qcom_cpufreq_kryo_name_version, | |
398 | }; | |
399 | ||
a8811ec7 AS |
400 | static const struct qcom_cpufreq_match_data match_data_krait = { |
401 | .get_version = qcom_cpufreq_krait_name_version, | |
402 | }; | |
403 | ||
f0d64f4a SG |
404 | static const struct qcom_cpufreq_match_data match_data_msm8909 = { |
405 | .get_version = qcom_cpufreq_simple_get_version, | |
406 | .genpd_names = generic_genpd_names, | |
407 | }; | |
408 | ||
1cb8339c NC |
409 | static const char *qcs404_genpd_names[] = { "cpr", NULL }; |
410 | ||
411 | static const struct qcom_cpufreq_match_data match_data_qcs404 = { | |
412 | .genpd_names = qcs404_genpd_names, | |
413 | }; | |
414 | ||
47e161a7 RM |
415 | static const struct qcom_cpufreq_match_data match_data_ipq6018 = { |
416 | .get_version = qcom_cpufreq_ipq6018_name_version, | |
417 | }; | |
418 | ||
4a3754f7 CM |
419 | static const struct qcom_cpufreq_match_data match_data_ipq8064 = { |
420 | .get_version = qcom_cpufreq_ipq8064_name_version, | |
421 | }; | |
422 | ||
0b9cd949 RM |
423 | static const struct qcom_cpufreq_match_data match_data_ipq8074 = { |
424 | .get_version = qcom_cpufreq_ipq8074_name_version, | |
425 | }; | |
426 | ||
7d127095 S |
427 | static int qcom_cpufreq_probe(struct platform_device *pdev) |
428 | { | |
57f2f8b4 | 429 | struct qcom_cpufreq_drv *drv; |
46e2856b IL |
430 | struct nvmem_cell *speedbin_nvmem; |
431 | struct device_node *np; | |
432 | struct device *cpu_dev; | |
01039fb8 FP |
433 | char pvs_name_buffer[] = "speedXX-pvsXX-vXX"; |
434 | char *pvs_name = pvs_name_buffer; | |
46e2856b | 435 | unsigned cpu; |
7d127095 | 436 | const struct of_device_id *match; |
46e2856b IL |
437 | int ret; |
438 | ||
439 | cpu_dev = get_cpu_device(0); | |
1dd2058f DC |
440 | if (!cpu_dev) |
441 | return -ENODEV; | |
46e2856b | 442 | |
46e2856b | 443 | np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); |
1dd2058f DC |
444 | if (!np) |
445 | return -ENOENT; | |
46e2856b | 446 | |
ff63282e DB |
447 | ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu") || |
448 | of_device_is_compatible(np, "operating-points-v2-krait-cpu"); | |
46e2856b IL |
449 | if (!ret) { |
450 | of_node_put(np); | |
451 | return -ENOENT; | |
452 | } | |
453 | ||
2a5d46c3 SG |
454 | drv = devm_kzalloc(&pdev->dev, struct_size(drv, cpus, num_possible_cpus()), |
455 | GFP_KERNEL); | |
57f2f8b4 NC |
456 | if (!drv) |
457 | return -ENOMEM; | |
458 | ||
459 | match = pdev->dev.platform_data; | |
460 | drv->data = match->data; | |
2a5d46c3 SG |
461 | if (!drv->data) |
462 | return -ENODEV; | |
46e2856b | 463 | |
57f2f8b4 NC |
464 | if (drv->data->get_version) { |
465 | speedbin_nvmem = of_nvmem_cell_get(np, NULL); | |
2a5d46c3 SG |
466 | if (IS_ERR(speedbin_nvmem)) |
467 | return dev_err_probe(cpu_dev, PTR_ERR(speedbin_nvmem), | |
468 | "Could not get nvmem cell\n"); | |
46e2856b | 469 | |
a8811ec7 AS |
470 | ret = drv->data->get_version(cpu_dev, |
471 | speedbin_nvmem, &pvs_name, drv); | |
57f2f8b4 NC |
472 | if (ret) { |
473 | nvmem_cell_put(speedbin_nvmem); | |
2a5d46c3 | 474 | return ret; |
57f2f8b4 NC |
475 | } |
476 | nvmem_cell_put(speedbin_nvmem); | |
477 | } | |
478 | of_node_put(np); | |
479 | ||
46e2856b | 480 | for_each_possible_cpu(cpu) { |
49cd000d VK |
481 | struct dev_pm_opp_config config = { |
482 | .supported_hw = NULL, | |
483 | }; | |
484 | ||
46e2856b IL |
485 | cpu_dev = get_cpu_device(cpu); |
486 | if (NULL == cpu_dev) { | |
487 | ret = -ENODEV; | |
49cd000d | 488 | goto free_opp; |
46e2856b IL |
489 | } |
490 | ||
57f2f8b4 | 491 | if (drv->data->get_version) { |
49cd000d VK |
492 | config.supported_hw = &drv->versions; |
493 | config.supported_hw_count = 1; | |
a8811ec7 | 494 | |
49cd000d VK |
495 | if (pvs_name) |
496 | config.prop_name = pvs_name; | |
1cb8339c NC |
497 | } |
498 | ||
499 | if (drv->data->genpd_names) { | |
49cd000d VK |
500 | config.genpd_names = drv->data->genpd_names; |
501 | config.virt_devs = NULL; | |
502 | } | |
503 | ||
504 | if (config.supported_hw || config.genpd_names) { | |
2a5d46c3 SG |
505 | drv->cpus[cpu].opp_token = dev_pm_opp_set_config(cpu_dev, &config); |
506 | if (drv->cpus[cpu].opp_token < 0) { | |
507 | ret = drv->cpus[cpu].opp_token; | |
49cd000d VK |
508 | dev_err(cpu_dev, "Failed to set OPP config\n"); |
509 | goto free_opp; | |
57f2f8b4 | 510 | } |
46e2856b IL |
511 | } |
512 | } | |
513 | ||
514 | cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1, | |
515 | NULL, 0); | |
0334906c | 516 | if (!IS_ERR(cpufreq_dt_pdev)) { |
57f2f8b4 | 517 | platform_set_drvdata(pdev, drv); |
46e2856b | 518 | return 0; |
0334906c | 519 | } |
46e2856b IL |
520 | |
521 | ret = PTR_ERR(cpufreq_dt_pdev); | |
522 | dev_err(cpu_dev, "Failed to register platform device\n"); | |
523 | ||
524 | free_opp: | |
49cd000d | 525 | for_each_possible_cpu(cpu) |
2a5d46c3 | 526 | dev_pm_opp_clear_config(drv->cpus[cpu].opp_token); |
46e2856b IL |
527 | return ret; |
528 | } | |
529 | ||
40273232 | 530 | static void qcom_cpufreq_remove(struct platform_device *pdev) |
5ad7346b | 531 | { |
57f2f8b4 | 532 | struct qcom_cpufreq_drv *drv = platform_get_drvdata(pdev); |
0334906c VK |
533 | unsigned int cpu; |
534 | ||
5ad7346b | 535 | platform_device_unregister(cpufreq_dt_pdev); |
0334906c | 536 | |
49cd000d | 537 | for_each_possible_cpu(cpu) |
2a5d46c3 | 538 | dev_pm_opp_clear_config(drv->cpus[cpu].opp_token); |
5ad7346b IL |
539 | } |
540 | ||
7d127095 S |
541 | static struct platform_driver qcom_cpufreq_driver = { |
542 | .probe = qcom_cpufreq_probe, | |
40273232 | 543 | .remove_new = qcom_cpufreq_remove, |
46e2856b | 544 | .driver = { |
7d127095 | 545 | .name = "qcom-cpufreq-nvmem", |
46e2856b IL |
546 | }, |
547 | }; | |
548 | ||
7d127095 | 549 | static const struct of_device_id qcom_cpufreq_match_list[] __initconst = { |
57f2f8b4 | 550 | { .compatible = "qcom,apq8096", .data = &match_data_kryo }, |
f0d64f4a | 551 | { .compatible = "qcom,msm8909", .data = &match_data_msm8909 }, |
57f2f8b4 | 552 | { .compatible = "qcom,msm8996", .data = &match_data_kryo }, |
1cb8339c | 553 | { .compatible = "qcom,qcs404", .data = &match_data_qcs404 }, |
ba5a61a0 | 554 | { .compatible = "qcom,ipq5332", .data = &match_data_kryo }, |
47e161a7 | 555 | { .compatible = "qcom,ipq6018", .data = &match_data_ipq6018 }, |
4a3754f7 | 556 | { .compatible = "qcom,ipq8064", .data = &match_data_ipq8064 }, |
0b9cd949 | 557 | { .compatible = "qcom,ipq8074", .data = &match_data_ipq8074 }, |
a8811ec7 | 558 | { .compatible = "qcom,apq8064", .data = &match_data_krait }, |
5b5b5806 | 559 | { .compatible = "qcom,ipq9574", .data = &match_data_kryo }, |
a8811ec7 AS |
560 | { .compatible = "qcom,msm8974", .data = &match_data_krait }, |
561 | { .compatible = "qcom,msm8960", .data = &match_data_krait }, | |
7d127095 | 562 | {}, |
46e2856b | 563 | }; |
a5a60316 | 564 | MODULE_DEVICE_TABLE(of, qcom_cpufreq_match_list); |
46e2856b IL |
565 | |
566 | /* | |
567 | * Since the driver depends on smem and nvmem drivers, which may | |
568 | * return EPROBE_DEFER, all the real activity is done in the probe, | |
569 | * which may be defered as well. The init here is only registering | |
570 | * the driver and the platform device. | |
571 | */ | |
7d127095 | 572 | static int __init qcom_cpufreq_init(void) |
46e2856b IL |
573 | { |
574 | struct device_node *np = of_find_node_by_path("/"); | |
575 | const struct of_device_id *match; | |
576 | int ret; | |
577 | ||
578 | if (!np) | |
579 | return -ENODEV; | |
580 | ||
7d127095 | 581 | match = of_match_node(qcom_cpufreq_match_list, np); |
46e2856b IL |
582 | of_node_put(np); |
583 | if (!match) | |
584 | return -ENODEV; | |
585 | ||
7d127095 | 586 | ret = platform_driver_register(&qcom_cpufreq_driver); |
46e2856b IL |
587 | if (unlikely(ret < 0)) |
588 | return ret; | |
589 | ||
7d127095 S |
590 | cpufreq_pdev = platform_device_register_data(NULL, "qcom-cpufreq-nvmem", |
591 | -1, match, sizeof(*match)); | |
592 | ret = PTR_ERR_OR_ZERO(cpufreq_pdev); | |
46e2856b IL |
593 | if (0 == ret) |
594 | return 0; | |
595 | ||
7d127095 | 596 | platform_driver_unregister(&qcom_cpufreq_driver); |
46e2856b IL |
597 | return ret; |
598 | } | |
7d127095 | 599 | module_init(qcom_cpufreq_init); |
46e2856b | 600 | |
7d127095 | 601 | static void __exit qcom_cpufreq_exit(void) |
5ad7346b | 602 | { |
7d127095 S |
603 | platform_device_unregister(cpufreq_pdev); |
604 | platform_driver_unregister(&qcom_cpufreq_driver); | |
5ad7346b | 605 | } |
7d127095 | 606 | module_exit(qcom_cpufreq_exit); |
5ad7346b | 607 | |
7d127095 | 608 | MODULE_DESCRIPTION("Qualcomm Technologies, Inc. CPUfreq driver"); |
46e2856b | 609 | MODULE_LICENSE("GPL v2"); |