diff options
-rwxr-xr-x | arch/arm/configs/u8500_defconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-ux500/board-mop500-regulators.c | 30 | ||||
-rw-r--r-- | arch/arm/mach-ux500/board-mop500.c | 2 | ||||
-rw-r--r-- | drivers/misc/sim_detect.c | 144 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 8 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/ab8500-debug.c | 18 | ||||
-rw-r--r-- | drivers/regulator/ab8500-ext.c | 292 | ||||
-rw-r--r-- | drivers/regulator/ab8500.c | 12 | ||||
-rw-r--r-- | include/linux/regulator/ab8500.h | 18 |
10 files changed, 505 insertions, 21 deletions
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig index d7220507f12..9ff6ff9accc 100755 --- a/arch/arm/configs/u8500_defconfig +++ b/arch/arm/configs/u8500_defconfig @@ -244,6 +244,7 @@ CONFIG_AB8500_CORE=y CONFIG_REGULATOR_DEBUG=y CONFIG_REGULATOR_VIRTUAL_CONSUMER=y CONFIG_REGULATOR_AB8500=y +CONFIG_REGULATOR_AB8500_EXT=y CONFIG_REGULATOR_AB8500_DEBUG=y CONFIG_REGULATOR_DB8500_PRCMU=y CONFIG_MEDIA_SUPPORT=y diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c index ebbd7d37bd9..2aab5ec83ff 100644 --- a/arch/arm/mach-ux500/board-mop500-regulators.c +++ b/arch/arm/mach-ux500/board-mop500-regulators.c @@ -353,6 +353,7 @@ static struct ab8500_regulator_reg_init ab8500_reg_init[] = { static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = { /* supplies to the display/camera */ [AB8500_LDO_AUX1] = { + .supply_regulator = "ab8500-ext-supply3", .constraints = { .name = "V-DISPLAY", .min_uV = 2800000, @@ -366,6 +367,7 @@ static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = { }, /* supplies to the on-board eMMC */ [AB8500_LDO_AUX2] = { + .supply_regulator = "ab8500-ext-supply3", .constraints = { .name = "V-eMMC1", .min_uV = 1100000, @@ -381,6 +383,7 @@ static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = { }, /* supply for VAUX3, supplies to SDcard slots */ [AB8500_LDO_AUX3] = { + .supply_regulator = "ab8500-ext-supply3", .constraints = { .name = "V-MMC-SD", .min_uV = 1100000, @@ -487,9 +490,36 @@ static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = { }, }; +/* supply for VextSupply3 */ +static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = { + /* SIM supply for 3 V SIM cards */ + REGULATOR_SUPPLY("vinvsim", "sim-detect.0"), +}; + +/* + * AB8500 external regulators + */ +static struct regulator_init_data ab8500_ext_regulators[] = { + /* fixed Vbat supplies VSMPS3_EXT_3V4 and VSMPS4_EXT_3V4 */ + [AB8500_EXT_SUPPLY3] = { + .constraints = { + .name = "ab8500-ext-supply3", + .min_uV = 3400000, + .max_uV = 3400000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .boot_on = 1, + }, + .num_consumer_supplies = + ARRAY_SIZE(ab8500_ext_supply3_consumers), + .consumer_supplies = ab8500_ext_supply3_consumers, + }, +}; + struct ab8500_regulator_platform_data ab8500_regulator_plat_data = { .reg_init = ab8500_reg_init, .num_reg_init = ARRAY_SIZE(ab8500_reg_init), .regulator = ab8500_regulators, .num_regulator = ARRAY_SIZE(ab8500_regulators), + .ext_regulator = ab8500_ext_regulators, + .num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators), }; diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 63391034ab6..f2e5df78078 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -797,7 +797,7 @@ static struct sim_detect_platform_data sim_detect_pdata = { .irq_num = MOP500_AB8500_VIR_GPIO_IRQ(6), }; struct platform_device u8500_sim_detect_device = { - .name = "sim_detect", + .name = "sim-detect", .id = 0, .dev = { .platform_data = &sim_detect_pdata, diff --git a/drivers/misc/sim_detect.c b/drivers/misc/sim_detect.c index c267fe95ee3..213645ca46d 100644 --- a/drivers/misc/sim_detect.c +++ b/drivers/misc/sim_detect.c @@ -2,29 +2,130 @@ * Copyright (C) ST-Ericsson SA 2011 * * Author: BIBEK BASU <bibek.basu@stericsson.com> + * * License terms: GNU General Public License (GPL) version 2 */ + #include <linux/kernel.h> #include <linux/types.h> #include <linux/init.h> #include <linux/module.h> #include <linux/err.h> +#include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/hrtimer.h> #include <linux/workqueue.h> +#include <linux/uaccess.h> #include <linux/modem/modem_client.h> #include <mach/sim_detect.h> +#include <linux/regulator/consumer.h> /* time in millisec */ #define TIMER_DELAY 10 + struct sim_detect{ struct work_struct timer_expired; struct device *dev; struct modem *modem; struct hrtimer timer; + struct mutex lock; + int voltage; + struct regulator *vinvsim_regulator; + bool regulator_enabled; +}; + +static ssize_t show_voltage(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sim_detect *data = dev_get_drvdata(dev); + int ret, len; + + ret = mutex_lock_interruptible(&data->lock); + if (ret < 0) + return ret; + + len = sprintf(buf, "%i\n", data->voltage); + + mutex_unlock(&data->lock); + + return len; +} + +static ssize_t write_voltage(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sim_detect *sim_detect = dev_get_drvdata(dev); + long val; + int ret; + + /* check input */ + if (strict_strtol(buf, 0, &val) != 0) { + dev_err(dev, "Invalid voltage class configured.\n"); + return count; + } + + switch (val) { + case -1: + case 0: + case 1800000: + case 3000000: + break; + default: + dev_err(dev, "Invalid voltage class configured.\n"); + return count; + } + + /* lock */ + ret = mutex_lock_interruptible(&sim_detect->lock); + if (ret < 0) + return ret; + + /* update state */ + sim_detect->voltage = val; + + /* call regulator */ + switch (sim_detect->voltage) { + case 0: + /* SIM voltage is unknown, turn on regulator for 3 V SIM */ + case 3000000: + /* Vinvsim supply is used only for 3 V SIM */ + if (!sim_detect->regulator_enabled) { + ret = regulator_enable(sim_detect->vinvsim_regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator.\n"); + goto out_unlock; + } + sim_detect->regulator_enabled = true; + } + break; + case 1800000: + case -1: + /* Vbatvsim is used otherwise */ + if (sim_detect->regulator_enabled) { + regulator_disable(sim_detect->vinvsim_regulator); + sim_detect->regulator_enabled = false; + } + } + +out_unlock: + /* unlock and return */ + mutex_unlock(&sim_detect->lock); + + return count; +} + +static DEVICE_ATTR(voltage, 0666, show_voltage, write_voltage); + +static struct attribute *sim_attributes[] = { + &dev_attr_voltage.attr, + NULL +}; + +static const struct attribute_group sim_attr_group = { + .attrs = sim_attributes, }; static void inform_modem_release(struct work_struct *work) @@ -87,6 +188,7 @@ static const struct dev_pm_ops sim_detect_dev_pm_ops = { }; #endif + static int __devinit sim_detect_probe(struct platform_device *pdev) { struct sim_detect_platform_data *plat = dev_get_platdata(&pdev->dev); @@ -98,25 +200,61 @@ static int __devinit sim_detect_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to allocate memory\n"); return -ENOMEM; } + + /* initialize data */ + mutex_init(&sim_detect->lock); + sim_detect->voltage = 0; + sim_detect->dev = &pdev->dev; INIT_WORK(&sim_detect->timer_expired, inform_modem_release); hrtimer_init(&sim_detect->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); sim_detect->timer.function = timer_callback; + sim_detect->modem = modem_get(sim_detect->dev, "u8500-shrm-modem"); if (IS_ERR(sim_detect->modem)) { ret = PTR_ERR(sim_detect->modem); dev_err(sim_detect->dev, "Could not retrieve the modem\n"); goto out_free; } + + /* set drvdata */ platform_set_drvdata(pdev, sim_detect); + + /* request irq */ ret = request_threaded_irq(plat->irq_num, NULL, sim_activity_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "sim activity", sim_detect); if (ret < 0) + goto out_put_modem; + + /* get regulator */ + sim_detect->regulator_enabled = false; + sim_detect->vinvsim_regulator = regulator_get(sim_detect->dev, + "vinvsim"); + if (IS_ERR(sim_detect->vinvsim_regulator)) { + dev_err(&pdev->dev, + "Failed to get regulator. (dev_name %s).\n", + dev_name(sim_detect->dev)); + ret = PTR_ERR(sim_detect->vinvsim_regulator); goto out_free_irq; + } + + /* register sysfs entry */ + ret = sysfs_create_group(&pdev->dev.kobj, &sim_attr_group); + if (ret != 0) { + dev_err(&pdev->dev, + "Failed to create attribute group: %d\n", ret); + goto out_free_regulator; + } + return 0; + +out_free_regulator: + regulator_put(sim_detect->vinvsim_regulator); out_free_irq: + free_irq(plat->irq_num, sim_detect); +out_put_modem: modem_put(sim_detect->modem); platform_set_drvdata(pdev, NULL); out_free: @@ -128,6 +266,8 @@ static int __devexit sim_detect_remove(struct platform_device *pdev) { struct sim_detect *sim_detect = platform_get_drvdata(pdev); + sysfs_remove_group(&pdev->dev.kobj, &sim_attr_group); + regulator_put(sim_detect->vinvsim_regulator); modem_put(sim_detect->modem); platform_set_drvdata(pdev, NULL); kfree(sim_detect); @@ -136,7 +276,7 @@ static int __devexit sim_detect_remove(struct platform_device *pdev) static struct platform_driver sim_detect_driver = { .driver = { - .name = "sim_detect", + .name = "sim-detect", .owner = THIS_MODULE, #ifdef CONFIG_PM .pm = &sim_detect_dev_pm_ops, @@ -160,5 +300,5 @@ module_exit(sim_detect_exit); MODULE_AUTHOR("BIBEK BASU <bibek.basu@stericsson.com>"); MODULE_DESCRIPTION("Detects SIM Hot Swap and wakes modem"); -MODULE_ALIAS("SIM DETECT INTERRUPT driver"); +MODULE_ALIAS("platform:sim-detect"); MODULE_LICENSE("GPL v2"); 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, ®val); + 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; } diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h index 095d94af589..df1e148da73 100644 --- a/include/linux/regulator/ab8500.h +++ b/include/linux/regulator/ab8500.h @@ -10,6 +10,8 @@ #ifndef __LINUX_MFD_AB8500_REGULATOR_H #define __LINUX_MFD_AB8500_REGULATOR_H +#include <linux/platform_device.h> + /* AB8500 regulators */ enum ab8500_regulator_id { AB8500_LDO_AUX1, @@ -86,11 +88,27 @@ enum ab8500_regulator_reg { AB8500_NUM_REGULATOR_REGISTERS, }; +/* AB8500 external regulators */ +enum ab8500_ext_regulator_id { + AB8500_EXT_SUPPLY3, + AB8500_NUM_EXT_REGULATORS, +}; + struct ab8500_regulator_platform_data { int num_reg_init; struct ab8500_regulator_reg_init *reg_init; int num_regulator; struct regulator_init_data *regulator; + int num_ext_regulator; + struct regulator_init_data *ext_regulator; }; +#ifdef CONFIG_REGULATOR_AB8500_EXT +__devinit int ab8500_ext_regulator_init(struct platform_device *pdev); +__devexit int ab8500_ext_regulator_exit(struct platform_device *pdev); +#else +inline __devinit int ab8500_ext_regulator_init(struct platform_device *pdev) {} +inline __devexit int ab8500_ext_regulator_exit(struct platform_device *pdev) {} +#endif + #endif |