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