From 24282a1ca33b4a2cdfb907fb7a3ba4d0f6e93311 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 9 Oct 2012 15:18:59 +0530 Subject: regulator: tps65090: Register all regulators in single probe call MFD driver registers the regulator driver once per device and hence it is require to register all regulators in single probe call. Following are details of changes done to achieve this: - Move the regulator enums to mfd header and remove the tps65090-regulator.h as it does not contain more info. - Add max regulator and register all regulators even if there is no regulator init data from platform. - Convert regulator init data to pointer type in platform data. - Add input supply name in regulator desc to provide input supply. - Separate desc information from driver information. - Disable external control bit to have control through register write. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- include/linux/mfd/tps65090.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'include/linux/mfd') diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 6bc31d85462..d06c63335a2 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -24,6 +24,23 @@ #include +/* TPS65090 Regulator ID */ +enum { + TPS65090_ID_DCDC1, + TPS65090_ID_DCDC2, + TPS65090_ID_DCDC3, + TPS65090_ID_FET1, + TPS65090_ID_FET2, + TPS65090_ID_FET3, + TPS65090_ID_FET4, + TPS65090_ID_FET5, + TPS65090_ID_FET6, + TPS65090_ID_FET7, + + /* Last entry for maximum ID */ + TPS65090_ID_MAX, +}; + struct tps65090 { struct mutex lock; struct device *dev; @@ -41,10 +58,21 @@ struct tps65090_subdev_info { void *platform_data; }; +/* + * struct tps65090_regulator_plat_data + * + * @reg_init_data: The regulator init data. + */ + +struct tps65090_regulator_plat_data { + struct regulator_init_data *reg_init_data; +}; + struct tps65090_platform_data { int irq_base; int num_subdevs; struct tps65090_subdev_info *subdevs; + struct tps65090_regulator_plat_data *reg_pdata[TPS65090_ID_MAX]; }; /* -- cgit v1.2.3-70-g09d2 From 8620ca9f77b71a0069a6151e859b988117ef1fa5 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 9 Oct 2012 15:19:00 +0530 Subject: regulator: tps65090: rename driver name and regulator name To make the names proper and more appropriate: Rename the driver name from tps65090-regulator to tps65090-pmic. Rename the regulators from TPS65090_ID_* to TPS65090_REGULATOR_* Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65090-regulator.c | 16 ++++++++-------- include/linux/mfd/tps65090.h | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 5f7f9311024..584a185f89e 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -41,7 +41,7 @@ static struct regulator_ops tps65090_ops = { { \ .name = "TPS65090_RAILS"#_id, \ .supply_name = _sname, \ - .id = TPS65090_ID_##_id, \ + .id = TPS65090_REGULATOR_##_id, \ .ops = &_ops, \ .enable_reg = _en_reg, \ .enable_mask = BIT(0), \ @@ -65,9 +65,9 @@ static struct regulator_desc tps65090_regulator_desc[] = { static inline bool is_dcdc(int id) { switch (id) { - case TPS65090_ID_DCDC1: - case TPS65090_ID_DCDC2: - case TPS65090_ID_DCDC3: + case TPS65090_REGULATOR_DCDC1: + case TPS65090_REGULATOR_DCDC2: + case TPS65090_REGULATOR_DCDC3: return true; default: return false; @@ -133,14 +133,14 @@ static int __devinit tps65090_regulator_probe(struct platform_device *pdev) return -EINVAL; } - pmic = devm_kzalloc(&pdev->dev, TPS65090_ID_MAX * sizeof(*pmic), + pmic = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * sizeof(*pmic), GFP_KERNEL); if (!pmic) { dev_err(&pdev->dev, "mem alloc for pmic failed\n"); return -ENOMEM; } - for (num = 0; num < TPS65090_ID_MAX; num++) { + for (num = 0; num < TPS65090_REGULATOR_MAX; num++) { tps_pdata = tps65090_pdata->reg_pdata[num]; ri = &pmic[num]; @@ -196,7 +196,7 @@ static int __devexit tps65090_regulator_remove(struct platform_device *pdev) struct tps65090_regulator *ri; int num; - for (num = 0; num < TPS65090_ID_MAX; ++num) { + for (num = 0; num < TPS65090_REGULATOR_MAX; ++num) { ri = &pmic[num]; regulator_unregister(ri->rdev); } @@ -205,7 +205,7 @@ static int __devexit tps65090_regulator_remove(struct platform_device *pdev) static struct platform_driver tps65090_regulator_driver = { .driver = { - .name = "tps65090-regulator", + .name = "tps65090-pmic", .owner = THIS_MODULE, }, .probe = tps65090_regulator_probe, diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index d06c63335a2..958bf155097 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -26,19 +26,19 @@ /* TPS65090 Regulator ID */ enum { - TPS65090_ID_DCDC1, - TPS65090_ID_DCDC2, - TPS65090_ID_DCDC3, - TPS65090_ID_FET1, - TPS65090_ID_FET2, - TPS65090_ID_FET3, - TPS65090_ID_FET4, - TPS65090_ID_FET5, - TPS65090_ID_FET6, - TPS65090_ID_FET7, + TPS65090_REGULATOR_DCDC1, + TPS65090_REGULATOR_DCDC2, + TPS65090_REGULATOR_DCDC3, + TPS65090_REGULATOR_FET1, + TPS65090_REGULATOR_FET2, + TPS65090_REGULATOR_FET3, + TPS65090_REGULATOR_FET4, + TPS65090_REGULATOR_FET5, + TPS65090_REGULATOR_FET6, + TPS65090_REGULATOR_FET7, /* Last entry for maximum ID */ - TPS65090_ID_MAX, + TPS65090_REGULATOR_MAX, }; struct tps65090 { @@ -72,7 +72,7 @@ struct tps65090_platform_data { int irq_base; int num_subdevs; struct tps65090_subdev_info *subdevs; - struct tps65090_regulator_plat_data *reg_pdata[TPS65090_ID_MAX]; + struct tps65090_regulator_plat_data *reg_pdata[TPS65090_REGULATOR_MAX]; }; /* -- cgit v1.2.3-70-g09d2 From 3a81ef8c27cea5c749a45765da4e06a7af75be2b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 9 Oct 2012 15:19:01 +0530 Subject: regulator: tps65090: Add support for LDO regulators TPS65090 supports the two LDOs, LDO1 and LDO2. These are always ON regulators. The output on these LDOs are available once the input voltage available for these LDOs. Add support for these LDOs regulators. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65090-regulator.c | 5 +++++ include/linux/mfd/tps65090.h | 2 ++ 2 files changed, 7 insertions(+) (limited to 'include/linux/mfd') diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 584a185f89e..0732d9ba3f4 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -37,6 +37,9 @@ static struct regulator_ops tps65090_ops = { .is_enabled = regulator_is_enabled_regmap, }; +static struct regulator_ops tps65090_ldo_ops = { +}; + #define tps65090_REG_DESC(_id, _sname, _en_reg, _ops) \ { \ .name = "TPS65090_RAILS"#_id, \ @@ -60,6 +63,8 @@ static struct regulator_desc tps65090_regulator_desc[] = { tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_ops), tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_ops), tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_ops), + tps65090_REG_DESC(LDO1, "vsys_l1", 0, tps65090_ldo_ops), + tps65090_REG_DESC(LDO2, "vsys_l2", 0, tps65090_ldo_ops), }; static inline bool is_dcdc(int id) diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 958bf155097..598921221ff 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -36,6 +36,8 @@ enum { TPS65090_REGULATOR_FET5, TPS65090_REGULATOR_FET6, TPS65090_REGULATOR_FET7, + TPS65090_REGULATOR_LDO1, + TPS65090_REGULATOR_LDO2, /* Last entry for maximum ID */ TPS65090_REGULATOR_MAX, -- cgit v1.2.3-70-g09d2 From f329b1755b475f64f0472cda1ae9602e092f6f05 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 9 Oct 2012 15:19:02 +0530 Subject: regulator: tps65090: add external control support for DCDC The TPS65090's DCDC output can also be enable/disable through the external digital input signal. Add support for enable/disable either through register access via I2C or through external control inputs. The external control inputs can be driven through GPIOs also and hence adding support for passing the GPIO number. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65090-regulator.c | 77 +++++++++++++++++++++++++--------- include/linux/mfd/tps65090.h | 7 +++- 2 files changed, 63 insertions(+), 21 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 0732d9ba3f4..412413838e1 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -31,10 +32,13 @@ struct tps65090_regulator { struct regulator_dev *rdev; }; -static struct regulator_ops tps65090_ops = { - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, +static struct regulator_ops tps65090_ext_control_ops = { +}; + +static struct regulator_ops tps65090_reg_contol_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, }; static struct regulator_ops tps65090_ldo_ops = { @@ -53,16 +57,16 @@ static struct regulator_ops tps65090_ldo_ops = { } static struct regulator_desc tps65090_regulator_desc[] = { - tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_ops), - tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_ops), - tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_ops), - tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_ops), - tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_ops), - tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_ops), - tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_ops), - tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_ops), - tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_ops), - tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_ops), + tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_reg_contol_ops), + tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_reg_contol_ops), + tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_reg_contol_ops), tps65090_REG_DESC(LDO1, "vsys_l1", 0, tps65090_ldo_ops), tps65090_REG_DESC(LDO2, "vsys_l2", 0, tps65090_ldo_ops), }; @@ -118,6 +122,22 @@ static int __devinit tps65090_regulator_disable_ext_control( return tps65090_config_ext_control(ri, false); } +static void __devinit tps65090_configure_regulator_config( + struct tps65090_regulator_plat_data *tps_pdata, + struct regulator_config *config) +{ + if (gpio_is_valid(tps_pdata->gpio)) { + int gpio_flag = GPIOF_OUT_INIT_LOW; + + if (tps_pdata->reg_init_data->constraints.always_on || + tps_pdata->reg_init_data->constraints.boot_on) + gpio_flag = GPIOF_OUT_INIT_HIGH; + + config->ena_gpio = tps_pdata->gpio; + config->ena_gpio_flags = gpio_flag; + } +} + static int __devinit tps65090_regulator_probe(struct platform_device *pdev) { struct tps65090 *tps65090_mfd = dev_get_drvdata(pdev->dev.parent); @@ -154,18 +174,24 @@ static int __devinit tps65090_regulator_probe(struct platform_device *pdev) /* * TPS5090 DCDC support the control from external digital input. - * It may be possible that during boot, the external control is - * enabled. Disabling external control for DCDC. + * Configure it as per platform data. */ if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data) { - ret = tps65090_regulator_disable_ext_control( + if (tps_pdata->enable_ext_control) { + tps65090_configure_regulator_config( + tps_pdata, &config); + ri->desc->ops = &tps65090_ext_control_ops; + } else { + ret = tps65090_regulator_disable_ext_control( ri, tps_pdata); - if (ret < 0) { - dev_err(&pdev->dev, + if (ret < 0) { + dev_err(&pdev->dev, "failed disable ext control\n"); - goto scrub; + goto scrub; + } } } + config.dev = &pdev->dev; config.driver_data = ri; config.regmap = tps65090_mfd->rmap; @@ -182,6 +208,17 @@ static int __devinit tps65090_regulator_probe(struct platform_device *pdev) goto scrub; } ri->rdev = rdev; + + /* Enable external control if it is require */ + if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data && + tps_pdata->enable_ext_control) { + ret = tps65090_config_ext_control(ri, true); + if (ret < 0) { + /* Increment num to get unregister rdev */ + num++; + goto scrub; + } + } } platform_set_drvdata(pdev, pmic); diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 598921221ff..804e280c1e1 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -64,10 +64,15 @@ struct tps65090_subdev_info { * struct tps65090_regulator_plat_data * * @reg_init_data: The regulator init data. + * @enable_ext_control: Enable extrenal control or not. Only available for + * DCDC1, DCDC2 and DCDC3. + * @gpio: Gpio number if external control is enabled and controlled through + * gpio. */ - struct tps65090_regulator_plat_data { struct regulator_init_data *reg_init_data; + bool enable_ext_control; + int gpio; }; struct tps65090_platform_data { -- cgit v1.2.3-70-g09d2 From 64e481603ab46bcd1466fdaffca50f25bf123f83 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 18 Oct 2012 19:36:09 +0530 Subject: mfd: tps6586x: move regulator dt parsing to regulator driver Moving regulator node parsing to regulator driver in place of parsing it on mfd driver. The motivation for this change are: - MFD core driver should not depends on regulator and able to instantiate device without regulator. - The API for matching regulators are in regulator core and it is good that regulator driver only calls this API. - Regulator specific support should be in regulator driver only to ease any enhancement/modification for regulators. - The regulator driver is now registered as mfd sub device and all regulator registration is done from single probe call. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/mfd/Kconfig | 1 - drivers/mfd/tps6586x.c | 76 ++------------ drivers/regulator/tps6586x-regulator.c | 181 ++++++++++++++++++++++++++++----- include/linux/mfd/tps6586x.h | 3 + 4 files changed, 166 insertions(+), 95 deletions(-) (limited to 'include/linux/mfd') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index acab3ef8a31..05acef8c764 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -201,7 +201,6 @@ config MFD_TPS6586X depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C - depends on REGULATOR help If you say yes here you get support for the TPS6586X series of Power Management chips. diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 46746436877..9d67bd92edf 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -24,8 +24,6 @@ #include #include #include -#include -#include #include #include @@ -98,6 +96,9 @@ static struct mfd_cell tps6586x_cell[] = { { .name = "tps6586x-gpio", }, + { + .name = "tps6586x-pmic", + }, { .name = "tps6586x-rtc", }, @@ -350,80 +351,19 @@ failed: } #ifdef CONFIG_OF -static struct of_regulator_match tps6586x_matches[] = { - { .name = "sys", .driver_data = (void *)TPS6586X_ID_SYS }, - { .name = "sm0", .driver_data = (void *)TPS6586X_ID_SM_0 }, - { .name = "sm1", .driver_data = (void *)TPS6586X_ID_SM_1 }, - { .name = "sm2", .driver_data = (void *)TPS6586X_ID_SM_2 }, - { .name = "ldo0", .driver_data = (void *)TPS6586X_ID_LDO_0 }, - { .name = "ldo1", .driver_data = (void *)TPS6586X_ID_LDO_1 }, - { .name = "ldo2", .driver_data = (void *)TPS6586X_ID_LDO_2 }, - { .name = "ldo3", .driver_data = (void *)TPS6586X_ID_LDO_3 }, - { .name = "ldo4", .driver_data = (void *)TPS6586X_ID_LDO_4 }, - { .name = "ldo5", .driver_data = (void *)TPS6586X_ID_LDO_5 }, - { .name = "ldo6", .driver_data = (void *)TPS6586X_ID_LDO_6 }, - { .name = "ldo7", .driver_data = (void *)TPS6586X_ID_LDO_7 }, - { .name = "ldo8", .driver_data = (void *)TPS6586X_ID_LDO_8 }, - { .name = "ldo9", .driver_data = (void *)TPS6586X_ID_LDO_9 }, - { .name = "ldo_rtc", .driver_data = (void *)TPS6586X_ID_LDO_RTC }, -}; - static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *client) { - const unsigned int num = ARRAY_SIZE(tps6586x_matches); struct device_node *np = client->dev.of_node; struct tps6586x_platform_data *pdata; - struct tps6586x_subdev_info *devs; - struct device_node *regs; - const char *sys_rail_name = NULL; - unsigned int count; - unsigned int i, j; - int err; - - regs = of_find_node_by_name(np, "regulators"); - if (!regs) - return NULL; - - err = of_regulator_match(&client->dev, regs, tps6586x_matches, num); - if (err < 0) { - of_node_put(regs); - return NULL; - } - - of_node_put(regs); - count = err; - - devs = devm_kzalloc(&client->dev, count * sizeof(*devs), GFP_KERNEL); - if (!devs) - return NULL; - - for (i = 0, j = 0; i < num && j < count; i++) { - struct regulator_init_data *reg_idata; - - if (!tps6586x_matches[i].init_data) - continue; - - reg_idata = tps6586x_matches[i].init_data; - devs[j].name = "tps6586x-regulator"; - devs[j].platform_data = tps6586x_matches[i].init_data; - devs[j].id = (int)tps6586x_matches[i].driver_data; - if (devs[j].id == TPS6586X_ID_SYS) - sys_rail_name = reg_idata->constraints.name; - - if ((devs[j].id == TPS6586X_ID_LDO_5) || - (devs[j].id == TPS6586X_ID_LDO_RTC)) - reg_idata->supply_regulator = sys_rail_name; - - devs[j].of_node = tps6586x_matches[i].of_node; - j++; - } pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) + if (!pdata) { + dev_err(&client->dev, "Memory allocation failed\n"); return NULL; + } - pdata->num_subdevs = count; - pdata->subdevs = devs; + pdata->num_subdevs = 0; + pdata->subdevs = NULL; pdata->gpio_base = -1; pdata->irq_base = -1; pdata->pm_off = of_property_read_bool(np, "ti,system-power-controller"); diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index ce1e7cb8d51..913c903bc3e 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -17,10 +17,12 @@ #include #include #include +#include #include #include #include #include +#include #include /* supply control and voltage setting */ @@ -255,10 +257,10 @@ 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) +static int tps6586x_regulator_set_slew_rate(struct platform_device *pdev, + int id, struct regulator_init_data *p) { 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; @@ -269,7 +271,7 @@ static int tps6586x_regulator_set_slew_rate(struct platform_device *pdev) return 0; /* only SM0 and SM1 can have the slew rate settings */ - switch (pdev->id) { + switch (id) { case TPS6586X_ID_SM_0: reg = TPS6586X_SM0SL; break; @@ -298,54 +300,181 @@ static inline struct tps6586x_regulator *find_regulator_info(int id) return NULL; } +#ifdef CONFIG_OF +static struct of_regulator_match tps6586x_matches[] = { + { .name = "sys", .driver_data = (void *)TPS6586X_ID_SYS }, + { .name = "sm0", .driver_data = (void *)TPS6586X_ID_SM_0 }, + { .name = "sm1", .driver_data = (void *)TPS6586X_ID_SM_1 }, + { .name = "sm2", .driver_data = (void *)TPS6586X_ID_SM_2 }, + { .name = "ldo0", .driver_data = (void *)TPS6586X_ID_LDO_0 }, + { .name = "ldo1", .driver_data = (void *)TPS6586X_ID_LDO_1 }, + { .name = "ldo2", .driver_data = (void *)TPS6586X_ID_LDO_2 }, + { .name = "ldo3", .driver_data = (void *)TPS6586X_ID_LDO_3 }, + { .name = "ldo4", .driver_data = (void *)TPS6586X_ID_LDO_4 }, + { .name = "ldo5", .driver_data = (void *)TPS6586X_ID_LDO_5 }, + { .name = "ldo6", .driver_data = (void *)TPS6586X_ID_LDO_6 }, + { .name = "ldo7", .driver_data = (void *)TPS6586X_ID_LDO_7 }, + { .name = "ldo8", .driver_data = (void *)TPS6586X_ID_LDO_8 }, + { .name = "ldo9", .driver_data = (void *)TPS6586X_ID_LDO_9 }, + { .name = "ldo_rtc", .driver_data = (void *)TPS6586X_ID_LDO_RTC }, +}; + +static struct tps6586x_platform_data *tps6586x_parse_regulator_dt( + struct platform_device *pdev, + struct of_regulator_match **tps6586x_reg_matches) +{ + const unsigned int num = ARRAY_SIZE(tps6586x_matches); + struct device_node *np = pdev->dev.parent->of_node; + struct device_node *regs; + const char *sys_rail = NULL; + unsigned int i; + struct tps6586x_platform_data *pdata; + int err; + + regs = of_find_node_by_name(np, "regulators"); + if (!regs) { + dev_err(&pdev->dev, "regulator node not found\n"); + return NULL; + } + + err = of_regulator_match(&pdev->dev, regs, tps6586x_matches, num); + if (err < 0) { + dev_err(&pdev->dev, "Regulator match failed, e %d\n", err); + of_node_put(regs); + return NULL; + } + + of_node_put(regs); + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "Memory alloction failed\n"); + return NULL; + } + + for (i = 0; i < num; i++) { + int id; + if (!tps6586x_matches[i].init_data) + continue; + + pdata->reg_init_data[i] = tps6586x_matches[i].init_data; + id = (int)tps6586x_matches[i].driver_data; + if (id == TPS6586X_ID_SYS) + sys_rail = pdata->reg_init_data[i]->constraints.name; + + if ((id == TPS6586X_ID_LDO_5) || (id == TPS6586X_ID_LDO_RTC)) + pdata->reg_init_data[i]->supply_regulator = sys_rail; + } + *tps6586x_reg_matches = tps6586x_matches; + return pdata; +} +#else +static struct tps6586x_platform_data *tps6586x_parse_regulator_dt( + struct platform_device *pdev, + struct of_regulator_match **tps6586x_reg_matches) +{ + *tps6586x_reg_matches = NULL; + return NULL; +} +#endif + static int __devinit tps6586x_regulator_probe(struct platform_device *pdev) { struct tps6586x_regulator *ri = NULL; struct regulator_config config = { }; - struct regulator_dev *rdev; - int id = pdev->id; + struct regulator_dev **rdev; + struct regulator_init_data *reg_data; + struct tps6586x_platform_data *pdata; + struct of_regulator_match *tps6586x_reg_matches = NULL; + int id; int err; dev_dbg(&pdev->dev, "Probing regulator %d\n", id); - ri = find_regulator_info(id); - if (ri == NULL) { - dev_err(&pdev->dev, "invalid regulator ID specified\n"); - return -EINVAL; - } + pdata = dev_get_platdata(pdev->dev.parent); + if ((!pdata) && (pdev->dev.parent->of_node)) + pdata = tps6586x_parse_regulator_dt(pdev, + &tps6586x_reg_matches); - err = tps6586x_regulator_preinit(pdev->dev.parent, ri); - if (err) - return err; + if (!pdata) { + dev_err(&pdev->dev, "Platform data not available, exiting\n"); + return -ENODEV; + } - config.dev = pdev->dev.parent; - config.of_node = pdev->dev.of_node; - config.init_data = pdev->dev.platform_data; - config.driver_data = ri; + rdev = devm_kzalloc(&pdev->dev, TPS6586X_ID_MAX_REGULATOR * + sizeof(*rdev), GFP_KERNEL); + if (!rdev) { + dev_err(&pdev->dev, "Mmemory alloc failed\n"); + return -ENOMEM; + } - rdev = regulator_register(&ri->desc, &config); - if (IS_ERR(rdev)) { - dev_err(&pdev->dev, "failed to register regulator %s\n", - ri->desc.name); - return PTR_ERR(rdev); + for (id = 0; id < TPS6586X_ID_MAX_REGULATOR; ++id) { + reg_data = pdata->reg_init_data[id]; + + ri = find_regulator_info(id); + if (!ri) { + dev_err(&pdev->dev, "invalid regulator ID specified\n"); + err = -EINVAL; + goto fail; + } + + err = tps6586x_regulator_preinit(pdev->dev.parent, ri); + if (err) { + dev_err(&pdev->dev, + "regulator %d preinit failed, e %d\n", id, err); + goto fail; + } + + config.dev = pdev->dev.parent; + config.init_data = reg_data; + config.driver_data = ri; + + if (tps6586x_reg_matches) + config.of_node = tps6586x_reg_matches[id].of_node; + + rdev[id] = regulator_register(&ri->desc, &config); + if (IS_ERR(rdev[id])) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc.name); + err = PTR_ERR(rdev[id]); + goto fail; + } + + if (reg_data) { + err = tps6586x_regulator_set_slew_rate(pdev, id, + reg_data); + if (err < 0) { + dev_err(&pdev->dev, + "Slew rate config failed, e %d\n", err); + regulator_unregister(rdev[id]); + goto fail; + } + } } platform_set_drvdata(pdev, rdev); + return 0; - return tps6586x_regulator_set_slew_rate(pdev); +fail: + while (--id >= 0) + regulator_unregister(rdev[id]); + return err; } static int __devexit tps6586x_regulator_remove(struct platform_device *pdev) { - struct regulator_dev *rdev = platform_get_drvdata(pdev); + struct regulator_dev **rdev = platform_get_drvdata(pdev); + int id = TPS6586X_ID_MAX_REGULATOR; + + while (--id >= 0) + regulator_unregister(rdev[id]); - regulator_unregister(rdev); return 0; } static struct platform_driver tps6586x_regulator_driver = { .driver = { - .name = "tps6586x-regulator", + .name = "tps6586x-pmic", .owner = THIS_MODULE, }, .probe = tps6586x_regulator_probe, diff --git a/include/linux/mfd/tps6586x.h b/include/linux/mfd/tps6586x.h index 2dd12319495..f8da0e15256 100644 --- a/include/linux/mfd/tps6586x.h +++ b/include/linux/mfd/tps6586x.h @@ -29,6 +29,7 @@ enum { TPS6586X_ID_LDO_8, TPS6586X_ID_LDO_9, TPS6586X_ID_LDO_RTC, + TPS6586X_ID_MAX_REGULATOR, }; enum { @@ -79,6 +80,8 @@ struct tps6586x_platform_data { int gpio_base; int irq_base; bool pm_off; + + struct regulator_init_data *reg_init_data[TPS6586X_ID_MAX_REGULATOR]; }; /* -- cgit v1.2.3-70-g09d2