summaryrefslogtreecommitdiff
path: root/drivers/regulator
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/Kconfig8
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/ab8500-debug.c18
-rw-r--r--drivers/regulator/ab8500-ext.c292
-rw-r--r--drivers/regulator/ab8500.c12
5 files changed, 313 insertions, 18 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index ec1cee88683..138edc60efc 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -281,6 +281,14 @@ config REGULATOR_AB8500
This driver supports the regulators found on the ST-Ericsson mixed
signal AB8500 PMIC
+config REGULATOR_AB8500_EXT
+ bool "ST-Ericsson AB8500 External Regulators"
+ depends on REGULATOR_AB8500
+ default y if REGULATOR_AB8500
+ help
+ This driver supports the external regulator controls found on the
+ ST-Ericsson mixed signal AB8500 PMIC
+
config REGULATOR_DBX500_PRCMU
bool
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index aff7d7b213e..0f94c582d93 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_AB5500) += ab5500.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
+obj-$(CONFIG_REGULATOR_AB8500_EXT) += ab8500-ext.o
obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
obj-$(CONFIG_REGULATOR_DB5500_PRCMU) += db5500-prcmu.o
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
diff --git a/drivers/regulator/ab8500-debug.c b/drivers/regulator/ab8500-debug.c
index 661386890af..76eff94d136 100644
--- a/drivers/regulator/ab8500-debug.c
+++ b/drivers/regulator/ab8500-debug.c
@@ -1524,14 +1524,13 @@ static struct ab8500_force_reg ab8500_force_reg[] = {
/*
* ExtSupplyRegu (HSI: 0x2a on v2-v40?)
* OTP: 0x15, HSI: 0x28, suspend: 0x28/0x3f (value/mask)
- * [5:4] VExtSupply3Regu[1:0] = 10 = Vext3 off
* [3:2] VExtSupply2Regu[1:0] = 10 = Vext2 in HW control
* [1:0] VExtSupply1Regu[1:0] = 00 = Vext1 off
*/
.name = "ExtSupplyRegu",
.bank = 0x04,
.addr = 0x08,
- .mask = 0x3f,
+ .mask = 0x0f,
.val = 0x08,
},
{
@@ -1750,21 +1749,6 @@ static int __devinit ab8500_regulator_debug_probe(struct platform_device *plf)
}
}
- /*
- * find ExtSupplyRegu register (bank 0x04, addr 0x08)
- * and update value (Vext3 in high power).
- */
- for (i = 0; i < ARRAY_SIZE(ab8500_force_reg); i++) {
- if (ab8500_force_reg[i].bank == 0x04 &&
- ab8500_force_reg[i].addr == 0x08) {
- u8 val, val_mask = 0x30;
-
- val = ab8500_force_reg[i].val;
- val = (val & ~val_mask) | (0x10 & val_mask);
- ab8500_force_reg[i].val = val;
- }
- }
-
/* make suspend-force default if board profile is v5x-power */
boot_info_backupram = ioremap(BOOT_INFO_BACKUPRAM1, 0x4);
diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c
new file mode 100644
index 00000000000..adf8e6b83f2
--- /dev/null
+++ b/drivers/regulator/ab8500-ext.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ *
+ * This file is based on drivers/regulator/ab8500.c
+ *
+ * AB8500 external regulators
+ *
+ * ab8500-ext supports the following regulators:
+ * - VextSupply3
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/regulator/ab8500.h>
+
+/**
+ * struct ab8500_ext_regulator_info - ab8500 regulator information
+ * @dev: device pointer
+ * @desc: regulator description
+ * @regulator_dev: regulator device
+ * @is_enabled: status of regulator (on/off)
+ * @fixed_uV: typical voltage (for fixed voltage supplies)
+ * @update_bank: bank to control on/off
+ * @update_reg: register to control on/off
+ * @update_mask: mask to enable/disable and set mode of regulator
+ * @update_val: bits holding the regulator current mode
+ * @update_val_en: bits to set EN pin active (LPn pin deactive)
+ * normally this means high power mode
+ * @update_val_en_lp: bits to set EN pin active and LPn pin active
+ * normally this means low power mode
+ * @delay: startup delay in ms
+ */
+struct ab8500_ext_regulator_info {
+ struct device *dev;
+ struct regulator_desc desc;
+ struct regulator_dev *regulator;
+ bool is_enabled;
+ int fixed_uV;
+ u8 update_bank;
+ u8 update_reg;
+ u8 update_mask;
+ u8 update_val;
+ u8 update_val_en;
+ u8 update_val_en_lp;
+};
+
+static int ab8500_ext_regulator_enable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, info->update_val);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set enable bits for regulator\n");
+
+ info->is_enabled = true;
+
+ dev_dbg(rdev_get_dev(rdev), "%s-enable (bank, reg, mask, value):"
+ " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, info->update_val);
+
+ return ret;
+}
+
+static int ab8500_ext_regulator_disable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, 0x0);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set disable bits for regulator\n");
+
+ info->is_enabled = false;
+
+ dev_dbg(rdev_get_dev(rdev), "%s-disable (bank, reg, mask, value):"
+ " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, 0x0);
+
+ return ret;
+}
+
+static int ab8500_ext_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = abx500_get_register_interruptible(info->dev,
+ info->update_bank, info->update_reg, &regval);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read 0x%x register\n", info->update_reg);
+ return ret;
+ }
+
+ dev_dbg(rdev_get_dev(rdev), "%s-is_enabled (bank, reg, mask, value):"
+ " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+
+ if (regval & info->update_mask)
+ info->is_enabled = true;
+ else
+ info->is_enabled = false;
+
+ return info->is_enabled;
+}
+
+static int ab8500_ext_fixed_get_voltage(struct regulator_dev *rdev)
+{
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ return info->fixed_uV;
+}
+
+static int ab8500_ext_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ /* return the uV for the fixed regulators */
+ if (info->fixed_uV)
+ return info->fixed_uV;
+
+ return -EINVAL;
+}
+
+static struct regulator_ops ab8500_ext_regulator_ops = {
+ .enable = ab8500_ext_regulator_enable,
+ .disable = ab8500_ext_regulator_disable,
+ .is_enabled = ab8500_ext_regulator_is_enabled,
+ .get_voltage = ab8500_ext_fixed_get_voltage,
+ .list_voltage = ab8500_ext_list_voltage,
+};
+
+
+static struct ab8500_ext_regulator_info
+ ab8500_ext_regulator_info[AB8500_NUM_EXT_REGULATORS] = {
+ [AB8500_EXT_SUPPLY3] = {
+ .desc = {
+ .name = "VEXTSUPPLY3",
+ .ops = &ab8500_ext_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_EXT_SUPPLY3,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 3400000,
+ .update_bank = 0x04,
+ .update_reg = 0x08,
+ .update_mask = 0x30,
+ .update_val = 0x10,
+ .update_val_en = 0x10,
+ .update_val_en_lp = 0x30,
+ },
+};
+
+__devinit int ab8500_ext_regulator_init(struct platform_device *pdev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *ppdata;
+ struct ab8500_regulator_platform_data *pdata;
+ int i, err;
+
+ if (!ab8500) {
+ dev_err(&pdev->dev, "null mfd parent\n");
+ return -EINVAL;
+ }
+ ppdata = dev_get_platdata(ab8500->dev);
+ if (!ppdata) {
+ dev_err(&pdev->dev, "null parent pdata\n");
+ return -EINVAL;
+ }
+
+ pdata = ppdata->regulator;
+ if (!pdata) {
+ dev_err(&pdev->dev, "null pdata\n");
+ return -EINVAL;
+ }
+
+ /* make sure the platform data has the correct size */
+ if (pdata->num_ext_regulator != ARRAY_SIZE(ab8500_ext_regulator_info)) {
+ dev_err(&pdev->dev, "Configuration error: size mismatch.\n");
+ return -EINVAL;
+ }
+
+ /* check for AB8500 2.x */
+ if (abx500_get_chip_id(&pdev->dev) < 0x30) {
+ struct ab8500_ext_regulator_info *info;
+
+ /* VextSupply3LPn is inverted on AB8500 2.x */
+ info = &ab8500_ext_regulator_info[AB8500_EXT_SUPPLY3];
+ info->update_val = 0x30;
+ info->update_val_en = 0x30;
+ info->update_val_en_lp = 0x10;
+ }
+
+ /* register all regulators */
+ for (i = 0; i < ARRAY_SIZE(ab8500_ext_regulator_info); i++) {
+ struct ab8500_ext_regulator_info *info = NULL;
+
+ /* assign per-regulator data */
+ info = &ab8500_ext_regulator_info[i];
+ info->dev = &pdev->dev;
+
+ /* register regulator with framework */
+ info->regulator = regulator_register(&info->desc, &pdev->dev,
+ &pdata->ext_regulator[i], info);
+ if (IS_ERR(info->regulator)) {
+ err = PTR_ERR(info->regulator);
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ info->desc.name);
+ /* when we fail, un-register all earlier regulators */
+ while (--i >= 0) {
+ info = &ab8500_ext_regulator_info[i];
+ regulator_unregister(info->regulator);
+ }
+ return err;
+ }
+
+ dev_dbg(rdev_get_dev(info->regulator),
+ "%s-probed\n", info->desc.name);
+ }
+
+ return 0;
+}
+
+__devexit int ab8500_ext_regulator_exit(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_ext_regulator_info); i++) {
+ struct ab8500_ext_regulator_info *info = NULL;
+ info = &ab8500_ext_regulator_info[i];
+
+ dev_vdbg(rdev_get_dev(info->regulator),
+ "%s-remove\n", info->desc.name);
+
+ regulator_unregister(info->regulator);
+ }
+
+ return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>");
+MODULE_DESCRIPTION("AB8500 external regulator driver");
+MODULE_ALIAS("platform:ab8500-ext-regulator");
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 3ad27a9715d..0480f55b101 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -1162,6 +1162,11 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
}
}
+ /* register external regulators (before Vaux1, 2 and 3) */
+ err = ab8500_ext_regulator_init(pdev);
+ if (err)
+ return err;
+
/* register all regulators */
for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
struct ab8500_regulator_info *info = NULL;
@@ -1206,7 +1211,7 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
{
- int i;
+ int i, err;
for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
struct ab8500_regulator_info *info = NULL;
@@ -1218,6 +1223,11 @@ static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
regulator_unregister(info->regulator);
}
+ /* remove external regulators (after Vaux1, 2 and 3) */
+ err = ab8500_ext_regulator_exit(pdev);
+ if (err)
+ return err;
+
return 0;
}