Merge with rsync://git-user@source.denx.net/git/u-boot.git
[platform/kernel/u-boot.git] / cpu / ppc4xx / 440gx_enet.c
index 4502085..24e6ef3 100644 (file)
@@ -175,6 +175,98 @@ static void ppc_440x_eth_halt (struct eth_device *dev)
 extern int phy_setup_aneg (unsigned char addr);
 extern int miiphy_reset (unsigned char addr);
 
+#if defined (CONFIG_440_GX)
+int ppc_440x_eth_setup_bridge(int devnum, bd_t * bis)
+{
+       unsigned long pfc1;
+       unsigned long zmiifer;
+       unsigned long rmiifer;
+
+       mfsdr(sdr_pfc1, pfc1);
+       pfc1 = SDR0_PFC1_EPS_DECODE(pfc1);
+
+       zmiifer = 0;
+       rmiifer = 0;
+
+       switch (pfc1) {
+       case 1:
+               zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0);
+               zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1);
+               zmiifer |= ZMII_FER_RMII << ZMII_FER_V(2);
+               zmiifer |= ZMII_FER_RMII << ZMII_FER_V(3);
+               bis->bi_phymode[0] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[1] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[2] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[3] = BI_PHYMODE_ZMII;
+               break;
+       case 2:
+               zmiifer = ZMII_FER_SMII << ZMII_FER_V(0);
+               zmiifer = ZMII_FER_SMII << ZMII_FER_V(1);
+               zmiifer = ZMII_FER_SMII << ZMII_FER_V(2);
+               zmiifer = ZMII_FER_SMII << ZMII_FER_V(3);
+               bis->bi_phymode[0] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[1] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[2] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[3] = BI_PHYMODE_ZMII;
+               break;
+       case 3:
+               zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0);
+               rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2);
+               bis->bi_phymode[0] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[1] = BI_PHYMODE_NONE;
+               bis->bi_phymode[2] = BI_PHYMODE_RGMII;
+               bis->bi_phymode[3] = BI_PHYMODE_NONE;
+               break;
+       case 4:
+               zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0);
+               zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1);
+               rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (2);
+               rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (3);
+               bis->bi_phymode[0] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[1] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[2] = BI_PHYMODE_RGMII;
+               bis->bi_phymode[3] = BI_PHYMODE_RGMII;
+               break;
+       case 5:
+               zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0);
+               zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1);
+               zmiifer |= ZMII_FER_SMII << ZMII_FER_V (2);
+               rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3);
+               bis->bi_phymode[0] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[1] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[2] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[3] = BI_PHYMODE_RGMII;
+               break;
+       case 6:
+               zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0);
+               zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1);
+               rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2);
+               bis->bi_phymode[0] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[1] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[2] = BI_PHYMODE_RGMII;
+               break;
+       case 0:
+       default:
+               zmiifer = ZMII_FER_MII << ZMII_FER_V(devnum);
+               rmiifer = 0x0;
+               bis->bi_phymode[0] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[1] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[2] = BI_PHYMODE_ZMII;
+               bis->bi_phymode[3] = BI_PHYMODE_ZMII;
+               break;
+       }
+
+       /* Ensure we setup mdio for this devnum and ONLY this devnum */
+       zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum);
+
+       out32 (ZMII_FER, zmiifer);
+       out32 (RGMII_FER, rmiifer);
+
+       return ((int)pfc1);
+
+}
+#endif
+
 static int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis)
 {
        int i;
@@ -187,6 +279,9 @@ static int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis)
        unsigned short devnum;
        unsigned short reg_short;
        sys_info_t sysinfo;
+#if defined(CONFIG_440_GX)
+       int ethgroup;
+#endif
 
        EMAC_440GX_HW_PST hw_p = dev->priv;
 
@@ -198,7 +293,6 @@ static int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis)
        /* Need to get the OPB frequency so we can access the PHY */
        get_sys_info (&sysinfo);
 
-
        msr = mfmsr ();
        mtmsr (msr & ~(MSR_EE));        /* disable interrupts */
 
@@ -229,7 +323,12 @@ static int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis)
        /* MAL Channel RESET */
        /* 1st reset MAL channel */
        /* Note: writing a 0 to a channel has no effect */
+#if defined(CONFIG_440_EP) || defined(CONFIG_440_GR)
+       mtdcr (maltxcarr, (MAL_TXRX_CASR >> (hw_p->devnum*2)));
+#else
        mtdcr (maltxcarr, (MAL_TXRX_CASR >> hw_p->devnum));
+#endif
+
        mtdcr (malrxcarr, (MAL_TXRX_CASR >> hw_p->devnum));
 
        /* wait for reset */
@@ -262,17 +361,28 @@ static int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis)
        reg = 0;
        out32 (ZMII_FER, 0);
        udelay (100);
-       out32 (ZMII_FER, ZMII_FER_MDI << ZMII_FER_V (devnum));
-       out32 (ZMII_SSR, 0x11110000);
-       /* reset emac so we have access to the phy */
-       __asm__ volatile ("eieio");
-
-       out32 (EMAC_M0 + hw_p->hw_addr, EMAC_M0_SRST);
-       __asm__ volatile ("eieio");
 
-       if ((devnum == 2) || (devnum == 3))
+#if defined(CONFIG_440_EP) || defined(CONFIG_440_GR)
+       out32 (ZMII_FER, (ZMII_FER_RMII | ZMII_FER_MDI) << ZMII_FER_V (devnum));
+#elif defined(CONFIG_440_GX)
+       ethgroup = ppc_440x_eth_setup_bridge(devnum, bis);
+#else
+       if ((devnum == 0) || (devnum == 1)) {
+               out32 (ZMII_FER, (ZMII_FER_SMII | ZMII_FER_MDI) << ZMII_FER_V (devnum));
+       }
+       else { /* ((devnum == 2) || (devnum == 3)) */
+               out32 (ZMII_FER, ZMII_FER_MDI << ZMII_FER_V (devnum));
                out32 (RGMII_FER, ((RGMII_FER_RGMII << RGMII_FER_V (2)) |
                                   (RGMII_FER_RGMII << RGMII_FER_V (3))));
+       }
+
+#endif
+       out32 (ZMII_SSR, ZMII_SSR_SP << ZMII_SSR_V(devnum));
+       __asm__ volatile ("eieio");
+
+       /* reset emac so we have access to the phy */
+
+       out32 (EMAC_M0 + hw_p->hw_addr, EMAC_M0_SRST);
        __asm__ volatile ("eieio");
 
        failsafe = 1000;
@@ -322,17 +432,49 @@ static int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis)
 
        bis->bi_phynum[devnum] = reg;
 
-       /* Reset the phy */
-       miiphy_reset (reg);
+       /*
+        * Reset the phy, only if its the first time through
+        * otherwise, just check the speeds & feeds
+        */
+       if (hw_p->first_init == 0) {
+               miiphy_reset (reg);
 
-       /* Start/Restart autonegotiation */
-       phy_setup_aneg (reg);
-       udelay (1000);
+#if defined(CONFIG_440_GX)
+#if defined(CONFIG_CIS8201_PHY)
+       /*
+        * Cicada 8201 PHY needs to have an extended register whacked
+        * for RGMII mode.
+        */
+       if ( ((devnum == 2) || (devnum ==3)) && (4 == ethgroup) ) {
+               miiphy_write (reg, 23, 0x1200);
+               /*
+                * Vitesse VSC8201/Cicada CIS8201 errata:
+                * Interoperability problem with Intel 82547EI phys
+                * This work around (provided by Vitesse) changes
+                * the default timer convergence from 8ms to 12ms
+                */
+               miiphy_write (reg, 0x1f, 0x2a30);
+               miiphy_write (reg, 0x08, 0x0200);
+               miiphy_write (reg, 0x1f, 0x52b5);
+               miiphy_write (reg, 0x02, 0x0004);
+               miiphy_write (reg, 0x01, 0x0671);
+               miiphy_write (reg, 0x00, 0x8fae);
+               miiphy_write (reg, 0x1f, 0x2a30);
+               miiphy_write (reg, 0x08, 0x0000);
+               miiphy_write (reg, 0x1f, 0x0000);
+               /* end Vitesse/Cicada errata */
+       }
+#endif
+#endif
+               /* Start/Restart autonegotiation */
+               phy_setup_aneg (reg);
+               udelay (1000);
+       }
 
        miiphy_read (reg, PHY_BMSR, &reg_short);
 
        /*
-        * Wait if PHY is able of autonegotiation and autonegotiation is not complete
+        * Wait if PHY is capable of autonegotiation and autonegotiation is not complete
         */
        if ((reg_short & PHY_BMSR_AUTN_ABLE)
            && !(reg_short & PHY_BMSR_AUTN_COMP)) {
@@ -367,9 +509,18 @@ static int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis)
                        (int) speed, (duplex == HALF) ? "HALF" : "FULL");
        }
 
+#if defined(CONFIG_440_EP) || defined(CONFIG_440_GR)
+       mfsdr(sdr_mfr, reg);
+       if (speed == 100) {
+               reg = (reg & ~SDR0_MFR_ZMII_MODE_MASK) | SDR0_MFR_ZMII_MODE_RMII_100M;
+       } else {
+               reg = (reg & ~SDR0_MFR_ZMII_MODE_MASK) | SDR0_MFR_ZMII_MODE_RMII_10M;
+       }
+       mtsdr(sdr_mfr, reg);
+#endif
        /* Set ZMII/RGMII speed according to the phy link speed */
        reg = in32 (ZMII_SSR);
-       if (speed == 100)
+       if ( (speed == 100) || (speed == 1000) )
                out32 (ZMII_SSR, reg | (ZMII_SSR_SP << ZMII_SSR_V (devnum)));
        else
                out32 (ZMII_SSR,
@@ -486,8 +637,12 @@ static int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis)
        switch (devnum) {
        case 1:
                /* setup MAL tx & rx channel pointers */
-               mtdcr (maltxbattr, 0x0);
+#if defined (CONFIG_440_EP) || defined (CONFIG_440_GR)
+               mtdcr (maltxctp2r, hw_p->tx);
+#else
                mtdcr (maltxctp1r, hw_p->tx);
+#endif
+               mtdcr (maltxbattr, 0x0);
                mtdcr (malrxbattr, 0x0);
                mtdcr (malrxctp1r, hw_p->rx);
                /* set RX buffer size */
@@ -526,7 +681,11 @@ static int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis)
        }
 
        /* Enable MAL transmit and receive channels */
+#if defined(CONFIG_440_EP) || defined(CONFIG_440_GR)
+       mtdcr (maltxcasr, (MAL_TXRX_CASR >> (hw_p->devnum*2)));
+#else
        mtdcr (maltxcasr, (MAL_TXRX_CASR >> hw_p->devnum));
+#endif
        mtdcr (malrxcasr, (MAL_TXRX_CASR >> hw_p->devnum));
 
        /* set transmit enable & receive enable */
@@ -537,8 +696,9 @@ static int ppc_440x_eth_init (struct eth_device *dev, bd_t * bis)
        mode_reg |= EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K;
 
        /* set speed */
-       /* TBS: do 1GbE */
-       if (speed == _100BASET)
+       if (speed == _1000BASET)
+               mode_reg = mode_reg | EMAC_M1_MF_1000MBPS | EMAC_M1_IST;
+       else if (speed == _100BASET)
                mode_reg = mode_reg | EMAC_M1_MF_100MBPS | EMAC_M1_IST;
        else
                mode_reg = mode_reg & ~0x00C00000;      /* 10 MBPS */
@@ -801,7 +961,7 @@ int enetInt (struct eth_device *dev)
                        mtdcr (uic0sr, UIC_MTE);
                }
                /* handle MAL RX EOB  interupt from a receive */
-               /* check for EOB on valid channels            */
+               /* check for EOB on valid channels            */
                if (my_uic0msr & UIC_MRE) {
                        mal_rx_eob = mfdcr (malrxeobisr);
                        if ((mal_rx_eob & (0x80000000 >> hw_p->devnum)) != 0) { /* call emac routine for channel x */
@@ -917,9 +1077,9 @@ static void enet_rcv (struct eth_device *dev, unsigned long malisr)
                                                    MAX_ERR_LOG)
                                                        hw_p->rx_err_index =
                                                                0;
-                                       }       /* emac_erros         */
+                                       }       /* emac_erros */
                                }       /* data_len < max mtu */
-                       }       /* if data_len        */
+                       }       /* if data_len */
                        if (!data_len) {        /* no data */
                                hw_p->rx[i].ctrl |= MAL_RX_CTRL_EMPTY;  /* Free Recv Buffer */
 
@@ -1015,19 +1175,24 @@ static int ppc_440x_eth_rx (struct eth_device *dev)
 int ppc_440x_eth_initialize (bd_t * bis)
 {
        static int virgin = 0;
-       unsigned long pfc1;
        struct eth_device *dev;
        int eth_num = 0;
-
        EMAC_440GX_HW_PST hw = NULL;
 
+#if defined(CONFIG_440_GX)
+       unsigned long pfc1;
+
        mfsdr (sdr_pfc1, pfc1);
        pfc1 &= ~(0x01e00000);
        pfc1 |= 0x01200000;
        mtsdr (sdr_pfc1, pfc1);
+#endif
        /* set phy num and mode */
        bis->bi_phynum[0] = CONFIG_PHY_ADDR;
+#if defined(CONFIG_PHY1_ADDR)
        bis->bi_phynum[1] = CONFIG_PHY1_ADDR;
+#endif
+#if defined(CONFIG_440_GX)
        bis->bi_phynum[2] = CONFIG_PHY2_ADDR;
        bis->bi_phynum[3] = CONFIG_PHY3_ADDR;
        bis->bi_phymode[0] = 0;
@@ -1035,84 +1200,96 @@ int ppc_440x_eth_initialize (bd_t * bis)
        bis->bi_phymode[2] = 2;
        bis->bi_phymode[3] = 2;
 
+#if defined (CONFIG_440_GX)
+       ppc_440x_eth_setup_bridge(0, bis);
+#endif
+#endif
+
        for (eth_num = 0; eth_num < EMAC_NUM_DEV; eth_num++) {
 
                /* See if we can actually bring up the interface, otherwise, skip it */
                switch (eth_num) {
+               default:                /* fall through */
                case 0:
                        if (memcmp (bis->bi_enetaddr, "\0\0\0\0\0\0", 6) == 0) {
                                bis->bi_phymode[eth_num] = BI_PHYMODE_NONE;
                                continue;
                        }
                        break;
+#ifdef CONFIG_HAS_ETH1
                case 1:
                        if (memcmp (bis->bi_enet1addr, "\0\0\0\0\0\0", 6) == 0) {
                                bis->bi_phymode[eth_num] = BI_PHYMODE_NONE;
                                continue;
                        }
                        break;
+#endif
+#ifdef CONFIG_HAS_ETH2
                case 2:
                        if (memcmp (bis->bi_enet2addr, "\0\0\0\0\0\0", 6) == 0) {
                                bis->bi_phymode[eth_num] = BI_PHYMODE_NONE;
                                continue;
                        }
                        break;
+#endif
+#ifdef CONFIG_HAS_ETH3
                case 3:
                        if (memcmp (bis->bi_enet3addr, "\0\0\0\0\0\0", 6) == 0) {
                                bis->bi_phymode[eth_num] = BI_PHYMODE_NONE;
                                continue;
                        }
                        break;
-               default:
-                       if (memcmp (bis->bi_enetaddr, "\0\0\0\0\0\0", 6) == 0) {
-                               bis->bi_phymode[eth_num] = BI_PHYMODE_NONE;
-                               continue;
-                       }
-                       break;
+#endif
                }
 
                /* Allocate device structure */
                dev = (struct eth_device *) malloc (sizeof (*dev));
                if (dev == NULL) {
-                       printf (__FUNCTION__
-                               "Cannot allocate eth_device %d\n", eth_num);
+                       printf ("ppc_440x_eth_initialize: "
+                               "Cannot allocate eth_device %d\n", eth_num);
                        return (-1);
                }
+               memset(dev, 0, sizeof(*dev));
 
                /* Allocate our private use data */
                hw = (EMAC_440GX_HW_PST) malloc (sizeof (*hw));
                if (hw == NULL) {
-                       printf (__FUNCTION__
-                               "Cannot allocate private hw data for eth_device %d",
+                       printf ("ppc_440x_eth_initialize: "
+                               "Cannot allocate private hw data for eth_device %d",
                                eth_num);
                        free (dev);
                        return (-1);
                }
+               memset(hw, 0, sizeof(*hw));
 
                switch (eth_num) {
+               default:                /* fall through */
                case 0:
                        hw->hw_addr = 0;
                        memcpy (dev->enetaddr, bis->bi_enetaddr, 6);
                        break;
+#ifdef CONFIG_HAS_ETH1
                case 1:
                        hw->hw_addr = 0x100;
                        memcpy (dev->enetaddr, bis->bi_enet1addr, 6);
                        break;
+#endif
+#ifdef CONFIG_HAS_ETH2
                case 2:
                        hw->hw_addr = 0x400;
                        memcpy (dev->enetaddr, bis->bi_enet2addr, 6);
                        break;
+#endif
+#ifdef CONFIG_HAS_ETH3
                case 3:
                        hw->hw_addr = 0x600;
                        memcpy (dev->enetaddr, bis->bi_enet3addr, 6);
                        break;
-               default:
-                       hw->hw_addr = 0;
-                       memcpy (dev->enetaddr, bis->bi_enetaddr, 6);
-                       break;
+#endif
                }
 
                hw->devnum = eth_num;
+               hw->print_speed = 1;
 
                sprintf (dev->name, "ppc_440x_eth%d", eth_num);
                dev->priv = (void *) hw;