]>
Commit | Line | Data |
---|---|---|
9a835fa6 DG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/ | |
4 | * Nishanth Menon <[email protected]> | |
5 | * Dave Gerlach <[email protected]> | |
6 | * | |
7 | * TI OPP supply driver that provides override into the regulator control | |
8 | * for generic opp core to handle devices with ABB regulator and/or | |
9 | * SmartReflex Class0. | |
10 | */ | |
11 | #include <linux/clk.h> | |
12 | #include <linux/cpufreq.h> | |
13 | #include <linux/device.h> | |
14 | #include <linux/io.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/notifier.h> | |
17 | #include <linux/of_device.h> | |
18 | #include <linux/of.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/pm_opp.h> | |
21 | #include <linux/regulator/consumer.h> | |
22 | #include <linux/slab.h> | |
23 | ||
24 | /** | |
25 | * struct ti_opp_supply_optimum_voltage_table - optimized voltage table | |
26 | * @reference_uv: reference voltage (usually Nominal voltage) | |
27 | * @optimized_uv: Optimized voltage from efuse | |
28 | */ | |
29 | struct ti_opp_supply_optimum_voltage_table { | |
30 | unsigned int reference_uv; | |
31 | unsigned int optimized_uv; | |
32 | }; | |
33 | ||
34 | /** | |
35 | * struct ti_opp_supply_data - OMAP specific opp supply data | |
36 | * @vdd_table: Optimized voltage mapping table | |
37 | * @num_vdd_table: number of entries in vdd_table | |
38 | * @vdd_absolute_max_voltage_uv: absolute maximum voltage in UV for the supply | |
39 | */ | |
40 | struct ti_opp_supply_data { | |
41 | struct ti_opp_supply_optimum_voltage_table *vdd_table; | |
42 | u32 num_vdd_table; | |
43 | u32 vdd_absolute_max_voltage_uv; | |
44 | }; | |
45 | ||
46 | static struct ti_opp_supply_data opp_data; | |
47 | ||
48 | /** | |
49 | * struct ti_opp_supply_of_data - device tree match data | |
50 | * @flags: specific type of opp supply | |
51 | * @efuse_voltage_mask: mask required for efuse register representing voltage | |
52 | * @efuse_voltage_uv: Are the efuse entries in micro-volts? if not, assume | |
53 | * milli-volts. | |
54 | */ | |
55 | struct ti_opp_supply_of_data { | |
56 | #define OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE BIT(1) | |
57 | #define OPPDM_HAS_NO_ABB BIT(2) | |
58 | const u8 flags; | |
59 | const u32 efuse_voltage_mask; | |
60 | const bool efuse_voltage_uv; | |
61 | }; | |
62 | ||
63 | /** | |
64 | * _store_optimized_voltages() - store optimized voltages | |
65 | * @dev: ti opp supply device for which we need to store info | |
66 | * @data: data specific to the device | |
67 | * | |
68 | * Picks up efuse based optimized voltages for VDD unique per device and | |
69 | * stores it in internal data structure for use during transition requests. | |
70 | * | |
71 | * Return: If successful, 0, else appropriate error value. | |
72 | */ | |
73 | static int _store_optimized_voltages(struct device *dev, | |
74 | struct ti_opp_supply_data *data) | |
75 | { | |
76 | void __iomem *base; | |
77 | struct property *prop; | |
78 | struct resource *res; | |
79 | const __be32 *val; | |
80 | int proplen, i; | |
81 | int ret = 0; | |
82 | struct ti_opp_supply_optimum_voltage_table *table; | |
83 | const struct ti_opp_supply_of_data *of_data = dev_get_drvdata(dev); | |
84 | ||
85 | /* pick up Efuse based voltages */ | |
86 | res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 0); | |
87 | if (!res) { | |
88 | dev_err(dev, "Unable to get IO resource\n"); | |
89 | ret = -ENODEV; | |
90 | goto out_map; | |
91 | } | |
92 | ||
93 | base = ioremap_nocache(res->start, resource_size(res)); | |
94 | if (!base) { | |
95 | dev_err(dev, "Unable to map Efuse registers\n"); | |
96 | ret = -ENOMEM; | |
97 | goto out_map; | |
98 | } | |
99 | ||
100 | /* Fetch efuse-settings. */ | |
101 | prop = of_find_property(dev->of_node, "ti,efuse-settings", NULL); | |
102 | if (!prop) { | |
103 | dev_err(dev, "No 'ti,efuse-settings' property found\n"); | |
104 | ret = -EINVAL; | |
105 | goto out; | |
106 | } | |
107 | ||
108 | proplen = prop->length / sizeof(int); | |
109 | data->num_vdd_table = proplen / 2; | |
110 | /* Verify for corrupted OPP entries in dt */ | |
111 | if (data->num_vdd_table * 2 * sizeof(int) != prop->length) { | |
112 | dev_err(dev, "Invalid 'ti,efuse-settings'\n"); | |
113 | ret = -EINVAL; | |
114 | goto out; | |
115 | } | |
116 | ||
117 | ret = of_property_read_u32(dev->of_node, "ti,absolute-max-voltage-uv", | |
118 | &data->vdd_absolute_max_voltage_uv); | |
119 | if (ret) { | |
120 | dev_err(dev, "ti,absolute-max-voltage-uv is missing\n"); | |
121 | ret = -EINVAL; | |
122 | goto out; | |
123 | } | |
124 | ||
6396bb22 KC |
125 | table = kcalloc(data->num_vdd_table, sizeof(*data->vdd_table), |
126 | GFP_KERNEL); | |
9a835fa6 DG |
127 | if (!table) { |
128 | ret = -ENOMEM; | |
129 | goto out; | |
130 | } | |
131 | data->vdd_table = table; | |
132 | ||
133 | val = prop->value; | |
134 | for (i = 0; i < data->num_vdd_table; i++, table++) { | |
135 | u32 efuse_offset; | |
136 | u32 tmp; | |
137 | ||
138 | table->reference_uv = be32_to_cpup(val++); | |
139 | efuse_offset = be32_to_cpup(val++); | |
140 | ||
141 | tmp = readl(base + efuse_offset); | |
142 | tmp &= of_data->efuse_voltage_mask; | |
143 | tmp >>= __ffs(of_data->efuse_voltage_mask); | |
144 | ||
145 | table->optimized_uv = of_data->efuse_voltage_uv ? tmp : | |
146 | tmp * 1000; | |
147 | ||
148 | dev_dbg(dev, "[%d] efuse=0x%08x volt_table=%d vset=%d\n", | |
149 | i, efuse_offset, table->reference_uv, | |
150 | table->optimized_uv); | |
151 | ||
152 | /* | |
153 | * Some older samples might not have optimized efuse | |
154 | * Use reference voltage for those - just add debug message | |
155 | * for them. | |
156 | */ | |
157 | if (!table->optimized_uv) { | |
158 | dev_dbg(dev, "[%d] efuse=0x%08x volt_table=%d:vset0\n", | |
159 | i, efuse_offset, table->reference_uv); | |
160 | table->optimized_uv = table->reference_uv; | |
161 | } | |
162 | } | |
163 | out: | |
164 | iounmap(base); | |
165 | out_map: | |
166 | return ret; | |
167 | } | |
168 | ||
169 | /** | |
170 | * _free_optimized_voltages() - free resources for optvoltages | |
171 | * @dev: device for which we need to free info | |
172 | * @data: data specific to the device | |
173 | */ | |
174 | static void _free_optimized_voltages(struct device *dev, | |
175 | struct ti_opp_supply_data *data) | |
176 | { | |
177 | kfree(data->vdd_table); | |
178 | data->vdd_table = NULL; | |
179 | data->num_vdd_table = 0; | |
180 | } | |
181 | ||
182 | /** | |
183 | * _get_optimal_vdd_voltage() - Finds optimal voltage for the supply | |
184 | * @dev: device for which we need to find info | |
185 | * @data: data specific to the device | |
186 | * @reference_uv: reference voltage (OPP voltage) for which we need value | |
187 | * | |
188 | * Return: if a match is found, return optimized voltage, else return | |
189 | * reference_uv, also return reference_uv if no optimization is needed. | |
190 | */ | |
191 | static int _get_optimal_vdd_voltage(struct device *dev, | |
192 | struct ti_opp_supply_data *data, | |
193 | int reference_uv) | |
194 | { | |
195 | int i; | |
196 | struct ti_opp_supply_optimum_voltage_table *table; | |
197 | ||
198 | if (!data->num_vdd_table) | |
199 | return reference_uv; | |
200 | ||
201 | table = data->vdd_table; | |
202 | if (!table) | |
203 | return -EINVAL; | |
204 | ||
205 | /* Find a exact match - this list is usually very small */ | |
206 | for (i = 0; i < data->num_vdd_table; i++, table++) | |
207 | if (table->reference_uv == reference_uv) | |
208 | return table->optimized_uv; | |
209 | ||
210 | /* IF things are screwed up, we'd make a mess on console.. ratelimit */ | |
211 | dev_err_ratelimited(dev, "%s: Failed optimized voltage match for %d\n", | |
212 | __func__, reference_uv); | |
213 | return reference_uv; | |
214 | } | |
215 | ||
216 | static int _opp_set_voltage(struct device *dev, | |
217 | struct dev_pm_opp_supply *supply, | |
218 | int new_target_uv, struct regulator *reg, | |
219 | char *reg_name) | |
220 | { | |
221 | int ret; | |
222 | unsigned long vdd_uv, uv_max; | |
223 | ||
224 | if (new_target_uv) | |
225 | vdd_uv = new_target_uv; | |
226 | else | |
227 | vdd_uv = supply->u_volt; | |
228 | ||
229 | /* | |
230 | * If we do have an absolute max voltage specified, then we should | |
231 | * use that voltage instead to allow for cases where the voltage rails | |
232 | * are ganged (example if we set the max for an opp as 1.12v, and | |
233 | * the absolute max is 1.5v, for another rail to get 1.25v, it cannot | |
234 | * be achieved if the regulator is constrainted to max of 1.12v, even | |
235 | * if it can function at 1.25v | |
236 | */ | |
237 | if (opp_data.vdd_absolute_max_voltage_uv) | |
238 | uv_max = opp_data.vdd_absolute_max_voltage_uv; | |
239 | else | |
240 | uv_max = supply->u_volt_max; | |
241 | ||
242 | if (vdd_uv > uv_max || | |
243 | vdd_uv < supply->u_volt_min || | |
244 | supply->u_volt_min > uv_max) { | |
245 | dev_warn(dev, | |
246 | "Invalid range voltages [Min:%lu target:%lu Max:%lu]\n", | |
247 | supply->u_volt_min, vdd_uv, uv_max); | |
248 | return -EINVAL; | |
249 | } | |
250 | ||
251 | dev_dbg(dev, "%s scaling to %luuV[min %luuV max %luuV]\n", reg_name, | |
252 | vdd_uv, supply->u_volt_min, | |
253 | uv_max); | |
254 | ||
255 | ret = regulator_set_voltage_triplet(reg, | |
256 | supply->u_volt_min, | |
257 | vdd_uv, | |
258 | uv_max); | |
259 | if (ret) { | |
260 | dev_err(dev, "%s failed for %luuV[min %luuV max %luuV]\n", | |
261 | reg_name, vdd_uv, supply->u_volt_min, | |
262 | uv_max); | |
263 | return ret; | |
264 | } | |
265 | ||
266 | return 0; | |
267 | } | |
268 | ||
269 | /** | |
270 | * ti_opp_supply_set_opp() - do the opp supply transition | |
271 | * @data: information on regulators and new and old opps provided by | |
272 | * opp core to use in transition | |
273 | * | |
274 | * Return: If successful, 0, else appropriate error value. | |
275 | */ | |
3eff5f67 | 276 | static int ti_opp_supply_set_opp(struct dev_pm_set_opp_data *data) |
9a835fa6 DG |
277 | { |
278 | struct dev_pm_opp_supply *old_supply_vdd = &data->old_opp.supplies[0]; | |
279 | struct dev_pm_opp_supply *old_supply_vbb = &data->old_opp.supplies[1]; | |
280 | struct dev_pm_opp_supply *new_supply_vdd = &data->new_opp.supplies[0]; | |
281 | struct dev_pm_opp_supply *new_supply_vbb = &data->new_opp.supplies[1]; | |
282 | struct device *dev = data->dev; | |
283 | unsigned long old_freq = data->old_opp.rate, freq = data->new_opp.rate; | |
284 | struct clk *clk = data->clk; | |
285 | struct regulator *vdd_reg = data->regulators[0]; | |
286 | struct regulator *vbb_reg = data->regulators[1]; | |
287 | int vdd_uv; | |
288 | int ret; | |
289 | ||
290 | vdd_uv = _get_optimal_vdd_voltage(dev, &opp_data, | |
622fecbc | 291 | new_supply_vdd->u_volt); |
9a835fa6 | 292 | |
ba038546 K |
293 | if (new_supply_vdd->u_volt_min < vdd_uv) |
294 | new_supply_vdd->u_volt_min = vdd_uv; | |
295 | ||
9a835fa6 DG |
296 | /* Scaling up? Scale voltage before frequency */ |
297 | if (freq > old_freq) { | |
298 | ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg, | |
299 | "vdd"); | |
300 | if (ret) | |
301 | goto restore_voltage; | |
302 | ||
303 | ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb"); | |
304 | if (ret) | |
305 | goto restore_voltage; | |
306 | } | |
307 | ||
308 | /* Change frequency */ | |
309 | dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", | |
310 | __func__, old_freq, freq); | |
311 | ||
312 | ret = clk_set_rate(clk, freq); | |
313 | if (ret) { | |
314 | dev_err(dev, "%s: failed to set clock rate: %d\n", __func__, | |
315 | ret); | |
316 | goto restore_voltage; | |
317 | } | |
318 | ||
319 | /* Scaling down? Scale voltage after frequency */ | |
320 | if (freq < old_freq) { | |
321 | ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb"); | |
322 | if (ret) | |
323 | goto restore_freq; | |
324 | ||
325 | ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg, | |
326 | "vdd"); | |
327 | if (ret) | |
328 | goto restore_freq; | |
329 | } | |
330 | ||
331 | return 0; | |
332 | ||
333 | restore_freq: | |
334 | ret = clk_set_rate(clk, old_freq); | |
335 | if (ret) | |
336 | dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n", | |
337 | __func__, old_freq); | |
338 | restore_voltage: | |
339 | /* This shouldn't harm even if the voltages weren't updated earlier */ | |
340 | if (old_supply_vdd->u_volt) { | |
341 | ret = _opp_set_voltage(dev, old_supply_vbb, 0, vbb_reg, "vbb"); | |
342 | if (ret) | |
343 | return ret; | |
344 | ||
345 | ret = _opp_set_voltage(dev, old_supply_vdd, 0, vdd_reg, | |
346 | "vdd"); | |
347 | if (ret) | |
348 | return ret; | |
349 | } | |
350 | ||
351 | return ret; | |
352 | } | |
353 | ||
354 | static const struct ti_opp_supply_of_data omap_generic_of_data = { | |
355 | }; | |
356 | ||
357 | static const struct ti_opp_supply_of_data omap_omap5_of_data = { | |
358 | .flags = OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE, | |
359 | .efuse_voltage_mask = 0xFFF, | |
360 | .efuse_voltage_uv = false, | |
361 | }; | |
362 | ||
363 | static const struct ti_opp_supply_of_data omap_omap5core_of_data = { | |
364 | .flags = OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE | OPPDM_HAS_NO_ABB, | |
365 | .efuse_voltage_mask = 0xFFF, | |
366 | .efuse_voltage_uv = false, | |
367 | }; | |
368 | ||
369 | static const struct of_device_id ti_opp_supply_of_match[] = { | |
370 | {.compatible = "ti,omap-opp-supply", .data = &omap_generic_of_data}, | |
371 | {.compatible = "ti,omap5-opp-supply", .data = &omap_omap5_of_data}, | |
372 | {.compatible = "ti,omap5-core-opp-supply", | |
373 | .data = &omap_omap5core_of_data}, | |
374 | {}, | |
375 | }; | |
376 | MODULE_DEVICE_TABLE(of, ti_opp_supply_of_match); | |
377 | ||
378 | static int ti_opp_supply_probe(struct platform_device *pdev) | |
379 | { | |
380 | struct device *dev = &pdev->dev; | |
381 | struct device *cpu_dev = get_cpu_device(0); | |
382 | const struct of_device_id *match; | |
383 | const struct ti_opp_supply_of_data *of_data; | |
384 | int ret = 0; | |
385 | ||
386 | match = of_match_device(ti_opp_supply_of_match, dev); | |
387 | if (!match) { | |
388 | /* We do not expect this to happen */ | |
389 | dev_err(dev, "%s: Unable to match device\n", __func__); | |
390 | return -ENODEV; | |
391 | } | |
392 | if (!match->data) { | |
393 | /* Again, unlikely.. but mistakes do happen */ | |
394 | dev_err(dev, "%s: Bad data in match\n", __func__); | |
395 | return -EINVAL; | |
396 | } | |
397 | of_data = match->data; | |
398 | ||
399 | dev_set_drvdata(dev, (void *)of_data); | |
400 | ||
401 | /* If we need optimized voltage */ | |
402 | if (of_data->flags & OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE) { | |
403 | ret = _store_optimized_voltages(dev, &opp_data); | |
404 | if (ret) | |
405 | return ret; | |
406 | } | |
407 | ||
408 | ret = PTR_ERR_OR_ZERO(dev_pm_opp_register_set_opp_helper(cpu_dev, | |
409 | ti_opp_supply_set_opp)); | |
410 | if (ret) | |
411 | _free_optimized_voltages(dev, &opp_data); | |
412 | ||
413 | return ret; | |
414 | } | |
415 | ||
416 | static struct platform_driver ti_opp_supply_driver = { | |
417 | .probe = ti_opp_supply_probe, | |
418 | .driver = { | |
419 | .name = "ti_opp_supply", | |
9a835fa6 DG |
420 | .of_match_table = of_match_ptr(ti_opp_supply_of_match), |
421 | }, | |
422 | }; | |
423 | module_platform_driver(ti_opp_supply_driver); | |
424 | ||
425 | MODULE_DESCRIPTION("Texas Instruments OMAP OPP Supply driver"); | |
426 | MODULE_AUTHOR("Texas Instruments Inc."); | |
427 | MODULE_LICENSE("GPL v2"); |