diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-03 18:56:38 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-03 18:56:38 (GMT) |
commit | 76f7a102c0290d3e24703b6cd3716d5a594d6173 (patch) | |
tree | 93c1558d63f217bd48c5accc7347bab4b5526904 /drivers/regulator | |
parent | 92295f632cefbdf15d46e9ac5f0fc3cfade35259 (diff) | |
parent | 70083c4c8c60d9ddc188f51e0960574badc919af (diff) | |
download | linux-76f7a102c0290d3e24703b6cd3716d5a594d6173.tar.xz |
Merge tag 'regulator-v3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator
Pull regulator updates from Mark Brown:
"Very quiet release here, as well as the usual driver specific updates
only a couple of new things:
- New drivers for TI ABB LDOs and MAX77693 PMICs
- Support for enabling bypass mode support via device tree"
* tag 'regulator-v3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (23 commits)
regulator: max77693: Remove NULL test for rmatch[i].init_data
regulator: max77693: Fix trivial typo
regulator: ab8500-ext: Staticize local symbols
regulator: max77693: Add max77693 regualtor driver.
regulator: max8973: fix a typo in documentation
regulator: max8973: initial DT support
regulators: max8973: fix multiple instance support
regulator: of: Added a property to indicate bypass mode support
regulator: ti-abb: Convert to use devm_ioremap_resource
regulator: tps62360: Fix crash in i2c_driver .probe
regulator: ab8500: Provide supply names for the AUX regulators
regulator: ab8500-ext: Enable for Device Tree
regulator: ab8500-ext: Register as a device in its own right
regulator: ab8500-ext: Provide a set_voltage call-back operation
regulator: ab8500: Ensure AB8500 external registers are probed first
regulator: core: add regulator_get_linear_step()
regulator: lp397x: use devm_kzalloc() to make cleanup paths simpler
regulator: lp872x: support the device tree feature
regulator: Remove unnecessary include of linux/delay.h from regulator drivers
regulator: isl6271a: Use NULL instead of 0
...
Diffstat (limited to 'drivers/regulator')
32 files changed, 1514 insertions, 90 deletions
diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index 493948a..8a7cb1f 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -406,7 +406,6 @@ static int pm8607_regulator_remove(struct platform_device *pdev) { struct pm8607_regulator_info *info = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(info->regulator); return 0; } diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 8bb2644..f1e6ad9 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -250,6 +250,15 @@ config REGULATOR_MAX77686 via I2C bus. The provided regulator is suitable for Exynos-4 chips to control VARM and VINT voltages. +config REGULATOR_MAX77693 + tristate "Maxim MAX77693 regulator" + depends on MFD_MAX77693 + help + This driver controls a Maxim 77693 regulator via I2C bus. + The regulators include two LDOs, 'SAFEOUT1', 'SAFEOUT2' + and one current regulator 'CHARGER'. This is suitable for + Exynos-4x12 chips. + config REGULATOR_PCAP tristate "Motorola PCAP2 regulator driver" depends on EZX_PCAP @@ -472,6 +481,16 @@ config REGULATOR_TWL4030 This driver supports the voltage regulators provided by this family of companion chips. +config REGULATOR_TI_ABB + bool "TI Adaptive Body Bias on-chip LDO" + depends on ARCH_OMAP + help + Select this option to support Texas Instruments' on-chip Adaptive Body + Bias (ABB) LDO regulators. It is recommended that this option be + enabled on required TI SoC. Certain Operating Performance Points + on TI SoCs may be unstable without enabling this as it provides + device specific optimized bias to allow/optimize functionality. + config REGULATOR_VEXPRESS tristate "Versatile Express regulators" depends on VEXPRESS_CONFIG diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 47a34ff..ba4a3cf 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o -obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o ab8500-ext.o +obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o @@ -41,6 +41,7 @@ obj-$(CONFIG_REGULATOR_MAX8973) += max8973-regulator.o obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o +obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o @@ -63,6 +64,7 @@ obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o +obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c index b4d4547..02ff691 100644 --- a/drivers/regulator/ab8500-ext.c +++ b/drivers/regulator/ab8500-ext.c @@ -16,9 +16,11 @@ #include <linux/kernel.h> #include <linux/err.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> #include <linux/mfd/abx500.h> #include <linux/mfd/abx500/ab8500.h> #include <linux/regulator/ab8500.h> @@ -229,6 +231,28 @@ static unsigned int ab8500_ext_regulator_get_mode(struct regulator_dev *rdev) return ret; } +static int ab8500_ext_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned *selector) +{ + struct regulation_constraints *regu_constraints = rdev->constraints; + + if (!regu_constraints) { + dev_err(rdev_get_dev(rdev), "No regulator constraints\n"); + return -EINVAL; + } + + if (regu_constraints->min_uV == min_uV && + regu_constraints->max_uV == max_uV) + return 0; + + dev_err(rdev_get_dev(rdev), + "Requested min %duV max %duV != constrained min %duV max %duV\n", + min_uV, max_uV, + regu_constraints->min_uV, regu_constraints->max_uV); + + return -EINVAL; +} + static int ab8500_ext_list_voltage(struct regulator_dev *rdev, unsigned selector) { @@ -252,6 +276,7 @@ static struct regulator_ops ab8500_ext_regulator_ops = { .is_enabled = ab8500_ext_regulator_is_enabled, .set_mode = ab8500_ext_regulator_set_mode, .get_mode = ab8500_ext_regulator_get_mode, + .set_voltage = ab8500_ext_set_voltage, .list_voltage = ab8500_ext_list_voltage, }; @@ -310,18 +335,37 @@ static struct ab8500_ext_regulator_info }, }; -int ab8500_ext_regulator_init(struct platform_device *pdev) +static struct of_regulator_match ab8500_ext_regulator_match[] = { + { .name = "ab8500_ext1", .driver_data = (void *) AB8500_EXT_SUPPLY1, }, + { .name = "ab8500_ext2", .driver_data = (void *) AB8500_EXT_SUPPLY2, }, + { .name = "ab8500_ext3", .driver_data = (void *) AB8500_EXT_SUPPLY3, }, +}; + +static int ab8500_ext_regulator_probe(struct platform_device *pdev) { struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); struct ab8500_platform_data *ppdata; struct ab8500_regulator_platform_data *pdata; + struct device_node *np = pdev->dev.of_node; struct regulator_config config = { }; int i, err; + if (np) { + err = of_regulator_match(&pdev->dev, np, + ab8500_ext_regulator_match, + ARRAY_SIZE(ab8500_ext_regulator_match)); + if (err < 0) { + dev_err(&pdev->dev, + "Error parsing regulator init data: %d\n", err); + return err; + } + } + if (!ab8500) { dev_err(&pdev->dev, "null mfd parent\n"); return -EINVAL; } + ppdata = dev_get_platdata(ab8500->dev); if (!ppdata) { dev_err(&pdev->dev, "null parent pdata\n"); @@ -362,8 +406,11 @@ int ab8500_ext_regulator_init(struct platform_device *pdev) pdata->ext_regulator[i].driver_data; config.dev = &pdev->dev; - config.init_data = &pdata->ext_regulator[i]; config.driver_data = info; + config.of_node = ab8500_ext_regulator_match[i].of_node; + config.init_data = (np) ? + ab8500_ext_regulator_match[i].init_data : + &pdata->ext_regulator[i]; /* register regulator with framework */ info->rdev = regulator_register(&info->desc, &config); @@ -386,7 +433,7 @@ int ab8500_ext_regulator_init(struct platform_device *pdev) return 0; } -void ab8500_ext_regulator_exit(struct platform_device *pdev) +static int ab8500_ext_regulator_remove(struct platform_device *pdev) { int i; @@ -399,7 +446,36 @@ void ab8500_ext_regulator_exit(struct platform_device *pdev) regulator_unregister(info->rdev); } + + return 0; +} + +static struct platform_driver ab8500_ext_regulator_driver = { + .probe = ab8500_ext_regulator_probe, + .remove = ab8500_ext_regulator_remove, + .driver = { + .name = "ab8500-ext-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init ab8500_ext_regulator_init(void) +{ + int ret; + + ret = platform_driver_register(&ab8500_ext_regulator_driver); + if (ret) + pr_err("Failed to register ab8500 ext regulator: %d\n", ret); + + return ret; +} +subsys_initcall(ab8500_ext_regulator_init); + +static void __exit ab8500_ext_regulator_exit(void) +{ + platform_driver_unregister(&ab8500_ext_regulator_driver); } +module_exit(ab8500_ext_regulator_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>"); diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index a19045e..603f192 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -719,6 +719,7 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), .volt_table = ldo_vauxn_voltages, .enable_time = 200, + .supply_name = "vin", }, .load_lp_uA = 5000, .update_bank = 0x04, @@ -741,6 +742,7 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), .volt_table = ldo_vauxn_voltages, .enable_time = 200, + .supply_name = "vin", }, .load_lp_uA = 5000, .update_bank = 0x04, @@ -763,6 +765,7 @@ static struct ab8500_regulator_info .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages), .volt_table = ldo_vaux3_voltages, .enable_time = 450, + .supply_name = "vin", }, .load_lp_uA = 5000, .update_bank = 0x04, @@ -3156,22 +3159,12 @@ static int ab8500_regulator_probe(struct platform_device *pdev) return err; } - if (!is_ab8505(ab8500)) { - /* register external regulators (before Vaux1, 2 and 3) */ - err = ab8500_ext_regulator_init(pdev); - if (err) - return err; - } - /* register all regulators */ for (i = 0; i < abx500_regulator.info_size; i++) { err = ab8500_regulator_register(pdev, &pdata->regulator[i], i, NULL); - if (err < 0) { - if (!is_ab8505(ab8500)) - ab8500_ext_regulator_exit(pdev); + if (err < 0) return err; - } } return 0; @@ -3180,7 +3173,6 @@ static int ab8500_regulator_probe(struct platform_device *pdev) static int ab8500_regulator_remove(struct platform_device *pdev) { int i, err; - struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); for (i = 0; i < abx500_regulator.info_size; i++) { struct ab8500_regulator_info *info = NULL; @@ -3192,10 +3184,6 @@ static int ab8500_regulator_remove(struct platform_device *pdev) regulator_unregister(info->regulator); } - /* remove external regulators (after Vaux1, 2 and 3) */ - if (!is_ab8505(ab8500)) - ab8500_ext_regulator_exit(pdev); - /* remove regulator debug */ err = ab8500_regulator_debug_exit(pdev); if (err) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 815d6df..288c75a 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2138,6 +2138,21 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector) EXPORT_SYMBOL_GPL(regulator_list_voltage); /** + * regulator_get_linear_step - return the voltage step size between VSEL values + * @regulator: regulator source + * + * Returns the voltage step size between VSEL values for linear + * regulators, or return 0 if the regulator isn't a linear regulator. + */ +unsigned int regulator_get_linear_step(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + + return rdev->desc->uV_step; +} +EXPORT_SYMBOL_GPL(regulator_get_linear_step); + +/** * regulator_is_supported_voltage - check if a voltage range can be supported * * @regulator: Regulator to check. diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index d1e5bee..b99c49b 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -130,7 +130,7 @@ static int isl6271a_probe(struct i2c_client *i2c, if (i == 0) config.init_data = init_data; else - config.init_data = 0; + config.init_data = NULL; config.driver_data = pmic; pmic->rdev[i] = regulator_register(&isl_rd[i], &config); diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index d8af9e7..3809b43 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -434,7 +434,7 @@ static int lp3971_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL); + lp3971 = devm_kzalloc(&i2c->dev, sizeof(struct lp3971), GFP_KERNEL); if (lp3971 == NULL) return -ENOMEM; @@ -449,19 +449,15 @@ static int lp3971_i2c_probe(struct i2c_client *i2c, ret = -ENODEV; if (ret < 0) { dev_err(&i2c->dev, "failed to detect device\n"); - goto err_detect; + return ret; } ret = setup_regulators(lp3971, pdata); if (ret < 0) - goto err_detect; + return ret; i2c_set_clientdata(i2c, lp3971); return 0; - -err_detect: - kfree(lp3971); - return ret; } static int lp3971_i2c_remove(struct i2c_client *i2c) @@ -473,7 +469,6 @@ static int lp3971_i2c_remove(struct i2c_client *i2c) regulator_unregister(lp3971->rdev[i]); kfree(lp3971->rdev); - kfree(lp3971); return 0; } diff --git a/drivers/regulator/lp3972.c b/drivers/regulator/lp3972.c index 61e4cf9..5730240 100644 --- a/drivers/regulator/lp3972.c +++ b/drivers/regulator/lp3972.c @@ -528,7 +528,7 @@ static int lp3972_i2c_probe(struct i2c_client *i2c, return -ENODEV; } - lp3972 = kzalloc(sizeof(struct lp3972), GFP_KERNEL); + lp3972 = devm_kzalloc(&i2c->dev, sizeof(struct lp3972), GFP_KERNEL); if (!lp3972) return -ENOMEM; @@ -546,19 +546,15 @@ static int lp3972_i2c_probe(struct i2c_client *i2c, } if (ret < 0) { dev_err(&i2c->dev, "failed to detect device. ret = %d\n", ret); - goto err_detect; + return ret; } ret = setup_regulators(lp3972, pdata); if (ret < 0) - goto err_detect; + return ret; i2c_set_clientdata(i2c, lp3972); return 0; - -err_detect: - kfree(lp3972); - return ret; } static int lp3972_i2c_remove(struct i2c_client *i2c) @@ -569,7 +565,6 @@ static int lp3972_i2c_remove(struct i2c_client *i2c) for (i = 0; i < lp3972->num_regulators; i++) regulator_unregister(lp3972->rdev[i]); kfree(lp3972->rdev); - kfree(lp3972); return 0; } diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index f5fc4a1..b16336b 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -18,6 +18,9 @@ #include <linux/regulator/lp872x.h> #include <linux/regulator/driver.h> #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/regulator/of_regulator.h> /* Registers : LP8720/8725 shared */ #define LP872X_GENERAL_CFG 0x00 @@ -723,8 +726,8 @@ static int lp872x_init_dvs(struct lp872x *lp) gpio = dvs->gpio; if (!gpio_is_valid(gpio)) { - dev_err(lp->dev, "invalid gpio: %d\n", gpio); - return -EINVAL; + dev_warn(lp->dev, "invalid gpio: %d\n", gpio); + goto set_default_dvs_mode; } pinstate = dvs->init_state; @@ -829,6 +832,103 @@ static const struct regmap_config lp872x_regmap_config = { .max_register = MAX_REGISTERS, }; +#ifdef CONFIG_OF + +#define LP872X_VALID_OPMODE (REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL) + +static struct of_regulator_match lp8720_matches[] = { + { .name = "ldo1", .driver_data = (void *)LP8720_ID_LDO1, }, + { .name = "ldo2", .driver_data = (void *)LP8720_ID_LDO2, }, + { .name = "ldo3", .driver_data = (void *)LP8720_ID_LDO3, }, + { .name = "ldo4", .driver_data = (void *)LP8720_ID_LDO4, }, + { .name = "ldo5", .driver_data = (void *)LP8720_ID_LDO5, }, + { .name = "buck", .driver_data = (void *)LP8720_ID_BUCK, }, +}; + +static struct of_regulator_match lp8725_matches[] = { + { .name = "ldo1", .driver_data = (void *)LP8725_ID_LDO1, }, + { .name = "ldo2", .driver_data = (void *)LP8725_ID_LDO2, }, + { .name = "ldo3", .driver_data = (void *)LP8725_ID_LDO3, }, + { .name = "ldo4", .driver_data = (void *)LP8725_ID_LDO4, }, + { .name = "ldo5", .driver_data = (void *)LP8725_ID_LDO5, }, + { .name = "lilo1", .driver_data = (void *)LP8725_ID_LILO1, }, + { .name = "lilo2", .driver_data = (void *)LP8725_ID_LILO2, }, + { .name = "buck1", .driver_data = (void *)LP8725_ID_BUCK1, }, + { .name = "buck2", .driver_data = (void *)LP8725_ID_BUCK2, }, +}; + +static struct lp872x_platform_data +*lp872x_populate_pdata_from_dt(struct device *dev, enum lp872x_id which) +{ + struct device_node *np = dev->of_node; + struct lp872x_platform_data *pdata; + struct of_regulator_match *match; + struct regulator_init_data *d; + int num_matches; + int count; + int i; + u8 dvs_state; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto out; + + of_property_read_u8(np, "ti,general-config", &pdata->general_config); + if (of_find_property(np, "ti,update-config", NULL)) + pdata->update_config = true; + + pdata->dvs = devm_kzalloc(dev, sizeof(struct lp872x_dvs), GFP_KERNEL); + if (!pdata->dvs) + goto out; + + pdata->dvs->gpio = of_get_named_gpio(np, "ti,dvs-gpio", 0); + of_property_read_u8(np, "ti,dvs-vsel", (u8 *)&pdata->dvs->vsel); + of_property_read_u8(np, "ti,dvs-state", &dvs_state); + pdata->dvs->init_state = dvs_state ? DVS_HIGH : DVS_LOW; + + if (of_get_child_count(np) == 0) + goto out; + + switch (which) { + case LP8720: + match = lp8720_matches; + num_matches = ARRAY_SIZE(lp8720_matches); + break; + case LP8725: + match = lp8725_matches; + num_matches = ARRAY_SIZE(lp8725_matches); + break; + default: + goto out; + } + + count = of_regulator_match(dev, np, match, num_matches); + if (count <= 0) + goto out; + + for (i = 0; i < num_matches; i++) { + pdata->regulator_data[i].id = (int)match[i].driver_data; + pdata->regulator_data[i].init_data = match[i].init_data; + + /* Operation mode configuration for buck/buck1/buck2 */ + if (strncmp(match[i].name, "buck", 4)) + continue; + + d = pdata->regulator_data[i].init_data; + d->constraints.valid_modes_mask |= LP872X_VALID_OPMODE; + d->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE; + } +out: + return pdata; +} +#else +static struct lp872x_platform_data +*lp872x_populate_pdata_from_dt(struct device *dev, enum lp872x_id which) +{ + return NULL; +} +#endif + static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) { struct lp872x *lp; @@ -838,6 +938,10 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) [LP8725] = LP8725_NUM_REGULATORS, }; + if (cl->dev.of_node) + cl->dev.platform_data = lp872x_populate_pdata_from_dt(&cl->dev, + (enum lp872x_id)id->driver_data); + lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); if (!lp) goto err_mem; @@ -882,6 +986,13 @@ static int lp872x_remove(struct i2c_client *cl) return 0; } +static const struct of_device_id lp872x_dt_ids[] = { + { .compatible = "ti,lp8720", }, + { .compatible = "ti,lp8725", }, + { } +}; +MODULE_DEVICE_TABLE(of, lp872x_dt_ids); + static const struct i2c_device_id lp872x_ids[] = { {"lp8720", LP8720}, {"lp8725", LP8725}, @@ -893,6 +1004,7 @@ static struct i2c_driver lp872x_driver = { .driver = { .name = "lp872x", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(lp872x_dt_ids), }, .probe = lp872x_probe, .remove = lp872x_remove, diff --git a/drivers/regulator/lp8755.c b/drivers/regulator/lp8755.c index f0f6ea0..d9e38b4 100644 --- a/drivers/regulator/lp8755.c +++ b/drivers/regulator/lp8755.c @@ -19,7 +19,6 @@ #include <linux/interrupt.h> #include <linux/gpio.h> #include <linux/regmap.h> -#include <linux/delay.h> #include <linux/uaccess.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c index eb1e1e8..0b015f2 100644 --- a/drivers/regulator/lp8788-buck.c +++ b/drivers/regulator/lp8788-buck.c @@ -533,7 +533,6 @@ static int lp8788_buck_remove(struct platform_device *pdev) { struct lp8788_buck *buck = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(buck->regulator); return 0; diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c index 0ce2c4c..0527d87 100644 --- a/drivers/regulator/lp8788-ldo.c +++ b/drivers/regulator/lp8788-ldo.c @@ -561,7 +561,6 @@ static int lp8788_dldo_remove(struct platform_device *pdev) { struct lp8788_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(ldo->regulator); return 0; @@ -622,7 +621,6 @@ static int lp8788_aldo_remove(struct platform_device *pdev) { struct lp8788_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(ldo->regulator); return 0; diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index 20935b1..f563057 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -24,7 +24,6 @@ #include <linux/kernel.h> #include <linux/bug.h> -#include <linux/delay.h> #include <linux/err.h> #include <linux/gpio.h> #include <linux/slab.h> diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c new file mode 100644 index 0000000..ce4b96c --- /dev/null +++ b/drivers/regulator/max77693.c @@ -0,0 +1,322 @@ +/* + * max77693.c - Regulator driver for the Maxim 77693 + * + * Copyright (C) 2013 Samsung Electronics + * Jonghwa Lee <jonghwa3.lee@samsung.com> + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max77686.c + */ + +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/max77693.h> +#include <linux/mfd/max77693-private.h> +#include <linux/regulator/of_regulator.h> + +#define CHGIN_ILIM_STEP_20mA 20000 + +struct max77693_pmic_dev { + struct device *dev; + struct max77693_dev *iodev; + int num_regulators; + struct regulator_dev **rdev; +}; + +/* CHARGER regulator ops */ +/* CHARGER regulator uses two bits for enabling */ +static int max77693_chg_is_enabled(struct regulator_dev *rdev) +{ + int ret; + u8 val; + + ret = max77693_read_reg(rdev->regmap, rdev->desc->enable_reg, &val); + if (ret) + return ret; + + return (val & rdev->desc->enable_mask) == rdev->desc->enable_mask; +} + +/* + * CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA + * 0x00, 0x01, 0x2, 0x03 = 60 mA + * 0x04 ~ 0x7E = (60 + (X - 3) * 20) mA + */ +static int max77693_chg_get_current_limit(struct regulator_dev *rdev) +{ + unsigned int chg_min_uA = rdev->constraints->min_uA; + unsigned int chg_max_uA = rdev->constraints->max_uA; + u8 reg, sel; + unsigned int val; + int ret; + + ret = max77693_read_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_09, ®); + if (ret < 0) + return ret; + + sel = reg & CHG_CNFG_09_CHGIN_ILIM_MASK; + + /* the first four codes for charger current are all 60mA */ + if (sel <= 3) + sel = 0; + else + sel -= 3; + + val = chg_min_uA + CHGIN_ILIM_STEP_20mA * sel; + if (val > chg_max_uA) + return -EINVAL; + + return val; +} + +static int max77693_chg_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + unsigned int chg_min_uA = rdev->constraints->min_uA; + int sel = 0; + + while (chg_min_uA + CHGIN_ILIM_STEP_20mA * sel < min_uA) + sel++; + + if (chg_min_uA + CHGIN_ILIM_STEP_20mA * sel > max_uA) + return -EINVAL; + + /* the first four codes for charger current are all 60mA */ + sel += 3; + + return max77693_write_reg(rdev->regmap, + MAX77693_CHG_REG_CHG_CNFG_09, sel); +} +/* end of CHARGER regulator ops */ + +static const unsigned int max77693_safeout_table[] = { + 4850000, + 4900000, + 4950000, + 3300000, +}; + +static struct regulator_ops max77693_safeout_ops = { + .list_voltage = regulator_list_voltage_table, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static struct regulator_ops max77693_charger_ops = { + .is_enabled = max77693_chg_is_enabled, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_current_limit = max77693_chg_get_current_limit, + .set_current_limit = max77693_chg_set_current_limit, +}; + +#define regulator_desc_esafeout(_num) { \ + .name = "ESAFEOUT"#_num, \ + .id = MAX77693_ESAFEOUT##_num, \ + .n_voltages = 4, \ + .ops = &max77693_safeout_ops, \ + .type = REGULATOR_VOLTAGE, \ + .volt_table = max77693_safeout_table, \ + .vsel_reg = MAX77693_CHG_REG_SAFEOUT_CTRL, \ + .vsel_mask = SAFEOUT_CTRL_SAFEOUT##_num##_MASK, \ + .enable_reg = MAX77693_CHG_REG_SAFEOUT_CTRL, \ + .enable_mask = SAFEOUT_CTRL_ENSAFEOUT##_num##_MASK , \ +} + +static struct regulator_desc regulators[] = { + regulator_desc_esafeout(1), + regulator_desc_esafeout(2), + { + .name = "CHARGER", + .id = MAX77693_CHARGER, + .ops = &max77693_charger_ops, + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + .enable_reg = MAX77693_CHG_REG_CHG_CNFG_00, + .enable_mask = CHG_CNFG_00_CHG_MASK | + CHG_CNFG_00_BUCK_MASK, + }, +}; + +#ifdef CONFIG_OF +static int max77693_pmic_dt_parse_rdata(struct device *dev, + struct max77693_regulator_data **rdata) +{ + struct device_node *np; + struct of_regulator_match *rmatch; + struct max77693_regulator_data *tmp; + int i, matched = 0; + + np = of_find_node_by_name(dev->parent->of_node, "regulators"); + if (!np) + return -EINVAL; + + rmatch = devm_kzalloc(dev, + sizeof(*rmatch) * ARRAY_SIZE(regulators), GFP_KERNEL); + if (!rmatch) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(regulators); i++) + rmatch[i].name = regulators[i].name; + + matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(regulators)); + if (matched <= 0) + return matched; + *rdata = devm_kzalloc(dev, sizeof(**rdata) * matched, GFP_KERNEL); + if (!(*rdata)) + return -ENOMEM; + + tmp = *rdata; + + for (i = 0; i < matched; i++) { + tmp->initdata = rmatch[i].init_data; + tmp->of_node = rmatch[i].of_node; + tmp->id = regulators[i].id; + tmp++; + } + + return matched; +} +#else +static int max77693_pmic_dt_parse_rdata(struct device *dev, + struct max77693_regulator_data **rdata) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int max77693_pmic_init_rdata(struct device *dev, + struct max77693_regulator_data **rdata) +{ + struct max77693_platform_data *pdata; + int num_regulators = 0; + + pdata = dev_get_platdata(dev->parent); + if (pdata) { + *rdata = pdata->regulators; + num_regulators = pdata->num_regulators; + } + + if (!(*rdata) && dev->parent->of_node) + num_regulators = max77693_pmic_dt_parse_rdata(dev, rdata); + + return num_regulators; +} + +static int max77693_pmic_probe(struct platform_device *pdev) +{ + struct max77693_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max77693_pmic_dev *max77693_pmic; + struct max77693_regulator_data *rdata = NULL; + int num_rdata, i, ret; + struct regulator_config config; + + num_rdata = max77693_pmic_init_rdata(&pdev->dev, &rdata); + if (!rdata || num_rdata <= 0) { + dev_err(&pdev->dev, "No init data supplied.\n"); + return -ENODEV; + } + + max77693_pmic = devm_kzalloc(&pdev->dev, + sizeof(struct max77693_pmic_dev), + GFP_KERNEL); + if (!max77693_pmic) + return -ENOMEM; + + max77693_pmic->rdev = devm_kzalloc(&pdev->dev, + sizeof(struct regulator_dev *) * num_rdata, + GFP_KERNEL); + if (!max77693_pmic->rdev) + return -ENOMEM; + + max77693_pmic->dev = &pdev->dev; + max77693_pmic->iodev = iodev; + max77693_pmic->num_regulators = num_rdata; + + config.dev = &pdev->dev; + config.regmap = iodev->regmap; + config.driver_data = max77693_pmic; + platform_set_drvdata(pdev, max77693_pmic); + + for (i = 0; i < max77693_pmic->num_regulators; i++) { + int id = rdata[i].id; + + config.init_data = rdata[i].initdata; + config.of_node = rdata[i].of_node; + + max77693_pmic->rdev[i] = regulator_register(®ulators[id], + &config); + if (IS_ERR(max77693_pmic->rdev[i])) { + ret = PTR_ERR(max77693_pmic->rdev[i]); + dev_err(max77693_pmic->dev, + "Failed to initialize regulator-%d\n", id); + max77693_pmic->rdev[i] = NULL; + goto err; + } + } + + return 0; + err: + while (--i >= 0) + regulator_unregister(max77693_pmic->rdev[i]); + + return ret; +} + +static int max77693_pmic_remove(struct platform_device *pdev) +{ + struct max77693_pmic_dev *max77693_pmic = platform_get_drvdata(pdev); + struct regulator_dev **rdev = max77693_pmic->rdev; + int i; + + for (i = 0; i < max77693_pmic->num_regulators; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + + return 0; +} + +static const struct platform_device_id max77693_pmic_id[] = { + {"max77693-pmic", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(platform, max77693_pmic_id); + +static struct platform_driver max77693_pmic_driver = { + .driver = { + .name = "max77693-pmic", + .owner = THIS_MODULE, + }, + .probe = max77693_pmic_probe, + .remove = max77693_pmic_remove, + .id_table = max77693_pmic_id, +}; + +module_platform_driver(max77693_pmic_driver); + +MODULE_DESCRIPTION("MAXIM MAX77693 regulator driver"); +MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index 3597da8..e6d54a5 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -327,7 +327,6 @@ static int max8925_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(rdev); return 0; diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index adb1414..0c5195a 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -26,10 +26,12 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/err.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/max8973-regulator.h> +#include <linux/regulator/of_regulator.h> #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/slab.h> @@ -100,6 +102,7 @@ struct max8973_chip { int curr_vout_reg; int curr_gpio_val; bool valid_dvs_gpio; + struct regulator_ops ops; }; /* @@ -240,7 +243,7 @@ static unsigned int max8973_dcdc_get_mode(struct regulator_dev *rdev) REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; } -static struct regulator_ops max8973_dcdc_ops = { +static const struct regulator_ops max8973_dcdc_ops = { .get_voltage_sel = max8973_dcdc_get_voltage_sel, .set_voltage_sel = max8973_dcdc_set_voltage_sel, .list_voltage = regulator_list_voltage_linear, @@ -369,7 +372,8 @@ static int max8973_probe(struct i2c_client *client, int ret; pdata = client->dev.platform_data; - if (!pdata) { + + if (!pdata && !client->dev.of_node) { dev_err(&client->dev, "No Platform data"); return -EIO; } @@ -388,30 +392,36 @@ static int max8973_probe(struct i2c_client *client, } i2c_set_clientdata(client, max); + max->ops = max8973_dcdc_ops; max->dev = &client->dev; max->desc.name = id->name; max->desc.id = 0; - max->desc.ops = &max8973_dcdc_ops; + max->desc.ops = &max->ops; max->desc.type = REGULATOR_VOLTAGE; max->desc.owner = THIS_MODULE; max->desc.min_uV = MAX8973_MIN_VOLATGE; max->desc.uV_step = MAX8973_VOLATGE_STEP; max->desc.n_voltages = MAX8973_BUCK_N_VOLTAGE; - if (!pdata->enable_ext_control) { + if (!pdata || !pdata->enable_ext_control) { max->desc.enable_reg = MAX8973_VOUT; max->desc.enable_mask = MAX8973_VOUT_ENABLE; - max8973_dcdc_ops.enable = regulator_enable_regmap; - max8973_dcdc_ops.disable = regulator_disable_regmap; - max8973_dcdc_ops.is_enabled = regulator_is_enabled_regmap; + max->ops.enable = regulator_enable_regmap; + max->ops.disable = regulator_disable_regmap; + max->ops.is_enabled = regulator_is_enabled_regmap; + } + + if (pdata) { + max->dvs_gpio = pdata->dvs_gpio; + max->enable_external_control = pdata->enable_ext_control; + max->curr_gpio_val = pdata->dvs_def_state; + max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state; + } else { + max->dvs_gpio = -EINVAL; + max->curr_vout_reg = MAX8973_VOUT; } - max->enable_external_control = pdata->enable_ext_control; - max->dvs_gpio = pdata->dvs_gpio; - max->curr_gpio_val = pdata->dvs_def_state; - max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state; max->lru_index[0] = max->curr_vout_reg; - max->valid_dvs_gpio = false; if (gpio_is_valid(max->dvs_gpio)) { int gpio_flags; @@ -437,16 +447,21 @@ static int max8973_probe(struct i2c_client *client, max->lru_index[i] = i; max->lru_index[0] = max->curr_vout_reg; max->lru_index[max->curr_vout_reg] = 0; + } else { + max->valid_dvs_gpio = false; } - ret = max8973_init_dcdc(max, pdata); - if (ret < 0) { - dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret); - return ret; + if (pdata) { + ret = max8973_init_dcdc(max, pdata); + if (ret < 0) { + dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret); + return ret; + } } config.dev = &client->dev; - config.init_data = pdata->reg_init_data; + config.init_data = pdata ? pdata->reg_init_data : + of_get_regulator_init_data(&client->dev, client->dev.of_node); config.driver_data = max; config.of_node = client->dev.of_node; config.regmap = max->regmap; diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c index fdf7f0a..5ff99d2 100644 --- a/drivers/regulator/mc13783-regulator.c +++ b/drivers/regulator/mc13783-regulator.c @@ -466,8 +466,6 @@ static int mc13783_regulator_remove(struct platform_device *pdev) struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); int i; - platform_set_drvdata(pdev, NULL); - for (i = 0; i < priv->num_regulators; i++) regulator_unregister(priv->regulators[i]); diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c index b716283..1037e07 100644 --- a/drivers/regulator/mc13892-regulator.c +++ b/drivers/regulator/mc13892-regulator.c @@ -636,8 +636,6 @@ static int mc13892_regulator_remove(struct platform_device *pdev) struct mc13xxx_regulator_priv *priv = platform_get_drvdata(pdev); int i; - platform_set_drvdata(pdev, NULL); - for (i = 0; i < priv->num_regulators; i++) regulator_unregister(priv->regulators[i]); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 66ca769..f3c8f8f 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -61,6 +61,9 @@ static void of_get_regulation_constraints(struct device_node *np, else /* status change should be possible if not always on. */ constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; + if (of_property_read_bool(np, "regulator-allow-bypass")) + constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS; + ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL); if (ramp_delay) constraints->ramp_delay = be32_to_cpu(*ramp_delay); diff --git a/drivers/regulator/pcap-regulator.c b/drivers/regulator/pcap-regulator.c index 4899342..1a73a29 100644 --- a/drivers/regulator/pcap-regulator.c +++ b/drivers/regulator/pcap-regulator.c @@ -260,7 +260,6 @@ static int 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; } diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index 534075e..54df9f7 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -106,7 +106,6 @@ static int pcf50633_regulator_remove(struct platform_device *pdev) { struct regulator_dev *rdev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); regulator_unregister(rdev); return 0; diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index cd9ea2e..c9f16e1 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -12,7 +12,6 @@ */ #include <linux/bug.h> -#include <linux/delay.h> #include <linux/err.h> #include <linux/gpio.h> #include <linux/slab.h> diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c new file mode 100644 index 0000000..3753ed0 --- /dev/null +++ b/drivers/regulator/ti-abb-regulator.c @@ -0,0 +1,910 @@ +/* + * Texas Instruments SoC Adaptive Body Bias(ABB) Regulator + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Mike Turquette <mturquette@ti.com> + * + * Copyright (C) 2012-2013 Texas Instruments, Inc. + * Andrii Tseglytskyi <andrii.tseglytskyi@ti.com> + * Nishanth Menon <nm@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> + +/* + * ABB LDO operating states: + * NOMINAL_OPP: bypasses the ABB LDO + * FAST_OPP: sets ABB LDO to Forward Body-Bias + * SLOW_OPP: sets ABB LDO to Reverse Body-Bias + */ +#define TI_ABB_NOMINAL_OPP 0 +#define TI_ABB_FAST_OPP 1 +#define TI_ABB_SLOW_OPP 3 + +/** + * struct ti_abb_info - ABB information per voltage setting + * @opp_sel: one of TI_ABB macro + * @vset: (optional) vset value that LDOVBB needs to be overriden with. + * + * Array of per voltage entries organized in the same order as regulator_desc's + * volt_table list. (selector is used to index from this array) + */ +struct ti_abb_info { + u32 opp_sel; + u32 vset; +}; + +/** + * struct ti_abb_reg - Register description for ABB block + * @setup_reg: setup register offset from base + * @control_reg: control register offset from base + * @sr2_wtcnt_value_mask: setup register- sr2_wtcnt_value mask + * @fbb_sel_mask: setup register- FBB sel mask + * @rbb_sel_mask: setup register- RBB sel mask + * @sr2_en_mask: setup register- enable mask + * @opp_change_mask: control register - mask to trigger LDOVBB change + * @opp_sel_mask: control register - mask for mode to operate + */ +struct ti_abb_reg { + u32 setup_reg; + u32 control_reg; + + /* Setup register fields */ + u32 sr2_wtcnt_value_mask; + u32 fbb_sel_mask; + u32 rbb_sel_mask; + u32 sr2_en_mask; + + /* Control register fields */ + u32 opp_change_mask; + u32 opp_sel_mask; +}; + +/** + * struct ti_abb - ABB instance data + * @rdesc: regulator descriptor + * @clk: clock(usually sysclk) supplying ABB block + * @base: base address of ABB block + * @int_base: interrupt register base address + * @efuse_base: (optional) efuse base address for ABB modes + * @ldo_base: (optional) LDOVBB vset override base address + * @regs: pointer to struct ti_abb_reg for ABB block + * @txdone_mask: mask on int_base for tranxdone interrupt + * @ldovbb_override_mask: mask to ldo_base for overriding default LDO VBB + * vset with value from efuse + * @ldovbb_vset_mask: mask to ldo_base for providing the VSET override + * @info: array to per voltage ABB configuration + * @current_info_idx: current index to info + * @settling_time: SoC specific settling time for LDO VBB + */ +struct ti_abb { + struct regulator_desc rdesc; + struct clk *clk; + void __iomem *base; + void __iomem *int_base; + void __iomem *efuse_base; + void __iomem *ldo_base; + + const struct ti_abb_reg *regs; + u32 txdone_mask; + u32 ldovbb_override_mask; + u32 ldovbb_vset_mask; + + struct ti_abb_info *info; + int current_info_idx; + + u32 settling_time; +}; + +/** + * ti_abb_rmw() - handy wrapper to set specific register bits + * @mask: mask for register field + * @value: value shifted to mask location and written + * @offset: offset of register + * @base: base address + * + * Return: final register value (may be unused) + */ +static inline u32 ti_abb_rmw(u32 mask, u32 value, u32 offset, + void __iomem *base) +{ + u32 val; + + val = readl(base + offset); + val &= ~mask; + val |= (value << __ffs(mask)) & mask; + writel(val, base + offset); + + return val; +} + +/** + * ti_abb_check_txdone() - handy wrapper to check ABB tranxdone status + * @abb: pointer to the abb instance + * + * Return: true or false + */ +static inline bool ti_abb_check_txdone(const struct ti_abb *abb) +{ + return !!(readl(abb->int_base) & abb->txdone_mask); +} + +/** + * ti_abb_clear_txdone() - handy wrapper to clear ABB tranxdone status + * @abb: pointer to the abb instance + */ +static inline void ti_abb_clear_txdone(const struct ti_abb *abb) +{ + writel(abb->txdone_mask, abb->int_base); +}; + +/** + * ti_abb_wait_tranx() - waits for ABB tranxdone event + * @dev: device + * @abb: pointer to the abb instance + * + * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time. + */ +static int ti_abb_wait_txdone(struct device *dev, struct ti_abb *abb) +{ + int timeout = 0; + bool status; + + while (timeout++ <= abb->settling_time) { + status = ti_abb_check_txdone(abb); + if (status) + break; + + udelay(1); + } + + if (timeout > abb->settling_time) { + dev_warn_ratelimited(dev, + "%s:TRANXDONE timeout(%duS) int=0x%08x\n", + __func__, timeout, readl(abb->int_base)); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * ti_abb_clear_all_txdone() - clears ABB tranxdone event + * @dev: device + * @abb: pointer to the abb instance + * + * Return: 0 on success or -ETIMEDOUT if the event is not cleared on time. + */ +static int ti_abb_clear_all_txdone(struct device *dev, const struct ti_abb *abb) +{ + int timeout = 0; + bool status; + + while (timeout++ <= abb->settling_time) { + ti_abb_clear_txdone(abb); + + status = ti_abb_check_txdone(abb); + if (!status) + break; + + udelay(1); + } + + if (timeout > abb->settling_time) { + dev_warn_ratelimited(dev, + "%s:TRANXDONE timeout(%duS) int=0x%08x\n", + __func__, timeout, readl(abb->int_base)); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * ti_abb_program_ldovbb() - program LDOVBB register for override value + * @dev: device + * @abb: pointer to the abb instance + * @info: ABB info to program + */ +static void ti_abb_program_ldovbb(struct device *dev, const struct ti_abb *abb, + struct ti_abb_info *info) +{ + u32 val; + + val = readl(abb->ldo_base); + /* clear up previous values */ + val &= ~(abb->ldovbb_override_mask | abb->ldovbb_vset_mask); + + switch (info->opp_sel) { + case TI_ABB_SLOW_OPP: + case TI_ABB_FAST_OPP: + val |= abb->ldovbb_override_mask; + val |= info->vset << __ffs(abb->ldovbb_vset_mask); + break; + } + + writel(val, abb->ldo_base); +} + +/** + * ti_abb_set_opp() - Setup ABB and LDO VBB for required bias + * @rdev: regulator device + * @abb: pointer to the abb instance + * @info: ABB info to program + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_set_opp(struct regulator_dev *rdev, struct ti_abb *abb, + struct ti_abb_info *info) +{ + const struct ti_abb_reg *regs = abb->regs; + struct device *dev = &rdev->dev; + int ret; + + ret = ti_abb_clear_all_txdone(dev, abb); + if (ret) + goto out; + + ti_abb_rmw(regs->fbb_sel_mask | regs->rbb_sel_mask, 0, regs->setup_reg, + abb->base); + + switch (info->opp_sel) { + case TI_ABB_SLOW_OPP: + ti_abb_rmw(regs->rbb_sel_mask, 1, regs->setup_reg, abb->base); + break; + case TI_ABB_FAST_OPP: + ti_abb_rmw(regs->fbb_sel_mask, 1, regs->setup_reg, abb->base); + break; + } + + /* program next state of ABB ldo */ + ti_abb_rmw(regs->opp_sel_mask, info->opp_sel, regs->control_reg, + abb->base); + + /* program LDO VBB vset override if needed */ + if (abb->ldo_base) + ti_abb_program_ldovbb(dev, abb, info); + + /* Initiate ABB ldo change */ + ti_abb_rmw(regs->opp_change_mask, 1, regs->control_reg, abb->base); + + /* Wait for ABB LDO to complete transition to new Bias setting */ + ret = ti_abb_wait_txdone(dev, abb); + if (ret) + goto out; + + ret = ti_abb_clear_all_txdone(dev, abb); + if (ret) + goto out; + +out: + return ret; +} + +/** + * ti_abb_set_voltage_sel() - regulator accessor function to set ABB LDO + * @rdev: regulator device + * @sel: selector to index into required ABB LDO settings (maps to + * regulator descriptor's volt_table) + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) +{ + const struct regulator_desc *desc = rdev->desc; + struct ti_abb *abb = rdev_get_drvdata(rdev); + struct device *dev = &rdev->dev; + struct ti_abb_info *info, *oinfo; + int ret = 0; + + if (!abb) { + dev_err_ratelimited(dev, "%s: No regulator drvdata\n", + __func__); + return -ENODEV; + } + + if (!desc->n_voltages || !abb->info) { + dev_err_ratelimited(dev, + "%s: No valid voltage table entries?\n", + __func__); + return -EINVAL; + } + + if (sel >= desc->n_voltages) { + dev_err(dev, "%s: sel idx(%d) >= n_voltages(%d)\n", __func__, + sel, desc->n_voltages); + return -EINVAL; + } + + /* If we are in the same index as we were, nothing to do here! */ + if (sel == abb->current_info_idx) { + dev_dbg(dev, "%s: Already at sel=%d\n", __func__, sel); + return ret; + } + + /* If data is exactly the same, then just update index, no change */ + info = &abb->info[sel]; + oinfo = &abb->info[abb->current_info_idx]; + if (!memcmp(info, oinfo, sizeof(*info))) { + dev_dbg(dev, "%s: Same data new idx=%d, old idx=%d\n", __func__, + sel, abb->current_info_idx); + goto out; + } + + ret = ti_abb_set_opp(rdev, abb, info); + +out: + if (!ret) + abb->current_info_idx = sel; + else + dev_err_ratelimited(dev, + "%s: Volt[%d] idx[%d] mode[%d] Fail(%d)\n", + __func__, desc->volt_table[sel], sel, + info->opp_sel, ret); + return ret; +} + +/** + * ti_abb_get_voltage_sel() - Regulator accessor to get current ABB LDO setting + * @rdev: regulator device + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_get_voltage_sel(struct regulator_dev *rdev) +{ + const struct regulator_desc *desc = rdev->desc; + struct ti_abb *abb = rdev_get_drvdata(rdev); + struct device *dev = &rdev->dev; + + if (!abb) { + dev_err_ratelimited(dev, "%s: No regulator drvdata\n", + __func__); + return -ENODEV; + } + + if (!desc->n_voltages || !abb->info) { + dev_err_ratelimited(dev, + "%s: No valid voltage table entries?\n", + __func__); + return -EINVAL; + } + + if (abb->current_info_idx >= (int)desc->n_voltages) { + dev_err(dev, "%s: Corrupted data? idx(%d) >= n_voltages(%d)\n", + __func__, abb->current_info_idx, desc->n_voltages); + return -EINVAL; + } + + return abb->current_info_idx; +} + +/** + * ti_abb_init_timings() - setup ABB clock timing for the current platform + * @dev: device + * @abb: pointer to the abb instance + * + * Return: 0 if timing is updated, else returns error result. + */ +static int ti_abb_init_timings(struct device *dev, struct ti_abb *abb) +{ + u32 clock_cycles; + u32 clk_rate, sr2_wt_cnt_val, cycle_rate; + const struct ti_abb_reg *regs = abb->regs; + int ret; + char *pname = "ti,settling-time"; + + /* read device tree properties */ + ret = of_property_read_u32(dev->of_node, pname, &abb->settling_time); + if (ret) { + dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret); + return ret; + } + + /* ABB LDO cannot be settle in 0 time */ + if (!abb->settling_time) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + return -EINVAL; + } + + pname = "ti,clock-cycles"; + ret = of_property_read_u32(dev->of_node, pname, &clock_cycles); + if (ret) { + dev_err(dev, "Unable to get property '%s'(%d)\n", pname, ret); + return ret; + } + /* ABB LDO cannot be settle in 0 clock cycles */ + if (!clock_cycles) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + return -EINVAL; + } + + abb->clk = devm_clk_get(dev, NULL); + if (IS_ERR(abb->clk)) { + ret = PTR_ERR(abb->clk); + dev_err(dev, "%s: Unable to get clk(%d)\n", __func__, ret); + return ret; + } + + /* + * SR2_WTCNT_VALUE is the settling time for the ABB ldo after a + * transition and must be programmed with the correct time at boot. + * The value programmed into the register is the number of SYS_CLK + * clock cycles that match a given wall time profiled for the ldo. + * This value depends on: + * settling time of ldo in micro-seconds (varies per OMAP family) + * # of clock cycles per SYS_CLK period (varies per OMAP family) + * the SYS_CLK frequency in MHz (varies per board) + * The formula is: + * + * ldo settling time (in micro-seconds) + * SR2_WTCNT_VALUE = ------------------------------------------ + * (# system clock cycles) * (sys_clk period) + * + * Put another way: + * + * SR2_WTCNT_VALUE = settling time / (# SYS_CLK cycles / SYS_CLK rate)) + * + * To avoid dividing by zero multiply both "# clock cycles" and + * "settling time" by 10 such that the final result is the one we want. + */ + + /* Convert SYS_CLK rate to MHz & prevent divide by zero */ + clk_rate = DIV_ROUND_CLOSEST(clk_get_rate(abb->clk), 1000000); + + /* Calculate cycle rate */ + cycle_rate = DIV_ROUND_CLOSEST(clock_cycles * 10, clk_rate); + + /* Calulate SR2_WTCNT_VALUE */ + sr2_wt_cnt_val = DIV_ROUND_CLOSEST(abb->settling_time * 10, cycle_rate); + + dev_dbg(dev, "%s: Clk_rate=%ld, sr2_cnt=0x%08x\n", __func__, + clk_get_rate(abb->clk), sr2_wt_cnt_val); + + ti_abb_rmw(regs->sr2_wtcnt_value_mask, sr2_wt_cnt_val, regs->setup_reg, + abb->base); + + return 0; +} + +/** + * ti_abb_init_table() - Initialize ABB table from device tree + * @dev: device + * @abb: pointer to the abb instance + * @rinit_data: regulator initdata + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_init_table(struct device *dev, struct ti_abb *abb, + struct regulator_init_data *rinit_data) +{ + struct ti_abb_info *info; + const struct property *prop; + const __be32 *abb_info; + const u32 num_values = 6; + char *pname = "ti,abb_info"; + u32 num_entries, i; + unsigned int *volt_table; + int min_uV = INT_MAX, max_uV = 0; + struct regulation_constraints *c = &rinit_data->constraints; + + prop = of_find_property(dev->of_node, pname, NULL); + if (!prop) { + dev_err(dev, "No '%s' property?\n", pname); + return -ENODEV; + } + + if (!prop->value) { + dev_err(dev, "Empty '%s' property?\n", pname); + return -ENODATA; + } + + /* + * Each abb_info is a set of n-tuple, where n is num_values, consisting + * of voltage and a set of detection logic for ABB information for that + * voltage to apply. + */ + num_entries = prop->length / sizeof(u32); + if (!num_entries || (num_entries % num_values)) { + dev_err(dev, "All '%s' list entries need %d vals\n", pname, + num_values); + return -EINVAL; + } + num_entries /= num_values; + + info = devm_kzalloc(dev, sizeof(*info) * num_entries, GFP_KERNEL); + if (!info) { + dev_err(dev, "Can't allocate info table for '%s' property\n", + pname); + return -ENOMEM; + } + abb->info = info; + + volt_table = devm_kzalloc(dev, sizeof(unsigned int) * num_entries, + GFP_KERNEL); + if (!volt_table) { + dev_err(dev, "Can't allocate voltage table for '%s' property\n", + pname); + return -ENOMEM; + } + + abb->rdesc.n_voltages = num_entries; + abb->rdesc.volt_table = volt_table; + /* We do not know where the OPP voltage is at the moment */ + abb->current_info_idx = -EINVAL; + + abb_info = prop->value; + for (i = 0; i < num_entries; i++, info++, volt_table++) { + u32 efuse_offset, rbb_mask, fbb_mask, vset_mask; + u32 efuse_val; + + /* NOTE: num_values should equal to entries picked up here */ + *volt_table = be32_to_cpup(abb_info++); + info->opp_sel = be32_to_cpup(abb_info++); + efuse_offset = be32_to_cpup(abb_info++); + rbb_mask = be32_to_cpup(abb_info++); + fbb_mask = be32_to_cpup(abb_info++); + vset_mask = be32_to_cpup(abb_info++); + + dev_dbg(dev, + "[%d]v=%d ABB=%d ef=0x%x rbb=0x%x fbb=0x%x vset=0x%x\n", + i, *volt_table, info->opp_sel, efuse_offset, rbb_mask, + fbb_mask, vset_mask); + + /* Find min/max for voltage set */ + if (min_uV > *volt_table) + min_uV = *volt_table; + if (max_uV < *volt_table) + max_uV = *volt_table; + + if (!abb->efuse_base) { + /* Ignore invalid data, but warn to help cleanup */ + if (efuse_offset || rbb_mask || fbb_mask || vset_mask) + dev_err(dev, "prop '%s': v=%d,bad efuse/mask\n", + pname, *volt_table); + goto check_abb; + } + + efuse_val = readl(abb->efuse_base + efuse_offset); + + /* Use ABB recommendation from Efuse */ + if (efuse_val & rbb_mask) + info->opp_sel = TI_ABB_SLOW_OPP; + else if (efuse_val & fbb_mask) + info->opp_sel = TI_ABB_FAST_OPP; + else if (rbb_mask || fbb_mask) + info->opp_sel = TI_ABB_NOMINAL_OPP; + + dev_dbg(dev, + "[%d]v=%d efusev=0x%x final ABB=%d\n", + i, *volt_table, efuse_val, info->opp_sel); + + /* Use recommended Vset bits from Efuse */ + if (!abb->ldo_base) { + if (vset_mask) + dev_err(dev, "prop'%s':v=%d vst=%x LDO base?\n", + pname, *volt_table, vset_mask); + continue; + } + info->vset = efuse_val & vset_mask >> __ffs(vset_mask); + dev_dbg(dev, "[%d]v=%d vset=%x\n", i, *volt_table, info->vset); +check_abb: + switch (info->opp_sel) { + case TI_ABB_NOMINAL_OPP: + case TI_ABB_FAST_OPP: + case TI_ABB_SLOW_OPP: + /* Valid values */ + break; + default: + dev_err(dev, "%s:[%d]v=%d, ABB=%d is invalid! Abort!\n", + __func__, i, *volt_table, info->opp_sel); + return -EINVAL; + } + } + + /* Setup the min/max voltage constraints from the supported list */ + c->min_uV = min_uV; + c->max_uV = max_uV; + + return 0; +} + +static struct regulator_ops ti_abb_reg_ops = { + .list_voltage = regulator_list_voltage_table, + + .set_voltage_sel = ti_abb_set_voltage_sel, + .get_voltage_sel = ti_abb_get_voltage_sel, +}; + +/* Default ABB block offsets, IF this changes in future, create new one */ +static const struct ti_abb_reg abb_regs_v1 = { + /* WARNING: registers are wrongly documented in TRM */ + .setup_reg = 0x04, + .control_reg = 0x00, + + .sr2_wtcnt_value_mask = (0xff << 8), + .fbb_sel_mask = (0x01 << 2), + .rbb_sel_mask = (0x01 << 1), + .sr2_en_mask = (0x01 << 0), + + .opp_change_mask = (0x01 << 2), + .opp_sel_mask = (0x03 << 0), +}; + +static const struct ti_abb_reg abb_regs_v2 = { + .setup_reg = 0x00, + .control_reg = 0x04, + + .sr2_wtcnt_value_mask = (0xff << 8), + .fbb_sel_mask = (0x01 << 2), + .rbb_sel_mask = (0x01 << 1), + .sr2_en_mask = (0x01 << 0), + + .opp_change_mask = (0x01 << 2), + .opp_sel_mask = (0x03 << 0), +}; + +static const struct of_device_id ti_abb_of_match[] = { + {.compatible = "ti,abb-v1", .data = &abb_regs_v1}, + {.compatible = "ti,abb-v2", .data = &abb_regs_v2}, + { }, +}; + +MODULE_DEVICE_TABLE(of, ti_abb_of_match); + +/** + * ti_abb_probe() - Initialize an ABB ldo instance + * @pdev: ABB platform device + * + * Initializes an individual ABB LDO for required Body-Bias. ABB is used to + * addional bias supply to SoC modules for power savings or mandatory stability + * configuration at certain Operating Performance Points(OPPs). + * + * Return: 0 on success or appropriate error value when fails + */ +static int ti_abb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *match; + struct resource *res; + struct ti_abb *abb; + struct regulator_init_data *initdata = NULL; + struct regulator_dev *rdev = NULL; + struct regulator_desc *desc; + struct regulation_constraints *c; + struct regulator_config config = { }; + char *pname; + int ret = 0; + + match = of_match_device(ti_abb_of_match, dev); + if (!match) { + /* We do not expect this to happen */ + ret = -ENODEV; + dev_err(dev, "%s: Unable to match device\n", __func__); + goto err; + } + if (!match->data) { + ret = -EINVAL; + dev_err(dev, "%s: Bad data in match\n", __func__); + goto err; + } + + abb = devm_kzalloc(dev, sizeof(struct ti_abb), GFP_KERNEL); + if (!abb) { + dev_err(dev, "%s: Unable to allocate ABB struct\n", __func__); + ret = -ENOMEM; + goto err; + } + abb->regs = match->data; + + /* Map ABB resources */ + pname = "base-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_err(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto err; + } + abb->base = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->base)) { + ret = PTR_ERR(abb->base); + goto err; + } + + pname = "int-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_err(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto err; + } + /* + * We may have shared interrupt register offsets which are + * write-1-to-clear between domains ensuring exclusivity. + */ + abb->int_base = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (!abb->int_base) { + dev_err(dev, "Unable to map '%s'\n", pname); + ret = -ENOMEM; + goto err; + } + + /* Map Optional resources */ + pname = "efuse-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_dbg(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto skip_opt; + } + + /* + * We may have shared efuse register offsets which are read-only + * between domains + */ + abb->efuse_base = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (!abb->efuse_base) { + dev_err(dev, "Unable to map '%s'\n", pname); + ret = -ENOMEM; + goto err; + } + + pname = "ldo-address"; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname); + if (!res) { + dev_dbg(dev, "Missing '%s' IO resource\n", pname); + ret = -ENODEV; + goto skip_opt; + } + abb->ldo_base = devm_ioremap_resource(dev, res); + if (IS_ERR(abb->ldo_base)) { + ret = PTR_ERR(abb->ldo_base); + goto err; + } + + /* IF ldo_base is set, the following are mandatory */ + pname = "ti,ldovbb-override-mask"; + ret = + of_property_read_u32(pdev->dev.of_node, pname, + &abb->ldovbb_override_mask); + if (ret) { + dev_err(dev, "Missing '%s' (%d)\n", pname, ret); + goto err; + } + if (!abb->ldovbb_override_mask) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + ret = -EINVAL; + goto err; + } + + pname = "ti,ldovbb-vset-mask"; + ret = + of_property_read_u32(pdev->dev.of_node, pname, + &abb->ldovbb_vset_mask); + if (ret) { + dev_err(dev, "Missing '%s' (%d)\n", pname, ret); + goto err; + } + if (!abb->ldovbb_vset_mask) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + ret = -EINVAL; + goto err; + } + +skip_opt: + pname = "ti,tranxdone-status-mask"; + ret = + of_property_read_u32(pdev->dev.of_node, pname, + &abb->txdone_mask); + if (ret) { + dev_err(dev, "Missing '%s' (%d)\n", pname, ret); + goto err; + } + if (!abb->txdone_mask) { + dev_err(dev, "Invalid property:'%s' set as 0!\n", pname); + ret = -EINVAL; + goto err; + } + + initdata = of_get_regulator_init_data(dev, pdev->dev.of_node); + if (!initdata) { + ret = -ENOMEM; + dev_err(dev, "%s: Unable to alloc regulator init data\n", + __func__); + goto err; + } + + /* init ABB opp_sel table */ + ret = ti_abb_init_table(dev, abb, initdata); + if (ret) + goto err; + + /* init ABB timing */ + ret = ti_abb_init_timings(dev, abb); + if (ret) + goto err; + + desc = &abb->rdesc; + desc->name = dev_name(dev); + desc->owner = THIS_MODULE; + desc->type = REGULATOR_VOLTAGE; + desc->ops = &ti_abb_reg_ops; + + c = &initdata->constraints; + if (desc->n_voltages > 1) + c->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; + c->always_on = true; + + config.dev = dev; + config.init_data = initdata; + config.driver_data = abb; + config.of_node = pdev->dev.of_node; + + rdev = regulator_register(desc, &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "%s: failed to register regulator(%d)\n", + __func__, ret); + goto err; + } + platform_set_drvdata(pdev, rdev); + + /* Enable the ldo if not already done by bootloader */ + ti_abb_rmw(abb->regs->sr2_en_mask, 1, abb->regs->setup_reg, abb->base); + + return 0; + +err: + dev_err(dev, "%s: Failed to initialize(%d)\n", __func__, ret); + return ret; +} + +/** + * ti_abb_remove() - cleanups + * @pdev: ABB platform device + * + * Return: 0 + */ +static int ti_abb_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + return 0; +} + +MODULE_ALIAS("platform:ti_abb"); + +static struct platform_driver ti_abb_driver = { + .probe = ti_abb_probe, + .remove = ti_abb_remove, + .driver = { + .name = "ti_abb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ti_abb_of_match), + }, +}; +module_platform_driver(ti_abb_driver); + +MODULE_DESCRIPTION("Texas Instruments ABB LDO regulator driver"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index 612919c..a490d5b 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -351,7 +351,6 @@ static int tps62360_probe(struct i2c_client *client, int chip_id; pdata = client->dev.platform_data; - chip_id = id->driver_data; if (client->dev.of_node) { const struct of_device_id *match; @@ -364,6 +363,11 @@ static int tps62360_probe(struct i2c_client *client, chip_id = (int)match->data; if (!pdata) pdata = of_get_tps62360_platform_data(&client->dev); + } else if (id) { + chip_id = id->driver_data; + } else { + dev_err(&client->dev, "No device tree match or id table match found\n"); + return -ENODEV; } if (!pdata) { @@ -402,7 +406,7 @@ static int tps62360_probe(struct i2c_client *client, return -ENODEV; } - tps->desc.name = id->name; + tps->desc.name = client->name; tps->desc.id = 0; tps->desc.ops = &tps62360_dcdc_ops; tps->desc.type = REGULATOR_VOLTAGE; diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index df39518..2df4616 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -405,8 +405,6 @@ static int tps65217_regulator_remove(struct platform_device *pdev) for (i = 0; i < TPS65217_NUM_REGULATOR; i++) regulator_unregister(tps->rdev[i]); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index 01c66e9..a9d4284 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -330,8 +330,6 @@ static int regulator_virtual_remove(struct platform_device *pdev) if (drvdata->enabled) regulator_disable(drvdata->regulator); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 0af6898..46938cf 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -567,8 +567,6 @@ static 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); - free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC")), dcdc); free_irq(wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV")), @@ -714,8 +712,6 @@ static int wm831x_buckp_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), dcdc); regulator_unregister(dcdc->regulator); @@ -849,8 +845,6 @@ static int wm831x_boostp_remove(struct platform_device *pdev) { struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(dcdc->wm831x, platform_get_irq_byname(pdev, "UV")), dcdc); regulator_unregister(dcdc->regulator); @@ -940,7 +934,6 @@ static 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); return 0; diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c index 68586ee..16ebdf9 100644 --- a/drivers/regulator/wm831x-isink.c +++ b/drivers/regulator/wm831x-isink.c @@ -225,8 +225,6 @@ static int wm831x_isink_remove(struct platform_device *pdev) { struct wm831x_isink *isink = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(isink->wm831x, platform_get_irq(pdev, 0)), isink); regulator_unregister(isink->regulator); diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 1ec379a..9ff883f 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -338,8 +338,6 @@ static int wm831x_gp_ldo_remove(struct platform_device *pdev) { struct wm831x_ldo *ldo = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - free_irq(wm831x_irq(ldo->wm831x, platform_get_irq_byname(pdev, "UV")), ldo); regulator_unregister(ldo->regulator); diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index c6a32ea..a09f03e 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -250,7 +250,6 @@ static int 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; diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index a612c35..8f2a8a7 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -185,8 +185,6 @@ static 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); return 0; |