]> err.no Git - linux-2.6/blobdiff - drivers/net/pasemi_mac.c
sc92031: start transmit return value bugfix
[linux-2.6] / drivers / net / pasemi_mac.c
index c6e24a8dcf72a105e2d4641b666f61f30be79c8d..bcd7f9814ed825a6699cfe8e6b7f509218dc48ae 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <net/checksum.h>
+#include <linux/inet_lro.h>
 
 #include <asm/irq.h>
 #include <asm/firmware.h>
 
 
 /* Must be a power of two */
-#define RX_RING_SIZE 1024
+#define RX_RING_SIZE 2048
 #define TX_RING_SIZE 4096
 
+#define LRO_MAX_AGGR 64
+
+#define PE_MIN_MTU     64
+#define PE_MAX_MTU     1500
+#define PE_DEF_MTU     ETH_DATA_LEN
+
 #define DEFAULT_MSG_ENABLE       \
        (NETIF_MSG_DRV          | \
         NETIF_MSG_PROBE        | \
@@ -79,8 +86,6 @@
                                 & ((ring)->size - 1))
 #define RING_AVAIL(ring)       ((ring->size) - RING_USED(ring))
 
-#define BUF_SIZE 1646 /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */
-
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>");
 MODULE_DESCRIPTION("PA Semi PWRficient Ethernet driver");
@@ -172,6 +177,24 @@ static int mac_to_intf(struct pasemi_mac *mac)
        return -1;
 }
 
+static void pasemi_mac_intf_disable(struct pasemi_mac *mac)
+{
+       unsigned int flags;
+
+       flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
+       flags &= ~PAS_MAC_CFG_PCFG_PE;
+       write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
+}
+
+static void pasemi_mac_intf_enable(struct pasemi_mac *mac)
+{
+       unsigned int flags;
+
+       flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
+       flags |= PAS_MAC_CFG_PCFG_PE;
+       write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
+}
+
 static int pasemi_get_mac_addr(struct pasemi_mac *mac)
 {
        struct pci_dev *pdev = mac->pdev;
@@ -206,7 +229,6 @@ static int pasemi_get_mac_addr(struct pasemi_mac *mac)
                return -ENOENT;
        }
 
-
        if (sscanf(maddr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &addr[0],
                   &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]) != 6) {
                dev_warn(&pdev->dev,
@@ -219,12 +241,70 @@ static int pasemi_get_mac_addr(struct pasemi_mac *mac)
        return 0;
 }
 
+static int pasemi_mac_set_mac_addr(struct net_device *dev, void *p)
+{
+       struct pasemi_mac *mac = netdev_priv(dev);
+       struct sockaddr *addr = p;
+       unsigned int adr0, adr1;
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EINVAL;
+
+       memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+
+       adr0 = dev->dev_addr[2] << 24 |
+              dev->dev_addr[3] << 16 |
+              dev->dev_addr[4] << 8 |
+              dev->dev_addr[5];
+       adr1 = read_mac_reg(mac, PAS_MAC_CFG_ADR1);
+       adr1 &= ~0xffff;
+       adr1 |= dev->dev_addr[0] << 8 | dev->dev_addr[1];
+
+       pasemi_mac_intf_disable(mac);
+       write_mac_reg(mac, PAS_MAC_CFG_ADR0, adr0);
+       write_mac_reg(mac, PAS_MAC_CFG_ADR1, adr1);
+       pasemi_mac_intf_enable(mac);
+
+       return 0;
+}
+
+static int get_skb_hdr(struct sk_buff *skb, void **iphdr,
+                      void **tcph, u64 *hdr_flags, void *data)
+{
+       u64 macrx = (u64) data;
+       unsigned int ip_len;
+       struct iphdr *iph;
+
+       /* IPv4 header checksum failed */
+       if ((macrx & XCT_MACRX_HTY_M) != XCT_MACRX_HTY_IPV4_OK)
+               return -1;
+
+       /* non tcp packet */
+       skb_reset_network_header(skb);
+       iph = ip_hdr(skb);
+       if (iph->protocol != IPPROTO_TCP)
+               return -1;
+
+       ip_len = ip_hdrlen(skb);
+       skb_set_transport_header(skb, ip_len);
+       *tcph = tcp_hdr(skb);
+
+       /* check if ip header and tcp header are complete */
+       if (iph->tot_len < ip_len + tcp_hdrlen(skb))
+               return -1;
+
+       *hdr_flags = LRO_IPV4 | LRO_TCP;
+       *iphdr = iph;
+
+       return 0;
+}
+
 static int pasemi_mac_unmap_tx_skb(struct pasemi_mac *mac,
+                                   const int nfrags,
                                    struct sk_buff *skb,
                                    const dma_addr_t *dmas)
 {
        int f;
-       int nfrags = skb_shinfo(skb)->nr_frags;
        struct pci_dev *pdev = mac->dma_pdev;
 
        pci_unmap_single(pdev, dmas[0], skb_headlen(skb), PCI_DMA_TODEVICE);
@@ -392,7 +472,7 @@ static void pasemi_mac_free_tx_resources(struct pasemi_mac *mac)
        unsigned int i, j;
        struct pasemi_mac_buffer *info;
        dma_addr_t dmas[MAX_SKB_FRAGS+1];
-       int freed;
+       int freed, nfrags;
        int start, limit;
 
        start = txring->next_to_clean;
@@ -405,10 +485,12 @@ static void pasemi_mac_free_tx_resources(struct pasemi_mac *mac)
        for (i = start; i < limit; i += freed) {
                info = &txring->ring_info[(i+1) & (TX_RING_SIZE-1)];
                if (info->dma && info->skb) {
-                       for (j = 0; j <= skb_shinfo(info->skb)->nr_frags; j++)
+                       nfrags = skb_shinfo(info->skb)->nr_frags;
+                       for (j = 0; j <= nfrags; j++)
                                dmas[j] = txring->ring_info[(i+1+j) &
                                                (TX_RING_SIZE-1)].dma;
-                       freed = pasemi_mac_unmap_tx_skb(mac, info->skb, dmas);
+                       freed = pasemi_mac_unmap_tx_skb(mac, nfrags,
+                                                       info->skb, dmas);
                } else
                        freed = 2;
        }
@@ -418,7 +500,7 @@ static void pasemi_mac_free_tx_resources(struct pasemi_mac *mac)
 
 }
 
-static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac)
+static void pasemi_mac_free_rx_buffers(struct pasemi_mac *mac)
 {
        struct pasemi_mac_rxring *rx = rx_ring(mac);
        unsigned int i;
@@ -438,7 +520,12 @@ static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac)
        }
 
        for (i = 0; i < RX_RING_SIZE; i++)
-               RX_DESC(rx, i) = 0;
+               RX_BUFF(rx, i) = 0;
+}
+
+static void pasemi_mac_free_rx_resources(struct pasemi_mac *mac)
+{
+       pasemi_mac_free_rx_buffers(mac);
 
        dma_free_coherent(&mac->dma_pdev->dev, RX_RING_SIZE * sizeof(u64),
                          rx_ring(mac)->buffers, rx_ring(mac)->buf_dma);
@@ -468,19 +555,14 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev,
                /* Entry in use? */
                WARN_ON(*buff);
 
-               /* skb might still be in there for recycle on short receives */
-               if (info->skb)
-                       skb = info->skb;
-               else {
-                       skb = dev_alloc_skb(BUF_SIZE);
-                       skb_reserve(skb, LOCAL_SKB_ALIGN);
-               }
+               skb = dev_alloc_skb(mac->bufsz);
+               skb_reserve(skb, LOCAL_SKB_ALIGN);
 
                if (unlikely(!skb))
                        break;
 
                dma = pci_map_single(mac->dma_pdev, skb->data,
-                                    BUF_SIZE - LOCAL_SKB_ALIGN,
+                                    mac->bufsz - LOCAL_SKB_ALIGN,
                                     PCI_DMA_FROMDEVICE);
 
                if (unlikely(dma_mapping_error(dma))) {
@@ -490,7 +572,7 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev,
 
                info->skb = skb;
                info->dma = dma;
-               *buff = XCT_RXB_LEN(BUF_SIZE) | XCT_RXB_ADDR(dma);
+               *buff = XCT_RXB_LEN(mac->bufsz) | XCT_RXB_ADDR(dma);
                fill++;
        }
 
@@ -504,15 +586,19 @@ static void pasemi_mac_replenish_rx_ring(const struct net_device *dev,
 
 static void pasemi_mac_restart_rx_intr(const struct pasemi_mac *mac)
 {
+       struct pasemi_mac_rxring *rx = rx_ring(mac);
        unsigned int reg, pcnt;
        /* Re-enable packet count interrupts: finally
         * ack the packet count interrupt we got in rx_intr.
         */
 
-       pcnt = *rx_ring(mac)->chan.status & PAS_STATUS_PCNT_M;
+       pcnt = *rx->chan.status & PAS_STATUS_PCNT_M;
 
        reg = PAS_IOB_DMA_RXCH_RESET_PCNT(pcnt) | PAS_IOB_DMA_RXCH_RESET_PINTC;
 
+       if (*rx->chan.status & PAS_STATUS_TIMER)
+               reg |= PAS_IOB_DMA_RXCH_RESET_TINTC;
+
        write_iob_reg(PAS_IOB_DMA_RXCH_RESET(mac->rx->chan.chno), reg);
 }
 
@@ -616,7 +702,7 @@ static int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx,
 
                len = (macrx & XCT_MACRX_LLEN_M) >> XCT_MACRX_LLEN_S;
 
-               pci_unmap_single(pdev, dma, BUF_SIZE-LOCAL_SKB_ALIGN,
+               pci_unmap_single(pdev, dma, mac->bufsz - LOCAL_SKB_ALIGN,
                                 PCI_DMA_FROMDEVICE);
 
                if (macrx & XCT_MACRX_CRC) {
@@ -627,21 +713,7 @@ static int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx,
                        goto next;
                }
 
-               if (len < 256) {
-                       struct sk_buff *new_skb;
-
-                       new_skb = netdev_alloc_skb(mac->netdev,
-                                                  len + LOCAL_SKB_ALIGN);
-                       if (new_skb) {
-                               skb_reserve(new_skb, LOCAL_SKB_ALIGN);
-                               memcpy(new_skb->data, skb->data, len);
-                               /* save the skb in buffer_info as good */
-                               skb = new_skb;
-                       }
-                       /* else just continue with the old one */
-               } else
-                       info->skb = NULL;
-
+               info->skb = NULL;
                info->dma = 0;
 
                if (likely((macrx & XCT_MACRX_HTY_M) == XCT_MACRX_HTY_IPV4_OK)) {
@@ -658,7 +730,7 @@ static int pasemi_mac_clean_rx(struct pasemi_mac_rxring *rx,
                skb_put(skb, len-4);
 
                skb->protocol = eth_type_trans(skb, mac->netdev);
-               netif_receive_skb(skb);
+               lro_receive_skb(&mac->lro_mgr, skb, (void *)macrx);
 
 next:
                RX_DESC(rx, n) = 0;
@@ -680,6 +752,8 @@ next:
 
        rx_ring(mac)->next_to_clean = n;
 
+       lro_flush_all(&mac->lro_mgr);
+
        /* Increase is in number of 16-byte entries, and since each descriptor
         * with an 8BRES takes up 3x8 bytes (padded to 4x8), increase with
         * count*2.
@@ -710,6 +784,8 @@ static int pasemi_mac_clean_tx(struct pasemi_mac_txring *txring)
        unsigned long flags;
        struct sk_buff *skbs[TX_CLEAN_BATCHSIZE];
        dma_addr_t dmas[TX_CLEAN_BATCHSIZE][MAX_SKB_FRAGS+1];
+       int nf[TX_CLEAN_BATCHSIZE];
+       int nr_frags;
 
        total_count = 0;
        batch_limit = TX_CLEAN_BATCHSIZE;
@@ -719,6 +795,8 @@ restart:
        start = txring->next_to_clean;
        ring_limit = txring->next_to_fill;
 
+       prefetch(&TX_DESC_INFO(txring, start+1).skb);
+
        /* Compensate for when fill has wrapped but clean has not */
        if (start > ring_limit)
                ring_limit += TX_RING_SIZE;
@@ -732,6 +810,9 @@ restart:
                u64 mactx = TX_DESC(txring, i);
                struct sk_buff *skb;
 
+               skb = TX_DESC_INFO(txring, i+1).skb;
+               nr_frags = TX_DESC_INFO(txring, i).dma;
+
                if ((mactx  & XCT_MACTX_E) ||
                    (*chan->status & PAS_STATUS_ERROR))
                        pasemi_mac_tx_error(mac, mactx);
@@ -740,21 +821,22 @@ restart:
                        /* Not yet transmitted */
                        break;
 
-               skb = TX_DESC_INFO(txring, i+1).skb;
-               skbs[descr_count] = skb;
+               buf_count = 2 + nr_frags;
+               /* Since we always fill with an even number of entries, make
+                * sure we skip any unused one at the end as well.
+                */
+               if (buf_count & 1)
+                       buf_count++;
 
-               buf_count = 2 + skb_shinfo(skb)->nr_frags;
-               for (j = 0; j <= skb_shinfo(skb)->nr_frags; j++)
+               for (j = 0; j <= nr_frags; j++)
                        dmas[descr_count][j] = TX_DESC_INFO(txring, i+1+j).dma;
 
+               skbs[descr_count] = skb;
+               nf[descr_count] = nr_frags;
+
                TX_DESC(txring, i) = 0;
                TX_DESC(txring, i+1) = 0;
 
-               /* Since we always fill with an even number of entries, make
-                * sure we skip any unused one at the end as well.
-                */
-               if (buf_count & 1)
-                       buf_count++;
                descr_count++;
        }
        txring->next_to_clean = i & (TX_RING_SIZE-1);
@@ -763,7 +845,7 @@ restart:
        netif_wake_queue(mac->netdev);
 
        for (i = 0; i < descr_count; i++)
-               pasemi_mac_unmap_tx_skb(mac, skbs[i], dmas[i]);
+               pasemi_mac_unmap_tx_skb(mac, nf[i], skbs[i], dmas[i]);
 
        total_count += descr_count;
 
@@ -795,8 +877,6 @@ static irqreturn_t pasemi_mac_rx_intr(int irq, void *data)
                reg |= PAS_IOB_DMA_RXCH_RESET_SINTC;
        if (*chan->status & PAS_STATUS_ERROR)
                reg |= PAS_IOB_DMA_RXCH_RESET_DINTC;
-       if (*chan->status & PAS_STATUS_TIMER)
-               reg |= PAS_IOB_DMA_RXCH_RESET_TINTC;
 
        netif_rx_schedule(dev, &mac->napi);
 
@@ -805,27 +885,43 @@ static irqreturn_t pasemi_mac_rx_intr(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+#define TX_CLEAN_INTERVAL HZ
+
+static void pasemi_mac_tx_timer(unsigned long data)
+{
+       struct pasemi_mac_txring *txring = (struct pasemi_mac_txring *)data;
+       struct pasemi_mac *mac = txring->mac;
+
+       pasemi_mac_clean_tx(txring);
+
+       mod_timer(&txring->clean_timer, jiffies + TX_CLEAN_INTERVAL);
+
+       pasemi_mac_restart_tx_intr(mac);
+}
+
 static irqreturn_t pasemi_mac_tx_intr(int irq, void *data)
 {
        struct pasemi_mac_txring *txring = data;
        const struct pasemi_dmachan *chan = &txring->chan;
-       unsigned int reg, pcnt;
+       struct pasemi_mac *mac = txring->mac;
+       unsigned int reg;
 
        if (!(*chan->status & PAS_STATUS_CAUSE_M))
                return IRQ_NONE;
 
-       pasemi_mac_clean_tx(txring);
-
-       pcnt = *chan->status & PAS_STATUS_PCNT_M;
-
-       reg = PAS_IOB_DMA_TXCH_RESET_PCNT(pcnt) | PAS_IOB_DMA_TXCH_RESET_PINTC;
+       reg = 0;
 
        if (*chan->status & PAS_STATUS_SOFT)
                reg |= PAS_IOB_DMA_TXCH_RESET_SINTC;
        if (*chan->status & PAS_STATUS_ERROR)
                reg |= PAS_IOB_DMA_TXCH_RESET_DINTC;
 
-       write_iob_reg(PAS_IOB_DMA_TXCH_RESET(chan->chno), reg);
+       mod_timer(&txring->clean_timer, jiffies + (TX_CLEAN_INTERVAL)*2);
+
+       netif_rx_schedule(mac->netdev, &mac->napi);
+
+       if (reg)
+               write_iob_reg(PAS_IOB_DMA_TXCH_RESET(chan->chno), reg);
 
        return IRQ_HANDLED;
 }
@@ -845,11 +941,14 @@ static void pasemi_adjust_link(struct net_device *dev)
                        printk(KERN_INFO "%s: Link is down.\n", dev->name);
 
                netif_carrier_off(dev);
+               pasemi_mac_intf_disable(mac);
                mac->link = 0;
 
                return;
-       } else
+       } else {
+               pasemi_mac_intf_enable(mac);
                netif_carrier_on(dev);
+       }
 
        flags = read_mac_reg(mac, PAS_MAC_CFG_PCFG);
        new_flags = flags & ~(PAS_MAC_CFG_PCFG_HD | PAS_MAC_CFG_PCFG_SPD_M |
@@ -913,7 +1012,7 @@ static int pasemi_mac_phy_init(struct net_device *dev)
                goto err;
 
        phy_id = *prop;
-       snprintf(mac->phy_id, BUS_ID_SIZE, PHY_ID_FMT, (int)r.start, phy_id);
+       snprintf(mac->phy_id, BUS_ID_SIZE, "%x:%02x", (int)r.start, phy_id);
 
        of_node_put(phy_dn);
 
@@ -956,10 +1055,6 @@ static int pasemi_mac_open(struct net_device *dev)
 
        write_mac_reg(mac, PAS_MAC_CFG_TXP, flags);
 
-       /* 0xffffff is max value, about 16ms */
-       write_iob_reg(PAS_IOB_DMA_COM_TIMEOUTCFG,
-                     PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT(0xffffff));
-
        ret = pasemi_mac_setup_rx_resources(dev);
        if (ret)
                goto out_rx_resources;
@@ -969,11 +1064,15 @@ static int pasemi_mac_open(struct net_device *dev)
        if (!mac->tx)
                goto out_tx_ring;
 
+       /* 0x3ff with 33MHz clock is about 31us */
+       write_iob_reg(PAS_IOB_DMA_COM_TIMEOUTCFG,
+                     PAS_IOB_DMA_COM_TIMEOUTCFG_TCNT(0x3ff));
+
        write_iob_reg(PAS_IOB_DMA_RXCH_CFG(mac->rx->chan.chno),
-                     PAS_IOB_DMA_RXCH_CFG_CNTTH(0));
+                     PAS_IOB_DMA_RXCH_CFG_CNTTH(256));
 
        write_iob_reg(PAS_IOB_DMA_TXCH_CFG(mac->tx->chan.chno),
-                     PAS_IOB_DMA_TXCH_CFG_CNTTH(128));
+                     PAS_IOB_DMA_TXCH_CFG_CNTTH(32));
 
        write_mac_reg(mac, PAS_MAC_IPC_CHNL,
                      PAS_MAC_IPC_CHNL_DCHNO(mac->rx->chan.chno) |
@@ -1008,8 +1107,7 @@ static int pasemi_mac_open(struct net_device *dev)
        pasemi_mac_restart_rx_intr(mac);
        pasemi_mac_restart_tx_intr(mac);
 
-       flags = PAS_MAC_CFG_PCFG_S1 | PAS_MAC_CFG_PCFG_PE |
-               PAS_MAC_CFG_PCFG_PR | PAS_MAC_CFG_PCFG_CE;
+       flags = PAS_MAC_CFG_PCFG_S1 | PAS_MAC_CFG_PCFG_PR | PAS_MAC_CFG_PCFG_CE;
 
        if (mac->type == MAC_TYPE_GMAC)
                flags |= PAS_MAC_CFG_PCFG_TSR_1G | PAS_MAC_CFG_PCFG_SPD_1G;
@@ -1020,11 +1118,17 @@ static int pasemi_mac_open(struct net_device *dev)
        write_mac_reg(mac, PAS_MAC_CFG_PCFG, flags);
 
        ret = pasemi_mac_phy_init(dev);
-       /* Some configs don't have PHYs (XAUI etc), so don't complain about
-        * failed init due to -ENODEV.
-        */
-       if (ret && ret != -ENODEV)
-               dev_warn(&mac->pdev->dev, "phy init failed: %d\n", ret);
+       if (ret) {
+               /* Since we won't get link notification, just enable RX */
+               pasemi_mac_intf_enable(mac);
+               if (mac->type == MAC_TYPE_GMAC) {
+                       /* Warn for missing PHY on SGMII (1Gig) ports */
+                       dev_warn(&mac->pdev->dev,
+                                "PHY init failed: %d.\n", ret);
+                       dev_warn(&mac->pdev->dev,
+                                "Defaulting to 1Gbit full duplex\n");
+               }
+       }
 
        netif_start_queue(dev);
        napi_enable(&mac->napi);
@@ -1054,6 +1158,12 @@ static int pasemi_mac_open(struct net_device *dev)
        if (mac->phydev)
                phy_start(mac->phydev);
 
+       init_timer(&mac->tx->clean_timer);
+       mac->tx->clean_timer.function = pasemi_mac_tx_timer;
+       mac->tx->clean_timer.data = (unsigned long)mac->tx;
+       mac->tx->clean_timer.expires = jiffies+HZ;
+       add_timer(&mac->tx->clean_timer);
+
        return 0;
 
 out_rx_int:
@@ -1072,11 +1182,71 @@ out_rx_resources:
 
 #define MAX_RETRIES 5000
 
+static void pasemi_mac_pause_txchan(struct pasemi_mac *mac)
+{
+       unsigned int sta, retries;
+       int txch = tx_ring(mac)->chan.chno;
+
+       write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch),
+                     PAS_DMA_TXCHAN_TCMDSTA_ST);
+
+       for (retries = 0; retries < MAX_RETRIES; retries++) {
+               sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch));
+               if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT))
+                       break;
+               cond_resched();
+       }
+
+       if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)
+               dev_err(&mac->dma_pdev->dev,
+                       "Failed to stop tx channel, tcmdsta %08x\n", sta);
+
+       write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 0);
+}
+
+static void pasemi_mac_pause_rxchan(struct pasemi_mac *mac)
+{
+       unsigned int sta, retries;
+       int rxch = rx_ring(mac)->chan.chno;
+
+       write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch),
+                     PAS_DMA_RXCHAN_CCMDSTA_ST);
+       for (retries = 0; retries < MAX_RETRIES; retries++) {
+               sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch));
+               if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT))
+                       break;
+               cond_resched();
+       }
+
+       if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)
+               dev_err(&mac->dma_pdev->dev,
+                       "Failed to stop rx channel, ccmdsta 08%x\n", sta);
+       write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 0);
+}
+
+static void pasemi_mac_pause_rxint(struct pasemi_mac *mac)
+{
+       unsigned int sta, retries;
+
+       write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
+                     PAS_DMA_RXINT_RCMDSTA_ST);
+       for (retries = 0; retries < MAX_RETRIES; retries++) {
+               sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if));
+               if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT))
+                       break;
+               cond_resched();
+       }
+
+       if (sta & PAS_DMA_RXINT_RCMDSTA_ACT)
+               dev_err(&mac->dma_pdev->dev,
+                       "Failed to stop rx interface, rcmdsta %08x\n", sta);
+       write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0);
+}
+
 static int pasemi_mac_close(struct net_device *dev)
 {
        struct pasemi_mac *mac = netdev_priv(dev);
        unsigned int sta;
-       int retries;
        int rxch, txch;
 
        rxch = rx_ring(mac)->chan.chno;
@@ -1087,6 +1257,8 @@ static int pasemi_mac_close(struct net_device *dev)
                phy_disconnect(mac->phydev);
        }
 
+       del_timer_sync(&mac->tx->clean_timer);
+
        netif_stop_queue(dev);
        napi_disable(&mac->napi);
 
@@ -1112,51 +1284,10 @@ static int pasemi_mac_close(struct net_device *dev)
        pasemi_mac_clean_tx(tx_ring(mac));
        pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE);
 
-       /* Disable interface */
-       write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch),
-                     PAS_DMA_TXCHAN_TCMDSTA_ST);
-       write_dma_reg( PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
-                     PAS_DMA_RXINT_RCMDSTA_ST);
-       write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch),
-                     PAS_DMA_RXCHAN_CCMDSTA_ST);
-
-       for (retries = 0; retries < MAX_RETRIES; retries++) {
-               sta = read_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(rxch));
-               if (!(sta & PAS_DMA_TXCHAN_TCMDSTA_ACT))
-                       break;
-               cond_resched();
-       }
-
-       if (sta & PAS_DMA_TXCHAN_TCMDSTA_ACT)
-               dev_err(&mac->dma_pdev->dev, "Failed to stop tx channel\n");
-
-       for (retries = 0; retries < MAX_RETRIES; retries++) {
-               sta = read_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch));
-               if (!(sta & PAS_DMA_RXCHAN_CCMDSTA_ACT))
-                       break;
-               cond_resched();
-       }
-
-       if (sta & PAS_DMA_RXCHAN_CCMDSTA_ACT)
-               dev_err(&mac->dma_pdev->dev, "Failed to stop rx channel\n");
-
-       for (retries = 0; retries < MAX_RETRIES; retries++) {
-               sta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if));
-               if (!(sta & PAS_DMA_RXINT_RCMDSTA_ACT))
-                       break;
-               cond_resched();
-       }
-
-       if (sta & PAS_DMA_RXINT_RCMDSTA_ACT)
-               dev_err(&mac->dma_pdev->dev, "Failed to stop rx interface\n");
-
-       /* Then, disable the channel. This must be done separately from
-        * stopping, since you can't disable when active.
-        */
-
-       write_dma_reg(PAS_DMA_TXCHAN_TCMDSTA(txch), 0);
-       write_dma_reg(PAS_DMA_RXCHAN_CCMDSTA(rxch), 0);
-       write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if), 0);
+       pasemi_mac_pause_txchan(mac);
+       pasemi_mac_pause_rxint(mac);
+       pasemi_mac_pause_rxchan(mac);
+       pasemi_mac_intf_disable(mac);
 
        free_irq(mac->tx->chan.irq, mac->tx);
        free_irq(mac->rx->chan.irq, mac->rx);
@@ -1238,6 +1369,7 @@ static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev)
        }
 
        TX_DESC(txring, fill) = mactx;
+       TX_DESC_INFO(txring, fill).dma = nfrags;
        fill++;
        TX_DESC_INFO(txring, fill).skb = skb;
        for (i = 0; i <= nfrags; i++) {
@@ -1304,10 +1436,67 @@ static int pasemi_mac_poll(struct napi_struct *napi, int budget)
                netif_rx_complete(dev, napi);
 
                pasemi_mac_restart_rx_intr(mac);
+               pasemi_mac_restart_tx_intr(mac);
        }
        return pkts;
 }
 
+static int pasemi_mac_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct pasemi_mac *mac = netdev_priv(dev);
+       unsigned int reg;
+       unsigned int rcmdsta;
+       int running;
+
+       if (new_mtu < PE_MIN_MTU || new_mtu > PE_MAX_MTU)
+               return -EINVAL;
+
+       running = netif_running(dev);
+
+       if (running) {
+               /* Need to stop the interface, clean out all already
+                * received buffers, free all unused buffers on the RX
+                * interface ring, then finally re-fill the rx ring with
+                * the new-size buffers and restart.
+                */
+
+               napi_disable(&mac->napi);
+               netif_tx_disable(dev);
+               pasemi_mac_intf_disable(mac);
+
+               rcmdsta = read_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if));
+               pasemi_mac_pause_rxint(mac);
+               pasemi_mac_clean_rx(rx_ring(mac), RX_RING_SIZE);
+               pasemi_mac_free_rx_buffers(mac);
+       }
+
+       /* Change maxf, i.e. what size frames are accepted.
+        * Need room for ethernet header and CRC word
+        */
+       reg = read_mac_reg(mac, PAS_MAC_CFG_MACCFG);
+       reg &= ~PAS_MAC_CFG_MACCFG_MAXF_M;
+       reg |= PAS_MAC_CFG_MACCFG_MAXF(new_mtu + ETH_HLEN + 4);
+       write_mac_reg(mac, PAS_MAC_CFG_MACCFG, reg);
+
+       dev->mtu = new_mtu;
+       /* MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */
+       mac->bufsz = new_mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128;
+
+       if (running) {
+               write_dma_reg(PAS_DMA_RXINT_RCMDSTA(mac->dma_if),
+                             rcmdsta | PAS_DMA_RXINT_RCMDSTA_EN);
+
+               rx_ring(mac)->next_to_fill = 0;
+               pasemi_mac_replenish_rx_ring(dev, RX_RING_SIZE-1);
+
+               napi_enable(&mac->napi);
+               netif_start_queue(dev);
+               pasemi_mac_intf_enable(mac);
+       }
+
+       return 0;
+}
+
 static int __devinit
 pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
@@ -1341,6 +1530,16 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->features = NETIF_F_IP_CSUM | NETIF_F_LLTX | NETIF_F_SG |
                        NETIF_F_HIGHDMA;
 
+       mac->lro_mgr.max_aggr = LRO_MAX_AGGR;
+       mac->lro_mgr.max_desc = MAX_LRO_DESCRIPTORS;
+       mac->lro_mgr.lro_arr = mac->lro_desc;
+       mac->lro_mgr.get_skb_header = get_skb_hdr;
+       mac->lro_mgr.features = LRO_F_NAPI | LRO_F_EXTRACT_VLAN_ID;
+       mac->lro_mgr.dev = mac->netdev;
+       mac->lro_mgr.ip_summed = CHECKSUM_UNNECESSARY;
+       mac->lro_mgr.ip_summed_aggr = CHECKSUM_UNNECESSARY;
+
+
        mac->dma_pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa007, NULL);
        if (!mac->dma_pdev) {
                dev_err(&mac->pdev->dev, "Can't find DMA Controller\n");
@@ -1385,6 +1584,12 @@ pasemi_mac_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->stop = pasemi_mac_close;
        dev->hard_start_xmit = pasemi_mac_start_tx;
        dev->set_multicast_list = pasemi_mac_set_rx_mode;
+       dev->set_mac_address = pasemi_mac_set_mac_addr;
+       dev->mtu = PE_DEF_MTU;
+       /* 1500 MTU + ETH_HLEN + VLAN_HLEN + 2 64B cachelines */
+       mac->bufsz = dev->mtu + ETH_HLEN + ETH_FCS_LEN + LOCAL_SKB_ALIGN + 128;
+
+       dev->change_mtu = pasemi_mac_change_mtu;
 
        if (err)
                goto out;