]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - board/freescale/b4860qds/b4860qds.c
Merge branch 'rmobile' of git://git.denx.de/u-boot-sh
[karo-tx-uboot.git] / board / freescale / b4860qds / b4860qds.c
index d9c88a074f88c923a3c5ef7bbd164aa4391de870..6a8fca61a0d451b6c407c87a3397bde2f1557eaa 100644 (file)
 #include <asm/fsl_portals.h>
 #include <asm/fsl_liodn.h>
 #include <fm_eth.h>
+#include <hwconfig.h>
 
 #include "../common/qixis.h"
 #include "../common/vsc3316_3308.h"
 #include "../common/idt8t49n222a_serdes_clk.h"
+#include "../common/zm7300.h"
 #include "b4860qds.h"
 #include "b4860qds_qixis.h"
 #include "b4860qds_crossbar_con.h"
@@ -94,12 +96,246 @@ int select_i2c_ch_pca(u8 ch)
        return 0;
 }
 
+/*
+ * read_voltage from sensor on I2C bus
+ * We use average of 4 readings, waiting for 532us befor another reading
+ */
+#define WAIT_FOR_ADC   532     /* wait for 532 microseconds for ADC */
+#define NUM_READINGS   4       /* prefer to be power of 2 for efficiency */
+
+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;
+}
+
+static int adjust_vdd(ulong vdd_override)
+{
+       int re_enable = disable_interrupts();
+       ccsr_gur_t __iomem *gur =
+               (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
+       u32 fusesr;
+       u8 vid;
+       int vdd_target, vdd_last;
+       int existing_voltage, temp_voltage, voltage; /* all in 1/10 mV */
+       int ret;
+       unsigned int orig_i2c_speed;
+       unsigned long vdd_string_override;
+       char *vdd_string;
+       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_pca(I2C_MUX_CH_VOL_MONITOR);
+       if (ret) {
+               printf("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];
+       debug("VID:Reading from from fuse,vid=%x vdd is %dmV\n",
+             vid, vdd_target/10);
+
+       /* check override variable for overriding VDD */
+       vdd_string = getenv("b4qds_vdd_mv");
+       if (vdd_override == 0 && vdd_string &&
+           !strict_strtoul(vdd_string, 10, &vdd_string_override))
+               vdd_override = vdd_string_override;
+       if (vdd_override >= 819 && vdd_override <= 1212) {
+               vdd_target = vdd_override * 10; /* convert to 1/10 mV */
+               debug("VDD override is %lu\n", vdd_override);
+       } else if (vdd_override != 0) {
+               printf("Invalid value.\n");
+       }
+
+       if (vdd_target == 0) {
+               printf("VID: VID not used\n");
+               ret = 0;
+               goto exit;
+       }
+
+       /*
+        * Read voltage monitor to check real voltage.
+        * Voltage monitor LSB is 4mv.
+        */
+       vdd_last = read_voltage();
+       if (vdd_last < 0) {
+               printf("VID: abort VID adjustment\n");
+               ret = -1;
+               goto exit;
+       }
+
+       debug("VID: Core voltage is at %d mV\n", vdd_last);
+       ret = select_i2c_ch_pca(I2C_MUX_CH_DPM);
+       if (ret) {
+               printf("VID: I2c failed to switch channel to DPM\n");
+               ret = -1;
+               goto exit;
+       }
+
+       /* Round up to the value of step of Voltage regulator */
+       voltage = roundup(vdd_target, ZM_STEP);
+       debug("VID: rounded up voltage = %d\n", voltage);
+
+       /* lower the speed to 100kHz to access ZM7300 device */
+       debug("VID: Setting bus speed to 100KHz if not already set\n");
+       orig_i2c_speed = i2c_get_bus_speed();
+       if (orig_i2c_speed != 100000)
+               i2c_set_bus_speed(100000);
+
+       /* Read the existing level on board, if equal to requsted one,
+          no need to re-set */
+       existing_voltage = zm_read_voltage();
+
+       /* allowing the voltage difference of one step 0.0125V acceptable */
+       if ((existing_voltage >= voltage) &&
+           (existing_voltage < (voltage + ZM_STEP))) {
+               debug("VID: voltage already set as requested,returning\n");
+               ret = existing_voltage;
+               goto out;
+       }
+       debug("VID: Changing voltage for board from %dmV to %dmV\n",
+             existing_voltage/10, voltage/10);
+
+       if (zm_disable_wp() < 0) {
+               ret = -1;
+               goto out;
+       }
+       /* Change Voltage: the change is done through all the steps in the
+          way, to avoid reset to the board due to power good signal fail
+          in big voltage change gap jump.
+       */
+       if (existing_voltage > voltage) {
+               temp_voltage = existing_voltage - ZM_STEP;
+                       while (temp_voltage >= voltage) {
+                               ret = zm_write_voltage(temp_voltage);
+                               if (ret == temp_voltage) {
+                                       temp_voltage -= ZM_STEP;
+                               } else {
+                                       /* ZM7300 device failed to set
+                                        * the voltage */
+                                       printf
+                                       ("VID:Stepping down vol failed:%dmV\n",
+                                        temp_voltage/10);
+                                    ret = -1;
+                                    goto out;
+                               }
+                       }
+       } else {
+               temp_voltage = existing_voltage + ZM_STEP;
+                       while (temp_voltage < (voltage + ZM_STEP)) {
+                               ret = zm_write_voltage(temp_voltage);
+                               if (ret == temp_voltage) {
+                                       temp_voltage += ZM_STEP;
+                               } else {
+                                       /* ZM7300 device failed to set
+                                        * the voltage */
+                                       printf
+                                       ("VID:Stepping up vol failed:%dmV\n",
+                                        temp_voltage/10);
+                                    ret = -1;
+                                    goto out;
+                               }
+                       }
+       }
+
+       if (zm_enable_wp() < 0)
+               ret = -1;
+
+       /* restore the speed to 400kHz */
+out:   debug("VID: Restore the I2C bus speed to %dKHz\n",
+                               orig_i2c_speed/1000);
+       i2c_set_bus_speed(orig_i2c_speed);
+       if (ret < 0)
+               goto exit;
+
+       ret = select_i2c_ch_pca(I2C_MUX_CH_VOL_MONITOR);
+       if (ret) {
+               printf("VID: I2c failed to switch channel\n");
+               ret = -1;
+               goto exit;
+       }
+       vdd_last = read_voltage();
+       select_i2c_ch_pca(I2C_CH_DEFAULT);
+
+       if (vdd_last > 0)
+               printf("VID: Core voltage %d mV\n", vdd_last);
+       else
+               ret = -1;
+
+exit:
+       if (re_enable)
+               enable_interrupts();
+       return ret;
+}
+
 int configure_vsc3316_3308(void)
 {
        ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
        unsigned int num_vsc16_con, num_vsc08_con;
        u32 serdes1_prtcl, serdes2_prtcl;
        int ret;
+       char buffer[HWCONFIG_BUFFER_SIZE];
+       char *buf = NULL;
 
        serdes1_prtcl = in_be32(&gur->rcwsr[4]) &
                        FSL_CORENET2_RCWSR4_SRDS1_PRTCL;
@@ -152,15 +388,18 @@ int configure_vsc3316_3308(void)
                }
                break;
 
+       case 0x01:
        case 0x02:
        case 0x04:
        case 0x05:
        case 0x06:
+       case 0x07:
        case 0x08:
        case 0x09:
        case 0x0A:
        case 0x0B:
        case 0x0C:
+       case 0x2F:
        case 0x30:
        case 0x32:
        case 0x33:
@@ -254,18 +493,21 @@ int configure_vsc3316_3308(void)
                return -1;
        }
 
+       num_vsc08_con = NUM_CON_VSC3308;
+       /* Configure VSC3308 crossbar switch */
+       ret = select_i2c_ch_pca(I2C_CH_VSC3308);
        switch (serdes2_prtcl) {
+#ifdef CONFIG_PPC_B4420
+       case 0x9d:
+#endif
        case 0x9E:
        case 0x9A:
        case 0x98:
-       case 0xb2:
+       case 0x48:
        case 0x49:
        case 0x4E:
-       case 0x8D:
+       case 0x79:
        case 0x7A:
-               num_vsc08_con = NUM_CON_VSC3308;
-               /* Configure VSC3308 crossbar switch */
-               ret = select_i2c_ch_pca(I2C_CH_VSC3308);
                if (!ret) {
                        ret = vsc3308_config(VSC3308_TX_ADDRESS,
                                        vsc08_tx_amc, num_vsc08_con);
@@ -279,6 +521,71 @@ int configure_vsc3316_3308(void)
                        return ret;
                }
                break;
+       case 0x80:
+       case 0x81:
+       case 0x82:
+       case 0x83:
+       case 0x84:
+       case 0x85:
+       case 0x86:
+       case 0x87:
+       case 0x88:
+       case 0x89:
+       case 0x8a:
+       case 0x8b:
+       case 0x8c:
+       case 0x8d:
+       case 0x8e:
+       case 0xb1:
+       case 0xb2:
+               if (!ret) {
+                       /*
+                        * Extract hwconfig from environment since environment
+                        * is not setup properly yet
+                        */
+                       getenv_f("hwconfig", buffer, sizeof(buffer));
+                       buf = buffer;
+
+                       if (hwconfig_subarg_cmp_f("fsl_b4860_serdes2",
+                                                 "sfp_amc", "sfp", buf)) {
+#ifdef CONFIG_SYS_FSL_B4860QDS_XFI_ERR
+                               /* change default VSC3308 for XFI erratum */
+                               ret = vsc3308_config_adjust(VSC3308_TX_ADDRESS,
+                                               vsc08_tx_sfp, num_vsc08_con);
+                               if (ret)
+                                       return ret;
+
+                               ret = vsc3308_config_adjust(VSC3308_RX_ADDRESS,
+                                               vsc08_rx_sfp, num_vsc08_con);
+                               if (ret)
+                                       return ret;
+#else
+                               ret = vsc3308_config(VSC3308_TX_ADDRESS,
+                                               vsc08_tx_sfp, num_vsc08_con);
+                               if (ret)
+                                       return ret;
+
+                               ret = vsc3308_config(VSC3308_RX_ADDRESS,
+                                               vsc08_rx_sfp, num_vsc08_con);
+                               if (ret)
+                                       return ret;
+#endif
+                       } else {
+                               ret = vsc3308_config(VSC3308_TX_ADDRESS,
+                                               vsc08_tx_amc, num_vsc08_con);
+                               if (ret)
+                                       return ret;
+
+                               ret = vsc3308_config(VSC3308_RX_ADDRESS,
+                                               vsc08_rx_amc, num_vsc08_con);
+                               if (ret)
+                                       return ret;
+                       }
+
+               } else {
+                       return ret;
+               }
+               break;
        default:
                printf("WARNING:VSC crossbars programming not supported for: %x"
                                        " SerDes2 Protocol.\n", serdes2_prtcl);
@@ -494,19 +801,23 @@ int config_serdes1_refclks(void)
         * to 122.88MHz
         */
        switch (serdes1_prtcl) {
+       case 0x29:
        case 0x2A:
        case 0x2C:
        case 0x2D:
        case 0x2E:
+       case 0x01:
        case 0x02:
        case 0x04:
        case 0x05:
        case 0x06:
+       case 0x07:
        case 0x08:
        case 0x09:
        case 0x0A:
        case 0x0B:
        case 0x0C:
+       case 0x2F:
        case 0x30:
        case 0x32:
        case 0x33:
@@ -619,8 +930,13 @@ int config_serdes2_refclks(void)
         * For this SerDes2's Refclk1 need to be set to 100MHz
         */
        switch (serdes2_prtcl) {
+#ifdef CONFIG_PPC_B4420
+       case 0x9d:
+#endif
        case 0x9E:
        case 0x9A:
+               /* fallthrough */
+       case 0xb1:
        case 0xb2:
                debug("Configuring IDT for PCIe SATA for srds_prctl:%x\n",
                        serdes2_prtcl);
@@ -674,8 +990,16 @@ out:
 int board_early_init_r(void)
 {
        const unsigned int flashbase = CONFIG_SYS_FLASH_BASE;
-       const u8 flash_esel = find_tlb_idx((void *)flashbase, 1);
+       int flash_esel = find_tlb_idx((void *)flashbase, 1);
        int ret;
+       u32 svr = SVR_SOC_VER(get_svr());
+
+       /* Create law for MAPLE only for personalities having MAPLE */
+       if ((svr == SVR_B4860) || (svr == SVR_B4440) ||
+           (svr == SVR_B4420) || (svr == SVR_B4220)) {
+               set_next_law(CONFIG_SYS_MAPLE_MEM_PHYS, LAW_SIZE_16M,
+                            LAW_TRGT_IF_MAPLE);
+       }
 
        /*
         * Remap Boot flash + PROMJET region to caching-inhibited
@@ -686,8 +1010,14 @@ int board_early_init_r(void)
        flush_dcache();
        invalidate_icache();
 
-       /* invalidate existing TLB entry for flash + promjet */
-       disable_tlb(flash_esel);
+       if (flash_esel == -1) {
+               /* very unlikely unless something is messed up */
+               puts("Error: Could not find TLB for FLASH BASE\n");
+               flash_esel = 2; /* give our best effort to continue */
+       } else {
+               /* invalidate existing TLB entry for flash + promjet */
+               disable_tlb(flash_esel);
+       }
 
        set_tlb(1, flashbase, CONFIG_SYS_FLASH_BASE_PHYS,
                        MAS3_SX|MAS3_SW|MAS3_SR, MAS2_I|MAS2_G,
@@ -697,6 +1027,13 @@ int board_early_init_r(void)
 #ifdef CONFIG_SYS_DPAA_QBMAN
        setup_portals();
 #endif
+       /*
+        * Adjust core voltage according to voltage ID
+        * This function changes I2C mux to channel 2.
+        */
+       if (adjust_vdd(0) < 0)
+               printf("Warning: Adjusting core voltage failed\n");
+
        /* SerDes1 refclks need to be set again, as default clks
         * are not suitable for CPRI and onboard SGMIIs to work
         * simultaneously.
@@ -858,7 +1195,7 @@ int misc_init_r(void)
        return 0;
 }
 
-void ft_board_setup(void *blob, bd_t *bd)
+int ft_board_setup(void *blob, bd_t *bd)
 {
        phys_addr_t base;
        phys_size_t size;
@@ -884,6 +1221,8 @@ void ft_board_setup(void *blob, bd_t *bd)
        fdt_fixup_fman_ethernet(blob);
        fdt_fixup_board_enet(blob);
 #endif
+
+       return 0;
 }
 
 /*