From 1bd588fd9d2611d2573066f49477fb5be2aa13de Mon Sep 17 00:00:00 2001 From: Alberto Panizzo Date: Mon, 14 Dec 2009 18:26:38 +0100 Subject: [PATCH 01/27] regulator: add voltage selection capability to mc13783 regulators v2 . This patch, complete the mc13783 regulator subsystem driver with voltage selecting capability. Main Switches (SW1AB, SW2AB) are not supported yet. version 2 diffs: - delete the "Switchers PLL" enable and multiplication factor value selecting capability because it is not a voltage or current regulator. This will be a part of Main switcher supporting task. - Correct many coding style problems pointed me out. Signed-off-by: Alberto Panizzo Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/mc13783-regulator.c | 345 +++++++++++++++++++++++--- 1 file changed, 315 insertions(+), 30 deletions(-) diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index 39c4953000458..a40e35ab85553 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -2,6 +2,7 @@ * Regulator Driver for Freescale MC13783 PMIC * * Copyright (C) 2008 Sascha Hauer, Pengutronix + * Copyright 2009 Alberto Panizzo * * 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 @@ -16,11 +17,44 @@ #include #include -#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_SWITCHERS5_SW3VSEL 18 +#define MC13783_REG_SWITCHERS5_SW3VSEL_M (3 << 18) + +#define MC13783_REG_REGULATORSETTING0 30 +#define MC13783_REG_REGULATORSETTING0_VIOLOVSEL 2 +#define MC13783_REG_REGULATORSETTING0_VDIGVSEL 4 +#define MC13783_REG_REGULATORSETTING0_VGENVSEL 6 +#define MC13783_REG_REGULATORSETTING0_VRFDIGVSEL 9 +#define MC13783_REG_REGULATORSETTING0_VRFREFVSEL 11 +#define MC13783_REG_REGULATORSETTING0_VRFCPVSEL 13 +#define MC13783_REG_REGULATORSETTING0_VSIMVSEL 14 +#define MC13783_REG_REGULATORSETTING0_VESIMVSEL 15 +#define MC13783_REG_REGULATORSETTING0_VCAMVSEL 16 + +#define MC13783_REG_REGULATORSETTING0_VIOLOVSEL_M (3 << 2) +#define MC13783_REG_REGULATORSETTING0_VDIGVSEL_M (3 << 4) +#define MC13783_REG_REGULATORSETTING0_VGENVSEL_M (7 << 6) +#define MC13783_REG_REGULATORSETTING0_VRFDIGVSEL_M (3 << 9) +#define MC13783_REG_REGULATORSETTING0_VRFREFVSEL_M (3 << 11) +#define MC13783_REG_REGULATORSETTING0_VRFCPVSEL_M (1 << 13) +#define MC13783_REG_REGULATORSETTING0_VSIMVSEL_M (1 << 14) +#define MC13783_REG_REGULATORSETTING0_VESIMVSEL_M (1 << 15) +#define MC13783_REG_REGULATORSETTING0_VCAMVSEL_M (7 << 16) + +#define MC13783_REG_REGULATORSETTING1 31 +#define MC13783_REG_REGULATORSETTING1_VVIBVSEL 0 +#define MC13783_REG_REGULATORSETTING1_VRF1VSEL 2 +#define MC13783_REG_REGULATORSETTING1_VRF2VSEL 4 +#define MC13783_REG_REGULATORSETTING1_VMMC1VSEL 6 +#define MC13783_REG_REGULATORSETTING1_VMMC2VSEL 9 + +#define MC13783_REG_REGULATORSETTING1_VVIBVSEL_M (3 << 0) +#define MC13783_REG_REGULATORSETTING1_VRF1VSEL_M (3 << 2) +#define MC13783_REG_REGULATORSETTING1_VRF2VSEL_M (3 << 4) +#define MC13783_REG_REGULATORSETTING1_VMMC1VSEL_M (7 << 6) +#define MC13783_REG_REGULATORSETTING1_VMMC2VSEL_M (7 << 9) #define MC13783_REG_REGULATORMODE0 32 #define MC13783_REG_REGULATORMODE0_VAUDIOEN (1 << 0) @@ -53,14 +87,88 @@ struct mc13783_regulator { struct regulator_desc desc; int reg; int enable_bit; + int vsel_reg; + int vsel_shift; + int vsel_mask; + int const *voltages; +}; + +/* Voltage Values */ +static const int const mc13783_sw3_val[] = { + 5000000, 5000000, 5000000, 5500000, +}; + +static const int const mc13783_vaudio_val[] = { + 2775000, +}; + +static const int const mc13783_viohi_val[] = { + 2775000, +}; + +static const int const mc13783_violo_val[] = { + 1200000, 1300000, 1500000, 1800000, +}; + +static const int const mc13783_vdig_val[] = { + 1200000, 1300000, 1500000, 1800000, +}; + +static const int const mc13783_vgen_val[] = { + 1200000, 1300000, 1500000, 1800000, + 1100000, 2000000, 2775000, 2400000, +}; + +static const int const mc13783_vrfdig_val[] = { + 1200000, 1500000, 1800000, 1875000, +}; + +static const int const mc13783_vrfref_val[] = { + 2475000, 2600000, 2700000, 2775000, +}; + +static const int const mc13783_vrfcp_val[] = { + 2700000, 2775000, +}; + +static const int const mc13783_vsim_val[] = { + 1800000, 2900000, 3000000, +}; + +static const int const mc13783_vesim_val[] = { + 1800000, 2900000, +}; + +static const int const mc13783_vcam_val[] = { + 1500000, 1800000, 2500000, 2550000, + 2600000, 2750000, 2800000, 3000000, +}; + +static const int const mc13783_vrfbg_val[] = { + 1250000, +}; + +static const int const mc13783_vvib_val[] = { + 1300000, 1800000, 2000000, 3000000, +}; + +static const int const mc13783_vmmc_val[] = { + 1600000, 1800000, 2000000, 2600000, + 2700000, 2800000, 2900000, 3000000, +}; + +static const int const mc13783_vrf_val[] = { + 1500000, 1875000, 2700000, 2775000, }; static struct regulator_ops mc13783_regulator_ops; +static struct regulator_ops mc13783_fixed_regulator_ops; -#define MC13783_DEFINE(prefix, _name, _reg) \ +#define MC13783_DEFINE(prefix, _name, _reg, _vsel_reg, _voltages) \ [MC13783_ ## prefix ## _ ## _name] = { \ .desc = { \ .name = #prefix "_" #_name, \ + .n_voltages = ARRAY_SIZE(_voltages), \ .ops = &mc13783_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ .id = MC13783_ ## prefix ## _ ## _name, \ @@ -68,36 +176,83 @@ static struct regulator_ops mc13783_regulator_ops; }, \ .reg = MC13783_REG_ ## _reg, \ .enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \ + .vsel_reg = MC13783_REG_ ## _vsel_reg, \ + .vsel_shift = MC13783_REG_ ## _vsel_reg ## _ ## _name ## VSEL,\ + .vsel_mask = MC13783_REG_ ## _vsel_reg ## _ ## _name ## VSEL_M,\ + .voltages = _voltages, \ } -#define MC13783_DEFINE_SW(_name, _reg) MC13783_DEFINE(SW, _name, _reg) -#define MC13783_DEFINE_REGU(_name, _reg) MC13783_DEFINE(REGU, _name, _reg) +#define MC13783_FIXED_DEFINE(prefix, _name, _reg, _voltages) \ + [MC13783_ ## prefix ## _ ## _name] = { \ + .desc = { \ + .name = #prefix "_" #_name, \ + .n_voltages = ARRAY_SIZE(_voltages), \ + .ops = &mc13783_fixed_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MC13783_ ## prefix ## _ ## _name, \ + .owner = THIS_MODULE, \ + }, \ + .reg = MC13783_REG_ ## _reg, \ + .enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \ + .voltages = _voltages, \ + } + +#define MC13783_GPO_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, _vsel_reg, _voltages) \ + MC13783_DEFINE(SW, _name, _reg, _vsel_reg, _voltages) +#define MC13783_DEFINE_REGU(_name, _reg, _vsel_reg, _voltages) \ + MC13783_DEFINE(REGU, _name, _reg, _vsel_reg, _voltages) 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), + MC13783_DEFINE_SW(SW3, SWITCHERS5, SWITCHERS5, mc13783_sw3_val), + + MC13783_FIXED_DEFINE(REGU, VAUDIO, REGULATORMODE0, mc13783_vaudio_val), + MC13783_FIXED_DEFINE(REGU, VIOHI, REGULATORMODE0, mc13783_viohi_val), + MC13783_DEFINE_REGU(VIOLO, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_violo_val), + MC13783_DEFINE_REGU(VDIG, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_vdig_val), + MC13783_DEFINE_REGU(VGEN, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_vgen_val), + MC13783_DEFINE_REGU(VRFDIG, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_vrfdig_val), + MC13783_DEFINE_REGU(VRFREF, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_vrfref_val), + MC13783_DEFINE_REGU(VRFCP, REGULATORMODE0, REGULATORSETTING0, \ + mc13783_vrfcp_val), + MC13783_DEFINE_REGU(VSIM, REGULATORMODE1, REGULATORSETTING0, \ + mc13783_vsim_val), + MC13783_DEFINE_REGU(VESIM, REGULATORMODE1, REGULATORSETTING0, \ + mc13783_vesim_val), + MC13783_DEFINE_REGU(VCAM, REGULATORMODE1, REGULATORSETTING0, \ + mc13783_vcam_val), + MC13783_FIXED_DEFINE(REGU, VRFBG, REGULATORMODE1, mc13783_vrfbg_val), + MC13783_DEFINE_REGU(VVIB, REGULATORMODE1, REGULATORSETTING1, \ + mc13783_vvib_val), + MC13783_DEFINE_REGU(VRF1, REGULATORMODE1, REGULATORSETTING1, \ + mc13783_vrf_val), + MC13783_DEFINE_REGU(VRF2, REGULATORMODE1, REGULATORSETTING1, \ + mc13783_vrf_val), + MC13783_DEFINE_REGU(VMMC1, REGULATORMODE1, REGULATORSETTING1, \ + mc13783_vmmc_val), + MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1, REGULATORSETTING1, \ + mc13783_vmmc_val), + MC13783_GPO_DEFINE(REGU, GPO1, POWERMISC), + MC13783_GPO_DEFINE(REGU, GPO2, POWERMISC), + MC13783_GPO_DEFINE(REGU, GPO3, POWERMISC), + MC13783_GPO_DEFINE(REGU, GPO4, POWERMISC), }; struct mc13783_regulator_priv { @@ -154,10 +309,140 @@ static int mc13783_regulator_is_enabled(struct regulator_dev *rdev) return (val & mc13783_regulators[id].enable_bit) != 0; } +static int mc13783_regulator_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + int id = rdev_get_id(rdev); + + if (selector >= mc13783_regulators[id].desc.n_voltages) + return -EINVAL; + + return mc13783_regulators[id].voltages[selector]; +} + +static int mc13783_get_best_voltage_index(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int reg_id = rdev_get_id(rdev); + int i; + int bestmatch; + int bestindex; + + /* + * Locate the minimum voltage fitting the criteria on + * this regulator. The switchable voltages are not + * in strict falling order so we need to check them + * all for the best match. + */ + bestmatch = INT_MAX; + bestindex = -1; + for (i = 0; i < mc13783_regulators[reg_id].desc.n_voltages; i++) { + if (mc13783_regulators[reg_id].voltages[i] >= min_uV && + mc13783_regulators[reg_id].voltages[i] < bestmatch) { + bestmatch = mc13783_regulators[reg_id].voltages[i]; + bestindex = i; + } + } + + if (bestindex < 0 || bestmatch > max_uV) { + dev_warn(&rdev->dev, "no possible value for %d<=x<=%d uV\n", + min_uV, max_uV); + return -EINVAL; + } + return bestindex; +} + +static int mc13783_regulator_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev); + int value, id = rdev_get_id(rdev); + int ret; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n", + __func__, id, min_uV, max_uV); + + /* Find the best index */ + value = mc13783_get_best_voltage_index(rdev, min_uV, max_uV); + dev_dbg(rdev_get_dev(rdev), "%s best value: %d \n", __func__, value); + if (value < 0) + return value; + + mc13783_lock(priv->mc13783); + ret = mc13783_reg_rmw(priv->mc13783, mc13783_regulators[id].vsel_reg, + mc13783_regulators[id].vsel_mask, + value << mc13783_regulators[id].vsel_shift); + mc13783_unlock(priv->mc13783); + + return ret; +} + +static int mc13783_regulator_get_voltage(struct regulator_dev *rdev) +{ + struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + unsigned int val; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + mc13783_lock(priv->mc13783); + ret = mc13783_reg_read(priv->mc13783, + mc13783_regulators[id].vsel_reg, &val); + mc13783_unlock(priv->mc13783); + + if (ret) + return ret; + + val = (val & mc13783_regulators[id].vsel_mask) + >> mc13783_regulators[id].vsel_shift; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val); + + BUG_ON(val < 0 || val > mc13783_regulators[id].desc.n_voltages); + + return mc13783_regulators[id].voltages[val]; +} + static struct regulator_ops mc13783_regulator_ops = { .enable = mc13783_regulator_enable, .disable = mc13783_regulator_disable, .is_enabled = mc13783_regulator_is_enabled, + .list_voltage = mc13783_regulator_list_voltage, + .set_voltage = mc13783_regulator_set_voltage, + .get_voltage = mc13783_regulator_get_voltage, +}; + +static int mc13783_fixed_regulator_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + int id = rdev_get_id(rdev); + + dev_dbg(rdev_get_dev(rdev), "%s id: %d min_uV: %d max_uV: %d\n", + __func__, id, min_uV, max_uV); + + if (min_uV > mc13783_regulators[id].voltages[0] && + max_uV < mc13783_regulators[id].voltages[0]) + return 0; + else + return -EINVAL; +} + +static int mc13783_fixed_regulator_get_voltage(struct regulator_dev *rdev) +{ + int id = rdev_get_id(rdev); + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + return mc13783_regulators[id].voltages[0]; +} + +static struct regulator_ops mc13783_fixed_regulator_ops = { + .enable = mc13783_regulator_enable, + .disable = mc13783_regulator_disable, + .is_enabled = mc13783_regulator_is_enabled, + .list_voltage = mc13783_regulator_list_voltage, + .set_voltage = mc13783_fixed_regulator_set_voltage, + .get_voltage = mc13783_fixed_regulator_get_voltage, }; static int __devinit mc13783_regulator_probe(struct platform_device *pdev) From 6faa7e0a4bb1215fb0a0093a0d426a72599e5982 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 23 Dec 2009 14:13:17 +0100 Subject: [PATCH 02/27] regulator/lp3971: Storage class should be before const qualifier The C99 specification states in section 6.11.5: The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature. Signed-off-by: Tobias Klauser Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/lp3971.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 4f33a0f4a1796..3ea639f29e95a 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -54,7 +54,7 @@ static int lp3971_set_bits(struct lp3971 *lp3971, u8 reg, u16 mask, u16 val); #define LP3971_BUCK2_BASE 0x29 #define LP3971_BUCK3_BASE 0x32 -const static int buck_base_addr[] = { +static const int buck_base_addr[] = { LP3971_BUCK1_BASE, LP3971_BUCK2_BASE, LP3971_BUCK3_BASE, @@ -63,7 +63,7 @@ const static int buck_base_addr[] = { #define LP3971_BUCK_TARGET_VOL1_REG(x) (buck_base_addr[x]) #define LP3971_BUCK_TARGET_VOL2_REG(x) (buck_base_addr[x]+1) -const static int buck_voltage_map[] = { +static const int buck_voltage_map[] = { 0, 800, 850, 900, 950, 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1800, 1900, 2500, 2800, @@ -96,17 +96,17 @@ const static int buck_voltage_map[] = { #define LDO_VOL_CONTR_SHIFT(x) ((x & 1) << 2) #define LDO_VOL_CONTR_MASK 0x0f -const static int ldo45_voltage_map[] = { +static const int ldo45_voltage_map[] = { 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, 1400, 1500, 1800, 1900, 2500, 2800, 3000, 3300, }; -const static int ldo123_voltage_map[] = { +static const int ldo123_voltage_map[] = { 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100, 3200, 3300, }; -const static int *ldo_voltage_map[] = { +static const int *ldo_voltage_map[] = { ldo123_voltage_map, /* LDO1 */ ldo123_voltage_map, /* LDO2 */ ldo123_voltage_map, /* LDO3 */ From 84b6826306119dc3c41ef9d7ed6c408112f63301 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 1 Dec 2009 21:12:27 +0000 Subject: [PATCH 03/27] regulator: Add notifier event on regulator disable The intended use case is for drivers which disable regulators to save power but need to do some work to restore the hardware state when restarting. If the supplies are not actually disabled due to board limits or sharing with other active devices this notifier allows the driver to avoid unneeded reinitialisation, particularly when used with runtime PM. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 7 +++++-- include/linux/regulator/consumer.h | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b60a4c9f8f168..6d2ce8a053314 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1341,6 +1341,9 @@ static int _regulator_disable(struct regulator_dev *rdev) __func__, rdev_get_name(rdev)); return ret; } + + _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, + NULL); } /* decrease our supplies ref count and disable if required */ @@ -1399,8 +1402,8 @@ static int _regulator_force_disable(struct regulator_dev *rdev) return ret; } /* notify other consumers that power has been forced off */ - _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE, - NULL); + _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | + REGULATOR_EVENT_DISABLE, NULL); } /* decrease our supplies ref count and disable if required */ diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 030d92255c7a0..28c9fd020d399 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -89,8 +89,9 @@ * REGULATION_OUT Regulator output is out of regulation. * FAIL Regulator output has failed. * OVER_TEMP Regulator over temp. - * FORCE_DISABLE Regulator shut down by software. + * FORCE_DISABLE Regulator forcibly shut down by software. * VOLTAGE_CHANGE Regulator voltage changed. + * DISABLE Regulator was disabled. * * NOTE: These events can be OR'ed together when passed into handler. */ @@ -102,6 +103,7 @@ #define REGULATOR_EVENT_OVER_TEMP 0x10 #define REGULATOR_EVENT_FORCE_DISABLE 0x20 #define REGULATOR_EVENT_VOLTAGE_CHANGE 0x40 +#define REGULATOR_EVENT_DISABLE 0x80 struct regulator; From 31aae2beeb3d601d556b6a8c39085940ad1e9f42 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 21 Dec 2009 12:21:52 +0000 Subject: [PATCH 04/27] regulator: Allow regulators to specify the time taken to ramp on enable Regulators may sometimes take longer to enable than the control operation used to do so, either because the regulator has ramp rate control used to limit inrush current or because the control operation is very fast (GPIO being the most common example of this). In order to ensure that consumers do not rely on the regulator before it is enabled provide an enable_time() operation and have the core delay for that time before returning to the caller. This is implemented as a function since the ramp rate may be specified in voltage per unit time and therefore the time depend on the configuration. In future it would be desirable to allow the bulk operations to run the delays for multiple enables in parallel but this is not currently supported. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 41 +++++++++++++++++++++++++++----- include/linux/regulator/driver.h | 6 +++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 6d2ce8a053314..ca8e1642538b7 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1084,6 +1085,13 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, return NULL; } +static int _regulator_get_enable_time(struct regulator_dev *rdev) +{ + if (!rdev->desc->ops->enable_time) + return 0; + return rdev->desc->ops->enable_time(rdev); +} + /* Internal regulator request function */ static struct regulator *_regulator_get(struct device *dev, const char *id, int exclusive) @@ -1251,7 +1259,7 @@ static int _regulator_can_change_status(struct regulator_dev *rdev) /* locks held by regulator_enable() */ static int _regulator_enable(struct regulator_dev *rdev) { - int ret; + int ret, delay; /* do we need to enable the supply regulator first */ if (rdev->supply) { @@ -1275,13 +1283,34 @@ static int _regulator_enable(struct regulator_dev *rdev) if (!_regulator_can_change_status(rdev)) return -EPERM; - if (rdev->desc->ops->enable) { - ret = rdev->desc->ops->enable(rdev); - if (ret < 0) - return ret; - } else { + if (!rdev->desc->ops->enable) return -EINVAL; + + /* Query before enabling in case configuration + * dependant. */ + ret = _regulator_get_enable_time(rdev); + if (ret >= 0) { + delay = ret; + } else { + printk(KERN_WARNING + "%s: enable_time() failed for %s: %d\n", + __func__, rdev_get_name(rdev), + ret); + delay = 0; } + + /* Allow the regulator to ramp; it would be useful + * to extend this for bulk operations so that the + * regulators can ramp together. */ + ret = rdev->desc->ops->enable(rdev); + if (ret < 0) + return ret; + + if (delay >= 1000) + mdelay(delay / 1000); + else if (delay) + udelay(delay); + } else if (ret < 0) { printk(KERN_ERR "%s: is_enabled() failed for %s: %d\n", __func__, rdev_get_name(rdev), ret); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 31f2055eae282..592cd7c642c22 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -58,6 +58,9 @@ enum regulator_status { * @get_optimum_mode: Get the most efficient operating mode for the regulator * when running with the specified parameters. * + * @enable_time: Time taken for the regulator voltage output voltage to + * stabalise after being enabled, in microseconds. + * * @set_suspend_voltage: Set the voltage for the regulator when the system * is suspended. * @set_suspend_enable: Mark the regulator as enabled when the system is @@ -93,6 +96,9 @@ struct regulator_ops { int (*set_mode) (struct regulator_dev *, unsigned int mode); unsigned int (*get_mode) (struct regulator_dev *); + /* Time taken to enable the regulator */ + int (*enable_time) (struct regulator_dev *); + /* report regulator status ... most other accessors report * control inputs, this reports results of combining inputs * from Linux (and other sources) with the actual load. From 75c8ac22e4b8ebea8169a090e64d034a96758644 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 4 Jan 2010 17:24:01 +0000 Subject: [PATCH 05/27] regulator: Implement enable_time() for WM835x ISINKs Signed-off-by: Mark Brown --- drivers/regulator/wm8350-regulator.c | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index e7b89e704af67..94227dd6ba7ba 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -290,6 +290,51 @@ static int wm8350_isink_is_enabled(struct regulator_dev *rdev) return -EINVAL; } +static int wm8350_isink_enable_time(struct regulator_dev *rdev) +{ + struct wm8350 *wm8350 = rdev_get_drvdata(rdev); + int isink = rdev_get_id(rdev); + int reg; + + switch (isink) { + case WM8350_ISINK_A: + reg = wm8350_reg_read(wm8350, WM8350_CSA_FLASH_CONTROL); + break; + case WM8350_ISINK_B: + reg = wm8350_reg_read(wm8350, WM8350_CSB_FLASH_CONTROL); + break; + default: + return -EINVAL; + } + + if (reg & WM8350_CS1_FLASH_MODE) { + switch (reg & WM8350_CS1_ON_RAMP_MASK) { + case 0: + return 0; + case 1: + return 1950; + case 2: + return 3910; + case 3: + return 7800; + } + } else { + switch (reg & WM8350_CS1_ON_RAMP_MASK) { + case 0: + return 0; + case 1: + return 250000; + case 2: + return 500000; + case 3: + return 1000000; + } + } + + return -EINVAL; +} + + int wm8350_isink_set_flash(struct wm8350 *wm8350, int isink, u16 mode, u16 trigger, u16 duration, u16 on_ramp, u16 off_ramp, u16 drive) @@ -1221,6 +1266,7 @@ static struct regulator_ops wm8350_isink_ops = { .enable = wm8350_isink_enable, .disable = wm8350_isink_disable, .is_enabled = wm8350_isink_is_enabled, + .enable_time = wm8350_isink_enable_time, }; static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { From eda79a3041a2cada0d4ee9491c99c3874b322356 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 12 Jan 2010 12:25:13 +0200 Subject: [PATCH 06/27] regulator: Add 'start-up time' to fixed voltage regulators Add a field to specify a delay for the start-up time of a fixed voltage regulator. Signed-off-by: Adrian Hunter Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/fixed.c | 5 +++++ include/linux/regulator/fixed.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index f9f516a3028aa..ee3e7eb97b1c9 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -24,12 +24,14 @@ #include #include #include +#include struct fixed_voltage_data { struct regulator_desc desc; struct regulator_dev *dev; int microvolts; int gpio; + unsigned startup_delay; unsigned enable_high:1; unsigned is_enabled:1; }; @@ -48,6 +50,8 @@ static int fixed_voltage_enable(struct regulator_dev *dev) if (gpio_is_valid(data->gpio)) { gpio_set_value_cansleep(data->gpio, data->enable_high); data->is_enabled = 1; + if (data->startup_delay) + udelay(data->startup_delay); } return 0; @@ -117,6 +121,7 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev) drvdata->microvolts = config->microvolts; drvdata->gpio = config->gpio; + drvdata->startup_delay = config->startup_delay; if (gpio_is_valid(config->gpio)) { drvdata->enable_high = config->enable_high; diff --git a/include/linux/regulator/fixed.h b/include/linux/regulator/fixed.h index e94a4a1c7c8a7..ffd7d508e726f 100644 --- a/include/linux/regulator/fixed.h +++ b/include/linux/regulator/fixed.h @@ -25,6 +25,7 @@ struct regulator_init_data; * @microvolts: Output voltage of regulator * @gpio: GPIO to use for enable control * set to -EINVAL if not used + * @startup_delay: Start-up time in microseconds * @enable_high: Polarity of enable GPIO * 1 = Active high, 0 = Active low * @enabled_at_boot: Whether regulator has been enabled at @@ -41,6 +42,7 @@ struct fixed_voltage_config { const char *supply_name; int microvolts; int gpio; + unsigned startup_delay; unsigned enable_high:1; unsigned enabled_at_boot:1; struct regulator_init_data *init_data; From f4b97b36b7c6b2d4455f27d6371869f915cbe8fd Mon Sep 17 00:00:00 2001 From: Alberto Panizzo Date: Tue, 19 Jan 2010 12:48:54 +0100 Subject: [PATCH 07/27] regulator: mc13783: consider Power Gates as digital regulators. GPO regulators are digital outputs that can be enabled or disabled by a dedicated bit in mc13783 POWERMISC register. In this family can be count in also Power Gates (PWGT1 and 2): enabled by a dedicated pin a Power Gate is an hardware driven supply where the output (PWGTnDRV) follow this law: Bit PWGTxSPIEN | Pin PWGTxEN | PWGTxDRV | Read Back 0 = default | | | PWGTxSPIEN ---------------+-------------+----------+------------ 1 | x | Low | 0 0 | 0 | High | 1 0 | 1 | Low | 0 As read back value of control bit reflects the PWGTxDRV state (not the control value previously written) and mc13783 POWERMISC register contain only regulator related bits, a dedicated function to manage these bits is created here with the aim of tracing the real value of PWGTxSPIEN bits and reproduce it on next writes. All POWERMISC users _must_ use the new function to not accidentally disable Power Gates supplies. v2 changes: -Better utilization of abstraction layers. -Voltage query support. GPO's and PWGTxDRV are fixed voltage regulator with voltage value of 3.1V and 5.5V respectively. Signed-off-by: Alberto Panizzo Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/mc13783-regulator.c | 132 ++++++++++++++++++++++++-- include/linux/mfd/mc13783.h | 2 + 2 files changed, 128 insertions(+), 6 deletions(-) diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index a40e35ab85553..f7b81845a1962 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -82,6 +82,11 @@ #define MC13783_REG_POWERMISC_GPO2EN (1 << 8) #define MC13783_REG_POWERMISC_GPO3EN (1 << 10) #define MC13783_REG_POWERMISC_GPO4EN (1 << 12) +#define MC13783_REG_POWERMISC_PWGT1SPIEN (1 << 15) +#define MC13783_REG_POWERMISC_PWGT2SPIEN (1 << 16) + +#define MC13783_REG_POWERMISC_PWGTSPI_M (3 << 15) + struct mc13783_regulator { struct regulator_desc desc; @@ -161,8 +166,17 @@ static const int const mc13783_vrf_val[] = { 1500000, 1875000, 2700000, 2775000, }; +static const int const mc13783_gpo_val[] = { + 3100000, +}; + +static const int const mc13783_pwgtdrv_val[] = { + 5500000, +}; + static struct regulator_ops mc13783_regulator_ops; static struct regulator_ops mc13783_fixed_regulator_ops; +static struct regulator_ops mc13783_gpo_regulator_ops; #define MC13783_DEFINE(prefix, _name, _reg, _vsel_reg, _voltages) \ [MC13783_ ## prefix ## _ ## _name] = { \ @@ -197,17 +211,19 @@ static struct regulator_ops mc13783_fixed_regulator_ops; .voltages = _voltages, \ } -#define MC13783_GPO_DEFINE(prefix, _name, _reg) \ +#define MC13783_GPO_DEFINE(prefix, _name, _reg, _voltages) \ [MC13783_ ## prefix ## _ ## _name] = { \ .desc = { \ .name = #prefix "_" #_name, \ - .ops = &mc13783_regulator_ops, \ + .n_voltages = ARRAY_SIZE(_voltages), \ + .ops = &mc13783_gpo_regulator_ops, \ .type = REGULATOR_VOLTAGE, \ .id = MC13783_ ## prefix ## _ ## _name, \ .owner = THIS_MODULE, \ }, \ .reg = MC13783_REG_ ## _reg, \ .enable_bit = MC13783_REG_ ## _reg ## _ ## _name ## EN, \ + .voltages = _voltages, \ } #define MC13783_DEFINE_SW(_name, _reg, _vsel_reg, _voltages) \ @@ -249,14 +265,17 @@ static struct mc13783_regulator mc13783_regulators[] = { mc13783_vmmc_val), MC13783_DEFINE_REGU(VMMC2, REGULATORMODE1, REGULATORSETTING1, \ mc13783_vmmc_val), - MC13783_GPO_DEFINE(REGU, GPO1, POWERMISC), - MC13783_GPO_DEFINE(REGU, GPO2, POWERMISC), - MC13783_GPO_DEFINE(REGU, GPO3, POWERMISC), - MC13783_GPO_DEFINE(REGU, GPO4, POWERMISC), + MC13783_GPO_DEFINE(REGU, GPO1, POWERMISC, mc13783_gpo_val), + MC13783_GPO_DEFINE(REGU, GPO2, POWERMISC, mc13783_gpo_val), + MC13783_GPO_DEFINE(REGU, GPO3, POWERMISC, mc13783_gpo_val), + MC13783_GPO_DEFINE(REGU, GPO4, POWERMISC, mc13783_gpo_val), + MC13783_GPO_DEFINE(REGU, PWGT1SPI, POWERMISC, mc13783_pwgtdrv_val), + MC13783_GPO_DEFINE(REGU, PWGT2SPI, POWERMISC, mc13783_pwgtdrv_val), }; struct mc13783_regulator_priv { struct mc13783 *mc13783; + u32 powermisc_pwgt_state; struct regulator_dev *regulators[]; }; @@ -445,6 +464,107 @@ static struct regulator_ops mc13783_fixed_regulator_ops = { .get_voltage = mc13783_fixed_regulator_get_voltage, }; +int mc13783_powermisc_rmw(struct mc13783_regulator_priv *priv, u32 mask, + u32 val) +{ + struct mc13783 *mc13783 = priv->mc13783; + int ret; + u32 valread; + + BUG_ON(val & ~mask); + + ret = mc13783_reg_read(mc13783, MC13783_REG_POWERMISC, &valread); + if (ret) + return ret; + + /* Update the stored state for Power Gates. */ + priv->powermisc_pwgt_state = + (priv->powermisc_pwgt_state & ~mask) | val; + priv->powermisc_pwgt_state &= MC13783_REG_POWERMISC_PWGTSPI_M; + + /* Construct the new register value */ + valread = (valread & ~mask) | val; + /* Overwrite the PWGTxEN with the stored version */ + valread = (valread & ~MC13783_REG_POWERMISC_PWGTSPI_M) | + priv->powermisc_pwgt_state; + + return mc13783_reg_write(mc13783, MC13783_REG_POWERMISC, valread); +} + +static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev) +{ + struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + int ret; + u32 en_val = mc13783_regulators[id].enable_bit; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + /* Power Gate enable value is 0 */ + if (id == MC13783_REGU_PWGT1SPI || + id == MC13783_REGU_PWGT2SPI) + en_val = 0; + + mc13783_lock(priv->mc13783); + ret = mc13783_powermisc_rmw(priv, mc13783_regulators[id].enable_bit, + en_val); + mc13783_unlock(priv->mc13783); + + return ret; +} + +static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev) +{ + struct mc13783_regulator_priv *priv = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + int ret; + u32 dis_val = 0; + + dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id); + + /* Power Gate disable value is 1 */ + if (id == MC13783_REGU_PWGT1SPI || + id == MC13783_REGU_PWGT2SPI) + dis_val = mc13783_regulators[id].enable_bit; + + mc13783_lock(priv->mc13783); + ret = mc13783_powermisc_rmw(priv, mc13783_regulators[id].enable_bit, + dis_val); + mc13783_unlock(priv->mc13783); + + return ret; +} + +static int mc13783_gpo_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; + + /* Power Gates state is stored in powermisc_pwgt_state + * where the meaning of bits is negated */ + val = (val & ~MC13783_REG_POWERMISC_PWGTSPI_M) | + (priv->powermisc_pwgt_state ^ MC13783_REG_POWERMISC_PWGTSPI_M); + + return (val & mc13783_regulators[id].enable_bit) != 0; +} + +static struct regulator_ops mc13783_gpo_regulator_ops = { + .enable = mc13783_gpo_regulator_enable, + .disable = mc13783_gpo_regulator_disable, + .is_enabled = mc13783_gpo_regulator_is_enabled, + .list_voltage = mc13783_regulator_list_voltage, + .set_voltage = mc13783_fixed_regulator_set_voltage, + .get_voltage = mc13783_fixed_regulator_get_voltage, +}; + static int __devinit mc13783_regulator_probe(struct platform_device *pdev) { struct mc13783_regulator_priv *priv; diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h index 35680409b8cfd..94cb51a640371 100644 --- a/include/linux/mfd/mc13783.h +++ b/include/linux/mfd/mc13783.h @@ -108,6 +108,8 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, #define MC13783_REGU_V2 28 #define MC13783_REGU_V3 29 #define MC13783_REGU_V4 30 +#define MC13783_REGU_PWGT1SPI 31 +#define MC13783_REGU_PWGT2SPI 32 #define MC13783_IRQ_ADCDONE 0 #define MC13783_IRQ_ADCBISDONE 1 From 79ac9bc32094be15732c421ea1d9a18751ec1ecf Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 25 Jan 2010 21:14:37 -0700 Subject: [PATCH 08/27] regulator: trivial: fix typos in user-visible Kconfig text Fix Kconfig text for some Wolfson Micro devices. Cc: Mark Brown Signed-off-by: Alex Chiang Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 262f62eec8374..c565e0d872875 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -91,14 +91,14 @@ config REGULATOR_WM831X of PMIC devices. config REGULATOR_WM8350 - tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC" + tristate "Wolfson Microelectronics WM8350 AudioPlus PMIC" depends on MFD_WM8350 help This driver provides support for the voltage and current regulators of the WM8350 AudioPlus PMIC. config REGULATOR_WM8400 - tristate "Wolfson Microelectroncis WM8400 AudioPlus PMIC" + tristate "Wolfson Microelectronics WM8400 AudioPlus PMIC" depends on MFD_WM8400 help This driver provides support for the voltage regulators of the From a71b797fdc672714bfff1fdc142042a95e97d7ba Mon Sep 17 00:00:00 2001 From: Haojian Zhuang Date: Mon, 25 Jan 2010 10:24:09 -0500 Subject: [PATCH 09/27] regulator: enable max8649 regulator driver Signed-off-by: Haojian Zhuang Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/max8649.c | 408 ++++++++++++++++++++++++++++++ include/linux/regulator/max8649.h | 44 ++++ 4 files changed, 460 insertions(+) create mode 100644 drivers/regulator/max8649.c create mode 100644 include/linux/regulator/max8649.h diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index c565e0d872875..2bc01ee9d9f29 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -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_MAX8649 + tristate "Maxim 8649 voltage regulator" + depends on I2C + help + This driver controls a Maxim 8649 voltage output regulator via + I2C bus. + config REGULATOR_MAX8660 tristate "Maxim 8660/8661 voltage regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index b3c806c794157..075835be43968 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -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_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c new file mode 100644 index 0000000000000..3ebdf698c648f --- /dev/null +++ b/drivers/regulator/max8649.c @@ -0,0 +1,408 @@ +/* + * Regulators driver for Maxim max8649 + * + * Copyright (C) 2009-2010 Marvell International Ltd. + * Haojian Zhuang + * + * 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 +#include +#include +#include +#include +#include +#include + +#define MAX8649_DCDC_VMIN 750000 /* uV */ +#define MAX8649_DCDC_VMAX 1380000 /* uV */ +#define MAX8649_DCDC_STEP 10000 /* uV */ +#define MAX8649_VOL_MASK 0x3f + +/* Registers */ +#define MAX8649_MODE0 0x00 +#define MAX8649_MODE1 0x01 +#define MAX8649_MODE2 0x02 +#define MAX8649_MODE3 0x03 +#define MAX8649_CONTROL 0x04 +#define MAX8649_SYNC 0x05 +#define MAX8649_RAMP 0x06 +#define MAX8649_CHIP_ID1 0x08 +#define MAX8649_CHIP_ID2 0x09 + +/* Bits */ +#define MAX8649_EN_PD (1 << 7) +#define MAX8649_VID0_PD (1 << 6) +#define MAX8649_VID1_PD (1 << 5) +#define MAX8649_VID_MASK (3 << 5) + +#define MAX8649_FORCE_PWM (1 << 7) +#define MAX8649_SYNC_EXTCLK (1 << 6) + +#define MAX8649_EXT_MASK (3 << 6) + +#define MAX8649_RAMP_MASK (7 << 5) +#define MAX8649_RAMP_DOWN (1 << 1) + +struct max8649_regulator_info { + struct regulator_dev *regulator; + struct i2c_client *i2c; + struct device *dev; + struct mutex io_lock; + + int vol_reg; + unsigned mode:2; /* bit[1:0] = VID1, VID0 */ + unsigned extclk_freq:2; + unsigned extclk:1; + unsigned ramp_timing:3; + unsigned ramp_down:1; +}; + +/* I2C operations */ + +static inline int max8649_read_device(struct i2c_client *i2c, + int reg, int bytes, void *dest) +{ + unsigned char data; + int ret; + + data = (unsigned char)reg; + ret = i2c_master_send(i2c, &data, 1); + if (ret < 0) + return ret; + ret = i2c_master_recv(i2c, dest, bytes); + if (ret < 0) + return ret; + return 0; +} + +static inline int max8649_write_device(struct i2c_client *i2c, + int reg, int bytes, void *src) +{ + unsigned char buf[bytes + 1]; + int ret; + + buf[0] = (unsigned char)reg; + memcpy(&buf[1], src, bytes); + + ret = i2c_master_send(i2c, buf, bytes + 1); + if (ret < 0) + return ret; + return 0; +} + +static int max8649_reg_read(struct i2c_client *i2c, int reg) +{ + struct max8649_regulator_info *info = i2c_get_clientdata(i2c); + unsigned char data; + int ret; + + mutex_lock(&info->io_lock); + ret = max8649_read_device(i2c, reg, 1, &data); + mutex_unlock(&info->io_lock); + + if (ret < 0) + return ret; + return (int)data; +} + +static int max8649_set_bits(struct i2c_client *i2c, int reg, + unsigned char mask, unsigned char data) +{ + struct max8649_regulator_info *info = i2c_get_clientdata(i2c); + unsigned char value; + int ret; + + mutex_lock(&info->io_lock); + ret = max8649_read_device(i2c, reg, 1, &value); + if (ret < 0) + goto out; + value &= ~mask; + value |= data; + ret = max8649_write_device(i2c, reg, 1, &value); +out: + mutex_unlock(&info->io_lock); + return ret; +} + +static inline int check_range(int min_uV, int max_uV) +{ + if ((min_uV < MAX8649_DCDC_VMIN) || (max_uV > MAX8649_DCDC_VMAX) + || (min_uV > max_uV)) + return -EINVAL; + return 0; +} + +static int max8649_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + return (MAX8649_DCDC_VMIN + index * MAX8649_DCDC_STEP); +} + +static int max8649_get_voltage(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data; + int ret; + + ret = max8649_reg_read(info->i2c, info->vol_reg); + if (ret < 0) + return ret; + data = (unsigned char)ret & MAX8649_VOL_MASK; + return max8649_list_voltage(rdev, data); +} + +static int max8649_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data, mask; + + if (check_range(min_uV, max_uV)) { + dev_err(info->dev, "invalid voltage range (%d, %d) uV\n", + min_uV, max_uV); + return -EINVAL; + } + data = (min_uV - MAX8649_DCDC_VMIN + MAX8649_DCDC_STEP - 1) + / MAX8649_DCDC_STEP; + mask = MAX8649_VOL_MASK; + + return max8649_set_bits(info->i2c, info->vol_reg, mask, data); +} + +/* EN_PD means pulldown on EN input */ +static int max8649_enable(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + return max8649_set_bits(info->i2c, MAX8649_CONTROL, MAX8649_EN_PD, 0); +} + +/* + * Applied internal pulldown resistor on EN input pin. + * If pulldown EN pin outside, it would be better. + */ +static int max8649_disable(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + return max8649_set_bits(info->i2c, MAX8649_CONTROL, MAX8649_EN_PD, + MAX8649_EN_PD); +} + +static int max8649_is_enabled(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + ret = max8649_reg_read(info->i2c, MAX8649_CONTROL); + if (ret < 0) + return ret; + return !((unsigned char)ret & MAX8649_EN_PD); +} + +static int max8649_enable_time(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + int voltage, rate, ret; + + /* get voltage */ + ret = max8649_reg_read(info->i2c, info->vol_reg); + if (ret < 0) + return ret; + ret &= MAX8649_VOL_MASK; + voltage = max8649_list_voltage(rdev, (unsigned char)ret); /* uV */ + + /* get rate */ + ret = max8649_reg_read(info->i2c, MAX8649_RAMP); + if (ret < 0) + return ret; + ret = (ret & MAX8649_RAMP_MASK) >> 5; + rate = (32 * 1000) >> ret; /* uV/uS */ + + return (voltage / rate); +} + +static int max8649_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + + switch (mode) { + case REGULATOR_MODE_FAST: + max8649_set_bits(info->i2c, info->vol_reg, MAX8649_FORCE_PWM, + MAX8649_FORCE_PWM); + break; + case REGULATOR_MODE_NORMAL: + max8649_set_bits(info->i2c, info->vol_reg, + MAX8649_FORCE_PWM, 0); + break; + default: + return -EINVAL; + } + return 0; +} + +static unsigned int max8649_get_mode(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + ret = max8649_reg_read(info->i2c, info->vol_reg); + if (ret & MAX8649_FORCE_PWM) + return REGULATOR_MODE_FAST; + return REGULATOR_MODE_NORMAL; +} + +static struct regulator_ops max8649_dcdc_ops = { + .set_voltage = max8649_set_voltage, + .get_voltage = max8649_get_voltage, + .list_voltage = max8649_list_voltage, + .enable = max8649_enable, + .disable = max8649_disable, + .is_enabled = max8649_is_enabled, + .enable_time = max8649_enable_time, + .set_mode = max8649_set_mode, + .get_mode = max8649_get_mode, + +}; + +static struct regulator_desc dcdc_desc = { + .name = "max8649", + .ops = &max8649_dcdc_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = 1 << 6, + .owner = THIS_MODULE, +}; + +static int __devinit max8649_regulator_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max8649_platform_data *pdata = client->dev.platform_data; + struct max8649_regulator_info *info = NULL; + unsigned char data; + int ret; + + info = kzalloc(sizeof(struct max8649_regulator_info), GFP_KERNEL); + if (!info) { + dev_err(&client->dev, "No enough memory\n"); + return -ENOMEM; + } + + info->i2c = client; + info->dev = &client->dev; + mutex_init(&info->io_lock); + i2c_set_clientdata(client, info); + + info->mode = pdata->mode; + switch (info->mode) { + case 0: + info->vol_reg = MAX8649_MODE0; + break; + case 1: + info->vol_reg = MAX8649_MODE1; + break; + case 2: + info->vol_reg = MAX8649_MODE2; + break; + case 3: + info->vol_reg = MAX8649_MODE3; + break; + default: + break; + } + + ret = max8649_reg_read(info->i2c, MAX8649_CHIP_ID1); + if (ret < 0) { + dev_err(info->dev, "Failed to detect ID of MAX8649:%d\n", + ret); + goto out; + } + dev_info(info->dev, "Detected MAX8649 (ID:%x)\n", ret); + + /* enable VID0 & VID1 */ + max8649_set_bits(info->i2c, MAX8649_CONTROL, MAX8649_VID_MASK, 0); + + /* enable/disable external clock synchronization */ + info->extclk = pdata->extclk; + data = (info->extclk) ? MAX8649_SYNC_EXTCLK : 0; + max8649_set_bits(info->i2c, info->vol_reg, MAX8649_SYNC_EXTCLK, data); + if (info->extclk) { + /* set external clock frequency */ + info->extclk_freq = pdata->extclk_freq; + max8649_set_bits(info->i2c, MAX8649_SYNC, MAX8649_EXT_MASK, + info->extclk_freq); + } + + if (pdata->ramp_timing) { + info->ramp_timing = pdata->ramp_timing; + max8649_set_bits(info->i2c, MAX8649_RAMP, MAX8649_RAMP_MASK, + info->ramp_timing << 5); + } + + info->ramp_down = pdata->ramp_down; + if (info->ramp_down) { + max8649_set_bits(info->i2c, MAX8649_RAMP, MAX8649_RAMP_DOWN, + MAX8649_RAMP_DOWN); + } + + info->regulator = regulator_register(&dcdc_desc, &client->dev, + pdata->regulator, info); + if (IS_ERR(info->regulator)) { + dev_err(info->dev, "failed to register regulator %s\n", + dcdc_desc.name); + ret = PTR_ERR(info->regulator); + goto out; + } + + dev_info(info->dev, "Max8649 regulator device is detected.\n"); + return 0; +out: + kfree(info); + return ret; +} + +static int __devexit max8649_regulator_remove(struct i2c_client *client) +{ + struct max8649_regulator_info *info = i2c_get_clientdata(client); + + if (info) { + if (info->regulator) + regulator_unregister(info->regulator); + kfree(info); + } + i2c_set_clientdata(client, NULL); + + return 0; +} + +static const struct i2c_device_id max8649_id[] = { + { "max8649", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max8649_id); + +static struct i2c_driver max8649_driver = { + .probe = max8649_regulator_probe, + .remove = __devexit_p(max8649_regulator_remove), + .driver = { + .name = "max8649", + }, + .id_table = max8649_id, +}; + +static int __init max8649_init(void) +{ + return i2c_add_driver(&max8649_driver); +} +subsys_initcall(max8649_init); + +static void __exit max8649_exit(void) +{ + i2c_del_driver(&max8649_driver); +} +module_exit(max8649_exit); + +/* Module information */ +MODULE_DESCRIPTION("MAXIM 8649 voltage regulator driver"); +MODULE_AUTHOR("Haojian Zhuang "); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/regulator/max8649.h b/include/linux/regulator/max8649.h new file mode 100644 index 0000000000000..417d14ecd5cbf --- /dev/null +++ b/include/linux/regulator/max8649.h @@ -0,0 +1,44 @@ +/* + * Interface of Maxim max8649 + * + * Copyright (C) 2009-2010 Marvell International Ltd. + * Haojian Zhuang + * + * 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. + */ + +#ifndef __LINUX_REGULATOR_MAX8649_H +#define __LINUX_REGULATOR_MAX8649_H + +#include + +enum { + MAX8649_EXTCLK_26MHZ = 0, + MAX8649_EXTCLK_13MHZ, + MAX8649_EXTCLK_19MHZ, /* 19.2MHz */ +}; + +enum { + MAX8649_RAMP_32MV = 0, + MAX8649_RAMP_16MV, + MAX8649_RAMP_8MV, + MAX8649_RAMP_4MV, + MAX8649_RAMP_2MV, + MAX8649_RAMP_1MV, + MAX8649_RAMP_0_5MV, + MAX8649_RAMP_0_25MV, +}; + +struct max8649_platform_data { + struct regulator_init_data *regulator; + + unsigned mode:2; /* bit[1:0] = VID1,VID0 */ + unsigned extclk_freq:2; + unsigned extclk:1; + unsigned ramp_timing:3; + unsigned ramp_down:1; +}; + +#endif /* __LINUX_REGULATOR_MAX8649_H */ From 69dc16c325bef32b0a1a1abf15ae4047174cafc1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 25 Jan 2010 19:41:57 +0000 Subject: [PATCH 10/27] regulator: Add WM8994 regulator support The WM8994 contains two LDOs with mixed hardware/software control to minimise the number of external supplies required while delivering optimal voltages to minimise power consumption. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/wm8994-regulator.c | 304 +++++++++++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 drivers/regulator/wm8994-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 2bc01ee9d9f29..3c07169498cf2 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -111,6 +111,13 @@ config REGULATOR_WM8400 This driver provides support for the voltage regulators of the WM8400 AudioPlus PMIC. +config REGULATOR_WM8994 + tristate "Wolfson Microelectronics WM8994 CODEC" + depends on MFD_WM8994 + help + This driver provides support for the voltage regulators on the + WM8994 CODEC. + config REGULATOR_DA903X tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC" depends on PMIC_DA903X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 075835be43968..7c59bcb106131 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o +obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c new file mode 100644 index 0000000000000..d09e018b31773 --- /dev/null +++ b/drivers/regulator/wm8994-regulator.c @@ -0,0 +1,304 @@ +/* + * wm8994-regulator.c -- Regulator driver for the WM8994 + * + * Copyright 2009 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct wm8994_ldo { + int enable; + int is_enabled; + struct regulator_dev *regulator; + struct wm8994 *wm8994; +}; + +#define WM8994_LDO1_MAX_SELECTOR 0x7 +#define WM8994_LDO2_MAX_SELECTOR 0x3 + +static int wm8994_ldo_enable(struct regulator_dev *rdev) +{ + struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); + + /* If we have no soft control assume that the LDO is always enabled. */ + if (!ldo->enable) + return 0; + + gpio_set_value(ldo->enable, 1); + ldo->is_enabled = 1; + + return 0; +} + +static int wm8994_ldo_disable(struct regulator_dev *rdev) +{ + struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); + + /* If we have no soft control assume that the LDO is always enabled. */ + if (!ldo->enable) + return -EINVAL; + + gpio_set_value(ldo->enable, 0); + ldo->is_enabled = 0; + + return 0; +} + +static int wm8994_ldo_is_enabled(struct regulator_dev *rdev) +{ + struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); + + return ldo->is_enabled; +} + +static int wm8994_ldo_enable_time(struct regulator_dev *rdev) +{ + /* 3ms is fairly conservative but this shouldn't be too performance + * critical; can be tweaked per-system if required. */ + return 3000; +} + +static int wm8994_ldo1_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector > WM8994_LDO1_MAX_SELECTOR) + return -EINVAL; + + return (selector * 100000) + 2400000; +} + +static int wm8994_ldo1_get_voltage(struct regulator_dev *rdev) +{ + struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); + int val; + + val = wm8994_reg_read(ldo->wm8994, WM8994_LDO_1); + if (val < 0) + return val; + + val = (val & WM8994_LDO1_VSEL_MASK) >> WM8994_LDO1_VSEL_SHIFT; + + return wm8994_ldo1_list_voltage(rdev, val); +} + +static int wm8994_ldo1_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); + int selector, v; + + selector = (min_uV - 2400000) / 100000; + v = wm8994_ldo1_list_voltage(rdev, selector); + if (v < 0 || v > max_uV) + return -EINVAL; + + selector <<= WM8994_LDO1_VSEL_SHIFT; + + return wm8994_set_bits(ldo->wm8994, WM8994_LDO_1, + WM8994_LDO1_VSEL_MASK, selector); +} + +static struct regulator_ops wm8994_ldo1_ops = { + .enable = wm8994_ldo_enable, + .disable = wm8994_ldo_disable, + .is_enabled = wm8994_ldo_is_enabled, + .enable_time = wm8994_ldo_enable_time, + + .list_voltage = wm8994_ldo1_list_voltage, + .get_voltage = wm8994_ldo1_get_voltage, + .set_voltage = wm8994_ldo1_set_voltage, +}; + +static int wm8994_ldo2_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector > WM8994_LDO2_MAX_SELECTOR) + return -EINVAL; + + return (selector * 100000) + 900000; +} + +static int wm8994_ldo2_get_voltage(struct regulator_dev *rdev) +{ + struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); + int val; + + val = wm8994_reg_read(ldo->wm8994, WM8994_LDO_2); + if (val < 0) + return val; + + val = (val & WM8994_LDO2_VSEL_MASK) >> WM8994_LDO2_VSEL_SHIFT; + + return wm8994_ldo2_list_voltage(rdev, val); +} + +static int wm8994_ldo2_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct wm8994_ldo *ldo = rdev_get_drvdata(rdev); + int selector, v; + + selector = (min_uV - 900000) / 100000; + v = wm8994_ldo2_list_voltage(rdev, selector); + if (v < 0 || v > max_uV) + return -EINVAL; + + selector <<= WM8994_LDO2_VSEL_SHIFT; + + return wm8994_set_bits(ldo->wm8994, WM8994_LDO_2, + WM8994_LDO2_VSEL_MASK, selector); +} + +static struct regulator_ops wm8994_ldo2_ops = { + .enable = wm8994_ldo_enable, + .disable = wm8994_ldo_disable, + .is_enabled = wm8994_ldo_is_enabled, + .enable_time = wm8994_ldo_enable_time, + + .list_voltage = wm8994_ldo2_list_voltage, + .get_voltage = wm8994_ldo2_get_voltage, + .set_voltage = wm8994_ldo2_set_voltage, +}; + +static struct regulator_desc wm8994_ldo_desc[] = { + { + .name = "LDO1", + .id = 1, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8994_LDO1_MAX_SELECTOR + 1, + .ops = &wm8994_ldo1_ops, + .owner = THIS_MODULE, + }, + { + .name = "LDO2", + .id = 2, + .type = REGULATOR_VOLTAGE, + .n_voltages = WM8994_LDO2_MAX_SELECTOR + 1, + .ops = &wm8994_ldo2_ops, + .owner = THIS_MODULE, + }, +}; + +static __devinit int wm8994_ldo_probe(struct platform_device *pdev) +{ + struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent); + struct wm8994_pdata *pdata = wm8994->dev->platform_data; + int id = pdev->id % ARRAY_SIZE(pdata->ldo); + struct wm8994_ldo *ldo; + int ret; + + dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); + + if (!pdata) + return -ENODEV; + + ldo = kzalloc(sizeof(struct wm8994_ldo), GFP_KERNEL); + if (ldo == NULL) { + dev_err(&pdev->dev, "Unable to allocate private data\n"); + return -ENOMEM; + } + + ldo->wm8994 = wm8994; + + ldo->is_enabled = 1; + + if (pdata->ldo[id].enable && gpio_is_valid(pdata->ldo[id].enable)) { + ldo->enable = pdata->ldo[id].enable; + + ret = gpio_request(ldo->enable, "WM8994 LDO enable"); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get enable GPIO: %d\n", + ret); + goto err; + } + + ret = gpio_direction_output(ldo->enable, ldo->is_enabled); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to set GPIO up: %d\n", + ret); + goto err_gpio; + } + } + + ldo->regulator = regulator_register(&wm8994_ldo_desc[id], &pdev->dev, + pdata->ldo[id].init_data, ldo); + if (IS_ERR(ldo->regulator)) { + ret = PTR_ERR(ldo->regulator); + dev_err(wm8994->dev, "Failed to register LDO%d: %d\n", + id + 1, ret); + goto err_gpio; + } + + platform_set_drvdata(pdev, ldo); + + return 0; + +err_gpio: + if (gpio_is_valid(ldo->enable)) + gpio_free(ldo->enable); +err: + kfree(ldo); + return ret; +} + +static __devexit int wm8994_ldo_remove(struct platform_device *pdev) +{ + struct wm8994_ldo *ldo = platform_get_drvdata(pdev); + + regulator_unregister(ldo->regulator); + if (gpio_is_valid(ldo->enable)) + gpio_free(ldo->enable); + kfree(ldo); + + return 0; +} + +static struct platform_driver wm8994_ldo_driver = { + .probe = wm8994_ldo_probe, + .remove = __devexit_p(wm8994_ldo_remove), + .driver = { + .name = "wm8994-ldo", + }, +}; + +static int __init wm8994_ldo_init(void) +{ + int ret; + + ret = platform_driver_register(&wm8994_ldo_driver); + if (ret != 0) + pr_err("Failed to register Wm8994 GP LDO driver: %d\n", ret); + + return ret; +} +subsys_initcall(wm8994_ldo_init); + +static void __exit wm8994_ldo_exit(void) +{ + platform_driver_unregister(&wm8994_ldo_driver); +} +module_exit(wm8994_ldo_exit); + +/* Module information */ +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("WM8994 LDO driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8994-ldo"); From 17133dc82422cb118c843c3e9bbd565c37506ec8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 28 Jan 2010 10:21:05 +0000 Subject: [PATCH 11/27] regulator: Convert fixed voltage regulator to use enable_time() It had an open coded version in enable(). Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/fixed.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index ee3e7eb97b1c9..a3d3bfc24b1f0 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -50,8 +50,6 @@ static int fixed_voltage_enable(struct regulator_dev *dev) if (gpio_is_valid(data->gpio)) { gpio_set_value_cansleep(data->gpio, data->enable_high); data->is_enabled = 1; - if (data->startup_delay) - udelay(data->startup_delay); } return 0; @@ -69,6 +67,13 @@ static int fixed_voltage_disable(struct regulator_dev *dev) return 0; } +static int fixed_voltage_enable_time(struct regulator_dev *dev) +{ + struct fixed_voltage_data *data = rdev_get_drvdata(dev); + + return data->startup_delay; +} + static int fixed_voltage_get_voltage(struct regulator_dev *dev) { struct fixed_voltage_data *data = rdev_get_drvdata(dev); @@ -91,6 +96,7 @@ static struct regulator_ops fixed_voltage_ops = { .is_enabled = fixed_voltage_is_enabled, .enable = fixed_voltage_enable, .disable = fixed_voltage_disable, + .enable_time = fixed_voltage_enable_time, .get_voltage = fixed_voltage_get_voltage, .list_voltage = fixed_voltage_list_voltage, }; From 9a7f6a4c6edc84748c6477c9df56691a0e61b8fd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 11 Feb 2010 17:22:45 +0000 Subject: [PATCH 12/27] regulator: Assume regulators are enabled if they don't report anything If a regulator driver does not provide a way to query if the driver is enabled then assume that it is enabled. This is very likely to reflect the actual state is more useful for callers than reporting an error. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ca8e1642538b7..75a26f7809184 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1466,9 +1466,9 @@ EXPORT_SYMBOL_GPL(regulator_force_disable); static int _regulator_is_enabled(struct regulator_dev *rdev) { - /* sanity check */ + /* If we don't know then assume that the regulator is always on */ if (!rdev->desc->ops->is_enabled) - return -EINVAL; + return 1; return rdev->desc->ops->is_enabled(rdev); } From 34abbd68efe09765465b81dfedeee9994f13302f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 12 Feb 2010 10:18:08 +0000 Subject: [PATCH 13/27] regulator: Provide optional dummy regulator for consumers In order to ease transitions with drivers are boards start using regulators provide an option to cause all regulator_get() calls to succeed, with a dummy always on regulator being supplied where one has not been configured. A warning is printed whenever the dummy regulator is used to aid system development. This regulator does not implement any regulator operations but will allow simple consumers which only do enable() and disable() calls to run. It is kept separate from the fixed voltage regulator to avoid Kconfig confusion on the part of users when it is extended to allow boards to explicitly use the dummy regulator to simplify cases where the majority of supplies are from fixed regulators without software control. This option is currently only effective for systems which do not specify full constriants. If required an override could also be provided to allow these systems to use the dummy regulator, though it is likely that unconfigured supplies on such systems will lead to error due to regulators being powered down more aggressively when not in use. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 11 +++++++ drivers/regulator/Makefile | 1 + drivers/regulator/core.c | 27 +++++++++++++++- drivers/regulator/dummy.c | 66 ++++++++++++++++++++++++++++++++++++++ drivers/regulator/dummy.h | 31 ++++++++++++++++++ 5 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 drivers/regulator/dummy.c create mode 100644 drivers/regulator/dummy.h diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 3c07169498cf2..834b484418293 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -27,6 +27,17 @@ config REGULATOR_DEBUG help Say yes here to enable debugging support. +config REGULATOR_DUMMY + bool "Provide a dummy regulator if regulator lookups fail" + help + If this option is enabled then when a regulator lookup fails + and the board has not specified that it has provided full + constraints then the regulator core will provide an always + enabled dummy regulator will be provided, allowing consumer + drivers to continue. + + A warning will be generated when this substitution is done. + config REGULATOR_FIXED_VOLTAGE tristate "Fixed voltage regulator support" help diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 7c59bcb106131..e845b66ad59c9 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o +obj-$(CONFIG_REGULATOR_DUMMY) += dummy.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 75a26f7809184..c7bbe30010f70 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -24,6 +24,8 @@ #include #include +#include "dummy.h" + #define REGULATOR_VERSION "0.5" static DEFINE_MUTEX(regulator_list_mutex); @@ -1123,6 +1125,22 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, goto found; } } + +#ifdef CONFIG_REGULATOR_DUMMY + if (!devname) + devname = "deviceless"; + + /* If the board didn't flag that it was fully constrained then + * substitute in a dummy regulator so consumers can continue. + */ + if (!has_full_constraints) { + pr_warning("%s supply %s not found, using dummy regulator\n", + devname, id); + rdev = dummy_regulator_rdev; + goto found; + } +#endif + mutex_unlock(®ulator_list_mutex); return regulator; @@ -2483,8 +2501,15 @@ EXPORT_SYMBOL_GPL(regulator_get_init_drvdata); static int __init regulator_init(void) { + int ret; + printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION); - return class_register(®ulator_class); + + ret = class_register(®ulator_class); + + regulator_dummy_init(); + + return ret; } /* init early to allow our consumers to complete system booting */ diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c new file mode 100644 index 0000000000000..c7410bde7b5d9 --- /dev/null +++ b/drivers/regulator/dummy.c @@ -0,0 +1,66 @@ +/* + * dummy.c + * + * Copyright 2010 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * This is useful for systems with mixed controllable and + * non-controllable regulators, as well as for allowing testing on + * systems with no controllable regulators. + */ + +#include +#include +#include +#include + +#include "dummy.h" + +struct regulator_dev *dummy_regulator_rdev; + +static struct regulator_init_data dummy_initdata; + +static struct regulator_ops dummy_ops; + +static struct regulator_desc dummy_desc = { + .name = "dummy", + .id = -1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &dummy_ops, +}; + +static struct platform_device *dummy_pdev; + +void __init regulator_dummy_init(void) +{ + int ret; + + dummy_pdev = platform_device_alloc("reg-dummy", -1); + if (!dummy_pdev) { + pr_err("Failed to allocate dummy regulator device\n"); + return; + } + + ret = platform_device_add(dummy_pdev); + if (ret != 0) { + pr_err("Failed to register dummy regulator device: %d\n", ret); + platform_device_put(dummy_pdev); + return; + } + + dummy_regulator_rdev = regulator_register(&dummy_desc, NULL, + &dummy_initdata, NULL); + if (IS_ERR(dummy_regulator_rdev)) { + ret = PTR_ERR(dummy_regulator_rdev); + pr_err("Failed to register regulator: %d\n", ret); + platform_device_unregister(dummy_pdev); + return; + } +} diff --git a/drivers/regulator/dummy.h b/drivers/regulator/dummy.h new file mode 100644 index 0000000000000..3921c0e242492 --- /dev/null +++ b/drivers/regulator/dummy.h @@ -0,0 +1,31 @@ +/* + * dummy.h + * + * Copyright 2010 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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; either version 2 of the + * License, or (at your option) any later version. + * + * This is useful for systems with mixed controllable and + * non-controllable regulators, as well as for allowing testing on + * systems with no controllable regulators. + */ + +#ifndef _DUMMY_H +#define _DUMMY_H + +struct regulator_dev; + +extern struct regulator_dev *dummy_regulator_rdev; + +#ifdef CONFIG_REGULATOR_DUMMY +void __init regulator_dummy_init(void); +#else +static inline void regulator_dummy_init(void) { } +#endif + +#endif From 2ebcf63245b4496808712ed884f6a330d73bc807 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 17 Feb 2010 20:54:15 +0530 Subject: [PATCH 14/27] twl6030: regulator: Configure STATE register instead of REMAP This is no REMAP register on twl6030, instead there is a STATE register to drive a resource to a given state. The state register can be used to specify what state the resource should enter when its associated with a GRP. Register Bit field description is as below. The patch programmes the corresponding STATE registers for all LDO's to turn ON when assocaited with GRP_P1. STATE REG: Bit7 |Bit6 |Bit5 |Bit4 |Bit3 |Bit2 |Bit1 |Bit0 P3_GRP |P2_GRP |P1_GRP |RES |RES |RES |State1 |State0 State can be specified as below 00: OFF 01: ON 10: OFF 11: SLEEP Signed-off-by: Rajendra Nayak Cc: Liam Girdwood Cc: Samuel Ortiz Cc: Mark Brown Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/twl-regulator.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 7e674859bd59b..5f394bb8701fa 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -519,16 +519,16 @@ static struct twlreg_info twl_regs[] = { /* 6030 REG with base as PMC Slave Misc : 0x0030 */ /* 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) + TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1, 0, 0x21), + TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 2, 0, 0x21), + TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 3, 0, 0x21), + TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 4, 0, 0x21), + TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 5, 0, 0x21), + TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 7, 0, 0x21), + TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0, 0x21), + TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0, 0x21), + TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0, 0x21), + TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0, 0x21) }; static int twlreg_probe(struct platform_device *pdev) From 4cf95663c829ddd47c76ba5b749e88864beef0a7 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:37:44 -0800 Subject: [PATCH 15/27] Regulators: virtual - use sysfs attribute groups Instead of open-coding sysfs attribute group use canned solution. Also add __devinit/__devexit markups for probe and remove methods and use 'bool' where it makes sense. Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/virtual.c | 64 +++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index addc032c84bfe..d96cecaac73d7 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -19,7 +19,7 @@ struct virtual_consumer_data { struct mutex lock; struct regulator *regulator; - int enabled; + bool enabled; int min_uV; int max_uV; int min_uA; @@ -49,7 +49,7 @@ static void update_voltage_constraints(struct device *dev, dev_dbg(dev, "Enabling regulator\n"); ret = regulator_enable(data->regulator); if (ret == 0) - data->enabled = 1; + data->enabled = true; else dev_err(dev, "regulator_enable() failed: %d\n", ret); @@ -59,7 +59,7 @@ static void update_voltage_constraints(struct device *dev, dev_dbg(dev, "Disabling regulator\n"); ret = regulator_disable(data->regulator); if (ret == 0) - data->enabled = 0; + data->enabled = false; else dev_err(dev, "regulator_disable() failed: %d\n", ret); @@ -89,7 +89,7 @@ static void update_current_limit_constraints(struct device *dev, dev_dbg(dev, "Enabling regulator\n"); ret = regulator_enable(data->regulator); if (ret == 0) - data->enabled = 1; + data->enabled = true; else dev_err(dev, "regulator_enable() failed: %d\n", ret); @@ -99,7 +99,7 @@ static void update_current_limit_constraints(struct device *dev, dev_dbg(dev, "Disabling regulator\n"); ret = regulator_disable(data->regulator); if (ret == 0) - data->enabled = 0; + data->enabled = false; else dev_err(dev, "regulator_disable() failed: %d\n", ret); @@ -270,24 +270,28 @@ static DEVICE_ATTR(min_microamps, 0666, show_min_uA, set_min_uA); static DEVICE_ATTR(max_microamps, 0666, show_max_uA, set_max_uA); static DEVICE_ATTR(mode, 0666, show_mode, set_mode); -static struct device_attribute *attributes[] = { - &dev_attr_min_microvolts, - &dev_attr_max_microvolts, - &dev_attr_min_microamps, - &dev_attr_max_microamps, - &dev_attr_mode, +static struct attribute *regulator_virtual_attributes[] = { + &dev_attr_min_microvolts.attr, + &dev_attr_max_microvolts.attr, + &dev_attr_min_microamps.attr, + &dev_attr_max_microamps.attr, + &dev_attr_mode.attr, + NULL }; -static int regulator_virtual_consumer_probe(struct platform_device *pdev) +static const struct attribute_group regulator_virtual_attr_group = { + .attrs = regulator_virtual_attributes, +}; + +static int __devinit regulator_virtual_probe(struct platform_device *pdev) { char *reg_id = pdev->dev.platform_data; struct virtual_consumer_data *drvdata; - int ret, i; + int ret; drvdata = kzalloc(sizeof(struct virtual_consumer_data), GFP_KERNEL); - if (drvdata == NULL) { + if (drvdata == NULL) return -ENOMEM; - } mutex_init(&drvdata->lock); @@ -299,13 +303,12 @@ static int regulator_virtual_consumer_probe(struct platform_device *pdev) goto err; } - for (i = 0; i < ARRAY_SIZE(attributes); i++) { - ret = device_create_file(&pdev->dev, attributes[i]); - if (ret != 0) { - dev_err(&pdev->dev, "Failed to create attr %d: %d\n", - i, ret); - goto err_regulator; - } + ret = sysfs_create_group(&pdev->dev.kobj, + ®ulator_virtual_attr_group); + if (ret != 0) { + dev_err(&pdev->dev, + "Failed to create attribute group: %d\n", ret); + goto err_regulator; } drvdata->mode = regulator_get_mode(drvdata->regulator); @@ -317,37 +320,36 @@ static int regulator_virtual_consumer_probe(struct platform_device *pdev) err_regulator: regulator_put(drvdata->regulator); err: - for (i = 0; i < ARRAY_SIZE(attributes); i++) - device_remove_file(&pdev->dev, attributes[i]); kfree(drvdata); return ret; } -static int regulator_virtual_consumer_remove(struct platform_device *pdev) +static int __devexit regulator_virtual_remove(struct platform_device *pdev) { struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev); - int i; - for (i = 0; i < ARRAY_SIZE(attributes); i++) - device_remove_file(&pdev->dev, attributes[i]); + sysfs_remove_group(&pdev->dev.kobj, ®ulator_virtual_attr_group); + if (drvdata->enabled) regulator_disable(drvdata->regulator); regulator_put(drvdata->regulator); kfree(drvdata); + platform_set_drvdata(pdev, NULL); + return 0; } static struct platform_driver regulator_virtual_consumer_driver = { - .probe = regulator_virtual_consumer_probe, - .remove = regulator_virtual_consumer_remove, + .probe = regulator_virtual_probe, + .remove = __devexit_p(regulator_virtual_remove), .driver = { .name = "reg-virt-consumer", + .owner = THIS_MODULE, }, }; - static int __init regulator_virtual_consumer_init(void) { return platform_driver_register(®ulator_virtual_consumer_driver); From 98bf7c057b35eaf418ac063c4aba4f9f86220b09 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:37:50 -0800 Subject: [PATCH 16/27] Regulators: ab3100 - fix probe and remove annotations Probe and remove methods should not be marked as __init/__exit but rather __devinit/__devexit so that the needed sections stay in memory in presence of CONFIG_HOTPLUG. This is needed even on non hotpluggable buses. Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/ab3100.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index b349db4504b79..7de950959ed23 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -561,7 +561,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { * for all the different regulators. */ -static int __init ab3100_regulators_probe(struct platform_device *pdev) +static int __devinit ab3100_regulators_probe(struct platform_device *pdev) { struct ab3100_platform_data *plfdata = pdev->dev.platform_data; struct ab3100 *ab3100 = platform_get_drvdata(pdev); @@ -641,7 +641,7 @@ static int __init ab3100_regulators_probe(struct platform_device *pdev) return 0; } -static int __exit ab3100_regulators_remove(struct platform_device *pdev) +static int __devexit ab3100_regulators_remove(struct platform_device *pdev) { int i; @@ -659,7 +659,7 @@ static struct platform_driver ab3100_regulators_driver = { .owner = THIS_MODULE, }, .probe = ab3100_regulators_probe, - .remove = __exit_p(ab3100_regulators_remove), + .remove = __devexit_p(ab3100_regulators_remove), }; static __init int ab3100_regulators_init(void) From 8ab3343dc61552cb8ad653e1013b1853cea9bea6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:37:55 -0800 Subject: [PATCH 17/27] Regulators: fixed - annotate probe and remove methods Add __devinit/__devexit markings to probe and remove methids of the driver, change types of variables containing boolean data to boolean, set up driver's owner field so we have proper sysfs link between driver and the module. Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/fixed.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index a3d3bfc24b1f0..d11f7622430bf 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -32,8 +32,8 @@ struct fixed_voltage_data { int microvolts; int gpio; unsigned startup_delay; - unsigned enable_high:1; - unsigned is_enabled:1; + bool enable_high; + bool is_enabled; }; static int fixed_voltage_is_enabled(struct regulator_dev *dev) @@ -49,7 +49,7 @@ static int fixed_voltage_enable(struct regulator_dev *dev) if (gpio_is_valid(data->gpio)) { gpio_set_value_cansleep(data->gpio, data->enable_high); - data->is_enabled = 1; + data->is_enabled = true; } return 0; @@ -61,7 +61,7 @@ static int fixed_voltage_disable(struct regulator_dev *dev) if (gpio_is_valid(data->gpio)) { gpio_set_value_cansleep(data->gpio, !data->enable_high); - data->is_enabled = 0; + data->is_enabled = false; } return 0; @@ -101,7 +101,7 @@ static struct regulator_ops fixed_voltage_ops = { .list_voltage = fixed_voltage_list_voltage, }; -static int regulator_fixed_voltage_probe(struct platform_device *pdev) +static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) { struct fixed_voltage_config *config = pdev->dev.platform_data; struct fixed_voltage_data *drvdata; @@ -174,7 +174,7 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev) /* Regulator without GPIO control is considered * always enabled */ - drvdata->is_enabled = 1; + drvdata->is_enabled = true; } drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev, @@ -202,7 +202,7 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev) return ret; } -static int regulator_fixed_voltage_remove(struct platform_device *pdev) +static int __devexit reg_fixed_voltage_remove(struct platform_device *pdev) { struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); @@ -216,10 +216,11 @@ static int regulator_fixed_voltage_remove(struct platform_device *pdev) } static struct platform_driver regulator_fixed_voltage_driver = { - .probe = regulator_fixed_voltage_probe, - .remove = regulator_fixed_voltage_remove, + .probe = reg_fixed_voltage_probe, + .remove = __devexit_p(reg_fixed_voltage_remove), .driver = { .name = "reg-fixed-voltage", + .owner = THIS_MODULE, }, }; From 24c29020bac8a35729e1f58270b3622abc2941a2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:38:01 -0800 Subject: [PATCH 18/27] Regulators: twl-regulator - mark probe function as __devinit Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/twl-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 5f394bb8701fa..9729d760fb4dd 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -531,7 +531,7 @@ static struct twlreg_info twl_regs[] = { TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0, 0x21) }; -static int twlreg_probe(struct platform_device *pdev) +static int __devinit twlreg_probe(struct platform_device *pdev) { int i; struct twlreg_info *info; From 54d13ab1038911249fc5769efee87fed000623c0 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:38:06 -0800 Subject: [PATCH 19/27] Regulators: tps65023-regulator - mark probe method as __devinit Also move error handling in probe() out of line and do not bother to reset fields in structures that are about to be freed. Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps65023-regulator.c | 35 ++++++++++++-------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 07fda0a75adf4..1f183543bdbd6 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -457,8 +457,8 @@ static struct regulator_ops tps65023_ldo_ops = { .list_voltage = tps65023_ldo_list_voltage, }; -static -int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int __devinit tps_65023_probe(struct i2c_client *client, + const struct i2c_device_id *id) { static int desc_id; const struct tps_info *info = (void *)id->driver_data; @@ -466,6 +466,7 @@ int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id) struct regulator_dev *rdev; struct tps_pmic *tps; int i; + int error; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; @@ -475,7 +476,6 @@ int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id) * coming from the board-evm file. */ init_data = client->dev.platform_data; - if (!init_data) return -EIO; @@ -502,21 +502,12 @@ int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Register the regulators */ rdev = regulator_register(&tps->desc[i], &client->dev, - init_data, tps); + init_data, tps); if (IS_ERR(rdev)) { dev_err(&client->dev, "failed to register %s\n", id->name); - - /* Unregister */ - while (i) - regulator_unregister(tps->rdev[--i]); - - tps->client = NULL; - - /* clear the client data in i2c */ - i2c_set_clientdata(client, NULL); - kfree(tps); - return PTR_ERR(rdev); + error = PTR_ERR(rdev); + goto fail; } /* Save regulator for cleanup */ @@ -526,6 +517,13 @@ int tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id) i2c_set_clientdata(client, tps); return 0; + + fail: + while (--i >= 0) + regulator_unregister(tps->rdev[i]); + + kfree(tps); + return error; } /** @@ -539,13 +537,12 @@ static int __devexit tps_65023_remove(struct i2c_client *client) struct tps_pmic *tps = i2c_get_clientdata(client); int i; + /* clear the client data in i2c */ + i2c_set_clientdata(client, NULL); + for (i = 0; i < TPS65023_NUM_REGULATOR; i++) regulator_unregister(tps->rdev[i]); - tps->client = NULL; - - /* clear the client data in i2c */ - i2c_set_clientdata(client, NULL); kfree(tps); return 0; From 56c23492b2266fcbe38f26a3de217902c70a895b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:38:12 -0800 Subject: [PATCH 20/27] Regulators: tps6507x-regulator - mark probe method as __devinit Also move error handling in probe() out of line and do not bother to reset fields in structures that are about to be freed. Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps6507x-regulator.c | 34 ++++++++++++-------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index f8a6dfbef7518..c2a9539acd723 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -538,8 +538,8 @@ static struct regulator_ops tps6507x_ldo_ops = { .list_voltage = tps6507x_ldo_list_voltage, }; -static -int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int __devinit tps_6507x_probe(struct i2c_client *client, + const struct i2c_device_id *id) { static int desc_id; const struct tps_info *info = (void *)id->driver_data; @@ -547,6 +547,7 @@ int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id) struct regulator_dev *rdev; struct tps_pmic *tps; int i; + int error; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -557,7 +558,6 @@ int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id) * coming from the board-evm file. */ init_data = client->dev.platform_data; - if (!init_data) return -EIO; @@ -586,18 +586,8 @@ int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id) if (IS_ERR(rdev)) { dev_err(&client->dev, "failed to register %s\n", id->name); - - /* Unregister */ - while (i) - regulator_unregister(tps->rdev[--i]); - - tps->client = NULL; - - /* clear the client data in i2c */ - i2c_set_clientdata(client, NULL); - - kfree(tps); - return PTR_ERR(rdev); + error = PTR_ERR(rdev); + goto fail; } /* Save regulator for cleanup */ @@ -607,6 +597,13 @@ int tps_6507x_probe(struct i2c_client *client, const struct i2c_device_id *id) i2c_set_clientdata(client, tps); return 0; + +fail: + while (--i >= 0) + regulator_unregister(tps->rdev[i]); + + kfree(tps); + return error; } /** @@ -620,13 +617,12 @@ static int __devexit tps_6507x_remove(struct i2c_client *client) struct tps_pmic *tps = i2c_get_clientdata(client); int i; + /* clear the client data in i2c */ + i2c_set_clientdata(client, NULL); + for (i = 0; i < TPS6507X_NUM_REGULATOR; i++) regulator_unregister(tps->rdev[i]); - tps->client = NULL; - - /* clear the client data in i2c */ - i2c_set_clientdata(client, NULL); kfree(tps); return 0; From ebbed04fe7f89c9cb00df32f7123bc1b84b456cb Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:38:17 -0800 Subject: [PATCH 21/27] Regulators: lp3971 - fail if platform data was not supplied There is no point in completing probe if platform data is missing so let's abort loading early. Also, use kcalloc when allocating several instances of the same data structure and mark setup_regulators() as __devinit since it is only called from lp3971_i2c_probe() which is __devinit. Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/lp3971.c | 58 +++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 3ea639f29e95a..f5532ed79272c 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -431,20 +431,20 @@ static int lp3971_set_bits(struct lp3971 *lp3971, u8 reg, u16 mask, u16 val) return ret; } -static int setup_regulators(struct lp3971 *lp3971, - struct lp3971_platform_data *pdata) +static int __devinit setup_regulators(struct lp3971 *lp3971, + struct lp3971_platform_data *pdata) { int i, err; - int num_regulators = pdata->num_regulators; - lp3971->num_regulators = num_regulators; - lp3971->rdev = kzalloc(sizeof(struct regulator_dev *) * num_regulators, - GFP_KERNEL); + + lp3971->num_regulators = pdata->num_regulators; + lp3971->rdev = kcalloc(pdata->num_regulators, + sizeof(struct regulator_dev *), GFP_KERNEL); /* Instantiate the regulators */ - for (i = 0; i < num_regulators; i++) { - int id = pdata->regulators[i].id; - lp3971->rdev[i] = regulator_register(®ulators[id], - lp3971->dev, pdata->regulators[i].initdata, lp3971); + for (i = 0; i < pdata->num_regulators; i++) { + struct lp3971_regulator_subdev *reg = &pdata->regulators[i]; + lp3971->rdev[i] = regulator_register(®ulators[reg->id], + lp3971->dev, reg->initdata, lp3971); if (IS_ERR(lp3971->rdev[i])) { err = PTR_ERR(lp3971->rdev[i]); @@ -455,10 +455,10 @@ static int setup_regulators(struct lp3971 *lp3971, } return 0; + error: - for (i = 0; i < num_regulators; i++) - if (lp3971->rdev[i]) - regulator_unregister(lp3971->rdev[i]); + while (--i >= 0) + regulator_unregister(lp3971->rdev[i]); kfree(lp3971->rdev); lp3971->rdev = NULL; return err; @@ -472,15 +472,17 @@ static int __devinit lp3971_i2c_probe(struct i2c_client *i2c, int ret; u16 val; - lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL); - if (lp3971 == NULL) { - ret = -ENOMEM; - goto err; + if (!pdata) { + dev_dbg(&i2c->dev, "No platform init data supplied\n"); + return -ENODEV; } + lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL); + if (lp3971 == NULL) + return -ENOMEM; + lp3971->i2c = i2c; lp3971->dev = &i2c->dev; - i2c_set_clientdata(i2c, lp3971); mutex_init(&lp3971->io_lock); @@ -493,19 +495,15 @@ static int __devinit lp3971_i2c_probe(struct i2c_client *i2c, goto err_detect; } - if (pdata) { - ret = setup_regulators(lp3971, pdata); - if (ret < 0) - goto err_detect; - } else - dev_warn(lp3971->dev, "No platform init data supplied\n"); + ret = setup_regulators(lp3971, pdata); + if (ret < 0) + goto err_detect; + i2c_set_clientdata(i2c, lp3971); return 0; err_detect: - i2c_set_clientdata(i2c, NULL); kfree(lp3971); -err: return ret; } @@ -513,11 +511,13 @@ static int __devexit lp3971_i2c_remove(struct i2c_client *i2c) { struct lp3971 *lp3971 = i2c_get_clientdata(i2c); int i; + + i2c_set_clientdata(i2c, NULL); + for (i = 0; i < lp3971->num_regulators; i++) - if (lp3971->rdev[i]) - regulator_unregister(lp3971->rdev[i]); + regulator_unregister(lp3971->rdev[i]); + kfree(lp3971->rdev); - i2c_set_clientdata(i2c, NULL); kfree(lp3971); return 0; From bd88c9b285b76f329243d5c80960c23a04004043 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:38:23 -0800 Subject: [PATCH 22/27] Regulators: max1586 - annotate probe and remove methods Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/max1586.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index 2c082d3ef4846..a49fc952c9a98 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -179,8 +179,8 @@ static struct regulator_desc max1586_reg[] = { }, }; -static int max1586_pmic_probe(struct i2c_client *client, - const struct i2c_device_id *i2c_id) +static int __devinit max1586_pmic_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) { struct regulator_dev **rdev; struct max1586_platform_data *pdata = client->dev.platform_data; @@ -235,7 +235,7 @@ static int max1586_pmic_probe(struct i2c_client *client, return ret; } -static int max1586_pmic_remove(struct i2c_client *client) +static int __devexit max1586_pmic_remove(struct i2c_client *client) { struct regulator_dev **rdev = i2c_get_clientdata(client); int i; @@ -257,9 +257,10 @@ MODULE_DEVICE_TABLE(i2c, max1586_id); static struct i2c_driver max1586_pmic_driver = { .probe = max1586_pmic_probe, - .remove = max1586_pmic_remove, + .remove = __devexit_p(max1586_pmic_remove), .driver = { .name = "max1586", + .owner = THIS_MODULE, }, .id_table = max1586_id, }; From 308f100f2ef8e8b9f65b0f3c2a1c7044ec0e65a5 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:38:28 -0800 Subject: [PATCH 23/27] Regulators: max8660 - annotate probe and remove methods Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/max8660.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index acc2fb7b60877..f12f1bb62138f 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -345,8 +345,8 @@ static struct regulator_desc max8660_reg[] = { }, }; -static int max8660_probe(struct i2c_client *client, - const struct i2c_device_id *i2c_id) +static int __devinit 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; @@ -354,7 +354,7 @@ static int max8660_probe(struct i2c_client *client, int boot_on, i, id, ret = -EINVAL; if (pdata->num_subdevs > MAX8660_V_END) { - dev_err(&client->dev, "Too much regulators found!\n"); + dev_err(&client->dev, "Too many regulators found!\n"); goto out; } @@ -462,7 +462,7 @@ static int max8660_probe(struct i2c_client *client, return ret; } -static int max8660_remove(struct i2c_client *client) +static int __devexit max8660_remove(struct i2c_client *client) { struct regulator_dev **rdev = i2c_get_clientdata(client); int i; @@ -485,9 +485,10 @@ MODULE_DEVICE_TABLE(i2c, max8660_id); static struct i2c_driver max8660_driver = { .probe = max8660_probe, - .remove = max8660_remove, + .remove = __devexit_p(max8660_remove), .driver = { .name = "max8660", + .owner = THIS_MODULE, }, .id_table = max8660_id, }; From 6a74857d1b75e4f58f1a09472b972058499258a4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:38:33 -0800 Subject: [PATCH 24/27] Regulators: pcap-regulator - clean up driver data after removal It is a good tone to reset driver data after unbinding the device. Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/pcap-regulator.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c index 33d7d899e0307..29d0566379ae4 100644 --- a/drivers/regulator/pcap-regulator.c +++ b/drivers/regulator/pcap-regulator.c @@ -288,16 +288,18 @@ static int __devexit pcap_regulator_remove(struct platform_device *pdev) struct regulator_dev *rdev = platform_get_drvdata(pdev); regulator_unregister(rdev); + platform_set_drvdata(pdev, NULL); return 0; } static struct platform_driver pcap_regulator_driver = { .driver = { - .name = "pcap-regulator", + .name = "pcap-regulator", + .owner = THIS_MODULE, }, - .probe = pcap_regulator_probe, - .remove = __devexit_p(pcap_regulator_remove), + .probe = pcap_regulator_probe, + .remove = __devexit_p(pcap_regulator_remove), }; static int __init pcap_regulator_init(void) From eb66d565e8eb73ab0c471fd3dac822dc663fe8ea Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:38:39 -0800 Subject: [PATCH 25/27] Regulators: wm831x-xxx - clean up driver data after removal It is a good tone to reset driver data after unbinding the device. Also set up drivers owner. Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/wm831x-dcdc.c | 12 ++++++++++++ drivers/regulator/wm831x-isink.c | 3 +++ drivers/regulator/wm831x-ldo.c | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 0a6577577e8d6..6e18e56d850ba 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -600,6 +600,8 @@ static __devexit int wm831x_buckv_remove(struct platform_device *pdev) struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x *wm831x = dcdc->wm831x; + platform_set_drvdata(pdev, NULL); + 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); @@ -615,6 +617,7 @@ static struct platform_driver wm831x_buckv_driver = { .remove = __devexit_p(wm831x_buckv_remove), .driver = { .name = "wm831x-buckv", + .owner = THIS_MODULE, }, }; @@ -769,6 +772,8 @@ static __devexit int wm831x_buckp_remove(struct platform_device *pdev) struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x *wm831x = dcdc->wm831x; + platform_set_drvdata(pdev, NULL); + wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); regulator_unregister(dcdc->regulator); kfree(dcdc); @@ -781,6 +786,7 @@ static struct platform_driver wm831x_buckp_driver = { .remove = __devexit_p(wm831x_buckp_remove), .driver = { .name = "wm831x-buckp", + .owner = THIS_MODULE, }, }; @@ -895,6 +901,8 @@ static __devexit int wm831x_boostp_remove(struct platform_device *pdev) struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x *wm831x = dcdc->wm831x; + platform_set_drvdata(pdev, NULL); + wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); regulator_unregister(dcdc->regulator); kfree(dcdc); @@ -907,6 +915,7 @@ static struct platform_driver wm831x_boostp_driver = { .remove = __devexit_p(wm831x_boostp_remove), .driver = { .name = "wm831x-boostp", + .owner = THIS_MODULE, }, }; @@ -979,6 +988,8 @@ static __devexit int wm831x_epe_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + regulator_unregister(dcdc->regulator); kfree(dcdc); @@ -990,6 +1001,7 @@ static struct platform_driver wm831x_epe_driver = { .remove = __devexit_p(wm831x_epe_remove), .driver = { .name = "wm831x-epe", + .owner = THIS_MODULE, }, }; diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 48857008758cc..ca0f6b6c384b3 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -222,6 +222,8 @@ static __devexit int wm831x_isink_remove(struct platform_device *pdev) struct wm831x_isink *isink = platform_get_drvdata(pdev); struct wm831x *wm831x = isink->wm831x; + platform_set_drvdata(pdev, NULL); + wm831x_free_irq(wm831x, platform_get_irq(pdev, 0), isink); regulator_unregister(isink->regulator); @@ -235,6 +237,7 @@ static struct platform_driver wm831x_isink_driver = { .remove = __devexit_p(wm831x_isink_remove), .driver = { .name = "wm831x-isink", + .owner = THIS_MODULE, }, }; diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 61e02ac2fda33..d2406c1519a19 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -371,6 +371,8 @@ static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev) struct wm831x_ldo *ldo = platform_get_drvdata(pdev); struct wm831x *wm831x = ldo->wm831x; + platform_set_drvdata(pdev, NULL); + wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), ldo); regulator_unregister(ldo->regulator); kfree(ldo); @@ -383,6 +385,7 @@ static struct platform_driver wm831x_gp_ldo_driver = { .remove = __devexit_p(wm831x_gp_ldo_remove), .driver = { .name = "wm831x-ldo", + .owner = THIS_MODULE, }, }; @@ -640,6 +643,7 @@ static struct platform_driver wm831x_aldo_driver = { .remove = __devexit_p(wm831x_aldo_remove), .driver = { .name = "wm831x-aldo", + .owner = THIS_MODULE, }, }; @@ -811,6 +815,7 @@ static struct platform_driver wm831x_alive_ldo_driver = { .remove = __devexit_p(wm831x_alive_ldo_remove), .driver = { .name = "wm831x-alive-ldo", + .owner = THIS_MODULE, }, }; From 598b3578ab9ee8e3eef322128485719668d8b93b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 23 Feb 2010 23:38:44 -0800 Subject: [PATCH 26/27] Regulators: wm8994 - clean up driver data after removal It is a good tone to reset driver data after unbinding the device. Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/wm8994-regulator.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index d09e018b31773..95454a4637b7e 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -26,7 +26,7 @@ struct wm8994_ldo { int enable; - int is_enabled; + bool is_enabled; struct regulator_dev *regulator; struct wm8994 *wm8994; }; @@ -43,7 +43,7 @@ static int wm8994_ldo_enable(struct regulator_dev *rdev) return 0; gpio_set_value(ldo->enable, 1); - ldo->is_enabled = 1; + ldo->is_enabled = true; return 0; } @@ -57,7 +57,7 @@ static int wm8994_ldo_disable(struct regulator_dev *rdev) return -EINVAL; gpio_set_value(ldo->enable, 0); - ldo->is_enabled = 0; + ldo->is_enabled = false; return 0; } @@ -218,7 +218,7 @@ static __devinit int wm8994_ldo_probe(struct platform_device *pdev) ldo->wm8994 = wm8994; - ldo->is_enabled = 1; + ldo->is_enabled = true; if (pdata->ldo[id].enable && gpio_is_valid(pdata->ldo[id].enable)) { ldo->enable = pdata->ldo[id].enable; @@ -263,6 +263,8 @@ static __devexit int wm8994_ldo_remove(struct platform_device *pdev) { struct wm8994_ldo *ldo = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + regulator_unregister(ldo->regulator); if (gpio_is_valid(ldo->enable)) gpio_free(ldo->enable); @@ -276,6 +278,7 @@ static struct platform_driver wm8994_ldo_driver = { .remove = __devexit_p(wm8994_ldo_remove), .driver = { .name = "wm8994-ldo", + .owner = THIS_MODULE, }, }; From 1ad02bbce64e5226b0582af85df4e481e2f6b7b9 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 25 Feb 2010 01:55:37 -0800 Subject: [PATCH 27/27] Regulators: wm8400 - cleanup platform driver data handling On Wed, Feb 24, 2010 at 08:40:56PM +0000, Mark Brown wrote: > On Wed, Feb 24, 2010 at 11:21:26AM -0800, Dmitry Torokhov wrote: > > On Wed, Feb 24, 2010 at 07:14:03PM +0000, Mark Brown wrote: > > > > This doesn't help unless you also provide a way for users to obtain a > > > struct wm8400. > > > Why would they need it? Only code that creates instances of wm8400 needs > > to know the definition of the sturcture, the rest can simply pass the > > pointer around. > > > I guess there is disconnect between us and I do not see any users of > > wm8400_register_regulator() in linux-next... Is there another tree I > > could peek at? > > There are no users in mainline. This would be called by board specific > code from the init callback of the wm8400 - you'd need to pass that > callback the struct wm8400. > > In any case, this is clearly an unrelated change to whatever else you > were doing to the driver so should be split off into a separate patch, > but if this is being changed at all then it'd be much more sensible to > change it to use a more modern pattern which completely removes the > wm8400_register_regulator() function and just uses platform data. Fair enough, I removed the offending part, updated patch below. -- Dmitry regulator: wm8400 - cleanup platform driver data handling Driver data set by platform_set_drvdata() is for private use of the driver currently bound to teh device and not for use by parent, subsystem and anyone else. Signed-off-by: Dmitry Torokhov Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/wm8400-regulator.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index d9a2c988c6e7b..924c7eb29ee99 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -317,14 +317,17 @@ static struct regulator_desc regulators[] = { static int __devinit wm8400_regulator_probe(struct platform_device *pdev) { + struct wm8400 *wm8400 = container_of(pdev, struct wm8400, regulators[pdev->id]); struct regulator_dev *rdev; rdev = regulator_register(®ulators[pdev->id], &pdev->dev, - pdev->dev.platform_data, dev_get_drvdata(&pdev->dev)); + pdev->dev.platform_data, wm8400); if (IS_ERR(rdev)) return PTR_ERR(rdev); + platform_set_drvdata(pdev, rdev); + return 0; } @@ -332,6 +335,7 @@ static int __devexit wm8400_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); regulator_unregister(rdev); return 0; @@ -370,7 +374,6 @@ int wm8400_register_regulator(struct device *dev, int reg, wm8400->regulators[reg].id = reg; wm8400->regulators[reg].dev.parent = dev; wm8400->regulators[reg].dev.platform_data = initdata; - dev_set_drvdata(&wm8400->regulators[reg].dev, wm8400); return platform_device_register(&wm8400->regulators[reg]); }