X-Git-Url: https://git.rohieb.name/openwrt.git/blobdiff_plain/decdb0499fd307cc6c95818a41aa8513d30c3eaa..ca93116b8d8097330d718e820c26ade1343622dd:/target/linux/rdc/patches-2.6.32/015-r6040_fix_multicast.patch?ds=sidebyside diff --git a/target/linux/rdc/patches-2.6.32/015-r6040_fix_multicast.patch b/target/linux/rdc/patches-2.6.32/015-r6040_fix_multicast.patch index f41b4b542..a55ec192f 100644 --- a/target/linux/rdc/patches-2.6.32/015-r6040_fix_multicast.patch +++ b/target/linux/rdc/patches-2.6.32/015-r6040_fix_multicast.patch @@ -1,35 +1,177 @@ +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. + + +The version is for net-next-2.6: + +Reported-by: Marc Leclerc +Tested-by: Marc Leclerc +Signed-off-by: Shawn Lin +Signed-off-by: Albert Chen --- --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c -@@ -136,7 +136,7 @@ - #define RX_DESC_SIZE (RX_DCNT * sizeof(struct r6040_descriptor)) - #define TX_DESC_SIZE (TX_DCNT * sizeof(struct r6040_descriptor)) - #define MBCR_DEFAULT 0x012A /* MAC Bus Control Register */ --#define MCAST_MAX 4 /* Max number multicast addresses to filter */ -+#define MCAST_MAX 3 /* Max number multicast addresses to filter */ - - /* Descriptor status */ - #define DSC_OWNER_MAC 0x8000 /* MAC is the owner of this descriptor */ -@@ -887,9 +887,6 @@ static void r6040_multicast_list(struct - crc >>= 26; - hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf)); +@@ -70,6 +70,8 @@ + + /* MAC registers */ + #define MCR0 0x00 /* Control register 0 */ ++#define PROMISC 0x0020 /* Promiscuous mode */ ++#define HASH_EN 0x0100 /* Enable multicast hash table function */ + #define MCR1 0x04 /* Control register 1 */ + #define MAC_RST 0x0001 /* Reset the MAC */ + #define MBCR 0x08 /* Bus control */ +@@ -837,76 +839,88 @@ static void r6040_multicast_list(struct + { + struct r6040_private *lp = netdev_priv(dev); + void __iomem *ioaddr = lp->base; +- u16 *adrp; +- u16 reg; + unsigned long flags; + struct dev_mc_list *dmi = dev->mc_list; + int i; ++ u16 hash_table[4] = { 0 }; + +- /* MAC Address */ +- adrp = (u16 *)dev->dev_addr; +- iowrite16(adrp[0], ioaddr + MID_0L); +- iowrite16(adrp[1], ioaddr + MID_0M); +- iowrite16(adrp[2], ioaddr + MID_0H); +- +- /* Promiscous Mode */ + spin_lock_irqsave(&lp->lock, flags); + + /* Clear AMCP & PROM bits */ +- reg = ioread16(ioaddr) & ~0x0120; ++ lp->mcr0 = ioread16(ioaddr + MCR0) & ~(PROMISC | HASH_EN); ++ ++ /* Promiscuous mode */ + if (dev->flags & IFF_PROMISC) { +- reg |= 0x0020; +- lp->mcr0 |= 0x0020; ++ lp->mcr0 |= PROMISC; + } +- /* Too many multicast addresses +- * accept all traffic */ +- else if ((dev->mc_count > MCAST_MAX) +- || (dev->flags & IFF_ALLMULTI)) +- reg |= 0x0020; + +- iowrite16(reg, ioaddr); +- spin_unlock_irqrestore(&lp->lock, flags); +- +- /* Build the hash table */ +- if (dev->mc_count > MCAST_MAX) { +- u16 hash_table[4]; +- u32 crc; ++ /* Enable multicast hash table function to ++ * receive all multicast packets. */ ++ else if (dev->flags & IFF_ALLMULTI) { ++ lp->mcr0 |= HASH_EN; ++ ++ 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); ++ } + + for (i = 0; i < 4; i++) +- hash_table[i] = 0; ++ hash_table[i] = 0xffff; ++ } + +- for (i = 0; i < dev->mc_count; i++) { +- char *addrs = dmi->dmi_addr; ++ /* Use internal multicast address registers if the number of ++ * multicast addresses is not greater than MCAST_MAX. */ ++ else if (dev->mc_count <= MCAST_MAX) { ++ i = 0; ++ while (i < dev->mc_count) { ++ u16 *adrp = (u16 *)dmi->dmi_addr; + + dmi = dmi->next; ++ 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)) +- continue; ++ lp->mcr0 |= HASH_EN; + +- crc = ether_crc_le(6, addrs); +- crc >>= 26; +- hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf)); ++ 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); } -- /* Write the index of the hash table */ -- for (i = 0; i < 4; i++) -- iowrite16(hash_table[i] << 14, ioaddr + MCR1); - /* Fill the MAC hash tables with their values */ +- /* Fill the MAC hash tables with their values */ ++ ++ /* Build multicast hash table */ ++ for (i = 0; i < dev->mc_count; i++) { ++ u8 *addrs = dmi->dmi_addr; ++ dmi = dmi->next; ++ ++ crc = ether_crc(ETH_ALEN, addrs); ++ hash_table[crc >> 4] |= 1 << (crc & 0xf); ++ } ++ } ++ iowrite16(lp->mcr0, ioaddr + MCR0); ++ ++ /* Fill the MAC hash tables with their values */ ++ if (lp->mcr0 && HASH_EN) { iowrite16(hash_table[0], ioaddr + MAR0); iowrite16(hash_table[1], ioaddr + MAR1); -@@ -905,9 +902,9 @@ static void r6040_multicast_list(struct - dmi = dmi->next; - } - for (i = dev->mc_count; i < MCAST_MAX; i++) { -- iowrite16(0xffff, ioaddr + MID_0L + 8*i); -- iowrite16(0xffff, ioaddr + MID_0M + 8*i); -- iowrite16(0xffff, ioaddr + MID_0H + 8*i); -+ iowrite16(0xffff, ioaddr + MID_1L + 8 * i); -+ iowrite16(0xffff, ioaddr + MID_1M + 8 * i); -+ iowrite16(0xffff, ioaddr + MID_1H + 8 * i); + iowrite16(hash_table[2], ioaddr + MAR2); + iowrite16(hash_table[3], ioaddr + MAR3); } +- /* Multicast Address 1~4 case */ +- dmi = dev->mc_list; +- for (i = 0, dmi; (i < dev->mc_count) && (i < MCAST_MAX); i++) { +- adrp = (u16 *)dmi->dmi_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); +- dmi = dmi->next; +- } +- for (i = dev->mc_count; i < MCAST_MAX; i++) { +- iowrite16(0xffff, ioaddr + MID_1L + 8*i); +- iowrite16(0xffff, ioaddr + MID_1M + 8*i); +- iowrite16(0xffff, ioaddr + MID_1H + 8*i); +- } ++ ++ spin_unlock_irqrestore(&lp->lock, flags); } + static void netdev_get_drvinfo(struct net_device *dev,