regulator: qcom-spmi: Add vendor specific configuration
authorStephen Boyd <sboyd@codeaurora.org>
Fri, 17 Jul 2015 21:41:55 +0000 (14:41 -0700)
committerMark Brown <broonie@kernel.org>
Fri, 24 Jul 2015 17:29:45 +0000 (18:29 +0100)
Add support for over current protection (OCP), pin control
selection, soft start strength, and auto-mode.

Cc: <devicetree@vger.kernel.org>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt
drivers/regulator/qcom_spmi-regulator.c

index 75b4604bad07c047dc817ef19305b1e5c68d7be1..d00bfd8624a500eb797aac8273f8fc5f1df3c785 100644 (file)
@@ -91,13 +91,65 @@ see regulator.txt - with additional custom properties described below:
 - regulator-initial-mode:
        Usage: optional
        Value type: <u32>
-       Descrption: 1 = Set initial mode to high power mode (HPM), also referred
-                   to as NPM.  HPM consumes more ground current than LPM, but
+       Description: 2 = Set initial mode to auto mode (automatically select
+                   between HPM and LPM); not available on boost type
+                   regulators.
+
+                   1 = Set initial mode to high power mode (HPM), also referred
+                   to as NPM. HPM consumes more ground current than LPM, but
                    it can source significantly higher load current. HPM is not
                    available on boost type regulators. For voltage switch type
                    regulators, HPM implies that over current protection and
-                   soft start are active all the time. 0 = Set initial mode to
-                   low power mode (LPM).
+                   soft start are active all the time.
+
+                   0 = Set initial mode to low power mode (LPM).
+
+- qcom,ocp-max-retries:
+       Usage: optional
+       Value type: <u32>
+       Description: Maximum number of times to try toggling a voltage switch
+                    off and back on as a result of consecutive over current
+                    events.
+
+- qcom,ocp-retry-delay:
+       Usage: optional
+       Value type: <u32>
+       Description: Time to delay in milliseconds between each voltage switch
+                    toggle after an over current event takes place.
+
+- qcom,pin-ctrl-enable:
+       Usage: optional
+       Value type: <u32>
+       Description: Bit mask specifying which hardware pins should be used to
+                    enable the regulator, if any; supported bits are:
+                       0 = ignore all hardware enable signals
+                       BIT(0) = follow HW0_EN signal
+                       BIT(1) = follow HW1_EN signal
+                       BIT(2) = follow HW2_EN signal
+                       BIT(3) = follow HW3_EN signal
+
+- qcom,pin-ctrl-hpm:
+       Usage: optional
+       Value type: <u32>
+       Description: Bit mask specifying which hardware pins should be used to
+                    force the regulator into high power mode, if any;
+                    supported bits are:
+                       0 = ignore all hardware enable signals
+                       BIT(0) = follow HW0_EN signal
+                       BIT(1) = follow HW1_EN signal
+                       BIT(2) = follow HW2_EN signal
+                       BIT(3) = follow HW3_EN signal
+                       BIT(4) = follow PMIC awake state
+
+- qcom,vs-soft-start-strength:
+       Usage: optional
+       Value type: <u32>
+       Description: This property sets the soft start strength for voltage
+                    switch type regulators; supported values are:
+                       0 = 0.05 uA
+                       1 = 0.25 uA
+                       2 = 0.55 uA
+                       3 = 0.75 uA
 
 Example:
 
index 9ef0e2f28ec4bd61d5f7f14559e5998f1b0e0af7..88a5dc88badc7e0c72b1deb770dd1128a8cfa661 100644 (file)
 #include <linux/regmap.h>
 #include <linux/list.h>
 
+/* Pin control enable input pins. */
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_NONE            0x00
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN0             0x01
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN1             0x02
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN2             0x04
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_EN3             0x08
+#define SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT      0x10
+
+/* Pin control high power mode input pins. */
+#define SPMI_REGULATOR_PIN_CTRL_HPM_NONE               0x00
+#define SPMI_REGULATOR_PIN_CTRL_HPM_EN0                        0x01
+#define SPMI_REGULATOR_PIN_CTRL_HPM_EN1                        0x02
+#define SPMI_REGULATOR_PIN_CTRL_HPM_EN2                        0x04
+#define SPMI_REGULATOR_PIN_CTRL_HPM_EN3                        0x08
+#define SPMI_REGULATOR_PIN_CTRL_HPM_SLEEP_B            0x10
+#define SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT         0x20
+
+/*
+ * Used with enable parameters to specify that hardware default register values
+ * should be left unaltered.
+ */
+#define SPMI_REGULATOR_USE_HW_DEFAULT                  2
+
+/* Soft start strength of a voltage switch type regulator */
+enum spmi_vs_soft_start_str {
+       SPMI_VS_SOFT_START_STR_0P05_UA = 0,
+       SPMI_VS_SOFT_START_STR_0P25_UA,
+       SPMI_VS_SOFT_START_STR_0P55_UA,
+       SPMI_VS_SOFT_START_STR_0P75_UA,
+       SPMI_VS_SOFT_START_STR_HW_DEFAULT,
+};
+
+/**
+ * struct spmi_regulator_init_data - spmi-regulator initialization data
+ * @pin_ctrl_enable:        Bit mask specifying which hardware pins should be
+ *                             used to enable the regulator, if any
+ *                         Value should be an ORing of
+ *                             SPMI_REGULATOR_PIN_CTRL_ENABLE_* constants.  If
+ *                             the bit specified by
+ *                             SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT is
+ *                             set, then pin control enable hardware registers
+ *                             will not be modified.
+ * @pin_ctrl_hpm:           Bit mask specifying which hardware pins should be
+ *                             used to force the regulator into high power
+ *                             mode, if any
+ *                         Value should be an ORing of
+ *                             SPMI_REGULATOR_PIN_CTRL_HPM_* constants.  If
+ *                             the bit specified by
+ *                             SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT is
+ *                             set, then pin control mode hardware registers
+ *                             will not be modified.
+ * @vs_soft_start_strength: This parameter sets the soft start strength for
+ *                             voltage switch type regulators.  Its value
+ *                             should be one of SPMI_VS_SOFT_START_STR_*.  If
+ *                             its value is SPMI_VS_SOFT_START_STR_HW_DEFAULT,
+ *                             then the soft start strength will be left at its
+ *                             default hardware value.
+ */
+struct spmi_regulator_init_data {
+       unsigned                                pin_ctrl_enable;
+       unsigned                                pin_ctrl_hpm;
+       enum spmi_vs_soft_start_str             vs_soft_start_strength;
+};
+
 /* These types correspond to unique register layouts. */
 enum spmi_regulator_logical_type {
        SPMI_REGULATOR_LOGICAL_TYPE_SMPS,
@@ -458,6 +522,14 @@ static int spmi_regulator_vs_enable(struct regulator_dev *rdev)
        return spmi_regulator_common_enable(rdev);
 }
 
+static int spmi_regulator_vs_ocp(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 reg = SPMI_VS_OCP_OVERRIDE;
+
+       return spmi_vreg_write(vreg, SPMI_VS_REG_OCP, &reg, 1);
+}
+
 static int spmi_regulator_common_disable(struct regulator_dev *rdev)
 {
        struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
@@ -791,6 +863,9 @@ static unsigned int spmi_regulator_common_get_mode(struct regulator_dev *rdev)
        if (reg & SPMI_COMMON_MODE_HPM_MASK)
                return REGULATOR_MODE_NORMAL;
 
+       if (reg & SPMI_COMMON_MODE_AUTO_MASK)
+               return REGULATOR_MODE_FAST;
+
        return REGULATOR_MODE_IDLE;
 }
 
@@ -798,11 +873,13 @@ static int
 spmi_regulator_common_set_mode(struct regulator_dev *rdev, unsigned int mode)
 {
        struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
-       u8 mask = SPMI_COMMON_MODE_HPM_MASK;
+       u8 mask = SPMI_COMMON_MODE_HPM_MASK | SPMI_COMMON_MODE_AUTO_MASK;
        u8 val = 0;
 
        if (mode == REGULATOR_MODE_NORMAL)
-               val = mask;
+               val = SPMI_COMMON_MODE_HPM_MASK;
+       else if (mode == REGULATOR_MODE_FAST)
+               val = SPMI_COMMON_MODE_AUTO_MASK;
 
        return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
 }
@@ -972,6 +1049,7 @@ static struct regulator_ops spmi_vs_ops = {
        .is_enabled             = spmi_regulator_common_is_enabled,
        .set_pull_down          = spmi_regulator_common_set_pull_down,
        .set_soft_start         = spmi_regulator_common_set_soft_start,
+       .set_over_current_protection = spmi_regulator_vs_ocp,
 };
 
 static struct regulator_ops spmi_boost_ops = {
@@ -1202,10 +1280,111 @@ static int spmi_regulator_ftsmps_init_slew_rate(struct spmi_regulator *vreg)
        return ret;
 }
 
+static int spmi_regulator_init_registers(struct spmi_regulator *vreg,
+                               const struct spmi_regulator_init_data *data)
+{
+       int ret;
+       enum spmi_regulator_logical_type type;
+       u8 ctrl_reg[8], reg, mask;
+
+       type = vreg->logical_type;
+
+       ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, 8);
+       if (ret)
+               return ret;
+
+       /* Set up enable pin control. */
+       if ((type == SPMI_REGULATOR_LOGICAL_TYPE_SMPS
+            || type == SPMI_REGULATOR_LOGICAL_TYPE_LDO
+            || type == SPMI_REGULATOR_LOGICAL_TYPE_VS)
+           && !(data->pin_ctrl_enable
+                       & SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT)) {
+               ctrl_reg[SPMI_COMMON_IDX_ENABLE] &=
+                       ~SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK;
+               ctrl_reg[SPMI_COMMON_IDX_ENABLE] |=
+                   data->pin_ctrl_enable & SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK;
+       }
+
+       /* Set up mode pin control. */
+       if ((type == SPMI_REGULATOR_LOGICAL_TYPE_SMPS
+           || type == SPMI_REGULATOR_LOGICAL_TYPE_LDO)
+               && !(data->pin_ctrl_hpm
+                       & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+               ctrl_reg[SPMI_COMMON_IDX_MODE] &=
+                       ~SPMI_COMMON_MODE_FOLLOW_ALL_MASK;
+               ctrl_reg[SPMI_COMMON_IDX_MODE] |=
+                       data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_ALL_MASK;
+       }
+
+       if (type == SPMI_REGULATOR_LOGICAL_TYPE_VS
+          && !(data->pin_ctrl_hpm & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+               ctrl_reg[SPMI_COMMON_IDX_MODE] &=
+                       ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK;
+               ctrl_reg[SPMI_COMMON_IDX_MODE] |=
+                      data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK;
+       }
+
+       if ((type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS
+               || type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS
+               || type == SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO)
+               && !(data->pin_ctrl_hpm
+                       & SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+               ctrl_reg[SPMI_COMMON_IDX_MODE] &=
+                       ~SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK;
+               ctrl_reg[SPMI_COMMON_IDX_MODE] |=
+                      data->pin_ctrl_hpm & SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK;
+       }
+
+       /* Write back any control register values that were modified. */
+       ret = spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, 8);
+       if (ret)
+               return ret;
+
+       /* Set soft start strength and over current protection for VS. */
+       if (type == SPMI_REGULATOR_LOGICAL_TYPE_VS) {
+               if (data->vs_soft_start_strength
+                               != SPMI_VS_SOFT_START_STR_HW_DEFAULT) {
+                       reg = data->vs_soft_start_strength
+                               & SPMI_VS_SOFT_START_SEL_MASK;
+                       mask = SPMI_VS_SOFT_START_SEL_MASK;
+                       return spmi_vreg_update_bits(vreg,
+                                                    SPMI_VS_REG_SOFT_START,
+                                                    reg, mask);
+               }
+       }
+
+       return 0;
+}
+
+static void spmi_regulator_get_dt_config(struct spmi_regulator *vreg,
+               struct device_node *node, struct spmi_regulator_init_data *data)
+{
+       /*
+        * Initialize configuration parameters to use hardware default in case
+        * no value is specified via device tree.
+        */
+       data->pin_ctrl_enable       = SPMI_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT;
+       data->pin_ctrl_hpm          = SPMI_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT;
+       data->vs_soft_start_strength    = SPMI_VS_SOFT_START_STR_HW_DEFAULT;
+
+       /* These bindings are optional, so it is okay if they aren't found. */
+       of_property_read_u32(node, "qcom,ocp-max-retries",
+               &vreg->ocp_max_retries);
+       of_property_read_u32(node, "qcom,ocp-retry-delay",
+               &vreg->ocp_retry_delay_ms);
+       of_property_read_u32(node, "qcom,pin-ctrl-enable",
+               &data->pin_ctrl_enable);
+       of_property_read_u32(node, "qcom,pin-ctrl-hpm", &data->pin_ctrl_hpm);
+       of_property_read_u32(node, "qcom,vs-soft-start-strength",
+               &data->vs_soft_start_strength);
+}
+
 static unsigned int spmi_regulator_of_map_mode(unsigned int mode)
 {
-       if (mode)
+       if (mode == 1)
                return REGULATOR_MODE_NORMAL;
+       if (mode == 2)
+               return REGULATOR_MODE_FAST;
 
        return REGULATOR_MODE_IDLE;
 }
@@ -1214,12 +1393,23 @@ static int spmi_regulator_of_parse(struct device_node *node,
                            const struct regulator_desc *desc,
                            struct regulator_config *config)
 {
+       struct spmi_regulator_init_data data = { };
        struct spmi_regulator *vreg = config->driver_data;
        struct device *dev = config->dev;
        int ret;
 
-       vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES;
-       vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS;
+       spmi_regulator_get_dt_config(vreg, node, &data);
+
+       if (!vreg->ocp_max_retries)
+               vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES;
+       if (!vreg->ocp_retry_delay_ms)
+               vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS;
+
+       ret = spmi_regulator_init_registers(vreg, &data);
+       if (ret) {
+               dev_err(dev, "common initialization failed, ret=%d\n", ret);
+               return ret;
+       }
 
        if (vreg->logical_type == SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS) {
                ret = spmi_regulator_ftsmps_init_slew_rate(vreg);