예제 #1
0
/* Voltage scale and accessory APIs */
static int _pre_volt_scale(struct omap_vdd_info *vdd,
		unsigned long target_volt, u8 *target_vsel, u8 *current_vsel)
{
	struct omap_volt_data *volt_data;
	const struct omap_vc_common_data *vc_common;
	const struct omap_vp_common_data *vp_common;
	u32 vc_cmdval, vp_errgain_val;

	vc_common = vdd->vc_data->vc_common;
	vp_common = vdd->vp_data->vp_common;

	/* Check if suffiecient pmic info is available for this vdd */
	if (!vdd->pmic_info) {
		pr_err("%s: Insufficient pmic info to scale the vdd_%s\n",
			__func__, vdd->voltdm.name);
		return -EINVAL;
	}

	if (!vdd->pmic_info->uv_to_vsel) {
		pr_err("%s: PMIC function to convert voltage in uV to"
			"vsel not registered. Hence unable to scale voltage"
			"for vdd_%s\n", __func__, vdd->voltdm.name);
		return -ENODATA;
	}

	if (!vdd->read_reg || !vdd->write_reg) {
		pr_err("%s: No read/write API for accessing vdd_%s regs\n",
			__func__, vdd->voltdm.name);
		return -EINVAL;
	}

	/* Get volt_data corresponding to target_volt */
	volt_data = omap_voltage_get_voltdata(&vdd->voltdm, target_volt);
	if (IS_ERR(volt_data))
		volt_data = NULL;

	*target_vsel = vdd->pmic_info->uv_to_vsel(target_volt);
	*current_vsel = vdd->read_reg(prm_mod_offs, vdd->vp_data->voltage);

	/* Setting the ON voltage to the new target voltage */
	vc_cmdval = vdd->read_reg(prm_mod_offs, vdd->vc_data->cmdval_reg);
	vc_cmdval &= ~vc_common->cmd_on_mask;
	vc_cmdval |= (*target_vsel << vc_common->cmd_on_shift);
	vdd->write_reg(vc_cmdval, prm_mod_offs, vdd->vc_data->cmdval_reg);

	/* Setting vp errorgain based on the voltage */
	if (volt_data) {
		vp_errgain_val = vdd->read_reg(prm_mod_offs,
					       vdd->vp_data->vpconfig);
		vdd->vp_rt_data.vpconfig_errorgain = volt_data->vp_errgain;
		vp_errgain_val &= ~vp_common->vpconfig_errorgain_mask;
		vp_errgain_val |= vdd->vp_rt_data.vpconfig_errorgain <<
			vp_common->vpconfig_errorgain_shift;
		vdd->write_reg(vp_errgain_val, prm_mod_offs,
			       vdd->vp_data->vpconfig);
	}

	return 0;
}
예제 #2
0
파일: vp.c 프로젝트: CSCLOG/beaglebone
int omap_vp_update_errorgain(struct voltagedomain *voltdm,
			     unsigned long target_volt)
{
	struct omap_volt_data *volt_data;

	if (!voltdm->vp)
		return -EINVAL;

	/* Get volt_data corresponding to target_volt */
	volt_data = omap_voltage_get_voltdata(voltdm, target_volt);
	if (IS_ERR(volt_data))
		return -EINVAL;

	/* Setting vp errorgain based on the voltage */
	voltdm->rmw(voltdm->vp->common->vpconfig_errorgain_mask,
		    volt_data->vp_errgain <<
		    __ffs(voltdm->vp->common->vpconfig_errorgain_mask),
		    voltdm->vp->vpconfig);

	return 0;
}
예제 #3
0
/**
 * omap_ldo_abb_init() - initialize the ABB LDO for associated for this domain
 * @voltdm:	voltdm for which we need to initialize the ABB LDO
 *
 * Programs up the the configurations that dont change in the domain
 *
 * Return 0 if all goes fine, else returns appropriate error value
 */
void __init omap_ldo_abb_init(struct voltagedomain *voltdm)
{
	u32 sys_clk_rate;
	u32 cycle_rate;
	u32 settling_time;
	u32 wait_count_val;
	u32 reg, trim, rbb;
	struct omap_ldo_abb_instance *abb;
	struct omap_volt_data *volt_data;

	if (IS_ERR_OR_NULL(voltdm)) {
		pr_err("%s: No voltdm?\n", __func__);
		return;
	}
	if (!voltdm->read || !voltdm->write || !voltdm->rmw) {
		pr_err("%s: No read/write/rmw API for accessing vdd_%s regs\n",
		       __func__, voltdm->name);
		return;
	}

	abb = voltdm->abb;
	if (IS_ERR_OR_NULL(abb))
		return;
	if (IS_ERR_OR_NULL(abb->ctrl_bits) || IS_ERR_OR_NULL(abb->setup_bits)) {
		pr_err("%s: Corrupted ABB configuration on vdd_%s regs\n",
		       __func__, voltdm->name);
		return;
	}

	/*
	 * SR2_WTCNT_VALUE must be programmed with the expected settling time
	 * for ABB ldo transition.  This value depends on the cycle rate for
	 * the ABB IP (varies per OMAP family), and the system clock frequency
	 * (varies per board).  The formula is:
	 *
	 * SR2_WTCNT_VALUE = SettlingTime / (CycleRate / SystemClkRate))
	 * where SettlingTime is in micro-seconds and SystemClkRate is in MHz.
	 *
	 * To avoid dividing by zero multiply both CycleRate and SettlingTime
	 * by 10 such that the final result is the one we want.
	 */

	/* Convert SYS_CLK rate to MHz & prevent divide by zero */
	sys_clk_rate = DIV_ROUND_CLOSEST(voltdm->sys_clk.rate, 1000000);
	cycle_rate = abb->cycle_rate * 10;
	settling_time = abb->settling_time * 10;

	/* Calculate cycle rate */
	cycle_rate = DIV_ROUND_CLOSEST(cycle_rate, sys_clk_rate);

	/* Calulate SR2_WTCNT_VALUE */
	wait_count_val = DIV_ROUND_CLOSEST(settling_time, cycle_rate);

	voltdm->rmw(abb->setup_bits->wait_count_mask,
		    wait_count_val << __ffs(abb->setup_bits->wait_count_mask),
		    abb->setup_reg);

	/*
	 * Determine MPU ABB state at OPP_TURBO on 4460
	 *
	 * On 4460 all OPPs have preset states for the MPU's ABB LDO, except
	 * for OPP_TURBO.  OPP_TURBO may require bypass, FBB or RBB depending
	 * on a combination of characterisation data blown into eFuse register
	 * CONTROL_STD_FUSE_OPP_DPLL_1.
	 *
	 * Bits 18 & 19 of that register signify DPLL_MPU trim (see
	 * arch/arm/mach-omap2/omap4-trim-quirks.c).  OPP_TURBO might put MPU's
	 * ABB LDO into bypass or FBB based on this value.
	 *
	 * Bit 20 siginifies if RBB should be enabled.  If set it will always
	 * override the values from bits 18 & 19.
	 *
	 * The table below captures the valid combinations:
	 *
	 * Bit 18|Bit 19|Bit 20|ABB type
	 * 0	  0	 0	bypass
	 * 0	  1	 0	bypass	(invalid combo)
	 * 1	  0	 0	FBB	(2.4GHz DPLL_MPU)
	 * 1	  1	 0	FBB	(3GHz DPLL_MPU)
	 * 0	  0	 1	RBB
	 * 0	  1	 1	RBB	(invalid combo)
	 * 1	  0	 1	RBB	(2.4GHz DPLL_MPU)
	 * 1	  1	 1	RBB	(3GHz DPLL_MPU)
	 */
	if (cpu_is_omap446x() && !strcmp("mpu", voltdm->name)) {
		/* read eFuse register here */
		reg = omap_ctrl_readl(OMAP4_CTRL_MODULE_CORE_STD_FUSE_OPP_DPLL_1);
		trim = reg & OMAP4460_MPU_OPP_DPLL_TRIM;
		rbb = reg & OMAP4460_MPU_OPP_DPLL_TURBO_RBB;

		volt_data = omap_voltage_get_voltdata(voltdm,
				OMAP4460_VDD_MPU_OPPTURBO_UV);

		/* OPP_TURBO is FAST_OPP (FBB) by default */
		if (rbb)
			volt_data->abb_type = OMAP_ABB_SLOW_OPP;
		else if (!trim)
			volt_data->abb_type = OMAP_ABB_NOMINAL_OPP;

	}
	/* Enable ABB */
	_abb_set_availability(voltdm, abb, true);

	/*
	 * Beware of the bootloader!
	 * Initialize current abb type based on what we read off the reg.
	 * we cant trust the initial state based off boot voltage's volt_data
	 * even. Not all bootloaders are nice :(
	 */
	abb->__cur_abb_type = (voltdm->read(abb->ctrl_reg) &
			       abb->ctrl_bits->opp_sel_mask) >>
				    __ffs(abb->ctrl_bits->opp_sel_mask);

	return;
}