Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 17 Dec 2009 16:08:36 +0000 (08:08 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 17 Dec 2009 16:08:36 +0000 (08:08 -0800)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6: (27 commits)
  regulator: wm831x_reg_read() failure unnoticed in wm831x_aldo_get_mode()
  twl-regulator: Fix reg_disable functionality for 4030 and 6030
  twl-regulator: Add turnon delay to reg_enable
  twl-regulator: Restore REMAP configuration in regulator probe
  twl-regulator: Add turnon-delay and REMAP config to twlreg_info struct
  twl-regulator: Define critical regulators as always_on
  twl-regulator: Add all twl4030 regulators to twlreg_info
  regulator: mc13783-regulator: correct the probing time.
  regulator: Fix unbalanced disables/enables in regulator_bulk_{enable,disable} error path
  regulator: core.c: Small coding style cleanup (indentation fixup)
  drivers/regulator: use PTR_ERR to get error code
  regulator: consumer.h - fix build when consumer.h is #included first.
  regulator/mc13783: various cleanups
  regulator/mc13783: rename source file to match other drivers
  Fix some AB3100 regulator issues
  regulator: keep index within bounds in da9034_get_ldo12_voltage()
  regulator: Ensure val is initialised in 88pm8607 choose_voltage()
  regulator: Remove duplicate consts from ab3100
  regulator: Handle regulators without suspend mode configuration
  regulator: Factor out regulator name pretty printing
  ...

17 files changed:
drivers/regulator/88pm8607.c [new file with mode: 0644]
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/ab3100.c
drivers/regulator/core.c
drivers/regulator/da903x.c
drivers/regulator/lp3971.c
drivers/regulator/max8660.c [new file with mode: 0644]
drivers/regulator/mc13783-regulator.c [new file with mode: 0644]
drivers/regulator/mc13783.c [deleted file]
drivers/regulator/twl-regulator.c
drivers/regulator/wm831x-dcdc.c
drivers/regulator/wm831x-ldo.c
include/linux/mfd/wm831x/pdata.h
include/linux/regulator/consumer.h
include/linux/regulator/machine.h
include/linux/regulator/max8660.h [new file with mode: 0644]

diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c
new file mode 100644 (file)
index 0000000..0471955
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ * Regulators driver for Marvell 88PM8607
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ *     Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/88pm8607.h>
+
+struct pm8607_regulator_info {
+       struct regulator_desc   desc;
+       struct pm8607_chip      *chip;
+       struct regulator_dev    *regulator;
+
+       int     min_uV;
+       int     max_uV;
+       int     step_uV;
+       int     vol_reg;
+       int     vol_shift;
+       int     vol_nbits;
+       int     update_reg;
+       int     update_bit;
+       int     enable_reg;
+       int     enable_bit;
+       int     slope_double;
+};
+
+static inline int check_range(struct pm8607_regulator_info *info,
+                               int min_uV, int max_uV)
+{
+       if (max_uV < info->min_uV || min_uV > info->max_uV || min_uV > max_uV)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index)
+{
+       struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
+       uint8_t chip_id = info->chip->chip_id;
+       int ret = -EINVAL;
+
+       switch (info->desc.id) {
+       case PM8607_ID_BUCK1:
+               ret = (index < 0x1d) ? (index * 25000 + 800000) :
+                       ((index < 0x20) ? 1500000 :
+                       ((index < 0x40) ? ((index - 0x20) * 25000) :
+                       -EINVAL));
+               break;
+       case PM8607_ID_BUCK3:
+               ret = (index < 0x3d) ? (index * 25000) :
+                       ((index < 0x40) ? 1500000 : -EINVAL);
+               if (ret < 0)
+                       break;
+               if (info->slope_double)
+                       ret <<= 1;
+               break;
+       case PM8607_ID_LDO1:
+               ret = (index == 0) ? 1800000 :
+                       ((index == 1) ? 1200000 :
+                       ((index == 2) ? 2800000 : -EINVAL));
+               break;
+       case PM8607_ID_LDO5:
+               ret = (index == 0) ? 2900000 :
+                       ((index == 1) ? 3000000 :
+                       ((index == 2) ? 3100000 : 3300000));
+               break;
+       case PM8607_ID_LDO7:
+       case PM8607_ID_LDO8:
+               ret = (index < 3) ? (index * 50000 + 1800000) :
+                       ((index < 8) ? (index * 50000 + 2550000) :
+                        -EINVAL);
+               break;
+       case PM8607_ID_LDO12:
+               ret = (index < 2) ? (index * 100000 + 1800000) :
+                       ((index < 7) ? (index * 100000 + 2500000) :
+                       ((index == 7) ? 3300000 : 1200000));
+               break;
+       case PM8607_ID_LDO2:
+       case PM8607_ID_LDO3:
+       case PM8607_ID_LDO9:
+               switch (chip_id) {
+               case PM8607_CHIP_A0:
+               case PM8607_CHIP_A1:
+                       ret = (index < 3) ? (index * 50000 + 1800000) :
+                               ((index < 8) ? (index * 50000 + 2550000) :
+                                -EINVAL);
+                       break;
+               case PM8607_CHIP_B0:
+                       ret = (index < 3) ? (index * 50000 + 1800000) :
+                               ((index < 7) ? (index * 50000 + 2550000) :
+                               3300000);
+                       break;
+               }
+               break;
+       case PM8607_ID_LDO4:
+               switch (chip_id) {
+               case PM8607_CHIP_A0:
+               case PM8607_CHIP_A1:
+                       ret = (index < 3) ? (index * 50000 + 1800000) :
+                               ((index < 8) ? (index * 50000 + 2550000) :
+                                -EINVAL);
+                       break;
+               case PM8607_CHIP_B0:
+                       ret = (index < 3) ? (index * 50000 + 1800000) :
+                               ((index < 6) ? (index * 50000 + 2550000) :
+                               ((index == 6) ? 2900000 : 3300000));
+                       break;
+               }
+               break;
+       case PM8607_ID_LDO6:
+               switch (chip_id) {
+               case PM8607_CHIP_A0:
+               case PM8607_CHIP_A1:
+                       ret = (index < 3) ? (index * 50000 + 1800000) :
+                               ((index < 8) ? (index * 50000 + 2450000) :
+                               -EINVAL);
+                       break;
+               case PM8607_CHIP_B0:
+                       ret = (index < 2) ? (index * 50000 + 1800000) :
+                               ((index < 7) ? (index * 50000 + 2500000) :
+                               3300000);
+                       break;
+               }
+               break;
+       case PM8607_ID_LDO10:
+               switch (chip_id) {
+               case PM8607_CHIP_A0:
+               case PM8607_CHIP_A1:
+                       ret = (index < 3) ? (index * 50000 + 1800000) :
+                               ((index < 8) ? (index * 50000 + 2550000) :
+                               1200000);
+                       break;
+               case PM8607_CHIP_B0:
+                       ret = (index < 3) ? (index * 50000 + 1800000) :
+                               ((index < 7) ? (index * 50000 + 2550000) :
+                               ((index == 7) ? 3300000 : 1200000));
+                       break;
+               }
+               break;
+       case PM8607_ID_LDO14:
+               switch (chip_id) {
+               case PM8607_CHIP_A0:
+               case PM8607_CHIP_A1:
+                       ret = (index < 3) ? (index * 50000 + 1800000) :
+                               ((index < 8) ? (index * 50000 + 2550000) :
+                                -EINVAL);
+                       break;
+               case PM8607_CHIP_B0:
+                       ret = (index < 2) ? (index * 50000 + 1800000) :
+                               ((index < 7) ? (index * 50000 + 2600000) :
+                               3300000);
+                       break;
+               }
+               break;
+       }
+       return ret;
+}
+
+static int choose_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
+{
+       struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
+       uint8_t chip_id = info->chip->chip_id;
+       int val = -ENOENT;
+       int ret;
+
+       switch (info->desc.id) {
+       case PM8607_ID_BUCK1:
+               if (min_uV >= 800000)           /* 800mV ~ 1500mV / 25mV */
+                       val = (min_uV - 775001) / 25000;
+               else {                          /* 25mV ~ 775mV / 25mV */
+                       val = (min_uV + 249999) / 25000;
+                       val += 32;
+               }
+               break;
+       case PM8607_ID_BUCK3:
+               if (info->slope_double)
+                       min_uV = min_uV >> 1;
+               val = (min_uV + 249999) / 25000; /* 0mV ~ 1500mV / 25mV */
+
+               break;
+       case PM8607_ID_LDO1:
+               if (min_uV > 1800000)
+                       val = 2;
+               else if (min_uV > 1200000)
+                       val = 0;
+               else
+                       val = 1;
+               break;
+       case PM8607_ID_LDO5:
+               if (min_uV > 3100000)
+                       val = 3;
+               else                            /* 2900mV ~ 3100mV / 100mV */
+                       val = (min_uV - 2800001) / 100000;
+               break;
+       case PM8607_ID_LDO7:
+       case PM8607_ID_LDO8:
+               if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
+                       if (min_uV <= 1800000)
+                               val = 0;        /* 1800mv */
+                       else if (min_uV <= 1900000)
+                               val = (min_uV - 1750001) / 50000;
+                       else
+                               val = 3;        /* 2700mV */
+               } else {                 /* 2700mV ~ 2900mV / 50mV */
+                       if (min_uV <= 2900000) {
+                               val = (min_uV - 2650001) / 50000;
+                               val += 3;
+                       } else
+                               val = -EINVAL;
+               }
+               break;
+       case PM8607_ID_LDO10:
+               if (min_uV > 2850000)
+                       val = 7;
+               else if (min_uV <= 1200000)
+                       val = 8;
+               else if (min_uV < 2700000)      /* 1800mV ~ 1900mV / 50mV */
+                       val = (min_uV - 1750001) / 50000;
+               else {                          /* 2700mV ~ 2850mV / 50mV */
+                       val = (min_uV - 2650001) / 50000;
+                       val += 3;
+               }
+               break;
+       case PM8607_ID_LDO12:
+               if (min_uV < 2700000) {         /* 1800mV ~ 1900mV / 100mV */
+                       if (min_uV <= 1200000)
+                               val = 8;        /* 1200mV */
+                       else if (min_uV <= 1800000)
+                               val = 0;        /* 1800mV */
+                       else if (min_uV <= 1900000)
+                               val = (min_uV - 1700001) / 100000;
+                       else
+                               val = 2;        /* 2700mV */
+               } else {                        /* 2700mV ~ 3100mV / 100mV */
+                       if (min_uV <= 3100000) {
+                               val = (min_uV - 2600001) / 100000;
+                               val += 2;
+                       } else if (min_uV <= 3300000)
+                               val = 7;
+                       else
+                               val = -EINVAL;
+               }
+               break;
+       case PM8607_ID_LDO2:
+       case PM8607_ID_LDO3:
+       case PM8607_ID_LDO9:
+               switch (chip_id) {
+               case PM8607_CHIP_A0:
+               case PM8607_CHIP_A1:
+                       if (min_uV < 2700000)   /* 1800mV ~ 1900mV / 50mV */
+                               if (min_uV <= 1800000)
+                                       val = 0;
+                               else if (min_uV <= 1900000)
+                                       val = (min_uV - 1750001) / 50000;
+                               else
+                                       val = 3;        /* 2700mV */
+                       else {                  /* 2700mV ~ 2900mV / 50mV */
+                               if (min_uV <= 2900000) {
+                                       val = (min_uV - 2650001) / 50000;
+                                       val += 3;
+                               } else
+                                       val = -EINVAL;
+                       }
+                       break;
+               case PM8607_CHIP_B0:
+                       if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
+                               if (min_uV <= 1800000)
+                                       val = 0;
+                               else if (min_uV <= 1900000)
+                                       val = (min_uV - 1750001) / 50000;
+                               else
+                                       val = 3;        /* 2700mV */
+                       } else {                 /* 2700mV ~ 2850mV / 50mV */
+                               if (min_uV <= 2850000) {
+                                       val = (min_uV - 2650001) / 50000;
+                                       val += 3;
+                               } else if (min_uV <= 3300000)
+                                       val = 7;
+                               else
+                                       val = -EINVAL;
+                       }
+                       break;
+               }
+               break;
+       case PM8607_ID_LDO4:
+               switch (chip_id) {
+               case PM8607_CHIP_A0:
+               case PM8607_CHIP_A1:
+                       if (min_uV < 2700000)   /* 1800mV ~ 1900mV / 50mV */
+                               if (min_uV <= 1800000)
+                                       val = 0;
+                               else if (min_uV <= 1900000)
+                                       val = (min_uV - 1750001) / 50000;
+                               else
+                                       val = 3;        /* 2700mV */
+                       else {                  /* 2700mV ~ 2900mV / 50mV */
+                               if (min_uV <= 2900000) {
+                                       val = (min_uV - 2650001) / 50000;
+                                       val += 3;
+                               } else
+                                       val = -EINVAL;
+                       }
+                       break;
+               case PM8607_CHIP_B0:
+                       if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
+                               if (min_uV <= 1800000)
+                                       val = 0;
+                               else if (min_uV <= 1900000)
+                                       val = (min_uV - 1750001) / 50000;
+                               else
+                                       val = 3;        /* 2700mV */
+                       } else {                 /* 2700mV ~ 2800mV / 50mV */
+                               if (min_uV <= 2850000) {
+                                       val = (min_uV - 2650001) / 50000;
+                                       val += 3;
+                               } else if (min_uV <= 2900000)
+                                       val = 6;
+                               else if (min_uV <= 3300000)
+                                       val = 7;
+                               else
+                                       val = -EINVAL;
+                       }
+                       break;
+               }
+               break;
+       case PM8607_ID_LDO6:
+               switch (chip_id) {
+               case PM8607_CHIP_A0:
+               case PM8607_CHIP_A1:
+                       if (min_uV < 2600000) { /* 1800mV ~ 1900mV / 50mV */
+                               if (min_uV <= 1800000)
+                                       val = 0;
+                               else if (min_uV <= 1900000)
+                                       val = (min_uV - 1750001) / 50000;
+                               else
+                                       val = 3;        /* 2600mV */
+                       } else {                /* 2600mV ~ 2800mV / 50mV */
+                               if (min_uV <= 2800000) {
+                                       val = (min_uV - 2550001) / 50000;
+                                       val += 3;
+                               } else
+                                       val = -EINVAL;
+                       }
+                       break;
+               case PM8607_CHIP_B0:
+                       if (min_uV < 2600000) { /* 1800mV ~ 1850mV / 50mV */
+                               if (min_uV <= 1800000)
+                                       val = 0;
+                               else if (min_uV <= 1850000)
+                                       val = (min_uV - 1750001) / 50000;
+                               else
+                                       val = 2;        /* 2600mV */
+                       } else {                /* 2600mV ~ 2800mV / 50mV */
+                               if (min_uV <= 2800000) {
+                                       val = (min_uV - 2550001) / 50000;
+                                       val += 2;
+                               } else if (min_uV <= 3300000)
+                                       val = 7;
+                               else
+                                       val = -EINVAL;
+                       }
+                       break;
+               }
+               break;
+       case PM8607_ID_LDO14:
+               switch (chip_id) {
+               case PM8607_CHIP_A0:
+               case PM8607_CHIP_A1:
+                       if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
+                               if (min_uV <= 1800000)
+                                       val = 0;
+                               else if (min_uV <= 1900000)
+                                       val = (min_uV - 1750001) / 50000;
+                               else
+                                       val = 3;        /* 2700mV */
+                       } else {                 /* 2700mV ~ 2900mV / 50mV */
+                               if (min_uV <= 2900000) {
+                                       val = (min_uV - 2650001) / 50000;
+                                       val += 3;
+                               } else
+                                       val = -EINVAL;
+                       }
+                       break;
+               case PM8607_CHIP_B0:
+                       if (min_uV < 2700000) { /* 1800mV ~ 1850mV / 50mV */
+                               if (min_uV <= 1800000)
+                                       val = 0;
+                               else if (min_uV <= 1850000)
+                                       val = (min_uV - 1750001) / 50000;
+                               else
+                                       val = 2;        /* 2700mV */
+                       } else {                 /* 2700mV ~ 2900mV / 50mV */
+                               if (min_uV <= 2900000) {
+                                       val = (min_uV - 2650001) / 50000;
+                                       val += 2;
+                               } else if (min_uV <= 3300000)
+                                       val = 7;
+                               else
+                                       val = -EINVAL;
+                       }
+                       break;
+               }
+               break;
+       }
+       if (val >= 0) {
+               ret = pm8607_list_voltage(rdev, val);
+               if (ret > max_uV) {
+                       pr_err("exceed voltage range (%d %d) uV",
+                               min_uV, max_uV);
+                       return -EINVAL;
+               }
+       } else
+               pr_err("invalid voltage range (%d %d) uV", min_uV, max_uV);
+       return val;
+}
+
+static int pm8607_set_voltage(struct regulator_dev *rdev,
+                             int min_uV, int max_uV)
+{
+       struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
+       struct pm8607_chip *chip = info->chip;
+       uint8_t val, mask;
+       int ret;
+
+       if (check_range(info, min_uV, max_uV)) {
+               pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV);
+               return -EINVAL;
+       }
+
+       ret = choose_voltage(rdev, min_uV, max_uV);
+       if (ret < 0)
+               return -EINVAL;
+       val = (uint8_t)(ret << info->vol_shift);
+       mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+
+       ret = pm8607_set_bits(chip, info->vol_reg, mask, val);
+       if (ret)
+               return ret;
+       switch (info->desc.id) {
+       case PM8607_ID_BUCK1:
+       case PM8607_ID_BUCK3:
+               ret = pm8607_set_bits(chip, info->update_reg,
+                                     1 << info->update_bit,
+                                     1 << info->update_bit);
+               break;
+       }
+       return ret;
+}
+
+static int pm8607_get_voltage(struct regulator_dev *rdev)
+{
+       struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
+       struct pm8607_chip *chip = info->chip;
+       uint8_t val, mask;
+       int ret;
+
+       ret = pm8607_reg_read(chip, info->vol_reg);
+       if (ret < 0)
+               return ret;
+
+       mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
+       val = ((unsigned char)ret & mask) >> info->vol_shift;
+
+       return pm8607_list_voltage(rdev, val);
+}
+
+static int pm8607_enable(struct regulator_dev *rdev)
+{
+       struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
+       struct pm8607_chip *chip = info->chip;
+
+       return pm8607_set_bits(chip, info->enable_reg,
+                              1 << info->enable_bit,
+                              1 << info->enable_bit);
+}
+
+static int pm8607_disable(struct regulator_dev *rdev)
+{
+       struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
+       struct pm8607_chip *chip = info->chip;
+
+       return pm8607_set_bits(chip, info->enable_reg,
+                              1 << info->enable_bit, 0);
+}
+
+static int pm8607_is_enabled(struct regulator_dev *rdev)
+{
+       struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
+       struct pm8607_chip *chip = info->chip;
+       int ret;
+
+       ret = pm8607_reg_read(chip, info->enable_reg);
+       if (ret < 0)
+               return ret;
+
+       return !!((unsigned char)ret & (1 << info->enable_bit));
+}
+
+static struct regulator_ops pm8607_regulator_ops = {
+       .set_voltage    = pm8607_set_voltage,
+       .get_voltage    = pm8607_get_voltage,
+       .enable         = pm8607_enable,
+       .disable        = pm8607_disable,
+       .is_enabled     = pm8607_is_enabled,
+};
+
+#define PM8607_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \
+{                                                                      \
+       .desc   = {                                                     \
+               .name   = "BUCK" #_id,                                  \
+               .ops    = &pm8607_regulator_ops,                        \
+               .type   = REGULATOR_VOLTAGE,                            \
+               .id     = PM8607_ID_BUCK##_id,                          \
+               .owner  = THIS_MODULE,                                  \
+       },                                                              \
+       .min_uV         = (min) * 1000,                                 \
+       .max_uV         = (max) * 1000,                                 \
+       .step_uV        = (step) * 1000,                                \
+       .vol_reg        = PM8607_##vreg,                                \
+       .vol_shift      = (0),                                          \
+       .vol_nbits      = (nbits),                                      \
+       .update_reg     = PM8607_##ureg,                                \
+       .update_bit     = (ubit),                                       \
+       .enable_reg     = PM8607_##ereg,                                \
+       .enable_bit     = (ebit),                                       \
+       .slope_double   = (0),                                          \
+}
+
+#define PM8607_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit)        \
+{                                                                      \
+       .desc   = {                                                     \
+               .name   = "LDO" #_id,                                   \
+               .ops    = &pm8607_regulator_ops,                        \
+               .type   = REGULATOR_VOLTAGE,                            \
+               .id     = PM8607_ID_LDO##_id,                           \
+               .owner  = THIS_MODULE,                                  \
+       },                                                              \
+       .min_uV         = (min) * 1000,                                 \
+       .max_uV         = (max) * 1000,                                 \
+       .step_uV        = (step) * 1000,                                \
+       .vol_reg        = PM8607_##vreg,                                \
+       .vol_shift      = (shift),                                      \
+       .vol_nbits      = (nbits),                                      \
+       .enable_reg     = PM8607_##ereg,                                \
+       .enable_bit     = (ebit),                                       \
+       .slope_double   = (0),                                          \
+}
+
+static struct pm8607_regulator_info pm8607_regulator_info[] = {
+       PM8607_DVC(1, 0, 1500, 25, BUCK1, 6, GO, 0, SUPPLIES_EN11, 0),
+       PM8607_DVC(3, 0, 1500, 25, BUCK3, 6, GO, 2, SUPPLIES_EN11, 2),
+
+       PM8607_LDO(1 , 1200, 2800, 0, LDO1 , 0, 2, SUPPLIES_EN11, 3),
+       PM8607_LDO(2 , 1800, 3300, 0, LDO2 , 0, 3, SUPPLIES_EN11, 4),
+       PM8607_LDO(3 , 1800, 3300, 0, LDO3 , 0, 3, SUPPLIES_EN11, 5),
+       PM8607_LDO(4 , 1800, 3300, 0, LDO4 , 0, 3, SUPPLIES_EN11, 6),
+       PM8607_LDO(5 , 2900, 3300, 0, LDO5 , 0, 2, SUPPLIES_EN11, 7),
+       PM8607_LDO(6 , 1800, 3300, 0, LDO6 , 0, 3, SUPPLIES_EN12, 0),
+       PM8607_LDO(7 , 1800, 2900, 0, LDO7 , 0, 3, SUPPLIES_EN12, 1),
+       PM8607_LDO(8 , 1800, 2900, 0, LDO8 , 0, 3, SUPPLIES_EN12, 2),
+       PM8607_LDO(9 , 1800, 3300, 0, LDO9 , 0, 3, SUPPLIES_EN12, 3),
+       PM8607_LDO(10, 1200, 3300, 0, LDO10, 0, 4, SUPPLIES_EN11, 4),
+       PM8607_LDO(12, 1200, 3300, 0, LDO12, 0, 4, SUPPLIES_EN11, 5),
+       PM8607_LDO(14, 1800, 3300, 0, LDO14, 0, 3, SUPPLIES_EN11, 6),
+};
+
+static inline struct pm8607_regulator_info *find_regulator_info(int id)
+{
+       struct pm8607_regulator_info *info;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pm8607_regulator_info); i++) {
+               info = &pm8607_regulator_info[i];
+               if (info->desc.id == id)
+                       return info;
+       }
+       return NULL;
+}
+
+static int __devinit pm8607_regulator_probe(struct platform_device *pdev)
+{
+       struct pm8607_chip *chip = dev_get_drvdata(pdev->dev.parent);
+       struct pm8607_platform_data *pdata = chip->dev->platform_data;
+       struct pm8607_regulator_info *info = NULL;
+
+       info = find_regulator_info(pdev->id);
+       if (info == NULL) {
+               dev_err(&pdev->dev, "invalid regulator ID specified\n");
+               return -EINVAL;
+       }
+
+       info->chip = chip;
+
+       info->regulator = regulator_register(&info->desc, &pdev->dev,
+                                            pdata->regulator[pdev->id], info);
+       if (IS_ERR(info->regulator)) {
+               dev_err(&pdev->dev, "failed to register regulator %s\n",
+                       info->desc.name);
+               return PTR_ERR(info->regulator);
+       }
+
+       /* check DVC ramp slope double */
+       if (info->desc.id == PM8607_ID_BUCK3)
+               if (info->chip->buck3_double)
+                       info->slope_double = 1;
+
+       platform_set_drvdata(pdev, info);
+       return 0;
+}
+
+static int __devexit pm8607_regulator_remove(struct platform_device *pdev)
+{
+       struct pm8607_regulator_info *info = platform_get_drvdata(pdev);
+
+       regulator_unregister(info->regulator);
+       return 0;
+}
+
+#define PM8607_REGULATOR_DRIVER(_name)                         \
+{                                                              \
+       .driver         = {                                     \
+               .name   = "88pm8607-" #_name,                   \
+               .owner  = THIS_MODULE,                          \
+       },                                                      \
+       .probe          = pm8607_regulator_probe,               \
+       .remove         = __devexit_p(pm8607_regulator_remove), \
+}
+
+static struct platform_driver pm8607_regulator_driver[] = {
+       PM8607_REGULATOR_DRIVER(buck1),
+       PM8607_REGULATOR_DRIVER(buck2),
+       PM8607_REGULATOR_DRIVER(buck3),
+       PM8607_REGULATOR_DRIVER(ldo1),
+       PM8607_REGULATOR_DRIVER(ldo2),
+       PM8607_REGULATOR_DRIVER(ldo3),
+       PM8607_REGULATOR_DRIVER(ldo4),
+       PM8607_REGULATOR_DRIVER(ldo5),
+       PM8607_REGULATOR_DRIVER(ldo6),
+       PM8607_REGULATOR_DRIVER(ldo7),
+       PM8607_REGULATOR_DRIVER(ldo8),
+       PM8607_REGULATOR_DRIVER(ldo9),
+       PM8607_REGULATOR_DRIVER(ldo10),
+       PM8607_REGULATOR_DRIVER(ldo12),
+       PM8607_REGULATOR_DRIVER(ldo14),
+};
+
+static int __init pm8607_regulator_init(void)
+{
+       int i, count, ret;
+
+       count = ARRAY_SIZE(pm8607_regulator_driver);
+       for (i = 0; i < count; i++) {
+               ret = platform_driver_register(&pm8607_regulator_driver[i]);
+               if (ret != 0)
+                       pr_err("Failed to register regulator driver: %d\n",
+                               ret);
+       }
+       return 0;
+}
+subsys_initcall(pm8607_regulator_init);
+
+static void __exit pm8607_regulator_exit(void)
+{
+       int i, count;
+
+       count = ARRAY_SIZE(pm8607_regulator_driver);
+       for (i = 0; i < count; i++)
+               platform_driver_unregister(&pm8607_regulator_driver[i]);
+}
+module_exit(pm8607_regulator_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_DESCRIPTION("Regulator Driver for Marvell 88PM8607 PMIC");
+MODULE_ALIAS("platform:88pm8607-regulator");
index 7cfdd65bebb4185efb9552d2be4afa9f3611385f..262f62eec837438f36a5adbf53ebdabae96fe9f7 100644 (file)
@@ -69,6 +69,13 @@ config REGULATOR_MAX1586
          regulator via I2C bus. The provided regulator is suitable
          for PXA27x chips to control VCC_CORE and VCC_USIM voltages.
 
+config REGULATOR_MAX8660
+       tristate "Maxim 8660/8661 voltage regulator"
+       depends on I2C
+       help
+         This driver controls a Maxim 8660/8661 voltage output
+         regulator via I2C bus.
+
 config REGULATOR_TWL4030
        bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC"
        depends on TWL4030_CORE
@@ -157,5 +164,11 @@ config REGULATOR_TPS6507X
          three step-down converters and two general-purpose LDO voltage regulators.
          It supports TI's software based Class-2 SmartReflex implementation.
 
+config REGULATOR_88PM8607
+       bool "Marvell 88PM8607 Power regulators"
+       depends on MFD_88PM8607=y
+       help
+         This driver supports 88PM8607 voltage regulator chips.
+
 endif
 
index 9ae3cc44e668d97dacaecee4489322de94f8d56e..b3c806c794157aac2f2604d16e9b6a47d82d0ac2 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
 obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
 obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
 obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
+obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
 obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
@@ -20,10 +21,11 @@ obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
 obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
 obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
 obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
-obj-$(CONFIG_REGULATOR_MC13783) += mc13783.o
+obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
 
 obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o
+obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
 
 ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
index 49aeee823a25889e8eebd1c21d15ec6b1949ea28..b349db4504b79c6d122ee94221793541af4e0f32 100644 (file)
@@ -81,7 +81,7 @@ static const u8 ab3100_reg_init_order[AB3100_NUM_REGULATORS+2] = {
 #define LDO_C_VOLTAGE 2650000
 #define LDO_D_VOLTAGE 2650000
 
-static const int const ldo_e_buck_typ_voltages[] = {
+static const int ldo_e_buck_typ_voltages[] = {
        1800000,
        1400000,
        1300000,
@@ -91,7 +91,7 @@ static const int const ldo_e_buck_typ_voltages[] = {
        900000,
 };
 
-static const int const ldo_f_typ_voltages[] = {
+static const int ldo_f_typ_voltages[] = {
        1800000,
        1400000,
        1300000,
@@ -102,21 +102,21 @@ static const int const ldo_f_typ_voltages[] = {
        2650000,
 };
 
-static const int const ldo_g_typ_voltages[] = {
+static const int ldo_g_typ_voltages[] = {
        2850000,
        2750000,
        1800000,
        1500000,
 };
 
-static const int const ldo_h_typ_voltages[] = {
+static const int ldo_h_typ_voltages[] = {
        2750000,
        1800000,
        1500000,
        1200000,
 };
 
-static const int const ldo_k_typ_voltages[] = {
+static const int ldo_k_typ_voltages[] = {
        2750000,
        1800000,
 };
@@ -241,24 +241,12 @@ static int ab3100_disable_regulator(struct regulator_dev *reg)
         * LDO D is a special regulator. When it is disabled, the entire
         * system is shut down. So this is handled specially.
         */
+       pr_info("Called ab3100_disable_regulator\n");
        if (abreg->regreg == AB3100_LDO_D) {
-               int i;
-
                dev_info(&reg->dev, "disabling LDO D - shut down system\n");
-               /*
-                * Set regulators to default values, ignore any errors,
-                * we're going DOWN
-                */
-               for (i = 0; i < ARRAY_SIZE(ab3100_reg_init_order); i++) {
-                       (void) ab3100_set_register_interruptible(abreg->ab3100,
-                                       ab3100_reg_init_order[i],
-                                       abreg->plfdata->reg_initvals[i]);
-               }
-
                /* Setting LDO D to 0x00 cuts the power to the SoC */
                return ab3100_set_register_interruptible(abreg->ab3100,
                                                         AB3100_LDO_D, 0x00U);
-
        }
 
        /*
@@ -607,13 +595,6 @@ static int __init ab3100_regulators_probe(struct platform_device *pdev)
                }
        }
 
-       if (err) {
-               dev_err(&pdev->dev,
-                       "LDO D regulator initialization failed with error %d\n",
-                       err);
-               return err;
-       }
-
        /* Register the regulators */
        for (i = 0; i < AB3100_NUM_REGULATORS; i++) {
                struct ab3100_regulator *reg = &ab3100_regulators[i];
@@ -688,7 +669,7 @@ static __init int ab3100_regulators_init(void)
 
 static __exit void ab3100_regulators_exit(void)
 {
-       platform_driver_register(&ab3100_regulators_driver);
+       platform_driver_unregister(&ab3100_regulators_driver);
 }
 
 subsys_initcall(ab3100_regulators_init);
index efe568deda12b7710e882e07d81388af9053e484..686ef270ecf7f01d23bf84a85b5f512be4566f10 100644 (file)
@@ -66,6 +66,16 @@ static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
 static void _notifier_call_chain(struct regulator_dev *rdev,
                                  unsigned long event, void *data);
 
+static const char *rdev_get_name(struct regulator_dev *rdev)
+{
+       if (rdev->constraints && rdev->constraints->name)
+               return rdev->constraints->name;
+       else if (rdev->desc->name)
+               return rdev->desc->name;
+       else
+               return "";
+}
+
 /* gets the regulator for a given consumer device */
 static struct regulator *get_device_regulator(struct device *dev)
 {
@@ -96,12 +106,12 @@ static int regulator_check_voltage(struct regulator_dev *rdev,
 
        if (!rdev->constraints) {
                printk(KERN_ERR "%s: no constraints for %s\n", __func__,
-                      rdev->desc->name);
+                      rdev_get_name(rdev));
                return -ENODEV;
        }
        if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) {
                printk(KERN_ERR "%s: operation not allowed for %s\n",
-                      __func__, rdev->desc->name);
+                      __func__, rdev_get_name(rdev));
                return -EPERM;
        }
 
@@ -124,12 +134,12 @@ static int regulator_check_current_limit(struct regulator_dev *rdev,
 
        if (!rdev->constraints) {
                printk(KERN_ERR "%s: no constraints for %s\n", __func__,
-                      rdev->desc->name);
+                      rdev_get_name(rdev));
                return -ENODEV;
        }
        if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_CURRENT)) {
                printk(KERN_ERR "%s: operation not allowed for %s\n",
-                      __func__, rdev->desc->name);
+                      __func__, rdev_get_name(rdev));
                return -EPERM;
        }
 
@@ -159,17 +169,17 @@ static int regulator_check_mode(struct regulator_dev *rdev, int mode)
 
        if (!rdev->constraints) {
                printk(KERN_ERR "%s: no constraints for %s\n", __func__,
-                      rdev->desc->name);
+                      rdev_get_name(rdev));
                return -ENODEV;
        }
        if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_MODE)) {
                printk(KERN_ERR "%s: operation not allowed for %s\n",
-                      __func__, rdev->desc->name);
+                      __func__, rdev_get_name(rdev));
                return -EPERM;
        }
        if (!(rdev->constraints->valid_modes_mask & mode)) {
                printk(KERN_ERR "%s: invalid mode %x for %s\n",
-                      __func__, mode, rdev->desc->name);
+                      __func__, mode, rdev_get_name(rdev));
                return -EINVAL;
        }
        return 0;
@@ -180,12 +190,12 @@ static int regulator_check_drms(struct regulator_dev *rdev)
 {
        if (!rdev->constraints) {
                printk(KERN_ERR "%s: no constraints for %s\n", __func__,
-                      rdev->desc->name);
+                      rdev_get_name(rdev));
                return -ENODEV;
        }
        if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) {
                printk(KERN_ERR "%s: operation not allowed for %s\n",
-                      __func__, rdev->desc->name);
+                      __func__, rdev_get_name(rdev));
                return -EPERM;
        }
        return 0;
@@ -230,16 +240,8 @@ static ssize_t regulator_name_show(struct device *dev,
                             struct device_attribute *attr, char *buf)
 {
        struct regulator_dev *rdev = dev_get_drvdata(dev);
-       const char *name;
 
-       if (rdev->constraints && rdev->constraints->name)
-               name = rdev->constraints->name;
-       else if (rdev->desc->name)
-               name = rdev->desc->name;
-       else
-               name = "";
-
-       return sprintf(buf, "%s\n", name);
+       return sprintf(buf, "%s\n", rdev_get_name(rdev));
 }
 
 static ssize_t regulator_print_opmode(char *buf, int mode)
@@ -388,7 +390,7 @@ static ssize_t regulator_total_uA_show(struct device *dev,
 
        mutex_lock(&rdev->mutex);
        list_for_each_entry(regulator, &rdev->consumer_list, list)
-           uA += regulator->uA_load;
+               uA += regulator->uA_load;
        mutex_unlock(&rdev->mutex);
        return sprintf(buf, "%d\n", uA);
 }
@@ -563,7 +565,7 @@ static void drms_uA_update(struct regulator_dev *rdev)
 
        /* calc total requested load */
        list_for_each_entry(sibling, &rdev->consumer_list, list)
-           current_uA += sibling->uA_load;
+               current_uA += sibling->uA_load;
 
        /* now get the optimum mode for our new total regulator load */
        mode = rdev->desc->ops->get_optimum_mode(rdev, input_uV,
@@ -579,10 +581,29 @@ static int suspend_set_state(struct regulator_dev *rdev,
        struct regulator_state *rstate)
 {
        int ret = 0;
+       bool can_set_state;
 
-       /* enable & disable are mandatory for suspend control */
-       if (!rdev->desc->ops->set_suspend_enable ||
-               !rdev->desc->ops->set_suspend_disable) {
+       can_set_state = rdev->desc->ops->set_suspend_enable &&
+               rdev->desc->ops->set_suspend_disable;
+
+       /* If we have no suspend mode configration don't set anything;
+        * only warn if the driver actually makes the suspend mode
+        * configurable.
+        */
+       if (!rstate->enabled && !rstate->disabled) {
+               if (can_set_state)
+                       printk(KERN_WARNING "%s: No configuration for %s\n",
+                              __func__, rdev_get_name(rdev));
+               return 0;
+       }
+
+       if (rstate->enabled && rstate->disabled) {
+               printk(KERN_ERR "%s: invalid configuration for %s\n",
+                      __func__, rdev_get_name(rdev));
+               return -EINVAL;
+       }
+
+       if (!can_set_state) {
                printk(KERN_ERR "%s: no way to set suspend state\n",
                        __func__);
                return -EINVAL;
@@ -641,25 +662,43 @@ static void print_constraints(struct regulator_dev *rdev)
 {
        struct regulation_constraints *constraints = rdev->constraints;
        char buf[80];
-       int count;
+       int count = 0;
+       int ret;
 
-       if (rdev->desc->type == REGULATOR_VOLTAGE) {
+       if (constraints->min_uV && constraints->max_uV) {
                if (constraints->min_uV == constraints->max_uV)
-                       count = sprintf(buf, "%d mV ",
-                                       constraints->min_uV / 1000);
+                       count += sprintf(buf + count, "%d mV ",
+                                        constraints->min_uV / 1000);
                else
-                       count = sprintf(buf, "%d <--> %d mV ",
-                                       constraints->min_uV / 1000,
-                                       constraints->max_uV / 1000);
-       } else {
+                       count += sprintf(buf + count, "%d <--> %d mV ",
+                                        constraints->min_uV / 1000,
+                                        constraints->max_uV / 1000);
+       }
+
+       if (!constraints->min_uV ||
+           constraints->min_uV != constraints->max_uV) {
+               ret = _regulator_get_voltage(rdev);
+               if (ret > 0)
+                       count += sprintf(buf + count, "at %d mV ", ret / 1000);
+       }
+
+       if (constraints->min_uA && constraints->max_uA) {
                if (constraints->min_uA == constraints->max_uA)
-                       count = sprintf(buf, "%d mA ",
-                                       constraints->min_uA / 1000);
+                       count += sprintf(buf + count, "%d mA ",
+                                        constraints->min_uA / 1000);
                else
-                       count = sprintf(buf, "%d <--> %d mA ",
-                                       constraints->min_uA / 1000,
-                                       constraints->max_uA / 1000);
+                       count += sprintf(buf + count, "%d <--> %d mA ",
+                                        constraints->min_uA / 1000,
+                                        constraints->max_uA / 1000);
        }
+
+       if (!constraints->min_uA ||
+           constraints->min_uA != constraints->max_uA) {
+               ret = _regulator_get_current_limit(rdev);
+               if (ret > 0)
+                       count += sprintf(buf + count, "at %d uA ", ret / 1000);
+       }
+
        if (constraints->valid_modes_mask & REGULATOR_MODE_FAST)
                count += sprintf(buf + count, "fast ");
        if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL)
@@ -669,33 +708,30 @@ static void print_constraints(struct regulator_dev *rdev)
        if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
                count += sprintf(buf + count, "standby");
 
-       printk(KERN_INFO "regulator: %s: %s\n", rdev->desc->name, buf);
+       printk(KERN_INFO "regulator: %s: %s\n", rdev_get_name(rdev), buf);
 }
 
-/**
- * set_machine_constraints - sets regulator constraints
- * @rdev: regulator source
- * @constraints: constraints to apply
- *
- * Allows platform initialisation code to define and constrain
- * regulator circuits e.g. valid voltage/current ranges, etc.  NOTE:
- * Constraints *must* be set by platform code in order for some
- * regulator operations to proceed i.e. set_voltage, set_current_limit,
- * set_mode.
- */
-static int set_machine_constraints(struct regulator_dev *rdev,
+static int machine_constraints_voltage(struct regulator_dev *rdev,
        struct regulation_constraints *constraints)
 {
-       int ret = 0;
-       const char *name;
        struct regulator_ops *ops = rdev->desc->ops;
+       const char *name = rdev_get_name(rdev);
+       int ret;
 
-       if (constraints->name)
-               name = constraints->name;
-       else if (rdev->desc->name)
-               name = rdev->desc->name;
-       else
-               name = "regulator";
+       /* do we need to apply the constraint voltage */
+       if (rdev->constraints->apply_uV &&
+               rdev->constraints->min_uV == rdev->constraints->max_uV &&
+               ops->set_voltage) {
+               ret = ops->set_voltage(rdev,
+                       rdev->constraints->min_uV, rdev->constraints->max_uV);
+                       if (ret < 0) {
+                               printk(KERN_ERR "%s: failed to apply %duV constraint to %s\n",
+                                      __func__,
+                                      rdev->constraints->min_uV, name);
+                               rdev->constraints = NULL;
+                               return ret;
+                       }
+       }
 
        /* constrain machine-level voltage specs to fit
         * the actual range supported by this regulator.
@@ -719,14 +755,13 @@ static int set_machine_constraints(struct regulator_dev *rdev,
 
                /* voltage constraints are optional */
                if ((cmin == 0) && (cmax == 0))
-                       goto out;
+                       return 0;
 
                /* else require explicit machine-level constraints */
                if (cmin <= 0 || cmax <= 0 || cmax < cmin) {
                        pr_err("%s: %s '%s' voltage constraints\n",
                                       __func__, "invalid", name);
-                       ret = -EINVAL;
-                       goto out;
+                       return -EINVAL;
                }
 
                /* initial: [cmin..cmax] valid, [min_uV..max_uV] not */
@@ -748,8 +783,7 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                if (max_uV < min_uV) {
                        pr_err("%s: %s '%s' voltage constraints\n",
                                       __func__, "unsupportable", name);
-                       ret = -EINVAL;
-                       goto out;
+                       return -EINVAL;
                }
 
                /* use regulator's subset of machine constraints */
@@ -767,22 +801,34 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                }
        }
 
+       return 0;
+}
+
+/**
+ * set_machine_constraints - sets regulator constraints
+ * @rdev: regulator source
+ * @constraints: constraints to apply
+ *
+ * Allows platform initialisation code to define and constrain
+ * regulator circuits e.g. valid voltage/current ranges, etc.  NOTE:
+ * Constraints *must* be set by platform code in order for some
+ * regulator operations to proceed i.e. set_voltage, set_current_limit,
+ * set_mode.
+ */
+static int set_machine_constraints(struct regulator_dev *rdev,
+       struct regulation_constraints *constraints)
+{
+       int ret = 0;
+       const char *name;
+       struct regulator_ops *ops = rdev->desc->ops;
+
        rdev->constraints = constraints;
 
-       /* do we need to apply the constraint voltage */
-       if (rdev->constraints->apply_uV &&
-               rdev->constraints->min_uV == rdev->constraints->max_uV &&
-               ops->set_voltage) {
-               ret = ops->set_voltage(rdev,
-                       rdev->constraints->min_uV, rdev->constraints->max_uV);
-                       if (ret < 0) {
-                               printk(KERN_ERR "%s: failed to apply %duV constraint to %s\n",
-                                      __func__,
-                                      rdev->constraints->min_uV, name);
-                               rdev->constraints = NULL;
-                               goto out;
-                       }
-       }
+       name = rdev_get_name(rdev);
+
+       ret = machine_constraints_voltage(rdev, constraints);
+       if (ret != 0)
+               goto out;
 
        /* do we need to setup our suspend state */
        if (constraints->initial_state) {
@@ -903,7 +949,7 @@ static int set_consumer_device_supply(struct regulator_dev *rdev,
                                dev_name(&node->regulator->dev),
                                node->regulator->desc->name,
                                supply,
-                               dev_name(&rdev->dev), rdev->desc->name);
+                               dev_name(&rdev->dev), rdev_get_name(rdev));
                return -EBUSY;
        }
 
@@ -1212,7 +1258,7 @@ static int _regulator_enable(struct regulator_dev *rdev)
                ret = _regulator_enable(rdev->supply);
                if (ret < 0) {
                        printk(KERN_ERR "%s: failed to enable %s: %d\n",
-                              __func__, rdev->desc->name, ret);
+                              __func__, rdev_get_name(rdev), ret);
                        return ret;
                }
        }
@@ -1238,7 +1284,7 @@ static int _regulator_enable(struct regulator_dev *rdev)
                        }
                } else if (ret < 0) {
                        printk(KERN_ERR "%s: is_enabled() failed for %s: %d\n",
-                              __func__, rdev->desc->name, ret);
+                              __func__, rdev_get_name(rdev), ret);
                        return ret;
                }
                /* Fallthrough on positive return values - already enabled */
@@ -1279,7 +1325,7 @@ static int _regulator_disable(struct regulator_dev *rdev)
 
        if (WARN(rdev->use_count <= 0,
                        "unbalanced disables for %s\n",
-                       rdev->desc->name))
+                       rdev_get_name(rdev)))
                return -EIO;
 
        /* are we the last user and permitted to disable ? */
@@ -1292,7 +1338,7 @@ static int _regulator_disable(struct regulator_dev *rdev)
                        ret = rdev->desc->ops->disable(rdev);
                        if (ret < 0) {
                                printk(KERN_ERR "%s: failed to disable %s\n",
-                                      __func__, rdev->desc->name);
+                                      __func__, rdev_get_name(rdev));
                                return ret;
                        }
                }
@@ -1349,7 +1395,7 @@ static int _regulator_force_disable(struct regulator_dev *rdev)
                ret = rdev->desc->ops->disable(rdev);
                if (ret < 0) {
                        printk(KERN_ERR "%s: failed to force disable %s\n",
-                              __func__, rdev->desc->name);
+                              __func__, rdev_get_name(rdev));
                        return ret;
                }
                /* notify other consumers that power has been forced off */
@@ -1766,7 +1812,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
        output_uV = rdev->desc->ops->get_voltage(rdev);
        if (output_uV <= 0) {
                printk(KERN_ERR "%s: invalid output voltage found for %s\n",
-                       __func__, rdev->desc->name);
+                       __func__, rdev_get_name(rdev));
                goto out;
        }
 
@@ -1777,13 +1823,13 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
                input_uV = rdev->constraints->input_uV;
        if (input_uV <= 0) {
                printk(KERN_ERR "%s: invalid input voltage found for %s\n",
-                       __func__, rdev->desc->name);
+                       __func__, rdev_get_name(rdev));
                goto out;
        }
 
        /* calc total requested load for this regulator */
        list_for_each_entry(consumer, &rdev->consumer_list, list)
-           total_uA_load += consumer->uA_load;
+               total_uA_load += consumer->uA_load;
 
        mode = rdev->desc->ops->get_optimum_mode(rdev,
                                                 input_uV, output_uV,
@@ -1791,7 +1837,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
        ret = regulator_check_mode(rdev, mode);
        if (ret < 0) {
                printk(KERN_ERR "%s: failed to get optimum mode for %s @"
-                       " %d uA %d -> %d uV\n", __func__, rdev->desc->name,
+                       " %d uA %d -> %d uV\n", __func__, rdev_get_name(rdev),
                        total_uA_load, input_uV, output_uV);
                goto out;
        }
@@ -1799,7 +1845,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
        ret = rdev->desc->ops->set_mode(rdev, mode);
        if (ret < 0) {
                printk(KERN_ERR "%s: failed to set optimum mode %x for %s\n",
-                       __func__, mode, rdev->desc->name);
+                       __func__, mode, rdev_get_name(rdev));
                goto out;
        }
        ret = mode;
@@ -1852,9 +1898,9 @@ static void _notifier_call_chain(struct regulator_dev *rdev,
 
        /* now notify regulator we supply */
        list_for_each_entry(_rdev, &rdev->supply_list, slist) {
-         mutex_lock(&_rdev->mutex);
-         _notifier_call_chain(_rdev, event, data);
-         mutex_unlock(&_rdev->mutex);
+               mutex_lock(&_rdev->mutex);
+               _notifier_call_chain(_rdev, event, data);
+               mutex_unlock(&_rdev->mutex);
        }
 }
 
@@ -1885,9 +1931,9 @@ int regulator_bulk_get(struct device *dev, int num_consumers,
                consumers[i].consumer = regulator_get(dev,
                                                      consumers[i].supply);
                if (IS_ERR(consumers[i].consumer)) {
-                       dev_err(dev, "Failed to get supply '%s'\n",
-                               consumers[i].supply);
                        ret = PTR_ERR(consumers[i].consumer);
+                       dev_err(dev, "Failed to get supply '%s': %d\n",
+                               consumers[i].supply, ret);
                        consumers[i].consumer = NULL;
                        goto err;
                }
@@ -1930,8 +1976,8 @@ int regulator_bulk_enable(int num_consumers,
        return 0;
 
 err:
-       printk(KERN_ERR "Failed to enable %s\n", consumers[i].supply);
-       for (i = 0; i < num_consumers; i++)
+       printk(KERN_ERR "Failed to enable %s: %d\n", consumers[i].supply, ret);
+       for (--i; i >= 0; --i)
                regulator_disable(consumers[i].consumer);
 
        return ret;
@@ -1965,8 +2011,9 @@ int regulator_bulk_disable(int num_consumers,
        return 0;
 
 err:
-       printk(KERN_ERR "Failed to disable %s\n", consumers[i].supply);
-       for (i = 0; i < num_consumers; i++)
+       printk(KERN_ERR "Failed to disable %s: %d\n", consumers[i].supply,
+              ret);
+       for (--i; i >= 0; --i)
                regulator_enable(consumers[i].consumer);
 
        return ret;
@@ -2316,7 +2363,7 @@ int regulator_suspend_prepare(suspend_state_t state)
 
                if (ret < 0) {
                        printk(KERN_ERR "%s: failed to prepare %s\n",
-                               __func__, rdev->desc->name);
+                               __func__, rdev_get_name(rdev));
                        goto out;
                }
        }
@@ -2429,12 +2476,7 @@ static int __init regulator_init_complete(void)
                ops = rdev->desc->ops;
                c = rdev->constraints;
 
-               if (c && c->name)
-                       name = c->name;
-               else if (rdev->desc->name)
-                       name = rdev->desc->name;
-               else
-                       name = "regulator";
+               name = rdev_get_name(rdev);
 
                if (!ops->disable || (c && c->always_on))
                        continue;
index aa224d936e0de4c92665faf14115bc5592e63bde..f8c4661a7a81ae82705b51691669e230553203b1 100644 (file)
@@ -331,7 +331,7 @@ static int da9034_get_ldo12_voltage(struct regulator_dev *rdev)
 static int da9034_list_ldo12_voltage(struct regulator_dev *rdev,
                                     unsigned selector)
 {
-       if (selector > ARRAY_SIZE(da9034_ldo12_data))
+       if (selector >= ARRAY_SIZE(da9034_ldo12_data))
                return -EINVAL;
        return da9034_ldo12_data[selector] * 1000;
 }
index 7803a320543b1da4f9da2d64ed33e0f108b2f634..76d08c282f9cac3a54133acc30b88fda81b37738 100644 (file)
@@ -446,8 +446,8 @@ static int setup_regulators(struct lp3971 *lp3971,
                lp3971->rdev[i] = regulator_register(&regulators[id],
                        lp3971->dev, pdata->regulators[i].initdata, lp3971);
 
-               err = IS_ERR(lp3971->rdev[i]);
-               if (err) {
+               if (IS_ERR(lp3971->rdev[i])) {
+                       err = PTR_ERR(lp3971->rdev[i]);
                        dev_err(lp3971->dev, "regulator init failed: %d\n",
                                err);
                        goto error;
diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c
new file mode 100644 (file)
index 0000000..acc2fb7
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * max8660.c  --  Voltage regulation for the Maxim 8660/8661
+ *
+ * based on max1586.c and wm8400-regulator.c
+ *
+ * Copyright (C) 2009 Wolfram Sang, Pengutronix e.K.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Some info:
+ *
+ * Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8660-MAX8661.pdf
+ *
+ * This chip is a bit nasty because it is a write-only device. Thus, the driver
+ * uses shadow registers to keep track of its values. The main problem appears
+ * to be the initialization: When Linux boots up, we cannot know if the chip is
+ * in the default state or not, so we would have to pass such information in
+ * platform_data. As this adds a bit of complexity to the driver, this is left
+ * out for now until it is really needed.
+ *
+ * [A|S|M]DTV1 registers are currently not used, but [A|S|M]DTV2.
+ *
+ * If the driver is feature complete, it might be worth to check if one set of
+ * functions for V3-V7 is sufficient. For maximum flexibility during
+ * development, they are separated for now.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/max8660.h>
+
+#define MAX8660_DCDC_MIN_UV     725000
+#define MAX8660_DCDC_MAX_UV    1800000
+#define MAX8660_DCDC_STEP        25000
+#define MAX8660_DCDC_MAX_SEL   0x2b
+
+#define MAX8660_LDO5_MIN_UV    1700000
+#define MAX8660_LDO5_MAX_UV    2000000
+#define MAX8660_LDO5_STEP        25000
+#define MAX8660_LDO5_MAX_SEL   0x0c
+
+#define MAX8660_LDO67_MIN_UV   1800000
+#define MAX8660_LDO67_MAX_UV   3300000
+#define MAX8660_LDO67_STEP      100000
+#define MAX8660_LDO67_MAX_SEL  0x0f
+
+enum {
+       MAX8660_OVER1,
+       MAX8660_OVER2,
+       MAX8660_VCC1,
+       MAX8660_ADTV1,
+       MAX8660_ADTV2,
+       MAX8660_SDTV1,
+       MAX8660_SDTV2,
+       MAX8660_MDTV1,
+       MAX8660_MDTV2,
+       MAX8660_L12VCR,
+       MAX8660_FPWM,
+       MAX8660_N_REGS, /* not a real register */
+};
+
+struct max8660 {
+       struct i2c_client *client;
+       u8 shadow_regs[MAX8660_N_REGS];         /* as chip is write only */
+       struct regulator_dev *rdev[];
+};
+
+static int max8660_write(struct max8660 *max8660, u8 reg, u8 mask, u8 val)
+{
+       static const u8 max8660_addresses[MAX8660_N_REGS] =
+         { 0x10, 0x12, 0x20, 0x23, 0x24, 0x29, 0x2a, 0x32, 0x33, 0x39, 0x80 };
+
+       int ret;
+       u8 reg_val = (max8660->shadow_regs[reg] & mask) | val;
+       dev_vdbg(&max8660->client->dev, "Writing reg %02x with %02x\n",
+                       max8660_addresses[reg], reg_val);
+
+       ret = i2c_smbus_write_byte_data(max8660->client,
+                       max8660_addresses[reg], reg_val);
+       if (ret == 0)
+               max8660->shadow_regs[reg] = reg_val;
+
+       return ret;
+}
+
+
+/*
+ * DCDC functions
+ */
+
+static int max8660_dcdc_is_enabled(struct regulator_dev *rdev)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 val = max8660->shadow_regs[MAX8660_OVER1];
+       u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4;
+       return !!(val & mask);
+}
+
+static int max8660_dcdc_enable(struct regulator_dev *rdev)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 bit = (rdev_get_id(rdev) == MAX8660_V3) ? 1 : 4;
+       return max8660_write(max8660, MAX8660_OVER1, 0xff, bit);
+}
+
+static int max8660_dcdc_disable(struct regulator_dev *rdev)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 mask = (rdev_get_id(rdev) == MAX8660_V3) ? ~1 : ~4;
+       return max8660_write(max8660, MAX8660_OVER1, mask, 0);
+}
+
+static int max8660_dcdc_list(struct regulator_dev *rdev, unsigned selector)
+{
+       if (selector > MAX8660_DCDC_MAX_SEL)
+               return -EINVAL;
+       return MAX8660_DCDC_MIN_UV + selector * MAX8660_DCDC_STEP;
+}
+
+static int max8660_dcdc_get(struct regulator_dev *rdev)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2;
+       u8 selector = max8660->shadow_regs[reg];
+       return MAX8660_DCDC_MIN_UV + selector * MAX8660_DCDC_STEP;
+}
+
+static int max8660_dcdc_set(struct regulator_dev *rdev, int min_uV, int max_uV)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 reg, selector, bits;
+       int ret;
+
+       if (min_uV < MAX8660_DCDC_MIN_UV || min_uV > MAX8660_DCDC_MAX_UV)
+               return -EINVAL;
+       if (max_uV < MAX8660_DCDC_MIN_UV || max_uV > MAX8660_DCDC_MAX_UV)
+               return -EINVAL;
+
+       selector = (min_uV - (MAX8660_DCDC_MIN_UV - MAX8660_DCDC_STEP + 1))
+                       / MAX8660_DCDC_STEP;
+
+       ret = max8660_dcdc_list(rdev, selector);
+       if (ret < 0 || ret > max_uV)
+               return -EINVAL;
+
+       reg = (rdev_get_id(rdev) == MAX8660_V3) ? MAX8660_ADTV2 : MAX8660_SDTV2;
+       ret = max8660_write(max8660, reg, 0, selector);
+       if (ret)
+               return ret;
+
+       /* Select target voltage register and activate regulation */
+       bits = (rdev_get_id(rdev) == MAX8660_V3) ? 0x03 : 0x30;
+       return max8660_write(max8660, MAX8660_VCC1, 0xff, bits);
+}
+
+static struct regulator_ops max8660_dcdc_ops = {
+       .is_enabled = max8660_dcdc_is_enabled,
+       .list_voltage = max8660_dcdc_list,
+       .set_voltage = max8660_dcdc_set,
+       .get_voltage = max8660_dcdc_get,
+};
+
+
+/*
+ * LDO5 functions
+ */
+
+static int max8660_ldo5_list(struct regulator_dev *rdev, unsigned selector)
+{
+       if (selector > MAX8660_LDO5_MAX_SEL)
+               return -EINVAL;
+       return MAX8660_LDO5_MIN_UV + selector * MAX8660_LDO5_STEP;
+}
+
+static int max8660_ldo5_get(struct regulator_dev *rdev)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 selector = max8660->shadow_regs[MAX8660_MDTV2];
+
+       return MAX8660_LDO5_MIN_UV + selector * MAX8660_LDO5_STEP;
+}
+
+static int max8660_ldo5_set(struct regulator_dev *rdev, int min_uV, int max_uV)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 selector;
+       int ret;
+
+       if (min_uV < MAX8660_LDO5_MIN_UV || min_uV > MAX8660_LDO5_MAX_UV)
+               return -EINVAL;
+       if (max_uV < MAX8660_LDO5_MIN_UV || max_uV > MAX8660_LDO5_MAX_UV)
+               return -EINVAL;
+
+       selector = (min_uV - (MAX8660_LDO5_MIN_UV - MAX8660_LDO5_STEP + 1))
+                       / MAX8660_LDO5_STEP;
+       ret = max8660_ldo5_list(rdev, selector);
+       if (ret < 0 || ret > max_uV)
+               return -EINVAL;
+
+       ret = max8660_write(max8660, MAX8660_MDTV2, 0, selector);
+       if (ret)
+               return ret;
+
+       /* Select target voltage register and activate regulation */
+       return max8660_write(max8660, MAX8660_VCC1, 0xff, 0xc0);
+}
+
+static struct regulator_ops max8660_ldo5_ops = {
+       .list_voltage = max8660_ldo5_list,
+       .set_voltage = max8660_ldo5_set,
+       .get_voltage = max8660_ldo5_get,
+};
+
+
+/*
+ * LDO67 functions
+ */
+
+static int max8660_ldo67_is_enabled(struct regulator_dev *rdev)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 val = max8660->shadow_regs[MAX8660_OVER2];
+       u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4;
+       return !!(val & mask);
+}
+
+static int max8660_ldo67_enable(struct regulator_dev *rdev)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 bit = (rdev_get_id(rdev) == MAX8660_V6) ? 2 : 4;
+       return max8660_write(max8660, MAX8660_OVER2, 0xff, bit);
+}
+
+static int max8660_ldo67_disable(struct regulator_dev *rdev)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 mask = (rdev_get_id(rdev) == MAX8660_V6) ? ~2 : ~4;
+       return max8660_write(max8660, MAX8660_OVER2, mask, 0);
+}
+
+static int max8660_ldo67_list(struct regulator_dev *rdev, unsigned selector)
+{
+       if (selector > MAX8660_LDO67_MAX_SEL)
+               return -EINVAL;
+       return MAX8660_LDO67_MIN_UV + selector * MAX8660_LDO67_STEP;
+}
+
+static int max8660_ldo67_get(struct regulator_dev *rdev)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 shift = (rdev_get_id(rdev) == MAX8660_V6) ? 0 : 4;
+       u8 selector = (max8660->shadow_regs[MAX8660_L12VCR] >> shift) & 0xf;
+
+       return MAX8660_LDO67_MIN_UV + selector * MAX8660_LDO67_STEP;
+}
+
+static int max8660_ldo67_set(struct regulator_dev *rdev, int min_uV, int max_uV)
+{
+       struct max8660 *max8660 = rdev_get_drvdata(rdev);
+       u8 selector;
+       int ret;
+
+       if (min_uV < MAX8660_LDO67_MIN_UV || min_uV > MAX8660_LDO67_MAX_UV)
+               return -EINVAL;
+       if (max_uV < MAX8660_LDO67_MIN_UV || max_uV > MAX8660_LDO67_MAX_UV)
+               return -EINVAL;
+
+       selector = (min_uV - (MAX8660_LDO67_MIN_UV - MAX8660_LDO67_STEP + 1))
+                       / MAX8660_LDO67_STEP;
+
+       ret = max8660_ldo67_list(rdev, selector);
+       if (ret < 0 || ret > max_uV)
+               return -EINVAL;
+
+       if (rdev_get_id(rdev) == MAX8660_V6)
+               return max8660_write(max8660, MAX8660_L12VCR, 0xf0, selector);
+       else
+               return max8660_write(max8660, MAX8660_L12VCR, 0x0f, selector << 4);
+}
+
+static struct regulator_ops max8660_ldo67_ops = {
+       .is_enabled = max8660_ldo67_is_enabled,
+       .enable = max8660_ldo67_enable,
+       .disable = max8660_ldo67_disable,
+       .list_voltage = max8660_ldo67_list,
+       .get_voltage = max8660_ldo67_get,
+       .set_voltage = max8660_ldo67_set,
+};
+
+static struct regulator_desc max8660_reg[] = {
+       {
+               .name = "V3(DCDC)",
+               .id = MAX8660_V3,
+               .ops = &max8660_dcdc_ops,
+               .type = REGULATOR_VOLTAGE,
+               .n_voltages = MAX8660_DCDC_MAX_SEL + 1,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "V4(DCDC)",
+               .id = MAX8660_V4,
+               .ops = &max8660_dcdc_ops,
+               .type = REGULATOR_VOLTAGE,
+               .n_voltages = MAX8660_DCDC_MAX_SEL + 1,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "V5(LDO)",
+               .id = MAX8660_V5,
+               .ops = &max8660_ldo5_ops,
+               .type = REGULATOR_VOLTAGE,
+               .n_voltages = MAX8660_LDO5_MAX_SEL + 1,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "V6(LDO)",
+               .id = MAX8660_V6,
+               .ops = &max8660_ldo67_ops,
+               .type = REGULATOR_VOLTAGE,
+               .n_voltages = MAX8660_LDO67_MAX_SEL + 1,
+               .owner = THIS_MODULE,
+       },
+       {
+               .name = "V7(LDO)",
+               .id = MAX8660_V7,
+               .ops = &max8660_ldo67_ops,
+               .type = REGULATOR_VOLTAGE,
+               .n_voltages = MAX8660_LDO67_MAX_SEL + 1,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int max8660_probe(struct i2c_client *client,
+                             const struct i2c_device_id *i2c_id)
+{
+       struct regulator_dev **rdev;
+       struct max8660_platform_data *pdata = client->dev.platform_data;
+       struct max8660 *max8660;
+       int boot_on, i, id, ret = -EINVAL;
+
+       if (pdata->num_subdevs > MAX8660_V_END) {
+               dev_err(&client->dev, "Too much regulators found!\n");
+               goto out;
+       }
+
+       max8660 = kzalloc(sizeof(struct max8660) +
+                       sizeof(struct regulator_dev *) * MAX8660_V_END,
+                       GFP_KERNEL);
+       if (!max8660) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       max8660->client = client;
+       rdev = max8660->rdev;
+
+       if (pdata->en34_is_high) {
+               /* Simulate always on */
+               max8660->shadow_regs[MAX8660_OVER1] = 5;
+       } else {
+               /* Otherwise devices can be toggled via software */
+               max8660_dcdc_ops.enable = max8660_dcdc_enable;
+               max8660_dcdc_ops.disable = max8660_dcdc_disable;
+       }
+
+       /*
+        * First, set up shadow registers to prevent glitches. As some
+        * registers are shared between regulators, everything must be properly
+        * set up for all regulators in advance.
+        */
+       max8660->shadow_regs[MAX8660_ADTV1] =
+               max8660->shadow_regs[MAX8660_ADTV2] =
+               max8660->shadow_regs[MAX8660_SDTV1] =
+               max8660->shadow_regs[MAX8660_SDTV2] = 0x1b;
+       max8660->shadow_regs[MAX8660_MDTV1] =
+               max8660->shadow_regs[MAX8660_MDTV2] = 0x04;
+
+       for (i = 0; i < pdata->num_subdevs; i++) {
+
+               if (!pdata->subdevs[i].platform_data)
+                       goto err_free;
+
+               boot_on = pdata->subdevs[i].platform_data->constraints.boot_on;
+
+               switch (pdata->subdevs[i].id) {
+               case MAX8660_V3:
+                       if (boot_on)
+                               max8660->shadow_regs[MAX8660_OVER1] |= 1;
+                       break;
+
+               case MAX8660_V4:
+                       if (boot_on)
+                               max8660->shadow_regs[MAX8660_OVER1] |= 4;
+                       break;
+
+               case MAX8660_V5:
+                       break;
+
+               case MAX8660_V6:
+                       if (boot_on)
+                               max8660->shadow_regs[MAX8660_OVER2] |= 2;
+                       break;
+
+               case MAX8660_V7:
+                       if (!strcmp(i2c_id->name, "max8661")) {
+                               dev_err(&client->dev, "Regulator not on this chip!\n");
+                               goto err_free;
+                       }
+
+                       if (boot_on)
+                               max8660->shadow_regs[MAX8660_OVER2] |= 4;
+                       break;
+
+               default:
+                       dev_err(&client->dev, "invalid regulator %s\n",
+                                pdata->subdevs[i].name);
+                       goto err_free;
+               }
+       }
+
+       /* Finally register devices */
+       for (i = 0; i < pdata->num_subdevs; i++) {
+
+               id = pdata->subdevs[i].id;
+
+               rdev[i] = regulator_register(&max8660_reg[id], &client->dev,
+                                            pdata->subdevs[i].platform_data,
+                                            max8660);
+               if (IS_ERR(rdev[i])) {
+                       ret = PTR_ERR(rdev[i]);
+                       dev_err(&client->dev, "failed to register %s\n",
+                               max8660_reg[id].name);
+                       goto err_unregister;
+               }
+       }
+
+       i2c_set_clientdata(client, rdev);
+       dev_info(&client->dev, "Maxim 8660/8661 regulator driver loaded\n");
+       return 0;
+
+err_unregister:
+       while (--i >= 0)
+               regulator_unregister(rdev[i]);
+err_free:
+       kfree(max8660);
+out:
+       return ret;
+}
+
+static int max8660_remove(struct i2c_client *client)
+{
+       struct regulator_dev **rdev = i2c_get_clientdata(client);
+       int i;
+
+       for (i = 0; i < MAX8660_V_END; i++)
+               if (rdev[i])
+                       regulator_unregister(rdev[i]);
+       kfree(rdev);
+       i2c_set_clientdata(client, NULL);
+
+       return 0;
+}
+
+static const struct i2c_device_id max8660_id[] = {
+       { "max8660", 0 },
+       { "max8661", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max8660_id);
+
+static struct i2c_driver max8660_driver = {
+       .probe = max8660_probe,
+       .remove = max8660_remove,
+       .driver         = {
+               .name   = "max8660",
+       },
+       .id_table       = max8660_id,
+};
+
+static int __init max8660_init(void)
+{
+       return i2c_add_driver(&max8660_driver);
+}
+subsys_initcall(max8660_init);
+
+static void __exit max8660_exit(void)
+{
+       i2c_del_driver(&max8660_driver);
+}
+module_exit(max8660_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MAXIM 8660/8661 voltage regulator driver");
+MODULE_AUTHOR("Wolfram Sang");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c
new file mode 100644 (file)
index 0000000..39c4953
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * Regulator Driver for Freescale MC13783 PMIC
+ *
+ * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mfd/mc13783.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+
+#define MC13783_REG_SWITCHERS4                 28
+#define MC13783_REG_SWITCHERS4_PLLEN                   (1 << 18)
+
+#define MC13783_REG_SWITCHERS5                 29
+#define MC13783_REG_SWITCHERS5_SW3EN                   (1 << 20)
+
+#define MC13783_REG_REGULATORMODE0             32
+#define MC13783_REG_REGULATORMODE0_VAUDIOEN            (1 << 0)
+#define MC13783_REG_REGULATORMODE0_VIOHIEN             (1 << 3)
+#define MC13783_REG_REGULATORMODE0_VIOLOEN             (1 << 6)
+#define MC13783_REG_REGULATORMODE0_VDIGEN              (1 << 9)
+#define MC13783_REG_REGULATORMODE0_VGENEN              (1 << 12)
+#define MC13783_REG_REGULATORMODE0_VRFDIGEN            (1 << 15)
+#define MC13783_REG_REGULATORMODE0_VRFREFEN            (1 << 18)
+#define MC13783_REG_REGULATORMODE0_VRFCPEN             (1 << 21)
+
+#define MC13783_REG_REGULATORMODE1             33
+#define MC13783_REG_REGULATORMODE1_VSIMEN              (1 << 0)
+#define MC13783_REG_REGULATORMODE1_VESIMEN             (1 << 3)
+#define MC13783_REG_REGULATORMODE1_VCAMEN              (1 << 6)
+#define MC13783_REG_REGULATORMODE1_VRFBGEN             (1 << 9)
+#define MC13783_REG_REGULATORMODE1_VVIBEN              (1 << 11)
+#define MC13783_REG_REGULATORMODE1_VRF1EN              (1 << 12)
+#define MC13783_REG_REGULATORMODE1_VRF2EN              (1 << 15)
+#define MC13783_REG_REGULATORMODE1_VMMC1EN             (1 << 18)
+#define MC13783_REG_REGULATORMODE1_VMMC2EN             (1 << 21)
+
+#define MC13783_REG_POWERMISC                  34
+#define MC13783_REG_POWERMISC_GPO1EN                   (1 << 6)
+#define MC13783_REG_POWERMISC_GPO2EN                   (1 << 8)
+#define MC13783_REG_POWERMISC_GPO3EN                   (1 << 10)
+#define MC13783_REG_POWERMISC_GPO4EN                   (1 << 12)
+
+struct mc13783_regulator {
+       struct regulator_desc desc;
+       int reg;
+       int enable_bit;
+};
+
+static struct regulator_ops mc13783_regulator_ops;
+
+#define MC13783_DEFINE(prefix, _name, _reg)                            \
+       [MC13783_ ## prefix ## _ ## _name] = {                          \
+               .desc = {                                               \
+                       .name = #prefix "_" #_name,                     \
+                       .ops = &mc13783_regulator_ops,                  \
+                       .type = REGULATOR_VOLTAGE,                      \
+                       .id = MC13783_ ## prefix ## _ ## _name,         \
+                       .owner = THIS_MODULE,                           \
+               },                                                      \
+               .reg = MC13783_REG_ ## _reg,                            \
+               .enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \
+       }
+
+#define MC13783_DEFINE_SW(_name, _reg) MC13783_DEFINE(SW, _name, _reg)
+#define MC13783_DEFINE_REGU(_name, _reg) MC13783_DEFINE(REGU, _name, _reg)
+
+static struct mc13783_regulator mc13783_regulators[] = {
+       MC13783_DEFINE_SW(SW3, SWITCHERS5),
+       MC13783_DEFINE_SW(PLL, SWITCHERS4),
+
+       MC13783_DEFINE_REGU(VAUDIO, REGULATORMODE0),
+       MC13783_DEFINE_REGU(VIOHI, REGULATORMODE0),
+       MC13783_DEFINE_REGU(VIOLO, REGULATORMODE0),
+       MC13783_DEFINE_REGU(VDIG, REGULATORMODE0),
+       MC13783_DEFINE_REGU(VGEN, REGULATORMODE0),
+       MC13783_DEFINE_REGU(VRFDIG, REGULATORMODE0),
+       MC13783_DEFINE_REGU(VRFREF, REGULATORMODE0),
+       MC13783_DEFINE_REGU(VRFCP, REGULATORMODE0),
+       MC13783_DEFINE_REGU(VSIM, REGULATORMODE1),
+       MC13783_DEFINE_REGU(VESIM, REGULATORMODE1),
+       MC13783_DEFINE_REGU(VCAM, REGULATORMODE1),
+       MC13783_DEFINE_REGU(VRFBG, REGULATORMODE1),
+       MC13783_DEFINE_REGU(VVIB, REGULATORMODE1),
+       MC13783_DEFINE_REGU(VRF1, REGULATORMODE1),
+       MC13783_DEFINE_REGU(VRF2, REGULATORMODE1),
+       MC13783_DEFINE_REGU(VMMC1, REGULATORMODE1),
+       MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1),
+       MC13783_DEFINE_REGU(GPO1, POWERMISC),
+       MC13783_DEFINE_REGU(GPO2, POWERMISC),
+       MC13783_DEFINE_REGU(GPO3, POWERMISC),
+       MC13783_DEFINE_REGU(GPO4, POWERMISC),
+};
+
+struct mc13783_regulator_priv {
+       struct mc13783 *mc13783;
+       struct regulator_dev *regulators[];
+};
+
+static int mc13783_regulator_enable(struct regulator_dev *rdev)
+{
+       struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+       int id = rdev_get_id(rdev);
+       int ret;
+
+       dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+       mc13783_lock(priv->mc13783);
+       ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].reg,
+                       mc13783_regulators[id].enable_bit,
+                       mc13783_regulators[id].enable_bit);
+       mc13783_unlock(priv->mc13783);
+
+       return ret;
+}
+
+static int mc13783_regulator_disable(struct regulator_dev *rdev)
+{
+       struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+       int id = rdev_get_id(rdev);
+       int ret;
+
+       dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
+
+       mc13783_lock(priv->mc13783);
+       ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].reg,
+                       mc13783_regulators[id].enable_bit, 0);
+       mc13783_unlock(priv->mc13783);
+
+       return ret;
+}
+
+static int mc13783_regulator_is_enabled(struct regulator_dev *rdev)
+{
+       struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev);
+       int ret, id = rdev_get_id(rdev);
+       unsigned int val;
+
+       mc13783_lock(priv->mc13783);
+       ret = mc13783_reg_read(priv->mc13783, mc13783_regulators[id].reg, &val);
+       mc13783_unlock(priv->mc13783);
+
+       if (ret)
+               return ret;
+
+       return (val & mc13783_regulators[id].enable_bit) != 0;
+}
+
+static struct regulator_ops mc13783_regulator_ops = {
+       .enable = mc13783_regulator_enable,
+       .disable = mc13783_regulator_disable,
+       .is_enabled = mc13783_regulator_is_enabled,
+};
+
+static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
+{
+       struct mc13783_regulator_priv *priv;
+       struct mc13783 *mc13783 = dev_get_drvdata(pdev->dev.parent);
+       struct mc13783_regulator_platform_data *pdata =
+               dev_get_platdata(&pdev->dev);
+       struct mc13783_regulator_init_data *init_data;
+       int i, ret;
+
+       dev_dbg(&pdev->dev, "mc13783_regulator_probe id %d\n", pdev->id);
+
+       priv = kzalloc(sizeof(*priv) +
+                       pdata->num_regulators * sizeof(priv->regulators[0]),
+                       GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->mc13783 = mc13783;
+
+       for (i = 0; i < pdata->num_regulators; i++) {
+               init_data = &pdata->regulators[i];
+               priv->regulators[i] = regulator_register(
+                               &mc13783_regulators[init_data->id].desc,
+                               &pdev->dev, init_data->init_data, priv);
+
+               if (IS_ERR(priv->regulators[i])) {
+                       dev_err(&pdev->dev, "failed to register regulator %s\n",
+                               mc13783_regulators[i].desc.name);
+                       ret = PTR_ERR(priv->regulators[i]);
+                       goto err;
+               }
+       }
+
+       platform_set_drvdata(pdev, priv);
+
+       return 0;
+err:
+       while (--i >= 0)
+               regulator_unregister(priv->regulators[i]);
+
+       kfree(priv);
+
+       return ret;
+}
+
+static int __devexit mc13783_regulator_remove(struct platform_device *pdev)
+{
+       struct mc13783_regulator_priv *priv = platform_get_drvdata(pdev);
+       struct mc13783_regulator_platform_data *pdata =
+               dev_get_platdata(&pdev->dev);
+       int i;
+
+       for (i = 0; i < pdata->num_regulators; i++)
+               regulator_unregister(priv->regulators[i]);
+
+       return 0;
+}
+
+static struct platform_driver mc13783_regulator_driver = {
+       .driver = {
+               .name   = "mc13783-regulator",
+               .owner  = THIS_MODULE,
+       },
+       .remove         = __devexit_p(mc13783_regulator_remove),
+       .probe          = mc13783_regulator_probe,
+};
+
+static int __init mc13783_regulator_init(void)
+{
+       return platform_driver_register(&mc13783_regulator_driver);
+}
+subsys_initcall(mc13783_regulator_init);
+
+static void __exit mc13783_regulator_exit(void)
+{
+       platform_driver_unregister(&mc13783_regulator_driver);
+}
+module_exit(mc13783_regulator_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
+MODULE_DESCRIPTION("Regulator Driver for Freescale MC13783 PMIC");
+MODULE_ALIAS("platform:mc13783-regulator");
diff --git a/drivers/regulator/mc13783.c b/drivers/regulator/mc13783.c
deleted file mode 100644 (file)
index 710211f..0000000
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Regulator Driver for Freescale MC13783 PMIC
- *
- * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/mfd/mc13783-private.h>
-#include <linux/regulator/machine.h>
-#include <linux/regulator/driver.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/mc13783.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/err.h>
-
-struct mc13783_regulator {
-       struct regulator_desc desc;
-       int reg;
-       int enable_bit;
-};
-
-static struct regulator_ops mc13783_regulator_ops;
-
-static struct mc13783_regulator mc13783_regulators[] = {
-       [MC13783_SW_SW3] = {
-               .desc = {
-                       .name   = "SW_SW3",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_SW_SW3,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_SWITCHERS_5,
-               .enable_bit = MC13783_SWCTRL_SW3_EN,
-       },
-       [MC13783_SW_PLL] = {
-               .desc = {
-                       .name   = "SW_PLL",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_SW_PLL,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_SWITCHERS_4,
-               .enable_bit = MC13783_SWCTRL_PLL_EN,
-       },
-       [MC13783_REGU_VAUDIO] = {
-               .desc = {
-                       .name   = "REGU_VAUDIO",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VAUDIO,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_0,
-               .enable_bit = MC13783_REGCTRL_VAUDIO_EN,
-       },
-       [MC13783_REGU_VIOHI] = {
-               .desc = {
-                       .name   = "REGU_VIOHI",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VIOHI,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_0,
-               .enable_bit = MC13783_REGCTRL_VIOHI_EN,
-       },
-       [MC13783_REGU_VIOLO] = {
-               .desc = {
-                       .name   = "REGU_VIOLO",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VIOLO,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_0,
-               .enable_bit = MC13783_REGCTRL_VIOLO_EN,
-       },
-       [MC13783_REGU_VDIG] = {
-               .desc = {
-                       .name   = "REGU_VDIG",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VDIG,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_0,
-               .enable_bit = MC13783_REGCTRL_VDIG_EN,
-       },
-       [MC13783_REGU_VGEN] = {
-               .desc = {
-                       .name   = "REGU_VGEN",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VGEN,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_0,
-               .enable_bit = MC13783_REGCTRL_VGEN_EN,
-       },
-       [MC13783_REGU_VRFDIG] = {
-               .desc = {
-                       .name   = "REGU_VRFDIG",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VRFDIG,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_0,
-               .enable_bit = MC13783_REGCTRL_VRFDIG_EN,
-       },
-       [MC13783_REGU_VRFREF] = {
-               .desc = {
-                       .name   = "REGU_VRFREF",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VRFREF,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_0,
-               .enable_bit = MC13783_REGCTRL_VRFREF_EN,
-       },
-       [MC13783_REGU_VRFCP] = {
-               .desc = {
-                       .name   = "REGU_VRFCP",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VRFCP,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_0,
-               .enable_bit = MC13783_REGCTRL_VRFCP_EN,
-       },
-       [MC13783_REGU_VSIM] = {
-               .desc = {
-                       .name   = "REGU_VSIM",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VSIM,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_1,
-               .enable_bit = MC13783_REGCTRL_VSIM_EN,
-       },
-       [MC13783_REGU_VESIM] = {
-               .desc = {
-                       .name   = "REGU_VESIM",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VESIM,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_1,
-               .enable_bit = MC13783_REGCTRL_VESIM_EN,
-       },
-       [MC13783_REGU_VCAM] = {
-               .desc = {
-                       .name   = "REGU_VCAM",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VCAM,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_1,
-               .enable_bit = MC13783_REGCTRL_VCAM_EN,
-       },
-       [MC13783_REGU_VRFBG] = {
-               .desc = {
-                       .name   = "REGU_VRFBG",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VRFBG,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_1,
-               .enable_bit = MC13783_REGCTRL_VRFBG_EN,
-       },
-       [MC13783_REGU_VVIB] = {
-               .desc = {
-                       .name   = "REGU_VVIB",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VVIB,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_1,
-               .enable_bit = MC13783_REGCTRL_VVIB_EN,
-       },
-       [MC13783_REGU_VRF1] = {
-               .desc = {
-                       .name   = "REGU_VRF1",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VRF1,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_1,
-               .enable_bit = MC13783_REGCTRL_VRF1_EN,
-       },
-       [MC13783_REGU_VRF2] = {
-               .desc = {
-                       .name   = "REGU_VRF2",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VRF2,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_1,
-               .enable_bit = MC13783_REGCTRL_VRF2_EN,
-       },
-       [MC13783_REGU_VMMC1] = {
-               .desc = {
-                       .name   = "REGU_VMMC1",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VMMC1,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_1,
-               .enable_bit = MC13783_REGCTRL_VMMC1_EN,
-       },
-       [MC13783_REGU_VMMC2] = {
-               .desc = {
-                       .name   = "REGU_VMMC2",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_VMMC2,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_REGULATOR_MODE_1,
-               .enable_bit = MC13783_REGCTRL_VMMC2_EN,
-       },
-       [MC13783_REGU_GPO1] = {
-               .desc = {
-                       .name   = "REGU_GPO1",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_GPO1,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_POWER_MISCELLANEOUS,
-               .enable_bit = MC13783_REGCTRL_GPO1_EN,
-       },
-       [MC13783_REGU_GPO2] = {
-               .desc = {
-                       .name   = "REGU_GPO2",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_GPO2,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_POWER_MISCELLANEOUS,
-               .enable_bit = MC13783_REGCTRL_GPO2_EN,
-       },
-       [MC13783_REGU_GPO3] = {
-               .desc = {
-                       .name   = "REGU_GPO3",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_GPO3,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_POWER_MISCELLANEOUS,
-               .enable_bit = MC13783_REGCTRL_GPO3_EN,
-       },
-       [MC13783_REGU_GPO4] = {
-               .desc = {
-                       .name   = "REGU_GPO4",
-                       .ops    = &mc13783_regulator_ops,
-                       .type   = REGULATOR_VOLTAGE,
-                       .id     = MC13783_REGU_GPO4,
-                       .owner  = THIS_MODULE,
-               },
-               .reg = MC13783_REG_POWER_MISCELLANEOUS,
-               .enable_bit = MC13783_REGCTRL_GPO4_EN,
-       },
-};
-
-struct mc13783_priv {
-       struct regulator_desc desc[ARRAY_SIZE(mc13783_regulators)];
-       struct mc13783 *mc13783;
-       struct regulator_dev *regulators[0];
-};
-
-static int mc13783_enable(struct regulator_dev *rdev)
-{
-       struct mc13783_priv *priv = rdev_get_drvdata(rdev);
-       int id = rdev_get_id(rdev);
-
-       dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
-
-       return mc13783_set_bits(priv->mc13783, mc13783_regulators[id].reg,
-                       mc13783_regulators[id].enable_bit,
-                       mc13783_regulators[id].enable_bit);
-}
-
-static int mc13783_disable(struct regulator_dev *rdev)
-{
-       struct mc13783_priv *priv = rdev_get_drvdata(rdev);
-       int id = rdev_get_id(rdev);
-
-       dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
-
-       return mc13783_set_bits(priv->mc13783, mc13783_regulators[id].reg,
-                       mc13783_regulators[id].enable_bit, 0);
-}
-
-static int mc13783_is_enabled(struct regulator_dev *rdev)
-{
-       struct mc13783_priv *priv = rdev_get_drvdata(rdev);
-       int ret, id = rdev_get_id(rdev);
-       unsigned int val;
-
-       ret = mc13783_reg_read(priv->mc13783, mc13783_regulators[id].reg, &val);
-       if (ret)
-               return ret;
-
-       return (val & mc13783_regulators[id].enable_bit) != 0;
-}
-
-static struct regulator_ops mc13783_regulator_ops = {
-       .enable         = mc13783_enable,
-       .disable        = mc13783_disable,
-       .is_enabled     = mc13783_is_enabled,
-};
-
-static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
-{
-       struct mc13783_priv *priv;
-       struct mc13783 *mc13783 = dev_get_drvdata(pdev->dev.parent);
-       struct mc13783_regulator_init_data *init_data;
-       int i, ret;
-
-       dev_dbg(&pdev->dev, "mc13783_regulator_probe id %d\n", pdev->id);
-
-       priv = kzalloc(sizeof(*priv) + mc13783->num_regulators * sizeof(void *),
-                       GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       priv->mc13783 = mc13783;
-
-       for (i = 0; i < mc13783->num_regulators; i++) {
-               init_data = &mc13783->regulators[i];
-               priv->regulators[i] = regulator_register(
-                               &mc13783_regulators[init_data->id].desc,
-                               &pdev->dev, init_data->init_data, priv);
-
-               if (IS_ERR(priv->regulators[i])) {
-                       dev_err(&pdev->dev, "failed to register regulator %s\n",
-                               mc13783_regulators[i].desc.name);
-                       ret = PTR_ERR(priv->regulators[i]);
-                       goto err;
-               }
-       }
-
-       platform_set_drvdata(pdev, priv);
-
-       return 0;
-err:
-       while (--i >= 0)
-               regulator_unregister(priv->regulators[i]);
-
-       kfree(priv);
-
-       return ret;
-}
-
-static int __devexit mc13783_regulator_remove(struct platform_device *pdev)
-{
-       struct mc13783_priv *priv = platform_get_drvdata(pdev);
-       struct mc13783 *mc13783 = priv->mc13783;
-       int i;
-
-       for (i = 0; i < mc13783->num_regulators; i++)
-               regulator_unregister(priv->regulators[i]);
-
-       return 0;
-}
-
-static struct platform_driver mc13783_regulator_driver = {
-       .driver = {
-               .name   = "mc13783-regulator",
-               .owner  = THIS_MODULE,
-       },
-       .remove         = __devexit_p(mc13783_regulator_remove),
-};
-
-static int __init mc13783_regulator_init(void)
-{
-       return platform_driver_probe(&mc13783_regulator_driver,
-                       mc13783_regulator_probe);
-}
-subsys_initcall(mc13783_regulator_init);
-
-static void __exit mc13783_regulator_exit(void)
-{
-       platform_driver_unregister(&mc13783_regulator_driver);
-}
-module_exit(mc13783_regulator_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de");
-MODULE_DESCRIPTION("Regulator Driver for Freescale MC13783 PMIC");
-MODULE_ALIAS("platform:mc13783-regulator");
index 7ea1c3a31081cd656b85ee62a1694ae943d34993..7e674859bd59b366288d43bf9baf2bc13dab8b11 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/err.h>
+#include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
@@ -40,6 +41,12 @@ struct twlreg_info {
        u8                      table_len;
        const u16               *table;
 
+       /* regulator specific turn-on delay */
+       u16                     delay;
+
+       /* State REMAP default configuration */
+       u8                      remap;
+
        /* chip constraints on regulator behavior */
        u16                     min_mV;
 
@@ -128,6 +135,7 @@ static int twlreg_enable(struct regulator_dev *rdev)
 {
        struct twlreg_info      *info = rdev_get_drvdata(rdev);
        int                     grp;
+       int                     ret;
 
        grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP);
        if (grp < 0)
@@ -138,7 +146,11 @@ static int twlreg_enable(struct regulator_dev *rdev)
        else
                grp |= P1_GRP_6030;
 
-       return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
+       ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
+
+       udelay(info->delay);
+
+       return ret;
 }
 
 static int twlreg_disable(struct regulator_dev *rdev)
@@ -151,9 +163,9 @@ static int twlreg_disable(struct regulator_dev *rdev)
                return grp;
 
        if (twl_class_is_4030())
-               grp &= ~P1_GRP_4030;
+               grp &= ~(P1_GRP_4030 | P2_GRP_4030 | P3_GRP_4030);
        else
-               grp &= ~P1_GRP_6030;
+               grp &= ~(P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030);
 
        return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp);
 }
@@ -294,6 +306,18 @@ static const u16 VSIM_VSEL_table[] = {
 static const u16 VDAC_VSEL_table[] = {
        1200, 1300, 1800, 1800,
 };
+static const u16 VDD1_VSEL_table[] = {
+       800, 1450,
+};
+static const u16 VDD2_VSEL_table[] = {
+       800, 1450, 1500,
+};
+static const u16 VIO_VSEL_table[] = {
+       1800, 1850,
+};
+static const u16 VINTANA2_VSEL_table[] = {
+       2500, 2750,
+};
 static const u16 VAUX1_6030_VSEL_table[] = {
        1000, 1300, 1800, 2500,
        2800, 2900, 3000, 3000,
@@ -414,20 +438,30 @@ static struct regulator_ops twlfixed_ops = {
 
 /*----------------------------------------------------------------------*/
 
-#define TWL4030_ADJUSTABLE_LDO(label, offset, num) \
-               TWL_ADJUSTABLE_LDO(label, offset, num, TWL4030)
-#define TWL4030_FIXED_LDO(label, offset, mVolts, num) \
-               TWL_FIXED_LDO(label, offset, mVolts, num, TWL4030)
-#define TWL6030_ADJUSTABLE_LDO(label, offset, num) \
-               TWL_ADJUSTABLE_LDO(label, offset, num, TWL6030)
-#define TWL6030_FIXED_LDO(label, offset, mVolts, num) \
-               TWL_FIXED_LDO(label, offset, mVolts, num, TWL6030)
-
-#define TWL_ADJUSTABLE_LDO(label, offset, num, family) { \
+#define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) \
+               TWL_ADJUSTABLE_LDO(label, offset, num, turnon_delay, \
+                       remap_conf, TWL4030)
+#define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
+                       remap_conf) \
+               TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
+                       remap_conf, TWL4030)
+#define TWL6030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, \
+                       remap_conf) \
+               TWL_ADJUSTABLE_LDO(label, offset, num, turnon_delay, \
+                       remap_conf, TWL6030)
+#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
+                       remap_conf) \
+               TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
+                       remap_conf, TWL6030)
+
+#define TWL_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf, \
+               family) { \
        .base = offset, \
        .id = num, \
        .table_len = ARRAY_SIZE(label##_VSEL_table), \
        .table = label##_VSEL_table, \
+       .delay = turnon_delay, \
+       .remap = remap_conf, \
        .desc = { \
                .name = #label, \
                .id = family##_REG_##label, \
@@ -438,10 +472,13 @@ static struct regulator_ops twlfixed_ops = {
                }, \
        }
 
-#define TWL_FIXED_LDO(label, offset, mVolts, num, family) { \
+#define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \
+               family) { \
        .base = offset, \
        .id = num, \
        .min_mV = mVolts, \
+       .delay = turnon_delay, \
+       .remap = remap_conf, \
        .desc = { \
                .name = #label, \
                .id = family##_REG_##label, \
@@ -457,43 +494,41 @@ static struct regulator_ops twlfixed_ops = {
  * software control over them after boot.
  */
 static struct twlreg_info twl_regs[] = {
-       TWL4030_ADJUSTABLE_LDO(VAUX1, 0x17, 1),
-       TWL4030_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2),
-       TWL4030_ADJUSTABLE_LDO(VAUX2, 0x1b, 2),
-       TWL4030_ADJUSTABLE_LDO(VAUX3, 0x1f, 3),
-       TWL4030_ADJUSTABLE_LDO(VAUX4, 0x23, 4),
-       TWL4030_ADJUSTABLE_LDO(VMMC1, 0x27, 5),
-       TWL4030_ADJUSTABLE_LDO(VMMC2, 0x2b, 6),
-       /*
-       TWL4030_ADJUSTABLE_LDO(VPLL1, 0x2f, 7),
-       */
-       TWL4030_ADJUSTABLE_LDO(VPLL2, 0x33, 8),
-       TWL4030_ADJUSTABLE_LDO(VSIM, 0x37, 9),
-       TWL4030_ADJUSTABLE_LDO(VDAC, 0x3b, 10),
-       /*
-       TWL4030_ADJUSTABLE_LDO(VINTANA1, 0x3f, 11),
-       TWL4030_ADJUSTABLE_LDO(VINTANA2, 0x43, 12),
-       TWL4030_ADJUSTABLE_LDO(VINTDIG, 0x47, 13),
-       TWL4030_SMPS(VIO, 0x4b, 14),
-       TWL4030_SMPS(VDD1, 0x55, 15),
-       TWL4030_SMPS(VDD2, 0x63, 16),
-        */
-       TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17),
-       TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18),
-       TWL4030_FIXED_LDO(VUSB3V1, 0x77, 3100, 19),
+       TWL4030_ADJUSTABLE_LDO(VAUX1, 0x17, 1, 100, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2, 100, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VAUX2, 0x1b, 2, 100, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VAUX3, 0x1f, 3, 100, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VAUX4, 0x23, 4, 100, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VMMC1, 0x27, 5, 100, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VMMC2, 0x2b, 6, 100, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VPLL1, 0x2f, 7, 100, 0x00),
+       TWL4030_ADJUSTABLE_LDO(VPLL2, 0x33, 8, 100, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VSIM, 0x37, 9, 100, 0x00),
+       TWL4030_ADJUSTABLE_LDO(VDAC, 0x3b, 10, 100, 0x08),
+       TWL4030_FIXED_LDO(VINTANA1, 0x3f, 1500, 11, 100, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VINTANA2, 0x43, 12, 100, 0x08),
+       TWL4030_FIXED_LDO(VINTDIG, 0x47, 1500, 13, 100, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VIO, 0x4b, 14, 1000, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VDD1, 0x55, 15, 1000, 0x08),
+       TWL4030_ADJUSTABLE_LDO(VDD2, 0x63, 16, 1000, 0x08),
+       TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17, 100, 0x08),
+       TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18, 100, 0x08),
+       TWL4030_FIXED_LDO(VUSB3V1, 0x77, 3100, 19, 150, 0x08),
        /* VUSBCP is managed *only* by the USB subchip */
 
        /* 6030 REG with base as PMC Slave Misc : 0x0030 */
-       TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1),
-       TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 2),
-       TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 3),
-       TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 4),
-       TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 5),
-       TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 7),
-       TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15),
-       TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16),
-       TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17),
-       TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18)
+       /* Turnon-delay and remap configuration values for 6030 are not
+          verified since the specification is not public */
+       TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1, 0, 0x08),
+       TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 2, 0, 0x08),
+       TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 3, 0, 0x08),
+       TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 4, 0, 0x08),
+       TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 5, 0, 0x08),
+       TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 7, 0, 0x08),
+       TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0, 0x08),
+       TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0, 0x08),
+       TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0, 0x08),
+       TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0, 0x08)
 };
 
 static int twlreg_probe(struct platform_device *pdev)
@@ -525,6 +560,19 @@ static int twlreg_probe(struct platform_device *pdev)
        c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE
                                | REGULATOR_CHANGE_MODE
                                | REGULATOR_CHANGE_STATUS;
+       switch (pdev->id) {
+       case TWL4030_REG_VIO:
+       case TWL4030_REG_VDD1:
+       case TWL4030_REG_VDD2:
+       case TWL4030_REG_VPLL1:
+       case TWL4030_REG_VINTANA1:
+       case TWL4030_REG_VINTANA2:
+       case TWL4030_REG_VINTDIG:
+               c->always_on = true;
+               break;
+       default:
+               break;
+       }
 
        rdev = regulator_register(&info->desc, &pdev->dev, initdata, info);
        if (IS_ERR(rdev)) {
@@ -534,6 +582,9 @@ static int twlreg_probe(struct platform_device *pdev)
        }
        platform_set_drvdata(pdev, rdev);
 
+       twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_REMAP,
+                                               info->remap);
+
        /* NOTE:  many regulators support short-circuit IRQs (presentable
         * as REGULATOR_OVER_CURRENT notifications?) configured via:
         *  - SC_CONFIG
index 2eefc1a0cf08eedcb68235dcd2dcfe0857910151..0a6577577e8d674252ea8b2a025f4a23515c5681 100644 (file)
@@ -19,6 +19,8 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/gpio.h>
 
 #include <linux/mfd/wm831x/core.h>
 #include <linux/mfd/wm831x/regulator.h>
@@ -39,6 +41,7 @@
 #define WM831X_DCDC_CONTROL_2     1
 #define WM831X_DCDC_ON_CONFIG     2
 #define WM831X_DCDC_SLEEP_CONTROL 3
+#define WM831X_DCDC_DVS_CONTROL   4
 
 /*
  * Shared
@@ -50,6 +53,10 @@ struct wm831x_dcdc {
        int base;
        struct wm831x *wm831x;
        struct regulator_dev *regulator;
+       int dvs_gpio;
+       int dvs_gpio_state;
+       int on_vsel;
+       int dvs_vsel;
 };
 
 static int wm831x_dcdc_is_enabled(struct regulator_dev *rdev)
@@ -240,11 +247,9 @@ static int wm831x_buckv_list_voltage(struct regulator_dev *rdev,
        return -EINVAL;
 }
 
-static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg,
-                                        int min_uV, int max_uV)
+static int wm831x_buckv_select_min_voltage(struct regulator_dev *rdev,
+                                          int min_uV, int max_uV)
 {
-       struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
-       struct wm831x *wm831x = dcdc->wm831x;
        u16 vsel;
 
        if (min_uV < 600000)
@@ -257,39 +262,126 @@ static int wm831x_buckv_set_voltage_int(struct regulator_dev *rdev, int reg,
        if (wm831x_buckv_list_voltage(rdev, vsel) > max_uV)
                return -EINVAL;
 
-       return wm831x_set_bits(wm831x, reg, WM831X_DC1_ON_VSEL_MASK, vsel);
+       return vsel;
+}
+
+static int wm831x_buckv_select_max_voltage(struct regulator_dev *rdev,
+                                          int min_uV, int max_uV)
+{
+       u16 vsel;
+
+       if (max_uV < 600000 || max_uV > 1800000)
+               return -EINVAL;
+
+       vsel = ((max_uV - 600000) / 12500) + 8;
+
+       if (wm831x_buckv_list_voltage(rdev, vsel) < min_uV ||
+           wm831x_buckv_list_voltage(rdev, vsel) < max_uV)
+               return -EINVAL;
+
+       return vsel;
+}
+
+static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state)
+{
+       struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+
+       if (state == dcdc->dvs_gpio_state)
+               return 0;
+
+       dcdc->dvs_gpio_state = state;
+       gpio_set_value(dcdc->dvs_gpio, state);
+
+       /* Should wait for DVS state change to be asserted if we have
+        * a GPIO for it, for now assume the device is configured
+        * for the fastest possible transition.
+        */
+
+       return 0;
 }
 
 static int wm831x_buckv_set_voltage(struct regulator_dev *rdev,
-                                    int min_uV, int max_uV)
+                                   int min_uV, int max_uV)
 {
        struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
-       u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
+       struct wm831x *wm831x = dcdc->wm831x;
+       int on_reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
+       int dvs_reg = dcdc->base + WM831X_DCDC_DVS_CONTROL;
+       int vsel, ret;
+
+       vsel = wm831x_buckv_select_min_voltage(rdev, min_uV, max_uV);
+       if (vsel < 0)
+               return vsel;
+
+       /* If this value is already set then do a GPIO update if we can */
+       if (dcdc->dvs_gpio && dcdc->on_vsel == vsel)
+               return wm831x_buckv_set_dvs(rdev, 0);
+
+       if (dcdc->dvs_gpio && dcdc->dvs_vsel == vsel)
+               return wm831x_buckv_set_dvs(rdev, 1);
+
+       /* Always set the ON status to the minimum voltage */
+       ret = wm831x_set_bits(wm831x, on_reg, WM831X_DC1_ON_VSEL_MASK, vsel);
+       if (ret < 0)
+               return ret;
+       dcdc->on_vsel = vsel;
+
+       if (!dcdc->dvs_gpio)
+               return ret;
+
+       /* Kick the voltage transition now */
+       ret = wm831x_buckv_set_dvs(rdev, 0);
+       if (ret < 0)
+               return ret;
+
+       /* Set the high voltage as the DVS voltage.  This is optimised
+        * for CPUfreq usage, most processors will keep the maximum
+        * voltage constant and lower the minimum with the frequency. */
+       vsel = wm831x_buckv_select_max_voltage(rdev, min_uV, max_uV);
+       if (vsel < 0) {
+               /* This should never happen - at worst the same vsel
+                * should be chosen */
+               WARN_ON(vsel < 0);
+               return 0;
+       }
+
+       /* Don't bother if it's the same VSEL we're already using */
+       if (vsel == dcdc->on_vsel)
+               return 0;
 
-       return wm831x_buckv_set_voltage_int(rdev, reg, min_uV, max_uV);
+       ret = wm831x_set_bits(wm831x, dvs_reg, WM831X_DC1_DVS_VSEL_MASK, vsel);
+       if (ret == 0)
+               dcdc->dvs_vsel = vsel;
+       else
+               dev_warn(wm831x->dev, "Failed to set DCDC DVS VSEL: %d\n",
+                        ret);
+
+       return 0;
 }
 
 static int wm831x_buckv_set_suspend_voltage(struct regulator_dev *rdev,
-                                            int uV)
+                                           int uV)
 {
        struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
+       struct wm831x *wm831x = dcdc->wm831x;
        u16 reg = dcdc->base + WM831X_DCDC_SLEEP_CONTROL;
+       int vsel;
+
+       vsel = wm831x_buckv_select_min_voltage(rdev, uV, uV);
+       if (vsel < 0)
+               return vsel;
 
-       return wm831x_buckv_set_voltage_int(rdev, reg, uV, uV);
+       return wm831x_set_bits(wm831x, reg, WM831X_DC1_SLP_VSEL_MASK, vsel);
 }
 
 static int wm831x_buckv_get_voltage(struct regulator_dev *rdev)
 {
        struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
-       struct wm831x *wm831x = dcdc->wm831x;
-       u16 reg = dcdc->base + WM831X_DCDC_ON_CONFIG;
-       int val;
 
-       val = wm831x_reg_read(wm831x, reg);
-       if (val < 0)
-               return val;
-
-       return wm831x_buckv_list_voltage(rdev, val & WM831X_DC1_ON_VSEL_MASK);
+       if (dcdc->dvs_gpio && dcdc->dvs_gpio_state)
+               return wm831x_buckv_list_voltage(rdev, dcdc->dvs_vsel);
+       else
+               return wm831x_buckv_list_voltage(rdev, dcdc->on_vsel);
 }
 
 /* Current limit options */
@@ -346,6 +438,64 @@ static struct regulator_ops wm831x_buckv_ops = {
        .set_suspend_mode = wm831x_dcdc_set_suspend_mode,
 };
 
+/*
+ * Set up DVS control.  We just log errors since we can still run
+ * (with reduced performance) if we fail.
+ */
+static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
+                                           struct wm831x_buckv_pdata *pdata)
+{
+       struct wm831x *wm831x = dcdc->wm831x;
+       int ret;
+       u16 ctrl;
+
+       if (!pdata || !pdata->dvs_gpio)
+               return;
+
+       switch (pdata->dvs_control_src) {
+       case 1:
+               ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT;
+               break;
+       case 2:
+               ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT;
+               break;
+       default:
+               dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n",
+                       pdata->dvs_control_src, dcdc->name);
+               return;
+       }
+
+       ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL,
+                             WM831X_DC1_DVS_SRC_MASK, ctrl);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n",
+                       dcdc->name, ret);
+               return;
+       }
+
+       ret = gpio_request(pdata->dvs_gpio, "DCDC DVS");
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n",
+                       dcdc->name, ret);
+               return;
+       }
+
+       /* gpiolib won't let us read the GPIO status so pick the higher
+        * of the two existing voltages so we take it as platform data.
+        */
+       dcdc->dvs_gpio_state = pdata->dvs_init_state;
+
+       ret = gpio_direction_output(pdata->dvs_gpio, dcdc->dvs_gpio_state);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to enable %s DVS GPIO: %d\n",
+                       dcdc->name, ret);
+               gpio_free(pdata->dvs_gpio);
+               return;
+       }
+
+       dcdc->dvs_gpio = pdata->dvs_gpio;
+}
+
 static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
 {
        struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
@@ -384,6 +534,23 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
        dcdc->desc.ops = &wm831x_buckv_ops;
        dcdc->desc.owner = THIS_MODULE;
 
+       ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to read ON VSEL: %d\n", ret);
+               goto err;
+       }
+       dcdc->on_vsel = ret & WM831X_DC1_ON_VSEL_MASK;
+
+       ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG);
+       if (ret < 0) {
+               dev_err(wm831x->dev, "Failed to read DVS VSEL: %d\n", ret);
+               goto err;
+       }
+       dcdc->dvs_vsel = ret & WM831X_DC1_DVS_VSEL_MASK;
+
+       if (pdata->dcdc[id])
+               wm831x_buckv_dvs_init(dcdc, pdata->dcdc[id]->driver_data);
+
        dcdc->regulator = regulator_register(&dcdc->desc, &pdev->dev,
                                             pdata->dcdc[id], dcdc);
        if (IS_ERR(dcdc->regulator)) {
@@ -422,6 +589,8 @@ err_uv:
 err_regulator:
        regulator_unregister(dcdc->regulator);
 err:
+       if (dcdc->dvs_gpio)
+               gpio_free(dcdc->dvs_gpio);
        kfree(dcdc);
        return ret;
 }
@@ -434,6 +603,8 @@ static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
        wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc);
        wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc);
        regulator_unregister(dcdc->regulator);
+       if (dcdc->dvs_gpio)
+               gpio_free(dcdc->dvs_gpio);
        kfree(dcdc);
 
        return 0;
index 902db56ce099676f6a2a3f2483dd0d7b3c75811f..61e02ac2fda334f9e17dd145cf65d938f816548e 100644 (file)
@@ -470,7 +470,7 @@ static unsigned int wm831x_aldo_get_mode(struct regulator_dev *rdev)
        struct wm831x_ldo *ldo = rdev_get_drvdata(rdev);
        struct wm831x *wm831x = ldo->wm831x;
        int on_reg = ldo->base + WM831X_LDO_ON_CONTROL;
-       unsigned int ret;
+       int ret;
 
        ret = wm831x_reg_read(wm831x, on_reg);
        if (ret < 0)
index 415c228743d562f3e93bc2c67a49b8dfbbe58a5b..fd322aca33ba89a9cec62eb7f3909f813b2267a5 100644 (file)
@@ -41,6 +41,23 @@ struct wm831x_battery_pdata {
        int timeout;        /** Charge cycle timeout, in minutes */
 };
 
+/**
+ * Configuration for the WM831x DC-DC BuckWise convertors.  This
+ * should be passed as driver_data in the regulator_init_data.
+ *
+ * Currently all the configuration is for the fast DVS switching
+ * support of the devices.  This allows MFPs on the device to be
+ * configured as an input to switch between two output voltages,
+ * allowing voltage transitions without the expense of an access over
+ * I2C or SPI buses.
+ */
+struct wm831x_buckv_pdata {
+       int dvs_gpio;        /** CPU GPIO to use for DVS switching */
+       int dvs_control_src; /** Hardware DVS source to use (1 or 2) */
+       int dvs_init_state;  /** DVS state to expect on startup */
+       int dvs_state_gpio;  /** CPU GPIO to use for monitoring status */
+};
+
 /* Sources for status LED configuration.  Values are register values
  * plus 1 to allow for a zero default for preserve.
  */
index 490c5b37b6d7f02f2f25003b4e7f4d4356a4bed9..030d92255c7a09b8fd6175f93e0a1862b96c21a5 100644 (file)
@@ -35,6 +35,8 @@
 #ifndef __LINUX_REGULATOR_CONSUMER_H_
 #define __LINUX_REGULATOR_CONSUMER_H_
 
+#include <linux/device.h>
+
 /*
  * Regulator operating modes.
  *
index 87f5f176d4ef31c521e133a9b24c92471d5d5eeb..234a8476cba8a5acadb627236819e89333838eec 100644 (file)
@@ -43,16 +43,20 @@ struct regulator;
 /**
  * struct regulator_state - regulator state during low power system states
  *
- * This describes a regulators state during a system wide low power state.
+ * This describes a regulators state during a system wide low power
+ * state.  One of enabled or disabled must be set for the
+ * configuration to be applied.
  *
  * @uV: Operating voltage during suspend.
  * @mode: Operating mode during suspend.
  * @enabled: Enabled during suspend.
+ * @disabled: Disabled during suspend.
  */
 struct regulator_state {
        int uV; /* suspend voltage */
        unsigned int mode; /* suspend regulator operating mode */
        int enabled; /* is regulator enabled in this suspend state */
+       int disabled; /* is the regulator disbled in this suspend state */
 };
 
 /**
diff --git a/include/linux/regulator/max8660.h b/include/linux/regulator/max8660.h
new file mode 100644 (file)
index 0000000..9936763
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * max8660.h  --  Voltage regulation for the Maxim 8660/8661
+ *
+ * Copyright (C) 2009 Wolfram Sang, Pengutronix e.K.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __LINUX_REGULATOR_MAX8660_H
+#define __LINUX_REGULATOR_MAX8660_H
+
+#include <linux/regulator/machine.h>
+
+enum {
+       MAX8660_V3,
+       MAX8660_V4,
+       MAX8660_V5,
+       MAX8660_V6,
+       MAX8660_V7,
+       MAX8660_V_END,
+};
+
+/**
+ * max8660_subdev_data - regulator subdev data
+ * @id: regulator id
+ * @name: regulator name
+ * @platform_data: regulator init data
+ */
+struct max8660_subdev_data {
+       int                             id;
+       char                            *name;
+       struct regulator_init_data      *platform_data;
+};
+
+/**
+ * max8660_platform_data - platform data for max8660
+ * @num_subdevs: number of regulators used
+ * @subdevs: pointer to regulators used
+ * @en34_is_high: if EN34 is driven high, regulators cannot be en-/disabled.
+ */
+struct max8660_platform_data {
+       int num_subdevs;
+       struct max8660_subdev_data *subdevs;
+       unsigned en34_is_high:1;
+};
+#endif