openwrt/target/linux/rdc/patches-2.6.32/015-r6040_fix_multicast.patch

152 lines
4.1 KiB
Diff

--- 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,96 @@ 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 *adrp;
+ u16 hash_table[4] = { 0 };
+
+ spin_lock_irqsave(&lp->lock, flags);
- /* MAC Address */
+ /* Keep our 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);
+ 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);
+ }
+
+ /* 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);
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 && 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,