|  | @@ -279,3 +279,106 @@ static void omap3_set_i2c_timings(struct voltagedomain *voltdm, bool off_mode)
 | 
	
		
			
				|  |  |   * This function first checks which mode is being used, and calls
 | 
	
		
			
				|  |  |   * omap3_set_i2c_timings() if the system is using I2C control mode.
 | 
	
		
			
				|  |  |   * sys_off_mode has the additional benefit that voltages can be
 | 
	
		
			
				|  |  | + * scaled to zero volt level with TWL4030 / TWL5030, I2C can only
 | 
	
		
			
				|  |  | + * scale to 600mV.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static void omap3_set_off_timings(struct voltagedomain *voltdm)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	unsigned long clksetup;
 | 
	
		
			
				|  |  | +	unsigned long voltsetup2;
 | 
	
		
			
				|  |  | +	unsigned long voltsetup2_old;
 | 
	
		
			
				|  |  | +	u32 val;
 | 
	
		
			
				|  |  | +	u32 tstart, tshut;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* check if sys_off_mode is used to control off-mode voltages */
 | 
	
		
			
				|  |  | +	val = voltdm->read(OMAP3_PRM_VOLTCTRL_OFFSET);
 | 
	
		
			
				|  |  | +	if (!(val & OMAP3430_SEL_OFF_MASK)) {
 | 
	
		
			
				|  |  | +		/* No, omap is controlling them over I2C */
 | 
	
		
			
				|  |  | +		omap3_set_i2c_timings(voltdm, true);
 | 
	
		
			
				|  |  | +		return;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	omap_pm_get_oscillator(&tstart, &tshut);
 | 
	
		
			
				|  |  | +	omap3_set_clksetup(tstart, voltdm);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	clksetup = voltdm->read(OMAP3_PRM_CLKSETUP_OFFSET);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* voltsetup 2 in us */
 | 
	
		
			
				|  |  | +	voltsetup2 = voltdm->vc_param->on / voltdm->pmic->slew_rate;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* convert to 32k clk cycles */
 | 
	
		
			
				|  |  | +	voltsetup2 = DIV_ROUND_UP(voltsetup2 * 32768, 1000000);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	voltsetup2_old = voltdm->read(OMAP3_PRM_VOLTSETUP2_OFFSET);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/*
 | 
	
		
			
				|  |  | +	 * Update voltsetup2 if higher than current value (needed because
 | 
	
		
			
				|  |  | +	 * we have multiple channels with different ramp times), also
 | 
	
		
			
				|  |  | +	 * update voltoffset always to value recommended by TRM
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	if (voltsetup2 > voltsetup2_old) {
 | 
	
		
			
				|  |  | +		voltdm->write(voltsetup2, OMAP3_PRM_VOLTSETUP2_OFFSET);
 | 
	
		
			
				|  |  | +		voltdm->write(clksetup - voltsetup2,
 | 
	
		
			
				|  |  | +			OMAP3_PRM_VOLTOFFSET_OFFSET);
 | 
	
		
			
				|  |  | +	} else
 | 
	
		
			
				|  |  | +		voltdm->write(clksetup - voltsetup2_old,
 | 
	
		
			
				|  |  | +			OMAP3_PRM_VOLTOFFSET_OFFSET);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/*
 | 
	
		
			
				|  |  | +	 * omap is not controlling voltage scaling during off-mode,
 | 
	
		
			
				|  |  | +	 * thus set voltsetup1 to 0
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	voltdm->rmw(voltdm->vfsm->voltsetup_mask, 0,
 | 
	
		
			
				|  |  | +		voltdm->vfsm->voltsetup_reg);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* voltoffset must be clksetup minus voltsetup2 according to TRM */
 | 
	
		
			
				|  |  | +	voltdm->write(clksetup - voltsetup2, OMAP3_PRM_VOLTOFFSET_OFFSET);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void __init omap3_vc_init_channel(struct voltagedomain *voltdm)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	omap3_set_off_timings(voltdm);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * omap4_calc_volt_ramp - calculates voltage ramping delays on omap4
 | 
	
		
			
				|  |  | + * @voltdm: channel to calculate values for
 | 
	
		
			
				|  |  | + * @voltage_diff: voltage difference in microvolts
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Calculates voltage ramp prescaler + counter values for a voltage
 | 
	
		
			
				|  |  | + * difference on omap4. Returns a field value suitable for writing to
 | 
	
		
			
				|  |  | + * VOLTSETUP register for a channel in following format:
 | 
	
		
			
				|  |  | + * bits[8:9] prescaler ... bits[0:5] counter. See OMAP4 TRM for reference.
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +static u32 omap4_calc_volt_ramp(struct voltagedomain *voltdm, u32 voltage_diff)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	u32 prescaler;
 | 
	
		
			
				|  |  | +	u32 cycles;
 | 
	
		
			
				|  |  | +	u32 time;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	time = voltage_diff / voltdm->pmic->slew_rate;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	cycles = voltdm->sys_clk.rate / 1000 * time / 1000;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	cycles /= 64;
 | 
	
		
			
				|  |  | +	prescaler = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* shift to next prescaler until no overflow */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* scale for div 256 = 64 * 4 */
 | 
	
		
			
				|  |  | +	if (cycles > 63) {
 | 
	
		
			
				|  |  | +		cycles /= 4;
 | 
	
		
			
				|  |  | +		prescaler++;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* scale for div 512 = 256 * 2 */
 | 
	
		
			
				|  |  | +	if (cycles > 63) {
 | 
	
		
			
				|  |  | +		cycles /= 2;
 | 
	
		
			
				|  |  | +		prescaler++;
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/* scale for div 2048 = 512 * 4 */
 | 
	
		
			
				|  |  | +	if (cycles > 63) {
 | 
	
		
			
				|  |  | +		cycles /= 4;
 | 
	
		
			
				|  |  | +		prescaler++;
 | 
	
		
			
				|  |  | +	}
 |