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