Commit 193cfca5 authored by Shawn Lin's avatar Shawn Lin Committed by David S. Miller

r6040: fix multicast operations

The original code does not work well when the number of mulitcast
address to handle is greater than MCAST_MAX. It only enable promiscous
mode instead of multicast hash table mode, so the hash table function
will not be activated and all multicast frames will be recieved in this
condition.

This patch fixes the following issues with the r6040 NIC operating in
multicast:

1) When the IFF_ALLMULTI flag is set, we should write 0xffff to the NIC
hash table registers to make it process multicast traffic.

2) When the number of multicast address to handle is smaller than
MCAST_MAX, we should use the NIC multicast registers MID1_{L,M,H}.

3) The hashing of the address was not correct, due to an invalid
substraction (15 - (crc & 0x0f)) instead of (crc & 0x0f) and an
incorrect crc algorithm (ether_crc_le) instead of (ether_crc).

4) If necessary, we should set HASH_EN flag in MCR0 to enable multicast
hash table function.
Reported-by: default avatarMarc Leclerc <marc-leclerc@signaturealpha.com>
Tested-by: default avatarMarc Leclerc <marc-leclerc@signaturealpha.com>
Signed-off-by: default avatarShawn Lin <shawn@dmp.com.tw>
Signed-off-by: default avatarAlbert Chen <albert.chen@rdc.com.tw>
Signed-off-by: default avatarFlorian Fainelli <florian@openwrt.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e0c56310
...@@ -69,6 +69,8 @@ ...@@ -69,6 +69,8 @@
/* MAC registers */ /* MAC registers */
#define MCR0 0x00 /* Control register 0 */ #define MCR0 0x00 /* Control register 0 */
#define MCR0_PROMISC 0x0020 /* Promiscuous mode */
#define MCR0_HASH_EN 0x0100 /* Enable multicast hash table function */
#define MCR1 0x04 /* Control register 1 */ #define MCR1 0x04 /* Control register 1 */
#define MAC_RST 0x0001 /* Reset the MAC */ #define MAC_RST 0x0001 /* Reset the MAC */
#define MBCR 0x08 /* Bus control */ #define MBCR 0x08 /* Bus control */
...@@ -851,77 +853,92 @@ static void r6040_multicast_list(struct net_device *dev) ...@@ -851,77 +853,92 @@ static void r6040_multicast_list(struct net_device *dev)
{ {
struct r6040_private *lp = netdev_priv(dev); struct r6040_private *lp = netdev_priv(dev);
void __iomem *ioaddr = lp->base; void __iomem *ioaddr = lp->base;
u16 *adrp;
u16 reg;
unsigned long flags; unsigned long flags;
struct netdev_hw_addr *ha; struct netdev_hw_addr *ha;
int i; int i;
u16 *adrp;
u16 hash_table[4] = { 0 };
spin_lock_irqsave(&lp->lock, flags);
/* MAC Address */ /* Keep our MAC Address */
adrp = (u16 *)dev->dev_addr; adrp = (u16 *)dev->dev_addr;
iowrite16(adrp[0], ioaddr + MID_0L); iowrite16(adrp[0], ioaddr + MID_0L);
iowrite16(adrp[1], ioaddr + MID_0M); iowrite16(adrp[1], ioaddr + MID_0M);
iowrite16(adrp[2], ioaddr + MID_0H); iowrite16(adrp[2], ioaddr + MID_0H);
/* Promiscous Mode */
spin_lock_irqsave(&lp->lock, flags);
/* Clear AMCP & PROM bits */ /* Clear AMCP & PROM bits */
reg = ioread16(ioaddr) & ~0x0120; lp->mcr0 = ioread16(ioaddr + MCR0) & ~(MCR0_PROMISC | MCR0_HASH_EN);
if (dev->flags & IFF_PROMISC) {
reg |= 0x0020;
lp->mcr0 |= 0x0020;
}
/* Too many multicast addresses
* accept all traffic */
else if ((netdev_mc_count(dev) > MCAST_MAX) ||
(dev->flags & IFF_ALLMULTI))
reg |= 0x0020;
iowrite16(reg, ioaddr); /* Promiscuous mode */
spin_unlock_irqrestore(&lp->lock, flags); if (dev->flags & IFF_PROMISC)
lp->mcr0 |= MCR0_PROMISC;
/* Build the hash table */ /* Enable multicast hash table function to
if (netdev_mc_count(dev) > MCAST_MAX) { * receive all multicast packets. */
u16 hash_table[4]; else if (dev->flags & IFF_ALLMULTI) {
u32 crc; lp->mcr0 |= MCR0_HASH_EN;
for (i = 0; i < 4; i++) for (i = 0; i < MCAST_MAX ; i++) {
hash_table[i] = 0; iowrite16(0, ioaddr + MID_1L + 8 * i);
iowrite16(0, ioaddr + MID_1M + 8 * i);
iowrite16(0, ioaddr + MID_1H + 8 * i);
}
for (i = 0; i < 4; i++)
hash_table[i] = 0xffff;
}
/* Use internal multicast address registers if the number of
* multicast addresses is not greater than MCAST_MAX. */
else if (netdev_mc_count(dev) <= MCAST_MAX) {
i = 0;
netdev_for_each_mc_addr(ha, dev) { netdev_for_each_mc_addr(ha, dev) {
char *addrs = ha->addr; u16 *adrp = (u16 *) ha->addr;
iowrite16(adrp[0], ioaddr + MID_1L + 8 * i);
iowrite16(adrp[1], ioaddr + MID_1M + 8 * i);
iowrite16(adrp[2], ioaddr + MID_1H + 8 * i);
i++;
}
while (i < MCAST_MAX) {
iowrite16(0, ioaddr + MID_1L + 8 * i);
iowrite16(0, ioaddr + MID_1M + 8 * i);
iowrite16(0, ioaddr + MID_1H + 8 * i);
i++;
}
}
/* Otherwise, Enable multicast hash table function. */
else {
u32 crc;
if (!(*addrs & 1)) lp->mcr0 |= MCR0_HASH_EN;
continue;
for (i = 0; i < MCAST_MAX ; i++) {
iowrite16(0, ioaddr + MID_1L + 8 * i);
iowrite16(0, ioaddr + MID_1M + 8 * i);
iowrite16(0, ioaddr + MID_1H + 8 * i);
}
crc = ether_crc_le(6, addrs); /* Build multicast hash table */
netdev_for_each_mc_addr(ha, dev) {
u8 *addrs = ha->addr;
crc = ether_crc(ETH_ALEN, addrs);
crc >>= 26; crc >>= 26;
hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf)); hash_table[crc >> 4] |= 1 << (crc & 0xf);
} }
/* Fill the MAC hash tables with their values */ }
iowrite16(lp->mcr0, ioaddr + MCR0);
/* Fill the MAC hash tables with their values */
if (lp->mcr0 && MCR0_HASH_EN) {
iowrite16(hash_table[0], ioaddr + MAR0); iowrite16(hash_table[0], ioaddr + MAR0);
iowrite16(hash_table[1], ioaddr + MAR1); iowrite16(hash_table[1], ioaddr + MAR1);
iowrite16(hash_table[2], ioaddr + MAR2); iowrite16(hash_table[2], ioaddr + MAR2);
iowrite16(hash_table[3], ioaddr + MAR3); iowrite16(hash_table[3], ioaddr + MAR3);
} }
/* Multicast Address 1~4 case */
i = 0; spin_unlock_irqrestore(&lp->lock, flags);
netdev_for_each_mc_addr(ha, dev) {
if (i >= MCAST_MAX)
break;
adrp = (u16 *) ha->addr;
iowrite16(adrp[0], ioaddr + MID_1L + 8 * i);
iowrite16(adrp[1], ioaddr + MID_1M + 8 * i);
iowrite16(adrp[2], ioaddr + MID_1H + 8 * i);
i++;
}
while (i < MCAST_MAX) {
iowrite16(0xffff, ioaddr + MID_1L + 8 * i);
iowrite16(0xffff, ioaddr + MID_1M + 8 * i);
iowrite16(0xffff, ioaddr + MID_1H + 8 * i);
i++;
}
} }
static void netdev_get_drvinfo(struct net_device *dev, static void netdev_get_drvinfo(struct net_device *dev,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment