diff options
Diffstat (limited to 'drivers/net/gianfar.c')
-rw-r--r-- | drivers/net/gianfar.c | 231 |
1 files changed, 152 insertions, 79 deletions
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 0f030b73cbb..146f9513aea 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -2,7 +2,8 @@ * drivers/net/gianfar.c * * Gianfar Ethernet Driver - * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560 + * This driver is designed for the non-CPM ethernet controllers + * on the 85xx and 83xx family of integrated processors * Based on 8260_io/fcc_enet.c * * Author: Andy Fleming @@ -22,8 +23,6 @@ * B-V +1.62 * * Theory of operation - * This driver is designed for the non-CPM ethernet controllers - * on the 85xx and 83xx family of integrated processors * * The driver is initialized through platform_device. Structures which * define the configuration needed by the board are defined in a @@ -110,7 +109,7 @@ #endif const char gfar_driver_name[] = "Gianfar Ethernet"; -const char gfar_driver_version[] = "1.2"; +const char gfar_driver_version[] = "1.3"; static int gfar_enet_open(struct net_device *dev); static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev); @@ -139,6 +138,10 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int l static void gfar_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp); static void gfar_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid); +void gfar_halt(struct net_device *dev); +void gfar_start(struct net_device *dev); +static void gfar_clear_exact_match(struct net_device *dev); +static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr); extern struct ethtool_ops gfar_ethtool_ops; @@ -146,12 +149,10 @@ MODULE_AUTHOR("Freescale Semiconductor, Inc"); MODULE_DESCRIPTION("Gianfar Ethernet Driver"); MODULE_LICENSE("GPL"); -int gfar_uses_fcb(struct gfar_private *priv) +/* Returns 1 if incoming frames use an FCB */ +static inline int gfar_uses_fcb(struct gfar_private *priv) { - if (priv->vlan_enable || priv->rx_csum_enable) - return 1; - else - return 0; + return (priv->vlan_enable || priv->rx_csum_enable); } /* Set up the ethernet device structure, private data, @@ -320,15 +321,10 @@ static int gfar_probe(struct platform_device *pdev) else priv->padding = 0; - dev->hard_header_len += priv->padding; - if (dev->features & NETIF_F_IP_CSUM) dev->hard_header_len += GMAC_FCB_LEN; priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE; -#ifdef CONFIG_GFAR_BUFSTASH - priv->rx_stash_size = STASH_LENGTH; -#endif priv->tx_ring_size = DEFAULT_TX_RING_SIZE; priv->rx_ring_size = DEFAULT_RX_RING_SIZE; @@ -350,6 +346,9 @@ static int gfar_probe(struct platform_device *pdev) goto register_fail; } + /* Create all the sysfs files */ + gfar_init_sysfs(dev); + /* Print out the device info */ printk(KERN_INFO DEVICE_NAME, dev->name); for (idx = 0; idx < 6; idx++) @@ -357,8 +356,7 @@ static int gfar_probe(struct platform_device *pdev) printk("\n"); /* Even more device info helps when determining which kernel */ - /* provided which set of benchmarks. Since this is global for all */ - /* devices, we only print it once */ + /* provided which set of benchmarks. */ #ifdef CONFIG_GFAR_NAPI printk(KERN_INFO "%s: Running with NAPI enabled\n", dev->name); #else @@ -463,19 +461,9 @@ static void init_registers(struct net_device *dev) /* Initialize the max receive buffer length */ gfar_write(&priv->regs->mrblr, priv->rx_buffer_size); -#ifdef CONFIG_GFAR_BUFSTASH - /* If we are stashing buffers, we need to set the - * extraction length to the size of the buffer */ - gfar_write(&priv->regs->attreli, priv->rx_stash_size << 16); -#endif - /* Initialize the Minimum Frame Length Register */ gfar_write(&priv->regs->minflr, MINFLR_INIT_SETTINGS); - /* Setup Attributes so that snooping is on for rx */ - gfar_write(&priv->regs->attr, ATTR_INIT_SETTINGS); - gfar_write(&priv->regs->attreli, ATTRELI_INIT_SETTINGS); - /* Assign the TBI an address which won't conflict with the PHYs */ gfar_write(&priv->regs->tbipa, TBIPA_VALUE); } @@ -577,8 +565,7 @@ static void free_skb_resources(struct gfar_private *priv) for (i = 0; i < priv->rx_ring_size; i++) { if (priv->rx_skbuff[i]) { dma_unmap_single(NULL, rxbdp->bufPtr, - priv->rx_buffer_size - + RXBUF_ALIGNMENT, + priv->rx_buffer_size, DMA_FROM_DEVICE); dev_kfree_skb_any(priv->rx_skbuff[i]); @@ -636,6 +623,7 @@ int startup_gfar(struct net_device *dev) struct gfar *regs = priv->regs; int err = 0; u32 rctrl = 0; + u32 attrs = 0; gfar_write(®s->imask, IMASK_INIT_CLEAR); @@ -795,18 +783,50 @@ int startup_gfar(struct net_device *dev) if (priv->rx_csum_enable) rctrl |= RCTRL_CHECKSUMMING; - if (priv->extended_hash) + if (priv->extended_hash) { rctrl |= RCTRL_EXTHASH; + gfar_clear_exact_match(dev); + rctrl |= RCTRL_EMEN; + } + if (priv->vlan_enable) rctrl |= RCTRL_VLAN; + if (priv->padding) { + rctrl &= ~RCTRL_PAL_MASK; + rctrl |= RCTRL_PADDING(priv->padding); + } + /* Init rctrl based on our settings */ gfar_write(&priv->regs->rctrl, rctrl); if (dev->features & NETIF_F_IP_CSUM) gfar_write(&priv->regs->tctrl, TCTRL_INIT_CSUM); + /* Set the extraction length and index */ + attrs = ATTRELI_EL(priv->rx_stash_size) | + ATTRELI_EI(priv->rx_stash_index); + + gfar_write(&priv->regs->attreli, attrs); + + /* Start with defaults, and add stashing or locking + * depending on the approprate variables */ + attrs = ATTR_INIT_SETTINGS; + + if (priv->bd_stash_en) + attrs |= ATTR_BDSTASH; + + if (priv->rx_stash_size != 0) + attrs |= ATTR_BUFSTASH; + + gfar_write(&priv->regs->attr, attrs); + + gfar_write(&priv->regs->fifo_tx_thr, priv->fifo_threshold); + gfar_write(&priv->regs->fifo_tx_starve, priv->fifo_starve); + gfar_write(&priv->regs->fifo_tx_starve_shutoff, priv->fifo_starve_off); + + /* Start the controller */ gfar_start(dev); return 0; @@ -851,34 +871,32 @@ static int gfar_enet_open(struct net_device *dev) return err; } -static struct txfcb *gfar_add_fcb(struct sk_buff *skb, struct txbd8 *bdp) +static inline struct txfcb *gfar_add_fcb(struct sk_buff *skb, struct txbd8 *bdp) { struct txfcb *fcb = (struct txfcb *)skb_push (skb, GMAC_FCB_LEN); memset(fcb, 0, GMAC_FCB_LEN); - /* Flag the bd so the controller looks for the FCB */ - bdp->status |= TXBD_TOE; - return fcb; } static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb) { - int len; + u8 flags = 0; /* If we're here, it's a IP packet with a TCP or UDP * payload. We set it to checksum, using a pseudo-header * we provide */ - fcb->ip = 1; - fcb->tup = 1; - fcb->ctu = 1; - fcb->nph = 1; + flags = TXFCB_DEFAULT; - /* Notify the controller what the protocol is */ - if (skb->nh.iph->protocol == IPPROTO_UDP) - fcb->udp = 1; + /* Tell the controller what the protocol is */ + /* And provide the already calculated phcs */ + if (skb->nh.iph->protocol == IPPROTO_UDP) { + flags |= TXFCB_UDP; + fcb->phcs = skb->h.uh->check; + } else + fcb->phcs = skb->h.th->check; /* l3os is the distance between the start of the * frame (skb->data) and the start of the IP hdr. @@ -887,17 +905,12 @@ static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb) fcb->l3os = (u16)(skb->nh.raw - skb->data - GMAC_FCB_LEN); fcb->l4os = (u16)(skb->h.raw - skb->nh.raw); - len = skb->nh.iph->tot_len - fcb->l4os; - - /* Provide the pseudoheader csum */ - fcb->phcs = ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, len, - skb->nh.iph->protocol, 0); + fcb->flags = flags; } -void gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb) +void inline gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb) { - fcb->vln = 1; + fcb->flags |= TXFCB_VLN; fcb->vlctl = vlan_tx_tag_get(skb); } @@ -908,6 +921,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) struct gfar_private *priv = netdev_priv(dev); struct txfcb *fcb = NULL; struct txbd8 *txbdp; + u16 status; /* Update transmit stats */ priv->stats.tx_bytes += skb->len; @@ -919,19 +933,22 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) txbdp = priv->cur_tx; /* Clear all but the WRAP status flags */ - txbdp->status &= TXBD_WRAP; + status = txbdp->status & TXBD_WRAP; /* Set up checksumming */ - if ((dev->features & NETIF_F_IP_CSUM) - && (CHECKSUM_HW == skb->ip_summed)) { + if (likely((dev->features & NETIF_F_IP_CSUM) + && (CHECKSUM_HW == skb->ip_summed))) { fcb = gfar_add_fcb(skb, txbdp); + status |= TXBD_TOE; gfar_tx_checksum(skb, fcb); } if (priv->vlan_enable && unlikely(priv->vlgrp && vlan_tx_tag_present(skb))) { - if (NULL == fcb) + if (unlikely(NULL == fcb)) { fcb = gfar_add_fcb(skb, txbdp); + status |= TXBD_TOE; + } gfar_tx_vlan(skb, fcb); } @@ -949,14 +966,16 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) (priv->skb_curtx + 1) & TX_RING_MOD_MASK(priv->tx_ring_size); /* Flag the BD as interrupt-causing */ - txbdp->status |= TXBD_INTERRUPT; + status |= TXBD_INTERRUPT; /* Flag the BD as ready to go, last in frame, and */ /* in need of CRC */ - txbdp->status |= (TXBD_READY | TXBD_LAST | TXBD_CRC); + status |= (TXBD_READY | TXBD_LAST | TXBD_CRC); dev->trans_start = jiffies; + txbdp->status = status; + /* If this was the last BD in the ring, the next one */ /* is at the beginning of the ring */ if (txbdp->status & TXBD_WRAP) @@ -1010,21 +1029,7 @@ static struct net_device_stats * gfar_get_stats(struct net_device *dev) /* Changes the mac address if the controller is not running. */ int gfar_set_mac_address(struct net_device *dev) { - struct gfar_private *priv = netdev_priv(dev); - int i; - char tmpbuf[MAC_ADDR_LEN]; - u32 tempval; - - /* Now copy it into the mac registers backwards, cuz */ - /* little endian is silly */ - for (i = 0; i < MAC_ADDR_LEN; i++) - tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->dev_addr[i]; - - gfar_write(&priv->regs->macstnaddr1, *((u32 *) (tmpbuf))); - - tempval = *((u32 *) (tmpbuf + 4)); - - gfar_write(&priv->regs->macstnaddr2, tempval); + gfar_set_mac_for_addr(dev, 0, dev->dev_addr); return 0; } @@ -1110,7 +1115,7 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu) INCREMENTAL_BUFFER_SIZE; /* Only stop and start the controller if it isn't already - * stopped */ + * stopped, and we changed something */ if ((oldsize != tempsize) && (dev->flags & IFF_UP)) stop_gfar(dev); @@ -1220,6 +1225,7 @@ static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs) struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp) { + unsigned int alignamount; struct gfar_private *priv = netdev_priv(dev); struct sk_buff *skb = NULL; unsigned int timeout = SKB_ALLOC_TIMEOUT; @@ -1231,18 +1237,18 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp) if (NULL == skb) return NULL; + alignamount = RXBUF_ALIGNMENT - + (((unsigned) skb->data) & (RXBUF_ALIGNMENT - 1)); + /* We need the data buffer to be aligned properly. We will reserve * as many bytes as needed to align the data properly */ - skb_reserve(skb, - RXBUF_ALIGNMENT - - (((unsigned) skb->data) & (RXBUF_ALIGNMENT - 1))); + skb_reserve(skb, alignamount); skb->dev = dev; bdp->bufPtr = dma_map_single(NULL, skb->data, - priv->rx_buffer_size + RXBUF_ALIGNMENT, - DMA_FROM_DEVICE); + priv->rx_buffer_size, DMA_FROM_DEVICE); bdp->length = 0; @@ -1350,7 +1356,7 @@ static inline void gfar_rx_checksum(struct sk_buff *skb, struct rxfcb *fcb) /* If valid headers were found, and valid sums * were verified, then we tell the kernel that no * checksumming is necessary. Otherwise, it is */ - if (fcb->cip && !fcb->eip && fcb->ctu && !fcb->etu) + if ((fcb->flags & RXFCB_CSUM_MASK) == (RXFCB_CIP | RXFCB_CTU)) skb->ip_summed = CHECKSUM_UNNECESSARY; else skb->ip_summed = CHECKSUM_NONE; @@ -1401,7 +1407,7 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, skb->protocol = eth_type_trans(skb, dev); /* Send the packet up the stack */ - if (unlikely(priv->vlgrp && fcb->vln)) + if (unlikely(priv->vlgrp && (fcb->flags & RXFCB_VLN))) ret = gfar_rx_vlan(skb, priv->vlgrp, fcb->vlctl); else ret = RECEIVE(skb); @@ -1620,6 +1626,7 @@ static void adjust_link(struct net_device *dev) spin_lock_irqsave(&priv->lock, flags); if (phydev->link) { u32 tempval = gfar_read(®s->maccfg2); + u32 ecntrl = gfar_read(®s->ecntrl); /* Now we make sure that we can be in full duplex mode. * If not, we operate in half-duplex mode. */ @@ -1644,6 +1651,13 @@ static void adjust_link(struct net_device *dev) case 10: tempval = ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII); + + /* Reduced mode distinguishes + * between 10 and 100 */ + if (phydev->speed == SPEED_100) + ecntrl |= ECNTRL_R100; + else + ecntrl &= ~(ECNTRL_R100); break; default: if (netif_msg_link(priv)) @@ -1657,6 +1671,7 @@ static void adjust_link(struct net_device *dev) } gfar_write(®s->maccfg2, tempval); + gfar_write(®s->ecntrl, ecntrl); if (!priv->oldlink) { new_state = 1; @@ -1721,6 +1736,9 @@ static void gfar_set_multi(struct net_device *dev) gfar_write(®s->gaddr6, 0xffffffff); gfar_write(®s->gaddr7, 0xffffffff); } else { + int em_num; + int idx; + /* zero out the hash */ gfar_write(®s->igaddr0, 0x0); gfar_write(®s->igaddr1, 0x0); @@ -1739,18 +1757,47 @@ static void gfar_set_multi(struct net_device *dev) gfar_write(®s->gaddr6, 0x0); gfar_write(®s->gaddr7, 0x0); + /* If we have extended hash tables, we need to + * clear the exact match registers to prepare for + * setting them */ + if (priv->extended_hash) { + em_num = GFAR_EM_NUM + 1; + gfar_clear_exact_match(dev); + idx = 1; + } else { + idx = 0; + em_num = 0; + } + if(dev->mc_count == 0) return; /* Parse the list, and set the appropriate bits */ for(mc_ptr = dev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { - gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr); + if (idx < em_num) { + gfar_set_mac_for_addr(dev, idx, + mc_ptr->dmi_addr); + idx++; + } else + gfar_set_hash_for_addr(dev, mc_ptr->dmi_addr); } } return; } + +/* Clears each of the exact match registers to zero, so they + * don't interfere with normal reception */ +static void gfar_clear_exact_match(struct net_device *dev) +{ + int idx; + u8 zero_arr[MAC_ADDR_LEN] = {0,0,0,0,0,0}; + + for(idx = 1;idx < GFAR_EM_NUM + 1;idx++) + gfar_set_mac_for_addr(dev, idx, (u8 *)zero_arr); +} + /* Set the appropriate hash bit for the given addr */ /* The algorithm works like so: * 1) Take the Destination Address (ie the multicast address), and @@ -1781,6 +1828,32 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr) return; } + +/* There are multiple MAC Address register pairs on some controllers + * This function sets the numth pair to a given address + */ +static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr) +{ + struct gfar_private *priv = netdev_priv(dev); + int idx; + char tmpbuf[MAC_ADDR_LEN]; + u32 tempval; + u32 *macptr = &priv->regs->macstnaddr1; + + macptr += num*2; + + /* Now copy it into the mac registers backwards, cuz */ + /* little endian is silly */ + for (idx = 0; idx < MAC_ADDR_LEN; idx++) + tmpbuf[MAC_ADDR_LEN - 1 - idx] = addr[idx]; + + gfar_write(macptr, *((u32 *) (tmpbuf))); + + tempval = *((u32 *) (tmpbuf + 4)); + + gfar_write(macptr+1, tempval); +} + /* GFAR error interrupt handler */ static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs) { |