1 // SPDX-License-Identifier: GPL-2.0-only
3 * TI CPUFreq/OPP hw-supported driver
5 * Copyright (C) 2016-2017 Texas Instruments, Inc.
6 * Dave Gerlach <d-gerlach@ti.com>
11 #include <linux/mfd/syscon.h>
12 #include <linux/module.h>
13 #include <linux/init.h>
15 #include <linux/platform_device.h>
16 #include <linux/pm_opp.h>
17 #include <linux/regmap.h>
18 #include <linux/slab.h>
19 #include <linux/sys_soc.h>
21 #define REVISION_MASK 0xF
22 #define REVISION_SHIFT 28
24 #define AM33XX_800M_ARM_MPU_MAX_FREQ 0x1E2F
25 #define AM43XX_600M_ARM_MPU_MAX_FREQ 0xFFA
27 #define DRA7_EFUSE_HAS_OD_MPU_OPP 11
28 #define DRA7_EFUSE_HAS_HIGH_MPU_OPP 15
29 #define DRA76_EFUSE_HAS_PLUS_MPU_OPP 18
30 #define DRA7_EFUSE_HAS_ALL_MPU_OPP 23
31 #define DRA76_EFUSE_HAS_ALL_MPU_OPP 24
33 #define DRA7_EFUSE_NOM_MPU_OPP BIT(0)
34 #define DRA7_EFUSE_OD_MPU_OPP BIT(1)
35 #define DRA7_EFUSE_HIGH_MPU_OPP BIT(2)
36 #define DRA76_EFUSE_PLUS_MPU_OPP BIT(3)
38 #define OMAP3_CONTROL_DEVICE_STATUS 0x4800244C
39 #define OMAP3_CONTROL_IDCODE 0x4830A204
40 #define OMAP34xx_ProdID_SKUID 0x4830A20C
41 #define OMAP3_SYSCON_BASE (0x48000000 + 0x2000 + 0x270)
43 #define AM625_EFUSE_K_MPU_OPP 11
44 #define AM625_EFUSE_S_MPU_OPP 19
45 #define AM625_EFUSE_T_MPU_OPP 20
47 #define AM625_SUPPORT_K_MPU_OPP BIT(0)
48 #define AM625_SUPPORT_S_MPU_OPP BIT(1)
49 #define AM625_SUPPORT_T_MPU_OPP BIT(2)
52 AM62A7_EFUSE_M_MPU_OPP
= 13,
53 AM62A7_EFUSE_N_MPU_OPP
,
54 AM62A7_EFUSE_O_MPU_OPP
,
55 AM62A7_EFUSE_P_MPU_OPP
,
56 AM62A7_EFUSE_Q_MPU_OPP
,
57 AM62A7_EFUSE_R_MPU_OPP
,
58 AM62A7_EFUSE_S_MPU_OPP
,
60 * The V, U, and T speed grade numbering is out of order
61 * to align with the AM625 more uniformly. I promise I know
64 AM62A7_EFUSE_V_MPU_OPP
,
65 AM62A7_EFUSE_U_MPU_OPP
,
66 AM62A7_EFUSE_T_MPU_OPP
,
69 #define AM62A7_SUPPORT_N_MPU_OPP BIT(0)
70 #define AM62A7_SUPPORT_R_MPU_OPP BIT(1)
71 #define AM62A7_SUPPORT_V_MPU_OPP BIT(2)
73 #define AM62P5_EFUSE_O_MPU_OPP 15
74 #define AM62P5_EFUSE_S_MPU_OPP 19
75 #define AM62P5_EFUSE_U_MPU_OPP 21
77 #define AM62P5_SUPPORT_O_MPU_OPP BIT(0)
78 #define AM62P5_SUPPORT_U_MPU_OPP BIT(2)
80 #define VERSION_COUNT 2
82 struct ti_cpufreq_data
;
84 struct ti_cpufreq_soc_data
{
85 const char * const *reg_names
;
86 unsigned long (*efuse_xlate
)(struct ti_cpufreq_data
*opp_data
,
88 unsigned long efuse_fallback
;
89 unsigned long efuse_offset
;
90 unsigned long efuse_mask
;
91 unsigned long efuse_shift
;
92 unsigned long rev_offset
;
94 /* Backward compatibility hack: Might have missing syscon */
95 #define TI_QUIRK_SYSCON_MAY_BE_MISSING 0x1
99 struct ti_cpufreq_data
{
100 struct device
*cpu_dev
;
101 struct device_node
*opp_node
;
102 struct regmap
*syscon
;
103 const struct ti_cpufreq_soc_data
*soc_data
;
106 static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data
*opp_data
,
110 efuse
= opp_data
->soc_data
->efuse_fallback
;
111 /* AM335x and AM437x use "OPP disable" bits, so invert */
115 static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data
*opp_data
,
118 unsigned long calculated_efuse
= DRA7_EFUSE_NOM_MPU_OPP
;
121 * The efuse on dra7 and am57 parts contains a specific
122 * value indicating the highest available OPP.
126 case DRA76_EFUSE_HAS_PLUS_MPU_OPP
:
127 case DRA76_EFUSE_HAS_ALL_MPU_OPP
:
128 calculated_efuse
|= DRA76_EFUSE_PLUS_MPU_OPP
;
130 case DRA7_EFUSE_HAS_ALL_MPU_OPP
:
131 case DRA7_EFUSE_HAS_HIGH_MPU_OPP
:
132 calculated_efuse
|= DRA7_EFUSE_HIGH_MPU_OPP
;
134 case DRA7_EFUSE_HAS_OD_MPU_OPP
:
135 calculated_efuse
|= DRA7_EFUSE_OD_MPU_OPP
;
138 return calculated_efuse
;
141 static unsigned long omap3_efuse_xlate(struct ti_cpufreq_data
*opp_data
,
144 /* OPP enable bit ("Speed Binned") */
148 static unsigned long am62p5_efuse_xlate(struct ti_cpufreq_data
*opp_data
,
151 unsigned long calculated_efuse
= AM62P5_SUPPORT_O_MPU_OPP
;
154 case AM62P5_EFUSE_U_MPU_OPP
:
155 case AM62P5_EFUSE_S_MPU_OPP
:
156 calculated_efuse
|= AM62P5_SUPPORT_U_MPU_OPP
;
158 case AM62P5_EFUSE_O_MPU_OPP
:
159 calculated_efuse
|= AM62P5_SUPPORT_O_MPU_OPP
;
162 return calculated_efuse
;
165 static unsigned long am62a7_efuse_xlate(struct ti_cpufreq_data
*opp_data
,
168 unsigned long calculated_efuse
= AM62A7_SUPPORT_N_MPU_OPP
;
171 case AM62A7_EFUSE_V_MPU_OPP
:
172 case AM62A7_EFUSE_U_MPU_OPP
:
173 case AM62A7_EFUSE_T_MPU_OPP
:
174 case AM62A7_EFUSE_S_MPU_OPP
:
175 calculated_efuse
|= AM62A7_SUPPORT_V_MPU_OPP
;
177 case AM62A7_EFUSE_R_MPU_OPP
:
178 case AM62A7_EFUSE_Q_MPU_OPP
:
179 case AM62A7_EFUSE_P_MPU_OPP
:
180 case AM62A7_EFUSE_O_MPU_OPP
:
181 calculated_efuse
|= AM62A7_SUPPORT_R_MPU_OPP
;
183 case AM62A7_EFUSE_N_MPU_OPP
:
184 case AM62A7_EFUSE_M_MPU_OPP
:
185 calculated_efuse
|= AM62A7_SUPPORT_N_MPU_OPP
;
188 return calculated_efuse
;
191 static unsigned long am625_efuse_xlate(struct ti_cpufreq_data
*opp_data
,
194 unsigned long calculated_efuse
= AM625_SUPPORT_K_MPU_OPP
;
197 case AM625_EFUSE_T_MPU_OPP
:
198 calculated_efuse
|= AM625_SUPPORT_T_MPU_OPP
;
200 case AM625_EFUSE_S_MPU_OPP
:
201 calculated_efuse
|= AM625_SUPPORT_S_MPU_OPP
;
203 case AM625_EFUSE_K_MPU_OPP
:
204 calculated_efuse
|= AM625_SUPPORT_K_MPU_OPP
;
207 return calculated_efuse
;
210 static struct ti_cpufreq_soc_data am3x_soc_data
= {
211 .efuse_xlate
= amx3_efuse_xlate
,
212 .efuse_fallback
= AM33XX_800M_ARM_MPU_MAX_FREQ
,
213 .efuse_offset
= 0x07fc,
214 .efuse_mask
= 0x1fff,
216 .multi_regulator
= false,
219 static struct ti_cpufreq_soc_data am4x_soc_data
= {
220 .efuse_xlate
= amx3_efuse_xlate
,
221 .efuse_fallback
= AM43XX_600M_ARM_MPU_MAX_FREQ
,
222 .efuse_offset
= 0x0610,
225 .multi_regulator
= false,
228 static struct ti_cpufreq_soc_data dra7_soc_data
= {
229 .efuse_xlate
= dra7_efuse_xlate
,
230 .efuse_offset
= 0x020c,
231 .efuse_mask
= 0xf80000,
234 .multi_regulator
= true,
238 * OMAP35x TRM (SPRUF98K):
239 * CONTROL_IDCODE (0x4830 A204) describes Silicon revisions.
240 * Control OMAP Status Register 15:0 (Address 0x4800 244C)
241 * to separate between omap3503, omap3515, omap3525, omap3530
242 * and feature presence.
243 * There are encodings for versions limited to 400/266MHz
245 * Not clear if this also holds for omap34xx.
246 * some eFuse values e.g. CONTROL_FUSE_OPP1_VDD1
247 * are stored in the SYSCON register range
248 * Register 0x4830A20C [ProdID.SKUID] [0:3]
249 * 0x0 for normal 600/430MHz device.
250 * 0x8 for 720/520MHz device.
251 * Not clear what omap34xx value is.
254 static struct ti_cpufreq_soc_data omap34xx_soc_data
= {
255 .efuse_xlate
= omap3_efuse_xlate
,
256 .efuse_offset
= OMAP34xx_ProdID_SKUID
- OMAP3_SYSCON_BASE
,
258 .efuse_mask
= BIT(3),
259 .rev_offset
= OMAP3_CONTROL_IDCODE
- OMAP3_SYSCON_BASE
,
260 .multi_regulator
= false,
261 .quirks
= TI_QUIRK_SYSCON_MAY_BE_MISSING
,
265 * AM/DM37x TRM (SPRUGN4M)
266 * CONTROL_IDCODE (0x4830 A204) describes Silicon revisions.
267 * Control Device Status Register 15:0 (Address 0x4800 244C)
268 * to separate between am3703, am3715, dm3725, dm3730
269 * and feature presence.
270 * Speed Binned = Bit 9
273 * some eFuse values e.g. CONTROL_FUSE_OPP 1G_VDD1
274 * are stored in the SYSCON register range.
275 * There is no 0x4830A20C [ProdID.SKUID] register (exists but
276 * seems to always read as 0).
279 static const char * const omap3_reg_names
[] = {"cpu0", "vbb", NULL
};
281 static struct ti_cpufreq_soc_data omap36xx_soc_data
= {
282 .reg_names
= omap3_reg_names
,
283 .efuse_xlate
= omap3_efuse_xlate
,
284 .efuse_offset
= OMAP3_CONTROL_DEVICE_STATUS
- OMAP3_SYSCON_BASE
,
286 .efuse_mask
= BIT(9),
287 .rev_offset
= OMAP3_CONTROL_IDCODE
- OMAP3_SYSCON_BASE
,
288 .multi_regulator
= true,
289 .quirks
= TI_QUIRK_SYSCON_MAY_BE_MISSING
,
293 * AM3517 is quite similar to AM/DM37x except that it has no
294 * high speed grade eFuse and no abb ldo
297 static struct ti_cpufreq_soc_data am3517_soc_data
= {
298 .efuse_xlate
= omap3_efuse_xlate
,
299 .efuse_offset
= OMAP3_CONTROL_DEVICE_STATUS
- OMAP3_SYSCON_BASE
,
302 .rev_offset
= OMAP3_CONTROL_IDCODE
- OMAP3_SYSCON_BASE
,
303 .multi_regulator
= false,
304 .quirks
= TI_QUIRK_SYSCON_MAY_BE_MISSING
,
307 static const struct soc_device_attribute k3_cpufreq_soc
[] = {
308 { .family
= "AM62X", .revision
= "SR1.0" },
309 { .family
= "AM62AX", .revision
= "SR1.0" },
310 { .family
= "AM62PX", .revision
= "SR1.0" },
314 static struct ti_cpufreq_soc_data am625_soc_data
= {
315 .efuse_xlate
= am625_efuse_xlate
,
316 .efuse_offset
= 0x0018,
317 .efuse_mask
= 0x07c0,
319 .rev_offset
= 0x0014,
320 .multi_regulator
= false,
323 static struct ti_cpufreq_soc_data am62a7_soc_data
= {
324 .efuse_xlate
= am62a7_efuse_xlate
,
326 .efuse_mask
= 0x07c0,
328 .rev_offset
= 0x0014,
329 .multi_regulator
= false,
332 static struct ti_cpufreq_soc_data am62p5_soc_data
= {
333 .efuse_xlate
= am62p5_efuse_xlate
,
335 .efuse_mask
= 0x07c0,
337 .rev_offset
= 0x0014,
338 .multi_regulator
= false,
342 * ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC
343 * @opp_data: pointer to ti_cpufreq_data context
344 * @efuse_value: Set to the value parsed from efuse
346 * Returns error code if efuse not read properly.
348 static int ti_cpufreq_get_efuse(struct ti_cpufreq_data
*opp_data
,
351 struct device
*dev
= opp_data
->cpu_dev
;
355 ret
= regmap_read(opp_data
->syscon
, opp_data
->soc_data
->efuse_offset
,
357 if (opp_data
->soc_data
->quirks
& TI_QUIRK_SYSCON_MAY_BE_MISSING
&& ret
== -EIO
) {
358 /* not a syscon register! */
359 void __iomem
*regs
= ioremap(OMAP3_SYSCON_BASE
+
360 opp_data
->soc_data
->efuse_offset
, 4);
369 "Failed to read the efuse value from syscon: %d\n",
374 efuse
= (efuse
& opp_data
->soc_data
->efuse_mask
);
375 efuse
>>= opp_data
->soc_data
->efuse_shift
;
377 *efuse_value
= opp_data
->soc_data
->efuse_xlate(opp_data
, efuse
);
383 * ti_cpufreq_get_rev() - Parse and return rev value present on SoC
384 * @opp_data: pointer to ti_cpufreq_data context
385 * @revision_value: Set to the value parsed from revision register
387 * Returns error code if revision not read properly.
389 static int ti_cpufreq_get_rev(struct ti_cpufreq_data
*opp_data
,
392 struct device
*dev
= opp_data
->cpu_dev
;
395 if (soc_device_match(k3_cpufreq_soc
)) {
397 * Since the SR is 1.0, hard code the revision_value as
398 * 0x1 here. This way we avoid re using the same register
399 * that is giving us required information inside socinfo
402 *revision_value
= 0x1;
406 ret
= regmap_read(opp_data
->syscon
, opp_data
->soc_data
->rev_offset
,
408 if (opp_data
->soc_data
->quirks
& TI_QUIRK_SYSCON_MAY_BE_MISSING
&& ret
== -EIO
) {
409 /* not a syscon register! */
410 void __iomem
*regs
= ioremap(OMAP3_SYSCON_BASE
+
411 opp_data
->soc_data
->rev_offset
, 4);
415 revision
= readl(regs
);
420 "Failed to read the revision number from syscon: %d\n",
425 *revision_value
= BIT((revision
>> REVISION_SHIFT
) & REVISION_MASK
);
431 static int ti_cpufreq_setup_syscon_register(struct ti_cpufreq_data
*opp_data
)
433 struct device
*dev
= opp_data
->cpu_dev
;
434 struct device_node
*np
= opp_data
->opp_node
;
436 opp_data
->syscon
= syscon_regmap_lookup_by_phandle(np
,
438 if (IS_ERR(opp_data
->syscon
)) {
440 "\"syscon\" is missing, cannot use OPPv2 table.\n");
441 return PTR_ERR(opp_data
->syscon
);
447 static const struct of_device_id ti_cpufreq_of_match
[] __maybe_unused
= {
448 { .compatible
= "ti,am33xx", .data
= &am3x_soc_data
, },
449 { .compatible
= "ti,am3517", .data
= &am3517_soc_data
, },
450 { .compatible
= "ti,am43", .data
= &am4x_soc_data
, },
451 { .compatible
= "ti,dra7", .data
= &dra7_soc_data
},
452 { .compatible
= "ti,omap34xx", .data
= &omap34xx_soc_data
, },
453 { .compatible
= "ti,omap36xx", .data
= &omap36xx_soc_data
, },
454 { .compatible
= "ti,am625", .data
= &am625_soc_data
, },
455 { .compatible
= "ti,am62a7", .data
= &am62a7_soc_data
, },
456 { .compatible
= "ti,am62p5", .data
= &am62p5_soc_data
, },
458 { .compatible
= "ti,omap3430", .data
= &omap34xx_soc_data
, },
459 { .compatible
= "ti,omap3630", .data
= &omap36xx_soc_data
, },
463 static const struct of_device_id
*ti_cpufreq_match_node(void)
465 struct device_node
*np
__free(device_node
) = of_find_node_by_path("/");
466 const struct of_device_id
*match
;
468 match
= of_match_node(ti_cpufreq_of_match
, np
);
473 static int ti_cpufreq_probe(struct platform_device
*pdev
)
475 u32 version
[VERSION_COUNT
];
476 const struct of_device_id
*match
;
477 struct ti_cpufreq_data
*opp_data
;
478 const char * const default_reg_names
[] = {"vdd", "vbb", NULL
};
480 struct dev_pm_opp_config config
= {
481 .supported_hw
= version
,
482 .supported_hw_count
= ARRAY_SIZE(version
),
485 match
= dev_get_platdata(&pdev
->dev
);
489 opp_data
= devm_kzalloc(&pdev
->dev
, sizeof(*opp_data
), GFP_KERNEL
);
493 opp_data
->soc_data
= match
->data
;
495 opp_data
->cpu_dev
= get_cpu_device(0);
496 if (!opp_data
->cpu_dev
) {
497 pr_err("%s: Failed to get device for CPU0\n", __func__
);
501 opp_data
->opp_node
= dev_pm_opp_of_get_opp_desc_node(opp_data
->cpu_dev
);
502 if (!opp_data
->opp_node
) {
503 dev_info(opp_data
->cpu_dev
,
504 "OPP-v2 not supported, cpufreq-dt will attempt to use legacy tables.\n");
505 goto register_cpufreq_dt
;
508 ret
= ti_cpufreq_setup_syscon_register(opp_data
);
513 * OPPs determine whether or not they are supported based on
518 ret
= ti_cpufreq_get_rev(opp_data
, &version
[0]);
522 ret
= ti_cpufreq_get_efuse(opp_data
, &version
[1]);
526 if (opp_data
->soc_data
->multi_regulator
) {
527 if (opp_data
->soc_data
->reg_names
)
528 config
.regulator_names
= opp_data
->soc_data
->reg_names
;
530 config
.regulator_names
= default_reg_names
;
533 ret
= dev_pm_opp_set_config(opp_data
->cpu_dev
, &config
);
535 dev_err_probe(opp_data
->cpu_dev
, ret
, "Failed to set OPP config\n");
539 of_node_put(opp_data
->opp_node
);
542 platform_device_register_simple("cpufreq-dt", -1, NULL
, 0);
547 of_node_put(opp_data
->opp_node
);
552 static int __init
ti_cpufreq_init(void)
554 const struct of_device_id
*match
;
556 /* Check to ensure we are on a compatible platform */
557 match
= ti_cpufreq_match_node();
559 platform_device_register_data(NULL
, "ti-cpufreq", -1, match
,
564 module_init(ti_cpufreq_init
);
566 static struct platform_driver ti_cpufreq_driver
= {
567 .probe
= ti_cpufreq_probe
,
569 .name
= "ti-cpufreq",
572 builtin_platform_driver(ti_cpufreq_driver
);
574 MODULE_DESCRIPTION("TI CPUFreq/OPP hw-supported driver");
575 MODULE_AUTHOR("Dave Gerlach <d-gerlach@ti.com>");
576 MODULE_LICENSE("GPL v2");