Patchwork [U-Boot,v2,batch,2,14/23] powerpc/t4240qds: Add voltage ID support

login
register
mail settings
Submitter York Sun
Date March 25, 2013, 5:33 p.m.
Message ID <1364232811-30856-14-git-send-email-yorksun@freescale.com>
Download mbox | patch
Permalink /patch/230793/
State Accepted, archived
Delegated to: Andy Fleming
Headers show

Comments

York Sun - March 25, 2013, 5:33 p.m.
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>
---
 board/freescale/t4qds/t4qds.c |  229 +++++++++++++++++++++++++++++++++++++++++
 include/configs/t4qds.h       |   12 ++-
 2 files changed, 239 insertions(+), 2 deletions(-)

Patch

diff --git a/board/freescale/t4qds/t4qds.c b/board/freescale/t4qds/t4qds.c
index e8c6998..a9cea0c 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 c5110c2..dbaa7ea 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