xref: /OK3568_Linux_fs/kernel/drivers/cpufreq/imx-cpufreq-dt.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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