regulator: tps65090: Make FETs more reliable by adding retries
[cascardo/linux.git] / drivers / regulator / tps65090-regulator.c
index 2e92ef6..2057e2e 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
 #include <linux/regulator/of_regulator.h>
 #include <linux/mfd/tps65090.h>
 
+#define MAX_CTRL_READ_TRIES    5
+#define MAX_FET_ENABLE_TRIES   1000
+
+#define CTRL_EN_BIT            0 /* Regulator enable bit, active high */
+#define CTRL_WT_BIT            2 /* Regulator wait time 0 bit */
+#define CTRL_PG_BIT            4 /* Regulator power good bit, 1=good */
+#define CTRL_TO_BIT            7 /* Regulator timeout bit, 1=wait */
+
+#define MAX_OVERCURRENT_WAIT   3 /* Overcurrent wait must be <= this */
+
+/**
+ * struct tps65090_regulator - Per-regulator data for a tps65090 regulator
+ *
+ * @dev: Pointer to our device.
+ * @desc: The struct regulator_desc for the regulator.
+ * @rdev: The struct regulator_dev for the regulator.
+ * @overcurrent_wait_valid: True if overcurrent_wait is valid.
+ * @overcurrent_wait: For FETs, the value to put in the WTFET bitfield.
+ */
+
 struct tps65090_regulator {
        struct device           *dev;
        struct regulator_desc   *desc;
        struct regulator_dev    *rdev;
+       bool                    overcurrent_wait_valid;
+       int                     overcurrent_wait;
 };
 
 static struct regulator_ops tps65090_ext_control_ops = {
 };
 
-static struct regulator_ops tps65090_reg_contol_ops = {
+/**
+ * tps65090_reg_set_overcurrent_wait - Setup overcurrent wait
+ *
+ * This will set the overcurrent wait time based on what's in the regulator
+ * info.
+ *
+ * @ri:                Overall regulator data
+ * @rdev:      Regulator device
+ *
+ * Return: 0 if no error, non-zero if there was an error writing the register.
+ */
+static int tps65090_reg_set_overcurrent_wait(struct tps65090_regulator *ri,
+                                            struct regulator_dev *rdev)
+{
+       int ret;
+
+       ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+                                MAX_OVERCURRENT_WAIT << CTRL_WT_BIT,
+                                ri->overcurrent_wait << CTRL_WT_BIT);
+       if (ret) {
+               dev_err(&rdev->dev, "Error updating overcurrent wait %#x\n",
+                       rdev->desc->enable_reg);
+       }
+
+       return ret;
+}
+
+/**
+ * tps65090_try_enable_fet - Try to enable a FET
+ *
+ * @rdev:      Regulator device
+ *
+ * Return: 0 if ok, -ENOTRECOVERABLE if the FET power good bit did not get
+ * set, or some other -ve value if another error occurred (e.g. i2c error)
+ */
+static int tps65090_try_enable_fet(struct regulator_dev *rdev)
+{
+       unsigned int control;
+       int ret, i;
+
+       ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+                                rdev->desc->enable_mask,
+                                rdev->desc->enable_mask);
+       if (ret < 0) {
+               dev_err(&rdev->dev, "Error in updating reg %#x\n",
+                       rdev->desc->enable_reg);
+               return ret;
+       }
+
+       for (i = 0; i < MAX_CTRL_READ_TRIES; i++) {
+               ret = regmap_read(rdev->regmap, rdev->desc->enable_reg,
+                                 &control);
+               if (ret < 0)
+                       return ret;
+
+               if (!(control & BIT(CTRL_TO_BIT)))
+                       break;
+
+               usleep_range(1000, 1500);
+       }
+       if (!(control & BIT(CTRL_PG_BIT)))
+               return -ENOTRECOVERABLE;
+
+       return 0;
+}
+
+/**
+ * tps65090_fet_enable - Enable a FET, trying a few times if it fails
+ *
+ * Some versions of the tps65090 have issues when turning on the FETs.
+ * This function goes through several steps to ensure the best chance of the
+ * FET going on.  Specifically:
+ * - We'll make sure that we bump the "overcurrent wait" to the maximum, which
+ *   increases the chances that we'll turn on properly.
+ * - We'll retry turning the FET on multiple times (turning off in between).
+ *
+ * @rdev:      Regulator device
+ *
+ * Return: 0 if ok, non-zero if it fails.
+ */
+static int tps65090_fet_enable(struct regulator_dev *rdev)
+{
+       int ret, tries;
+
+       /*
+        * Try enabling multiple times until we succeed since sometimes the
+        * first try times out.
+        */
+       tries = 0;
+       while (true) {
+               ret = tps65090_try_enable_fet(rdev);
+               if (!ret)
+                       break;
+               if (ret != -ENOTRECOVERABLE || tries == MAX_FET_ENABLE_TRIES)
+                       goto err;
+
+               /* Try turning the FET off (and then on again) */
+               ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+                                        rdev->desc->enable_mask, 0);
+               if (ret)
+                       goto err;
+
+               tries++;
+       }
+
+       if (tries)
+               dev_warn(&rdev->dev, "reg %#x enable ok after %d tries\n",
+                        rdev->desc->enable_reg, tries);
+
+       return 0;
+err:
+       dev_warn(&rdev->dev, "reg %#x enable failed\n", rdev->desc->enable_reg);
+       WARN_ON(1);
+
+       return ret;
+}
+
+static struct regulator_ops tps65090_reg_control_ops = {
        .enable         = regulator_enable_regmap,
        .disable        = regulator_disable_regmap,
        .is_enabled     = regulator_is_enabled_regmap,
 };
 
+static struct regulator_ops tps65090_fet_control_ops = {
+       .enable         = tps65090_fet_enable,
+       .disable        = regulator_disable_regmap,
+       .is_enabled     = regulator_is_enabled_regmap,
+};
+
 static struct regulator_ops tps65090_ldo_ops = {
 };
 
-#define tps65090_REG_DESC(_id, _sname, _en_reg, _ops)  \
+#define tps65090_REG_DESC(_id, _sname, _en_reg, _en_bits, _ops)        \
 {                                                      \
        .name = "TPS65090_RAILS"#_id,                   \
        .supply_name = _sname,                          \
        .id = TPS65090_REGULATOR_##_id,                 \
        .ops = &_ops,                                   \
        .enable_reg = _en_reg,                          \
-       .enable_mask = BIT(0),                          \
+       .enable_val = _en_bits,                         \
+       .enable_mask = _en_bits,                        \
        .type = REGULATOR_VOLTAGE,                      \
        .owner = THIS_MODULE,                           \
 }
 
 static struct regulator_desc tps65090_regulator_desc[] = {
-       tps65090_REG_DESC(DCDC1, "vsys1",   0x0C, tps65090_reg_contol_ops),
-       tps65090_REG_DESC(DCDC2, "vsys2",   0x0D, tps65090_reg_contol_ops),
-       tps65090_REG_DESC(DCDC3, "vsys3",   0x0E, tps65090_reg_contol_ops),
-       tps65090_REG_DESC(FET1,  "infet1",  0x0F, tps65090_reg_contol_ops),
-       tps65090_REG_DESC(FET2,  "infet2",  0x10, tps65090_reg_contol_ops),
-       tps65090_REG_DESC(FET3,  "infet3",  0x11, tps65090_reg_contol_ops),
-       tps65090_REG_DESC(FET4,  "infet4",  0x12, tps65090_reg_contol_ops),
-       tps65090_REG_DESC(FET5,  "infet5",  0x13, tps65090_reg_contol_ops),
-       tps65090_REG_DESC(FET6,  "infet6",  0x14, tps65090_reg_contol_ops),
-       tps65090_REG_DESC(FET7,  "infet7",  0x15, tps65090_reg_contol_ops),
-       tps65090_REG_DESC(LDO1,  "vsys-l1", 0,    tps65090_ldo_ops),
-       tps65090_REG_DESC(LDO2,  "vsys-l2", 0,    tps65090_ldo_ops),
+       tps65090_REG_DESC(DCDC1, "vsys1",   0x0C, BIT(CTRL_EN_BIT),
+                         tps65090_reg_control_ops),
+       tps65090_REG_DESC(DCDC2, "vsys2",   0x0D, BIT(CTRL_EN_BIT),
+                         tps65090_reg_control_ops),
+       tps65090_REG_DESC(DCDC3, "vsys3",   0x0E, BIT(CTRL_EN_BIT),
+                         tps65090_reg_control_ops),
+
+       tps65090_REG_DESC(FET1,  "infet1",  0x0F,
+                         BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+                         tps65090_fet_control_ops),
+       tps65090_REG_DESC(FET2,  "infet2",  0x10,
+                         BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+                         tps65090_fet_control_ops),
+       tps65090_REG_DESC(FET3,  "infet3",  0x11,
+                         BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+                         tps65090_fet_control_ops),
+       tps65090_REG_DESC(FET4,  "infet4",  0x12,
+                         BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+                         tps65090_fet_control_ops),
+       tps65090_REG_DESC(FET5,  "infet5",  0x13,
+                         BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+                         tps65090_fet_control_ops),
+       tps65090_REG_DESC(FET6,  "infet6",  0x14,
+                         BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+                         tps65090_fet_control_ops),
+       tps65090_REG_DESC(FET7,  "infet7",  0x15,
+                         BIT(CTRL_EN_BIT) | BIT(CTRL_PG_BIT),
+                         tps65090_fet_control_ops),
+
+       tps65090_REG_DESC(LDO1,  "vsys-l1", 0, 0,
+                         tps65090_ldo_ops),
+       tps65090_REG_DESC(LDO2,  "vsys-l2", 0, 0,
+                         tps65090_ldo_ops),
 };
 
 static inline bool is_dcdc(int id)
@@ -209,6 +377,11 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data(
                        rpdata->gpio = of_get_named_gpio(np,
                                        "dcdc-ext-control-gpios", 0);
 
+               if (of_property_read_u32(tps65090_matches[idx].of_node,
+                                        "ti,overcurrent-wait",
+                                        &rpdata->overcurrent_wait) == 0)
+                       rpdata->overcurrent_wait_valid = true;
+
                tps65090_pdata->reg_pdata[idx] = rpdata;
        }
        return tps65090_pdata;
@@ -258,6 +431,8 @@ static int tps65090_regulator_probe(struct platform_device *pdev)
                ri = &pmic[num];
                ri->dev = &pdev->dev;
                ri->desc = &tps65090_regulator_desc[num];
+               ri->overcurrent_wait_valid = tps_pdata->overcurrent_wait_valid;
+               ri->overcurrent_wait = tps_pdata->overcurrent_wait;
 
                /*
                 * TPS5090 DCDC support the control from external digital input.
@@ -299,6 +474,12 @@ static int tps65090_regulator_probe(struct platform_device *pdev)
                }
                ri->rdev = rdev;
 
+               if (ri->overcurrent_wait_valid) {
+                       ret = tps65090_reg_set_overcurrent_wait(ri, rdev);
+                       if (ret < 0)
+                               return ret;
+               }
+
                /* Enable external control if it is require */
                if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data &&
                                tps_pdata->enable_ext_control) {