|
@@ -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)
|
|
|
{
|