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