]> git.karo-electronics.de Git - karo-tx-redboot.git/blobdiff - packages/devs/eth/fec/v2_0/src/if_fec.c
TX53 Release 2011-12-20
[karo-tx-redboot.git] / packages / devs / eth / fec / v2_0 / src / if_fec.c
index c2b6e61641ae15c30ac96fa8456b084c4fadaa39..ff9d2867ddd0eed3599e9ac58f9c15665aa00187 100644 (file)
@@ -299,29 +299,10 @@ mxc_fec_set_mac_address(volatile mxc_fec_reg_t *hw_reg, unsigned char *enaddr)
        mxc_fec_reg_write(hw_reg, paur, value << 16);
 }
 
-/*!
- * This function enables the FEC for reception of packets
- */
-static void
-mxc_fec_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
+#ifdef CYGOPT_HAL_ARM_MXC_FEC_MIIGSK
+static int mxc_fec_mii_setup(mxc_fec_priv_t *priv)
 {
-       mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
-       volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
-
-       if (!(priv && hw_reg)) {
-               diag_printf("BUG[start]: FEC driver not initialized\n");
-               return;
-       }
-       if (enaddr == NULL) {
-               diag_printf("BUG[start]: no MAC address supplied\n");
-               return;
-       }
-       mxc_fec_set_mac_address(hw_reg, enaddr);
-
-       priv->tx_busy = 0;
-       mxc_fec_reg_write(hw_reg, rdar, mxc_fec_reg_read(hw_reg, rdar) | FEC_RX_TX_ACTIVE);
-       mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) | FEC_ETHER_EN);
-#ifdef CYGPKG_HAL_ARM_MX25
+       volatile mxc_fec_reg_t *hw_reg = priv->hw_reg;
        /*
         * setup the MII gasket for RMII mode
         */
@@ -334,7 +315,9 @@ mxc_fec_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
                hal_delay_us(FEC_COMMON_TICK);
 
        /* configure gasket for RMII, 50 MHz, no loopback, and no echo */
-       mxc_fec_reg_write16(hw_reg, miigsk_cfgr, MIIGSK_CFGR_IF_MODE_RMII);
+       mxc_fec_reg_write16(hw_reg, miigsk_cfgr, MIIGSK_CFGR_IF_MODE_RMII |
+                                               ((!priv || (priv->status & FEC_STATUS_100M)) ?
+                                                       0 : MIIGSK_CFGR_FRCONT));
 
        /* re-enable the gasket */
        mxc_fec_reg_write16(hw_reg, miigsk_enr, MIIGSK_ENR_EN);
@@ -344,10 +327,40 @@ mxc_fec_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
        while ((mxc_fec_reg_read16(hw_reg, miigsk_enr) & MIIGSK_ENR_READY) == 0) {
                if (--max_loops <= 0) {
                        diag_printf("WAIT for MII Gasket ready timed out\n");
-                       break;
+                       return -1;
                }
        }
+       return 0;
+}
+#else
+static inline int mxc_fec_mii_setup(mxc_fec_priv_t *priv)
+{
+       return 0;
+}
 #endif
+
+/*!
+ * This function enables the FEC for reception of packets
+ */
+static void
+mxc_fec_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
+{
+       mxc_fec_priv_t *priv = sc ? sc->driver_private : NULL;
+       volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
+
+       if (!(priv && hw_reg)) {
+               diag_printf("BUG[start]: FEC driver not initialized\n");
+               return;
+       }
+       if (enaddr == NULL) {
+               diag_printf("BUG[start]: no MAC address supplied\n");
+               return;
+       }
+       mxc_fec_set_mac_address(hw_reg, enaddr);
+
+       priv->tx_busy = 0;
+       mxc_fec_reg_write(hw_reg, rdar, FEC_RX_TX_ACTIVE);
+       mxc_fec_reg_write(hw_reg, ecr, FEC_ETHER_EN);
 }
 
 /*!
@@ -412,19 +425,16 @@ mxc_fec_can_send(struct eth_drv_sc *sc)
 #ifdef CYGPKG_DEVS_ETH_PHY
                unsigned short value;
                value = _eth_phy_state(priv->phy);
+               if (value & ETH_PHY_STAT_LINK) {
+                       mxc_fec_phy_status(priv, value, true);
+               }
 #else
                unsigned short value;
                mxc_fec_mii_read(hw_reg, priv->phy_addr, 1, &value);
-#endif
                if (value & PHY_STATUS_LINK_ST) {
-                       if (!(priv->status & FEC_STATUS_LINK_ON)) {
-                               mxc_fec_phy_status(priv, value, true);
-                       }
-               } else {
-                       if (priv->status & FEC_STATUS_LINK_ON) {
-                               mxc_fec_phy_status(priv, value, true);
-                       }
+                       mxc_fec_phy_status(priv, 0, true);
                }
+#endif
        }
 
        return priv->status & FEC_STATUS_LINK_ON;
@@ -576,7 +586,7 @@ mxc_fec_check_rx_bd(struct eth_drv_sc *sc)
                        p++;
                }
                priv->rx_cur = p;
-               mxc_fec_reg_write(hw_reg, rdar, mxc_fec_reg_read(hw_reg, rdar) | FEC_RX_TX_ACTIVE);
+               mxc_fec_reg_write(hw_reg, rdar, FEC_RX_TX_ACTIVE);
        }
 }
 
@@ -590,6 +600,8 @@ mxc_fec_poll(struct eth_drv_sc *sc)
        volatile mxc_fec_reg_t *hw_reg = priv ? priv->hw_reg : NULL;
        unsigned long value;
        int dbg = net_debug;
+       static unsigned long last_poll;
+       int poll_intvl = (priv->status & FEC_STATUS_LINK_ON) ? 100 : 10;
 
        if (priv == NULL || hw_reg == NULL) {
                diag_printf("BUG[POLL]: FEC driver not initialized\n");
@@ -609,22 +621,43 @@ mxc_fec_poll(struct eth_drv_sc *sc)
                priv->tx_busy = 0;
        } else {
                if (value & FEC_EVENT_TX) {
+                       last_poll = 0;
                        sc->funs->eth_drv->tx_done(sc, priv->tx_key, 0);
                        priv->tx_busy = 0;
                }
        }
-
        if (value & FEC_EVENT_RX) {
+               last_poll = 0;
                mxc_fec_check_rx_bd(sc);
        }
 
        if (value & FEC_EVENT_HBERR) {
-               diag_printf("WARNGING[POLL]: Hearbeat error!\n");
+               diag_printf("WARNGING[POLL]: Heartbeat error!\n");
        }
 
        if (value & FEC_EVENT_EBERR) {
                diag_printf("WARNING[POLL]: Ethernet Bus Error!\n");
        }
+
+       if (value & (FEC_EVENT_TX_ERR | FEC_EVENT_HBERR | FEC_EVENT_EBERR) ||
+               last_poll++ > poll_intvl) {
+#ifdef CYGPKG_DEVS_ETH_PHY
+               unsigned short value;
+               value = _eth_phy_state(priv->phy);
+               if (!(value & ETH_PHY_STAT_LINK) ^
+                       !(priv->status & FEC_STATUS_LINK_ON)) {
+                       mxc_fec_phy_status(priv, value, true);
+               }
+#else
+               unsigned short value;
+               mxc_fec_mii_read(hw_reg, priv->phy_addr, 1, &value);
+               if (!(value & PHY_STATUS_LINK_ST) ^
+                       !(priv->status & FEC_STATUS_LINK_ON)) {
+                       mxc_fec_phy_status(priv, 0, true);
+               }
+#endif
+               last_poll = 0;
+       }
 }
 
 static int
@@ -678,7 +711,7 @@ mxc_fec_chip_init(mxc_fec_priv_t *dev)
        unsigned long ipg_clk;
        unsigned long clkdiv;
 
-       mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) | FEC_RESET);
+       mxc_fec_reg_write(hw_reg, ecr, FEC_RESET);
        while (mxc_fec_reg_read(hw_reg, ecr) & FEC_RESET) {
                hal_delay_us(FEC_COMMON_TICK);
        }
@@ -714,13 +747,15 @@ mxc_fec_chip_init(mxc_fec_priv_t *dev)
        /* must be done before enabling the MII gasket
         * (otherwise MIIGSK_ENR_READY will never assert)
         */
-       mxc_fec_reg_write(hw_reg, ecr, mxc_fec_reg_read(hw_reg, ecr) | FEC_ETHER_EN);
+       mxc_fec_reg_write(hw_reg, ecr, FEC_ETHER_EN);
 }
 
 static void mxc_fec_phy_status(mxc_fec_priv_t *dev, unsigned short value, bool show)
 {
+       int changed = 0;
 #ifdef CYGPKG_DEVS_ETH_PHY
        if (value & ETH_PHY_STAT_LINK) {
+               changed = !(dev->status & FEC_STATUS_LINK_ON);
                dev->status |= FEC_STATUS_LINK_ON;
                if (value & ETH_PHY_STAT_FDX) {
                        dev->status |= FEC_STATUS_FULL_DPLX;
@@ -728,18 +763,23 @@ static void mxc_fec_phy_status(mxc_fec_priv_t *dev, unsigned short value, bool s
                        dev->status &= ~FEC_STATUS_FULL_DPLX;
                }
                if (value & ETH_PHY_STAT_100MB) {
+                       changed |= !(dev->status & ETH_PHY_STAT_100MB);
                        dev->status |= FEC_STATUS_100M;
                } else {
+                       changed |= !!(dev->status & ETH_PHY_STAT_100MB);
                        dev->status &= ~FEC_STATUS_100M;
                }
        } else {
+               changed = !!(dev->status & FEC_STATUS_LINK_ON);
                dev->status &= ~FEC_STATUS_LINK_ON;
        }
 #else
        mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_STATUS_REG, &value);
        if (value & PHY_STATUS_LINK_ST) {
+               changed |= !(dev->status & FEC_STATUS_LINK_ON);
                dev->status |= FEC_STATUS_LINK_ON;
        } else {
+               changed |= dev->status & FEC_STATUS_LINK_ON;
                dev->status &= ~FEC_STATUS_LINK_ON;
        }
 
@@ -750,12 +790,32 @@ static void mxc_fec_phy_status(mxc_fec_priv_t *dev, unsigned short value, bool s
                dev->status &= ~FEC_STATUS_FULL_DPLX;
        }
        if (value & PHY_DIAG_RATE) {
+               changed |= !(dev->status & FEC_STATUS_100M);
                dev->status |= FEC_STATUS_100M;
        } else {
+               changed |= dev->status & FEC_STATUS_100M;
                dev->status &= ~FEC_STATUS_100M;
        }
 #endif
-       if (!show) {
+       if (changed) {
+               if (dev->status & FEC_STATUS_FULL_DPLX) {
+                       mxc_fec_reg_write(dev->hw_reg, tcr,
+                                                       (mxc_fec_reg_read(dev->hw_reg, tcr) & ~FEC_TCR_HBC) |
+                                                       FEC_TCR_FDEN);
+                       mxc_fec_reg_write(dev->hw_reg, rcr,
+                                                       (mxc_fec_reg_read(dev->hw_reg, rcr) & ~FEC_RCR_DRT) |
+                                                       FEC_RCR_FCE);
+               } else {
+                       mxc_fec_reg_write(dev->hw_reg, tcr,
+                                                       (mxc_fec_reg_read(dev->hw_reg, tcr) & ~FEC_TCR_FDEN) |
+                                                       FEC_TCR_HBC);
+                       mxc_fec_reg_write(dev->hw_reg, rcr,
+                                                       (mxc_fec_reg_read(dev->hw_reg, rcr) & ~FEC_RCR_FCE) |
+                                                       FEC_RCR_DRT);
+               }
+               mxc_fec_mii_setup(dev);
+       }
+       if (!show || !changed) {
                return;
        }
        if (dev->status & FEC_STATUS_LINK_ON) {
@@ -778,7 +838,7 @@ mxc_fec_phy_init(mxc_fec_priv_t *dev)
        unsigned short value = 0;
        unsigned long timeout = FEC_COMMON_TIMEOUT;
 
-       /*Reset PHY*/
+       /* Reset PHY */
        mxc_fec_mii_write(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG, PHY_CTRL_RESET);
        while (timeout--) {
                if (mxc_fec_mii_read(dev->hw_reg, dev->phy_addr, PHY_CTRL_REG, &value)) {
@@ -913,9 +973,9 @@ mxc_fec_phy_init(mxc_fec_priv_t *dev)
        }
 #endif
        diag_printf("FEC: [ %s ] [ %s ] [ %s ]:\n",
-               (dev->status&FEC_STATUS_FULL_DPLX)?"FULL_DUPLEX":"HALF_DUPLEX",
-               (dev->status&FEC_STATUS_LINK_ON)?"connected":"disconnected",
-               (dev->status&FEC_STATUS_100M)?"100M bps":"10M bps");
+               (dev->status & FEC_STATUS_FULL_DPLX) ? "FULL_DUPLEX" : "HALF_DUPLEX",
+               (dev->status & FEC_STATUS_LINK_ON) ? "connected" : "disconnected",
+               (dev->status & FEC_STATUS_100M) ? "100M bps" : "10M bps");
 #endif
        return true;
 }