/* * Perform a masked read-modify-write to a PMIC register only if the new value * differs from the value currently in the register. This removes redundant * register writing. */ static int qpnp_vreg_masked_read_write(struct qpnp_regulator *vreg, u16 addr, u8 val, u8 mask) { int rc; u8 reg; rc = qpnp_vreg_read(vreg, addr, ®, 1); if (rc) { vreg_err(vreg, "read failed; addr=0x%03X, rc=%d\n", addr, rc); return rc; } return qpnp_vreg_masked_write(vreg, addr, val, mask, ®); }
static int qpnp_regulator_match(struct qpnp_regulator *vreg) { const struct qpnp_regulator_mapping *mapping; struct device_node *node = vreg->spmi_dev->dev.of_node; int rc, i; u32 type_reg[2], dig_major_rev; u8 version[QPNP_COMMON_REG_SUBTYPE - QPNP_COMMON_REG_DIG_MAJOR_REV + 1]; u8 type, subtype; rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_DIG_MAJOR_REV, version, ARRAY_SIZE(version)); if (rc) { vreg_err(vreg, "could not read version registers, rc=%d\n", rc); return rc; } dig_major_rev = version[QPNP_COMMON_REG_DIG_MAJOR_REV - QPNP_COMMON_REG_DIG_MAJOR_REV]; type = version[QPNP_COMMON_REG_TYPE - QPNP_COMMON_REG_DIG_MAJOR_REV]; subtype = version[QPNP_COMMON_REG_SUBTYPE - QPNP_COMMON_REG_DIG_MAJOR_REV]; rc = of_property_read_u32_array(node, "qcom,force-type", type_reg, 2); if (!rc) { type = type_reg[0]; subtype = type_reg[1]; } rc = -ENODEV; for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) { mapping = &supported_regulators[i]; if (mapping->type == type && mapping->subtype == subtype && mapping->revision_min <= dig_major_rev && mapping->revision_max >= dig_major_rev) { vreg->logical_type = mapping->logical_type; vreg->set_points = mapping->set_points; vreg->hpm_min_load = mapping->hpm_min_load; vreg->rdesc.ops = mapping->ops; vreg->rdesc.n_voltages = mapping->set_points->n_voltages; rc = 0; break; } } return rc; }
static int qpnp_regulator_match(struct qpnp_regulator *vreg) { const struct qpnp_regulator_mapping *mapping; struct device_node *node = vreg->spmi_dev->dev.of_node; int rc, i; u8 raw_type[2], type, subtype; u32 type_reg[2]; rc = of_property_read_u32_array(node, "qcom,force-type", type_reg, 2); if (!rc) { type = type_reg[0]; subtype = type_reg[1]; } else { rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_TYPE, raw_type, 2); if (rc) { vreg_err(vreg, "could not read type register, rc=%d\n", rc); return rc; } type = raw_type[0]; subtype = raw_type[1]; } rc = -ENODEV; for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) { mapping = &supported_regulators[i]; if (mapping->type == type && mapping->subtype == subtype) { vreg->logical_type = mapping->logical_type; vreg->set_points = mapping->set_points; vreg->hpm_min_load = mapping->hpm_min_load; vreg->rdesc.ops = mapping->ops; vreg->rdesc.n_voltages = mapping->set_points->n_voltages; rc = 0; break; } } 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; }