diff options
| -rw-r--r-- | target/linux/rdc/patches-2.6.32/015-r6040_fix_multicast.patch | 177 | 
1 files changed, 177 insertions, 0 deletions
| 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 new file mode 100644 index 000000000..a55ec192f --- /dev/null +++ b/target/linux/rdc/patches-2.6.32/015-r6040_fix_multicast.patch @@ -0,0 +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 <marc-leclerc@signaturealpha.com> +Tested-by: Marc Leclerc <marc-leclerc@signaturealpha.com> +Signed-off-by: Shawn Lin <shawn@dmp.com.tw> +Signed-off-by: Albert Chen <albert.chen@rdc.com.tw> +--- +--- a/drivers/net/r6040.c ++++ b/drivers/net/r6040.c +@@ -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); + 		} +-		/* 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); + 		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, | 
