static int qpnp_regulator_vs_enable(struct regulator_dev *rdev) { struct qpnp_regulator *vreg = rdev_get_drvdata(rdev); int rc; u8 reg; if (vreg->ocp_enable == QPNP_REGULATOR_ENABLE) { /* Disable OCP */ reg = QPNP_VS_OCP_DISABLE; rc = qpnp_vreg_write(vreg, QPNP_VS_REG_OCP, ®, 1); if (rc) goto fail; } rc = qpnp_regulator_common_enable(rdev); if (rc) goto fail; if (vreg->ocp_enable == QPNP_REGULATOR_ENABLE) { /* Wait for inrush current to subsided, then enable OCP. */ udelay(vreg->ocp_enable_time); reg = QPNP_VS_OCP_ENABLE_MASK; rc = qpnp_vreg_write(vreg, QPNP_VS_REG_OCP, ®, 1); if (rc) goto fail; } return rc; fail: vreg_err(vreg, "qpnp_vreg_write failed, rc=%d\n", rc); return rc; }
/* * qpnp_vreg_write_optimized - write the minimum sized contiguous subset of buf * @vreg: qpnp_regulator pointer for this regulator * @addr: local SPMI address offset from this peripheral's base address * @buf: new data to write into the SPMI registers * @buf_save: old data in the registers * @len: number of bytes to write * * This function checks for unchanged register values between buf and buf_save * starting at both ends of buf. Only the contiguous subset in the middle of * buf starting and ending with new values is sent. * * Consider the following example: * buf offset: 0 1 2 3 4 5 6 7 * reg state: U U C C U C U U * (U = unchanged, C = changed) * In this example registers 2 through 5 will be written with a single * transaction. */ static inline int qpnp_vreg_write_optimized(struct qpnp_regulator *vreg, u16 addr, u8 *buf, u8 *buf_save, int len) { int i, rc, start, end; for (i = 0; i < len; i++) if (buf[i] != buf_save[i]) break; start = i; for (i = len - 1; i >= 0; i--) if (buf[i] != buf_save[i]) break; end = i; if (start > end) { /* No modified register values present. */ return 0; } rc = qpnp_vreg_write(vreg, addr + start, &buf[start], end - start + 1); if (!rc) for (i = start; i <= end; i++) buf_save[i] = buf[i]; return rc; }
/* * Perform a masked write to a PMIC register only if the new value differs * from the last value written to the register. This removes redundant * register writing. */ static int qpnp_vreg_masked_write(struct qpnp_regulator *vreg, u16 addr, u8 val, u8 mask, u8 *reg_save) { int rc = 0; u8 reg; reg = (*reg_save & ~mask) | (val & mask); if (reg != *reg_save) { rc = qpnp_vreg_write(vreg, addr, ®, 1); if (rc) { vreg_err(vreg, "write failed; addr=0x%03X, rc=%d\n", addr, rc); } else { *reg_save = reg; } } return rc; }
static int qpnp_regulator_common_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) { struct qpnp_regulator *vreg = rdev_get_drvdata(rdev); int rc, range_sel, voltage_sel; u8 buf[2]; rc = qpnp_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel, &voltage_sel); if (rc) { vreg_err(vreg, "could not set voltage, rc=%d\n", rc); return rc; } buf[0] = range_sel; buf[1] = voltage_sel; if ((vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE] != range_sel) && (vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET] == voltage_sel)) { /* Handle latched range change. */ rc = qpnp_vreg_write(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE, buf, 2); if (!rc) { vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE] = buf[0]; vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET] = buf[1]; } } else { /* Either write can be optimized away safely. */ rc = qpnp_vreg_write_optimized(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE, buf, &vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE], 2); } if (rc) vreg_err(vreg, "SPMI write failed, rc=%d\n", rc); else qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_VOLTAGE); return rc; }
static int qpnp_regulator_init_registers(struct qpnp_regulator *vreg, struct qpnp_regulator_platform_data *pdata) { int rc, i; enum qpnp_regulator_logical_type type; u8 ctrl_reg[8], reg, mask; type = vreg->logical_type; rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE, vreg->ctrl_reg, 8); if (rc) { vreg_err(vreg, "spmi read failed, rc=%d\n", rc); return rc; } for (i = 0; i < ARRAY_SIZE(ctrl_reg); i++) ctrl_reg[i] = vreg->ctrl_reg[i]; /* Set up enable pin control. */ if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO || type == QPNP_REGULATOR_LOGICAL_TYPE_VS) && !(pdata->pin_ctrl_enable & QPNP_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT)) { ctrl_reg[QPNP_COMMON_IDX_ENABLE] &= ~QPNP_COMMON_ENABLE_FOLLOW_ALL_MASK; ctrl_reg[QPNP_COMMON_IDX_ENABLE] |= pdata->pin_ctrl_enable & QPNP_COMMON_ENABLE_FOLLOW_ALL_MASK; } /* Set up auto mode control. */ if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO || type == QPNP_REGULATOR_LOGICAL_TYPE_VS || type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS) && (pdata->auto_mode_enable != QPNP_REGULATOR_USE_HW_DEFAULT)) { ctrl_reg[QPNP_COMMON_IDX_MODE] &= ~QPNP_COMMON_MODE_AUTO_MASK; ctrl_reg[QPNP_COMMON_IDX_MODE] |= (pdata->auto_mode_enable ? QPNP_COMMON_MODE_AUTO_MASK : 0); } /* Set up mode pin control. */ if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO) && !(pdata->pin_ctrl_hpm & QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { ctrl_reg[QPNP_COMMON_IDX_MODE] &= ~QPNP_COMMON_MODE_FOLLOW_ALL_MASK; ctrl_reg[QPNP_COMMON_IDX_MODE] |= pdata->pin_ctrl_hpm & QPNP_COMMON_MODE_FOLLOW_ALL_MASK; } if (type == QPNP_REGULATOR_LOGICAL_TYPE_VS && !(pdata->pin_ctrl_hpm & QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { ctrl_reg[QPNP_COMMON_IDX_MODE] &= ~QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK; ctrl_reg[QPNP_COMMON_IDX_MODE] |= pdata->pin_ctrl_hpm & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK; } if (type == QPNP_REGULATOR_LOGICAL_TYPE_LDO && pdata->bypass_mode_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { ctrl_reg[QPNP_COMMON_IDX_MODE] &= ~QPNP_COMMON_MODE_BYPASS_MASK; ctrl_reg[QPNP_COMMON_IDX_MODE] |= (pdata->bypass_mode_enable ? QPNP_COMMON_MODE_BYPASS_MASK : 0); } /* Set boost current limit. */ if (type == QPNP_REGULATOR_LOGICAL_TYPE_BOOST && pdata->boost_current_limit != QPNP_BOOST_CURRENT_LIMIT_HW_DEFAULT) { ctrl_reg[QPNP_BOOST_IDX_CURRENT_LIMIT] &= ~QPNP_BOOST_CURRENT_LIMIT_MASK; ctrl_reg[QPNP_BOOST_IDX_CURRENT_LIMIT] |= pdata->boost_current_limit & QPNP_BOOST_CURRENT_LIMIT_MASK; } /* Write back any control register values that were modified. */ rc = qpnp_vreg_write_optimized(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, vreg->ctrl_reg, 8); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } /* Set pull down. */ if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO || type == QPNP_REGULATOR_LOGICAL_TYPE_VS) && pdata->pull_down_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { reg = pdata->pull_down_enable ? QPNP_COMMON_PULL_DOWN_ENABLE_MASK : 0; rc = qpnp_vreg_write(vreg, QPNP_COMMON_REG_PULL_DOWN, ®, 1); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } } if (type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS && pdata->pull_down_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { /* FTSMPS has other bits in the pull down control register. */ reg = pdata->pull_down_enable ? QPNP_COMMON_PULL_DOWN_ENABLE_MASK : 0; rc = qpnp_vreg_masked_read_write(vreg, QPNP_COMMON_REG_PULL_DOWN, reg, QPNP_COMMON_PULL_DOWN_ENABLE_MASK); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } } /* Set soft start for LDO. */ if (type == QPNP_REGULATOR_LOGICAL_TYPE_LDO && pdata->soft_start_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { reg = pdata->soft_start_enable ? QPNP_LDO_SOFT_START_ENABLE_MASK : 0; rc = qpnp_vreg_write(vreg, QPNP_LDO_REG_SOFT_START, ®, 1); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } } /* Set soft start strength and over current protection for VS. */ if (type == QPNP_REGULATOR_LOGICAL_TYPE_VS) { reg = 0; mask = 0; if (pdata->soft_start_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { reg |= pdata->soft_start_enable ? QPNP_VS_SOFT_START_ENABLE_MASK : 0; mask |= QPNP_VS_SOFT_START_ENABLE_MASK; } if (pdata->vs_soft_start_strength != QPNP_VS_SOFT_START_STR_HW_DEFAULT) { reg |= pdata->vs_soft_start_strength & QPNP_VS_SOFT_START_SEL_MASK; mask |= QPNP_VS_SOFT_START_SEL_MASK; } rc = qpnp_vreg_masked_read_write(vreg, QPNP_VS_REG_SOFT_START, reg, mask); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } if (pdata->ocp_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { reg = pdata->ocp_enable ? QPNP_VS_OCP_ENABLE_MASK : 0; rc = qpnp_vreg_write(vreg, QPNP_VS_REG_OCP, ®, 1); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } } } return rc; }
static int qpnp_regulator_init_registers(struct qpnp_regulator *vreg, struct qpnp_regulator_platform_data *pdata) { int rc, i; enum qpnp_regulator_logical_type type; u8 ctrl_reg[8], reg, mask; type = vreg->logical_type; rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE, vreg->ctrl_reg, 8); if (rc) { vreg_err(vreg, "spmi read failed, rc=%d\n", rc); return rc; } for (i = 0; i < ARRAY_SIZE(ctrl_reg); i++) ctrl_reg[i] = vreg->ctrl_reg[i]; if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO || type == QPNP_REGULATOR_LOGICAL_TYPE_VS) && !(pdata->pin_ctrl_enable & QPNP_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT)) { ctrl_reg[QPNP_COMMON_IDX_ENABLE] &= ~QPNP_COMMON_ENABLE_FOLLOW_ALL_MASK; ctrl_reg[QPNP_COMMON_IDX_ENABLE] |= pdata->pin_ctrl_enable & QPNP_COMMON_ENABLE_FOLLOW_ALL_MASK; } if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO || type == QPNP_REGULATOR_LOGICAL_TYPE_VS || type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS) && (pdata->hpm_enable != QPNP_REGULATOR_USE_HW_DEFAULT)) { ctrl_reg[QPNP_COMMON_IDX_MODE] &= ~QPNP_COMMON_MODE_HPM_MASK; ctrl_reg[QPNP_COMMON_IDX_MODE] |= (pdata->hpm_enable ? QPNP_COMMON_MODE_HPM_MASK : 0); } if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO || type == QPNP_REGULATOR_LOGICAL_TYPE_VS || type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS) && (pdata->auto_mode_enable != QPNP_REGULATOR_USE_HW_DEFAULT)) { ctrl_reg[QPNP_COMMON_IDX_MODE] &= ~QPNP_COMMON_MODE_AUTO_MASK; ctrl_reg[QPNP_COMMON_IDX_MODE] |= (pdata->auto_mode_enable ? QPNP_COMMON_MODE_AUTO_MASK : 0); } if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO) && !(pdata->pin_ctrl_hpm & QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { ctrl_reg[QPNP_COMMON_IDX_MODE] &= ~QPNP_COMMON_MODE_FOLLOW_ALL_MASK; ctrl_reg[QPNP_COMMON_IDX_MODE] |= pdata->pin_ctrl_hpm & QPNP_COMMON_MODE_FOLLOW_ALL_MASK; } if (type == QPNP_REGULATOR_LOGICAL_TYPE_VS && !(pdata->pin_ctrl_hpm & QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) { ctrl_reg[QPNP_COMMON_IDX_MODE] &= ~QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK; ctrl_reg[QPNP_COMMON_IDX_MODE] |= pdata->pin_ctrl_hpm & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK; } if (type == QPNP_REGULATOR_LOGICAL_TYPE_LDO && pdata->bypass_mode_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { ctrl_reg[QPNP_COMMON_IDX_MODE] &= ~QPNP_COMMON_MODE_BYPASS_MASK; ctrl_reg[QPNP_COMMON_IDX_MODE] |= (pdata->bypass_mode_enable ? QPNP_COMMON_MODE_BYPASS_MASK : 0); } if (type == QPNP_REGULATOR_LOGICAL_TYPE_BOOST && pdata->boost_current_limit != QPNP_BOOST_CURRENT_LIMIT_HW_DEFAULT) { reg = pdata->boost_current_limit; mask = QPNP_BOOST_CURRENT_LIMIT_MASK; rc = qpnp_vreg_masked_read_write(vreg, QPNP_BOOST_REG_CURRENT_LIMIT, reg, mask); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } } rc = qpnp_vreg_write_optimized(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE, ctrl_reg, vreg->ctrl_reg, 8); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO || type == QPNP_REGULATOR_LOGICAL_TYPE_VS) && pdata->pull_down_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { reg = pdata->pull_down_enable ? QPNP_COMMON_PULL_DOWN_ENABLE_MASK : 0; rc = qpnp_vreg_write(vreg, QPNP_COMMON_REG_PULL_DOWN, ®, 1); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } } if (type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS && pdata->pull_down_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { reg = pdata->pull_down_enable ? QPNP_COMMON_PULL_DOWN_ENABLE_MASK : 0; rc = qpnp_vreg_masked_read_write(vreg, QPNP_COMMON_REG_PULL_DOWN, reg, QPNP_COMMON_PULL_DOWN_ENABLE_MASK); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } } if (type == QPNP_REGULATOR_LOGICAL_TYPE_LDO && pdata->soft_start_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { reg = pdata->soft_start_enable ? QPNP_LDO_SOFT_START_ENABLE_MASK : 0; rc = qpnp_vreg_write(vreg, QPNP_LDO_REG_SOFT_START, ®, 1); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } } if (type == QPNP_REGULATOR_LOGICAL_TYPE_VS) { reg = 0; mask = 0; if (pdata->soft_start_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { reg |= pdata->soft_start_enable ? QPNP_VS_SOFT_START_ENABLE_MASK : 0; mask |= QPNP_VS_SOFT_START_ENABLE_MASK; } if (pdata->vs_soft_start_strength != QPNP_VS_SOFT_START_STR_HW_DEFAULT) { reg |= pdata->vs_soft_start_strength & QPNP_VS_SOFT_START_SEL_MASK; mask |= QPNP_VS_SOFT_START_SEL_MASK; } rc = qpnp_vreg_masked_read_write(vreg, QPNP_VS_REG_SOFT_START, reg, mask); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } if (pdata->ocp_enable != QPNP_REGULATOR_USE_HW_DEFAULT) { reg = pdata->ocp_enable ? QPNP_VS_OCP_NO_OVERRIDE : QPNP_VS_OCP_OVERRIDE; rc = qpnp_vreg_write(vreg, QPNP_VS_REG_OCP, ®, 1); if (rc) { vreg_err(vreg, "spmi write failed, rc=%d\n", rc); return rc; } } } return rc; }