]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/net/ucc_geth.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6.git] / drivers / net / ucc_geth.c
index 9dd16c9b1a10a1c9cd29f36750a3faca359ab14d..52a6750b82015c85ee808bd530cf94f233361041 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006-2007 Freescale Semicondutor, Inc. All rights reserved.
+ * Copyright (C) 2006-2009 Freescale Semicondutor, Inc. All rights reserved.
  *
  * Author: Shlomi Gridish <gridish@freescale.com>
  *        Li Yang <leoli@freescale.com>
@@ -27,6 +27,7 @@
 #include <linux/mii.h>
 #include <linux/phy.h>
 #include <linux/workqueue.h>
+#include <linux/of_mdio.h>
 #include <linux/of_platform.h>
 
 #include <asm/uaccess.h>
@@ -208,9 +209,10 @@ static struct sk_buff *get_new_skb(struct ucc_geth_private *ugeth,
 {
        struct sk_buff *skb = NULL;
 
-       skb = dev_alloc_skb(ugeth->ug_info->uf_info.max_rx_buf_length +
-                                 UCC_GETH_RX_DATA_BUF_ALIGNMENT);
-
+       skb = __skb_dequeue(&ugeth->rx_recycle);
+       if (!skb)
+               skb = dev_alloc_skb(ugeth->ug_info->uf_info.max_rx_buf_length +
+                                   UCC_GETH_RX_DATA_BUF_ALIGNMENT);
        if (skb == NULL)
                return NULL;
 
@@ -1409,6 +1411,9 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth)
            (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) {
                upsmr |= UCC_GETH_UPSMR_TBIM;
        }
+       if ((ugeth->phy_interface == PHY_INTERFACE_MODE_SGMII))
+               upsmr |= UCC_GETH_UPSMR_SGMM;
+
        out_be32(&uf_regs->upsmr, upsmr);
 
        /* Disable autonegotiation in tbi mode, because by default it
@@ -1530,6 +1535,49 @@ static void adjust_link(struct net_device *dev)
        spin_unlock_irqrestore(&ugeth->lock, flags);
 }
 
+/* Initialize TBI PHY interface for communicating with the
+ * SERDES lynx PHY on the chip.  We communicate with this PHY
+ * through the MDIO bus on each controller, treating it as a
+ * "normal" PHY at the address found in the UTBIPA register.  We assume
+ * that the UTBIPA register is valid.  Either the MDIO bus code will set
+ * it to a value that doesn't conflict with other PHYs on the bus, or the
+ * value doesn't matter, as there are no other PHYs on the bus.
+ */
+static void uec_configure_serdes(struct net_device *dev)
+{
+       struct ucc_geth_private *ugeth = netdev_priv(dev);
+       struct ucc_geth_info *ug_info = ugeth->ug_info;
+       struct phy_device *tbiphy;
+
+       if (!ug_info->tbi_node) {
+               dev_warn(&dev->dev, "SGMII mode requires that the device "
+                       "tree specify a tbi-handle\n");
+               return;
+       }
+
+       tbiphy = of_phy_find_device(ug_info->tbi_node);
+       if (!tbiphy) {
+               dev_err(&dev->dev, "error: Could not get TBI device\n");
+               return;
+       }
+
+       /*
+        * If the link is already up, we must already be ok, and don't need to
+        * configure and reset the TBI<->SerDes link.  Maybe U-Boot configured
+        * everything for us?  Resetting it takes the link down and requires
+        * several seconds for it to come back.
+        */
+       if (phy_read(tbiphy, ENET_TBI_MII_SR) & TBISR_LSTATUS)
+               return;
+
+       /* Single clk mode, mii mode off(for serdes communication) */
+       phy_write(tbiphy, ENET_TBI_MII_ANA, TBIANA_SETTINGS);
+
+       phy_write(tbiphy, ENET_TBI_MII_TBICON, TBICON_CLK_SELECT);
+
+       phy_write(tbiphy, ENET_TBI_MII_CR, TBICR_SETTINGS);
+}
+
 /* Configure the PHY for dev.
  * returns 0 if success.  -1 if failure
  */
@@ -1543,14 +1591,19 @@ static int init_phy(struct net_device *dev)
        priv->oldspeed = 0;
        priv->oldduplex = -1;
 
-       phydev = phy_connect(dev, ug_info->phy_bus_id, &adjust_link, 0,
-                            priv->phy_interface);
-
-       if (IS_ERR(phydev)) {
-               printk("%s: Could not attach to PHY\n", dev->name);
-               return PTR_ERR(phydev);
+       phydev = of_phy_connect(dev, ug_info->phy_node, &adjust_link, 0,
+                               priv->phy_interface);
+       if (!phydev)
+               phydev = of_phy_connect_fixed_link(dev, &adjust_link,
+                                                  priv->phy_interface);
+       if (!phydev) {
+               dev_err(&dev->dev, "Could not attach to PHY\n");
+               return -ENODEV;
        }
 
+       if (priv->phy_interface == PHY_INTERFACE_MODE_SGMII)
+               uec_configure_serdes(dev);
+
        phydev->supported &= (ADVERTISED_10baseT_Half |
                                 ADVERTISED_10baseT_Full |
                                 ADVERTISED_100baseT_Half |
@@ -1934,6 +1987,8 @@ static void ucc_geth_memclean(struct ucc_geth_private *ugeth)
                iounmap(ugeth->ug_regs);
                ugeth->ug_regs = NULL;
        }
+
+       skb_queue_purge(&ugeth->rx_recycle);
 }
 
 static void ucc_geth_set_multi(struct net_device *dev)
@@ -2150,6 +2205,8 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
                return -ENOMEM;
        }
 
+       skb_queue_head_init(&ugeth->rx_recycle);
+
        return 0;
 }
 
@@ -3121,7 +3178,7 @@ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev)
 #endif
        spin_unlock_irq(&ugeth->lock);
 
-       return 0;
+       return NETDEV_TX_OK;
 }
 
 static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit)
@@ -3156,8 +3213,10 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit
                        if (netif_msg_rx_err(ugeth))
                                ugeth_err("%s, %d: ERROR!!! skb - 0x%08x",
                                           __func__, __LINE__, (u32) skb);
-                       if (skb)
-                               dev_kfree_skb_any(skb);
+                       if (skb) {
+                               skb->data = skb->head + NET_SKB_PAD;
+                               __skb_queue_head(&ugeth->rx_recycle, skb);
+                       }
 
                        ugeth->rx_skbuff[rxQ][ugeth->skb_currx[rxQ]] = NULL;
                        dev->stats.rx_dropped++;
@@ -3215,6 +3274,8 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ)
 
        /* Normal processing. */
        while ((bd_status & T_R) == 0) {
+               struct sk_buff *skb;
+
                /* BD contains already transmitted buffer.   */
                /* Handle the transmitted buffer and release */
                /* the BD to be used with the current frame  */
@@ -3224,9 +3285,16 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ)
 
                dev->stats.tx_packets++;
 
-               /* Free the sk buffer associated with this TxBD */
-               dev_kfree_skb_irq(ugeth->
-                                 tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]]);
+               skb = ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]];
+
+               if (skb_queue_len(&ugeth->rx_recycle) < RX_BD_RING_LEN &&
+                            skb_recycle_check(skb,
+                                   ugeth->ug_info->uf_info.max_rx_buf_length +
+                                   UCC_GETH_RX_DATA_BUF_ALIGNMENT))
+                       __skb_queue_head(&ugeth->rx_recycle, skb);
+               else
+                       dev_kfree_skb(skb);
+
                ugeth->tx_skbuff[txQ][ugeth->skb_dirtytx[txQ]] = NULL;
                ugeth->skb_dirtytx[txQ] =
                    (ugeth->skb_dirtytx[txQ] +
@@ -3255,13 +3323,19 @@ static int ucc_geth_poll(struct napi_struct *napi, int budget)
 
        ug_info = ugeth->ug_info;
 
+       /* Tx event processing */
+       spin_lock(&ugeth->lock);
+       for (i = 0; i < ug_info->numQueuesTx; i++)
+               ucc_geth_tx(ugeth->ndev, i);
+       spin_unlock(&ugeth->lock);
+
        howmany = 0;
        for (i = 0; i < ug_info->numQueuesRx; i++)
                howmany += ucc_geth_rx(ugeth, i, budget - howmany);
 
        if (howmany < budget) {
                napi_complete(napi);
-               setbits32(ugeth->uccf->p_uccm, UCCE_RX_EVENTS);
+               setbits32(ugeth->uccf->p_uccm, UCCE_RX_EVENTS | UCCE_TX_EVENTS);
        }
 
        return howmany;
@@ -3275,8 +3349,6 @@ static irqreturn_t ucc_geth_irq_handler(int irq, void *info)
        struct ucc_geth_info *ug_info;
        register u32 ucce;
        register u32 uccm;
-       register u32 tx_mask;
-       u8 i;
 
        ugeth_vdbg("%s: IN", __func__);
 
@@ -3290,27 +3362,14 @@ static irqreturn_t ucc_geth_irq_handler(int irq, void *info)
        out_be32(uccf->p_ucce, ucce);
 
        /* check for receive events that require processing */
-       if (ucce & UCCE_RX_EVENTS) {
+       if (ucce & (UCCE_RX_EVENTS | UCCE_TX_EVENTS)) {
                if (napi_schedule_prep(&ugeth->napi)) {
-                       uccm &= ~UCCE_RX_EVENTS;
+                       uccm &= ~(UCCE_RX_EVENTS | UCCE_TX_EVENTS);
                        out_be32(uccf->p_uccm, uccm);
                        __napi_schedule(&ugeth->napi);
                }
        }
 
-       /* Tx event processing */
-       if (ucce & UCCE_TX_EVENTS) {
-               spin_lock(&ugeth->lock);
-               tx_mask = UCC_GETH_UCCE_TXB0;
-               for (i = 0; i < ug_info->numQueuesTx; i++) {
-                       if (ucce & tx_mask)
-                               ucc_geth_tx(dev, i);
-                       ucce &= ~tx_mask;
-                       tx_mask <<= 1;
-               }
-               spin_unlock(&ugeth->lock);
-       }
-
        /* Errors and other events */
        if (ucce & UCCE_OTHER) {
                if (ucce & UCC_GETH_UCCE_BSY)
@@ -3339,6 +3398,37 @@ static void ucc_netpoll(struct net_device *dev)
 }
 #endif /* CONFIG_NET_POLL_CONTROLLER */
 
+static int ucc_geth_set_mac_addr(struct net_device *dev, void *p)
+{
+       struct ucc_geth_private *ugeth = netdev_priv(dev);
+       struct sockaddr *addr = p;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+       /*
+        * If device is not running, we will set mac addr register
+        * when opening the device.
+        */
+       if (!netif_running(dev))
+               return 0;
+
+       spin_lock_irq(&ugeth->lock);
+       init_mac_station_addr_regs(dev->dev_addr[0],
+                                  dev->dev_addr[1],
+                                  dev->dev_addr[2],
+                                  dev->dev_addr[3],
+                                  dev->dev_addr[4],
+                                  dev->dev_addr[5],
+                                  &ugeth->ug_regs->macstnaddr1,
+                                  &ugeth->ug_regs->macstnaddr2);
+       spin_unlock_irq(&ugeth->lock);
+
+       return 0;
+}
+
 /* Called when something needs to use the ethernet device */
 /* Returns 0 for success. */
 static int ucc_geth_open(struct net_device *dev)
@@ -3506,6 +3596,8 @@ static phy_interface_t to_phy_interface(const char *phy_connection_type)
                return PHY_INTERFACE_MODE_RGMII_RXID;
        if (strcasecmp(phy_connection_type, "rtbi") == 0)
                return PHY_INTERFACE_MODE_RTBI;
+       if (strcasecmp(phy_connection_type, "sgmii") == 0)
+               return PHY_INTERFACE_MODE_SGMII;
 
        return PHY_INTERFACE_MODE_MII;
 }
@@ -3515,7 +3607,7 @@ static const struct net_device_ops ucc_geth_netdev_ops = {
        .ndo_stop               = ucc_geth_close,
        .ndo_start_xmit         = ucc_geth_start_xmit,
        .ndo_validate_addr      = eth_validate_addr,
-       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_set_mac_address    = ucc_geth_set_mac_addr,
        .ndo_change_mtu         = eth_change_mtu,
        .ndo_set_multicast_list = ucc_geth_set_multi,
        .ndo_tx_timeout         = ucc_geth_timeout,
@@ -3528,15 +3620,11 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
 {
        struct device *device = &ofdev->dev;
        struct device_node *np = ofdev->node;
-       struct device_node *mdio;
        struct net_device *dev = NULL;
        struct ucc_geth_private *ugeth = NULL;
        struct ucc_geth_info *ug_info;
        struct resource res;
-       struct device_node *phy;
        int err, ucc_num, max_speed = 0;
-       const phandle *ph;
-       const u32 *fixed_link;
        const unsigned int *prop;
        const char *sprop;
        const void *mac_addr;
@@ -3552,6 +3640,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
                PHY_INTERFACE_MODE_RMII, PHY_INTERFACE_MODE_RGMII,
                PHY_INTERFACE_MODE_GMII, PHY_INTERFACE_MODE_RGMII,
                PHY_INTERFACE_MODE_TBI, PHY_INTERFACE_MODE_RTBI,
+               PHY_INTERFACE_MODE_SGMII,
        };
 
        ugeth_vdbg("%s: IN", __func__);
@@ -3633,48 +3722,17 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
 
        ug_info->uf_info.regs = res.start;
        ug_info->uf_info.irq = irq_of_parse_and_map(np, 0);
-       fixed_link = of_get_property(np, "fixed-link", NULL);
-       if (fixed_link) {
-               snprintf(ug_info->phy_bus_id, sizeof(ug_info->phy_bus_id),
-                        PHY_ID_FMT, "0", fixed_link[0]);
-               phy = NULL;
-       } else {
-               char bus_name[MII_BUS_ID_SIZE];
 
-               ph = of_get_property(np, "phy-handle", NULL);
-               phy = of_find_node_by_phandle(*ph);
+       ug_info->phy_node = of_parse_phandle(np, "phy-handle", 0);
 
-               if (phy == NULL)
-                       return -ENODEV;
-
-               /* set the PHY address */
-               prop = of_get_property(phy, "reg", NULL);
-               if (prop == NULL)
-                       return -1;
-
-               /* Set the bus id */
-               mdio = of_get_parent(phy);
-
-               if (mdio == NULL)
-                       return -ENODEV;
-
-               err = of_address_to_resource(mdio, 0, &res);
-
-               if (err) {
-                       of_node_put(mdio);
-                       return err;
-               }
-               fsl_pq_mdio_bus_name(bus_name, mdio);
-               of_node_put(mdio);
-               snprintf(ug_info->phy_bus_id, sizeof(ug_info->phy_bus_id),
-                       "%s:%02x", bus_name, *prop);
-       }
+       /* Find the TBI PHY node.  If it's not there, we don't support SGMII */
+       ug_info->tbi_node = of_parse_phandle(np, "tbi-handle", 0);
 
        /* get the phy interface type, or default to MII */
        prop = of_get_property(np, "phy-connection-type", NULL);
        if (!prop) {
                /* handle interface property present in old trees */
-               prop = of_get_property(phy, "interface", NULL);
+               prop = of_get_property(ug_info->phy_node, "interface", NULL);
                if (prop != NULL) {
                        phy_interface = enet_to_phy_interface[*prop];
                        max_speed = enet_to_speed[*prop];
@@ -3694,6 +3752,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
                case PHY_INTERFACE_MODE_RGMII_TXID:
                case PHY_INTERFACE_MODE_TBI:
                case PHY_INTERFACE_MODE_RTBI:
+               case PHY_INTERFACE_MODE_SGMII:
                        max_speed = SPEED_1000;
                        break;
                default:
@@ -3751,7 +3810,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma
        dev->netdev_ops = &ucc_geth_netdev_ops;
        dev->watchdog_timeo = TX_TIMEOUT;
        INIT_WORK(&ugeth->timeout_work, ucc_geth_timeout_work);
-       netif_napi_add(dev, &ugeth->napi, ucc_geth_poll, UCC_GETH_DEV_WEIGHT);
+       netif_napi_add(dev, &ugeth->napi, ucc_geth_poll, 64);
        dev->mtu = 1500;
 
        ugeth->msg_enable = netif_msg_init(debug.msg_enable, UGETH_MSG_DEFAULT);