linux/drivers/regulator/wm8400-regulator.c
<<
>>
Prefs
   1/*
   2 * Regulator support for WM8400
   3 *
   4 * Copyright 2008 Wolfson Microelectronics PLC.
   5 *
   6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License as
  10 * published by the Free Software Foundation; either version 2 of the
  11 * License, or (at your option) any later version.
  12 *
  13 */
  14
  15#include <linux/bug.h>
  16#include <linux/err.h>
  17#include <linux/kernel.h>
  18#include <linux/regulator/driver.h>
  19#include <linux/mfd/wm8400-private.h>
  20
  21static int wm8400_ldo_is_enabled(struct regulator_dev *dev)
  22{
  23        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
  24        u16 val;
  25
  26        val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
  27        return (val & WM8400_LDO1_ENA) != 0;
  28}
  29
  30static int wm8400_ldo_enable(struct regulator_dev *dev)
  31{
  32        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
  33
  34        return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
  35                               WM8400_LDO1_ENA, WM8400_LDO1_ENA);
  36}
  37
  38static int wm8400_ldo_disable(struct regulator_dev *dev)
  39{
  40        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
  41
  42        return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
  43                               WM8400_LDO1_ENA, 0);
  44}
  45
  46static int wm8400_ldo_list_voltage(struct regulator_dev *dev,
  47                                   unsigned selector)
  48{
  49        if (selector > WM8400_LDO1_VSEL_MASK)
  50                return -EINVAL;
  51
  52        if (selector < 15)
  53                return 900000 + (selector * 50000);
  54        else
  55                return 1600000 + ((selector - 14) * 100000);
  56}
  57
  58static int wm8400_ldo_get_voltage(struct regulator_dev *dev)
  59{
  60        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
  61        u16 val;
  62
  63        val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev));
  64        val &= WM8400_LDO1_VSEL_MASK;
  65
  66        return wm8400_ldo_list_voltage(dev, val);
  67}
  68
  69static int wm8400_ldo_set_voltage(struct regulator_dev *dev,
  70                                  int min_uV, int max_uV)
  71{
  72        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
  73        u16 val;
  74
  75        if (min_uV < 900000 || min_uV > 3300000)
  76                return -EINVAL;
  77
  78        if (min_uV < 1700000) {
  79                /* Steps of 50mV from 900mV;  */
  80                val = (min_uV - 850001) / 50000;
  81
  82                if ((val * 50000) + 900000 > max_uV)
  83                        return -EINVAL;
  84                BUG_ON((val * 50000) + 900000 < min_uV);
  85        } else {
  86                /* Steps of 100mV from 1700mV */
  87                val = ((min_uV - 1600001) / 100000);
  88
  89                if ((val * 100000) + 1700000 > max_uV)
  90                        return -EINVAL;
  91                BUG_ON((val * 100000) + 1700000 < min_uV);
  92
  93                val += 0xf;
  94        }
  95
  96        return wm8400_set_bits(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev),
  97                               WM8400_LDO1_VSEL_MASK, val);
  98}
  99
 100static struct regulator_ops wm8400_ldo_ops = {
 101        .is_enabled = wm8400_ldo_is_enabled,
 102        .enable = wm8400_ldo_enable,
 103        .disable = wm8400_ldo_disable,
 104        .list_voltage = wm8400_ldo_list_voltage,
 105        .get_voltage = wm8400_ldo_get_voltage,
 106        .set_voltage = wm8400_ldo_set_voltage,
 107};
 108
 109static int wm8400_dcdc_is_enabled(struct regulator_dev *dev)
 110{
 111        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
 112        int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
 113        u16 val;
 114
 115        val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
 116        return (val & WM8400_DC1_ENA) != 0;
 117}
 118
 119static int wm8400_dcdc_enable(struct regulator_dev *dev)
 120{
 121        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
 122        int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
 123
 124        return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
 125                               WM8400_DC1_ENA, WM8400_DC1_ENA);
 126}
 127
 128static int wm8400_dcdc_disable(struct regulator_dev *dev)
 129{
 130        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
 131        int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
 132
 133        return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
 134                               WM8400_DC1_ENA, 0);
 135}
 136
 137static int wm8400_dcdc_list_voltage(struct regulator_dev *dev,
 138                                    unsigned selector)
 139{
 140        if (selector > WM8400_DC1_VSEL_MASK)
 141                return -EINVAL;
 142
 143        return 850000 + (selector * 25000);
 144}
 145
 146static int wm8400_dcdc_get_voltage(struct regulator_dev *dev)
 147{
 148        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
 149        u16 val;
 150        int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
 151
 152        val = wm8400_reg_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset);
 153        val &= WM8400_DC1_VSEL_MASK;
 154
 155        return 850000 + (25000 * val);
 156}
 157
 158static int wm8400_dcdc_set_voltage(struct regulator_dev *dev,
 159                                  int min_uV, int max_uV)
 160{
 161        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
 162        u16 val;
 163        int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
 164
 165        if (min_uV < 850000)
 166                return -EINVAL;
 167
 168        val = (min_uV - 825001) / 25000;
 169
 170        if (850000 + (25000 * val) > max_uV)
 171                return -EINVAL;
 172        BUG_ON(850000 + (25000 * val) < min_uV);
 173
 174        return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
 175                               WM8400_DC1_VSEL_MASK, val);
 176}
 177
 178static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev)
 179{
 180        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
 181        int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
 182        u16 data[2];
 183        int ret;
 184
 185        ret = wm8400_block_read(wm8400, WM8400_DCDC1_CONTROL_1 + offset, 2,
 186                                data);
 187        if (ret != 0)
 188                return 0;
 189
 190        /* Datasheet: hibernate */
 191        if (data[0] & WM8400_DC1_SLEEP)
 192                return REGULATOR_MODE_STANDBY;
 193
 194        /* Datasheet: standby */
 195        if (!(data[0] & WM8400_DC1_ACTIVE))
 196                return REGULATOR_MODE_IDLE;
 197
 198        /* Datasheet: active with or without force PWM */
 199        if (data[1] & WM8400_DC1_FRC_PWM)
 200                return REGULATOR_MODE_FAST;
 201        else
 202                return REGULATOR_MODE_NORMAL;
 203}
 204
 205static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)
 206{
 207        struct wm8400 *wm8400 = rdev_get_drvdata(dev);
 208        int offset = (rdev_get_id(dev) - WM8400_DCDC1) * 2;
 209        int ret;
 210
 211        switch (mode) {
 212        case REGULATOR_MODE_FAST:
 213                /* Datasheet: active with force PWM */
 214                ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset,
 215                                      WM8400_DC1_FRC_PWM, WM8400_DC1_FRC_PWM);
 216                if (ret != 0)
 217                        return ret;
 218
 219                return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
 220                                       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
 221                                       WM8400_DC1_ACTIVE);
 222
 223        case REGULATOR_MODE_NORMAL:
 224                /* Datasheet: active */
 225                ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_2 + offset,
 226                                      WM8400_DC1_FRC_PWM, 0);
 227                if (ret != 0)
 228                        return ret;
 229
 230                return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
 231                                       WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP,
 232                                       WM8400_DC1_ACTIVE);
 233
 234        case REGULATOR_MODE_IDLE:
 235                /* Datasheet: standby */
 236                ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
 237                                      WM8400_DC1_ACTIVE, 0);
 238                if (ret != 0)
 239                        return ret;
 240                return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
 241                                       WM8400_DC1_SLEEP, 0);
 242
 243        default:
 244                return -EINVAL;
 245        }
 246}
 247
 248static unsigned int wm8400_dcdc_get_optimum_mode(struct regulator_dev *dev,
 249                                                 int input_uV, int output_uV,
 250                                                 int load_uA)
 251{
 252        return REGULATOR_MODE_NORMAL;
 253}
 254
 255static struct regulator_ops wm8400_dcdc_ops = {
 256        .is_enabled = wm8400_dcdc_is_enabled,
 257        .enable = wm8400_dcdc_enable,
 258        .disable = wm8400_dcdc_disable,
 259        .list_voltage = wm8400_dcdc_list_voltage,
 260        .get_voltage = wm8400_dcdc_get_voltage,
 261        .set_voltage = wm8400_dcdc_set_voltage,
 262        .get_mode = wm8400_dcdc_get_mode,
 263        .set_mode = wm8400_dcdc_set_mode,
 264        .get_optimum_mode = wm8400_dcdc_get_optimum_mode,
 265};
 266
 267static struct regulator_desc regulators[] = {
 268        {
 269                .name = "LDO1",
 270                .id = WM8400_LDO1,
 271                .ops = &wm8400_ldo_ops,
 272                .n_voltages = WM8400_LDO1_VSEL_MASK + 1,
 273                .type = REGULATOR_VOLTAGE,
 274                .owner = THIS_MODULE,
 275        },
 276        {
 277                .name = "LDO2",
 278                .id = WM8400_LDO2,
 279                .ops = &wm8400_ldo_ops,
 280                .n_voltages = WM8400_LDO2_VSEL_MASK + 1,
 281                .type = REGULATOR_VOLTAGE,
 282                .owner = THIS_MODULE,
 283        },
 284        {
 285                .name = "LDO3",
 286                .id = WM8400_LDO3,
 287                .ops = &wm8400_ldo_ops,
 288                .n_voltages = WM8400_LDO3_VSEL_MASK + 1,
 289                .type = REGULATOR_VOLTAGE,
 290                .owner = THIS_MODULE,
 291        },
 292        {
 293                .name = "LDO4",
 294                .id = WM8400_LDO4,
 295                .ops = &wm8400_ldo_ops,
 296                .n_voltages = WM8400_LDO4_VSEL_MASK + 1,
 297                .type = REGULATOR_VOLTAGE,
 298                .owner = THIS_MODULE,
 299        },
 300        {
 301                .name = "DCDC1",
 302                .id = WM8400_DCDC1,
 303                .ops = &wm8400_dcdc_ops,
 304                .n_voltages = WM8400_DC1_VSEL_MASK + 1,
 305                .type = REGULATOR_VOLTAGE,
 306                .owner = THIS_MODULE,
 307        },
 308        {
 309                .name = "DCDC2",
 310                .id = WM8400_DCDC2,
 311                .ops = &wm8400_dcdc_ops,
 312                .n_voltages = WM8400_DC2_VSEL_MASK + 1,
 313                .type = REGULATOR_VOLTAGE,
 314                .owner = THIS_MODULE,
 315        },
 316};
 317
 318static int __devinit wm8400_regulator_probe(struct platform_device *pdev)
 319{
 320        struct regulator_dev *rdev;
 321
 322        rdev = regulator_register(&regulators[pdev->id], &pdev->dev,
 323                pdev->dev.platform_data, dev_get_drvdata(&pdev->dev));
 324
 325        if (IS_ERR(rdev))
 326                return PTR_ERR(rdev);
 327
 328        return 0;
 329}
 330
 331static int __devexit wm8400_regulator_remove(struct platform_device *pdev)
 332{
 333        struct regulator_dev *rdev = platform_get_drvdata(pdev);
 334
 335        regulator_unregister(rdev);
 336
 337        return 0;
 338}
 339
 340static struct platform_driver wm8400_regulator_driver = {
 341        .driver = {
 342                .name = "wm8400-regulator",
 343        },
 344        .probe = wm8400_regulator_probe,
 345        .remove = __devexit_p(wm8400_regulator_remove),
 346};
 347
 348/**
 349 * wm8400_register_regulator - enable software control of a WM8400 regulator
 350 *
 351 * This function enables software control of a WM8400 regulator via
 352 * the regulator API.  It is intended to be called from the
 353 * platform_init() callback of the WM8400 MFD driver.
 354 *
 355 * @param dev      The WM8400 device to operate on.
 356 * @param reg      The regulator to control.
 357 * @param initdata Regulator initdata for the regulator.
 358 */
 359int wm8400_register_regulator(struct device *dev, int reg,
 360                              struct regulator_init_data *initdata)
 361{
 362        struct wm8400 *wm8400 = dev_get_drvdata(dev);
 363
 364        if (wm8400->regulators[reg].name)
 365                return -EBUSY;
 366
 367        initdata->driver_data = wm8400;
 368
 369        wm8400->regulators[reg].name = "wm8400-regulator";
 370        wm8400->regulators[reg].id = reg;
 371        wm8400->regulators[reg].dev.parent = dev;
 372        wm8400->regulators[reg].dev.platform_data = initdata;
 373        dev_set_drvdata(&wm8400->regulators[reg].dev, wm8400);
 374
 375        return platform_device_register(&wm8400->regulators[reg]);
 376}
 377EXPORT_SYMBOL_GPL(wm8400_register_regulator);
 378
 379static int __init wm8400_regulator_init(void)
 380{
 381        return platform_driver_register(&wm8400_regulator_driver);
 382}
 383subsys_initcall(wm8400_regulator_init);
 384
 385static void __exit wm8400_regulator_exit(void)
 386{
 387        platform_driver_unregister(&wm8400_regulator_driver);
 388}
 389module_exit(wm8400_regulator_exit);
 390
 391MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 392MODULE_DESCRIPTION("WM8400 regulator driver");
 393MODULE_LICENSE("GPL");
 394MODULE_ALIAS("platform:wm8400-regulator");
 395