Exemplo n.º 1
0
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, &reg, 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, &reg, 1);
		if (rc)
			goto fail;
	}

	return rc;
fail:
	vreg_err(vreg, "qpnp_vreg_write failed, rc=%d\n", rc);

	return rc;
}
Exemplo n.º 2
0
/*
 * 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;
}
Exemplo n.º 3
0
/*
 * 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, &reg, 1);

		if (rc) {
			vreg_err(vreg, "write failed; addr=0x%03X, rc=%d\n",
				addr, rc);
		} else {
			*reg_save = reg;
		}
	}

	return rc;
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 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, &reg, 1);
			if (rc) {
				vreg_err(vreg, "spmi write failed, rc=%d\n",
					rc);
				return rc;
			}
		}
	}

	return rc;
}