From 824bd1be3ed01d67197098650d0c62b176087b11 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 20 May 2019 11:34:45 +0200 Subject: regulator: da9063: move definitions out of a header into the driver Those definitions are only used within the driver meanwhile, so put them there. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Acked-by: Steve Twiss Tested-by: Steve Twiss Reviewed-by: Geert Uytterhoeven Signed-off-by: Mark Brown --- include/linux/mfd/da9063/pdata.h | 49 ---------------------------------------- 1 file changed, 49 deletions(-) (limited to 'include') diff --git a/include/linux/mfd/da9063/pdata.h b/include/linux/mfd/da9063/pdata.h index 50bed4f89c1a..21a2d107f0cf 100644 --- a/include/linux/mfd/da9063/pdata.h +++ b/include/linux/mfd/da9063/pdata.h @@ -16,55 +16,6 @@ #ifndef __MFD_DA9063_PDATA_H__ #define __MFD_DA9063_PDATA_H__ -#include - -/* - * Regulator configuration - */ -/* DA9063 and DA9063L regulator IDs */ -enum { - /* BUCKs */ - DA9063_ID_BCORE1, - DA9063_ID_BCORE2, - DA9063_ID_BPRO, - DA9063_ID_BMEM, - DA9063_ID_BIO, - DA9063_ID_BPERI, - - /* BCORE1 and BCORE2 in merged mode */ - DA9063_ID_BCORES_MERGED, - /* BMEM and BIO in merged mode */ - DA9063_ID_BMEM_BIO_MERGED, - /* When two BUCKs are merged, they cannot be reused separately */ - - /* LDOs on both DA9063 and DA9063L */ - DA9063_ID_LDO3, - DA9063_ID_LDO7, - DA9063_ID_LDO8, - DA9063_ID_LDO9, - DA9063_ID_LDO11, - - /* DA9063-only LDOs */ - DA9063_ID_LDO1, - DA9063_ID_LDO2, - DA9063_ID_LDO4, - DA9063_ID_LDO5, - DA9063_ID_LDO6, - DA9063_ID_LDO10, -}; - -/* Regulators platform data */ -struct da9063_regulator_data { - int id; - struct regulator_init_data *initdata; -}; - -struct da9063_regulators_pdata { - unsigned n_regulators; - struct da9063_regulator_data *regulator_data; -}; - - /* * RGB LED configuration */ -- cgit v1.2.3 From 9a5ed0bac86edce4097abf7595a7de050b2f87fa Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 12 Jun 2019 09:42:22 +0200 Subject: regulator: wm831x: Convert to use GPIO descriptors This converts the Wolfson Micro WM831x DCDC converter to use a GPIO descriptor for the GPIO driving the DVS pin. There is just one (non-DT) machine in the kernel using this, and that is the Wolfson Micro (now Cirrus) Cragganmore 6410 so we patch this board to pass a descriptor table and fix up the driver accordingly. Cc: Charles Keepax Cc: Richard Fitzgerald Cc: patches@opensource.cirrus.com Signed-off-by: Linus Walleij Acked-by: Charles Keepax Signed-off-by: Mark Brown --- arch/arm/mach-s3c64xx/mach-crag6410.c | 21 ++++++++++++++++++++- drivers/regulator/wm831x-dcdc.c | 29 +++++++++++++---------------- include/linux/mfd/wm831x/pdata.h | 1 - 3 files changed, 33 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index 379424d72ae7..8ec6a4f5eb05 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -398,7 +399,6 @@ static struct pca953x_platform_data crag6410_pca_data = { /* VDDARM is controlled by DVS1 connected to GPK(0) */ static struct wm831x_buckv_pdata vddarm_pdata = { .dvs_control_src = 1, - .dvs_gpio = S3C64XX_GPK(0), }; static struct regulator_consumer_supply vddarm_consumers[] = { @@ -596,6 +596,24 @@ static struct wm831x_pdata crag_pmic_pdata = { .touch = &touch_pdata, }; +/* + * VDDARM is eventually ending up as a regulator hanging on the MFD cell device + * "wm831x-buckv.1" spawn from drivers/mfd/wm831x-core.c. + * + * From the note on the platform data we can see that this is clearly DVS1 + * and assigned as dcdc1 resource to the MFD core which sets .id of the cell + * spawning the DVS1 platform device to 1, then the cell platform device + * name is calculated from 10*instance + id resulting in the device name + * "wm831x-buckv.11" + */ +static struct gpiod_lookup_table crag_pmic_gpiod_table = { + .dev_id = "wm831x-buckv.11", + .table = { + GPIO_LOOKUP("GPIOK", 0, "dvs", GPIO_ACTIVE_HIGH), + { }, + }, +}; + static struct i2c_board_info i2c_devs0[] = { { I2C_BOARD_INFO("24c08", 0x50), }, { I2C_BOARD_INFO("tca6408", 0x20), @@ -836,6 +854,7 @@ static void __init crag6410_machine_init(void) s3c_fb_set_platdata(&crag6410_lcd_pdata); dwc2_hsotg_set_platdata(&crag6410_hsotg_pdata); + gpiod_add_lookup_table(&crag_pmic_gpiod_table); i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0)); i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1)); diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index b422eef97b77..018dbbd96771 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -50,7 +50,7 @@ struct wm831x_dcdc { int base; struct wm831x *wm831x; struct regulator_dev *regulator; - int dvs_gpio; + struct gpio_desc *dvs_gpiod; int dvs_gpio_state; int on_vsel; int dvs_vsel; @@ -217,7 +217,7 @@ static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state) return 0; dcdc->dvs_gpio_state = state; - gpio_set_value(dcdc->dvs_gpio, state); + gpiod_set_value(dcdc->dvs_gpiod, state); /* Should wait for DVS state change to be asserted if we have * a GPIO for it, for now assume the device is configured @@ -237,10 +237,10 @@ static int wm831x_buckv_set_voltage_sel(struct regulator_dev *rdev, int ret; /* If this value is already set then do a GPIO update if we can */ - if (dcdc->dvs_gpio && dcdc->on_vsel == vsel) + if (dcdc->dvs_gpiod && dcdc->on_vsel == vsel) return wm831x_buckv_set_dvs(rdev, 0); - if (dcdc->dvs_gpio && dcdc->dvs_vsel == vsel) + if (dcdc->dvs_gpiod && dcdc->dvs_vsel == vsel) return wm831x_buckv_set_dvs(rdev, 1); /* Always set the ON status to the minimum voltage */ @@ -249,7 +249,7 @@ static int wm831x_buckv_set_voltage_sel(struct regulator_dev *rdev, return ret; dcdc->on_vsel = vsel; - if (!dcdc->dvs_gpio) + if (!dcdc->dvs_gpiod) return ret; /* Kick the voltage transition now */ @@ -296,7 +296,7 @@ static int wm831x_buckv_get_voltage_sel(struct regulator_dev *rdev) { struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); - if (dcdc->dvs_gpio && dcdc->dvs_gpio_state) + if (dcdc->dvs_gpiod && dcdc->dvs_gpio_state) return dcdc->dvs_vsel; else return dcdc->on_vsel; @@ -337,7 +337,7 @@ static void wm831x_buckv_dvs_init(struct platform_device *pdev, int ret; u16 ctrl; - if (!pdata || !pdata->dvs_gpio) + if (!pdata) return; /* gpiolib won't let us read the GPIO status so pick the higher @@ -345,17 +345,14 @@ static void wm831x_buckv_dvs_init(struct platform_device *pdev, */ dcdc->dvs_gpio_state = pdata->dvs_init_state; - ret = devm_gpio_request_one(&pdev->dev, pdata->dvs_gpio, - dcdc->dvs_gpio_state ? GPIOF_INIT_HIGH : 0, - "DCDC DVS"); - if (ret < 0) { - dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n", - dcdc->name, ret); + dcdc->dvs_gpiod = devm_gpiod_get(&pdev->dev, "dvs", + dcdc->dvs_gpio_state ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW); + if (IS_ERR(dcdc->dvs_gpiod)) { + dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %ld\n", + dcdc->name, PTR_ERR(dcdc->dvs_gpiod)); return; } - dcdc->dvs_gpio = pdata->dvs_gpio; - switch (pdata->dvs_control_src) { case 1: ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT; diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h index dcc9631b3052..1b8bb36e13b8 100644 --- a/include/linux/mfd/wm831x/pdata.h +++ b/include/linux/mfd/wm831x/pdata.h @@ -52,7 +52,6 @@ struct wm831x_battery_pdata { * I2C or SPI buses. */ struct wm831x_buckv_pdata { - int dvs_gpio; /** CPU GPIO to use for DVS switching */ int dvs_control_src; /** Hardware DVS source to use (1 or 2) */ int dvs_init_state; /** DVS state to expect on startup */ int dvs_state_gpio; /** CPU GPIO to use for monitoring status */ -- cgit v1.2.3 From fd742eaab827b47c5f2de6c1811a17075608da60 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 9 Jun 2019 13:48:12 +0200 Subject: regulator: max8952: Convert to use GPIO descriptors This finalizes the descriptor conversion of the MAX8952 driver by letting the VID0 and VID1 GPIOs be fetched from descriptors. Both VID0 and VID1 must be supplied for the VID selection to work, I add some code to preserve the semantics that if only one of the two VID gpios is supplied, it will be initialized to low. This might be a bit overzealous, but I want to preserve any implicit semantics. This is currently only used by device tree in-kernel but it is still also possible to supply the same GPIOs using a machine descriptor table if a board file is used. Ideally this should be phased over to using gpio-regulator.c that does the same thing, but it might require some refactoring and needs testing on real hardware. Cc: Tomasz Figa Cc: MyungJoo Ham Cc: Marek Szyprowski Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/max8952.c | 62 ++++++++++++++++++--------------------- include/linux/regulator/max8952.h | 3 -- 2 files changed, 28 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index 451237efb359..8f0e4dc810f0 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -26,11 +26,9 @@ #include #include #include -#include #include #include #include -#include #include #include @@ -50,7 +48,8 @@ enum { struct max8952_data { struct i2c_client *client; struct max8952_platform_data *pdata; - + struct gpio_desc *vid0_gpiod; + struct gpio_desc *vid1_gpiod; bool vid0; bool vid1; }; @@ -100,16 +99,15 @@ static int max8952_set_voltage_sel(struct regulator_dev *rdev, { struct max8952_data *max8952 = rdev_get_drvdata(rdev); - if (!gpio_is_valid(max8952->pdata->gpio_vid0) || - !gpio_is_valid(max8952->pdata->gpio_vid1)) { + if (!max8952->vid0_gpiod || !max8952->vid1_gpiod) { /* DVS not supported */ return -EPERM; } max8952->vid0 = selector & 0x1; max8952->vid1 = (selector >> 1) & 0x1; - gpio_set_value(max8952->pdata->gpio_vid0, max8952->vid0); - gpio_set_value(max8952->pdata->gpio_vid1, max8952->vid1); + gpiod_set_value(max8952->vid0_gpiod, max8952->vid0); + gpiod_set_value(max8952->vid1_gpiod, max8952->vid1); return 0; } @@ -147,9 +145,6 @@ static struct max8952_platform_data *max8952_parse_dt(struct device *dev) if (!pd) return NULL; - pd->gpio_vid0 = of_get_named_gpio(np, "max8952,vid-gpios", 0); - pd->gpio_vid1 = of_get_named_gpio(np, "max8952,vid-gpios", 1); - if (of_property_read_u32(np, "max8952,default-mode", &pd->default_mode)) dev_warn(dev, "Default mode not specified, assuming 0\n"); @@ -200,7 +195,7 @@ static int max8952_pmic_probe(struct i2c_client *client, struct gpio_desc *gpiod; enum gpiod_flags gflags; - int ret = 0, err = 0; + int ret = 0; if (client->dev.of_node) pdata = max8952_parse_dt(&client->dev); @@ -253,32 +248,31 @@ static int max8952_pmic_probe(struct i2c_client *client, max8952->vid0 = pdata->default_mode & 0x1; max8952->vid1 = (pdata->default_mode >> 1) & 0x1; - if (gpio_is_valid(pdata->gpio_vid0) && - gpio_is_valid(pdata->gpio_vid1)) { - unsigned long gpio_flags; - - gpio_flags = max8952->vid0 ? - GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; - if (devm_gpio_request_one(&client->dev, pdata->gpio_vid0, - gpio_flags, "MAX8952 VID0")) - err = 1; - - gpio_flags = max8952->vid1 ? - GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; - if (devm_gpio_request_one(&client->dev, pdata->gpio_vid1, - gpio_flags, "MAX8952 VID1")) - err = 2; - } else - err = 3; - - if (err) { + /* Fetch vid0 and vid1 GPIOs if available */ + gflags = max8952->vid0 ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; + max8952->vid0_gpiod = devm_gpiod_get_index_optional(&client->dev, + "max8952,vid", + 0, gflags); + if (IS_ERR(max8952->vid0_gpiod)) + return PTR_ERR(max8952->vid0_gpiod); + gflags = max8952->vid1 ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; + max8952->vid1_gpiod = devm_gpiod_get_index_optional(&client->dev, + "max8952,vid", + 1, gflags); + if (IS_ERR(max8952->vid1_gpiod)) + return PTR_ERR(max8952->vid1_gpiod); + + /* If either VID GPIO is missing just disable this */ + if (!max8952->vid0_gpiod || !max8952->vid1_gpiod) { dev_warn(&client->dev, "VID0/1 gpio invalid: " - "DVS not available.\n"); + "DVS not available.\n"); max8952->vid0 = 0; max8952->vid1 = 0; - /* Mark invalid */ - pdata->gpio_vid0 = -1; - pdata->gpio_vid1 = -1; + /* Make sure if we have any descriptors they get set to low */ + if (max8952->vid0_gpiod) + gpiod_set_value(max8952->vid0_gpiod, 0); + if (max8952->vid1_gpiod) + gpiod_set_value(max8952->vid1_gpiod, 0); /* Disable Pulldown of EN only */ max8952_write_reg(max8952, MAX8952_REG_CONTROL, 0x60); diff --git a/include/linux/regulator/max8952.h b/include/linux/regulator/max8952.h index 686c42c041b5..33b6e2c09c05 100644 --- a/include/linux/regulator/max8952.h +++ b/include/linux/regulator/max8952.h @@ -118,9 +118,6 @@ enum { #define MAX8952_NUM_DVS_MODE 4 struct max8952_platform_data { - int gpio_vid0; - int gpio_vid1; - u32 default_mode; u32 dvs_mode[MAX8952_NUM_DVS_MODE]; /* MAX8952_DVS_MODEx_XXXXmV */ -- cgit v1.2.3 From fd5d10059d5ead12dd12f05ae6d96e70d1fac3df Mon Sep 17 00:00:00 2001 From: Felix Riemann Date: Thu, 20 Jun 2019 08:45:00 +0100 Subject: regulator: da9061/62: Adjust LDO voltage selection minimum value According to the DA9061 and DA9062 datasheets the LDO voltage selection registers have a lower value of 0x02. This applies to voltage registers VLDO1_A, VLDO2_A, VLDO3_A and VLDO4_A. This linear offset of 0x02 was previously not observed by the driver, causing the LDO output voltage to be systematically lower by two steps (= 0.1V). This patch fixes the minimum linear selector offset by setting it to a value of 2 and increases the n_voltages by the same amount allowing voltages in the range 0x02 -> 0.9V to 0x38 -> 3.6V to be correctly selected. Also fixes an incorrect calculaton for the n_voltages value in the regulator LDO2. These fixes effect all LDO regulators for DA9061 and DA9062. Acked-by: Steve Twiss Tested-by: Steve Twiss Signed-off-by: Felix Riemann Signed-off-by: Steve Twiss Signed-off-by: Mark Brown --- drivers/regulator/da9062-regulator.c | 40 +++++++++++++++++++++--------------- include/linux/mfd/da9062/registers.h | 3 +++ 2 files changed, 27 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c index a02e0488410f..2ffc64622451 100644 --- a/drivers/regulator/da9062-regulator.c +++ b/drivers/regulator/da9062-regulator.c @@ -493,12 +493,13 @@ static const struct da9062_regulator_info local_da9061_regulator_info[] = { .desc.ops = &da9062_ldo_ops, .desc.min_uV = (900) * 1000, .desc.uV_step = (50) * 1000, - .desc.n_voltages = ((3600) - (900))/(50) + 1, + .desc.n_voltages = ((3600) - (900))/(50) + 1 + + DA9062AA_VLDO_A_MIN_SEL, .desc.enable_reg = DA9062AA_LDO1_CONT, .desc.enable_mask = DA9062AA_LDO1_EN_MASK, .desc.vsel_reg = DA9062AA_VLDO1_A, .desc.vsel_mask = DA9062AA_VLDO1_A_MASK, - .desc.linear_min_sel = 0, + .desc.linear_min_sel = DA9062AA_VLDO_A_MIN_SEL, .sleep = REG_FIELD(DA9062AA_VLDO1_A, __builtin_ffs((int)DA9062AA_LDO1_SL_A_MASK) - 1, sizeof(unsigned int) * 8 - @@ -525,12 +526,13 @@ static const struct da9062_regulator_info local_da9061_regulator_info[] = { .desc.ops = &da9062_ldo_ops, .desc.min_uV = (900) * 1000, .desc.uV_step = (50) * 1000, - .desc.n_voltages = ((3600) - (600))/(50) + 1, + .desc.n_voltages = ((3600) - (900))/(50) + 1 + + DA9062AA_VLDO_A_MIN_SEL, .desc.enable_reg = DA9062AA_LDO2_CONT, .desc.enable_mask = DA9062AA_LDO2_EN_MASK, .desc.vsel_reg = DA9062AA_VLDO2_A, .desc.vsel_mask = DA9062AA_VLDO2_A_MASK, - .desc.linear_min_sel = 0, + .desc.linear_min_sel = DA9062AA_VLDO_A_MIN_SEL, .sleep = REG_FIELD(DA9062AA_VLDO2_A, __builtin_ffs((int)DA9062AA_LDO2_SL_A_MASK) - 1, sizeof(unsigned int) * 8 - @@ -557,12 +559,13 @@ static const struct da9062_regulator_info local_da9061_regulator_info[] = { .desc.ops = &da9062_ldo_ops, .desc.min_uV = (900) * 1000, .desc.uV_step = (50) * 1000, - .desc.n_voltages = ((3600) - (900))/(50) + 1, + .desc.n_voltages = ((3600) - (900))/(50) + 1 + + DA9062AA_VLDO_A_MIN_SEL, .desc.enable_reg = DA9062AA_LDO3_CONT, .desc.enable_mask = DA9062AA_LDO3_EN_MASK, .desc.vsel_reg = DA9062AA_VLDO3_A, .desc.vsel_mask = DA9062AA_VLDO3_A_MASK, - .desc.linear_min_sel = 0, + .desc.linear_min_sel = DA9062AA_VLDO_A_MIN_SEL, .sleep = REG_FIELD(DA9062AA_VLDO3_A, __builtin_ffs((int)DA9062AA_LDO3_SL_A_MASK) - 1, sizeof(unsigned int) * 8 - @@ -589,12 +592,13 @@ static const struct da9062_regulator_info local_da9061_regulator_info[] = { .desc.ops = &da9062_ldo_ops, .desc.min_uV = (900) * 1000, .desc.uV_step = (50) * 1000, - .desc.n_voltages = ((3600) - (900))/(50) + 1, + .desc.n_voltages = ((3600) - (900))/(50) + 1 + + DA9062AA_VLDO_A_MIN_SEL, .desc.enable_reg = DA9062AA_LDO4_CONT, .desc.enable_mask = DA9062AA_LDO4_EN_MASK, .desc.vsel_reg = DA9062AA_VLDO4_A, .desc.vsel_mask = DA9062AA_VLDO4_A_MASK, - .desc.linear_min_sel = 0, + .desc.linear_min_sel = DA9062AA_VLDO_A_MIN_SEL, .sleep = REG_FIELD(DA9062AA_VLDO4_A, __builtin_ffs((int)DA9062AA_LDO4_SL_A_MASK) - 1, sizeof(unsigned int) * 8 - @@ -769,12 +773,13 @@ static const struct da9062_regulator_info local_da9062_regulator_info[] = { .desc.ops = &da9062_ldo_ops, .desc.min_uV = (900) * 1000, .desc.uV_step = (50) * 1000, - .desc.n_voltages = ((3600) - (900))/(50) + 1, + .desc.n_voltages = ((3600) - (900))/(50) + 1 + + DA9062AA_VLDO_A_MIN_SEL, .desc.enable_reg = DA9062AA_LDO1_CONT, .desc.enable_mask = DA9062AA_LDO1_EN_MASK, .desc.vsel_reg = DA9062AA_VLDO1_A, .desc.vsel_mask = DA9062AA_VLDO1_A_MASK, - .desc.linear_min_sel = 0, + .desc.linear_min_sel = DA9062AA_VLDO_A_MIN_SEL, .sleep = REG_FIELD(DA9062AA_VLDO1_A, __builtin_ffs((int)DA9062AA_LDO1_SL_A_MASK) - 1, sizeof(unsigned int) * 8 - @@ -801,12 +806,13 @@ static const struct da9062_regulator_info local_da9062_regulator_info[] = { .desc.ops = &da9062_ldo_ops, .desc.min_uV = (900) * 1000, .desc.uV_step = (50) * 1000, - .desc.n_voltages = ((3600) - (600))/(50) + 1, + .desc.n_voltages = ((3600) - (900))/(50) + 1 + + DA9062AA_VLDO_A_MIN_SEL, .desc.enable_reg = DA9062AA_LDO2_CONT, .desc.enable_mask = DA9062AA_LDO2_EN_MASK, .desc.vsel_reg = DA9062AA_VLDO2_A, .desc.vsel_mask = DA9062AA_VLDO2_A_MASK, - .desc.linear_min_sel = 0, + .desc.linear_min_sel = DA9062AA_VLDO_A_MIN_SEL, .sleep = REG_FIELD(DA9062AA_VLDO2_A, __builtin_ffs((int)DA9062AA_LDO2_SL_A_MASK) - 1, sizeof(unsigned int) * 8 - @@ -833,12 +839,13 @@ static const struct da9062_regulator_info local_da9062_regulator_info[] = { .desc.ops = &da9062_ldo_ops, .desc.min_uV = (900) * 1000, .desc.uV_step = (50) * 1000, - .desc.n_voltages = ((3600) - (900))/(50) + 1, + .desc.n_voltages = ((3600) - (900))/(50) + 1 + + DA9062AA_VLDO_A_MIN_SEL, .desc.enable_reg = DA9062AA_LDO3_CONT, .desc.enable_mask = DA9062AA_LDO3_EN_MASK, .desc.vsel_reg = DA9062AA_VLDO3_A, .desc.vsel_mask = DA9062AA_VLDO3_A_MASK, - .desc.linear_min_sel = 0, + .desc.linear_min_sel = DA9062AA_VLDO_A_MIN_SEL, .sleep = REG_FIELD(DA9062AA_VLDO3_A, __builtin_ffs((int)DA9062AA_LDO3_SL_A_MASK) - 1, sizeof(unsigned int) * 8 - @@ -865,12 +872,13 @@ static const struct da9062_regulator_info local_da9062_regulator_info[] = { .desc.ops = &da9062_ldo_ops, .desc.min_uV = (900) * 1000, .desc.uV_step = (50) * 1000, - .desc.n_voltages = ((3600) - (900))/(50) + 1, + .desc.n_voltages = ((3600) - (900))/(50) + 1 + + DA9062AA_VLDO_A_MIN_SEL, .desc.enable_reg = DA9062AA_LDO4_CONT, .desc.enable_mask = DA9062AA_LDO4_EN_MASK, .desc.vsel_reg = DA9062AA_VLDO4_A, .desc.vsel_mask = DA9062AA_VLDO4_A_MASK, - .desc.linear_min_sel = 0, + .desc.linear_min_sel = DA9062AA_VLDO_A_MIN_SEL, .sleep = REG_FIELD(DA9062AA_VLDO4_A, __builtin_ffs((int)DA9062AA_LDO4_SL_A_MASK) - 1, sizeof(unsigned int) * 8 - diff --git a/include/linux/mfd/da9062/registers.h b/include/linux/mfd/da9062/registers.h index fe04b708742b..2906bf6160fb 100644 --- a/include/linux/mfd/da9062/registers.h +++ b/include/linux/mfd/da9062/registers.h @@ -797,6 +797,9 @@ #define DA9062AA_BUCK3_SL_A_SHIFT 7 #define DA9062AA_BUCK3_SL_A_MASK BIT(7) +/* DA9062AA_VLDO[1-4]_A common */ +#define DA9062AA_VLDO_A_MIN_SEL 2 + /* DA9062AA_VLDO1_A = 0x0A9 */ #define DA9062AA_VLDO1_A_SHIFT 0 #define DA9062AA_VLDO1_A_MASK 0x3f -- cgit v1.2.3 From 65d80db2ee92330269e90313c6af782036f4d23d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 20 Jun 2019 20:35:27 +0200 Subject: regulator: s2mps11: Add support for disabling S2MPS11 regulators in suspend The driver supported turning off regulators in suspend only for S2MPS14 device. However this makes also sense for S2MPS11 and can reduce the power consumption during suspend to RAM. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 210 ++++++++++++++++++++---------------- include/linux/mfd/samsung/s2mps11.h | 5 + 2 files changed, 120 insertions(+), 95 deletions(-) (limited to 'include') diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 93570712eb56..9c06ecd80a90 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -34,7 +34,7 @@ struct s2mps11_info { enum sec_device_type dev_type; /* - * One bit for each S2MPS13/S2MPS14/S2MPU02 regulator whether + * One bit for each S2MPS11/S2MPS13/S2MPS14/S2MPU02 regulator whether * the suspend mode was enabled. */ DECLARE_BITMAP(suspend_state, S2MPS_REGULATOR_MAX); @@ -225,27 +225,133 @@ ramp_disable: 1 << enable_shift, 0); } +static int s2mps11_regulator_enable(struct regulator_dev *rdev) +{ + struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); + int rdev_id = rdev_get_id(rdev); + unsigned int val; + + switch (s2mps11->dev_type) { + case S2MPS11X: + if (test_bit(rdev_id, s2mps11->suspend_state)) + val = S2MPS14_ENABLE_SUSPEND; + else + val = rdev->desc->enable_mask; + break; + case S2MPS13X: + case S2MPS14X: + if (test_bit(rdev_id, s2mps11->suspend_state)) + val = S2MPS14_ENABLE_SUSPEND; + else if (s2mps11->ext_control_gpiod[rdev_id]) + val = S2MPS14_ENABLE_EXT_CONTROL; + else + val = rdev->desc->enable_mask; + break; + case S2MPU02: + if (test_bit(rdev_id, s2mps11->suspend_state)) + val = S2MPU02_ENABLE_SUSPEND; + else + val = rdev->desc->enable_mask; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, val); +} + +static int s2mps11_regulator_set_suspend_disable(struct regulator_dev *rdev) +{ + int ret; + unsigned int val, state; + struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); + int rdev_id = rdev_get_id(rdev); + + /* Below LDO should be always on or does not support suspend mode. */ + switch (s2mps11->dev_type) { + case S2MPS11X: + switch (rdev_id) { + case S2MPS11_LDO2: + case S2MPS11_LDO36: + case S2MPS11_LDO37: + case S2MPS11_LDO38: + return 0; + default: + state = S2MPS14_ENABLE_SUSPEND; + break; + } + break; + case S2MPS13X: + case S2MPS14X: + switch (rdev_id) { + case S2MPS14_LDO3: + return 0; + default: + state = S2MPS14_ENABLE_SUSPEND; + break; + } + break; + case S2MPU02: + switch (rdev_id) { + case S2MPU02_LDO13: + case S2MPU02_LDO14: + case S2MPU02_LDO15: + case S2MPU02_LDO17: + case S2MPU02_BUCK7: + state = S2MPU02_DISABLE_SUSPEND; + break; + default: + state = S2MPU02_ENABLE_SUSPEND; + break; + } + break; + default: + return -EINVAL; + } + + ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); + if (ret < 0) + return ret; + + set_bit(rdev_id, s2mps11->suspend_state); + /* + * Don't enable suspend mode if regulator is already disabled because + * this would effectively for a short time turn on the regulator after + * resuming. + * However we still want to toggle the suspend_state bit for regulator + * in case if it got enabled before suspending the system. + */ + if (!(val & rdev->desc->enable_mask)) + return 0; + + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, state); +} + static const struct regulator_ops s2mps11_ldo_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, - .enable = regulator_enable_regmap, + .enable = s2mps11_regulator_enable, .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_suspend_disable = s2mps11_regulator_set_suspend_disable, }; static const struct regulator_ops s2mps11_buck_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, - .enable = regulator_enable_regmap, + .enable = s2mps11_regulator_enable, .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = s2mps11_regulator_set_voltage_time_sel, .set_ramp_delay = s2mps11_set_ramp_delay, + .set_suspend_disable = s2mps11_regulator_set_suspend_disable, }; #define regulator_desc_s2mps11_ldo(num, step) { \ @@ -501,102 +607,16 @@ static const struct regulator_desc s2mps13_regulators[] = { regulator_desc_s2mps13_buck8_10(10, MIN_500_MV, STEP_6_25_MV, 0x10), }; -static int s2mps14_regulator_enable(struct regulator_dev *rdev) -{ - struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); - int rdev_id = rdev_get_id(rdev); - unsigned int val; - - switch (s2mps11->dev_type) { - case S2MPS13X: - case S2MPS14X: - if (test_bit(rdev_id, s2mps11->suspend_state)) - val = S2MPS14_ENABLE_SUSPEND; - else if (s2mps11->ext_control_gpiod[rdev_id]) - val = S2MPS14_ENABLE_EXT_CONTROL; - else - val = rdev->desc->enable_mask; - break; - case S2MPU02: - if (test_bit(rdev_id, s2mps11->suspend_state)) - val = S2MPU02_ENABLE_SUSPEND; - else - val = rdev->desc->enable_mask; - break; - default: - return -EINVAL; - } - - return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, - rdev->desc->enable_mask, val); -} - -static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev) -{ - int ret; - unsigned int val, state; - struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); - int rdev_id = rdev_get_id(rdev); - - /* Below LDO should be always on or does not support suspend mode. */ - switch (s2mps11->dev_type) { - case S2MPS13X: - case S2MPS14X: - switch (rdev_id) { - case S2MPS14_LDO3: - return 0; - default: - state = S2MPS14_ENABLE_SUSPEND; - break; - } - break; - case S2MPU02: - switch (rdev_id) { - case S2MPU02_LDO13: - case S2MPU02_LDO14: - case S2MPU02_LDO15: - case S2MPU02_LDO17: - case S2MPU02_BUCK7: - state = S2MPU02_DISABLE_SUSPEND; - break; - default: - state = S2MPU02_ENABLE_SUSPEND; - break; - } - break; - default: - return -EINVAL; - } - - ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val); - if (ret < 0) - return ret; - - set_bit(rdev_id, s2mps11->suspend_state); - /* - * Don't enable suspend mode if regulator is already disabled because - * this would effectively for a short time turn on the regulator after - * resuming. - * However we still want to toggle the suspend_state bit for regulator - * in case if it got enabled before suspending the system. - */ - if (!(val & rdev->desc->enable_mask)) - return 0; - - return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, - rdev->desc->enable_mask, state); -} - static const struct regulator_ops s2mps14_reg_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, - .enable = s2mps14_regulator_enable, + .enable = s2mps11_regulator_enable, .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, - .set_suspend_disable = s2mps14_regulator_set_suspend_disable, + .set_suspend_disable = s2mps11_regulator_set_suspend_disable, }; #define regulator_desc_s2mps14_ldo(num, min, step) { \ @@ -888,24 +908,24 @@ static const struct regulator_ops s2mpu02_ldo_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, - .enable = s2mps14_regulator_enable, + .enable = s2mps11_regulator_enable, .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, - .set_suspend_disable = s2mps14_regulator_set_suspend_disable, + .set_suspend_disable = s2mps11_regulator_set_suspend_disable, }; static const struct regulator_ops s2mpu02_buck_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, - .enable = s2mps14_regulator_enable, + .enable = s2mps11_regulator_enable, .disable = regulator_disable_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, - .set_suspend_disable = s2mps14_regulator_set_suspend_disable, + .set_suspend_disable = s2mps11_regulator_set_suspend_disable, .set_ramp_delay = s2mpu02_set_ramp_delay, }; diff --git a/include/linux/mfd/samsung/s2mps11.h b/include/linux/mfd/samsung/s2mps11.h index 6e7668a389a1..f6c035eb87be 100644 --- a/include/linux/mfd/samsung/s2mps11.h +++ b/include/linux/mfd/samsung/s2mps11.h @@ -188,4 +188,9 @@ enum s2mps11_regulators { #define S2MPS11_BUCK6_RAMP_EN_SHIFT 0 #define S2MPS11_PMIC_EN_SHIFT 6 +/* + * Bits for "enable suspend" (On/Off controlled by PWREN) + * are the same as in S2MPS14: S2MPS14_ENABLE_SUSPEND + */ + #endif /* __LINUX_MFD_S2MPS11_H */ -- cgit v1.2.3 From d8ca7d184b33af7913c244900df77c6cad6a5590 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 24 Jun 2019 00:08:31 +0300 Subject: regulator: core: Introduce API for regulators coupling customization Right now regulator core supports only one type of regulators coupling, the "voltage max-spread" which keeps voltages of coupled regulators in a given range from each other. A more sophisticated coupling may be required in practice, one example is the NVIDIA Tegra SoCs which besides the max-spreading have other restrictions that must be adhered. Introduce API that allow platforms to provide their own customized coupling algorithms. Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- drivers/regulator/core.c | 136 ++++++++++++++++++++++++++++++++------ drivers/regulator/of_regulator.c | 63 ++++++++++++------ include/linux/regulator/coupler.h | 62 +++++++++++++++++ include/linux/regulator/driver.h | 6 +- include/linux/regulator/machine.h | 2 +- 5 files changed, 225 insertions(+), 44 deletions(-) create mode 100644 include/linux/regulator/coupler.h (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 955a0a15b9cb..12c870f790f5 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_map_list); static LIST_HEAD(regulator_ena_gpio_list); static LIST_HEAD(regulator_supply_alias_list); +static LIST_HEAD(regulator_coupler_list); static bool has_full_constraints; static struct dentry *debugfs_root; @@ -3439,11 +3441,10 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev, struct coupling_desc *c_desc = &rdev->coupling_desc; struct regulator_dev **c_rdevs = c_desc->coupled_rdevs; struct regulation_constraints *constraints = rdev->constraints; - int max_spread = constraints->max_spread; int desired_min_uV = 0, desired_max_uV = INT_MAX; int max_current_uV = 0, min_current_uV = INT_MAX; int highest_min_uV = 0, target_uV, possible_uV; - int i, ret; + int i, ret, max_spread; bool done; *current_uV = -1; @@ -3497,6 +3498,8 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev, } } + max_spread = constraints->max_spread[0]; + /* * Let target_uV be equal to the desired one if possible. * If not, set it to minimum voltage, allowed by other coupled @@ -3578,9 +3581,11 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, struct regulator_dev **c_rdevs; struct regulator_dev *best_rdev; struct coupling_desc *c_desc = &rdev->coupling_desc; + struct regulator_coupler *coupler = c_desc->coupler; int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev; - bool best_c_rdev_done, c_rdev_done[MAX_COUPLED]; unsigned int delta, best_delta; + unsigned long c_rdev_done = 0; + bool best_c_rdev_done; c_rdevs = c_desc->coupled_rdevs; n_coupled = c_desc->n_coupled; @@ -3597,8 +3602,9 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, return -EPERM; } - for (i = 0; i < n_coupled; i++) - c_rdev_done[i] = false; + /* Invoke custom balancer for customized couplers */ + if (coupler && coupler->balance_voltage) + return coupler->balance_voltage(coupler, rdev, state); /* * Find the best possible voltage change on each loop. Leave the loop @@ -3625,7 +3631,7 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, */ int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0; - if (c_rdev_done[i]) + if (test_bit(i, &c_rdev_done)) continue; ret = regulator_get_optimal_voltage(c_rdevs[i], @@ -3660,7 +3666,8 @@ static int regulator_balance_voltage(struct regulator_dev *rdev, if (ret < 0) goto out; - c_rdev_done[best_c_rdev] = best_c_rdev_done; + if (best_c_rdev_done) + set_bit(best_c_rdev, &c_rdev_done); } while (n_coupled > 1); @@ -4712,8 +4719,60 @@ static int regulator_register_resolve_supply(struct device *dev, void *data) return 0; } +int regulator_coupler_register(struct regulator_coupler *coupler) +{ + mutex_lock(®ulator_list_mutex); + list_add_tail(&coupler->list, ®ulator_coupler_list); + mutex_unlock(®ulator_list_mutex); + + return 0; +} + +static struct regulator_coupler * +regulator_find_coupler(struct regulator_dev *rdev) +{ + struct regulator_coupler *coupler; + int err; + + /* + * Note that regulators are appended to the list and the generic + * coupler is registered first, hence it will be attached at last + * if nobody cared. + */ + list_for_each_entry_reverse(coupler, ®ulator_coupler_list, list) { + err = coupler->attach_regulator(coupler, rdev); + if (!err) { + if (!coupler->balance_voltage && + rdev->coupling_desc.n_coupled > 2) + goto err_unsupported; + + return coupler; + } + + if (err < 0) + return ERR_PTR(err); + + if (err == 1) + continue; + + break; + } + + return ERR_PTR(-EINVAL); + +err_unsupported: + if (coupler->detach_regulator) + coupler->detach_regulator(coupler, rdev); + + rdev_err(rdev, + "Voltage balancing for multiple regulator couples is unimplemented\n"); + + return ERR_PTR(-EPERM); +} + static void regulator_resolve_coupling(struct regulator_dev *rdev) { + struct regulator_coupler *coupler = rdev->coupling_desc.coupler; struct coupling_desc *c_desc = &rdev->coupling_desc; int n_coupled = c_desc->n_coupled; struct regulator_dev *c_rdev; @@ -4729,6 +4788,12 @@ static void regulator_resolve_coupling(struct regulator_dev *rdev) if (!c_rdev) continue; + if (c_rdev->coupling_desc.coupler != coupler) { + rdev_err(rdev, "coupler mismatch with %s\n", + rdev_get_name(c_rdev)); + return; + } + regulator_lock(c_rdev); c_desc->coupled_rdevs[i] = c_rdev; @@ -4742,10 +4807,12 @@ static void regulator_resolve_coupling(struct regulator_dev *rdev) static void regulator_remove_coupling(struct regulator_dev *rdev) { + struct regulator_coupler *coupler = rdev->coupling_desc.coupler; struct coupling_desc *__c_desc, *c_desc = &rdev->coupling_desc; struct regulator_dev *__c_rdev, *c_rdev; unsigned int __n_coupled, n_coupled; int i, k; + int err; n_coupled = c_desc->n_coupled; @@ -4775,21 +4842,33 @@ static void regulator_remove_coupling(struct regulator_dev *rdev) c_desc->coupled_rdevs[i] = NULL; c_desc->n_resolved--; } + + if (coupler && coupler->detach_regulator) { + err = coupler->detach_regulator(coupler, rdev); + if (err) + rdev_err(rdev, "failed to detach from coupler: %d\n", + err); + } + + kfree(rdev->coupling_desc.coupled_rdevs); + rdev->coupling_desc.coupled_rdevs = NULL; } static int regulator_init_coupling(struct regulator_dev *rdev) { - int n_phandles; + int err, n_phandles; + size_t alloc_size; if (!IS_ENABLED(CONFIG_OF)) n_phandles = 0; else n_phandles = of_get_n_coupled(rdev); - if (n_phandles + 1 > MAX_COUPLED) { - rdev_err(rdev, "too many regulators coupled\n"); - return -EPERM; - } + alloc_size = sizeof(*rdev) * (n_phandles + 1); + + rdev->coupling_desc.coupled_rdevs = kzalloc(alloc_size, GFP_KERNEL); + if (!rdev->coupling_desc.coupled_rdevs) + return -ENOMEM; /* * Every regulator should always have coupling descriptor filled with @@ -4803,23 +4882,35 @@ static int regulator_init_coupling(struct regulator_dev *rdev) if (n_phandles == 0) return 0; - /* regulator, which can't change its voltage, can't be coupled */ - if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) { - rdev_err(rdev, "voltage operation not allowed\n"); + if (!of_check_coupling_data(rdev)) return -EPERM; - } - if (rdev->constraints->max_spread <= 0) { - rdev_err(rdev, "wrong max_spread value\n"); - return -EPERM; + rdev->coupling_desc.coupler = regulator_find_coupler(rdev); + if (IS_ERR(rdev->coupling_desc.coupler)) { + err = PTR_ERR(rdev->coupling_desc.coupler); + rdev_err(rdev, "failed to get coupler: %d\n", err); + return err; } - if (!of_check_coupling_data(rdev)) + return 0; +} + +static int generic_coupler_attach(struct regulator_coupler *coupler, + struct regulator_dev *rdev) +{ + if (rdev->coupling_desc.n_coupled > 2) { + rdev_err(rdev, + "Voltage balancing for multiple regulator couples is unimplemented\n"); return -EPERM; + } return 0; } +static struct regulator_coupler generic_regulator_coupler = { + .attach_regulator = generic_coupler_attach, +}; + /** * regulator_register - register regulator * @regulator_desc: regulator to register @@ -4981,7 +5072,9 @@ regulator_register(const struct regulator_desc *regulator_desc, if (ret < 0) goto wash; + mutex_lock(®ulator_list_mutex); ret = regulator_init_coupling(rdev); + mutex_unlock(®ulator_list_mutex); if (ret < 0) goto wash; @@ -5030,6 +5123,7 @@ regulator_register(const struct regulator_desc *regulator_desc, unset_supplies: mutex_lock(®ulator_list_mutex); unset_regulator_supplies(rdev); + regulator_remove_coupling(rdev); mutex_unlock(®ulator_list_mutex); wash: kfree(rdev->constraints); @@ -5485,6 +5579,8 @@ static int __init regulator_init(void) #endif regulator_dummy_init(); + regulator_coupler_register(&generic_regulator_coupler); + return ret; } diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 6dca0ba044d8..db1cb2714b92 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -25,7 +25,8 @@ static const char *const regulator_states[PM_SUSPEND_MAX + 1] = { [PM_SUSPEND_MAX] = "regulator-state-disk", }; -static void of_get_regulation_constraints(struct device_node *np, +static int of_get_regulation_constraints(struct device *dev, + struct device_node *np, struct regulator_init_data **init_data, const struct regulator_desc *desc) { @@ -34,8 +35,13 @@ static void of_get_regulation_constraints(struct device_node *np, struct device_node *suspend_np; unsigned int mode; int ret, i, len; + int n_phandles; u32 pval; + n_phandles = of_count_phandle_with_args(np, "regulator-coupled-with", + NULL); + n_phandles = max(n_phandles, 0); + constraints->name = of_get_property(np, "regulator-name", NULL); if (!of_property_read_u32(np, "regulator-min-microvolt", &pval)) @@ -167,9 +173,17 @@ static void of_get_regulation_constraints(struct device_node *np, if (!of_property_read_u32(np, "regulator-system-load", &pval)) constraints->system_load = pval; - if (!of_property_read_u32(np, "regulator-coupled-max-spread", - &pval)) - constraints->max_spread = pval; + if (n_phandles) { + constraints->max_spread = devm_kzalloc(dev, + sizeof(*constraints->max_spread) * n_phandles, + GFP_KERNEL); + + if (!constraints->max_spread) + return -ENOMEM; + + of_property_read_u32_array(np, "regulator-coupled-max-spread", + constraints->max_spread, n_phandles); + } if (!of_property_read_u32(np, "regulator-max-step-microvolt", &pval)) @@ -246,6 +260,8 @@ static void of_get_regulation_constraints(struct device_node *np, suspend_state = NULL; suspend_np = NULL; } + + return 0; } /** @@ -271,7 +287,9 @@ struct regulator_init_data *of_get_regulator_init_data(struct device *dev, if (!init_data) return NULL; /* Out of memory? */ - of_get_regulation_constraints(node, &init_data, desc); + if (of_get_regulation_constraints(dev, node, &init_data, desc)) + return NULL; + return init_data; } EXPORT_SYMBOL_GPL(of_get_regulator_init_data); @@ -477,7 +495,8 @@ int of_get_n_coupled(struct regulator_dev *rdev) /* Looks for "to_find" device_node in src's "regulator-coupled-with" property */ static bool of_coupling_find_node(struct device_node *src, - struct device_node *to_find) + struct device_node *to_find, + int *index) { int n_phandles, i; bool found = false; @@ -499,8 +518,10 @@ static bool of_coupling_find_node(struct device_node *src, of_node_put(tmp); - if (found) + if (found) { + *index = i; break; + } } return found; @@ -521,22 +542,23 @@ static bool of_coupling_find_node(struct device_node *src, */ bool of_check_coupling_data(struct regulator_dev *rdev) { - int max_spread = rdev->constraints->max_spread; struct device_node *node = rdev->dev.of_node; int n_phandles = of_get_n_coupled(rdev); struct device_node *c_node; + int index; int i; bool ret = true; - if (max_spread <= 0) { - dev_err(&rdev->dev, "max_spread value invalid\n"); - return false; - } - /* iterate over rdev's phandles */ for (i = 0; i < n_phandles; i++) { + int max_spread = rdev->constraints->max_spread[i]; int c_max_spread, c_n_phandles; + if (max_spread <= 0) { + dev_err(&rdev->dev, "max_spread value invalid\n"); + return false; + } + c_node = of_parse_phandle(node, "regulator-coupled-with", i); @@ -553,22 +575,23 @@ bool of_check_coupling_data(struct regulator_dev *rdev) goto clean; } - if (of_property_read_u32(c_node, "regulator-coupled-max-spread", - &c_max_spread)) { + if (!of_coupling_find_node(c_node, node, &index)) { + dev_err(&rdev->dev, "missing 2-way linking for coupled regulators\n"); ret = false; goto clean; } - if (c_max_spread != max_spread) { - dev_err(&rdev->dev, - "coupled regulators max_spread mismatch\n"); + if (of_property_read_u32_index(c_node, "regulator-coupled-max-spread", + index, &c_max_spread)) { ret = false; goto clean; } - if (!of_coupling_find_node(c_node, node)) { - dev_err(&rdev->dev, "missing 2-way linking for coupled regulators\n"); + if (c_max_spread != max_spread) { + dev_err(&rdev->dev, + "coupled regulators max_spread mismatch\n"); ret = false; + goto clean; } clean: diff --git a/include/linux/regulator/coupler.h b/include/linux/regulator/coupler.h new file mode 100644 index 000000000000..98dd1f74d605 --- /dev/null +++ b/include/linux/regulator/coupler.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * coupler.h -- SoC Regulator support, coupler API. + * + * Regulator Coupler Interface. + */ + +#ifndef __LINUX_REGULATOR_COUPLER_H_ +#define __LINUX_REGULATOR_COUPLER_H_ + +#include +#include + +struct regulator_coupler; +struct regulator_dev; + +/** + * struct regulator_coupler - customized regulator's coupler + * + * Regulator's coupler allows to customize coupling algorithm. + * + * @list: couplers list entry + * @attach_regulator: Callback invoked on creation of a coupled regulator, + * couples are unresolved at this point. The callee should + * check that it could handle the regulator and return 0 on + * success, -errno on failure and 1 if given regulator is + * not suitable for this coupler (case of having multiple + * regulators in a system). Callback shall be implemented. + * @detach_regulator: Callback invoked on destruction of a coupled regulator. + * This callback is optional and could be NULL. + * @balance_voltage: Callback invoked when voltage of a coupled regulator is + * changing. Called with all of the coupled rdev's being held + * under "consumer lock". The callee should perform voltage + * balancing, changing voltage of the coupled regulators as + * needed. It's up to the coupler to verify the voltage + * before changing it in hardware, i.e. coupler should + * check consumer's min/max and etc. This callback is + * optional and could be NULL, in which case a generic + * voltage balancer will be used. + */ +struct regulator_coupler { + struct list_head list; + + int (*attach_regulator)(struct regulator_coupler *coupler, + struct regulator_dev *rdev); + int (*detach_regulator)(struct regulator_coupler *coupler, + struct regulator_dev *rdev); + int (*balance_voltage)(struct regulator_coupler *coupler, + struct regulator_dev *rdev, + suspend_state_t state); +}; + +#ifdef CONFIG_REGULATOR +int regulator_coupler_register(struct regulator_coupler *coupler); +#else +static inline int regulator_coupler_register(struct regulator_coupler *coupler) +{ + return 0; +} +#endif + +#endif diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 377da2357118..31b38a2b6995 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -15,8 +15,6 @@ #ifndef __LINUX_REGULATOR_DRIVER_H_ #define __LINUX_REGULATOR_DRIVER_H_ -#define MAX_COUPLED 2 - #include #include #include @@ -426,7 +424,8 @@ struct regulator_config { * incremented. */ struct coupling_desc { - struct regulator_dev *coupled_rdevs[MAX_COUPLED]; + struct regulator_dev **coupled_rdevs; + struct regulator_coupler *coupler; int n_resolved; int n_coupled; }; @@ -552,4 +551,5 @@ void regulator_unlock(struct regulator_dev *rdev); */ int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc, unsigned int selector); + #endif diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 1d34a70ffda2..21db06e5c1ed 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -156,7 +156,7 @@ struct regulation_constraints { int system_load; /* used for coupled regulators */ - int max_spread; + u32 *max_spread; /* used for changing voltage in steps */ int max_uV_step; -- cgit v1.2.3 From d22b85a1b97d12a4940ef9d778f6122546736f78 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 24 Jun 2019 00:08:32 +0300 Subject: regulator: core: Expose some of core functions needed by couplers Expose some of internal functions that are required for implementation of customized regulator couplers. Signed-off-by: Dmitry Osipenko Signed-off-by: Mark Brown --- drivers/regulator/core.c | 58 ++++++++++++++++++--------------------- include/linux/regulator/coupler.h | 35 +++++++++++++++++++++++ 2 files changed, 62 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 12c870f790f5..b9bc45128b8c 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -100,7 +100,6 @@ struct regulator_supply_alias { static int _regulator_is_enabled(struct regulator_dev *rdev); static int _regulator_disable(struct regulator *regulator); -static int _regulator_get_voltage(struct regulator_dev *rdev); static int _regulator_get_current_limit(struct regulator_dev *rdev); static unsigned int _regulator_get_mode(struct regulator_dev *rdev); static int _notifier_call_chain(struct regulator_dev *rdev, @@ -109,15 +108,12 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV); static int regulator_balance_voltage(struct regulator_dev *rdev, suspend_state_t state); -static int regulator_set_voltage_rdev(struct regulator_dev *rdev, - int min_uV, int max_uV, - suspend_state_t state); static struct regulator *create_regulator(struct regulator_dev *rdev, struct device *dev, const char *supply_name); static void _regulator_put(struct regulator *regulator); -static const char *rdev_get_name(struct regulator_dev *rdev) +const char *rdev_get_name(struct regulator_dev *rdev) { if (rdev->constraints && rdev->constraints->name) return rdev->constraints->name; @@ -431,8 +427,8 @@ static struct device_node *of_get_regulator(struct device *dev, const char *supp } /* Platform voltage constraint check */ -static int regulator_check_voltage(struct regulator_dev *rdev, - int *min_uV, int *max_uV) +int regulator_check_voltage(struct regulator_dev *rdev, + int *min_uV, int *max_uV) { BUG_ON(*min_uV > *max_uV); @@ -464,9 +460,9 @@ static int regulator_check_states(suspend_state_t state) /* Make sure we select a voltage that suits the needs of all * regulator consumers */ -static int regulator_check_consumers(struct regulator_dev *rdev, - int *min_uV, int *max_uV, - suspend_state_t state) +int regulator_check_consumers(struct regulator_dev *rdev, + int *min_uV, int *max_uV, + suspend_state_t state) { struct regulator *regulator; struct regulator_voltage *voltage; @@ -577,7 +573,7 @@ static ssize_t regulator_uV_show(struct device *dev, ssize_t ret; regulator_lock(rdev); - ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev)); + ret = sprintf(buf, "%d\n", regulator_get_voltage_rdev(rdev)); regulator_unlock(rdev); return ret; @@ -948,7 +944,7 @@ static int drms_uA_update(struct regulator_dev *rdev) rdev_err(rdev, "failed to set load %d\n", current_uA); } else { /* get output voltage */ - output_uV = _regulator_get_voltage(rdev); + output_uV = regulator_get_voltage_rdev(rdev); if (output_uV <= 0) { rdev_err(rdev, "invalid output voltage found\n"); return -EINVAL; @@ -1061,7 +1057,7 @@ static void print_constraints(struct regulator_dev *rdev) if (!constraints->min_uV || constraints->min_uV != constraints->max_uV) { - ret = _regulator_get_voltage(rdev); + ret = regulator_get_voltage_rdev(rdev); if (ret > 0) count += scnprintf(buf + count, len - count, "at %d mV ", ret / 1000); @@ -1120,7 +1116,7 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, if (rdev->constraints->apply_uV && rdev->constraints->min_uV && rdev->constraints->max_uV) { int target_min, target_max; - int current_uV = _regulator_get_voltage(rdev); + int current_uV = regulator_get_voltage_rdev(rdev); if (current_uV == -ENOTRECOVERABLE) { /* This regulator can't be read and must be initialized */ @@ -1130,7 +1126,7 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, _regulator_do_set_voltage(rdev, rdev->constraints->min_uV, rdev->constraints->max_uV); - current_uV = _regulator_get_voltage(rdev); + current_uV = regulator_get_voltage_rdev(rdev); } if (current_uV < 0) { @@ -3072,7 +3068,7 @@ static int _regulator_call_set_voltage(struct regulator_dev *rdev, struct pre_voltage_change_data data; int ret; - data.old_uV = _regulator_get_voltage(rdev); + data.old_uV = regulator_get_voltage_rdev(rdev); data.min_uV = min_uV; data.max_uV = max_uV; ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_VOLTAGE_CHANGE, @@ -3096,7 +3092,7 @@ static int _regulator_call_set_voltage_sel(struct regulator_dev *rdev, struct pre_voltage_change_data data; int ret; - data.old_uV = _regulator_get_voltage(rdev); + data.old_uV = regulator_get_voltage_rdev(rdev); data.min_uV = uV; data.max_uV = uV; ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_VOLTAGE_CHANGE, @@ -3149,7 +3145,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, unsigned int selector; int old_selector = -1; const struct regulator_ops *ops = rdev->desc->ops; - int old_uV = _regulator_get_voltage(rdev); + int old_uV = regulator_get_voltage_rdev(rdev); trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV); @@ -3176,7 +3172,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, best_val = ops->list_voltage(rdev, selector); else - best_val = _regulator_get_voltage(rdev); + best_val = regulator_get_voltage_rdev(rdev); } } else if (ops->set_voltage_sel) { @@ -3295,7 +3291,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, * changing the voltage. */ if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) { - current_uV = _regulator_get_voltage(rdev); + current_uV = regulator_get_voltage_rdev(rdev); if (min_uV <= current_uV && current_uV <= max_uV) { voltage->min_uV = min_uV; voltage->max_uV = max_uV; @@ -3332,8 +3328,8 @@ out: return ret; } -static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, - int max_uV, suspend_state_t state) +int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, + int max_uV, suspend_state_t state) { int best_supply_uV = 0; int supply_change_uV = 0; @@ -3361,7 +3357,7 @@ static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV, best_supply_uV += rdev->desc->min_dropout_uV; - current_supply_uV = _regulator_get_voltage(rdev->supply->rdev); + current_supply_uV = regulator_get_voltage_rdev(rdev->supply->rdev); if (current_supply_uV < 0) { ret = current_supply_uV; goto out; @@ -3412,7 +3408,7 @@ static int regulator_limit_voltage_step(struct regulator_dev *rdev, return 1; if (*current_uV < 0) { - *current_uV = _regulator_get_voltage(rdev); + *current_uV = regulator_get_voltage_rdev(rdev); if (*current_uV < 0) return *current_uV; @@ -3517,7 +3513,7 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev, if (!_regulator_is_enabled(c_rdevs[i])) continue; - tmp_act = _regulator_get_voltage(c_rdevs[i]); + tmp_act = regulator_get_voltage_rdev(c_rdevs[i]); if (tmp_act < 0) return tmp_act; @@ -3559,7 +3555,7 @@ finish: if (n_coupled > 1 && *current_uV == -1) { if (_regulator_is_enabled(rdev)) { - ret = _regulator_get_voltage(rdev); + ret = regulator_get_voltage_rdev(rdev); if (ret < 0) return ret; @@ -3923,7 +3919,7 @@ out: } EXPORT_SYMBOL_GPL(regulator_sync_voltage); -static int _regulator_get_voltage(struct regulator_dev *rdev) +int regulator_get_voltage_rdev(struct regulator_dev *rdev) { int sel, ret; bool bypassed; @@ -3940,7 +3936,7 @@ static int _regulator_get_voltage(struct regulator_dev *rdev) return -EPROBE_DEFER; } - return _regulator_get_voltage(rdev->supply->rdev); + return regulator_get_voltage_rdev(rdev->supply->rdev); } } @@ -3956,7 +3952,7 @@ static int _regulator_get_voltage(struct regulator_dev *rdev) } else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) { ret = rdev->desc->fixed_uV; } else if (rdev->supply) { - ret = _regulator_get_voltage(rdev->supply->rdev); + ret = regulator_get_voltage_rdev(rdev->supply->rdev); } else { return -EINVAL; } @@ -3981,7 +3977,7 @@ int regulator_get_voltage(struct regulator *regulator) int ret; regulator_lock_dependent(regulator->rdev, &ww_ctx); - ret = _regulator_get_voltage(regulator->rdev); + ret = regulator_get_voltage_rdev(regulator->rdev); regulator_unlock_dependent(regulator->rdev, &ww_ctx); return ret; @@ -5377,7 +5373,7 @@ static void regulator_summary_show_subtree(struct seq_file *s, rdev->use_count, rdev->open_count, rdev->bypass_count, regulator_opmode_to_str(opmode)); - seq_printf(s, "%5dmV ", _regulator_get_voltage(rdev) / 1000); + seq_printf(s, "%5dmV ", regulator_get_voltage_rdev(rdev) / 1000); seq_printf(s, "%5dmA ", _regulator_get_current_limit_unlocked(rdev) / 1000); diff --git a/include/linux/regulator/coupler.h b/include/linux/regulator/coupler.h index 98dd1f74d605..0212d6255e4e 100644 --- a/include/linux/regulator/coupler.h +++ b/include/linux/regulator/coupler.h @@ -52,11 +52,46 @@ struct regulator_coupler { #ifdef CONFIG_REGULATOR int regulator_coupler_register(struct regulator_coupler *coupler); +const char *rdev_get_name(struct regulator_dev *rdev); +int regulator_check_consumers(struct regulator_dev *rdev, + int *min_uV, int *max_uV, + suspend_state_t state); +int regulator_check_voltage(struct regulator_dev *rdev, + int *min_uV, int *max_uV); +int regulator_get_voltage_rdev(struct regulator_dev *rdev); +int regulator_set_voltage_rdev(struct regulator_dev *rdev, + int min_uV, int max_uV, + suspend_state_t state); #else static inline int regulator_coupler_register(struct regulator_coupler *coupler) { return 0; } +static inline const char *rdev_get_name(struct regulator_dev *rdev) +{ + return NULL; +} +static inline int regulator_check_consumers(struct regulator_dev *rdev, + int *min_uV, int *max_uV, + suspend_state_t state) +{ + return -EINVAL; +} +static inline int regulator_check_voltage(struct regulator_dev *rdev, + int *min_uV, int *max_uV) +{ + return -EINVAL; +} +static inline int regulator_get_voltage_rdev(struct regulator_dev *rdev) +{ + return -EINVAL; +} +static inline int regulator_set_voltage_rdev(struct regulator_dev *rdev, + int min_uV, int max_uV, + suspend_state_t state) +{ + return -EINVAL; +} #endif #endif -- cgit v1.2.3 From 9d83dcb3e4553f34ee1c4f09d65173159f9eb7a7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 29 Jun 2019 13:44:46 +0200 Subject: regulator: s2mps11: Adjust supported buck voltages to real values The driver was registering buck regulators with unsupported range of voltages for S2MPS11 devices. Basically it assumed that all 256 values are possible for a single 8-bit I2C register controlling buck's voltage. This is not true, as datasheet describes subset of these which can be used. For example for buck[12346] the minimum voltage is 650 mV which corresponds to register value of 0x8. The driver was however registering regulator starting at 600 mV, so for a step of 6.25 mV this gave the same result. However this allowed to try to configure regulators to unsupported values. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 27 +++++++++++++++++---------- include/linux/mfd/samsung/core.h | 1 + include/linux/mfd/samsung/s2mps11.h | 4 +++- 3 files changed, 21 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 7a89030187a4..5b7ba7c6c4f6 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -269,9 +269,10 @@ static const struct regulator_ops s2mps11_buck_ops = { .ops = &s2mps11_buck_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ - .min_uV = MIN_600_MV, \ + .min_uV = MIN_650_MV, \ .uV_step = STEP_6_25_MV, \ - .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .linear_min_sel = 8, \ + .n_voltages = S2MPS11_BUCK12346_N_VOLTAGES, \ .ramp_delay = S2MPS11_RAMP_DELAY, \ .vsel_reg = S2MPS11_REG_B1CTRL2 + (num - 1) * 2, \ .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ @@ -285,9 +286,10 @@ static const struct regulator_ops s2mps11_buck_ops = { .ops = &s2mps11_buck_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ - .min_uV = MIN_600_MV, \ + .min_uV = MIN_650_MV, \ .uV_step = STEP_6_25_MV, \ - .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .linear_min_sel = 8, \ + .n_voltages = S2MPS11_BUCK5_N_VOLTAGES, \ .ramp_delay = S2MPS11_RAMP_DELAY, \ .vsel_reg = S2MPS11_REG_B5CTRL2, \ .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ @@ -295,7 +297,7 @@ static const struct regulator_ops s2mps11_buck_ops = { .enable_mask = S2MPS11_ENABLE_MASK \ } -#define regulator_desc_s2mps11_buck67810(num, min, step) { \ +#define regulator_desc_s2mps11_buck67810(num, min, step, min_sel, voltages) { \ .name = "BUCK"#num, \ .id = S2MPS11_BUCK##num, \ .ops = &s2mps11_buck_ops, \ @@ -303,7 +305,8 @@ static const struct regulator_ops s2mps11_buck_ops = { .owner = THIS_MODULE, \ .min_uV = min, \ .uV_step = step, \ - .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ + .linear_min_sel = min_sel, \ + .n_voltages = voltages, \ .ramp_delay = S2MPS11_RAMP_DELAY, \ .vsel_reg = S2MPS11_REG_B6CTRL2 + (num - 6) * 2, \ .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ @@ -371,11 +374,15 @@ static const struct regulator_desc s2mps11_regulators[] = { regulator_desc_s2mps11_buck1_4(3), regulator_desc_s2mps11_buck1_4(4), regulator_desc_s2mps11_buck5, - regulator_desc_s2mps11_buck67810(6, MIN_600_MV, STEP_6_25_MV), - regulator_desc_s2mps11_buck67810(7, MIN_750_MV, STEP_12_5_MV), - regulator_desc_s2mps11_buck67810(8, MIN_750_MV, STEP_12_5_MV), + regulator_desc_s2mps11_buck67810(6, MIN_650_MV, STEP_6_25_MV, 8, + S2MPS11_BUCK12346_N_VOLTAGES), + regulator_desc_s2mps11_buck67810(7, MIN_750_MV, STEP_12_5_MV, 0, + S2MPS11_BUCK7810_N_VOLTAGES), + regulator_desc_s2mps11_buck67810(8, MIN_750_MV, STEP_12_5_MV, 0, + S2MPS11_BUCK7810_N_VOLTAGES), regulator_desc_s2mps11_buck9, - regulator_desc_s2mps11_buck67810(10, MIN_750_MV, STEP_12_5_MV), + regulator_desc_s2mps11_buck67810(10, MIN_750_MV, STEP_12_5_MV, 0, + S2MPS11_BUCK7810_N_VOLTAGES), }; static const struct regulator_ops s2mps14_reg_ops; diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index 3ca17eb89aa2..f1631a39acfc 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h @@ -20,6 +20,7 @@ #define MIN_850_MV 850000 #define MIN_800_MV 800000 #define MIN_750_MV 750000 +#define MIN_650_MV 650000 #define MIN_600_MV 600000 #define MIN_500_MV 500000 diff --git a/include/linux/mfd/samsung/s2mps11.h b/include/linux/mfd/samsung/s2mps11.h index 6e7668a389a1..fc67c9e75bba 100644 --- a/include/linux/mfd/samsung/s2mps11.h +++ b/include/linux/mfd/samsung/s2mps11.h @@ -170,7 +170,9 @@ enum s2mps11_regulators { #define S2MPS11_ENABLE_MASK (0x03 << S2MPS11_ENABLE_SHIFT) #define S2MPS11_ENABLE_SHIFT 0x06 #define S2MPS11_LDO_N_VOLTAGES (S2MPS11_LDO_VSEL_MASK + 1) -#define S2MPS11_BUCK_N_VOLTAGES (S2MPS11_BUCK_VSEL_MASK + 1) +#define S2MPS11_BUCK12346_N_VOLTAGES 153 +#define S2MPS11_BUCK5_N_VOLTAGES 216 +#define S2MPS11_BUCK7810_N_VOLTAGES 225 #define S2MPS11_BUCK9_N_VOLTAGES (S2MPS11_BUCK9_VSEL_MASK + 1) #define S2MPS11_RAMP_DELAY 25000 /* uV/us */ -- cgit v1.2.3 From 2da8d9473e20a2f6645dcb0cea4848a2c1e83af9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 3 Jul 2019 18:10:34 +0200 Subject: regulator: implement selector stepping Some regulators require that the requested voltage be reached gradually by setting all or some of the intermediate values. Implement a new field in the regulator description struct that allows users to specify the number of selectors by which the regulator API should step when ramping the voltage up/down. Signed-off-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/20190703161035.31808-2-brgl@bgdev.pl Signed-off-by: Mark Brown --- drivers/regulator/core.c | 63 ++++++++++++++++++++++++++++++++++++++++ include/linux/regulator/driver.h | 6 ++++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 9d3ed13b7f12..df82e2a8442a 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3106,6 +3106,66 @@ static int _regulator_call_set_voltage_sel(struct regulator_dev *rdev, return ret; } +static int _regulator_set_voltage_sel_step(struct regulator_dev *rdev, + int uV, int new_selector) +{ + const struct regulator_ops *ops = rdev->desc->ops; + int diff, old_sel, curr_sel, ret; + + /* Stepping is only needed if the regulator is enabled. */ + if (!_regulator_is_enabled(rdev)) + goto final_set; + + if (!ops->get_voltage_sel) + return -EINVAL; + + old_sel = ops->get_voltage_sel(rdev); + if (old_sel < 0) + return old_sel; + + diff = new_selector - old_sel; + if (diff == 0) + return 0; /* No change needed. */ + + if (diff > 0) { + /* Stepping up. */ + for (curr_sel = old_sel + rdev->desc->vsel_step; + curr_sel < new_selector; + curr_sel += rdev->desc->vsel_step) { + /* + * Call the callback directly instead of using + * _regulator_call_set_voltage_sel() as we don't + * want to notify anyone yet. Same in the branch + * below. + */ + ret = ops->set_voltage_sel(rdev, curr_sel); + if (ret) + goto try_revert; + } + } else { + /* Stepping down. */ + for (curr_sel = old_sel - rdev->desc->vsel_step; + curr_sel > new_selector; + curr_sel -= rdev->desc->vsel_step) { + ret = ops->set_voltage_sel(rdev, curr_sel); + if (ret) + goto try_revert; + } + } + +final_set: + /* The final selector will trigger the notifiers. */ + return _regulator_call_set_voltage_sel(rdev, uV, new_selector); + +try_revert: + /* + * At least try to return to the previous voltage if setting a new + * one failed. + */ + (void)ops->set_voltage_sel(rdev, old_sel); + return ret; +} + static int _regulator_set_voltage_time(struct regulator_dev *rdev, int old_uV, int new_uV) { @@ -3179,6 +3239,9 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, selector = ret; if (old_selector == selector) ret = 0; + else if (rdev->desc->vsel_step) + ret = _regulator_set_voltage_sel_step( + rdev, best_val, selector); else ret = _regulator_call_set_voltage_sel( rdev, best_val, selector); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 377da2357118..f0d7b0496e54 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -286,6 +286,11 @@ enum regulator_type { * @vsel_range_mask: Mask for register bitfield used for range selector * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ * @vsel_mask: Mask for register bitfield used for selector + * @vsel_step: Specify the resolution of selector stepping when setting + * voltage. If 0, then no stepping is done (requested selector is + * set directly), if >0 then the regulator API will ramp the + * voltage up/down gradually each time increasing/decreasing the + * selector by the specified step value. * @csel_reg: Register for current limit selector using regmap set_current_limit * @csel_mask: Mask for register bitfield used for current limit selector * @apply_reg: Register for initiate voltage change on the output when @@ -360,6 +365,7 @@ struct regulator_desc { unsigned int vsel_range_mask; unsigned int vsel_reg; unsigned int vsel_mask; + unsigned int vsel_step; unsigned int csel_reg; unsigned int csel_mask; unsigned int apply_reg; -- cgit v1.2.3