xref: /openbmc/linux/drivers/cpufreq/ti-cpufreq.c (revision 6aa7de05)
1 /*
2  * TI CPUFreq/OPP hw-supported driver
3  *
4  * Copyright (C) 2016-2017 Texas Instruments, Inc.
5  *	 Dave Gerlach <d-gerlach@ti.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  */
16 
17 #include <linux/cpu.h>
18 #include <linux/io.h>
19 #include <linux/mfd/syscon.h>
20 #include <linux/init.h>
21 #include <linux/of.h>
22 #include <linux/of_platform.h>
23 #include <linux/pm_opp.h>
24 #include <linux/regmap.h>
25 #include <linux/slab.h>
26 
27 #define REVISION_MASK				0xF
28 #define REVISION_SHIFT				28
29 
30 #define AM33XX_800M_ARM_MPU_MAX_FREQ		0x1E2F
31 #define AM43XX_600M_ARM_MPU_MAX_FREQ		0xFFA
32 
33 #define DRA7_EFUSE_HAS_OD_MPU_OPP		11
34 #define DRA7_EFUSE_HAS_HIGH_MPU_OPP		15
35 #define DRA7_EFUSE_HAS_ALL_MPU_OPP		23
36 
37 #define DRA7_EFUSE_NOM_MPU_OPP			BIT(0)
38 #define DRA7_EFUSE_OD_MPU_OPP			BIT(1)
39 #define DRA7_EFUSE_HIGH_MPU_OPP			BIT(2)
40 
41 #define VERSION_COUNT				2
42 
43 struct ti_cpufreq_data;
44 
45 struct ti_cpufreq_soc_data {
46 	unsigned long (*efuse_xlate)(struct ti_cpufreq_data *opp_data,
47 				     unsigned long efuse);
48 	unsigned long efuse_fallback;
49 	unsigned long efuse_offset;
50 	unsigned long efuse_mask;
51 	unsigned long efuse_shift;
52 	unsigned long rev_offset;
53 };
54 
55 struct ti_cpufreq_data {
56 	struct device *cpu_dev;
57 	struct device_node *opp_node;
58 	struct regmap *syscon;
59 	const struct ti_cpufreq_soc_data *soc_data;
60 };
61 
62 static unsigned long amx3_efuse_xlate(struct ti_cpufreq_data *opp_data,
63 				      unsigned long efuse)
64 {
65 	if (!efuse)
66 		efuse = opp_data->soc_data->efuse_fallback;
67 	/* AM335x and AM437x use "OPP disable" bits, so invert */
68 	return ~efuse;
69 }
70 
71 static unsigned long dra7_efuse_xlate(struct ti_cpufreq_data *opp_data,
72 				      unsigned long efuse)
73 {
74 	unsigned long calculated_efuse = DRA7_EFUSE_NOM_MPU_OPP;
75 
76 	/*
77 	 * The efuse on dra7 and am57 parts contains a specific
78 	 * value indicating the highest available OPP.
79 	 */
80 
81 	switch (efuse) {
82 	case DRA7_EFUSE_HAS_ALL_MPU_OPP:
83 	case DRA7_EFUSE_HAS_HIGH_MPU_OPP:
84 		calculated_efuse |= DRA7_EFUSE_HIGH_MPU_OPP;
85 	case DRA7_EFUSE_HAS_OD_MPU_OPP:
86 		calculated_efuse |= DRA7_EFUSE_OD_MPU_OPP;
87 	}
88 
89 	return calculated_efuse;
90 }
91 
92 static struct ti_cpufreq_soc_data am3x_soc_data = {
93 	.efuse_xlate = amx3_efuse_xlate,
94 	.efuse_fallback = AM33XX_800M_ARM_MPU_MAX_FREQ,
95 	.efuse_offset = 0x07fc,
96 	.efuse_mask = 0x1fff,
97 	.rev_offset = 0x600,
98 };
99 
100 static struct ti_cpufreq_soc_data am4x_soc_data = {
101 	.efuse_xlate = amx3_efuse_xlate,
102 	.efuse_fallback = AM43XX_600M_ARM_MPU_MAX_FREQ,
103 	.efuse_offset = 0x0610,
104 	.efuse_mask = 0x3f,
105 	.rev_offset = 0x600,
106 };
107 
108 static struct ti_cpufreq_soc_data dra7_soc_data = {
109 	.efuse_xlate = dra7_efuse_xlate,
110 	.efuse_offset = 0x020c,
111 	.efuse_mask = 0xf80000,
112 	.efuse_shift = 19,
113 	.rev_offset = 0x204,
114 };
115 
116 /**
117  * ti_cpufreq_get_efuse() - Parse and return efuse value present on SoC
118  * @opp_data: pointer to ti_cpufreq_data context
119  * @efuse_value: Set to the value parsed from efuse
120  *
121  * Returns error code if efuse not read properly.
122  */
123 static int ti_cpufreq_get_efuse(struct ti_cpufreq_data *opp_data,
124 				u32 *efuse_value)
125 {
126 	struct device *dev = opp_data->cpu_dev;
127 	u32 efuse;
128 	int ret;
129 
130 	ret = regmap_read(opp_data->syscon, opp_data->soc_data->efuse_offset,
131 			  &efuse);
132 	if (ret) {
133 		dev_err(dev,
134 			"Failed to read the efuse value from syscon: %d\n",
135 			ret);
136 		return ret;
137 	}
138 
139 	efuse = (efuse & opp_data->soc_data->efuse_mask);
140 	efuse >>= opp_data->soc_data->efuse_shift;
141 
142 	*efuse_value = opp_data->soc_data->efuse_xlate(opp_data, efuse);
143 
144 	return 0;
145 }
146 
147 /**
148  * ti_cpufreq_get_rev() - Parse and return rev value present on SoC
149  * @opp_data: pointer to ti_cpufreq_data context
150  * @revision_value: Set to the value parsed from revision register
151  *
152  * Returns error code if revision not read properly.
153  */
154 static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data,
155 			      u32 *revision_value)
156 {
157 	struct device *dev = opp_data->cpu_dev;
158 	u32 revision;
159 	int ret;
160 
161 	ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset,
162 			  &revision);
163 	if (ret) {
164 		dev_err(dev,
165 			"Failed to read the revision number from syscon: %d\n",
166 			ret);
167 		return ret;
168 	}
169 
170 	*revision_value = BIT((revision >> REVISION_SHIFT) & REVISION_MASK);
171 
172 	return 0;
173 }
174 
175 static int ti_cpufreq_setup_syscon_register(struct ti_cpufreq_data *opp_data)
176 {
177 	struct device *dev = opp_data->cpu_dev;
178 	struct device_node *np = opp_data->opp_node;
179 
180 	opp_data->syscon = syscon_regmap_lookup_by_phandle(np,
181 							"syscon");
182 	if (IS_ERR(opp_data->syscon)) {
183 		dev_err(dev,
184 			"\"syscon\" is missing, cannot use OPPv2 table.\n");
185 		return PTR_ERR(opp_data->syscon);
186 	}
187 
188 	return 0;
189 }
190 
191 static const struct of_device_id ti_cpufreq_of_match[] = {
192 	{ .compatible = "ti,am33xx", .data = &am3x_soc_data, },
193 	{ .compatible = "ti,am43", .data = &am4x_soc_data, },
194 	{ .compatible = "ti,dra7", .data = &dra7_soc_data },
195 	{},
196 };
197 
198 static int ti_cpufreq_init(void)
199 {
200 	u32 version[VERSION_COUNT];
201 	struct device_node *np;
202 	const struct of_device_id *match;
203 	struct ti_cpufreq_data *opp_data;
204 	int ret;
205 
206 	np = of_find_node_by_path("/");
207 	match = of_match_node(ti_cpufreq_of_match, np);
208 	if (!match)
209 		return -ENODEV;
210 
211 	opp_data = kzalloc(sizeof(*opp_data), GFP_KERNEL);
212 	if (!opp_data)
213 		return -ENOMEM;
214 
215 	opp_data->soc_data = match->data;
216 
217 	opp_data->cpu_dev = get_cpu_device(0);
218 	if (!opp_data->cpu_dev) {
219 		pr_err("%s: Failed to get device for CPU0\n", __func__);
220 		return -ENODEV;
221 	}
222 
223 	opp_data->opp_node = dev_pm_opp_of_get_opp_desc_node(opp_data->cpu_dev);
224 	if (!opp_data->opp_node) {
225 		dev_info(opp_data->cpu_dev,
226 			 "OPP-v2 not supported, cpufreq-dt will attempt to use legacy tables.\n");
227 		goto register_cpufreq_dt;
228 	}
229 
230 	ret = ti_cpufreq_setup_syscon_register(opp_data);
231 	if (ret)
232 		goto fail_put_node;
233 
234 	/*
235 	 * OPPs determine whether or not they are supported based on
236 	 * two metrics:
237 	 *	0 - SoC Revision
238 	 *	1 - eFuse value
239 	 */
240 	ret = ti_cpufreq_get_rev(opp_data, &version[0]);
241 	if (ret)
242 		goto fail_put_node;
243 
244 	ret = ti_cpufreq_get_efuse(opp_data, &version[1]);
245 	if (ret)
246 		goto fail_put_node;
247 
248 	ret = PTR_ERR_OR_ZERO(dev_pm_opp_set_supported_hw(opp_data->cpu_dev,
249 							  version, VERSION_COUNT));
250 	if (ret) {
251 		dev_err(opp_data->cpu_dev,
252 			"Failed to set supported hardware\n");
253 		goto fail_put_node;
254 	}
255 
256 	of_node_put(opp_data->opp_node);
257 
258 register_cpufreq_dt:
259 	platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
260 
261 	return 0;
262 
263 fail_put_node:
264 	of_node_put(opp_data->opp_node);
265 
266 	return ret;
267 }
268 device_initcall(ti_cpufreq_init);
269