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