From 2330b05c095bdeaaf1261c54cd2d4b9127496996 Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Sat, 26 Mar 2016 10:28:13 +0200 Subject: [PATCH 1/5] regulator: twl: Make sure we have access to powerbus before trying to write to it According to the TRM, we need to enable i2c access to powerbus before writing to it. Also, a new write to powerbus should not be attempted if there is a pending transfer. The current code does not implement that functionality and while there are no known problems caused by that, it is better to follow what TRM says. Signed-off-by: Ivaylo Dimitrov Signed-off-by: Mark Brown --- drivers/regulator/twl-regulator.c | 78 +++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 8 deletions(-) diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 955a6fb1355c..aad748b00e1a 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -21,7 +21,7 @@ #include #include #include - +#include /* * The TWL4030/TW5030/TPS659x0/TWL6030 family chips include power management, a @@ -188,6 +188,74 @@ static int twl6030reg_is_enabled(struct regulator_dev *rdev) return grp && (val == TWL6030_CFG_STATE_ON); } +#define PB_I2C_BUSY BIT(0) +#define PB_I2C_BWEN BIT(1) + +/* Wait until buffer empty/ready to send a word on power bus. */ +static int twl4030_wait_pb_ready(void) +{ + + int ret; + int timeout = 10; + u8 val; + + do { + ret = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val, + TWL4030_PM_MASTER_PB_CFG); + if (ret < 0) + return ret; + + if (!(val & PB_I2C_BUSY)) + return 0; + + mdelay(1); + timeout--; + } while (timeout); + + return -ETIMEDOUT; +} + +/* Send a word over the powerbus */ +static int twl4030_send_pb_msg(unsigned msg) +{ + u8 val; + int ret; + + /* save powerbus configuration */ + ret = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &val, + TWL4030_PM_MASTER_PB_CFG); + if (ret < 0) + return ret; + + /* Enable i2c access to powerbus */ + ret = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val | PB_I2C_BWEN, + TWL4030_PM_MASTER_PB_CFG); + if (ret < 0) + return ret; + + ret = twl4030_wait_pb_ready(); + if (ret < 0) + return ret; + + ret = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, msg >> 8, + TWL4030_PM_MASTER_PB_WORD_MSB); + if (ret < 0) + return ret; + + ret = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, msg & 0xff, + TWL4030_PM_MASTER_PB_WORD_LSB); + if (ret < 0) + return ret; + + ret = twl4030_wait_pb_ready(); + if (ret < 0) + return ret; + + /* Restore powerbus configuration */ + return twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val, + TWL_MODULE_PM_MASTER); +} + static int twl4030reg_enable(struct regulator_dev *rdev) { struct twlreg_info *info = rdev_get_drvdata(rdev); @@ -324,13 +392,7 @@ static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode) if (!(status & (P3_GRP_4030 | P2_GRP_4030 | P1_GRP_4030))) return -EACCES; - status = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, - message >> 8, TWL4030_PM_MASTER_PB_WORD_MSB); - if (status < 0) - return status; - - return twl_i2c_write_u8(TWL_MODULE_PM_MASTER, - message & 0xff, TWL4030_PM_MASTER_PB_WORD_LSB); + return twl4030_send_pb_msg(message); } static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode) From 32e5deac3627a508f43806788dafa933b51d5d46 Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Sat, 26 Mar 2016 10:28:15 +0200 Subject: [PATCH 2/5] regulator: twl: Regulator mode should not depend on regulator enabled state When machine constraints are applied, regulator framework first sets initial mode (if any) and then enables the regulator if needed. The current code in twl4030reg_set_mode always checks if the regulator is enabled before applying the mode. That results in -EACCES error returned for "always-on" regulators which have "initial-mode" set in the board DTS. Fix that by removing the unneeded check. Signed-off-by: Ivaylo Dimitrov Signed-off-by: Mark Brown --- drivers/regulator/twl-regulator.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index aad748b00e1a..7355616194ab 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -371,7 +371,6 @@ static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode) { struct twlreg_info *info = rdev_get_drvdata(rdev); unsigned message; - int status; /* We can only set the mode through state machine commands... */ switch (mode) { @@ -385,13 +384,6 @@ static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode) return -EINVAL; } - /* Ensure the resource is associated with some group */ - status = twlreg_grp(rdev); - if (status < 0) - return status; - if (!(status & (P3_GRP_4030 | P2_GRP_4030 | P1_GRP_4030))) - return -EACCES; - return twl4030_send_pb_msg(message); } From a221f95ef4257a48c4f8cba8e804431ab66a359d Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Tue, 5 Apr 2016 08:59:34 +0300 Subject: [PATCH 3/5] regulator: twl: Provide of_map_mode for twl4030 of_map_mode is needed so to be possible to set initial regulators mode from the board DTS. Otherwise, for DT boot, regulators are left in their default state after reset/reboot. Document device specific modes as well. Signed-off-by: Ivaylo Dimitrov Signed-off-by: Mark Brown --- .../bindings/regulator/twl-regulator.txt | 6 +++++ drivers/regulator/twl-regulator.c | 22 ++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/twl-regulator.txt b/Documentation/devicetree/bindings/regulator/twl-regulator.txt index 75b0c1669504..74a91c4f8530 100644 --- a/Documentation/devicetree/bindings/regulator/twl-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/twl-regulator.txt @@ -57,6 +57,12 @@ For twl4030 regulators/LDOs Optional properties: - Any optional property defined in bindings/regulator/regulator.txt +For twl4030 regulators/LDOs: + - regulator-initial-mode: + - 0x08 - Sleep mode, the nominal output voltage is maintained with low power + consumption with low load current capability. + - 0x0e - Active mode, the regulator can deliver its nominal output voltage + with full-load current capability. Example: diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 7355616194ab..11c6a5c98c46 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -387,6 +387,18 @@ static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode) return twl4030_send_pb_msg(message); } +static inline unsigned int twl4030reg_map_mode(unsigned int mode) +{ + switch (mode) { + case RES_STATE_ACTIVE: + return REGULATOR_MODE_NORMAL; + case RES_STATE_SLEEP: + return REGULATOR_MODE_STANDBY; + default: + return -EINVAL; + } +} + static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode) { struct twlreg_info *info = rdev_get_drvdata(rdev); @@ -889,10 +901,11 @@ 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) + 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) + 0x0, TWL6030, twl6030fixed_ops, 0x0) #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) \ static const struct twlreg_info TWL4030_INFO_##label = { \ @@ -909,6 +922,7 @@ static const struct twlreg_info TWL4030_INFO_##label = { \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ .enable_time = turnon_delay, \ + .of_map_mode = twl4030reg_map_mode, \ }, \ } @@ -924,6 +938,7 @@ static const struct twlreg_info TWL4030_INFO_##label = { \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ .enable_time = turnon_delay, \ + .of_map_mode = twl4030reg_map_mode, \ }, \ } @@ -969,7 +984,7 @@ static const struct twlreg_info TWL6032_INFO_##label = { \ } #define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \ - family, operations) \ + family, operations, map_mode) \ static const struct twlreg_info TWLFIXED_INFO_##label = { \ .base = offset, \ .id = num, \ @@ -984,6 +999,7 @@ static const struct twlreg_info TWLFIXED_INFO_##label = { \ .owner = THIS_MODULE, \ .min_uV = mVolts * 1000, \ .enable_time = turnon_delay, \ + .of_map_mode = map_mode, \ }, \ } From 74d8b45fa344129b3dfd37019877ba68b1287e18 Mon Sep 17 00:00:00 2001 From: Ivaylo Dimitrov Date: Wed, 6 Apr 2016 09:06:03 +0300 Subject: [PATCH 4/5] regulator: twl: Fix a typo in twl4030_send_pb_msg Commit <2330b05c095bdeaaf1261c54cd2d4b9127496996> ("regulator: twl: Make sure we have access to powerbus before trying to write to it") has implemented the needed logic to correctly access powerbus through i2c, however it brought a typo when powerbus configuration is restored, which results in writing to a wrong register. Fix that by providing the correct register value. Signed-off-by: Ivaylo Dimitrov Signed-off-by: Mark Brown --- drivers/regulator/twl-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 11c6a5c98c46..faeb5ee92c9e 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -253,7 +253,7 @@ static int twl4030_send_pb_msg(unsigned msg) /* Restore powerbus configuration */ return twl_i2c_write_u8(TWL_MODULE_PM_MASTER, val, - TWL_MODULE_PM_MASTER); + TWL4030_PM_MASTER_PB_CFG); } static int twl4030reg_enable(struct regulator_dev *rdev) From ae714c3b8e50a64d967188f3bc585bcd6a79539d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Apr 2016 18:19:08 +0100 Subject: [PATCH 5/5] regulator: tps6524x: Fix broken use of spi_dev_get() The tps6524x driver uses spi_dev_get() to take a copy of the SPI device it uses but has no obvious reason to do so and never calls spi_dev_put() to release the reference. Fix this to just a straight copy. Signed-off-by: Mark Brown --- drivers/regulator/tps6524x-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c index 9d6ea3a4dccd..67cac2682f50 100644 --- a/drivers/regulator/tps6524x-regulator.c +++ b/drivers/regulator/tps6524x-regulator.c @@ -600,7 +600,7 @@ static int pmic_probe(struct spi_device *spi) memset(hw, 0, sizeof(struct tps6524x)); hw->dev = dev; - hw->spi = spi_dev_get(spi); + hw->spi = spi; mutex_init(&hw->lock); for (i = 0; i < N_REGULATORS; i++, info++, init_data++) {