1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright 2019 NXP
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun #include <linux/clk.h>
7*4882a593Smuzhiyun #include <linux/cpu.h>
8*4882a593Smuzhiyun #include <linux/cpufreq.h>
9*4882a593Smuzhiyun #include <linux/err.h>
10*4882a593Smuzhiyun #include <linux/init.h>
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/nvmem-consumer.h>
14*4882a593Smuzhiyun #include <linux/of.h>
15*4882a593Smuzhiyun #include <linux/platform_device.h>
16*4882a593Smuzhiyun #include <linux/pm_opp.h>
17*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include "cpufreq-dt.h"
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define OCOTP_CFG3_SPEED_GRADE_SHIFT 8
23*4882a593Smuzhiyun #define OCOTP_CFG3_SPEED_GRADE_MASK (0x3 << 8)
24*4882a593Smuzhiyun #define IMX8MN_OCOTP_CFG3_SPEED_GRADE_MASK (0xf << 8)
25*4882a593Smuzhiyun #define OCOTP_CFG3_MKT_SEGMENT_SHIFT 6
26*4882a593Smuzhiyun #define OCOTP_CFG3_MKT_SEGMENT_MASK (0x3 << 6)
27*4882a593Smuzhiyun #define IMX8MP_OCOTP_CFG3_MKT_SEGMENT_SHIFT 5
28*4882a593Smuzhiyun #define IMX8MP_OCOTP_CFG3_MKT_SEGMENT_MASK (0x3 << 5)
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #define IMX7ULP_MAX_RUN_FREQ 528000
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun /* cpufreq-dt device registered by imx-cpufreq-dt */
33*4882a593Smuzhiyun static struct platform_device *cpufreq_dt_pdev;
34*4882a593Smuzhiyun static struct opp_table *cpufreq_opp_table;
35*4882a593Smuzhiyun static struct device *cpu_dev;
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun enum IMX7ULP_CPUFREQ_CLKS {
38*4882a593Smuzhiyun ARM,
39*4882a593Smuzhiyun CORE,
40*4882a593Smuzhiyun SCS_SEL,
41*4882a593Smuzhiyun HSRUN_CORE,
42*4882a593Smuzhiyun HSRUN_SCS_SEL,
43*4882a593Smuzhiyun FIRC,
44*4882a593Smuzhiyun };
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun static struct clk_bulk_data imx7ulp_clks[] = {
47*4882a593Smuzhiyun { .id = "arm" },
48*4882a593Smuzhiyun { .id = "core" },
49*4882a593Smuzhiyun { .id = "scs_sel" },
50*4882a593Smuzhiyun { .id = "hsrun_core" },
51*4882a593Smuzhiyun { .id = "hsrun_scs_sel" },
52*4882a593Smuzhiyun { .id = "firc" },
53*4882a593Smuzhiyun };
54*4882a593Smuzhiyun
imx7ulp_get_intermediate(struct cpufreq_policy * policy,unsigned int index)55*4882a593Smuzhiyun static unsigned int imx7ulp_get_intermediate(struct cpufreq_policy *policy,
56*4882a593Smuzhiyun unsigned int index)
57*4882a593Smuzhiyun {
58*4882a593Smuzhiyun return clk_get_rate(imx7ulp_clks[FIRC].clk);
59*4882a593Smuzhiyun }
60*4882a593Smuzhiyun
imx7ulp_target_intermediate(struct cpufreq_policy * policy,unsigned int index)61*4882a593Smuzhiyun static int imx7ulp_target_intermediate(struct cpufreq_policy *policy,
62*4882a593Smuzhiyun unsigned int index)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun unsigned int newfreq = policy->freq_table[index].frequency;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun clk_set_parent(imx7ulp_clks[SCS_SEL].clk, imx7ulp_clks[FIRC].clk);
67*4882a593Smuzhiyun clk_set_parent(imx7ulp_clks[HSRUN_SCS_SEL].clk, imx7ulp_clks[FIRC].clk);
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun if (newfreq > IMX7ULP_MAX_RUN_FREQ)
70*4882a593Smuzhiyun clk_set_parent(imx7ulp_clks[ARM].clk,
71*4882a593Smuzhiyun imx7ulp_clks[HSRUN_CORE].clk);
72*4882a593Smuzhiyun else
73*4882a593Smuzhiyun clk_set_parent(imx7ulp_clks[ARM].clk, imx7ulp_clks[CORE].clk);
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun return 0;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun static struct cpufreq_dt_platform_data imx7ulp_data = {
79*4882a593Smuzhiyun .target_intermediate = imx7ulp_target_intermediate,
80*4882a593Smuzhiyun .get_intermediate = imx7ulp_get_intermediate,
81*4882a593Smuzhiyun };
82*4882a593Smuzhiyun
imx_cpufreq_dt_probe(struct platform_device * pdev)83*4882a593Smuzhiyun static int imx_cpufreq_dt_probe(struct platform_device *pdev)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun struct platform_device *dt_pdev;
86*4882a593Smuzhiyun u32 cell_value, supported_hw[2];
87*4882a593Smuzhiyun int speed_grade, mkt_segment;
88*4882a593Smuzhiyun int ret;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun cpu_dev = get_cpu_device(0);
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun if (!of_find_property(cpu_dev->of_node, "cpu-supply", NULL))
93*4882a593Smuzhiyun return -ENODEV;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun if (of_machine_is_compatible("fsl,imx7ulp")) {
96*4882a593Smuzhiyun ret = clk_bulk_get(cpu_dev, ARRAY_SIZE(imx7ulp_clks),
97*4882a593Smuzhiyun imx7ulp_clks);
98*4882a593Smuzhiyun if (ret)
99*4882a593Smuzhiyun return ret;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun dt_pdev = platform_device_register_data(NULL, "cpufreq-dt",
102*4882a593Smuzhiyun -1, &imx7ulp_data,
103*4882a593Smuzhiyun sizeof(imx7ulp_data));
104*4882a593Smuzhiyun if (IS_ERR(dt_pdev)) {
105*4882a593Smuzhiyun clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks);
106*4882a593Smuzhiyun ret = PTR_ERR(dt_pdev);
107*4882a593Smuzhiyun dev_err(&pdev->dev, "Failed to register cpufreq-dt: %d\n", ret);
108*4882a593Smuzhiyun return ret;
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun cpufreq_dt_pdev = dt_pdev;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun return 0;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun ret = nvmem_cell_read_u32(cpu_dev, "speed_grade", &cell_value);
117*4882a593Smuzhiyun if (ret)
118*4882a593Smuzhiyun return ret;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun if (of_machine_is_compatible("fsl,imx8mn") ||
121*4882a593Smuzhiyun of_machine_is_compatible("fsl,imx8mp"))
122*4882a593Smuzhiyun speed_grade = (cell_value & IMX8MN_OCOTP_CFG3_SPEED_GRADE_MASK)
123*4882a593Smuzhiyun >> OCOTP_CFG3_SPEED_GRADE_SHIFT;
124*4882a593Smuzhiyun else
125*4882a593Smuzhiyun speed_grade = (cell_value & OCOTP_CFG3_SPEED_GRADE_MASK)
126*4882a593Smuzhiyun >> OCOTP_CFG3_SPEED_GRADE_SHIFT;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun if (of_machine_is_compatible("fsl,imx8mp"))
129*4882a593Smuzhiyun mkt_segment = (cell_value & IMX8MP_OCOTP_CFG3_MKT_SEGMENT_MASK)
130*4882a593Smuzhiyun >> IMX8MP_OCOTP_CFG3_MKT_SEGMENT_SHIFT;
131*4882a593Smuzhiyun else
132*4882a593Smuzhiyun mkt_segment = (cell_value & OCOTP_CFG3_MKT_SEGMENT_MASK)
133*4882a593Smuzhiyun >> OCOTP_CFG3_MKT_SEGMENT_SHIFT;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun /*
136*4882a593Smuzhiyun * Early samples without fuses written report "0 0" which may NOT
137*4882a593Smuzhiyun * match any OPP defined in DT. So clamp to minimum OPP defined in
138*4882a593Smuzhiyun * DT to avoid warning for "no OPPs".
139*4882a593Smuzhiyun *
140*4882a593Smuzhiyun * Applies to i.MX8M series SoCs.
141*4882a593Smuzhiyun */
142*4882a593Smuzhiyun if (mkt_segment == 0 && speed_grade == 0) {
143*4882a593Smuzhiyun if (of_machine_is_compatible("fsl,imx8mm") ||
144*4882a593Smuzhiyun of_machine_is_compatible("fsl,imx8mq"))
145*4882a593Smuzhiyun speed_grade = 1;
146*4882a593Smuzhiyun if (of_machine_is_compatible("fsl,imx8mn") ||
147*4882a593Smuzhiyun of_machine_is_compatible("fsl,imx8mp"))
148*4882a593Smuzhiyun speed_grade = 0xb;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun supported_hw[0] = BIT(speed_grade);
152*4882a593Smuzhiyun supported_hw[1] = BIT(mkt_segment);
153*4882a593Smuzhiyun dev_info(&pdev->dev, "cpu speed grade %d mkt segment %d supported-hw %#x %#x\n",
154*4882a593Smuzhiyun speed_grade, mkt_segment, supported_hw[0], supported_hw[1]);
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun cpufreq_opp_table = dev_pm_opp_set_supported_hw(cpu_dev, supported_hw, 2);
157*4882a593Smuzhiyun if (IS_ERR(cpufreq_opp_table)) {
158*4882a593Smuzhiyun ret = PTR_ERR(cpufreq_opp_table);
159*4882a593Smuzhiyun dev_err(&pdev->dev, "Failed to set supported opp: %d\n", ret);
160*4882a593Smuzhiyun return ret;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun cpufreq_dt_pdev = platform_device_register_data(
164*4882a593Smuzhiyun &pdev->dev, "cpufreq-dt", -1, NULL, 0);
165*4882a593Smuzhiyun if (IS_ERR(cpufreq_dt_pdev)) {
166*4882a593Smuzhiyun dev_pm_opp_put_supported_hw(cpufreq_opp_table);
167*4882a593Smuzhiyun ret = PTR_ERR(cpufreq_dt_pdev);
168*4882a593Smuzhiyun dev_err(&pdev->dev, "Failed to register cpufreq-dt: %d\n", ret);
169*4882a593Smuzhiyun return ret;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun return 0;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun
imx_cpufreq_dt_remove(struct platform_device * pdev)175*4882a593Smuzhiyun static int imx_cpufreq_dt_remove(struct platform_device *pdev)
176*4882a593Smuzhiyun {
177*4882a593Smuzhiyun platform_device_unregister(cpufreq_dt_pdev);
178*4882a593Smuzhiyun if (!of_machine_is_compatible("fsl,imx7ulp"))
179*4882a593Smuzhiyun dev_pm_opp_put_supported_hw(cpufreq_opp_table);
180*4882a593Smuzhiyun else
181*4882a593Smuzhiyun clk_bulk_put(ARRAY_SIZE(imx7ulp_clks), imx7ulp_clks);
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun return 0;
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun static struct platform_driver imx_cpufreq_dt_driver = {
187*4882a593Smuzhiyun .probe = imx_cpufreq_dt_probe,
188*4882a593Smuzhiyun .remove = imx_cpufreq_dt_remove,
189*4882a593Smuzhiyun .driver = {
190*4882a593Smuzhiyun .name = "imx-cpufreq-dt",
191*4882a593Smuzhiyun },
192*4882a593Smuzhiyun };
193*4882a593Smuzhiyun module_platform_driver(imx_cpufreq_dt_driver);
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun MODULE_ALIAS("platform:imx-cpufreq-dt");
196*4882a593Smuzhiyun MODULE_DESCRIPTION("Freescale i.MX cpufreq speed grading driver");
197*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
198