|
@@ -26,6 +26,70 @@
|
|
|
#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, ®, 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);
|