From 13bed58ce8748d430a26e353a09b89f9d613a71f Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Thu, 5 May 2016 13:32:57 +0800 Subject: [PATCH 01/24] regulator: fixed: add support for ACPI interface Add support to retrieve fixed voltage configure information through ACPI interface. This is needed for Intel Bay Trail devices, where a GPIO is used to control the USB vbus. Signed-off-by: Lu Baolu Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 988a7472c2ab..a43b0e8a438d 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -30,6 +30,9 @@ #include #include #include +#include +#include +#include struct fixed_voltage_data { struct regulator_desc desc; @@ -94,6 +97,44 @@ of_get_fixed_voltage_config(struct device *dev, return config; } +/** + * acpi_get_fixed_voltage_config - extract fixed_voltage_config structure info + * @dev: device requesting for fixed_voltage_config + * @desc: regulator description + * + * Populates fixed_voltage_config structure by extracting data through ACPI + * interface, returns a pointer to the populated structure of NULL if memory + * alloc fails. + */ +static struct fixed_voltage_config * +acpi_get_fixed_voltage_config(struct device *dev, + const struct regulator_desc *desc) +{ + struct fixed_voltage_config *config; + const char *supply_name; + struct gpio_desc *gpiod; + int ret; + + config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL); + if (!config) + return ERR_PTR(-ENOMEM); + + ret = device_property_read_string(dev, "supply-name", &supply_name); + if (!ret) + config->supply_name = supply_name; + + gpiod = gpiod_get(dev, "gpio", GPIOD_ASIS); + if (IS_ERR(gpiod)) + return ERR_PTR(-ENODEV); + + config->gpio = desc_to_gpio(gpiod); + config->enable_high = device_property_read_bool(dev, + "enable-active-high"); + gpiod_put(gpiod); + + return config; +} + static struct regulator_ops fixed_voltage_ops = { }; @@ -114,6 +155,11 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) &drvdata->desc); if (IS_ERR(config)) return PTR_ERR(config); + } else if (ACPI_HANDLE(&pdev->dev)) { + config = acpi_get_fixed_voltage_config(&pdev->dev, + &drvdata->desc); + if (IS_ERR(config)) + return PTR_ERR(config); } else { config = dev_get_platdata(&pdev->dev); } From 88f0e3a5e6ed674f485d3a09d53c2ead0000378d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 21 Oct 2016 14:47:42 +0100 Subject: [PATCH 02/24] regulator: arizona-ldo1: Set appropriate value for ramp_delay Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/arizona-ldo1.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index f7c88ff90c43..302b57cb89c6 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -130,6 +130,7 @@ static const struct regulator_desc arizona_ldo1_hc = { .uV_step = 50000, .n_voltages = 8, .enable_time = 1500, + .ramp_delay = 24000, .owner = THIS_MODULE, }; @@ -153,6 +154,7 @@ static const struct regulator_desc arizona_ldo1 = { .uV_step = 25000, .n_voltages = 13, .enable_time = 500, + .ramp_delay = 24000, .owner = THIS_MODULE, }; From e98d5fef8556f1da7f903fd1908feed84bb0f90d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 21 Oct 2016 16:40:05 +0800 Subject: [PATCH 03/24] regulator: tps6507x: Drop pointless static qualifier for *reg_data variable There is no need to use static for this local variable. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps6507x-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index dad0bac09ecf..c179a3a221af 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -375,7 +375,7 @@ static struct tps6507x_board *tps6507x_parse_dt_reg_data( struct device_node *np = pdev->dev.parent->of_node; struct device_node *regulators; struct of_regulator_match *matches; - static struct regulator_init_data *reg_data; + struct regulator_init_data *reg_data; int idx = 0, count, ret; tps_board = devm_kzalloc(&pdev->dev, sizeof(*tps_board), From 1b5b42216469b05ef4b5916cb40b127dfab1da88 Mon Sep 17 00:00:00 2001 From: Axel Haslam Date: Thu, 3 Nov 2016 12:11:42 +0100 Subject: [PATCH 04/24] regulator: core: Add new API to poll for error conditions Regulator consumers can receive event notifications when errors are reported to the driver, but currently, there is no way for a regulator consumer to know when the error is over. To allow a regulator consumer to poll for error conditions add a new API: regulator_get_error_flags. Signed-off-by: Axel Haslam Signed-off-by: Mark Brown --- drivers/regulator/core.c | 33 ++++++++++++++++++++++++++++++ include/linux/regulator/consumer.h | 26 +++++++++++++++++++++++ include/linux/regulator/driver.h | 4 ++++ 3 files changed, 63 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 67426c0477d3..08260c215895 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3359,6 +3359,39 @@ unsigned int regulator_get_mode(struct regulator *regulator) } EXPORT_SYMBOL_GPL(regulator_get_mode); +static int _regulator_get_error_flags(struct regulator_dev *rdev, + unsigned int *flags) +{ + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->get_error_flags) { + ret = -EINVAL; + goto out; + } + + ret = rdev->desc->ops->get_error_flags(rdev, flags); +out: + mutex_unlock(&rdev->mutex); + return ret; +} + +/** + * regulator_get_error_flags - get regulator error information + * @regulator: regulator source + * @flags: pointer to store error flags + * + * Get the current regulator error information. + */ +int regulator_get_error_flags(struct regulator *regulator, + unsigned int *flags) +{ + return _regulator_get_error_flags(regulator->rdev, flags); +} +EXPORT_SYMBOL_GPL(regulator_get_error_flags); + /** * regulator_set_load - set regulator load * @regulator: regulator source diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 692108222271..528eb1f5273e 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -120,6 +120,25 @@ struct regmap; #define REGULATOR_EVENT_PRE_DISABLE 0x400 #define REGULATOR_EVENT_ABORT_DISABLE 0x800 +/* + * Regulator errors that can be queried using regulator_get_error_flags + * + * UNDER_VOLTAGE Regulator output is under voltage. + * OVER_CURRENT Regulator output current is too high. + * REGULATION_OUT Regulator output is out of regulation. + * FAIL Regulator output has failed. + * OVER_TEMP Regulator over temp. + * + * NOTE: These errors can be OR'ed together. + */ + +#define REGULATOR_ERROR_UNDER_VOLTAGE BIT(1) +#define REGULATOR_ERROR_OVER_CURRENT BIT(2) +#define REGULATOR_ERROR_REGULATION_OUT BIT(3) +#define REGULATOR_ERROR_FAIL BIT(4) +#define REGULATOR_ERROR_OVER_TEMP BIT(5) + + /** * struct pre_voltage_change_data - Data sent with PRE_VOLTAGE_CHANGE event * @@ -237,6 +256,8 @@ int regulator_get_current_limit(struct regulator *regulator); int regulator_set_mode(struct regulator *regulator, unsigned int mode); unsigned int regulator_get_mode(struct regulator *regulator); +int regulator_get_error_flags(struct regulator *regulator, + unsigned int *flags); int regulator_set_load(struct regulator *regulator, int load_uA); int regulator_allow_bypass(struct regulator *regulator, bool allow); @@ -477,6 +498,11 @@ static inline unsigned int regulator_get_mode(struct regulator *regulator) return REGULATOR_MODE_NORMAL; } +static inline int regulator_get_error_flags(struct regulator *regulator) +{ + return -EINVAL; +} + static inline int regulator_set_load(struct regulator *regulator, int load_uA) { return REGULATOR_MODE_NORMAL; diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 37b532410528..dac8e7b16bc6 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -100,6 +100,7 @@ struct regulator_linear_range { * * @set_mode: Set the configured operating mode for the regulator. * @get_mode: Get the configured operating mode for the regulator. + * @get_error_flags: Get the current error(s) for the regulator. * @get_status: Return actual (not as-configured) status of regulator, as a * REGULATOR_STATUS value (or negative errno) * @get_optimum_mode: Get the most efficient operating mode for the regulator @@ -169,6 +170,9 @@ struct regulator_ops { int (*set_mode) (struct regulator_dev *, unsigned int mode); unsigned int (*get_mode) (struct regulator_dev *); + /* retrieve current error flags on the regulator */ + int (*get_error_flags)(struct regulator_dev *, unsigned int *flags); + /* Time taken to enable or set voltage on the regulator */ int (*enable_time) (struct regulator_dev *); int (*set_ramp_delay) (struct regulator_dev *, int ramp_delay); From 85b037442e3f0e84296ab1010fd6b057eee18496 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 10 Nov 2016 10:45:18 +0000 Subject: [PATCH 05/24] regulators: helpers: Fix handling of bypass_val_on in get_bypass_regmap The handling of bypass_val_on that was added in regulator_get_bypass_regmap is done unconditionally however several drivers don't define a value for bypass_val_on. This results in those drivers reporting bypass being enabled when it is not. In regulator_set_bypass_regmap we use bypass_mask if bypass_val_on is zero. This patch adds similar handling in regulator_get_bypass_regmap. Fixes: commit dd1a571daee7 ("regulator: helpers: Ensure bypass register field matches ON value") Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/helpers.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index bcf38fd5106a..379cdacc05d8 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -454,13 +454,17 @@ EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap); int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable) { unsigned int val; + unsigned int val_on = rdev->desc->bypass_val_on; int ret; ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val); if (ret != 0) return ret; - *enable = (val & rdev->desc->bypass_mask) == rdev->desc->bypass_val_on; + if (!val_on) + val_on = rdev->desc->bypass_mask; + + *enable = (val & rdev->desc->bypass_mask) == val_on; return 0; } From 618c808968852609d2d9f0e5cfc351a4807ef8d0 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 11 Nov 2016 11:12:43 +0800 Subject: [PATCH 06/24] regulator: axp20x: Fix axp809 ldo_io registration error on cold boot The maximum supported voltage for ldo_io# is 3.3V, but on cold boot the selector comes up at 0x1f, which maps to 3.8V. This was previously corrected by Allwinner's U-boot, which set all regulators on the PMICs to some pre-configured voltage. With recent progress in U-boot SPL support, this is no longer the case. In any case we should handle this quirk in the kernel driver as well. This invalid setting causes _regulator_get_voltage() to fail with -EINVAL which causes regulator registration to fail when constrains are used: [ 1.054181] vcc-pg: failed to get the current voltage(-22) [ 1.059670] axp20x-regulator axp20x-regulator.0: Failed to register ldo_io0 [ 1.069749] axp20x-regulator: probe of axp20x-regulator.0 failed with error -22 This commits makes the axp20x regulator driver accept the 0x1f register value, fixing this. The datasheet does not guarantee reliable operation above 3.3V, so on boards where this regulator is used the regulator-max-microvolt setting must be 3.3V or less. This is essentially the same as the commit f40d4896bf32 ("regulator: axp20x: Fix axp22x ldo_io registration error on cold boot") for AXP22x PMICs. Fixes: a51f9f4622a3 ("regulator: axp20x: support AXP809 variant") Signed-off-by: Chen-Yu Tsai Signed-off-by: Mark Brown --- drivers/regulator/axp20x-regulator.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index 54382ef902c6..e6a512ebeae2 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -337,10 +337,18 @@ static const struct regulator_desc axp809_regulators[] = { AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)), AXP_DESC(AXP809, ELDO3, "eldo3", "eldoin", 700, 3300, 100, AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)), - AXP_DESC_IO(AXP809, LDO_IO0, "ldo_io0", "ips", 700, 3300, 100, + /* + * Note the datasheet only guarantees reliable operation up to + * 3.3V, this needs to be enforced via dts provided constraints + */ + AXP_DESC_IO(AXP809, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100, AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), - AXP_DESC_IO(AXP809, LDO_IO1, "ldo_io1", "ips", 700, 3300, 100, + /* + * Note the datasheet only guarantees reliable operation up to + * 3.3V, this needs to be enforced via dts provided constraints + */ + AXP_DESC_IO(AXP809, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100, AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07, AXP22X_IO_ENABLED, AXP22X_IO_DISABLED), AXP_DESC_FIXED(AXP809, RTC_LDO, "rtc_ldo", "ips", 1800), From 205321f0927ad2303e7f71767d402e0ff36a9a87 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 10 Nov 2016 10:59:15 +0530 Subject: [PATCH 07/24] regulator: lp873x: Add support for populating input supply In order to have a proper topology of regulators for a platform, each registering regulator needs to populate supply_name field for identifying its supply's name. Add supply_name field for lp873x regulators. Acked-by: Rob Herring Signed-off-by: Lokesh Vutla Acked-for-MFD-by: Lee Jones Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/lp873x.txt | 8 ++++++++ drivers/regulator/lp873x-regulator.c | 1 + 2 files changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/lp873x.txt b/Documentation/devicetree/bindings/mfd/lp873x.txt index 52766c2035f7..ae9cf39bd101 100644 --- a/Documentation/devicetree/bindings/mfd/lp873x.txt +++ b/Documentation/devicetree/bindings/mfd/lp873x.txt @@ -7,6 +7,9 @@ Required properties: - #gpio-cells: Should be two. The first cell is the pin number and the second cell is used to specify flags. See ../gpio/gpio.txt for more information. + - xxx-in-supply: Phandle to parent supply node of each regulator + populated under regulators node. xxx can be + buck0, buck1, ldo0 or ldo1. - regulators: List of child nodes that specify the regulator initialization data. Example: @@ -17,6 +20,11 @@ pmic: lp8733@60 { gpio-controller; #gpio-cells = <2>; + buck0-in-supply = <&vsys_3v3>; + buck1-in-supply = <&vsys_3v3>; + ldo0-in-supply = <&vsys_3v3>; + ldo1-in-supply = <&vsys_3v3>; + regulators { lp8733_buck0: buck0 { regulator-name = "lp8733-buck0"; diff --git a/drivers/regulator/lp873x-regulator.c b/drivers/regulator/lp873x-regulator.c index e504b9148226..70e3df653381 100644 --- a/drivers/regulator/lp873x-regulator.c +++ b/drivers/regulator/lp873x-regulator.c @@ -24,6 +24,7 @@ [_id] = { \ .desc = { \ .name = _name, \ + .supply_name = _of "-in", \ .id = _id, \ .of_match = of_match_ptr(_of), \ .regulators_node = of_match_ptr("regulators"),\ From 09f2ba0b0b7c44ecea49cf69a708203b76ba5535 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Thu, 10 Nov 2016 17:21:29 +0800 Subject: [PATCH 08/24] regulator: gpio: properly check return value of of_get_named_gpio The function of_get_named_gpio() could return -ENOENT, -EPROBE_DEFER -EINVAL and so on. Currently, for the optional property "enable-gpio", we only check -EPROBE_DEFER, this is not enough since there may be misconfigured "enable-gpio" in the DTB, of_get_named_gpio() will return -EINVAL in this case, we should return immediately here. And for the optional property "gpios", we didn't check the return value, the driver will continue to the point where gpio_request_array() is called, it doesn't make sense to continue if we got -EPROBE_DEFER or -EINVAL here. This patch tries to address these two issues by properly checking the return value of of_get_named_gpio. Signed-off-by: Jisheng Zhang Signed-off-by: Mark Brown --- drivers/regulator/gpio-regulator.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 83e89e5d4752..0fce06acfaec 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -162,8 +162,8 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np, of_property_read_u32(np, "startup-delay-us", &config->startup_delay); config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); - if (config->enable_gpio == -EPROBE_DEFER) - return ERR_PTR(-EPROBE_DEFER); + if (config->enable_gpio < 0 && config->enable_gpio != -ENOENT) + return ERR_PTR(config->enable_gpio); /* Fetch GPIOs. - optional property*/ ret = of_gpio_count(np); @@ -190,8 +190,11 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np, for (i = 0; i < config->nr_gpios; i++) { gpio = of_get_named_gpio(np, "gpios", i); - if (gpio < 0) + if (gpio < 0) { + if (gpio != -ENOENT) + return ERR_PTR(gpio); break; + } config->gpios[i].gpio = gpio; if (proplen > 0) { of_property_read_u32_index(np, "gpios-states", From 295070e9aa015abb9b92cccfbb1e43954e938133 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 12 Nov 2016 15:22:38 +0100 Subject: [PATCH 09/24] regulator: stw481x-vmmc: fix ages old enable error The regulator has never been properly enabled, it has been dormant all the time. It's strange that MMC was working at all, but it likely worked by the signals going through the levelshifter and reaching the card anyways. Fixes: 3615a34ea1a6 ("regulator: add STw481x VMMC driver") Signed-off-by: Linus Walleij Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/regulator/stw481x-vmmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/stw481x-vmmc.c b/drivers/regulator/stw481x-vmmc.c index 7d2ae3e9e942..342f5da79975 100644 --- a/drivers/regulator/stw481x-vmmc.c +++ b/drivers/regulator/stw481x-vmmc.c @@ -47,7 +47,8 @@ static struct regulator_desc vmmc_regulator = { .volt_table = stw481x_vmmc_voltages, .enable_time = 200, /* FIXME: look this up */ .enable_reg = STW_CONF1, - .enable_mask = STW_CONF1_PDN_VMMC, + .enable_mask = STW_CONF1_PDN_VMMC | STW_CONF1_MMC_LS_STATUS, + .enable_val = STW_CONF1_PDN_VMMC, .vsel_reg = STW_CONF1, .vsel_mask = STW_CONF1_VMMC_MASK, }; From 53a20465e6b674eb6ce594b0e9e8940495e95f33 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 15 Nov 2016 15:01:09 +0000 Subject: [PATCH 10/24] regulator: pwm: Add missing quotes to DT example Reported-by: Peter Rosin Signed-off-by: Mark Brown Acked-by: Rob Herring --- Documentation/devicetree/bindings/regulator/pwm-regulator.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt index 3aeba9f86ed8..bf85aa9ad6a7 100644 --- a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt @@ -59,7 +59,7 @@ Any property defined as part of the core regulator binding can also be used. Continuous Voltage With Enable GPIO Example: pwm_regulator { - compatible = "pwm-regulator; + compatible = "pwm-regulator"; pwms = <&pwm1 0 8448 0>; enable-gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>; regulator-min-microvolt = <1016000>; @@ -76,7 +76,7 @@ Continuous Voltage With Enable GPIO Example: Voltage Table Example: pwm_regulator { - compatible = "pwm-regulator; + compatible = "pwm-regulator"; pwms = <&pwm1 0 8448 0>; regulator-min-microvolt = <1016000>; regulator-max-microvolt = <1114000>; From 9a40cb0cb8b55ecfdcd3cec1381bcc46ec488588 Mon Sep 17 00:00:00 2001 From: Venkat Reddy Talla Date: Tue, 15 Nov 2016 22:51:20 +0530 Subject: [PATCH 11/24] regulator: max77620: remove unused variable max77620_reuglator_pdata structure variable reg_idata is not used anywhere in the regulator driver, so removing it. Signed-off-by: Venkat Reddy Talla Signed-off-by: Mark Brown --- drivers/regulator/max77620-regulator.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/regulator/max77620-regulator.c b/drivers/regulator/max77620-regulator.c index a1b49a6d538f..c39a56b41901 100644 --- a/drivers/regulator/max77620-regulator.c +++ b/drivers/regulator/max77620-regulator.c @@ -73,7 +73,6 @@ struct max77620_regulator_info { }; struct max77620_regulator_pdata { - struct regulator_init_data *reg_idata; int active_fps_src; int active_fps_pd_slot; int active_fps_pu_slot; From dab780a3b489b038a47d8fbbc49c33aae1e7a682 Mon Sep 17 00:00:00 2001 From: Nicolae Rosia Date: Sat, 12 Nov 2016 14:42:14 +0200 Subject: [PATCH 12/24] regulator: twl-regulator: rework fixed regulator definition TWL603X and TWL4030 are different and have different code logic. Rework the regulator definition method so we can split the file easily in twl4030 and twl6030. Signed-off-by: Nicolae Rosia Signed-off-by: Mark Brown --- drivers/regulator/twl-regulator.c | 38 +++++++++++++++++++------------ 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 210681d6b743..23f7b31f6355 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -898,15 +898,6 @@ static struct regulator_ops twlsmps_ops = { /*----------------------------------------------------------------------*/ -#define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ - remap_conf) \ - TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ - remap_conf, TWL4030, twl4030fixed_ops, \ - twl4030reg_map_mode) -#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \ - TWL_FIXED_LDO(label, offset, mVolts, 0x0, turnon_delay, \ - 0x0, TWL6030, twl6030fixed_ops, NULL) - #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) \ static const struct twlreg_info TWL4030_INFO_##label = { \ .base = offset, \ @@ -983,8 +974,27 @@ static const struct twlreg_info TWL6032_INFO_##label = { \ }, \ } -#define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \ - family, operations, map_mode) \ +#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \ +static const struct twlreg_info TWLFIXED_INFO_##label = { \ + .base = offset, \ + .id = 0, \ + .min_mV = mVolts, \ + .remap = 0, \ + .desc = { \ + .name = #label, \ + .id = TWL6030##_REG_##label, \ + .n_voltages = 1, \ + .ops = &twl6030fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = mVolts * 1000, \ + .enable_time = turnon_delay, \ + .of_map_mode = NULL, \ + }, \ + } + +#define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ + remap_conf) \ static const struct twlreg_info TWLFIXED_INFO_##label = { \ .base = offset, \ .id = num, \ @@ -992,14 +1002,14 @@ static const struct twlreg_info TWLFIXED_INFO_##label = { \ .remap = remap_conf, \ .desc = { \ .name = #label, \ - .id = family##_REG_##label, \ + .id = TWL4030##_REG_##label, \ .n_voltages = 1, \ - .ops = &operations, \ + .ops = &twl4030fixed_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ .min_uV = mVolts * 1000, \ .enable_time = turnon_delay, \ - .of_map_mode = map_mode, \ + .of_map_mode = twl4030reg_map_mode, \ }, \ } From 25d82337705e24203b27e23762144950f87a1016 Mon Sep 17 00:00:00 2001 From: Nicolae Rosia Date: Sat, 12 Nov 2016 14:42:12 +0200 Subject: [PATCH 13/24] regulator: twl: make driver DT only All users are DT only, remove unused code. Signed-off-by: Nicolae Rosia Signed-off-by: Mark Brown --- drivers/regulator/twl-regulator.c | 37 +++++-------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 23f7b31f6355..a777c9d0d2df 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -1175,37 +1175,20 @@ static int twlreg_probe(struct platform_device *pdev) struct regulator_init_data *initdata; struct regulation_constraints *c; struct regulator_dev *rdev; - struct twl_regulator_driver_data *drvdata; const struct of_device_id *match; struct regulator_config config = { }; match = of_match_device(twl_of_match, &pdev->dev); - if (match) { - template = match->data; - id = template->desc.id; - initdata = of_get_regulator_init_data(&pdev->dev, - pdev->dev.of_node, - &template->desc); - drvdata = NULL; - } else { - id = pdev->id; - initdata = dev_get_platdata(&pdev->dev); - for (i = 0, template = NULL; i < ARRAY_SIZE(twl_of_match); i++) { - template = twl_of_match[i].data; - if (template && template->desc.id == id) - break; - } - if (i == ARRAY_SIZE(twl_of_match)) - return -ENODEV; - - drvdata = initdata->driver_data; - if (!drvdata) - return -EINVAL; - } + if (!match) + return -ENODEV; + template = match->data; if (!template) return -ENODEV; + id = template->desc.id; + initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, + &template->desc); if (!initdata) return -EINVAL; @@ -1213,14 +1196,6 @@ static int twlreg_probe(struct platform_device *pdev) if (!info) return -ENOMEM; - if (drvdata) { - /* copy the driver data into regulator data */ - info->features = drvdata->features; - info->data = drvdata->data; - info->set_voltage = drvdata->set_voltage; - info->get_voltage = drvdata->get_voltage; - } - /* Constrain board-specific capabilities according to what * this driver and the chip itself can actually do. */ From 8313a4fb66b15816883792c9beed45b8978b1c8b Mon Sep 17 00:00:00 2001 From: Nicolae Rosia Date: Sat, 12 Nov 2016 14:42:13 +0200 Subject: [PATCH 14/24] regulator: twl: kill unused functions This code was used by OMAP platform based boards which are now DT only. Proper support for SMPS is missing in this driver. Signed-off-by: Nicolae Rosia Signed-off-by: Mark Brown --- drivers/regulator/twl-regulator.c | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index a777c9d0d2df..3f9f53d98ef0 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -60,13 +60,6 @@ struct twlreg_info { /* chip specific features */ unsigned long features; - /* - * optional override functions for voltage set/get - * these are currently only used for SMPS regulators - */ - int (*get_voltage)(void *data); - int (*set_voltage)(void *data, int target_uV); - /* data passed from board for external get/set voltage */ void *data; }; @@ -565,12 +558,7 @@ twl4030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, struct twlreg_info *info = rdev_get_drvdata(rdev); int vsel = DIV_ROUND_UP(min_uV - 600000, 12500); - if (info->set_voltage) { - return info->set_voltage(info->data, min_uV); - } else { - twlreg_write(info, TWL_MODULE_PM_RECEIVER, - VREG_VOLTAGE_SMPS_4030, vsel); - } + twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS_4030, vsel); return 0; } @@ -580,9 +568,6 @@ static int twl4030smps_get_voltage(struct regulator_dev *rdev) struct twlreg_info *info = rdev_get_drvdata(rdev); int vsel; - if (info->get_voltage) - return info->get_voltage(info->data); - vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS_4030); @@ -597,21 +582,11 @@ static struct regulator_ops twl4030smps_ops = { static int twl6030coresmps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { - struct twlreg_info *info = rdev_get_drvdata(rdev); - - if (info->set_voltage) - return info->set_voltage(info->data, min_uV); - return -ENODEV; } static int twl6030coresmps_get_voltage(struct regulator_dev *rdev) { - struct twlreg_info *info = rdev_get_drvdata(rdev); - - if (info->get_voltage) - return info->get_voltage(info->data); - return -ENODEV; } @@ -1169,7 +1144,7 @@ MODULE_DEVICE_TABLE(of, twl_of_match); static int twlreg_probe(struct platform_device *pdev) { - int i, id; + int id; struct twlreg_info *info; const struct twlreg_info *template; struct regulator_init_data *initdata; From cac28ae60e3dae1814f12f0d24d041a67153692c Mon Sep 17 00:00:00 2001 From: Nicolae Rosia Date: Sat, 12 Nov 2016 14:42:15 +0200 Subject: [PATCH 15/24] regulator: twl: split twl6030 logic into its own file In order to not break existing users, we keep using the same CONFIG symbol. This makes it easier to add support for TWL6032 and refactor mfd/twl-core. Checkpatch warnings are inherited from twl-regulator.c and will be addressed in a subsequent patch. Signed-off-by: Nicolae Rosia Signed-off-by: Mark Brown --- MAINTAINERS | 1 + drivers/regulator/Makefile | 2 +- drivers/regulator/twl-regulator.c | 600 +------------------ drivers/regulator/twl6030-regulator.c | 805 ++++++++++++++++++++++++++ 4 files changed, 812 insertions(+), 596 deletions(-) create mode 100644 drivers/regulator/twl6030-regulator.c diff --git a/MAINTAINERS b/MAINTAINERS index 1cd38a7e0064..1317123f100d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8721,6 +8721,7 @@ F: drivers/regulator/tps65217-regulator.c F: drivers/regulator/tps65218-regulator.c F: drivers/regulator/tps65910-regulator.c F: drivers/regulator/twl-regulator.c +F: drivers/regulator/twl6030-regulator.c F: include/linux/i2c-omap.h OMAP DEVICE TREE SUPPORT diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 2142a5d3fc08..14294692beb9 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -104,7 +104,7 @@ obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o 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_TWL4030) += twl-regulator.o twl6030-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 3f9f53d98ef0..d572b8a583e4 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -24,7 +24,7 @@ #include /* - * The TWL4030/TW5030/TPS659x0/TWL6030 family chips include power management, a + * The TWL4030/TW5030/TPS659x0 family chips include power management, a * USB OTG transceiver, an RTC, ADC, PWM, and lots more. Some versions * include an audio codec, battery charger, and more voltage regulators. * These chips are often used in OMAP-based systems. @@ -81,33 +81,6 @@ struct twlreg_info { #define VREG_STATE 2 #define VREG_VOLTAGE 3 #define VREG_VOLTAGE_SMPS 4 -/* TWL6030 Misc register offsets */ -#define VREG_BC_ALL 1 -#define VREG_BC_REF 2 -#define VREG_BC_PROC 3 -#define VREG_BC_CLK_RST 4 - -/* TWL6030 LDO register values for CFG_STATE */ -#define TWL6030_CFG_STATE_OFF 0x00 -#define TWL6030_CFG_STATE_ON 0x01 -#define TWL6030_CFG_STATE_OFF2 0x02 -#define TWL6030_CFG_STATE_SLEEP 0x03 -#define TWL6030_CFG_STATE_GRP_SHIFT 5 -#define TWL6030_CFG_STATE_APP_SHIFT 2 -#define TWL6030_CFG_STATE_APP_MASK (0x03 << TWL6030_CFG_STATE_APP_SHIFT) -#define TWL6030_CFG_STATE_APP(v) (((v) & TWL6030_CFG_STATE_APP_MASK) >>\ - TWL6030_CFG_STATE_APP_SHIFT) - -/* Flags for SMPS Voltage reading */ -#define SMPS_OFFSET_EN BIT(0) -#define SMPS_EXTENDED_EN BIT(1) - -/* twl6032 SMPS EPROM values */ -#define TWL6030_SMPS_OFFSET 0xB0 -#define TWL6030_SMPS_MULT 0xB3 -#define SMPS_MULTOFFSET_SMPS4 BIT(0) -#define SMPS_MULTOFFSET_VIO BIT(1) -#define SMPS_MULTOFFSET_SMPS3 BIT(6) static inline int twlreg_read(struct twlreg_info *info, unsigned slave_subgp, unsigned offset) @@ -161,26 +134,6 @@ static int twl4030reg_is_enabled(struct regulator_dev *rdev) return state & P1_GRP_4030; } -static int twl6030reg_is_enabled(struct regulator_dev *rdev) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - int grp = 0, val; - - if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) { - grp = twlreg_grp(rdev); - if (grp < 0) - return grp; - grp &= P1_GRP_6030; - } else { - grp = 1; - } - - val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); - val = TWL6030_CFG_STATE_APP(val); - - return grp && (val == TWL6030_CFG_STATE_ON); -} - #define PB_I2C_BUSY BIT(0) #define PB_I2C_BWEN BIT(1) @@ -266,23 +219,6 @@ static int twl4030reg_enable(struct regulator_dev *rdev) return ret; } -static int twl6030reg_enable(struct regulator_dev *rdev) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - int grp = 0; - int ret; - - if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) - grp = twlreg_grp(rdev); - if (grp < 0) - return grp; - - ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, - grp << TWL6030_CFG_STATE_GRP_SHIFT | - TWL6030_CFG_STATE_ON); - return ret; -} - static int twl4030reg_disable(struct regulator_dev *rdev) { struct twlreg_info *info = rdev_get_drvdata(rdev); @@ -300,23 +236,6 @@ static int twl4030reg_disable(struct regulator_dev *rdev) return ret; } -static int twl6030reg_disable(struct regulator_dev *rdev) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - int grp = 0; - int ret; - - if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) - grp = P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030; - - /* For 6030, set the off state for all grps enabled */ - ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, - (grp) << TWL6030_CFG_STATE_GRP_SHIFT | - TWL6030_CFG_STATE_OFF); - - return ret; -} - static int twl4030reg_get_status(struct regulator_dev *rdev) { int state = twlreg_grp(rdev); @@ -333,33 +252,6 @@ static int twl4030reg_get_status(struct regulator_dev *rdev) : REGULATOR_STATUS_STANDBY; } -static int twl6030reg_get_status(struct regulator_dev *rdev) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - int val; - - val = twlreg_grp(rdev); - if (val < 0) - return val; - - val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); - - switch (TWL6030_CFG_STATE_APP(val)) { - case TWL6030_CFG_STATE_ON: - return REGULATOR_STATUS_NORMAL; - - case TWL6030_CFG_STATE_SLEEP: - return REGULATOR_STATUS_STANDBY; - - case TWL6030_CFG_STATE_OFF: - case TWL6030_CFG_STATE_OFF2: - default: - break; - } - - return REGULATOR_STATUS_OFF; -} - static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode) { struct twlreg_info *info = rdev_get_drvdata(rdev); @@ -392,36 +284,6 @@ static inline unsigned int twl4030reg_map_mode(unsigned int mode) } } -static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - int grp = 0; - int val; - - if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) - grp = twlreg_grp(rdev); - - if (grp < 0) - return grp; - - /* Compose the state register settings */ - val = grp << TWL6030_CFG_STATE_GRP_SHIFT; - /* We can only set the mode through state machine commands... */ - switch (mode) { - case REGULATOR_MODE_NORMAL: - val |= TWL6030_CFG_STATE_ON; - break; - case REGULATOR_MODE_STANDBY: - val |= TWL6030_CFG_STATE_SLEEP; - break; - - default: - return -EINVAL; - } - - return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, val); -} - /*----------------------------------------------------------------------*/ /* @@ -579,75 +441,6 @@ static struct regulator_ops twl4030smps_ops = { .get_voltage = twl4030smps_get_voltage, }; -static int twl6030coresmps_set_voltage(struct regulator_dev *rdev, int min_uV, - int max_uV, unsigned *selector) -{ - return -ENODEV; -} - -static int twl6030coresmps_get_voltage(struct regulator_dev *rdev) -{ - return -ENODEV; -} - -static struct regulator_ops twl6030coresmps_ops = { - .set_voltage = twl6030coresmps_set_voltage, - .get_voltage = twl6030coresmps_get_voltage, -}; - -static int twl6030ldo_list_voltage(struct regulator_dev *rdev, unsigned sel) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - - switch (sel) { - case 0: - return 0; - case 1 ... 24: - /* Linear mapping from 00000001 to 00011000: - * Absolute voltage value = 1.0 V + 0.1 V × (sel – 00000001) - */ - return (info->min_mV + 100 * (sel - 1)) * 1000; - case 25 ... 30: - return -EINVAL; - case 31: - return 2750000; - default: - return -EINVAL; - } -} - -static int -twl6030ldo_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - - return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE, - selector); -} - -static int twl6030ldo_get_voltage_sel(struct regulator_dev *rdev) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - int vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE); - - return vsel; -} - -static struct regulator_ops twl6030ldo_ops = { - .list_voltage = twl6030ldo_list_voltage, - - .set_voltage_sel = twl6030ldo_set_voltage_sel, - .get_voltage_sel = twl6030ldo_get_voltage_sel, - - .enable = twl6030reg_enable, - .disable = twl6030reg_disable, - .is_enabled = twl6030reg_is_enabled, - - .set_mode = twl6030reg_set_mode, - - .get_status = twl6030reg_get_status, -}; - /*----------------------------------------------------------------------*/ static struct regulator_ops twl4030fixed_ops = { @@ -662,215 +455,6 @@ static struct regulator_ops twl4030fixed_ops = { .get_status = twl4030reg_get_status, }; -static struct regulator_ops twl6030fixed_ops = { - .list_voltage = regulator_list_voltage_linear, - - .enable = twl6030reg_enable, - .disable = twl6030reg_disable, - .is_enabled = twl6030reg_is_enabled, - - .set_mode = twl6030reg_set_mode, - - .get_status = twl6030reg_get_status, -}; - -/* - * SMPS status and control - */ - -static int twl6030smps_list_voltage(struct regulator_dev *rdev, unsigned index) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - - int voltage = 0; - - switch (info->flags) { - case SMPS_OFFSET_EN: - voltage = 100000; - /* fall through */ - case 0: - switch (index) { - case 0: - voltage = 0; - break; - case 58: - voltage = 1350 * 1000; - break; - case 59: - voltage = 1500 * 1000; - break; - case 60: - voltage = 1800 * 1000; - break; - case 61: - voltage = 1900 * 1000; - break; - case 62: - voltage = 2100 * 1000; - break; - default: - voltage += (600000 + (12500 * (index - 1))); - } - break; - case SMPS_EXTENDED_EN: - switch (index) { - case 0: - voltage = 0; - break; - case 58: - voltage = 2084 * 1000; - break; - case 59: - voltage = 2315 * 1000; - break; - case 60: - voltage = 2778 * 1000; - break; - case 61: - voltage = 2932 * 1000; - break; - case 62: - voltage = 3241 * 1000; - break; - default: - voltage = (1852000 + (38600 * (index - 1))); - } - break; - case SMPS_OFFSET_EN | SMPS_EXTENDED_EN: - switch (index) { - case 0: - voltage = 0; - break; - case 58: - voltage = 4167 * 1000; - break; - case 59: - voltage = 2315 * 1000; - break; - case 60: - voltage = 2778 * 1000; - break; - case 61: - voltage = 2932 * 1000; - break; - case 62: - voltage = 3241 * 1000; - break; - default: - voltage = (2161000 + (38600 * (index - 1))); - } - break; - } - - return voltage; -} - -static int twl6030smps_map_voltage(struct regulator_dev *rdev, int min_uV, - int max_uV) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - int vsel = 0; - - switch (info->flags) { - case 0: - if (min_uV == 0) - vsel = 0; - else if ((min_uV >= 600000) && (min_uV <= 1300000)) { - vsel = DIV_ROUND_UP(min_uV - 600000, 12500); - vsel++; - } - /* Values 1..57 for vsel are linear and can be calculated - * values 58..62 are non linear. - */ - else if ((min_uV > 1900000) && (min_uV <= 2100000)) - vsel = 62; - else if ((min_uV > 1800000) && (min_uV <= 1900000)) - vsel = 61; - else if ((min_uV > 1500000) && (min_uV <= 1800000)) - vsel = 60; - else if ((min_uV > 1350000) && (min_uV <= 1500000)) - vsel = 59; - else if ((min_uV > 1300000) && (min_uV <= 1350000)) - vsel = 58; - else - return -EINVAL; - break; - case SMPS_OFFSET_EN: - if (min_uV == 0) - vsel = 0; - else if ((min_uV >= 700000) && (min_uV <= 1420000)) { - vsel = DIV_ROUND_UP(min_uV - 700000, 12500); - vsel++; - } - /* Values 1..57 for vsel are linear and can be calculated - * values 58..62 are non linear. - */ - else if ((min_uV > 1900000) && (min_uV <= 2100000)) - vsel = 62; - else if ((min_uV > 1800000) && (min_uV <= 1900000)) - vsel = 61; - else if ((min_uV > 1350000) && (min_uV <= 1800000)) - vsel = 60; - else if ((min_uV > 1350000) && (min_uV <= 1500000)) - vsel = 59; - else if ((min_uV > 1300000) && (min_uV <= 1350000)) - vsel = 58; - else - return -EINVAL; - break; - case SMPS_EXTENDED_EN: - if (min_uV == 0) { - vsel = 0; - } else if ((min_uV >= 1852000) && (max_uV <= 4013600)) { - vsel = DIV_ROUND_UP(min_uV - 1852000, 38600); - vsel++; - } - break; - case SMPS_OFFSET_EN|SMPS_EXTENDED_EN: - if (min_uV == 0) { - vsel = 0; - } else if ((min_uV >= 2161000) && (min_uV <= 4321000)) { - vsel = DIV_ROUND_UP(min_uV - 2161000, 38600); - vsel++; - } - break; - } - - return vsel; -} - -static int twl6030smps_set_voltage_sel(struct regulator_dev *rdev, - unsigned int selector) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - - return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS, - selector); -} - -static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev) -{ - struct twlreg_info *info = rdev_get_drvdata(rdev); - - return twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS); -} - -static struct regulator_ops twlsmps_ops = { - .list_voltage = twl6030smps_list_voltage, - .map_voltage = twl6030smps_map_voltage, - - .set_voltage_sel = twl6030smps_set_voltage_sel, - .get_voltage_sel = twl6030smps_get_voltage_sel, - - .enable = twl6030reg_enable, - .disable = twl6030reg_disable, - .is_enabled = twl6030reg_is_enabled, - - .set_mode = twl6030reg_set_mode, - - .get_status = twl6030reg_get_status, -}; - /*----------------------------------------------------------------------*/ #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) \ @@ -908,66 +492,6 @@ static const struct twlreg_info TWL4030_INFO_##label = { \ }, \ } -#define TWL6030_ADJUSTABLE_SMPS(label) \ -static const struct twlreg_info TWL6030_INFO_##label = { \ - .desc = { \ - .name = #label, \ - .id = TWL6030_REG_##label, \ - .ops = &twl6030coresmps_ops, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ - }, \ - } - -#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \ -static const struct twlreg_info TWL6030_INFO_##label = { \ - .base = offset, \ - .min_mV = min_mVolts, \ - .max_mV = max_mVolts, \ - .desc = { \ - .name = #label, \ - .id = TWL6030_REG_##label, \ - .n_voltages = 32, \ - .ops = &twl6030ldo_ops, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ - }, \ - } - -#define TWL6032_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \ -static const struct twlreg_info TWL6032_INFO_##label = { \ - .base = offset, \ - .min_mV = min_mVolts, \ - .max_mV = max_mVolts, \ - .desc = { \ - .name = #label, \ - .id = TWL6032_REG_##label, \ - .n_voltages = 32, \ - .ops = &twl6030ldo_ops, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ - }, \ - } - -#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \ -static const struct twlreg_info TWLFIXED_INFO_##label = { \ - .base = offset, \ - .id = 0, \ - .min_mV = mVolts, \ - .remap = 0, \ - .desc = { \ - .name = #label, \ - .id = TWL6030##_REG_##label, \ - .n_voltages = 1, \ - .ops = &twl6030fixed_ops, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ - .min_uV = mVolts * 1000, \ - .enable_time = turnon_delay, \ - .of_map_mode = NULL, \ - }, \ - } - #define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ remap_conf) \ static const struct twlreg_info TWLFIXED_INFO_##label = { \ @@ -988,21 +512,6 @@ static const struct twlreg_info TWLFIXED_INFO_##label = { \ }, \ } -#define TWL6032_ADJUSTABLE_SMPS(label, offset) \ -static const struct twlreg_info TWLSMPS_INFO_##label = { \ - .base = offset, \ - .min_mV = 600, \ - .max_mV = 2100, \ - .desc = { \ - .name = #label, \ - .id = TWL6032_REG_##label, \ - .n_voltages = 63, \ - .ops = &twlsmps_ops, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ - }, \ - } - /* * We list regulators here if systems need some level of * software control over them after boot. @@ -1023,60 +532,11 @@ TWL4030_ADJUSTABLE_LDO(VIO, 0x4b, 14, 1000, 0x08); TWL4030_ADJUSTABLE_SMPS(VDD1, 0x55, 15, 1000, 0x08); TWL4030_ADJUSTABLE_SMPS(VDD2, 0x63, 16, 1000, 0x08); /* VUSBCP is managed *only* by the USB subchip */ -/* 6030 REG with base as PMC Slave Misc : 0x0030 */ -/* Turnon-delay and remap configuration values for 6030 are not - verified since the specification is not public */ -TWL6030_ADJUSTABLE_SMPS(VDD1); -TWL6030_ADJUSTABLE_SMPS(VDD2); -TWL6030_ADJUSTABLE_SMPS(VDD3); -TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300); -TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300); -TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300); -TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300); -TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300); -TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300); -/* 6025 are renamed compared to 6030 versions */ -TWL6032_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300); TWL4030_FIXED_LDO(VINTANA1, 0x3f, 1500, 11, 100, 0x08); TWL4030_FIXED_LDO(VINTDIG, 0x47, 1500, 13, 100, 0x08); TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17, 100, 0x08); TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18, 100, 0x08); TWL4030_FIXED_LDO(VUSB3V1, 0x77, 3100, 19, 150, 0x08); -TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0); -TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0); -TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0); -TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0); -TWL6030_FIXED_LDO(V1V8, 0x16, 1800, 0); -TWL6030_FIXED_LDO(V2V1, 0x1c, 2100, 0); -TWL6032_ADJUSTABLE_SMPS(SMPS3, 0x34); -TWL6032_ADJUSTABLE_SMPS(SMPS4, 0x10); -TWL6032_ADJUSTABLE_SMPS(VIO, 0x16); - -static u8 twl_get_smps_offset(void) -{ - u8 value; - - twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value, - TWL6030_SMPS_OFFSET); - return value; -} - -static u8 twl_get_smps_mult(void) -{ - u8 value; - - twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value, - TWL6030_SMPS_MULT); - return value; -} #define TWL_OF_MATCH(comp, family, label) \ { \ @@ -1106,38 +566,11 @@ static const struct of_device_id twl_of_match[] = { TWL4030_OF_MATCH("ti,twl4030-vio", VIO), TWL4030_OF_MATCH("ti,twl4030-vdd1", VDD1), TWL4030_OF_MATCH("ti,twl4030-vdd2", VDD2), - TWL6030_OF_MATCH("ti,twl6030-vdd1", VDD1), - TWL6030_OF_MATCH("ti,twl6030-vdd2", VDD2), - TWL6030_OF_MATCH("ti,twl6030-vdd3", VDD3), - TWL6030_OF_MATCH("ti,twl6030-vaux1", VAUX1_6030), - TWL6030_OF_MATCH("ti,twl6030-vaux2", VAUX2_6030), - TWL6030_OF_MATCH("ti,twl6030-vaux3", VAUX3_6030), - TWL6030_OF_MATCH("ti,twl6030-vmmc", VMMC), - TWL6030_OF_MATCH("ti,twl6030-vpp", VPP), - TWL6030_OF_MATCH("ti,twl6030-vusim", VUSIM), - TWL6032_OF_MATCH("ti,twl6032-ldo2", LDO2), - TWL6032_OF_MATCH("ti,twl6032-ldo4", LDO4), - TWL6032_OF_MATCH("ti,twl6032-ldo3", LDO3), - TWL6032_OF_MATCH("ti,twl6032-ldo5", LDO5), - TWL6032_OF_MATCH("ti,twl6032-ldo1", LDO1), - TWL6032_OF_MATCH("ti,twl6032-ldo7", LDO7), - TWL6032_OF_MATCH("ti,twl6032-ldo6", LDO6), - TWL6032_OF_MATCH("ti,twl6032-ldoln", LDOLN), - TWL6032_OF_MATCH("ti,twl6032-ldousb", LDOUSB), TWLFIXED_OF_MATCH("ti,twl4030-vintana1", VINTANA1), TWLFIXED_OF_MATCH("ti,twl4030-vintdig", VINTDIG), TWLFIXED_OF_MATCH("ti,twl4030-vusb1v5", VUSB1V5), TWLFIXED_OF_MATCH("ti,twl4030-vusb1v8", VUSB1V8), TWLFIXED_OF_MATCH("ti,twl4030-vusb3v1", VUSB3V1), - TWLFIXED_OF_MATCH("ti,twl6030-vana", VANA), - TWLFIXED_OF_MATCH("ti,twl6030-vcxio", VCXIO), - TWLFIXED_OF_MATCH("ti,twl6030-vdac", VDAC), - TWLFIXED_OF_MATCH("ti,twl6030-vusb", VUSB), - TWLFIXED_OF_MATCH("ti,twl6030-v1v8", V1V8), - TWLFIXED_OF_MATCH("ti,twl6030-v2v1", V2V1), - TWLSMPS_OF_MATCH("ti,twl6032-smps3", SMPS3), - TWLSMPS_OF_MATCH("ti,twl6032-smps4", SMPS4), - TWLSMPS_OF_MATCH("ti,twl6032-vio", VIO), {}, }; MODULE_DEVICE_TABLE(of, twl_of_match); @@ -1193,27 +626,6 @@ static int twlreg_probe(struct platform_device *pdev) break; } - switch (id) { - case TWL6032_REG_SMPS3: - if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS3) - info->flags |= SMPS_EXTENDED_EN; - if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS3) - info->flags |= SMPS_OFFSET_EN; - break; - case TWL6032_REG_SMPS4: - if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS4) - info->flags |= SMPS_EXTENDED_EN; - if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS4) - info->flags |= SMPS_OFFSET_EN; - break; - case TWL6032_REG_VIO: - if (twl_get_smps_mult() & SMPS_MULTOFFSET_VIO) - info->flags |= SMPS_EXTENDED_EN; - if (twl_get_smps_offset() & SMPS_MULTOFFSET_VIO) - info->flags |= SMPS_OFFSET_EN; - break; - } - config.dev = &pdev->dev; config.init_data = initdata; config.driver_data = info; @@ -1227,9 +639,7 @@ static int twlreg_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, rdev); - if (twl_class_is_4030()) - twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_REMAP, - info->remap); + twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_REMAP, info->remap); /* NOTE: many regulators support short-circuit IRQs (presentable * as REGULATOR_OVER_CURRENT notifications?) configured via: @@ -1242,7 +652,7 @@ static int twlreg_probe(struct platform_device *pdev) return 0; } -MODULE_ALIAS("platform:twl_reg"); +MODULE_ALIAS("platform:twl4030_reg"); static struct platform_driver twlreg_driver = { .probe = twlreg_probe, @@ -1250,7 +660,7 @@ static struct platform_driver twlreg_driver = { * "twl_regulator.12" (and friends) to "twl_regulator.1". */ .driver = { - .name = "twl_reg", + .name = "twl4030_reg", .of_match_table = of_match_ptr(twl_of_match), }, }; @@ -1267,5 +677,5 @@ static void __exit twlreg_exit(void) } module_exit(twlreg_exit) -MODULE_DESCRIPTION("TWL regulator driver"); +MODULE_DESCRIPTION("TWL4030 regulator driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/twl6030-regulator.c b/drivers/regulator/twl6030-regulator.c new file mode 100644 index 000000000000..a446137f57bb --- /dev/null +++ b/drivers/regulator/twl6030-regulator.c @@ -0,0 +1,805 @@ +/* + * Split TWL6030 logic from twl-regulator.c: + * Copyright (C) 2008 David Brownell + * + * Copyright (C) 2016 Nicolae Rosia + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct twlreg_info { + /* start of regulator's PM_RECEIVER control register bank */ + u8 base; + + /* twl resource ID, for resource control state machine */ + u8 id; + + /* voltage in mV = table[VSEL]; table_len must be a power-of-two */ + u8 table_len; + const u16 *table; + + /* State REMAP default configuration */ + u8 remap; + + /* chip constraints on regulator behavior */ + u16 min_mV; + u16 max_mV; + + u8 flags; + + /* used by regulator core */ + struct regulator_desc desc; + + /* chip specific features */ + unsigned long features; + + /* data passed from board for external get/set voltage */ + void *data; +}; + + +/* LDO control registers ... offset is from the base of its register bank. + * The first three registers of all power resource banks help hardware to + * manage the various resource groups. + */ +/* Common offset in TWL4030/6030 */ +#define VREG_GRP 0 +/* TWL6030 register offsets */ +#define VREG_TRANS 1 +#define VREG_STATE 2 +#define VREG_VOLTAGE 3 +#define VREG_VOLTAGE_SMPS 4 +/* TWL6030 Misc register offsets */ +#define VREG_BC_ALL 1 +#define VREG_BC_REF 2 +#define VREG_BC_PROC 3 +#define VREG_BC_CLK_RST 4 + +/* TWL6030 LDO register values for CFG_STATE */ +#define TWL6030_CFG_STATE_OFF 0x00 +#define TWL6030_CFG_STATE_ON 0x01 +#define TWL6030_CFG_STATE_OFF2 0x02 +#define TWL6030_CFG_STATE_SLEEP 0x03 +#define TWL6030_CFG_STATE_GRP_SHIFT 5 +#define TWL6030_CFG_STATE_APP_SHIFT 2 +#define TWL6030_CFG_STATE_APP_MASK (0x03 << TWL6030_CFG_STATE_APP_SHIFT) +#define TWL6030_CFG_STATE_APP(v) (((v) & TWL6030_CFG_STATE_APP_MASK) >>\ + TWL6030_CFG_STATE_APP_SHIFT) + +/* Flags for SMPS Voltage reading */ +#define SMPS_OFFSET_EN BIT(0) +#define SMPS_EXTENDED_EN BIT(1) + +/* twl6032 SMPS EPROM values */ +#define TWL6030_SMPS_OFFSET 0xB0 +#define TWL6030_SMPS_MULT 0xB3 +#define SMPS_MULTOFFSET_SMPS4 BIT(0) +#define SMPS_MULTOFFSET_VIO BIT(1) +#define SMPS_MULTOFFSET_SMPS3 BIT(6) + +static inline int +twlreg_read(struct twlreg_info *info, unsigned slave_subgp, unsigned offset) +{ + u8 value; + int status; + + status = twl_i2c_read_u8(slave_subgp, + &value, info->base + offset); + return (status < 0) ? status : value; +} + +static inline int +twlreg_write(struct twlreg_info *info, unsigned slave_subgp, unsigned offset, + u8 value) +{ + return twl_i2c_write_u8(slave_subgp, + value, info->base + offset); +} + +/* generic power resource operations, which work on all regulators */ +static int twlreg_grp(struct regulator_dev *rdev) +{ + return twlreg_read(rdev_get_drvdata(rdev), TWL_MODULE_PM_RECEIVER, + VREG_GRP); +} + +/* + * Enable/disable regulators by joining/leaving the P1 (processor) group. + * We assume nobody else is updating the DEV_GRP registers. + */ +/* definition for 6030 family */ +#define P3_GRP_6030 BIT(2) /* secondary processor, modem, etc */ +#define P2_GRP_6030 BIT(1) /* "peripherals" */ +#define P1_GRP_6030 BIT(0) /* CPU/Linux */ + +static int twl6030reg_is_enabled(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0, val; + + if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) { + grp = twlreg_grp(rdev); + if (grp < 0) + return grp; + grp &= P1_GRP_6030; + } else { + grp = 1; + } + + val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); + val = TWL6030_CFG_STATE_APP(val); + + return grp && (val == TWL6030_CFG_STATE_ON); +} + +#define PB_I2C_BUSY BIT(0) +#define PB_I2C_BWEN BIT(1) + + +static int twl6030reg_enable(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0; + int ret; + + if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) + grp = twlreg_grp(rdev); + if (grp < 0) + return grp; + + ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, + grp << TWL6030_CFG_STATE_GRP_SHIFT | + TWL6030_CFG_STATE_ON); + return ret; +} + +static int twl6030reg_disable(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0; + int ret; + + if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) + grp = P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030; + + /* For 6030, set the off state for all grps enabled */ + ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, + (grp) << TWL6030_CFG_STATE_GRP_SHIFT | + TWL6030_CFG_STATE_OFF); + + return ret; +} + +static int twl6030reg_get_status(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int val; + + val = twlreg_grp(rdev); + if (val < 0) + return val; + + val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); + + switch (TWL6030_CFG_STATE_APP(val)) { + case TWL6030_CFG_STATE_ON: + return REGULATOR_STATUS_NORMAL; + + case TWL6030_CFG_STATE_SLEEP: + return REGULATOR_STATUS_STANDBY; + + case TWL6030_CFG_STATE_OFF: + case TWL6030_CFG_STATE_OFF2: + default: + break; + } + + return REGULATOR_STATUS_OFF; +} + +static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp = 0; + int val; + + if (!(twl_class_is_6030() && (info->features & TWL6032_SUBCLASS))) + grp = twlreg_grp(rdev); + + if (grp < 0) + return grp; + + /* Compose the state register settings */ + val = grp << TWL6030_CFG_STATE_GRP_SHIFT; + /* We can only set the mode through state machine commands... */ + switch (mode) { + case REGULATOR_MODE_NORMAL: + val |= TWL6030_CFG_STATE_ON; + break; + case REGULATOR_MODE_STANDBY: + val |= TWL6030_CFG_STATE_SLEEP; + break; + + default: + return -EINVAL; + } + + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, val); +} + +static int twl6030coresmps_set_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV, unsigned *selector) +{ + return -ENODEV; +} + +static int twl6030coresmps_get_voltage(struct regulator_dev *rdev) +{ + return -ENODEV; +} + +static struct regulator_ops twl6030coresmps_ops = { + .set_voltage = twl6030coresmps_set_voltage, + .get_voltage = twl6030coresmps_get_voltage, +}; + +static int twl6030ldo_list_voltage(struct regulator_dev *rdev, unsigned sel) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + switch (sel) { + case 0: + return 0; + case 1 ... 24: + /* Linear mapping from 00000001 to 00011000: + * Absolute voltage value = 1.0 V + 0.1 V × (sel – 00000001) + */ + return (info->min_mV + 100 * (sel - 1)) * 1000; + case 25 ... 30: + return -EINVAL; + case 31: + return 2750000; + default: + return -EINVAL; + } +} + +static int +twl6030ldo_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE, + selector); +} + +static int twl6030ldo_get_voltage_sel(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE); + + return vsel; +} + +static struct regulator_ops twl6030ldo_ops = { + .list_voltage = twl6030ldo_list_voltage, + + .set_voltage_sel = twl6030ldo_set_voltage_sel, + .get_voltage_sel = twl6030ldo_get_voltage_sel, + + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, + + .set_mode = twl6030reg_set_mode, + + .get_status = twl6030reg_get_status, +}; + +static struct regulator_ops twl6030fixed_ops = { + .list_voltage = regulator_list_voltage_linear, + + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, + + .set_mode = twl6030reg_set_mode, + + .get_status = twl6030reg_get_status, +}; + +/* + * SMPS status and control + */ + +static int twl6030smps_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + int voltage = 0; + + switch (info->flags) { + case SMPS_OFFSET_EN: + voltage = 100000; + /* fall through */ + case 0: + switch (index) { + case 0: + voltage = 0; + break; + case 58: + voltage = 1350 * 1000; + break; + case 59: + voltage = 1500 * 1000; + break; + case 60: + voltage = 1800 * 1000; + break; + case 61: + voltage = 1900 * 1000; + break; + case 62: + voltage = 2100 * 1000; + break; + default: + voltage += (600000 + (12500 * (index - 1))); + } + break; + case SMPS_EXTENDED_EN: + switch (index) { + case 0: + voltage = 0; + break; + case 58: + voltage = 2084 * 1000; + break; + case 59: + voltage = 2315 * 1000; + break; + case 60: + voltage = 2778 * 1000; + break; + case 61: + voltage = 2932 * 1000; + break; + case 62: + voltage = 3241 * 1000; + break; + default: + voltage = (1852000 + (38600 * (index - 1))); + } + break; + case SMPS_OFFSET_EN | SMPS_EXTENDED_EN: + switch (index) { + case 0: + voltage = 0; + break; + case 58: + voltage = 4167 * 1000; + break; + case 59: + voltage = 2315 * 1000; + break; + case 60: + voltage = 2778 * 1000; + break; + case 61: + voltage = 2932 * 1000; + break; + case 62: + voltage = 3241 * 1000; + break; + default: + voltage = (2161000 + (38600 * (index - 1))); + } + break; + } + + return voltage; +} + +static int twl6030smps_map_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel = 0; + + switch (info->flags) { + case 0: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 600000) && (min_uV <= 1300000)) { + vsel = DIV_ROUND_UP(min_uV - 600000, 12500); + vsel++; + } + /* Values 1..57 for vsel are linear and can be calculated + * values 58..62 are non linear. + */ + else if ((min_uV > 1900000) && (min_uV <= 2100000)) + vsel = 62; + else if ((min_uV > 1800000) && (min_uV <= 1900000)) + vsel = 61; + else if ((min_uV > 1500000) && (min_uV <= 1800000)) + vsel = 60; + else if ((min_uV > 1350000) && (min_uV <= 1500000)) + vsel = 59; + else if ((min_uV > 1300000) && (min_uV <= 1350000)) + vsel = 58; + else + return -EINVAL; + break; + case SMPS_OFFSET_EN: + if (min_uV == 0) + vsel = 0; + else if ((min_uV >= 700000) && (min_uV <= 1420000)) { + vsel = DIV_ROUND_UP(min_uV - 700000, 12500); + vsel++; + } + /* Values 1..57 for vsel are linear and can be calculated + * values 58..62 are non linear. + */ + else if ((min_uV > 1900000) && (min_uV <= 2100000)) + vsel = 62; + else if ((min_uV > 1800000) && (min_uV <= 1900000)) + vsel = 61; + else if ((min_uV > 1350000) && (min_uV <= 1800000)) + vsel = 60; + else if ((min_uV > 1350000) && (min_uV <= 1500000)) + vsel = 59; + else if ((min_uV > 1300000) && (min_uV <= 1350000)) + vsel = 58; + else + return -EINVAL; + break; + case SMPS_EXTENDED_EN: + if (min_uV == 0) { + vsel = 0; + } else if ((min_uV >= 1852000) && (max_uV <= 4013600)) { + vsel = DIV_ROUND_UP(min_uV - 1852000, 38600); + vsel++; + } + break; + case SMPS_OFFSET_EN|SMPS_EXTENDED_EN: + if (min_uV == 0) { + vsel = 0; + } else if ((min_uV >= 2161000) && (min_uV <= 4321000)) { + vsel = DIV_ROUND_UP(min_uV - 2161000, 38600); + vsel++; + } + break; + } + + return vsel; +} + +static int twl6030smps_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS, + selector); +} + +static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS); +} + +static struct regulator_ops twlsmps_ops = { + .list_voltage = twl6030smps_list_voltage, + .map_voltage = twl6030smps_map_voltage, + + .set_voltage_sel = twl6030smps_set_voltage_sel, + .get_voltage_sel = twl6030smps_get_voltage_sel, + + .enable = twl6030reg_enable, + .disable = twl6030reg_disable, + .is_enabled = twl6030reg_is_enabled, + + .set_mode = twl6030reg_set_mode, + + .get_status = twl6030reg_get_status, +}; + +/*----------------------------------------------------------------------*/ + +#define TWL6030_ADJUSTABLE_SMPS(label) \ +static const struct twlreg_info TWL6030_INFO_##label = { \ + .desc = { \ + .name = #label, \ + .id = TWL6030_REG_##label, \ + .ops = &twl6030coresmps_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \ +static const struct twlreg_info TWL6030_INFO_##label = { \ + .base = offset, \ + .min_mV = min_mVolts, \ + .max_mV = max_mVolts, \ + .desc = { \ + .name = #label, \ + .id = TWL6030_REG_##label, \ + .n_voltages = 32, \ + .ops = &twl6030ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define TWL6032_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \ +static const struct twlreg_info TWL6032_INFO_##label = { \ + .base = offset, \ + .min_mV = min_mVolts, \ + .max_mV = max_mVolts, \ + .desc = { \ + .name = #label, \ + .id = TWL6032_REG_##label, \ + .n_voltages = 32, \ + .ops = &twl6030ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \ +static const struct twlreg_info TWLFIXED_INFO_##label = { \ + .base = offset, \ + .id = 0, \ + .min_mV = mVolts, \ + .remap = 0, \ + .desc = { \ + .name = #label, \ + .id = TWL6030##_REG_##label, \ + .n_voltages = 1, \ + .ops = &twl6030fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = mVolts * 1000, \ + .enable_time = turnon_delay, \ + .of_map_mode = NULL, \ + }, \ + } + +#define TWL6032_ADJUSTABLE_SMPS(label, offset) \ +static const struct twlreg_info TWLSMPS_INFO_##label = { \ + .base = offset, \ + .min_mV = 600, \ + .max_mV = 2100, \ + .desc = { \ + .name = #label, \ + .id = TWL6032_REG_##label, \ + .n_voltages = 63, \ + .ops = &twlsmps_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +/* VUSBCP is managed *only* by the USB subchip */ +/* 6030 REG with base as PMC Slave Misc : 0x0030 */ +/* Turnon-delay and remap configuration values for 6030 are not + verified since the specification is not public */ +TWL6030_ADJUSTABLE_SMPS(VDD1); +TWL6030_ADJUSTABLE_SMPS(VDD2); +TWL6030_ADJUSTABLE_SMPS(VDD3); +TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300); +/* 6025 are renamed compared to 6030 versions */ +TWL6032_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300); +TWL6032_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300); +TWL6032_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300); +TWL6032_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300); +TWL6032_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300); +TWL6032_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300); +TWL6032_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300); +TWL6032_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300); +TWL6032_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300); +TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0); +TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0); +TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0); +TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0); +TWL6030_FIXED_LDO(V1V8, 0x16, 1800, 0); +TWL6030_FIXED_LDO(V2V1, 0x1c, 2100, 0); +TWL6032_ADJUSTABLE_SMPS(SMPS3, 0x34); +TWL6032_ADJUSTABLE_SMPS(SMPS4, 0x10); +TWL6032_ADJUSTABLE_SMPS(VIO, 0x16); + +static u8 twl_get_smps_offset(void) +{ + u8 value; + + twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value, + TWL6030_SMPS_OFFSET); + return value; +} + +static u8 twl_get_smps_mult(void) +{ + u8 value; + + twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value, + TWL6030_SMPS_MULT); + return value; +} + +#define TWL_OF_MATCH(comp, family, label) \ + { \ + .compatible = comp, \ + .data = &family##_INFO_##label, \ + } + +#define TWL6030_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWL6030, label) +#define TWL6032_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWL6032, label) +#define TWLFIXED_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWLFIXED, label) +#define TWLSMPS_OF_MATCH(comp, label) TWL_OF_MATCH(comp, TWLSMPS, label) + +static const struct of_device_id twl_of_match[] = { + TWL6030_OF_MATCH("ti,twl6030-vdd1", VDD1), + TWL6030_OF_MATCH("ti,twl6030-vdd2", VDD2), + TWL6030_OF_MATCH("ti,twl6030-vdd3", VDD3), + TWL6030_OF_MATCH("ti,twl6030-vaux1", VAUX1_6030), + TWL6030_OF_MATCH("ti,twl6030-vaux2", VAUX2_6030), + TWL6030_OF_MATCH("ti,twl6030-vaux3", VAUX3_6030), + TWL6030_OF_MATCH("ti,twl6030-vmmc", VMMC), + TWL6030_OF_MATCH("ti,twl6030-vpp", VPP), + TWL6030_OF_MATCH("ti,twl6030-vusim", VUSIM), + TWL6032_OF_MATCH("ti,twl6032-ldo2", LDO2), + TWL6032_OF_MATCH("ti,twl6032-ldo4", LDO4), + TWL6032_OF_MATCH("ti,twl6032-ldo3", LDO3), + TWL6032_OF_MATCH("ti,twl6032-ldo5", LDO5), + TWL6032_OF_MATCH("ti,twl6032-ldo1", LDO1), + TWL6032_OF_MATCH("ti,twl6032-ldo7", LDO7), + TWL6032_OF_MATCH("ti,twl6032-ldo6", LDO6), + TWL6032_OF_MATCH("ti,twl6032-ldoln", LDOLN), + TWL6032_OF_MATCH("ti,twl6032-ldousb", LDOUSB), + TWLFIXED_OF_MATCH("ti,twl6030-vana", VANA), + TWLFIXED_OF_MATCH("ti,twl6030-vcxio", VCXIO), + TWLFIXED_OF_MATCH("ti,twl6030-vdac", VDAC), + TWLFIXED_OF_MATCH("ti,twl6030-vusb", VUSB), + TWLFIXED_OF_MATCH("ti,twl6030-v1v8", V1V8), + TWLFIXED_OF_MATCH("ti,twl6030-v2v1", V2V1), + TWLSMPS_OF_MATCH("ti,twl6032-smps3", SMPS3), + TWLSMPS_OF_MATCH("ti,twl6032-smps4", SMPS4), + TWLSMPS_OF_MATCH("ti,twl6032-vio", VIO), + {}, +}; +MODULE_DEVICE_TABLE(of, twl_of_match); + +static int twlreg_probe(struct platform_device *pdev) +{ + int id; + struct twlreg_info *info; + const struct twlreg_info *template; + struct regulator_init_data *initdata; + struct regulation_constraints *c; + struct regulator_dev *rdev; + const struct of_device_id *match; + struct regulator_config config = { }; + + match = of_match_device(twl_of_match, &pdev->dev); + if (!match) + return -ENODEV; + + template = match->data; + if (!template) + return -ENODEV; + + id = template->desc.id; + initdata = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node, + &template->desc); + if (!initdata) + return -EINVAL; + + info = devm_kmemdup(&pdev->dev, template, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + /* Constrain board-specific capabilities according to what + * this driver and the chip itself can actually do. + */ + c = &initdata->constraints; + c->valid_modes_mask &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY; + c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS; + + switch (id) { + case TWL6032_REG_SMPS3: + if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS3) + info->flags |= SMPS_EXTENDED_EN; + if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS3) + info->flags |= SMPS_OFFSET_EN; + break; + case TWL6032_REG_SMPS4: + if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS4) + info->flags |= SMPS_EXTENDED_EN; + if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS4) + info->flags |= SMPS_OFFSET_EN; + break; + case TWL6032_REG_VIO: + if (twl_get_smps_mult() & SMPS_MULTOFFSET_VIO) + info->flags |= SMPS_EXTENDED_EN; + if (twl_get_smps_offset() & SMPS_MULTOFFSET_VIO) + info->flags |= SMPS_OFFSET_EN; + break; + } + + config.dev = &pdev->dev; + config.init_data = initdata; + config.driver_data = info; + config.of_node = pdev->dev.of_node; + + rdev = devm_regulator_register(&pdev->dev, &info->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "can't register %s, %ld\n", + info->desc.name, PTR_ERR(rdev)); + return PTR_ERR(rdev); + } + platform_set_drvdata(pdev, rdev); + + /* NOTE: many regulators support short-circuit IRQs (presentable + * as REGULATOR_OVER_CURRENT notifications?) configured via: + * - SC_CONFIG + * - SC_DETECT1 (vintana2, vmmc1/2, vaux1/2/3/4) + * - SC_DETECT2 (vusb, vdac, vio, vdd1/2, vpll2) + * - IT_CONFIG + */ + + return 0; +} + +MODULE_ALIAS("platform:twl6030_reg"); + +static struct platform_driver twlreg_driver = { + .probe = twlreg_probe, + /* NOTE: short name, to work around driver model truncation of + * "twl_regulator.12" (and friends) to "twl_regulator.1". + */ + .driver = { + .name = "twl6030_reg", + .of_match_table = of_match_ptr(twl_of_match), + }, +}; + +static int __init twlreg_init(void) +{ + return platform_driver_register(&twlreg_driver); +} +subsys_initcall(twlreg_init); + +static void __exit twlreg_exit(void) +{ + platform_driver_unregister(&twlreg_driver); +} +module_exit(twlreg_exit) + +MODULE_DESCRIPTION("TWL6030 regulator driver"); +MODULE_LICENSE("GPL"); From 15cc95c442859e8b26b59234569d926c246e45f1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 23 Nov 2016 21:13:20 +0800 Subject: [PATCH 16/24] regulator: twl: Remove unused fields from struct twlreg_info The min_mV, max_mV and flags fields are not used, so remove them. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/twl-regulator.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index d572b8a583e4..6c9ec84121bd 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -48,12 +48,6 @@ struct twlreg_info { /* State REMAP default configuration */ u8 remap; - /* chip constraints on regulator behavior */ - u16 min_mV; - u16 max_mV; - - u8 flags; - /* used by regulator core */ struct regulator_desc desc; @@ -497,7 +491,6 @@ static const struct twlreg_info TWL4030_INFO_##label = { \ static const struct twlreg_info TWLFIXED_INFO_##label = { \ .base = offset, \ .id = num, \ - .min_mV = mVolts, \ .remap = remap_conf, \ .desc = { \ .name = #label, \ From 3498c5e15db3af90aee7dbe9a0910225422b85c1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 23 Nov 2016 21:13:21 +0800 Subject: [PATCH 17/24] regulator: twl6030: Remove unused fields from struct twlreg_info The table_len, *table, remap and max_mV fields are not used, so remove them. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/twl6030-regulator.c | 46 ++++++++++----------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/drivers/regulator/twl6030-regulator.c b/drivers/regulator/twl6030-regulator.c index a446137f57bb..4864b9d742c0 100644 --- a/drivers/regulator/twl6030-regulator.c +++ b/drivers/regulator/twl6030-regulator.c @@ -31,16 +31,8 @@ struct twlreg_info { /* twl resource ID, for resource control state machine */ u8 id; - /* voltage in mV = table[VSEL]; table_len must be a power-of-two */ - u8 table_len; - const u16 *table; - - /* State REMAP default configuration */ - u8 remap; - /* chip constraints on regulator behavior */ u16 min_mV; - u16 max_mV; u8 flags; @@ -535,11 +527,10 @@ static const struct twlreg_info TWL6030_INFO_##label = { \ }, \ } -#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \ +#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts) \ static const struct twlreg_info TWL6030_INFO_##label = { \ .base = offset, \ .min_mV = min_mVolts, \ - .max_mV = max_mVolts, \ .desc = { \ .name = #label, \ .id = TWL6030_REG_##label, \ @@ -550,11 +541,10 @@ static const struct twlreg_info TWL6030_INFO_##label = { \ }, \ } -#define TWL6032_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \ +#define TWL6032_ADJUSTABLE_LDO(label, offset, min_mVolts) \ static const struct twlreg_info TWL6032_INFO_##label = { \ .base = offset, \ .min_mV = min_mVolts, \ - .max_mV = max_mVolts, \ .desc = { \ .name = #label, \ .id = TWL6032_REG_##label, \ @@ -570,7 +560,6 @@ static const struct twlreg_info TWLFIXED_INFO_##label = { \ .base = offset, \ .id = 0, \ .min_mV = mVolts, \ - .remap = 0, \ .desc = { \ .name = #label, \ .id = TWL6030##_REG_##label, \ @@ -588,7 +577,6 @@ static const struct twlreg_info TWLFIXED_INFO_##label = { \ static const struct twlreg_info TWLSMPS_INFO_##label = { \ .base = offset, \ .min_mV = 600, \ - .max_mV = 2100, \ .desc = { \ .name = #label, \ .id = TWL6032_REG_##label, \ @@ -606,22 +594,22 @@ static const struct twlreg_info TWLSMPS_INFO_##label = { \ TWL6030_ADJUSTABLE_SMPS(VDD1); TWL6030_ADJUSTABLE_SMPS(VDD2); TWL6030_ADJUSTABLE_SMPS(VDD3); -TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300); -TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300); -TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300); -TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300); -TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300); -TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300); +TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000); +TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000); +TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000); +TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000); +TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000); +TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000); /* 6025 are renamed compared to 6030 versions */ -TWL6032_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300); -TWL6032_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300); +TWL6032_ADJUSTABLE_LDO(LDO2, 0x54, 1000); +TWL6032_ADJUSTABLE_LDO(LDO4, 0x58, 1000); +TWL6032_ADJUSTABLE_LDO(LDO3, 0x5c, 1000); +TWL6032_ADJUSTABLE_LDO(LDO5, 0x68, 1000); +TWL6032_ADJUSTABLE_LDO(LDO1, 0x6c, 1000); +TWL6032_ADJUSTABLE_LDO(LDO7, 0x74, 1000); +TWL6032_ADJUSTABLE_LDO(LDO6, 0x60, 1000); +TWL6032_ADJUSTABLE_LDO(LDOLN, 0x64, 1000); +TWL6032_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000); TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0); TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0); TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0); From 383d0fca7035a12f1201277d33e8fc87c9d60c9a Mon Sep 17 00:00:00 2001 From: Venkat Reddy Talla Date: Thu, 17 Nov 2016 23:24:35 +0530 Subject: [PATCH 18/24] regulator: max77620: add support to configure MPOK Adding support to configure regulator POK mapping bit to control nRST_IO and GPIO1 POK function. In tegra based platform which uses MAX20024 pmic, when some of regulators are configured FPS_NONE(flexible power sequencer) causes PMIC GPIO1 to go low which lead to various other rails turning off, to avoid this MPOK bit of those regulators need to be set to 0 so that PMIC GPIO1 will not go low. Signed-off-by: Venkat Reddy Talla Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/max77620-regulator.c | 46 ++++++++++++++++++++++++++ include/linux/mfd/max77620.h | 2 ++ 2 files changed, 48 insertions(+) diff --git a/drivers/regulator/max77620-regulator.c b/drivers/regulator/max77620-regulator.c index c39a56b41901..d088a7c79e60 100644 --- a/drivers/regulator/max77620-regulator.c +++ b/drivers/regulator/max77620-regulator.c @@ -80,6 +80,7 @@ struct max77620_regulator_pdata { int suspend_fps_pd_slot; int suspend_fps_pu_slot; int current_mode; + int power_ok; int ramp_rate_setting; }; @@ -350,11 +351,48 @@ static int max77620_set_slew_rate(struct max77620_regulator *pmic, int id, return 0; } +static int max77620_config_power_ok(struct max77620_regulator *pmic, int id) +{ + struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id]; + struct max77620_regulator_info *rinfo = pmic->rinfo[id]; + struct max77620_chip *chip = dev_get_drvdata(pmic->dev->parent); + u8 val, mask; + int ret; + + switch (chip->chip_id) { + case MAX20024: + if (rpdata->power_ok >= 0) { + if (rinfo->type == MAX77620_REGULATOR_TYPE_SD) + mask = MAX20024_SD_CFG1_MPOK_MASK; + else + mask = MAX20024_LDO_CFG2_MPOK_MASK; + + val = rpdata->power_ok ? mask : 0; + + ret = regmap_update_bits(pmic->rmap, rinfo->cfg_addr, + mask, val); + if (ret < 0) { + dev_err(pmic->dev, "Reg 0x%02x update failed %d\n", + rinfo->cfg_addr, ret); + return ret; + } + } + break; + + default: + break; + } + + return 0; +} + static int max77620_init_pmic(struct max77620_regulator *pmic, int id) { struct max77620_regulator_pdata *rpdata = &pmic->reg_pdata[id]; int ret; + max77620_config_power_ok(pmic, id); + /* Update power mode */ ret = max77620_regulator_get_power_mode(pmic, id); if (ret < 0) @@ -594,6 +632,12 @@ static int max77620_of_parse_cb(struct device_node *np, np, "maxim,suspend-fps-power-down-slot", &pval); rpdata->suspend_fps_pd_slot = (!ret) ? pval : -1; + ret = of_property_read_u32(np, "maxim,power-ok-control", &pval); + if (!ret) + rpdata->power_ok = pval; + else + rpdata->power_ok = -1; + ret = of_property_read_u32(np, "maxim,ramp-rate-setting", &pval); rpdata->ramp_rate_setting = (!ret) ? pval : 0; @@ -806,6 +850,8 @@ static int max77620_regulator_resume(struct device *dev) for (id = 0; id < MAX77620_NUM_REGS; id++) { reg_pdata = &pmic->reg_pdata[id]; + max77620_config_power_ok(pmic, id); + max77620_regulator_set_fps_slots(pmic, id, false); if (reg_pdata->active_fps_src < 0) continue; diff --git a/include/linux/mfd/max77620.h b/include/linux/mfd/max77620.h index 3ca0af07fc78..ad2a9a852aea 100644 --- a/include/linux/mfd/max77620.h +++ b/include/linux/mfd/max77620.h @@ -180,6 +180,7 @@ #define MAX77620_SD_CFG1_FPWM_SD_MASK BIT(2) #define MAX77620_SD_CFG1_FPWM_SD_SKIP 0 #define MAX77620_SD_CFG1_FPWM_SD_FPWM BIT(2) +#define MAX20024_SD_CFG1_MPOK_MASK BIT(1) #define MAX77620_SD_CFG1_FSRADE_SD_MASK BIT(0) #define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0 #define MAX77620_SD_CFG1_FSRADE_SD_ENABLE BIT(0) @@ -187,6 +188,7 @@ /* LDO_CNFG2 */ #define MAX77620_LDO_POWER_MODE_MASK 0xC0 #define MAX77620_LDO_POWER_MODE_SHIFT 6 +#define MAX20024_LDO_CFG2_MPOK_MASK BIT(2) #define MAX77620_LDO_CFG2_ADE_MASK BIT(1) #define MAX77620_LDO_CFG2_ADE_DISABLE 0 #define MAX77620_LDO_CFG2_ADE_ENABLE BIT(1) From 983779235a4d08f94e8cda073200423e0ff01d2e Mon Sep 17 00:00:00 2001 From: Venkat Reddy Talla Date: Thu, 17 Nov 2016 23:24:36 +0530 Subject: [PATCH 19/24] regulator: max77620: add documentation for MPOK property Adding documentation for maxim,power-ok-control dts property Signed-off-by: Venkat Reddy Talla Acked-by: Rob Herring Acked-by: Lee Jones Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/max77620.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/max77620.txt b/Documentation/devicetree/bindings/mfd/max77620.txt index 2ad44f7e4880..9c16d51cc15b 100644 --- a/Documentation/devicetree/bindings/mfd/max77620.txt +++ b/Documentation/devicetree/bindings/mfd/max77620.txt @@ -106,6 +106,18 @@ Here supported time periods by device in microseconds are as follows: MAX77620 supports 40, 80, 160, 320, 640, 1280, 2560 and 5120 microseconds. MAX20024 supports 20, 40, 80, 160, 320, 640, 1280 and 2540 microseconds. +-maxim,power-ok-control: configure map power ok bit + 1: Enables POK(Power OK) to control nRST_IO and GPIO1 + POK function. + 0: Disables POK control. + if property missing, do not configure MPOK bit. + If POK mapping is enabled for GPIO1/nRST_IO then, + GPIO1/nRST_IO pins are HIGH only if all rails + that have POK control enabled are HIGH. + If any of the rails goes down(which are enabled for POK + control) then, GPIO1/nRST_IO goes LOW. + this property is valid for max20024 only. + For DT binding details of different sub modules like GPIO, pincontrol, regulator, power, please refer respective device-tree binding document under their respective sub-system directories. From 109c75afa1cf7c08015f19e354bed581f29f7a94 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 29 Nov 2016 11:50:03 +0000 Subject: [PATCH 20/24] regulator: core: Correct type of mode in regulator_mode_constrain Every function handling the mode within the regulator core uses an unsigned int for mode, except for regulator_mode_constrain. This patch changes the type of mode within regulator_mode_constrain which fixes several instances where we are passing pointers to unsigned ints then treating them as an int within this function. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 67426c0477d3..b6b3aa8ef5db 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -293,7 +293,8 @@ static int regulator_check_current_limit(struct regulator_dev *rdev, } /* operating mode constraint check */ -static int regulator_mode_constrain(struct regulator_dev *rdev, int *mode) +static int regulator_mode_constrain(struct regulator_dev *rdev, + unsigned int *mode) { switch (*mode) { case REGULATOR_MODE_FAST: From d8ca5bd158f738c4fa6974ee388c381f64db7905 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Thu, 1 Dec 2016 10:44:16 -0600 Subject: [PATCH 21/24] regulator: tps65086: Fix 25mV ranges for BUCK regulators The BUCK regulators 3, 4, and 5 also have a 10mV step mode, adjust the tables and logic to reflect the data-sheet for these regulators. fixes: d2a2e729a666 ("regulator: tps65086: Add regulator driver for the TPS65086 PMIC") Signed-off-by: Andrew F. Davis Signed-off-by: Mark Brown --- .../devicetree/bindings/mfd/tps65086.txt | 2 +- drivers/regulator/tps65086-regulator.c | 54 ++++++++++--------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/Documentation/devicetree/bindings/mfd/tps65086.txt b/Documentation/devicetree/bindings/mfd/tps65086.txt index d3705612a846..9cfa886fe99f 100644 --- a/Documentation/devicetree/bindings/mfd/tps65086.txt +++ b/Documentation/devicetree/bindings/mfd/tps65086.txt @@ -23,7 +23,7 @@ Required properties: defined below. Optional regulator properties: - - ti,regulator-step-size-25mv : This is applicable for buck[1,2,6], set this + - ti,regulator-step-size-25mv : This is applicable for buck[1-6], set this if the regulator is factory set with a 25mv step voltage mapping. - ti,regulator-decay : This is applicable for buck[1-6], set this if diff --git a/drivers/regulator/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c index 33f389d583ef..caf174ffa316 100644 --- a/drivers/regulator/tps65086-regulator.c +++ b/drivers/regulator/tps65086-regulator.c @@ -71,18 +71,17 @@ struct tps65086_regulator { unsigned int decay_mask; }; -static const struct regulator_linear_range tps65086_buck126_10mv_ranges[] = { +static const struct regulator_linear_range tps65086_10mv_ranges[] = { REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0), REGULATOR_LINEAR_RANGE(410000, 0x1, 0x7F, 10000), }; static const struct regulator_linear_range tps65086_buck126_25mv_ranges[] = { - REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0), - REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x18, 0), + REGULATOR_LINEAR_RANGE(1000000, 0x0, 0x18, 0), REGULATOR_LINEAR_RANGE(1025000, 0x19, 0x7F, 25000), }; -static const struct regulator_linear_range tps65086_buck345_ranges[] = { +static const struct regulator_linear_range tps65086_buck345_25mv_ranges[] = { REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0), REGULATOR_LINEAR_RANGE(425000, 0x1, 0x7F, 25000), }; @@ -125,27 +124,27 @@ static int tps65086_of_parse_cb(struct device_node *dev, static struct tps65086_regulator regulators[] = { TPS65086_REGULATOR("BUCK1", "buck1", BUCK1, 0x80, TPS65086_BUCK1CTRL, BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(0), - tps65086_buck126_10mv_ranges, TPS65086_BUCK1CTRL, + tps65086_10mv_ranges, TPS65086_BUCK1CTRL, BIT(0)), TPS65086_REGULATOR("BUCK2", "buck2", BUCK2, 0x80, TPS65086_BUCK2CTRL, BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(1), - tps65086_buck126_10mv_ranges, TPS65086_BUCK2CTRL, + tps65086_10mv_ranges, TPS65086_BUCK2CTRL, BIT(0)), TPS65086_REGULATOR("BUCK3", "buck3", BUCK3, 0x80, TPS65086_BUCK3VID, BUCK_VID_MASK, TPS65086_BUCK123CTRL, BIT(2), - tps65086_buck345_ranges, TPS65086_BUCK3DECAY, + tps65086_10mv_ranges, TPS65086_BUCK3DECAY, BIT(0)), TPS65086_REGULATOR("BUCK4", "buck4", BUCK4, 0x80, TPS65086_BUCK4VID, BUCK_VID_MASK, TPS65086_BUCK4CTRL, BIT(0), - tps65086_buck345_ranges, TPS65086_BUCK4VID, + tps65086_10mv_ranges, TPS65086_BUCK4VID, BIT(0)), TPS65086_REGULATOR("BUCK5", "buck5", BUCK5, 0x80, TPS65086_BUCK5VID, BUCK_VID_MASK, TPS65086_BUCK5CTRL, BIT(0), - tps65086_buck345_ranges, TPS65086_BUCK5CTRL, + tps65086_10mv_ranges, TPS65086_BUCK5CTRL, BIT(0)), TPS65086_REGULATOR("BUCK6", "buck6", BUCK6, 0x80, TPS65086_BUCK6VID, BUCK_VID_MASK, TPS65086_BUCK6CTRL, BIT(0), - tps65086_buck126_10mv_ranges, TPS65086_BUCK6CTRL, + tps65086_10mv_ranges, TPS65086_BUCK6CTRL, BIT(0)), TPS65086_REGULATOR("LDOA1", "ldoa1", LDOA1, 0xF, TPS65086_LDOA1CTRL, VDOA1_VID_MASK, TPS65086_LDOA1CTRL, BIT(0), @@ -162,18 +161,6 @@ static struct tps65086_regulator regulators[] = { TPS65086_SWITCH("VTT", "vtt", VTT, TPS65086_SWVTT_EN, BIT(4)), }; -static inline bool has_25mv_mode(int id) -{ - switch (id) { - case BUCK1: - case BUCK2: - case BUCK6: - return true; - default: - return false; - } -} - static int tps65086_of_parse_cb(struct device_node *dev, const struct regulator_desc *desc, struct regulator_config *config) @@ -181,12 +168,27 @@ static int tps65086_of_parse_cb(struct device_node *dev, int ret; /* Check for 25mV step mode */ - if (has_25mv_mode(desc->id) && - of_property_read_bool(config->of_node, "ti,regulator-step-size-25mv")) { - regulators[desc->id].desc.linear_ranges = + if (of_property_read_bool(config->of_node, "ti,regulator-step-size-25mv")) { + switch (desc->id) { + case BUCK1: + case BUCK2: + case BUCK6: + regulators[desc->id].desc.linear_ranges = tps65086_buck126_25mv_ranges; - regulators[desc->id].desc.n_linear_ranges = + regulators[desc->id].desc.n_linear_ranges = ARRAY_SIZE(tps65086_buck126_25mv_ranges); + break; + case BUCK3: + case BUCK4: + case BUCK5: + regulators[desc->id].desc.linear_ranges = + tps65086_buck345_25mv_ranges; + regulators[desc->id].desc.n_linear_ranges = + ARRAY_SIZE(tps65086_buck345_25mv_ranges); + break; + default: + dev_warn(config->dev, "25mV step mode only valid for BUCK regulators\n"); + } } /* Check for decay mode */ From b2661e983f5b55d4895673bd5eafa14b1e7c8881 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 4 Dec 2016 19:48:11 -0600 Subject: [PATCH 22/24] regulator: core: add newline in debug message This adds a trailing newline to a debug message. Signed-off-by: David Lechner Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b6b3aa8ef5db..cf23b1e2ac4c 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -204,7 +204,7 @@ static struct device_node *of_get_regulator(struct device *dev, const char *supp regnode = of_parse_phandle(dev->of_node, prop_name, 0); if (!regnode) { - dev_dbg(dev, "Looking up %s property in node %s failed", + dev_dbg(dev, "Looking up %s property in node %s failed\n", prop_name, dev->of_node->full_name); return NULL; } From 30103b5b6432a51c3822a26dc340e35d91237f39 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 4 Dec 2016 16:52:31 -0600 Subject: [PATCH 23/24] regulator: Fix regulator_get_error_flags() signature mismatch The function signature of does not match regulator_get_error_flags() when CONFIG_REGULATOR is not defined vs. when it is not defined. This makes both declarations match to prevent compiler errors. Signed-off-by: David Lechner Signed-off-by: Mark Brown --- include/linux/regulator/consumer.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 528eb1f5273e..ea0fffa5faeb 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -498,7 +498,8 @@ static inline unsigned int regulator_get_mode(struct regulator *regulator) return REGULATOR_MODE_NORMAL; } -static inline int regulator_get_error_flags(struct regulator *regulator) +static inline int regulator_get_error_flags(struct regulator *regulator, + unsigned int *flags) { return -EINVAL; } From f8192880a102849d84fe0f5e27bc8625a33e031c Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Mon, 5 Dec 2016 10:21:00 -0600 Subject: [PATCH 24/24] regulator: tps65086: Fix 25mV ranges for BUCK regulators 1, 2, and 6 These regualtors output 0v when vsel is 0. The datasheet will be updated to reflect this. Signed-off-by: Andrew F. Davis Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/regulator/tps65086-regulator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c index caf174ffa316..ecb0371780af 100644 --- a/drivers/regulator/tps65086-regulator.c +++ b/drivers/regulator/tps65086-regulator.c @@ -77,7 +77,8 @@ static const struct regulator_linear_range tps65086_10mv_ranges[] = { }; static const struct regulator_linear_range tps65086_buck126_25mv_ranges[] = { - REGULATOR_LINEAR_RANGE(1000000, 0x0, 0x18, 0), + REGULATOR_LINEAR_RANGE(0, 0x0, 0x0, 0), + REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x18, 0), REGULATOR_LINEAR_RANGE(1025000, 0x19, 0x7F, 25000), };