Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/shaggy...
[pandora-kernel.git] / drivers / net / niu.c
index 112ab07..5f6beab 100644 (file)
@@ -33,8 +33,8 @@
 
 #define DRV_MODULE_NAME                "niu"
 #define PFX DRV_MODULE_NAME    ": "
-#define DRV_MODULE_VERSION     "0.5"
-#define DRV_MODULE_RELDATE     "October 5, 2007"
+#define DRV_MODULE_VERSION     "0.6"
+#define DRV_MODULE_RELDATE     "January 5, 2008"
 
 static char version[] __devinitdata =
        DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
@@ -801,22 +801,90 @@ static int bcm8704_init_user_dev3(struct niu *np)
        return 0;
 }
 
-static int xcvr_init_10g(struct niu *np)
+static int mrvl88x2011_act_led(struct niu *np, int val)
+{
+       int     err;
+
+       err  = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR,
+               MRVL88X2011_LED_8_TO_11_CTL);
+       if (err < 0)
+               return err;
+
+       err &= ~MRVL88X2011_LED(MRVL88X2011_LED_ACT,MRVL88X2011_LED_CTL_MASK);
+       err |=  MRVL88X2011_LED(MRVL88X2011_LED_ACT,val);
+
+       return mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR,
+                         MRVL88X2011_LED_8_TO_11_CTL, err);
+}
+
+static int mrvl88x2011_led_blink_rate(struct niu *np, int rate)
+{
+       int     err;
+
+       err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR,
+                       MRVL88X2011_LED_BLINK_CTL);
+       if (err >= 0) {
+               err &= ~MRVL88X2011_LED_BLKRATE_MASK;
+               err |= (rate << 4);
+
+               err = mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV2_ADDR,
+                                MRVL88X2011_LED_BLINK_CTL, err);
+       }
+
+       return err;
+}
+
+static int xcvr_init_10g_mrvl88x2011(struct niu *np)
+{
+       int     err;
+
+       /* Set LED functions */
+       err = mrvl88x2011_led_blink_rate(np, MRVL88X2011_LED_BLKRATE_134MS);
+       if (err)
+               return err;
+
+       /* led activity */
+       err = mrvl88x2011_act_led(np, MRVL88X2011_LED_CTL_OFF);
+       if (err)
+               return err;
+
+       err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR,
+                       MRVL88X2011_GENERAL_CTL);
+       if (err < 0)
+               return err;
+
+       err |= MRVL88X2011_ENA_XFPREFCLK;
+
+       err = mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR,
+                        MRVL88X2011_GENERAL_CTL, err);
+       if (err < 0)
+               return err;
+
+       err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR,
+                       MRVL88X2011_PMA_PMD_CTL_1);
+       if (err < 0)
+               return err;
+
+       if (np->link_config.loopback_mode == LOOPBACK_MAC)
+               err |= MRVL88X2011_LOOPBACK;
+       else
+               err &= ~MRVL88X2011_LOOPBACK;
+
+       err = mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR,
+                        MRVL88X2011_PMA_PMD_CTL_1, err);
+       if (err < 0)
+               return err;
+
+       /* Enable PMD  */
+       return mdio_write(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR,
+                         MRVL88X2011_10G_PMD_TX_DIS, MRVL88X2011_ENA_PMDTX);
+}
+
+static int xcvr_init_10g_bcm8704(struct niu *np)
 {
        struct niu_link_config *lp = &np->link_config;
        u16 analog_stat0, tx_alarm_status;
        int err;
-       u64 val;
-
-       val = nr64_mac(XMAC_CONFIG);
-       val &= ~XMAC_CONFIG_LED_POLARITY;
-       val |= XMAC_CONFIG_FORCE_LED_ON;
-       nw64_mac(XMAC_CONFIG, val);
-
-       /* XXX shared resource, lock parent XXX */
-       val = nr64(MIF_CONFIG);
-       val |= MIF_CONFIG_INDIRECT_MODE;
-       nw64(MIF_CONFIG, val);
 
        err = bcm8704_reset(np);
        if (err)
@@ -896,6 +964,38 @@ static int xcvr_init_10g(struct niu *np)
        return 0;
 }
 
+static int xcvr_init_10g(struct niu *np)
+{
+       int phy_id, err;
+       u64 val;
+
+       val = nr64_mac(XMAC_CONFIG);
+       val &= ~XMAC_CONFIG_LED_POLARITY;
+       val |= XMAC_CONFIG_FORCE_LED_ON;
+       nw64_mac(XMAC_CONFIG, val);
+
+       /* XXX shared resource, lock parent XXX */
+       val = nr64(MIF_CONFIG);
+       val |= MIF_CONFIG_INDIRECT_MODE;
+       nw64(MIF_CONFIG, val);
+
+       phy_id = phy_decode(np->parent->port_phy, np->port);
+       phy_id = np->parent->phy_probe_info.phy_id[phy_id][np->port];
+
+       /* handle different phy types */
+       switch (phy_id & NIU_PHY_ID_MASK) {
+       case NIU_PHY_ID_MRVL88X2011:
+               err = xcvr_init_10g_mrvl88x2011(np);
+               break;
+
+       default: /* bcom 8704 */
+               err = xcvr_init_10g_bcm8704(np);
+               break;
+       }
+
+       return 0;
+}
+
 static int mii_reset(struct niu *np)
 {
        int limit, err;
@@ -1045,6 +1145,7 @@ static int niu_serdes_init(struct niu *np)
 }
 
 static void niu_init_xif(struct niu *);
+static void niu_handle_led(struct niu *, int status);
 
 static int niu_link_status_common(struct niu *np, int link_up)
 {
@@ -1066,30 +1167,83 @@ static int niu_link_status_common(struct niu *np, int link_up)
 
                spin_lock_irqsave(&np->lock, flags);
                niu_init_xif(np);
+               niu_handle_led(np, 1);
                spin_unlock_irqrestore(&np->lock, flags);
 
                netif_carrier_on(dev);
        } else if (netif_carrier_ok(dev) && !link_up) {
                niuwarn(LINK, "%s: Link is down\n", dev->name);
+               spin_lock_irqsave(&np->lock, flags);
+               niu_handle_led(np, 0);
+               spin_unlock_irqrestore(&np->lock, flags);
                netif_carrier_off(dev);
        }
 
        return 0;
 }
 
-static int link_status_10g(struct niu *np, int *link_up_p)
+static int link_status_10g_mrvl(struct niu *np, int *link_up_p)
 {
-       unsigned long flags;
-       int err, link_up;
+       int err, link_up, pma_status, pcs_status;
 
        link_up = 0;
 
-       spin_lock_irqsave(&np->lock, flags);
+       err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR,
+                       MRVL88X2011_10G_PMD_STATUS_2);
+       if (err < 0)
+               goto out;
 
-       err = -EINVAL;
-       if (np->link_config.loopback_mode != LOOPBACK_DISABLED)
+       /* Check PMA/PMD Register: 1.0001.2 == 1 */
+       err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV1_ADDR,
+                       MRVL88X2011_PMA_PMD_STATUS_1);
+       if (err < 0)
+               goto out;
+
+       pma_status = ((err & MRVL88X2011_LNK_STATUS_OK) ? 1 : 0);
+
+        /* Check PMC Register : 3.0001.2 == 1: read twice */
+       err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR,
+                       MRVL88X2011_PMA_PMD_STATUS_1);
+       if (err < 0)
                goto out;
 
+       err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV3_ADDR,
+                       MRVL88X2011_PMA_PMD_STATUS_1);
+       if (err < 0)
+               goto out;
+
+       pcs_status = ((err & MRVL88X2011_LNK_STATUS_OK) ? 1 : 0);
+
+        /* Check XGXS Register : 4.0018.[0-3,12] */
+       err = mdio_read(np, np->phy_addr, MRVL88X2011_USER_DEV4_ADDR,
+                       MRVL88X2011_10G_XGXS_LANE_STAT);
+       if (err < 0)
+               goto out;
+
+       if (err == (PHYXS_XGXS_LANE_STAT_ALINGED | PHYXS_XGXS_LANE_STAT_LANE3 |
+                   PHYXS_XGXS_LANE_STAT_LANE2 | PHYXS_XGXS_LANE_STAT_LANE1 |
+                   PHYXS_XGXS_LANE_STAT_LANE0 | PHYXS_XGXS_LANE_STAT_MAGIC |
+                   0x800))
+               link_up = (pma_status && pcs_status) ? 1 : 0;
+
+       np->link_config.active_speed = SPEED_10000;
+       np->link_config.active_duplex = DUPLEX_FULL;
+       err = 0;
+out:
+       mrvl88x2011_act_led(np, (link_up ?
+                                MRVL88X2011_LED_CTL_PCS_ACT :
+                                MRVL88X2011_LED_CTL_OFF));
+
+       *link_up_p = link_up;
+       return err;
+}
+
+static int link_status_10g_bcom(struct niu *np, int *link_up_p)
+{
+       int err, link_up;
+
+       link_up = 0;
+
        err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
                        BCM8704_PMD_RCV_SIGDET);
        if (err < 0)
@@ -1129,14 +1283,43 @@ static int link_status_10g(struct niu *np, int *link_up_p)
        err = 0;
 
 out:
+       *link_up_p = link_up;
+       return err;
+}
+
+static int link_status_10g(struct niu *np, int *link_up_p)
+{
+       unsigned long flags;
+       int err = -EINVAL;
+
+       spin_lock_irqsave(&np->lock, flags);
+
+       if (np->link_config.loopback_mode == LOOPBACK_DISABLED) {
+               int phy_id;
+
+               phy_id = phy_decode(np->parent->port_phy, np->port);
+               phy_id = np->parent->phy_probe_info.phy_id[phy_id][np->port];
+
+               /* handle different phy types */
+               switch (phy_id & NIU_PHY_ID_MASK) {
+               case NIU_PHY_ID_MRVL88X2011:
+                       err = link_status_10g_mrvl(np, link_up_p);
+                       break;
+
+               default: /* bcom 8704 */
+                       err = link_status_10g_bcom(np, link_up_p);
+                       break;
+               }
+       }
+
        spin_unlock_irqrestore(&np->lock, flags);
 
-       *link_up_p = link_up;
        return err;
 }
 
 static int link_status_1g(struct niu *np, int *link_up_p)
 {
+       struct niu_link_config *lp = &np->link_config;
        u16 current_speed, bmsr;
        unsigned long flags;
        u8 current_duplex;
@@ -1204,6 +1387,8 @@ static int link_status_1g(struct niu *np, int *link_up_p)
                                link_up = 0;
                }
        }
+       lp->active_speed = current_speed;
+       lp->active_duplex = current_duplex;
        err = 0;
 
 out:
@@ -2236,6 +2421,8 @@ static int niu_process_rx_pkt(struct niu *np, struct rx_ring_info *rp)
        skb->protocol = eth_type_trans(skb, np->dev);
        netif_receive_skb(skb);
 
+       np->dev->last_rx = jiffies;
+
        return num_rcr;
 }
 
@@ -2503,15 +2690,19 @@ static int niu_rx_error(struct niu *np, struct rx_ring_info *rp)
        u64 stat = nr64(RX_DMA_CTL_STAT(rp->rx_channel));
        int err = 0;
 
-       dev_err(np->device, PFX "%s: RX channel %u error, stat[%llx]\n",
-               np->dev->name, rp->rx_channel, (unsigned long long) stat);
-
-       niu_log_rxchan_errors(np, rp, stat);
 
        if (stat & (RX_DMA_CTL_STAT_CHAN_FATAL |
                    RX_DMA_CTL_STAT_PORT_FATAL))
                err = -EINVAL;
 
+       if (err) {
+               dev_err(np->device, PFX "%s: RX channel %u error, stat[%llx]\n",
+                       np->dev->name, rp->rx_channel,
+                       (unsigned long long) stat);
+
+               niu_log_rxchan_errors(np, rp, stat);
+       }
+
        nw64(RX_DMA_CTL_STAT(rp->rx_channel),
             stat & RX_DMA_CTL_WRITE_CLEAR_ERRS);
 
@@ -2744,13 +2935,16 @@ static int niu_device_error(struct niu *np)
        return -ENODEV;
 }
 
-static int niu_slowpath_interrupt(struct niu *np, struct niu_ldg *lp)
+static int niu_slowpath_interrupt(struct niu *np, struct niu_ldg *lp,
+                             u64 v0, u64 v1, u64 v2)
 {
-       u64 v0 = lp->v0;
-       u64 v1 = lp->v1;
-       u64 v2 = lp->v2;
+
        int i, err = 0;
 
+       lp->v0 = v0;
+       lp->v1 = v1;
+       lp->v2 = v2;
+
        if (v1 & 0x00000000ffffffffULL) {
                u32 rx_vec = (v1 & 0xffffffff);
 
@@ -2759,8 +2953,13 @@ static int niu_slowpath_interrupt(struct niu *np, struct niu_ldg *lp)
 
                        if (rx_vec & (1 << rp->rx_channel)) {
                                int r = niu_rx_error(np, rp);
-                               if (r)
+                               if (r) {
                                        err = r;
+                               } else {
+                                       if (!v0)
+                                               nw64(RX_DMA_CTL_STAT(rp->rx_channel),
+                                                    RX_DMA_CTL_STAT_MEX);
+                               }
                        }
                }
        }
@@ -2798,7 +2997,7 @@ static int niu_slowpath_interrupt(struct niu *np, struct niu_ldg *lp)
        if (err)
                niu_enable_interrupts(np, 0);
 
-       return -EINVAL;
+       return err;
 }
 
 static void niu_rxchan_intr(struct niu *np, struct rx_ring_info *rp,
@@ -2900,7 +3099,7 @@ static irqreturn_t niu_interrupt(int irq, void *dev_id)
        }
 
        if (unlikely((v0 & ((u64)1 << LDN_MIF)) || v1 || v2)) {
-               int err = niu_slowpath_interrupt(np, lp);
+               int err = niu_slowpath_interrupt(np, lp, v0, v1, v2);
                if (err)
                        goto out;
        }
@@ -3915,16 +4114,14 @@ static int niu_init_ipp(struct niu *np)
        return 0;
 }
 
-static void niu_init_xif_xmac(struct niu *np)
+static void niu_handle_led(struct niu *np, int status)
 {
-       struct niu_link_config *lp = &np->link_config;
        u64 val;
-
        val = nr64_mac(XMAC_CONFIG);
 
        if ((np->flags & NIU_FLAGS_10G) != 0 &&
            (np->flags & NIU_FLAGS_FIBER) != 0) {
-               if (netif_carrier_ok(np->dev)) {
+               if (status) {
                        val |= XMAC_CONFIG_LED_POLARITY;
                        val &= ~XMAC_CONFIG_FORCE_LED_ON;
                } else {
@@ -3933,6 +4130,15 @@ static void niu_init_xif_xmac(struct niu *np)
                }
        }
 
+       nw64_mac(XMAC_CONFIG, val);
+}
+
+static void niu_init_xif_xmac(struct niu *np)
+{
+       struct niu_link_config *lp = &np->link_config;
+       u64 val;
+
+       val = nr64_mac(XMAC_CONFIG);
        val &= ~XMAC_CONFIG_SEL_POR_CLK_SRC;
 
        val |= XMAC_CONFIG_TX_OUTPUT_EN;
@@ -4776,6 +4982,8 @@ static int niu_close(struct net_device *dev)
 
        niu_free_channels(np);
 
+       niu_handle_led(np, 0);
+
        return 0;
 }
 
@@ -5180,7 +5388,8 @@ static int niu_start_xmit(struct sk_buff *skb, struct net_device *dev)
                }
                kfree_skb(skb);
                skb = skb_new;
-       }
+       } else
+               skb_orphan(skb);
 
        align = ((unsigned long) skb->data & (16 - 1));
        headroom = align + sizeof(struct tx_pkt_hdr);
@@ -6268,7 +6477,8 @@ static int __devinit phy_record(struct niu_parent *parent,
        if (dev_id_1 < 0 || dev_id_2 < 0)
                return 0;
        if (type == PHY_TYPE_PMA_PMD || type == PHY_TYPE_PCS) {
-               if ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8704)
+               if (((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8704) &&
+                   ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_MRVL88X2011))
                        return 0;
        } else {
                if ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM5464R)