feat(stpmic1): add new services
Add support for ICC, sink mode, bypass mode,
active discharge and list voltages.
Handle LDO3 sink source mode in a different way to avoid
setting voltage while in sink source mode.
Change-Id: Ib1b909fd8a153f542917f650e43e24317a570534
Signed-off-by: Pascal Paillet <p.paillet@st.com>
diff --git a/drivers/st/pmic/stpmic1.c b/drivers/st/pmic/stpmic1.c
index 714d532..37eb50b 100644
--- a/drivers/st/pmic/stpmic1.c
+++ b/drivers/st/pmic/stpmic1.c
@@ -23,10 +23,17 @@
uint8_t pull_down;
uint8_t mask_reset_reg;
uint8_t mask_reset;
+ uint8_t icc_reg;
+ uint8_t icc_mask;
};
static struct i2c_handle_s *pmic_i2c_handle;
static uint16_t pmic_i2c_addr;
+/*
+ * Special mode corresponds to LDO3 in sink source mode or in bypass mode.
+ * LDO3 doesn't switch back from special to normal mode.
+ */
+static bool ldo3_special_mode;
/* Voltage tables in mV */
static const uint16_t buck1_voltage_table[] = {
@@ -347,10 +354,13 @@
3300,
3300,
3300,
- 500,
- 0xFFFF, /* VREFDDR */
};
+/* Special mode table is used for sink source OR bypass mode */
+static const uint16_t ldo3_special_mode_table[] = {
+ 0,
+};
+
static const uint16_t ldo5_voltage_table[] = {
1700,
1700,
@@ -438,6 +448,8 @@
.pull_down = BUCK1_PULL_DOWN_SHIFT,
.mask_reset_reg = MASK_RESET_BUCK_REG,
.mask_reset = BUCK1_MASK_RESET,
+ .icc_reg = BUCK_ICC_TURNOFF_REG,
+ .icc_mask = BUCK1_ICC_SHIFT,
},
{
.dt_node_name = "buck2",
@@ -450,6 +462,8 @@
.pull_down = BUCK2_PULL_DOWN_SHIFT,
.mask_reset_reg = MASK_RESET_BUCK_REG,
.mask_reset = BUCK2_MASK_RESET,
+ .icc_reg = BUCK_ICC_TURNOFF_REG,
+ .icc_mask = BUCK2_ICC_SHIFT,
},
{
.dt_node_name = "buck3",
@@ -462,6 +476,8 @@
.pull_down = BUCK3_PULL_DOWN_SHIFT,
.mask_reset_reg = MASK_RESET_BUCK_REG,
.mask_reset = BUCK3_MASK_RESET,
+ .icc_reg = BUCK_ICC_TURNOFF_REG,
+ .icc_mask = BUCK3_ICC_SHIFT,
},
{
.dt_node_name = "buck4",
@@ -474,6 +490,8 @@
.pull_down = BUCK4_PULL_DOWN_SHIFT,
.mask_reset_reg = MASK_RESET_BUCK_REG,
.mask_reset = BUCK4_MASK_RESET,
+ .icc_reg = BUCK_ICC_TURNOFF_REG,
+ .icc_mask = BUCK4_ICC_SHIFT,
},
{
.dt_node_name = "ldo1",
@@ -484,6 +502,8 @@
.low_power_reg = LDO1_PWRCTRL_REG,
.mask_reset_reg = MASK_RESET_LDO_REG,
.mask_reset = LDO1_MASK_RESET,
+ .icc_reg = LDO_ICC_TURNOFF_REG,
+ .icc_mask = LDO1_ICC_SHIFT,
},
{
.dt_node_name = "ldo2",
@@ -494,6 +514,8 @@
.low_power_reg = LDO2_PWRCTRL_REG,
.mask_reset_reg = MASK_RESET_LDO_REG,
.mask_reset = LDO2_MASK_RESET,
+ .icc_reg = LDO_ICC_TURNOFF_REG,
+ .icc_mask = LDO2_ICC_SHIFT,
},
{
.dt_node_name = "ldo3",
@@ -504,6 +526,8 @@
.low_power_reg = LDO3_PWRCTRL_REG,
.mask_reset_reg = MASK_RESET_LDO_REG,
.mask_reset = LDO3_MASK_RESET,
+ .icc_reg = LDO_ICC_TURNOFF_REG,
+ .icc_mask = LDO3_ICC_SHIFT,
},
{
.dt_node_name = "ldo4",
@@ -514,6 +538,8 @@
.low_power_reg = LDO4_PWRCTRL_REG,
.mask_reset_reg = MASK_RESET_LDO_REG,
.mask_reset = LDO4_MASK_RESET,
+ .icc_reg = LDO_ICC_TURNOFF_REG,
+ .icc_mask = LDO4_ICC_SHIFT,
},
{
.dt_node_name = "ldo5",
@@ -524,6 +550,8 @@
.low_power_reg = LDO5_PWRCTRL_REG,
.mask_reset_reg = MASK_RESET_LDO_REG,
.mask_reset = LDO5_MASK_RESET,
+ .icc_reg = LDO_ICC_TURNOFF_REG,
+ .icc_mask = LDO5_ICC_SHIFT,
},
{
.dt_node_name = "ldo6",
@@ -534,6 +562,8 @@
.low_power_reg = LDO6_PWRCTRL_REG,
.mask_reset_reg = MASK_RESET_LDO_REG,
.mask_reset = LDO6_MASK_RESET,
+ .icc_reg = LDO_ICC_TURNOFF_REG,
+ .icc_mask = LDO6_ICC_SHIFT,
},
{
.dt_node_name = "vref_ddr",
@@ -551,6 +581,8 @@
.voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table),
.control_reg = USB_CONTROL_REG,
.enable_mask = BOOST_ENABLED,
+ .icc_reg = BUCK_ICC_TURNOFF_REG,
+ .icc_mask = BOOST_ICC_SHIFT,
},
{
.dt_node_name = "pwr_sw1",
@@ -558,6 +590,8 @@
.voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table),
.control_reg = USB_CONTROL_REG,
.enable_mask = USBSW_OTG_SWITCH_ENABLED,
+ .icc_reg = BUCK_ICC_TURNOFF_REG,
+ .icc_mask = PWR_SW1_ICC_SHIFT,
},
{
.dt_node_name = "pwr_sw2",
@@ -565,6 +599,8 @@
.voltage_table_size = ARRAY_SIZE(fixed_5v_voltage_table),
.control_reg = USB_CONTROL_REG,
.enable_mask = SWIN_SWOUT_ENABLED,
+ .icc_reg = BUCK_ICC_TURNOFF_REG,
+ .icc_mask = PWR_SW2_ICC_SHIFT,
},
};
@@ -649,11 +685,21 @@
const struct regul_struct *regul = get_regulator_data(name);
uint8_t mask;
+ if ((strncmp(name, "ldo3", 5) == 0) && ldo3_special_mode) {
+ /*
+ * when the LDO3 is in special mode, we do not change voltage,
+ * because by setting voltage, the LDO would leaves sink-source
+ * mode. There is obviously no reason to leave sink-source mode
+ * at runtime.
+ */
+ return 0;
+ }
+
/* Voltage can be set for buck<N> or ldo<N> (except ldo4) regulators */
if (strncmp(name, "buck", 4) == 0) {
mask = BUCK_VOLTAGE_MASK;
} else if ((strncmp(name, "ldo", 3) == 0) &&
- (strncmp(name, "ldo4", 4) != 0)) {
+ (strncmp(name, "ldo4", 5) != 0)) {
mask = LDO_VOLTAGE_MASK;
} else {
return 0;
@@ -682,12 +728,90 @@
{
const struct regul_struct *regul = get_regulator_data(name);
+ if (regul->mask_reset_reg == 0U) {
+ return -EPERM;
+ }
+
return stpmic1_register_update(regul->mask_reset_reg,
BIT(regul->mask_reset),
LDO_BUCK_RESET_MASK <<
regul->mask_reset);
}
+int stpmic1_regulator_icc_set(const char *name)
+{
+ const struct regul_struct *regul = get_regulator_data(name);
+
+ if (regul->mask_reset_reg == 0U) {
+ return -EPERM;
+ }
+
+ return stpmic1_register_update(regul->icc_reg,
+ BIT(regul->icc_mask),
+ BIT(regul->icc_mask));
+}
+
+int stpmic1_regulator_sink_mode_set(const char *name)
+{
+ if (strncmp(name, "ldo3", 5) != 0) {
+ return -EPERM;
+ }
+
+ ldo3_special_mode = true;
+
+ /* disable bypass mode, enable sink mode */
+ return stpmic1_register_update(LDO3_CONTROL_REG,
+ LDO3_DDR_SEL << LDO_BUCK_VOLTAGE_SHIFT,
+ LDO3_BYPASS | LDO_VOLTAGE_MASK);
+}
+
+int stpmic1_regulator_bypass_mode_set(const char *name)
+{
+ if (strncmp(name, "ldo3", 5) != 0) {
+ return -EPERM;
+ }
+
+ ldo3_special_mode = true;
+
+ /* enable bypass mode, disable sink mode */
+ return stpmic1_register_update(LDO3_CONTROL_REG,
+ LDO3_BYPASS,
+ LDO3_BYPASS | LDO_VOLTAGE_MASK);
+}
+
+int stpmic1_active_discharge_mode_set(const char *name)
+{
+ if (strncmp(name, "pwr_sw1", 8) == 0) {
+ return stpmic1_register_update(USB_CONTROL_REG,
+ VBUS_OTG_DISCHARGE,
+ VBUS_OTG_DISCHARGE);
+ }
+
+ if (strncmp(name, "pwr_sw2", 8) == 0) {
+ return stpmic1_register_update(USB_CONTROL_REG,
+ SW_OUT_DISCHARGE,
+ SW_OUT_DISCHARGE);
+ }
+
+ return -EPERM;
+}
+
+int stpmic1_regulator_levels_mv(const char *name, const uint16_t **levels,
+ size_t *levels_count)
+{
+ const struct regul_struct *regul = get_regulator_data(name);
+
+ if ((strncmp(name, "ldo3", 5) == 0) && ldo3_special_mode) {
+ *levels_count = ARRAY_SIZE(ldo3_special_mode_table);
+ *levels = ldo3_special_mode_table;
+ } else {
+ *levels_count = regul->voltage_table_size;
+ *levels = regul->voltage_table;
+ }
+
+ return 0;
+}
+
int stpmic1_regulator_voltage_get(const char *name)
{
const struct regul_struct *regul = get_regulator_data(name);
@@ -695,11 +819,15 @@
uint8_t mask;
int status;
+ if ((strncmp(name, "ldo3", 5) == 0) && ldo3_special_mode) {
+ return 0;
+ }
+
/* Voltage can be set for buck<N> or ldo<N> (except ldo4) regulators */
if (strncmp(name, "buck", 4) == 0) {
mask = BUCK_VOLTAGE_MASK;
} else if ((strncmp(name, "ldo", 3) == 0) &&
- (strncmp(name, "ldo4", 4) != 0)) {
+ (strncmp(name, "ldo4", 5) != 0)) {
mask = LDO_VOLTAGE_MASK;
} else {
return 0;
diff --git a/include/drivers/st/stpmic1.h b/include/drivers/st/stpmic1.h
index a69e5b5..2dfc7f8 100644
--- a/include/drivers/st/stpmic1.h
+++ b/include/drivers/st/stpmic1.h
@@ -103,6 +103,22 @@
#define BUCK4_PULL_DOWN_SHIFT 6
#define VREF_DDR_PULL_DOWN_SHIFT 4
+/* ICC register */
+#define BUCK1_ICC_SHIFT 0
+#define BUCK2_ICC_SHIFT 1
+#define BUCK3_ICC_SHIFT 2
+#define BUCK4_ICC_SHIFT 3
+#define PWR_SW1_ICC_SHIFT 4
+#define PWR_SW2_ICC_SHIFT 5
+#define BOOST_ICC_SHIFT 6
+
+#define LDO1_ICC_SHIFT 0
+#define LDO2_ICC_SHIFT 1
+#define LDO3_ICC_SHIFT 2
+#define LDO4_ICC_SHIFT 3
+#define LDO5_ICC_SHIFT 4
+#define LDO6_ICC_SHIFT 5
+
/* Buck Mask reset register */
#define BUCK1_MASK_RESET 0
#define BUCK2_MASK_RESET 1
@@ -118,6 +134,10 @@
#define LDO6_MASK_RESET 5
#define VREF_DDR_MASK_RESET 6
+/* LDO3 Special modes */
+#define LDO3_BYPASS BIT(7)
+#define LDO3_DDR_SEL 31U
+
/* Main PMIC Control Register (MAIN_CONTROL_REG) */
#define ICC_EVENT_ENABLED BIT(4)
#define PWRCTRL_POLARITY_HIGH BIT(3)
@@ -145,6 +165,8 @@
/* USB Control Register */
#define BOOST_OVP_DISABLED BIT(7)
#define VBUS_OTG_DETECTION_DISABLED BIT(6)
+#define SW_OUT_DISCHARGE BIT(5)
+#define VBUS_OTG_DISCHARGE BIT(4)
#define OCP_LIMIT_HIGH BIT(3)
#define SWIN_SWOUT_ENABLED BIT(2)
#define USBSW_OTG_SWITCH_ENABLED BIT(1)
@@ -159,9 +181,15 @@
int stpmic1_regulator_disable(const char *name);
bool stpmic1_is_regulator_enabled(const char *name);
int stpmic1_regulator_voltage_set(const char *name, uint16_t millivolts);
+int stpmic1_regulator_levels_mv(const char *name, const uint16_t **levels,
+ size_t *levels_count);
int stpmic1_regulator_voltage_get(const char *name);
int stpmic1_regulator_pull_down_set(const char *name);
int stpmic1_regulator_mask_reset_set(const char *name);
+int stpmic1_regulator_icc_set(const char *name);
+int stpmic1_regulator_sink_mode_set(const char *name);
+int stpmic1_regulator_bypass_mode_set(const char *name);
+int stpmic1_active_discharge_mode_set(const char *name);
void stpmic1_bind_i2c(struct i2c_handle_s *i2c_handle, uint16_t i2c_addr);
int stpmic1_get_version(unsigned long *version);