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>
This commit is contained in:
York Sun 2013-03-25 07:33:22 +00:00 committed by Andy Fleming
parent e1d5a2773f
commit 97c7fe61b8
2 changed files with 239 additions and 2 deletions

View File

@ -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();

View File

@ -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