diff options
| author | York Sun <yorksun@freescale.com> | 2013-03-25 07:33:22 +0000 | 
|---|---|---|
| committer | Andy Fleming <afleming@freescale.com> | 2013-05-14 16:00:28 -0500 | 
| commit | 97c7fe61b84bf836ccf3d9aac624d4241f6e3b4c (patch) | |
| tree | b9c6d2ace3875abf0b6ea0cb87bbba9a6796446a | |
| parent | e1d5a2773f30a0b2b8ad3bfd88f118154c7af940 (diff) | |
| download | olio-uboot-2014.01-97c7fe61b84bf836ccf3d9aac624d4241f6e3b4c.tar.xz olio-uboot-2014.01-97c7fe61b84bf836ccf3d9aac624d4241f6e3b4c.zip | |
powerpc/t4240qds: Add voltage ID support
T4240 has voltage ID fuse. Read the fuse and configure the voltage
correctly. Core voltage has higher tolerance on over side than below.
Signed-off-by: York Sun <yorksun@freescale.com>
Signed-off-by: Andy Fleming <afleming@freescale.com>
| -rw-r--r-- | board/freescale/t4qds/t4qds.c | 229 | ||||
| -rw-r--r-- | include/configs/t4qds.h | 12 | 
2 files changed, 239 insertions, 2 deletions
| diff --git a/board/freescale/t4qds/t4qds.c b/board/freescale/t4qds/t4qds.c index 737d650dd..a84218cdf 100644 --- a/board/freescale/t4qds/t4qds.c +++ b/board/freescale/t4qds/t4qds.c @@ -132,6 +132,228 @@ int select_i2c_ch_pca9547(u8 ch)  	return 0;  } +/* + * read_voltage from sensor on I2C bus + * We use average of 4 readings, waiting for 532us befor another reading + */ +#define NUM_READINGS	4	/* prefer to be power of 2 for efficiency */ +#define WAIT_FOR_ADC	532	/* wait for 532 microseconds for ADC */ + +static inline int read_voltage(void) +{ +	int i, ret, voltage_read = 0; +	u16 vol_mon; + +	for (i = 0; i < NUM_READINGS; i++) { +		ret = i2c_read(I2C_VOL_MONITOR_ADDR, +			I2C_VOL_MONITOR_BUS_V_OFFSET, 1, (void *)&vol_mon, 2); +		if (ret) { +			printf("VID: failed to read core voltage\n"); +			return ret; +		} +		if (vol_mon & I2C_VOL_MONITOR_BUS_V_OVF) { +			printf("VID: Core voltage sensor error\n"); +			return -1; +		} +		debug("VID: bus voltage reads 0x%04x\n", vol_mon); +		/* LSB = 4mv */ +		voltage_read += (vol_mon >> I2C_VOL_MONITOR_BUS_V_SHIFT) * 4; +		udelay(WAIT_FOR_ADC); +	} +	/* calculate the average */ +	voltage_read /= NUM_READINGS; + +	return voltage_read; +} + +/* + * We need to calculate how long before the voltage starts to drop or increase + * It returns with the loop count. Each loop takes several readings (532us) + */ +static inline int wait_for_voltage_change(int vdd_last) +{ +	int timeout, vdd_current; + +	vdd_current = read_voltage(); +	/* wait until voltage starts to drop */ +	for (timeout = 0; abs(vdd_last - vdd_current) <= 4 && +		timeout < 100; timeout++) { +		vdd_current = read_voltage(); +	} +	if (timeout >= 100) { +		printf("VID: Voltage adjustment timeout\n"); +		return -1; +	} +	return timeout; +} + +/* + * argument 'wait' is the time we know the voltage difference can be measured + * this function keeps reading the voltage until it is stable + */ +static inline int wait_for_voltage_stable(int wait) +{ +	int timeout, vdd_current, vdd_last; + +	vdd_last = read_voltage(); +	udelay(wait * NUM_READINGS * WAIT_FOR_ADC); +	/* wait until voltage is stable */ +	vdd_current = read_voltage(); +	for (timeout = 0; abs(vdd_last - vdd_current) >= 4 && +		timeout < 100; timeout++) { +		vdd_last = vdd_current; +		udelay(wait * NUM_READINGS * WAIT_FOR_ADC); +		vdd_current = read_voltage(); +	} +	if (timeout >= 100) { +		printf("VID: Voltage adjustment timeout\n"); +		return -1; +	} + +	return vdd_current; +} + +static inline int set_voltage(u8 vid) +{ +	int wait, vdd_last; + +	vdd_last = read_voltage(); +	QIXIS_WRITE(brdcfg[6], vid); +	wait = wait_for_voltage_change(vdd_last); +	if (wait < 0) +		return -1; +	debug("VID: Waited %d us\n", wait * NUM_READINGS * WAIT_FOR_ADC); +	wait = wait ? wait : 1; + +	vdd_last = wait_for_voltage_stable(wait); +	if (vdd_last < 0) +		return -1; +	debug("VID: Current voltage is %d mV\n", vdd_last); + +	return vdd_last; +} + + +static int adjust_vdd(void) +{ +	int re_enable = disable_interrupts(); +	ccsr_gur_t __iomem *gur = +		(void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); +	u32 fusesr; +	u8 vid, vid_current; +	int vdd_target, vdd_current, vdd_last; +	int ret; +	static const uint16_t vdd[32] = { +		0,	/* unused */ +		9875,	/* 0.9875V */ +		9750, +		9625, +		9500, +		9375, +		9250, +		9125, +		9000, +		8875, +		8750, +		8625, +		8500, +		8375, +		8250, +		8125, +		10000,	/* 1.0000V */ +		10125, +		10250, +		10375, +		10500, +		10625, +		10750, +		10875, +		11000, +		0,	/* reserved */ +	}; +	struct vdd_drive { +		u8 vid; +		unsigned voltage; +	}; + +	ret = select_i2c_ch_pca9547(I2C_MUX_CH_VOL_MONITOR); +	if (ret) { +		debug("VID: I2c failed to switch channel\n"); +		ret = -1; +		goto exit; +	} + +	/* get the voltage ID from fuse status register */ +	fusesr = in_be32(&gur->dcfg_fusesr); +	vid = (fusesr >> FSL_CORENET_DCFG_FUSESR_VID_SHIFT) & +		FSL_CORENET_DCFG_FUSESR_VID_MASK; +	if (vid == FSL_CORENET_DCFG_FUSESR_VID_MASK) { +		vid = (fusesr >> FSL_CORENET_DCFG_FUSESR_ALTVID_SHIFT) & +			FSL_CORENET_DCFG_FUSESR_ALTVID_MASK; +	} +	vdd_target = vdd[vid]; +	if (vdd_target == 0) { +		debug("VID: VID not used\n"); +		ret = 0; +		goto exit; +	} else { +		/* round up and divice by 10 to get a value in mV */ +		vdd_target = DIV_ROUND_UP(vdd_target, 10); +		debug("VID: vid = %d mV\n", vdd_target); +	} + +	/* +	 * Check current board VID setting +	 * Voltage regulator support output to 6.250mv step +	 * The highes voltage allowed for this board is (vid=0x40) 1.21250V +	 * the lowest is (vid=0x7f) 0.81875V +	 */ +	vid_current =  QIXIS_READ(brdcfg[6]); +	vdd_current = 121250 - (vid_current - 0x40) * 625; +	debug("VID: Current vid setting is (0x%x) %d mV\n", +	      vid_current, vdd_current/100); + +	/* +	 * Read voltage monitor to check real voltage. +	 * Voltage monitor LSB is 4mv. +	 */ +	vdd_last = read_voltage(); +	if (vdd_last < 0) { +		printf("VID: Could not read voltage sensor abort VID adjustment\n"); +		ret = -1; +		goto exit; +	} +	debug("VID: Core voltage is at %d mV\n", vdd_last); +	/* +	 * Adjust voltage to at or 8mV above target. +	 * Each step of adjustment is 6.25mV. +	 * Stepping down too fast may cause over current. +	 */ +	while (vdd_last > 0 && vid_current < 0x80 && +		vdd_last > (vdd_target + 8)) { +		vid_current++; +		vdd_last = set_voltage(vid_current); +	} +	/* +	 * Check if we need to step up +	 * This happens when board voltage switch was set too low +	 */ +	while (vdd_last > 0 && vid_current >= 0x40 && +		vdd_last < vdd_target + 2) { +		vid_current--; +		vdd_last = set_voltage(vid_current); +	} +	if (vdd_last > 0) +		printf("VID: Core voltage %d mV\n", vdd_last); +	else +		ret = -1; + +exit: +	if (re_enable) +		enable_interrupts(); +	return ret; +} +  /* Configure Crossbar switches for Front-Side SerDes Ports */  int config_frontside_crossbar_vsc3316(void)  { @@ -285,6 +507,13 @@ int board_early_init_r(void)  	/* Disable remote I2C connectoin */  	QIXIS_WRITE(brdcfg[5], BRDCFG5_RESET); +	/* +	 * Adjust core voltage according to voltage ID +	 * This function changes I2C mux to channel 2. +	 */ +	if (adjust_vdd()) +		printf("Warning: Adjusting core voltage failed.\n"); +  	/* Configure board SERDES ports crossbar */  	config_frontside_crossbar_vsc3316();  	config_backside_crossbar_mux(); diff --git a/include/configs/t4qds.h b/include/configs/t4qds.h index c5110c2bc..dbaa7ea9d 100644 --- a/include/configs/t4qds.h +++ b/include/configs/t4qds.h @@ -444,11 +444,19 @@ unsigned long get_board_ddr_clk(void);  #define I2C_MUX_PCA_ADDR_PRI		0x77 /* I2C bus multiplexer,primary */  #define I2C_MUX_PCA_ADDR_SEC		0x76 /* I2C bus multiplexer,secondary */ -/* VSC Crossbar switches */ -#define CONFIG_VSC_CROSSBAR  #define I2C_MUX_CH_DEFAULT	0x8 +#define I2C_MUX_CH_VOL_MONITOR	0xa  #define I2C_MUX_CH_VSC3316_FS	0xc  #define I2C_MUX_CH_VSC3316_BS	0xd + +/* Voltage monitor on channel 2*/ +#define I2C_VOL_MONITOR_ADDR		0x40 +#define I2C_VOL_MONITOR_BUS_V_OFFSET	0x2 +#define I2C_VOL_MONITOR_BUS_V_OVF	0x1 +#define I2C_VOL_MONITOR_BUS_V_SHIFT	3 + +/* VSC Crossbar switches */ +#define CONFIG_VSC_CROSSBAR  #define VSC3316_FSM_TX_ADDR	0x70  #define VSC3316_FSM_RX_ADDR	0x71 |