summaryrefslogtreecommitdiffstats
path: root/drivers/opp
diff options
context:
space:
mode:
authorDave Gerlach <d-gerlach@ti.com>2017-12-14 23:25:28 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-12-17 13:17:44 -0500
commit9a835fa6e47f27b1ae71390b6f12efce7335aaac (patch)
treec5dd78bb910d24826a8e00a22ede865598a8ae34 /drivers/opp
parent212b7287ae6098337fffa9c0cd7e139dceb98125 (diff)
PM / OPP: Add ti-opp-supply driver
Introduce a ti-opp-supply driver that will use new multiple regulator support that is part of the OPP core This is needed on TI platforms like DRA7/AM57 in order to control both CPU regulator and Adaptive Body Bias (ABB) regulator. These regulators must be scaled in sequence during an OPP transition depending on whether or not the frequency is being scaled up or down. This driver also implements AVS Class0 for these parts by looking up the required values from registers in the SoC and programming adjusted optimal voltage values for each OPP. Signed-off-by: Dave Gerlach <d-gerlach@ti.com> Acked-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/opp')
-rw-r--r--drivers/opp/Makefile1
-rw-r--r--drivers/opp/ti-opp-supply.c425
2 files changed, 426 insertions, 0 deletions
diff --git a/drivers/opp/Makefile b/drivers/opp/Makefile
index e70ceb406fe9..6ce6aefacc81 100644
--- a/drivers/opp/Makefile
+++ b/drivers/opp/Makefile
@@ -2,3 +2,4 @@ ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
2obj-y += core.o cpu.o 2obj-y += core.o cpu.o
3obj-$(CONFIG_OF) += of.o 3obj-$(CONFIG_OF) += of.o
4obj-$(CONFIG_DEBUG_FS) += debugfs.o 4obj-$(CONFIG_DEBUG_FS) += debugfs.o
5obj-$(CONFIG_ARM_TI_CPUFREQ) += ti-opp-supply.o
diff --git a/drivers/opp/ti-opp-supply.c b/drivers/opp/ti-opp-supply.c
new file mode 100644
index 000000000000..44dae3e51aac
--- /dev/null
+++ b/drivers/opp/ti-opp-supply.c
@@ -0,0 +1,425 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://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 */
29struct 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 */
40struct 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
46static 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 */
55struct 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 */
73static 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
125 table = kzalloc(sizeof(*data->vdd_table) *
126 data->num_vdd_table, GFP_KERNEL);
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 }
163out:
164 iounmap(base);
165out_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 */
174static 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 */
191static 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
216static 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 */
276int ti_opp_supply_set_opp(struct dev_pm_set_opp_data *data)
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,
291 new_supply_vbb->u_volt);
292
293 /* Scaling up? Scale voltage before frequency */
294 if (freq > old_freq) {
295 ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg,
296 "vdd");
297 if (ret)
298 goto restore_voltage;
299
300 ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb");
301 if (ret)
302 goto restore_voltage;
303 }
304
305 /* Change frequency */
306 dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n",
307 __func__, old_freq, freq);
308
309 ret = clk_set_rate(clk, freq);
310 if (ret) {
311 dev_err(dev, "%s: failed to set clock rate: %d\n", __func__,
312 ret);
313 goto restore_voltage;
314 }
315
316 /* Scaling down? Scale voltage after frequency */
317 if (freq < old_freq) {
318 ret = _opp_set_voltage(dev, new_supply_vbb, 0, vbb_reg, "vbb");
319 if (ret)
320 goto restore_freq;
321
322 ret = _opp_set_voltage(dev, new_supply_vdd, vdd_uv, vdd_reg,
323 "vdd");
324 if (ret)
325 goto restore_freq;
326 }
327
328 return 0;
329
330restore_freq:
331 ret = clk_set_rate(clk, old_freq);
332 if (ret)
333 dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
334 __func__, old_freq);
335restore_voltage:
336 /* This shouldn't harm even if the voltages weren't updated earlier */
337 if (old_supply_vdd->u_volt) {
338 ret = _opp_set_voltage(dev, old_supply_vbb, 0, vbb_reg, "vbb");
339 if (ret)
340 return ret;
341
342 ret = _opp_set_voltage(dev, old_supply_vdd, 0, vdd_reg,
343 "vdd");
344 if (ret)
345 return ret;
346 }
347
348 return ret;
349}
350
351static const struct ti_opp_supply_of_data omap_generic_of_data = {
352};
353
354static const struct ti_opp_supply_of_data omap_omap5_of_data = {
355 .flags = OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE,
356 .efuse_voltage_mask = 0xFFF,
357 .efuse_voltage_uv = false,
358};
359
360static const struct ti_opp_supply_of_data omap_omap5core_of_data = {
361 .flags = OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE | OPPDM_HAS_NO_ABB,
362 .efuse_voltage_mask = 0xFFF,
363 .efuse_voltage_uv = false,
364};
365
366static const struct of_device_id ti_opp_supply_of_match[] = {
367 {.compatible = "ti,omap-opp-supply", .data = &omap_generic_of_data},
368 {.compatible = "ti,omap5-opp-supply", .data = &omap_omap5_of_data},
369 {.compatible = "ti,omap5-core-opp-supply",
370 .data = &omap_omap5core_of_data},
371 {},
372};
373MODULE_DEVICE_TABLE(of, ti_opp_supply_of_match);
374
375static int ti_opp_supply_probe(struct platform_device *pdev)
376{
377 struct device *dev = &pdev->dev;
378 struct device *cpu_dev = get_cpu_device(0);
379 const struct of_device_id *match;
380 const struct ti_opp_supply_of_data *of_data;
381 int ret = 0;
382
383 match = of_match_device(ti_opp_supply_of_match, dev);
384 if (!match) {
385 /* We do not expect this to happen */
386 dev_err(dev, "%s: Unable to match device\n", __func__);
387 return -ENODEV;
388 }
389 if (!match->data) {
390 /* Again, unlikely.. but mistakes do happen */
391 dev_err(dev, "%s: Bad data in match\n", __func__);
392 return -EINVAL;
393 }
394 of_data = match->data;
395
396 dev_set_drvdata(dev, (void *)of_data);
397
398 /* If we need optimized voltage */
399 if (of_data->flags & OPPDM_EFUSE_CLASS0_OPTIMIZED_VOLTAGE) {
400 ret = _store_optimized_voltages(dev, &opp_data);
401 if (ret)
402 return ret;
403 }
404
405 ret = PTR_ERR_OR_ZERO(dev_pm_opp_register_set_opp_helper(cpu_dev,
406 ti_opp_supply_set_opp));
407 if (ret)
408 _free_optimized_voltages(dev, &opp_data);
409
410 return ret;
411}
412
413static struct platform_driver ti_opp_supply_driver = {
414 .probe = ti_opp_supply_probe,
415 .driver = {
416 .name = "ti_opp_supply",
417 .owner = THIS_MODULE,
418 .of_match_table = of_match_ptr(ti_opp_supply_of_match),
419 },
420};
421module_platform_driver(ti_opp_supply_driver);
422
423MODULE_DESCRIPTION("Texas Instruments OMAP OPP Supply driver");
424MODULE_AUTHOR("Texas Instruments Inc.");
425MODULE_LICENSE("GPL v2");