예제 #1
0
/**
 * rpm_vreg_set_frequency - sets the frequency of a switching regulator
 * @vreg: ID for regulator
 * @freq: enum corresponding to desired frequency
 *
 * Returns 0 on success or errno.
 */
int rpm_vreg_set_frequency(int vreg_id, enum rpm_vreg_freq freq)
{
	unsigned int mask[2] = {0}, val[2] = {0};
	struct vreg *vreg;
	int rc;

	/*
	 * HACK: make this function a no-op for 8064 so that it can be called by
	 * consumers on 8064 before RPM capabilities are present.
	 */
	if (cpu_is_apq8064())
		return 0;

	if (!config) {
		pr_err("rpm-regulator driver has not probed yet.\n");
		return -ENODEV;
	}

	if (vreg_id < config->vreg_id_min || vreg_id > config->vreg_id_max) {
		pr_err("invalid regulator id=%d\n", vreg_id);
		return -EINVAL;
	}

	vreg = &config->vregs[vreg_id];

	if (freq < 0 || freq > RPM_VREG_FREQ_1p20) {
		vreg_err(vreg, "invalid frequency=%d\n", freq);
		return -EINVAL;
	}
	if (!vreg->pdata.sleep_selectable) {
		vreg_err(vreg, "regulator is not marked sleep selectable\n");
		return -EINVAL;
	}
	if (!vreg->part->freq.mask) {
		vreg_err(vreg, "frequency not supported\n");
		return -EINVAL;
	}

	val[vreg->part->freq.word] = freq << vreg->part->freq.shift;
	mask[vreg->part->freq.word] = vreg->part->freq.mask;

	rc = vreg_set_noirq(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1, mask[0],
			   val[0], mask[1], val[1], vreg->part->request_len, 0);
	if (rc)
		vreg_err(vreg, "vreg_set failed, rc=%d\n", rc);

	printk("rpm-regulator driver has probed .\n");

	return rc;
}
static int vreg_set(struct vreg *vreg, unsigned mask0, unsigned val0,
		unsigned mask1, unsigned val1, unsigned cnt)
{
	unsigned prev0 = 0, prev1 = 0;
	int rc;

	/*
	 * Bypass the normal route for regulators that can be called to change
	 * just the active set values.
	 */
	if (vreg->pdata.sleep_selectable)
		return vreg_set_noirq(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1,
					mask0, val0, mask1, val1, cnt, 1);

	prev0 = vreg->req[0].value;
	vreg->req[0].value &= ~mask0;
	vreg->req[0].value |= val0 & mask0;

	prev1 = vreg->req[1].value;
	vreg->req[1].value &= ~mask1;
	vreg->req[1].value |= val1 & mask1;

	/* Ignore duplicate requests */
	if (vreg->req[0].value == vreg->prev_active_req[0].value &&
	    vreg->req[1].value == vreg->prev_active_req[1].value) {
		if (msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_DUPLICATE)
			rpm_regulator_duplicate(vreg, MSM_RPM_CTX_SET_0, cnt);
		return 0;
	}

	rc = msm_rpm_set(MSM_RPM_CTX_SET_0, vreg->req, cnt);
	if (rc) {
		vreg->req[0].value = prev0;
		vreg->req[1].value = prev1;

		vreg_err(vreg, "msm_rpm_set failed, set=active, id=%d, rc=%d\n",
			vreg->req[0].id, rc);
	} else {
		if (msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_REQUEST)
			rpm_regulator_req(vreg, MSM_RPM_CTX_SET_0);
		vreg->prev_active_req[0].value = vreg->req[0].value;
		vreg->prev_active_req[1].value = vreg->req[1].value;
	}

	return rc;
}
/**
 * rpm_vreg_set_voltage - vote for a min_uV value of specified regualtor
 * @vreg: ID for regulator
 * @voter: ID for the voter
 * @min_uV: minimum acceptable voltage (in uV) that is voted for
 * @max_uV: maximum acceptable voltage (in uV) that is voted for
 * @sleep_also: 0 for active set only, non-0 for active set and sleep set
 *
 * Returns 0 on success or errno.
 *
 * This function is used to vote for the voltage of a regulator without
 * using the regulator framework.  It is needed by consumers which hold spin
 * locks or have interrupts disabled because the regulator framework can sleep.
 * It is also needed by consumers which wish to only vote for active set
 * regulator voltage.
 *
 * If sleep_also == 0, then a sleep-set value of 0V will be voted for.
 *
 * This function may only be called for regulators which have the sleep flag
 * specified in their private data.
 *
 * Consumers can vote to disable a regulator with this function by passing
 * min_uV = 0 and max_uV = 0.
 */
int rpm_vreg_set_voltage(int vreg_id, enum rpm_vreg_voter voter, int min_uV,
			 int max_uV, int sleep_also)
{
	unsigned int mask[2] = {0}, val[2] = {0};
	struct vreg_range *range;
	struct vreg *vreg;
	int uV = min_uV;
	int lim_min_uV, lim_max_uV, i, rc;

	/*
	 * HACK: make this function a no-op for 8064 so that it can be called by
	 * consumers on 8064 before RPM capabilities are present. (needed for
	 * acpuclock driver)
	 */
	if (cpu_is_apq8064())
		return 0;

	if (!config) {
		pr_err("rpm-regulator driver has not probed yet.\n");
		return -ENODEV;
	}

	if (vreg_id < config->vreg_id_min || vreg_id > config->vreg_id_max) {
		pr_err("invalid regulator id=%d\n", vreg_id);
		return -EINVAL;
	}

	vreg = &config->vregs[vreg_id];
	range = &vreg->set_points->range[0];

	if (!vreg->pdata.sleep_selectable) {
		vreg_err(vreg, "regulator is not marked sleep selectable\n");
		return -EINVAL;
	}

	/* Allow min_uV == max_uV == 0 to represent a disable request. */
	if (min_uV != 0 || max_uV != 0) {
		/*
		 * Check if request voltage is outside of allowed range. The
		 * regulator core has already checked that constraint range
		 * is inside of the physically allowed range.
		 */
		lim_min_uV = vreg->pdata.init_data.constraints.min_uV;
		lim_max_uV = vreg->pdata.init_data.constraints.max_uV;

		if (uV < lim_min_uV && max_uV >= lim_min_uV)
			uV = lim_min_uV;

		if (uV < lim_min_uV || uV > lim_max_uV) {
			vreg_err(vreg, "request v=[%d, %d] is outside allowed "
				"v=[%d, %d]\n", min_uV, max_uV, lim_min_uV,
				lim_max_uV);
			return -EINVAL;
		}

		/* Find the range which uV is inside of. */
		for (i = vreg->set_points->count - 1; i > 0; i--) {
			if (uV > vreg->set_points->range[i - 1].max_uV) {
				range = &vreg->set_points->range[i];
				break;
			}
		}

		/*
		 * Force uV to be an allowed set point and apply a ceiling
		 * function to non-set point values.
		 */
		uV = (uV - range->min_uV + range->step_uV - 1) / range->step_uV;
		uV = uV * range->step_uV + range->min_uV;

		if (uV > max_uV) {
			vreg_err(vreg,
			  "request v=[%d, %d] cannot be met by any set point; "
			  "next set point: %d\n",
			  min_uV, max_uV, uV);
			return -EINVAL;
		}
	}

	if (vreg->part->uV.mask) {
		val[vreg->part->uV.word] = uV << vreg->part->uV.shift;
		mask[vreg->part->uV.word] = vreg->part->uV.mask;
	} else {
		val[vreg->part->mV.word]
			= MICRO_TO_MILLI(uV) << vreg->part->mV.shift;
		mask[vreg->part->mV.word] = vreg->part->mV.mask;
	}

	rc = vreg_set_noirq(vreg, voter, sleep_also, mask[0], val[0], mask[1],
			    val[1], vreg->part->request_len, 1);
	if (rc)
		vreg_err(vreg, "vreg_set_noirq failed, rc=%d\n", rc);

	return rc;
}