From 97c7fe61b84bf836ccf3d9aac624d4241f6e3b4c Mon Sep 17 00:00:00 2001 From: York Sun Date: Mon, 25 Mar 2013 07:33:22 +0000 Subject: [PATCH] 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 Signed-off-by: Andy Fleming --- board/freescale/t4qds/t4qds.c | 229 ++++++++++++++++++++++++++++++++++ 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 737d650dda5..a84218cdf4a 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 c5110c2bc38..dbaa7ea9d33 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