aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2009-09-22 11:47:11 -0400
committerLiam Girdwood <lrg@slimlogic.co.uk>2009-12-17 05:27:22 -0500
commite24a04c44cf312e88b50006a91ad7ffc1c0d97a5 (patch)
treea8f4b9c2f5caaed9646a1118b8d9cd84de134439
parent27f37e4bfed803be338dcc78845d4a67eefb40a0 (diff)
regulator: Implement WM831x BuckWise DC-DC convertor DVS support
The BuckWise DC-DC convertors in WM831x devices support switching to a second output voltage using the logic level on one of the device pins. This is intended to allow rapid voltage switching for uses like cpufreq, replacing the I2C or SPI write used to configure the voltage of the regulator with a much faster GPIO status change. This is implemented by keeping the DVS voltage configured as the maximum voltage permitted for the regulator. If a request is made for the maximum voltage then the GPIO is used to switch to the DVS voltage, otherwise the normal ON voltage is updated and used. This follows the idiom used by most cpufreq drivers, which drop the minimum voltage as the core frequency is dropped but use a constant maximum - raising the voltage should normally be fast, but lowering it may be slower. Configuration of the DVS MFP on the device should be done externally, for example via OTP. Support is present in the hardware for monitoring the status of the transition using a second GPIO. This is not currently implemented but platform data is provided for it - the driver currently assumes that the device will be configured to transition immediately - but platform data is provided to reduce merge issues once it is. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Samuel Ortiz <sameo@linux.intel.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
-rw-r--r--drivers/regulator/wm831x-dcdc.c207
-rw-r--r--include/linux/mfd/wm831x/pdata.h17
2 files changed, 206 insertions, 18 deletions
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
index 2eefc1a0cf0..0a6577577e8 100644
--- a/drivers/regulator/wm831x-dcdc.c
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -19,6 +19,8 @@
19#include <linux/i2c.h> 19#include <linux/i2c.h>
20#include <linux/platform_device.h> 20#include <linux/platform_device.h>
21#include <linux/regulator/driver.h> 21#include <linux/regulator/driver.h>
22#include <linux/regulator/machine.h>
23#include <linux/gpio.h>
22 24
23#include <linux/mfd/wm831x/core.h> 25#include <linux/mfd/wm831x/core.h>
24#include <linux/mfd/wm831x/regulator.h> 26#include <linux/mfd/wm831x/regulator.h>
@@ -39,6 +41,7 @@
39#define WM831X_DCDC_CONTROL_2 1 41#define WM831X_DCDC_CONTROL_2 1
40#define WM831X_DCDC_ON_CONFIG 2 42#define WM831X_DCDC_ON_CONFIG 2
41#define WM831X_DCDC_SLEEP_CONTROL 3 43#define WM831X_DCDC_SLEEP_CONTROL 3
44#define WM831X_DCDC_DVS_CONTROL 4
42 45
43/* 46/*
44 * Shared 47 * Shared
@@ -50,6 +53,10 @@ struct wm831x_dcdc {
50 int base; 53 int base;
51 struct wm831x *wm831x; 54 struct wm831x *wm831x;
52 struct regulator_dev *regulator; 55 struct regulator_dev *regulator;
56 int dvs_gpio;
57 int dvs_gpio_state;
58 int on_vsel;
59 int dvs_vsel;
53}; 60};
54 61
55static int wm831x_dcdc_is_enabled(struct regulator_dev *rdev) 62static int wm831x_dcdc_is_enabled(struct regulator_dev *rdev)
@@ -240,11 +247,9 @@ static int wm831x_buckv_list_voltage(struct regulator_dev *rdev,
240 return -EINVAL; 247 return -EINVAL;
241} 248}
242 249
243static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg, 250static int wm831x_buckv_select_min_voltage(struct regulator_dev *rdev,
244 int min_uV, int max_uV) 251 int min_uV, int max_uV)
245{ 252{
246 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
247 struct wm831x *wm831x = dcdc->wm831x;
248 u16 vsel; 253 u16 vsel;
249 254
250 if (min_uV < 600000) 255 if (min_uV < 600000)
@@ -257,39 +262,126 @@ static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg,
257 if (wm831x_buckv_list_voltage(rdev, vsel) > max_uV) 262 if (wm831x_buckv_list_voltage(rdev, vsel) > max_uV)
258 return -EINVAL; 263 return -EINVAL;
259 264
260 return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_VSEL_MASK, vsel); 265 return vsel;
266}
267
268static int wm831x_buckv_select_max_voltage(struct regulator_dev *rdev,
269 int min_uV, int max_uV)
270{
271 u16 vsel;
272
273 if (max_uV < 600000 || max_uV > 1800000)
274 return -EINVAL;
275
276 vsel = ((max_uV - 600000) / 12500) + 8;
277
278 if (wm831x_buckv_list_voltage(rdev, vsel) < min_uV ||
279 wm831x_buckv_list_voltage(rdev, vsel) < max_uV)
280 return -EINVAL;
281
282 return vsel;
283}
284
285static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state)
286{
287 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
288
289 if (state == dcdc->dvs_gpio_state)
290 return 0;
291
292 dcdc->dvs_gpio_state = state;
293 gpio_set_value(dcdc->dvs_gpio, state);
294
295 /* Should wait for DVS state change to be asserted if we have
296 * a GPIO for it, for now assume the device is configured
297 * for the fastest possible transition.
298 */
299
300 return 0;
261} 301}
262 302
263static int wm831x_buckv_set_voltage(struct regulator_dev *rdev, 303static int wm831x_buckv_set_voltage(struct regulator_dev *rdev,
264 int min_uV, int max_uV) 304 int min_uV, int max_uV)
265{ 305{
266 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 306 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
267 u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG; 307 struct wm831x *wm831x = dcdc->wm831x;
308 int on_reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
309 int dvs_reg = dcdc->base + WM831X_DCDC_DVS_CONTROL;
310 int vsel, ret;
311
312 vsel = wm831x_buckv_select_min_voltage(rdev, min_uV, max_uV);
313 if (vsel < 0)
314 return vsel;
315
316 /* If this value is already set then do a GPIO update if we can */
317 if (dcdc->dvs_gpio && dcdc->on_vsel == vsel)
318 return wm831x_buckv_set_dvs(rdev, 0);
319
320 if (dcdc->dvs_gpio && dcdc->dvs_vsel == vsel)
321 return wm831x_buckv_set_dvs(rdev, 1);
322
323 /* Always set the ON status to the minimum voltage */
324 ret = wm831x_set_bits(wm831x, on_reg, WM831X_DC1_ON_VSEL_MASK, vsel);
325 if (ret < 0)
326 return ret;
327 dcdc->on_vsel = vsel;
328
329 if (!dcdc->dvs_gpio)
330 return ret;
331
332 /* Kick the voltage transition now */
333 ret = wm831x_buckv_set_dvs(rdev, 0);
334 if (ret < 0)
335 return ret;
336
337 /* Set the high voltage as the DVS voltage. This is optimised
338 * for CPUfreq usage, most processors will keep the maximum
339 * voltage constant and lower the minimum with the frequency. */
340 vsel = wm831x_buckv_select_max_voltage(rdev, min_uV, max_uV);
341 if (vsel < 0) {
342 /* This should never happen - at worst the same vsel
343 * should be chosen */
344 WARN_ON(vsel < 0);
345 return 0;
346 }
347
348 /* Don't bother if it's the same VSEL we're already using */
349 if (vsel == dcdc->on_vsel)
350 return 0;
268 351
269 return wm831x_buckv_set_voltage_int(rdev, reg, min_uV, max_uV); 352 ret = wm831x_set_bits(wm831x, dvs_reg, WM831X_DC1_DVS_VSEL_MASK, vsel);
353 if (ret == 0)
354 dcdc->dvs_vsel = vsel;
355 else
356 dev_warn(wm831x->dev, "Failed to set DCDC DVS VSEL: %d\n",
357 ret);
358
359 return 0;
270} 360}
271 361
272static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev, 362static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev,
273 int uV) 363 int uV)
274{ 364{
275 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 365 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
366 struct wm831x *wm831x = dcdc->wm831x;
276 u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL; 367 u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
368 int vsel;
369
370 vsel = wm831x_buckv_select_min_voltage(rdev, uV, uV);
371 if (vsel < 0)
372 return vsel;
277 373
278 return wm831x_buckv_set_voltage_int(rdev, reg, uV, uV); 374 return wm831x_set_bits(wm831x, reg, WM831X_DC1_SLP_VSEL_MASK, vsel);
279} 375}
280 376
281static int wm831x_buckv_get_voltage(struct regulator_dev *rdev) 377static int wm831x_buckv_get_voltage(struct regulator_dev *rdev)
282{ 378{
283 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); 379 struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
284 struct wm831x *wm831x = dcdc->wm831x;
285 u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
286 int val;
287 380
288 val = wm831x_reg_read(wm831x, reg); 381 if (dcdc->dvs_gpio && dcdc->dvs_gpio_state)
289 if (val < 0) 382 return wm831x_buckv_list_voltage(rdev, dcdc->dvs_vsel);
290 return val; 383 else
291 384 return wm831x_buckv_list_voltage(rdev, dcdc->on_vsel);
292 return wm831x_buckv_list_voltage(rdev, val & WM831X_DC1_ON_VSEL_MASK);
293} 385}
294 386
295/* Current limit options */ 387/* Current limit options */
@@ -346,6 +438,64 @@ static struct regulator_ops wm831x_buckv_ops = {
346 .set_suspend_mode = wm831x_dcdc_set_suspend_mode, 438 .set_suspend_mode = wm831x_dcdc_set_suspend_mode,
347}; 439};
348 440
441/*
442 * Set up DVS control. We just log errors since we can still run
443 * (with reduced performance) if we fail.
444 */
445static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
446 struct wm831x_buckv_pdata *pdata)
447{
448 struct wm831x *wm831x = dcdc->wm831x;
449 int ret;
450 u16 ctrl;
451
452 if (!pdata || !pdata->dvs_gpio)
453 return;
454
455 switch (pdata->dvs_control_src) {
456 case 1:
457 ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT;
458 break;
459 case 2:
460 ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT;
461 break;
462 default:
463 dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n",
464 pdata->dvs_control_src, dcdc->name);
465 return;
466 }
467
468 ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL,
469 WM831X_DC1_DVS_SRC_MASK, ctrl);
470 if (ret < 0) {
471 dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n",
472 dcdc->name, ret);
473 return;
474 }
475
476 ret = gpio_request(pdata->dvs_gpio, "DCDC DVS");
477 if (ret < 0) {
478 dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n",
479 dcdc->name, ret);
480 return;
481 }
482
483 /* gpiolib won't let us read the GPIO status so pick the higher
484 * of the two existing voltages so we take it as platform data.
485 */
486 dcdc->dvs_gpio_state = pdata->dvs_init_state;
487
488 ret = gpio_direction_output(pdata->dvs_gpio, dcdc->dvs_gpio_state);
489 if (ret < 0) {
490 dev_err(wm831x->dev, "Failed to enable %s DVS GPIO: %d\n",
491 dcdc->name, ret);
492 gpio_free(pdata->dvs_gpio);
493 return;
494 }
495
496 dcdc->dvs_gpio = pdata->dvs_gpio;
497}
498
349static __devinit int wm831x_buckv_probe(struct platform_device *pdev) 499static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
350{ 500{
351 struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); 501 struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
@@ -384,6 +534,23 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
384 dcdc->desc.ops = &wm831x_buckv_ops; 534 dcdc->desc.ops = &wm831x_buckv_ops;
385 dcdc->desc.owner = THIS_MODULE; 535 dcdc->desc.owner = THIS_MODULE;
386 536
537 ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG);
538 if (ret < 0) {
539 dev_err(wm831x->dev, "Failed to read ON VSEL: %d\n", ret);
540 goto err;
541 }
542 dcdc->on_vsel = ret & WM831X_DC1_ON_VSEL_MASK;
543
544 ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG);
545 if (ret < 0) {
546 dev_err(wm831x->dev, "Failed to read DVS VSEL: %d\n", ret);
547 goto err;
548 }
549 dcdc->dvs_vsel = ret & WM831X_DC1_DVS_VSEL_MASK;
550
551 if (pdata->dcdc[id])
552 wm831x_buckv_dvs_init(dcdc, pdata->dcdc[id]->driver_data);
553
387 dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev, 554 dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev,
388 pdata->dcdc[id], dcdc); 555 pdata->dcdc[id], dcdc);
389 if (IS_ERR(dcdc->regulator)) { 556 if (IS_ERR(dcdc->regulator)) {
@@ -422,6 +589,8 @@ err_uv:
422err_regulator: 589err_regulator:
423 regulator_unregister(dcdc->regulator); 590 regulator_unregister(dcdc->regulator);
424err: 591err:
592 if (dcdc->dvs_gpio)
593 gpio_free(dcdc->dvs_gpio);
425 kfree(dcdc); 594 kfree(dcdc);
426 return ret; 595 return ret;
427} 596}
@@ -434,6 +603,8 @@ static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
434 wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc); 603 wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc);
435 wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); 604 wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
436 regulator_unregister(dcdc->regulator); 605 regulator_unregister(dcdc->regulator);
606 if (dcdc->dvs_gpio)
607 gpio_free(dcdc->dvs_gpio);
437 kfree(dcdc); 608 kfree(dcdc);
438 609
439 return 0; 610 return 0;
diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h
index 415c228743d..fd322aca33b 100644
--- a/include/linux/mfd/wm831x/pdata.h
+++ b/include/linux/mfd/wm831x/pdata.h
@@ -41,6 +41,23 @@ struct wm831x_battery_pdata {
41 int timeout; /** Charge cycle timeout, in minutes */ 41 int timeout; /** Charge cycle timeout, in minutes */
42}; 42};
43 43
44/**
45 * Configuration for the WM831x DC-DC BuckWise convertors. This
46 * should be passed as driver_data in the regulator_init_data.
47 *
48 * Currently all the configuration is for the fast DVS switching
49 * support of the devices. This allows MFPs on the device to be
50 * configured as an input to switch between two output voltages,
51 * allowing voltage transitions without the expense of an access over
52 * I2C or SPI buses.
53 */
54struct wm831x_buckv_pdata {
55 int dvs_gpio; /** CPU GPIO to use for DVS switching */
56 int dvs_control_src; /** Hardware DVS source to use (1 or 2) */
57 int dvs_init_state; /** DVS state to expect on startup */
58 int dvs_state_gpio; /** CPU GPIO to use for monitoring status */
59};
60
44/* Sources for status LED configuration. Values are register values 61/* Sources for status LED configuration. Values are register values
45 * plus 1 to allow for a zero default for preserve. 62 * plus 1 to allow for a zero default for preserve.
46 */ 63 */