From 3a7d021b5e1de205c964c30d0ceda660501dc107 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 30 Jul 2011 06:13:09 +0200 Subject: regulator: aat2870-regulator.c needs module.h aat2870-regulator.c needs to include linux/module.h to fix multiple build errors. drivers/regulator/aat2870-regulator.c:145: error: 'THIS_MODULE' undeclared here (not in a function) drivers/regulator/aat2870-regulator.c:230: warning: type defaults to 'int' in declaration of 'MODULE_DESCRIPTION' drivers/regulator/aat2870-regulator.c:231: warning: type defaults to 'int' in declaration of 'MODULE_LICENSE' drivers/regulator/aat2870-regulator.c:232: warning: type defaults to 'int' in declaration of 'MODULE_AUTHOR' Signed-off-by: Randy Dunlap Signed-off-by: Liam Girdwood --- drivers/regulator/aat2870-regulator.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c index cd4104542f0..5abeb3ac3e8 100644 --- a/drivers/regulator/aat2870-regulator.c +++ b/drivers/regulator/aat2870-regulator.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 88585b83e011e661bcd2cf48d47f4634bdd92a14 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Mon, 18 Jul 2011 11:02:17 +0800 Subject: regulator: tps65912: Remove unused define of TPS65912_MAX_REG_ID Currently we define TPS65912_MAX_REG_ID as TPS65912_REG_LDO_10, but TPS65912_REG_LDO_10 is not defined at all. ( It looks like a typo of TPS65912_REG_LDO10 ) Currently, TPS65912_MAX_REG_ID is not used in this driver, it is safe to just remove it. Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps65912-regulator.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index 3a9313e00fa..39d4a1749e7 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -43,8 +43,6 @@ #define TPS65912_REG_LDO9 12 #define TPS65912_REG_LDO10 13 -#define TPS65912_MAX_REG_ID TPS65912_REG_LDO_10 - /* Number of step-down converters available */ #define TPS65912_NUM_DCDC 4 -- cgit v1.2.3 From 99cd25ce10249c7f9d7172fe2ed266b023cd3323 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 4 Aug 2011 16:18:11 +0800 Subject: regulator: 88pm8607: Fix off-by-one value range checking in the case of no id is matched In the case of no id is matched, the variable i is equal to ARRAY_SIZE(pm8607_regulator_info). Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/88pm8607.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index d63fddb0fbb..e821b2159b4 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -412,7 +412,7 @@ static int __devinit pm8607_regulator_probe(struct platform_device *pdev) if (info->desc.id == res->start) break; } - if ((i < 0) || (i > PM8607_ID_RG_MAX)) { + if (i == ARRAY_SIZE(pm8607_regulator_info)) { dev_err(&pdev->dev, "Failed to find regulator %llu\n", (unsigned long long)res->start); return -EINVAL; -- cgit v1.2.3 From e69c499790a35ef5ad164fd99f0b691e138640da Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 2 Aug 2011 12:54:56 +0800 Subject: regulator: Ensure enough enable time for max8649 Integer division may truncate the result, thus max8649_enable_time() may return slightly shorter enable time. This patch fixes it. Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/max8649.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index 30eb9e54f7e..1062cf9f02d 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -221,7 +221,7 @@ static int max8649_enable_time(struct regulator_dev *rdev) ret = (ret & MAX8649_RAMP_MASK) >> 5; rate = (32 * 1000) >> ret; /* uV/uS */ - return (voltage / rate); + return DIV_ROUND_UP(voltage, rate); } static int max8649_set_mode(struct regulator_dev *rdev, unsigned int mode) -- cgit v1.2.3 From dd32e11cdbc73e570f9fe7425fa820c97070a2c4 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 3 Aug 2011 09:58:03 +0900 Subject: regulator: max8952: removed unused mutex. This patch removes a mutex that is never used in the driver. Reported-by: Axel Lin Signed-off-by: MyungJoo Ham Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/max8952.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index 486ed8141fc..3883d85c5b8 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -47,7 +46,6 @@ enum { struct max8952_data { struct i2c_client *client; struct device *dev; - struct mutex mutex; struct max8952_platform_data *pdata; struct regulator_dev *rdev; @@ -208,7 +206,6 @@ static int __devinit max8952_pmic_probe(struct i2c_client *client, max8952->client = client; max8952->dev = &client->dev; max8952->pdata = pdata; - mutex_init(&max8952->mutex); max8952->rdev = regulator_register(®ulator, max8952->dev, &pdata->reg_data, max8952); -- cgit v1.2.3 From 0fcdb109a50afb8b26709175937ba3de9486770b Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 2 Aug 2011 15:34:12 +0800 Subject: regulator: tps6507x: Remove num_voltages array We can get n_voltages for each regulator from table_len of struct tps_info. Signed-off-by: Axel Lin Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps6507x-regulator.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index bfffabc21ed..bdef70365f5 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -90,12 +90,6 @@ static const u16 LDO2_VSEL_table[] = { 3000, 3100, 3200, 3300, }; -static unsigned int num_voltages[] = {ARRAY_SIZE(VDCDCx_VSEL_table), - ARRAY_SIZE(VDCDCx_VSEL_table), - ARRAY_SIZE(VDCDCx_VSEL_table), - ARRAY_SIZE(LDO1_VSEL_table), - ARRAY_SIZE(LDO2_VSEL_table)}; - struct tps_info { const char *name; unsigned min_uV; @@ -598,7 +592,7 @@ int tps6507x_pmic_probe(struct platform_device *pdev) tps->desc[i].name = info->name; tps->desc[i].id = i; - tps->desc[i].n_voltages = num_voltages[i]; + tps->desc[i].n_voltages = info->table_len; tps->desc[i].ops = (i > TPS6507X_DCDC_3 ? &tps6507x_pmic_ldo_ops : &tps6507x_pmic_dcdc_ops); tps->desc[i].type = REGULATOR_VOLTAGE; -- cgit v1.2.3 From fc999b83799074832367d3cfd724c341c849a7da Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Thu, 4 Aug 2011 13:33:49 +0200 Subject: regulator: tps65023: Fixes i2c configuration issues Allow i2c core voltage adjustments by clearing CORE ADJ Allowed bit in CTRL2 Signed-off-by: Marcus Folkesson Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps65023-regulator.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 701a5900f83..a81abd4871b 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -63,6 +63,13 @@ #define TPS65023_REG_CTRL_LDO2_EN BIT(2) #define TPS65023_REG_CTRL_LDO1_EN BIT(1) +/* REG_CTRL2 bitfields */ +#define TPS65023_REG_CTRL2_GO BIT(7) +#define TPS65023_REG_CTRL2_CORE_ADJ BIT(6) +#define TPS65023_REG_CTRL2_DCDC2 BIT(2) +#define TPS65023_REG_CTRL2_DCDC1 BIT(2) +#define TPS65023_REG_CTRL2_DCDC3 BIT(0) + /* LDO_CTRL bitfields */ #define TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_id) ((ldo_id)*4) #define TPS65023_LDO_CTRL_LDOx_MASK(ldo_id) (0xF0 >> ((ldo_id)*4)) @@ -475,6 +482,10 @@ static int __devinit tps_65023_probe(struct i2c_client *client, i2c_set_clientdata(client, tps); + /* Enable setting output voltage by I2C */ + tps_65023_clear_bits(tps, TPS65023_REG_CON_CTRL2, + TPS65023_REG_CTRL2_CORE_ADJ); + return 0; fail: -- cgit v1.2.3 From 500c524aad173864a58e128d0be9713fa5846471 Mon Sep 17 00:00:00 2001 From: Xin Xie Date: Tue, 9 Aug 2011 18:47:50 +0800 Subject: regulator: tps6586x: add SMx slew rate setting Add output vlotage slew rate setting for SM0/SM1 Signed-off-by: Xin Xie Signed-off-by: Danny Huang Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps6586x-regulator.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index bb04a75a4c9..dbcf09d5080 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -332,6 +332,36 @@ static inline int tps6586x_regulator_preinit(struct device *parent, 1 << ri->enable_bit[1]); } +static int tps6586x_regulator_set_slew_rate(struct platform_device *pdev) +{ + struct device *parent = pdev->dev.parent; + struct regulator_init_data *p = pdev->dev.platform_data; + struct tps6586x_settings *setting = p->driver_data; + uint8_t reg; + + if (setting == NULL) + return 0; + + if (!(setting->slew_rate & TPS6586X_SLEW_RATE_SET)) + return 0; + + /* only SM0 and SM1 can have the slew rate settings */ + switch (pdev->id) { + case TPS6586X_ID_SM_0: + reg = TPS6586X_SM0SL; + break; + case TPS6586X_ID_SM_1: + reg = TPS6586X_SM1SL; + break; + default: + dev_warn(&pdev->dev, "Only SM0/SM1 can set slew rate\n"); + return -EINVAL; + } + + return tps6586x_write(parent, reg, + setting->slew_rate & TPS6586X_SLEW_RATE_MASK); +} + static inline struct tps6586x_regulator *find_regulator_info(int id) { struct tps6586x_regulator *ri; @@ -374,7 +404,7 @@ static int __devinit tps6586x_regulator_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rdev); - return 0; + return tps6586x_regulator_set_slew_rate(pdev); } static int __devexit tps6586x_regulator_remove(struct platform_device *pdev) -- cgit v1.2.3 From ba55a9741da6c85176987c15e24383b858749aa2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 23 Aug 2011 17:39:10 +0100 Subject: regulator: Add debugfs file showing the supply map table Useful for working out why things aren't getting plugged together properly. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/core.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d8e6a429e8b..9a33fe2021b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2907,6 +2907,43 @@ void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data) } EXPORT_SYMBOL_GPL(regulator_get_init_drvdata); +#ifdef CONFIG_DEBUG_FS +static ssize_t supply_map_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + ssize_t len, ret = 0; + struct regulator_map *map; + + if (!buf) + return -ENOMEM; + + list_for_each_entry(map, ®ulator_map_list, list) { + len = snprintf(buf + ret, PAGE_SIZE - ret, + "%s -> %s.%s\n", + rdev_get_name(map->regulator), map->dev_name, + map->supply); + if (len >= 0) + ret += len; + if (ret > PAGE_SIZE) { + ret = PAGE_SIZE; + break; + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + kfree(buf); + + return ret; +} + +static const struct file_operations supply_map_fops = { + .read = supply_map_read_file, + .llseek = default_llseek, +}; +#endif + static int __init regulator_init(void) { int ret; @@ -2919,6 +2956,10 @@ static int __init regulator_init(void) pr_warn("regulator: Failed to create debugfs directory\n"); debugfs_root = NULL; } + + if (IS_ERR(debugfs_create_file("supply_map", 0444, debugfs_root, + NULL, &supply_map_fops))) + pr_warn("regulator: Failed to create supplies debugfs\n"); #endif regulator_dummy_init(); -- cgit v1.2.3 From f068ad8cca7532bc42115d61489de00fe57c3909 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Mon, 8 Aug 2011 20:29:32 +0200 Subject: regulator: tps65023: Fixes i2c configuration issues Allow i2c core voltage adjustments by clearing CORE ADJ Allowed bit in CTRL2 Signed-off-by: Marcus Folkesson Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps65023-regulator.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index a81abd4871b..b912daae9fa 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -70,6 +70,13 @@ #define TPS65023_REG_CTRL2_DCDC1 BIT(2) #define TPS65023_REG_CTRL2_DCDC3 BIT(0) +/* REG_CTRL2 bitfields */ +#define TPS65023_REG_CTRL2_GO BIT(7) +#define TPS65023_REG_CTRL2_CORE_ADJ BIT(6) +#define TPS65023_REG_CTRL2_DCDC2 BIT(2) +#define TPS65023_REG_CTRL2_DCDC1 BIT(1) +#define TPS65023_REG_CTRL2_DCDC3 BIT(0) + /* LDO_CTRL bitfields */ #define TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_id) ((ldo_id)*4) #define TPS65023_LDO_CTRL_LDOx_MASK(ldo_id) (0xF0 >> ((ldo_id)*4)) @@ -482,6 +489,10 @@ static int __devinit tps_65023_probe(struct i2c_client *client, i2c_set_clientdata(client, tps); + /* Enable setting output voltage by I2C */ + tps_65023_clear_bits(tps, TPS65023_REG_CON_CTRL2, + TPS65023_REG_CTRL2_CORE_ADJ); + /* Enable setting output voltage by I2C */ tps_65023_clear_bits(tps, TPS65023_REG_CON_CTRL2, TPS65023_REG_CTRL2_CORE_ADJ); -- cgit v1.2.3 From cc17ef3f0463ba17c8767578da8c4e5fbdd4b447 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Mon, 8 Aug 2011 20:29:33 +0200 Subject: regulator: tps65023: Set missing bit for update core-voltage Setting the GO bit in CTRL2 for updating the core voltage Signed-off-by: Marcus Folkesson Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps65023-regulator.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index b912daae9fa..11c9e1f1407 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -284,6 +284,7 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, struct tps_pmic *tps = rdev_get_drvdata(dev); int dcdc = rdev_get_id(dev); int vsel; + int ret; if (dcdc != TPS65023_DCDC_1) return -EINVAL; @@ -306,11 +307,21 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, *selector = vsel; - /* write to the register in case we found a match */ if (vsel == tps->info[dcdc]->table_len) - return -EINVAL; - else - return tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel); + goto failed; + + ret = tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel); + + /* Tell the chip that we have changed the value in DEFCORE + * and its time to update the core voltage + */ + tps_65023_set_bits(tps, TPS65023_REG_CON_CTRL2, + TPS65023_REG_CTRL2_GO); + + return ret; + +failed: + return -EINVAL; } static int tps65023_ldo_get_voltage(struct regulator_dev *dev) -- cgit v1.2.3 From 1c3ede05d123f1484b28fa7c8500a1a29e34e3ba Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Mon, 8 Aug 2011 20:29:34 +0200 Subject: regulator: tps65023: Setting correct core regulator for tps65021 TPS65023 is using VDCDC1 as core regulator and TPS65021 is using VDCDC3. Core-regulator and voltage-tables may differ between different regulators. These two is now passed as driver data. Signed-off-by: Marcus Folkesson Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps65023-regulator.c | 96 ++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 11c9e1f1407..42740ffc0be 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -99,7 +99,7 @@ #define TPS65023_MAX_REG_ID TPS65023_LDO_2 /* Supported voltage values for regulators */ -static const u16 VDCDC1_VSEL_table[] = { +static const u16 VCORE_VSEL_table[] = { 800, 825, 850, 875, 900, 925, 950, 975, 1000, 1025, 1050, 1075, @@ -110,20 +110,20 @@ static const u16 VDCDC1_VSEL_table[] = { 1500, 1525, 1550, 1600, }; -static const u16 LDO1_VSEL_table[] = { + + +/* Supported voltage values for LDO regulators + * for tps65021 and tps65023 */ +static const u16 TPS65023_LDO1_VSEL_table[] = { 1000, 1100, 1300, 1800, 2200, 2600, 2800, 3150, }; -static const u16 LDO2_VSEL_table[] = { +static const u16 TPS65023_LDO2_VSEL_table[] = { 1050, 1200, 1300, 1800, 2500, 2800, 3000, 3300, }; -static unsigned int num_voltages[] = {ARRAY_SIZE(VDCDC1_VSEL_table), - 0, 0, ARRAY_SIZE(LDO1_VSEL_table), - ARRAY_SIZE(LDO2_VSEL_table)}; - /* Regulator specific details */ struct tps_info { const char *name; @@ -141,6 +141,13 @@ struct tps_pmic { struct regulator_dev *rdev[TPS65023_NUM_REGULATOR]; const struct tps_info *info[TPS65023_NUM_REGULATOR]; struct regmap *regmap; + u8 core_regulator; +}; + +/* Struct passed as driver data */ +struct tps_driver_data { + const struct tps_info *info; + u8 core_regulator; }; static int tps_65023_set_bits(struct tps_pmic *tps, u8 reg, u8 mask) @@ -267,7 +274,7 @@ static int tps65023_dcdc_get_voltage(struct regulator_dev *dev) if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) return -EINVAL; - if (dcdc == TPS65023_DCDC_1) { + if (dcdc == tps->core_regulator) { data = tps_65023_reg_read(tps, TPS65023_REG_DEF_CORE); if (data < 0) return data; @@ -286,9 +293,8 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, int vsel; int ret; - if (dcdc != TPS65023_DCDC_1) + if (dcdc != tps->core_regulator) return -EINVAL; - if (min_uV < tps->info[dcdc]->min_uV || min_uV > tps->info[dcdc]->max_uV) return -EINVAL; @@ -387,7 +393,7 @@ static int tps65023_dcdc_list_voltage(struct regulator_dev *dev, if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) return -EINVAL; - if (dcdc == TPS65023_DCDC_1) { + if (dcdc == tps->core_regulator) { if (selector >= tps->info[dcdc]->table_len) return -EINVAL; else @@ -439,7 +445,8 @@ static struct regmap_config tps65023_regmap_config = { static int __devinit tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct tps_info *info = (void *)id->driver_data; + const struct tps_driver_data *drv_data = (void *)id->driver_data; + const struct tps_info *info = drv_data->info; struct regulator_init_data *init_data; struct regulator_dev *rdev; struct tps_pmic *tps; @@ -471,6 +478,7 @@ static int __devinit tps_65023_probe(struct i2c_client *client, /* common for all regulators */ tps->client = client; + tps->core_regulator = drv_data->core_regulator; for (i = 0; i < TPS65023_NUM_REGULATOR; i++, info++, init_data++) { /* Store regulator specific information */ @@ -478,7 +486,7 @@ static int __devinit tps_65023_probe(struct i2c_client *client, tps->desc[i].name = info->name; tps->desc[i].id = i; - tps->desc[i].n_voltages = num_voltages[i]; + tps->desc[i].n_voltages = info->table_len; tps->desc[i].ops = (i > TPS65023_DCDC_3 ? &tps65023_ldo_ops : &tps65023_dcdc_ops); tps->desc[i].type = REGULATOR_VOLTAGE; @@ -540,13 +548,49 @@ static int __devexit tps_65023_remove(struct i2c_client *client) return 0; } +static const struct tps_info tps65021_regs[] = { + { + .name = "VDCDC1", + .min_uV = 3300000, + .max_uV = 3300000, + .fixed = 1, + }, + { + .name = "VDCDC2", + .min_uV = 1800000, + .max_uV = 1800000, + .fixed = 1, + }, + { + .name = "VDCDC3", + .min_uV = 800000, + .max_uV = 1600000, + .table_len = ARRAY_SIZE(VCORE_VSEL_table), + .table = VCORE_VSEL_table, + }, + { + .name = "LDO1", + .min_uV = 1000000, + .max_uV = 3150000, + .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table), + .table = TPS65023_LDO1_VSEL_table, + }, + { + .name = "LDO2", + .min_uV = 1050000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table), + .table = TPS65023_LDO2_VSEL_table, + }, +}; + static const struct tps_info tps65023_regs[] = { { .name = "VDCDC1", .min_uV = 800000, .max_uV = 1600000, - .table_len = ARRAY_SIZE(VDCDC1_VSEL_table), - .table = VDCDC1_VSEL_table, + .table_len = ARRAY_SIZE(VCORE_VSEL_table), + .table = VCORE_VSEL_table, }, { .name = "VDCDC2", @@ -564,23 +608,33 @@ static const struct tps_info tps65023_regs[] = { .name = "LDO1", .min_uV = 1000000, .max_uV = 3150000, - .table_len = ARRAY_SIZE(LDO1_VSEL_table), - .table = LDO1_VSEL_table, + .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table), + .table = TPS65023_LDO1_VSEL_table, }, { .name = "LDO2", .min_uV = 1050000, .max_uV = 3300000, - .table_len = ARRAY_SIZE(LDO2_VSEL_table), - .table = LDO2_VSEL_table, + .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table), + .table = TPS65023_LDO2_VSEL_table, }, }; +static struct tps_driver_data tps65021_drv_data = { + .info = tps65021_regs, + .core_regulator = TPS65023_DCDC_3, +}; + +static struct tps_driver_data tps65023_drv_data = { + .info = tps65023_regs, + .core_regulator = TPS65023_DCDC_1, +}; + static const struct i2c_device_id tps_65023_id[] = { {.name = "tps65023", - .driver_data = (unsigned long) tps65023_regs,}, + .driver_data = (unsigned long) &tps65023_drv_data}, {.name = "tps65021", - .driver_data = (unsigned long) tps65023_regs,}, + .driver_data = (unsigned long) &tps65021_drv_data,}, { }, }; -- cgit v1.2.3 From 437afd2ad6ba252fdbad9a1ad2610992fea55bd6 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Mon, 8 Aug 2011 20:29:35 +0200 Subject: regulator: tps65023: Added support for the similiar TPS65020 chip Defines a new voltage-table and allows registering of the tps65020 device. Signed-off-by: Marcus Folkesson Acked-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/tps65023-regulator.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 42740ffc0be..b02c6708635 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -110,7 +110,16 @@ static const u16 VCORE_VSEL_table[] = { 1500, 1525, 1550, 1600, }; +/* Supported voltage values for LDO regulators for tps65020 */ +static const u16 TPS65020_LDO1_VSEL_table[] = { + 1000, 1050, 1100, 1300, + 1800, 2500, 3000, 3300, +}; +static const u16 TPS65020_LDO2_VSEL_table[] = { + 1000, 1050, 1100, 1300, + 1800, 2500, 3000, 3300, +}; /* Supported voltage values for LDO regulators * for tps65021 and tps65023 */ @@ -548,6 +557,43 @@ static int __devexit tps_65023_remove(struct i2c_client *client) return 0; } +static const struct tps_info tps65020_regs[] = { + { + .name = "VDCDC1", + .min_uV = 3300000, + .max_uV = 3300000, + .fixed = 1, + }, + { + .name = "VDCDC2", + .min_uV = 1800000, + .max_uV = 1800000, + .fixed = 1, + }, + { + .name = "VDCDC3", + .min_uV = 800000, + .max_uV = 1600000, + .table_len = ARRAY_SIZE(VCORE_VSEL_table), + .table = VCORE_VSEL_table, + }, + + { + .name = "LDO1", + .min_uV = 1000000, + .max_uV = 3150000, + .table_len = ARRAY_SIZE(TPS65020_LDO1_VSEL_table), + .table = TPS65020_LDO1_VSEL_table, + }, + { + .name = "LDO2", + .min_uV = 1050000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(TPS65020_LDO2_VSEL_table), + .table = TPS65020_LDO2_VSEL_table, + }, +}; + static const struct tps_info tps65021_regs[] = { { .name = "VDCDC1", @@ -620,6 +666,11 @@ static const struct tps_info tps65023_regs[] = { }, }; +static struct tps_driver_data tps65020_drv_data = { + .info = tps65020_regs, + .core_regulator = TPS65023_DCDC_3, +}; + static struct tps_driver_data tps65021_drv_data = { .info = tps65021_regs, .core_regulator = TPS65023_DCDC_3, @@ -635,6 +686,8 @@ static const struct i2c_device_id tps_65023_id[] = { .driver_data = (unsigned long) &tps65023_drv_data}, {.name = "tps65021", .driver_data = (unsigned long) &tps65021_drv_data,}, + {.name = "tps65020", + .driver_data = (unsigned long) &tps65020_drv_data}, { }, }; -- cgit v1.2.3 From e3efe6669bf9c3cbd955b5a2976c70d79e8fd745 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 19 Sep 2011 18:51:39 -0700 Subject: regulator: remove duplicate REG_CTRL2 defines in tps65023 There are two sets of defines for the REG_CTRL2 bitfields and one of them has TPS65023_REG_CTRL2_DCDC1 defined incorrectly. Remove the duplicates and leave the correct one for TPS65023_REG_CTRL2_DCDC1. This fixes the following sparse warnings: drivers/regulator/tps65023-regulator.c:77:9: warning: preprocessor token TPS65023_REG_CTRL2_DCDC1 redefined drivers/regulator/tps65023-regulator.c:70:9: this was the original definition Signed-off-by: H Hartley Sweeten Signed-off-by: Mark Brown --- drivers/regulator/tps65023-regulator.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index b02c6708635..9fb4c7b8175 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -63,13 +63,6 @@ #define TPS65023_REG_CTRL_LDO2_EN BIT(2) #define TPS65023_REG_CTRL_LDO1_EN BIT(1) -/* REG_CTRL2 bitfields */ -#define TPS65023_REG_CTRL2_GO BIT(7) -#define TPS65023_REG_CTRL2_CORE_ADJ BIT(6) -#define TPS65023_REG_CTRL2_DCDC2 BIT(2) -#define TPS65023_REG_CTRL2_DCDC1 BIT(2) -#define TPS65023_REG_CTRL2_DCDC3 BIT(0) - /* REG_CTRL2 bitfields */ #define TPS65023_REG_CTRL2_GO BIT(7) #define TPS65023_REG_CTRL2_CORE_ADJ BIT(6) -- cgit v1.2.3 From 3f0292ae8bb100cc8f96106a3de277df48134887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Wed, 5 Oct 2011 12:27:05 +0200 Subject: regulator: Add driver for gpio-controlled regulators This patch adds support for regulators that can be controlled via gpios. Examples for such regulators are the TI-tps65024x voltage regulators with 4 fixed and 1 runtime-switchable voltage regulators or the TI-bq240XX charger regulators. The number of controlling gpios is not limited, the mapping between voltage/current and target gpio state is done via the states map and the driver can be used for either voltage or current regulators. A mapping for a regulator with two GPIOs could look like: gpios = { { .gpio = GPIO1, .flags = GPIOF_OUT_INIT_HIGH, .label = "gpio name 1" }, { .gpio = GPIO2, .flags = GPIOF_OUT_INIT_LOW, .label = "gpio name 2" }, } The flags element of the gpios array determines the initial state of the gpio, set during probe. The initial state of the regulator is also calculated from these values states = { { .value = volt_or_cur1, .gpios = (0 << 1) | (0 << 0) }, { .value = volt_or_cur2, .gpios = (0 << 1) | (1 << 0) }, { .value = volt_or_cur3, .gpios = (1 << 1) | (0 << 0) }, { .value = volt_or_cur4, .gpios = (1 << 1) | (1 << 0) }, } The target-state for the n-th gpio is determined by the n-th bit in the bitfield of the target-value. Signed-off-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/gpio-regulator.c | 357 +++++++++++++++++++++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 drivers/regulator/gpio-regulator.c (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index c7fd2c0e3f2..5e0c7b66473 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -64,6 +64,15 @@ config REGULATOR_USERSPACE_CONSUMER If unsure, say no. +config REGULATOR_GPIO + tristate "GPIO regulator support" + help + This driver provides support for regulators that can be + controlled via gpios. + It is capable of supporting current and voltage regulators + and the platform has to provide a mapping of GPIO-states + to target volts/amps. + config REGULATOR_BQ24022 tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" help diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 040d5aa6353..93a6318f532 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o +obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c new file mode 100644 index 00000000000..abf32ad6f57 --- /dev/null +++ b/drivers/regulator/gpio-regulator.c @@ -0,0 +1,357 @@ +/* + * gpio-regulator.c + * + * Copyright 2011 Heiko Stuebner + * + * based on fixed.c + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * Copyright (c) 2009 Nokia Corporation + * Roger Quadros + * + * 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 +#include +#include +#include +#include + +struct gpio_regulator_data { + struct regulator_desc desc; + struct regulator_dev *dev; + + int enable_gpio; + bool enable_high; + bool is_enabled; + unsigned startup_delay; + + struct gpio *gpios; + int nr_gpios; + + struct gpio_regulator_state *states; + int nr_states; + + int state; +}; + +static int gpio_regulator_is_enabled(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + return data->is_enabled; +} + +static int gpio_regulator_enable(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + if (gpio_is_valid(data->enable_gpio)) { + gpio_set_value_cansleep(data->enable_gpio, data->enable_high); + data->is_enabled = true; + } + + return 0; +} + +static int gpio_regulator_disable(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + if (gpio_is_valid(data->enable_gpio)) { + gpio_set_value_cansleep(data->enable_gpio, !data->enable_high); + data->is_enabled = false; + } + + return 0; +} + +static int gpio_regulator_enable_time(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + return data->startup_delay; +} + +static int gpio_regulator_get_value(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + int ptr; + + for (ptr = 0; ptr < data->nr_states; ptr++) + if (data->states[ptr].gpios == data->state) + return data->states[ptr].value; + + return -EINVAL; +} + +static int gpio_regulator_set_value(struct regulator_dev *dev, + int min, int max) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + int ptr, target, state; + + target = -1; + for (ptr = 0; ptr < data->nr_states; ptr++) + if (data->states[ptr].value >= min && + data->states[ptr].value <= max) + target = data->states[ptr].gpios; + + if (target < 0) + return -EINVAL; + + for (ptr = 0; ptr < data->nr_gpios; ptr++) { + state = (target & (1 << ptr)) >> ptr; + gpio_set_value(data->gpios[ptr].gpio, state); + } + data->state = target; + + return 0; +} + +static int gpio_regulator_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, + unsigned *selector) +{ + return gpio_regulator_set_value(dev, min_uV, max_uV); +} + +static int gpio_regulator_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + if (selector >= data->nr_states) + return -EINVAL; + + return data->states[selector].value; +} + +static int gpio_regulator_set_current_limit(struct regulator_dev *dev, + int min_uA, int max_uA) +{ + return gpio_regulator_set_value(dev, min_uA, max_uA); +} + +static struct regulator_ops gpio_regulator_voltage_ops = { + .is_enabled = gpio_regulator_is_enabled, + .enable = gpio_regulator_enable, + .disable = gpio_regulator_disable, + .enable_time = gpio_regulator_enable_time, + .get_voltage = gpio_regulator_get_value, + .set_voltage = gpio_regulator_set_voltage, + .list_voltage = gpio_regulator_list_voltage, +}; + +static struct regulator_ops gpio_regulator_current_ops = { + .is_enabled = gpio_regulator_is_enabled, + .enable = gpio_regulator_enable, + .disable = gpio_regulator_disable, + .enable_time = gpio_regulator_enable_time, + .get_current_limit = gpio_regulator_get_value, + .set_current_limit = gpio_regulator_set_current_limit, +}; + +static int __devinit gpio_regulator_probe(struct platform_device *pdev) +{ + struct gpio_regulator_config *config = pdev->dev.platform_data; + struct gpio_regulator_data *drvdata; + int ptr, ret, state; + + drvdata = kzalloc(sizeof(struct gpio_regulator_data), GFP_KERNEL); + if (drvdata == NULL) { + dev_err(&pdev->dev, "Failed to allocate device data\n"); + return -ENOMEM; + } + + drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); + if (drvdata->desc.name == NULL) { + dev_err(&pdev->dev, "Failed to allocate supply name\n"); + ret = -ENOMEM; + goto err; + } + + drvdata->gpios = kmemdup(config->gpios, + config->nr_gpios * sizeof(struct gpio), + GFP_KERNEL); + if (drvdata->gpios == NULL) { + dev_err(&pdev->dev, "Failed to allocate gpio data\n"); + ret = -ENOMEM; + goto err_name; + } + + drvdata->states = kmemdup(config->states, + config->nr_states * + sizeof(struct gpio_regulator_state), + GFP_KERNEL); + if (drvdata->states == NULL) { + dev_err(&pdev->dev, "Failed to allocate state data\n"); + ret = -ENOMEM; + goto err_memgpio; + } + drvdata->nr_states = config->nr_states; + + drvdata->desc.owner = THIS_MODULE; + + /* handle regulator type*/ + switch (config->type) { + case REGULATOR_VOLTAGE: + drvdata->desc.type = REGULATOR_VOLTAGE; + drvdata->desc.ops = &gpio_regulator_voltage_ops; + drvdata->desc.n_voltages = config->nr_states; + break; + case REGULATOR_CURRENT: + drvdata->desc.type = REGULATOR_CURRENT; + drvdata->desc.ops = &gpio_regulator_current_ops; + break; + default: + dev_err(&pdev->dev, "No regulator type set\n"); + ret = -EINVAL; + goto err_memgpio; + break; + } + + drvdata->enable_gpio = config->enable_gpio; + drvdata->startup_delay = config->startup_delay; + + if (gpio_is_valid(config->enable_gpio)) { + drvdata->enable_high = config->enable_high; + + ret = gpio_request(config->enable_gpio, config->supply_name); + if (ret) { + dev_err(&pdev->dev, + "Could not obtain regulator enable GPIO %d: %d\n", + config->enable_gpio, ret); + goto err_memstate; + } + + /* set output direction without changing state + * to prevent glitch + */ + if (config->enabled_at_boot) { + drvdata->is_enabled = true; + ret = gpio_direction_output(config->enable_gpio, + config->enable_high); + } else { + drvdata->is_enabled = false; + ret = gpio_direction_output(config->enable_gpio, + !config->enable_high); + } + + if (ret) { + dev_err(&pdev->dev, + "Could not configure regulator enable GPIO %d direction: %d\n", + config->enable_gpio, ret); + goto err_enablegpio; + } + } else { + /* Regulator without GPIO control is considered + * always enabled + */ + drvdata->is_enabled = true; + } + + drvdata->nr_gpios = config->nr_gpios; + ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios); + if (ret) { + dev_err(&pdev->dev, + "Could not obtain regulator setting GPIOs: %d\n", ret); + goto err_enablegpio; + } + + /* build initial state from gpio init data. */ + state = 0; + for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) { + if (config->gpios[ptr].flags & GPIOF_OUT_INIT_HIGH) + state |= (1 << ptr); + } + drvdata->state = state; + + drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev, + config->init_data, drvdata); + if (IS_ERR(drvdata->dev)) { + ret = PTR_ERR(drvdata->dev); + dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); + goto err_stategpio; + } + + platform_set_drvdata(pdev, drvdata); + + return 0; + +err_stategpio: + gpio_free_array(drvdata->gpios, drvdata->nr_gpios); +err_enablegpio: + if (gpio_is_valid(config->enable_gpio)) + gpio_free(config->enable_gpio); +err_memstate: + kfree(drvdata->states); +err_memgpio: + kfree(drvdata->gpios); +err_name: + kfree(drvdata->desc.name); +err: + kfree(drvdata); + return ret; +} + +static int __devexit gpio_regulator_remove(struct platform_device *pdev) +{ + struct gpio_regulator_data *drvdata = platform_get_drvdata(pdev); + + regulator_unregister(drvdata->dev); + + gpio_free_array(drvdata->gpios, drvdata->nr_gpios); + + kfree(drvdata->states); + kfree(drvdata->gpios); + + if (gpio_is_valid(drvdata->enable_gpio)) + gpio_free(drvdata->enable_gpio); + + kfree(drvdata->desc.name); + kfree(drvdata); + + return 0; +} + +static struct platform_driver gpio_regulator_driver = { + .probe = gpio_regulator_probe, + .remove = __devexit_p(gpio_regulator_remove), + .driver = { + .name = "gpio-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_regulator_init(void) +{ + return platform_driver_register(&gpio_regulator_driver); +} +subsys_initcall(gpio_regulator_init); + +static void __exit gpio_regulator_exit(void) +{ + platform_driver_unregister(&gpio_regulator_driver); +} +module_exit(gpio_regulator_exit); + +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_DESCRIPTION("gpio voltage regulator"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-regulator"); -- cgit v1.2.3 From ecc37edf7b670616a9dc78a0bdd4911a22d551ec Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 11 Oct 2011 13:59:13 +0100 Subject: regulator: Add module.h include to gpio-regulator Reported-by: Stephen Rothwell Signed-off-by: Mark Brown --- drivers/regulator/gpio-regulator.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index abf32ad6f57..f0acf52498b 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3 From d162b3c54b206ee64c87651309ed4442846cafbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Tue, 11 Oct 2011 23:11:01 +0200 Subject: regulator: gpio-regulator add dependency on GENERIC_GPIO Without GENERIC_GPIO "struct gpio" is undefined leading to errors. Reported-by: Randy Dunlap Acked-by: Randy Dunlap Signed-off-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 5e0c7b66473..9713b1b860c 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -66,6 +66,7 @@ config REGULATOR_USERSPACE_CONSUMER config REGULATOR_GPIO tristate "GPIO regulator support" + depends on GENERIC_GPIO help This driver provides support for regulators that can be controlled via gpios. -- cgit v1.2.3 From d1685e4e2c3854782272f32b71f2f3eff5c6e0d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Fri, 14 Oct 2011 18:00:29 +0200 Subject: regulator: Fix possible nullpointer dereference in regulator_enable() In the case where _regulator_enable returns an error it was not checked if a supplying regulator exists before trying to disable it, leading to a null pointer-dereference if no supplying regulator existed. Signed-off-by: Heiko Stuebner Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 9a33fe2021b..87d9328f169 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1425,7 +1425,7 @@ int regulator_enable(struct regulator *regulator) ret = _regulator_enable(rdev); mutex_unlock(&rdev->mutex); - if (ret != 0) + if (ret != 0 && rdev->supply) regulator_disable(rdev->supply); return ret; -- cgit v1.2.3