From 6c36a7074436e181fb3df41f66bbdaf53980951e Mon Sep 17 00:00:00 2001 From: frederic RODO Date: Thu, 12 Jul 2007 19:07:24 +0200 Subject: macb: Use generic PHY layer Convert the macb driver to use the generic PHY layer in drivers/net/phy. Signed-off-by: Frederic RODO Signed-off-by: Haavard Skinnemoen Signed-off-by: Jeff Garzik --- drivers/net/macb.c | 451 ++++++++++++++++++++++++----------------------------- 1 file changed, 200 insertions(+), 251 deletions(-) (limited to 'drivers/net/macb.c') diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 0e04f7ac3f2..83c35fd91e0 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -17,13 +17,12 @@ #include #include #include -#include -#include #include -#include #include +#include #include +#include #include "macb.h" @@ -85,172 +84,202 @@ static void __init macb_get_hwaddr(struct macb *bp) memcpy(bp->dev->dev_addr, addr, sizeof(addr)); } -static void macb_enable_mdio(struct macb *bp) +static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum) { - unsigned long flags; - u32 reg; - - spin_lock_irqsave(&bp->lock, flags); - reg = macb_readl(bp, NCR); - reg |= MACB_BIT(MPE); - macb_writel(bp, NCR, reg); - macb_writel(bp, IER, MACB_BIT(MFD)); - spin_unlock_irqrestore(&bp->lock, flags); -} - -static void macb_disable_mdio(struct macb *bp) -{ - unsigned long flags; - u32 reg; - - spin_lock_irqsave(&bp->lock, flags); - reg = macb_readl(bp, NCR); - reg &= ~MACB_BIT(MPE); - macb_writel(bp, NCR, reg); - macb_writel(bp, IDR, MACB_BIT(MFD)); - spin_unlock_irqrestore(&bp->lock, flags); -} - -static int macb_mdio_read(struct net_device *dev, int phy_id, int location) -{ - struct macb *bp = netdev_priv(dev); + struct macb *bp = bus->priv; int value; - mutex_lock(&bp->mdio_mutex); - - macb_enable_mdio(bp); macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_SOF) | MACB_BF(RW, MACB_MAN_READ) - | MACB_BF(PHYA, phy_id) - | MACB_BF(REGA, location) + | MACB_BF(PHYA, mii_id) + | MACB_BF(REGA, regnum) | MACB_BF(CODE, MACB_MAN_CODE))); - wait_for_completion(&bp->mdio_complete); + /* wait for end of transfer */ + while (!MACB_BFEXT(IDLE, macb_readl(bp, NSR))) + cpu_relax(); value = MACB_BFEXT(DATA, macb_readl(bp, MAN)); - macb_disable_mdio(bp); - mutex_unlock(&bp->mdio_mutex); return value; } -static void macb_mdio_write(struct net_device *dev, int phy_id, - int location, int val) +static int macb_mdio_write(struct mii_bus *bus, int mii_id, int regnum, + u16 value) { - struct macb *bp = netdev_priv(dev); - - dev_dbg(&bp->pdev->dev, "mdio_write %02x:%02x <- %04x\n", - phy_id, location, val); - - mutex_lock(&bp->mdio_mutex); - macb_enable_mdio(bp); + struct macb *bp = bus->priv; macb_writel(bp, MAN, (MACB_BF(SOF, MACB_MAN_SOF) | MACB_BF(RW, MACB_MAN_WRITE) - | MACB_BF(PHYA, phy_id) - | MACB_BF(REGA, location) + | MACB_BF(PHYA, mii_id) + | MACB_BF(REGA, regnum) | MACB_BF(CODE, MACB_MAN_CODE) - | MACB_BF(DATA, val))); + | MACB_BF(DATA, value))); - wait_for_completion(&bp->mdio_complete); + /* wait for end of transfer */ + while (!MACB_BFEXT(IDLE, macb_readl(bp, NSR))) + cpu_relax(); + + return 0; +} - macb_disable_mdio(bp); - mutex_unlock(&bp->mdio_mutex); +static int macb_mdio_reset(struct mii_bus *bus) +{ + return 0; } -static int macb_phy_probe(struct macb *bp) +static void macb_handle_link_change(struct net_device *dev) { - int phy_address; - u16 phyid1, phyid2; + struct macb *bp = netdev_priv(dev); + struct phy_device *phydev = bp->phy_dev; + unsigned long flags; - for (phy_address = 0; phy_address < 32; phy_address++) { - phyid1 = macb_mdio_read(bp->dev, phy_address, MII_PHYSID1); - phyid2 = macb_mdio_read(bp->dev, phy_address, MII_PHYSID2); + int status_change = 0; - if (phyid1 != 0xffff && phyid1 != 0x0000 - && phyid2 != 0xffff && phyid2 != 0x0000) - break; + spin_lock_irqsave(&bp->lock, flags); + + if (phydev->link) { + if ((bp->speed != phydev->speed) || + (bp->duplex != phydev->duplex)) { + u32 reg; + + reg = macb_readl(bp, NCFGR); + reg &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); + + if (phydev->duplex) + reg |= MACB_BIT(FD); + if (phydev->speed) + reg |= MACB_BIT(SPD); + + macb_writel(bp, NCFGR, reg); + + bp->speed = phydev->speed; + bp->duplex = phydev->duplex; + status_change = 1; + } } - if (phy_address == 32) - return -ENODEV; + if (phydev->link != bp->link) { + if (phydev->link) + netif_schedule(dev); + else { + bp->speed = 0; + bp->duplex = -1; + } + bp->link = phydev->link; - dev_info(&bp->pdev->dev, - "detected PHY at address %d (ID %04x:%04x)\n", - phy_address, phyid1, phyid2); + status_change = 1; + } - bp->mii.phy_id = phy_address; - return 0; + spin_unlock_irqrestore(&bp->lock, flags); + + if (status_change) { + if (phydev->link) + printk(KERN_INFO "%s: link up (%d/%s)\n", + dev->name, phydev->speed, + DUPLEX_FULL == phydev->duplex ? "Full":"Half"); + else + printk(KERN_INFO "%s: link down\n", dev->name); + } } -static void macb_set_media(struct macb *bp, int media) +/* based on au1000_eth. c*/ +static int macb_mii_probe(struct net_device *dev) { - u32 reg; + struct macb *bp = netdev_priv(dev); + struct phy_device *phydev = NULL; + struct eth_platform_data *pdata; + int phy_addr; - spin_lock_irq(&bp->lock); - reg = macb_readl(bp, NCFGR); - reg &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); - if (media & (ADVERTISE_100HALF | ADVERTISE_100FULL)) - reg |= MACB_BIT(SPD); - if (media & ADVERTISE_FULL) - reg |= MACB_BIT(FD); - macb_writel(bp, NCFGR, reg); - spin_unlock_irq(&bp->lock); + /* find the first phy */ + for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { + if (bp->mii_bus.phy_map[phy_addr]) { + phydev = bp->mii_bus.phy_map[phy_addr]; + break; + } + } + + if (!phydev) { + printk (KERN_ERR "%s: no PHY found\n", dev->name); + return -1; + } + + pdata = bp->pdev->dev.platform_data; + /* TODO : add pin_irq */ + + /* attach the mac to the phy */ + if (pdata && pdata->is_rmii) { + phydev = phy_connect(dev, phydev->dev.bus_id, + &macb_handle_link_change, 0, PHY_INTERFACE_MODE_RMII); + } else { + phydev = phy_connect(dev, phydev->dev.bus_id, + &macb_handle_link_change, 0, PHY_INTERFACE_MODE_MII); + } + + if (IS_ERR(phydev)) { + printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); + return PTR_ERR(phydev); + } + + /* mask with MAC supported features */ + phydev->supported &= PHY_BASIC_FEATURES; + + phydev->advertising = phydev->supported; + + bp->link = 0; + bp->speed = 0; + bp->duplex = -1; + bp->phy_dev = phydev; + + return 0; } -static void macb_check_media(struct macb *bp, int ok_to_print, int init_media) +static int macb_mii_init(struct macb *bp) { - struct mii_if_info *mii = &bp->mii; - unsigned int old_carrier, new_carrier; - int advertise, lpa, media, duplex; + struct eth_platform_data *pdata; + int err = -ENXIO, i; - /* if forced media, go no further */ - if (mii->force_media) - return; + /* Enable managment port */ + macb_writel(bp, NCR, MACB_BIT(MPE)); - /* check current and old link status */ - old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0; - new_carrier = (unsigned int) mii_link_ok(mii); + bp->mii_bus.name = "MACB_mii_bus", + bp->mii_bus.read = &macb_mdio_read, + bp->mii_bus.write = &macb_mdio_write, + bp->mii_bus.reset = &macb_mdio_reset, + bp->mii_bus.id = bp->pdev->id, + bp->mii_bus.priv = bp, + bp->mii_bus.dev = &bp->dev->dev; + pdata = bp->pdev->dev.platform_data; - /* if carrier state did not change, assume nothing else did */ - if (!init_media && old_carrier == new_carrier) - return; + if (pdata) + bp->mii_bus.phy_mask = pdata->phy_mask; - /* no carrier, nothing much to do */ - if (!new_carrier) { - netif_carrier_off(mii->dev); - printk(KERN_INFO "%s: link down\n", mii->dev->name); - return; + bp->mii_bus.irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); + if (!bp->mii_bus.irq) { + err = -ENOMEM; + goto err_out; } - /* - * we have carrier, see who's on the other end - */ - netif_carrier_on(mii->dev); + for (i = 0; i < PHY_MAX_ADDR; i++) + bp->mii_bus.irq[i] = PHY_POLL; - /* get MII advertise and LPA values */ - if (!init_media && mii->advertising) { - advertise = mii->advertising; - } else { - advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE); - mii->advertising = advertise; - } - lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA); + platform_set_drvdata(bp->dev, &bp->mii_bus); - /* figure out media and duplex from advertise and LPA values */ - media = mii_nway_result(lpa & advertise); - duplex = (media & ADVERTISE_FULL) ? 1 : 0; + if (mdiobus_register(&bp->mii_bus)) + goto err_out_free_mdio_irq; - if (ok_to_print) - printk(KERN_INFO "%s: link up, %sMbps, %s-duplex, lpa 0x%04X\n", - mii->dev->name, - media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? "100" : "10", - duplex ? "full" : "half", lpa); + if (macb_mii_probe(bp->dev) != 0) { + goto err_out_unregister_bus; + } - mii->full_duplex = duplex; + return 0; - /* Let the MAC know about the new link state */ - macb_set_media(bp, media); +err_out_unregister_bus: + mdiobus_unregister(&bp->mii_bus); +err_out_free_mdio_irq: + kfree(bp->mii_bus.irq); +err_out: + return err; } static void macb_update_stats(struct macb *bp) @@ -265,16 +294,6 @@ static void macb_update_stats(struct macb *bp) *p += __raw_readl(reg); } -static void macb_periodic_task(struct work_struct *work) -{ - struct macb *bp = container_of(work, struct macb, periodic_task.work); - - macb_update_stats(bp); - macb_check_media(bp, 1, 0); - - schedule_delayed_work(&bp->periodic_task, HZ); -} - static void macb_tx(struct macb *bp) { unsigned int tail; @@ -519,9 +538,6 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) spin_lock(&bp->lock); while (status) { - if (status & MACB_BIT(MFD)) - complete(&bp->mdio_complete); - /* close possible race with dev_close */ if (unlikely(!netif_running(dev))) { macb_writel(bp, IDR, ~0UL); @@ -535,7 +551,8 @@ static irqreturn_t macb_interrupt(int irq, void *dev_id) * until we have processed the buffers */ macb_writel(bp, IDR, MACB_RX_INT_FLAGS); - dev_dbg(&bp->pdev->dev, "scheduling RX softirq\n"); + dev_dbg(&bp->pdev->dev, + "scheduling RX softirq\n"); __netif_rx_schedule(dev); } } @@ -765,7 +782,7 @@ static void macb_init_hw(struct macb *bp) macb_writel(bp, TBQP, bp->tx_ring_dma); /* Enable TX and RX */ - macb_writel(bp, NCR, MACB_BIT(RE) | MACB_BIT(TE)); + macb_writel(bp, NCR, MACB_BIT(RE) | MACB_BIT(TE) | MACB_BIT(MPE)); /* Enable interrupts */ macb_writel(bp, IER, (MACB_BIT(RCOMP) @@ -776,18 +793,7 @@ static void macb_init_hw(struct macb *bp) | MACB_BIT(TCOMP) | MACB_BIT(ISR_ROVR) | MACB_BIT(HRESP))); -} -static void macb_init_phy(struct net_device *dev) -{ - struct macb *bp = netdev_priv(dev); - - /* Set some reasonable default settings */ - macb_mdio_write(dev, bp->mii.phy_id, MII_ADVERTISE, - ADVERTISE_CSMA | ADVERTISE_ALL); - macb_mdio_write(dev, bp->mii.phy_id, MII_BMCR, - (BMCR_SPEED100 | BMCR_ANENABLE - | BMCR_ANRESTART | BMCR_FULLDPLX)); } static int macb_open(struct net_device *dev) @@ -797,6 +803,10 @@ static int macb_open(struct net_device *dev) dev_dbg(&bp->pdev->dev, "open\n"); + /* if the phy is not yet register, retry later*/ + if (!bp->phy_dev) + return -EAGAIN; + if (!is_valid_ether_addr(dev->dev_addr)) return -EADDRNOTAVAIL; @@ -810,12 +820,11 @@ static int macb_open(struct net_device *dev) macb_init_rings(bp); macb_init_hw(bp); - macb_init_phy(dev); - macb_check_media(bp, 1, 1); - netif_start_queue(dev); + /* schedule a link state check */ + phy_start(bp->phy_dev); - schedule_delayed_work(&bp->periodic_task, HZ); + netif_start_queue(dev); return 0; } @@ -825,10 +834,11 @@ static int macb_close(struct net_device *dev) struct macb *bp = netdev_priv(dev); unsigned long flags; - cancel_rearming_delayed_work(&bp->periodic_task); - netif_stop_queue(dev); + if (bp->phy_dev) + phy_stop(bp->phy_dev); + spin_lock_irqsave(&bp->lock, flags); macb_reset_hw(bp); netif_carrier_off(dev); @@ -845,6 +855,9 @@ static struct net_device_stats *macb_get_stats(struct net_device *dev) struct net_device_stats *nstat = &bp->stats; struct macb_stats *hwstat = &bp->hw_stats; + /* read stats from hardware */ + macb_update_stats(bp); + /* Convert HW stats into netdevice stats */ nstat->rx_errors = (hwstat->rx_fcs_errors + hwstat->rx_align_errors + @@ -882,18 +895,27 @@ static struct net_device_stats *macb_get_stats(struct net_device *dev) static int macb_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct macb *bp = netdev_priv(dev); + struct phy_device *phydev = bp->phy_dev; + + if (!phydev) + return -ENODEV; - return mii_ethtool_gset(&bp->mii, cmd); + return phy_ethtool_gset(phydev, cmd); } static int macb_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct macb *bp = netdev_priv(dev); + struct phy_device *phydev = bp->phy_dev; - return mii_ethtool_sset(&bp->mii, cmd); + if (!phydev) + return -ENODEV; + + return phy_ethtool_sset(phydev, cmd); } -static void macb_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +static void macb_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { struct macb *bp = netdev_priv(dev); @@ -902,104 +924,34 @@ static void macb_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *inf strcpy(info->bus_info, bp->pdev->dev.bus_id); } -static int macb_nway_reset(struct net_device *dev) -{ - struct macb *bp = netdev_priv(dev); - return mii_nway_restart(&bp->mii); -} - static struct ethtool_ops macb_ethtool_ops = { .get_settings = macb_get_settings, .set_settings = macb_set_settings, .get_drvinfo = macb_get_drvinfo, - .nway_reset = macb_nway_reset, .get_link = ethtool_op_get_link, }; static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct macb *bp = netdev_priv(dev); + struct phy_device *phydev = bp->phy_dev; if (!netif_running(dev)) return -EINVAL; - return generic_mii_ioctl(&bp->mii, if_mii(rq), cmd, NULL); -} - -static ssize_t macb_mii_show(const struct device *_dev, char *buf, - unsigned long addr) -{ - struct net_device *dev = to_net_dev(_dev); - struct macb *bp = netdev_priv(dev); - ssize_t ret = -EINVAL; - - if (netif_running(dev)) { - int value; - value = macb_mdio_read(dev, bp->mii.phy_id, addr); - ret = sprintf(buf, "0x%04x\n", (uint16_t)value); - } - - return ret; -} - -#define MII_ENTRY(name, addr) \ -static ssize_t show_##name(struct device *_dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return macb_mii_show(_dev, buf, addr); \ -} \ -static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL) - -MII_ENTRY(bmcr, MII_BMCR); -MII_ENTRY(bmsr, MII_BMSR); -MII_ENTRY(physid1, MII_PHYSID1); -MII_ENTRY(physid2, MII_PHYSID2); -MII_ENTRY(advertise, MII_ADVERTISE); -MII_ENTRY(lpa, MII_LPA); -MII_ENTRY(expansion, MII_EXPANSION); - -static struct attribute *macb_mii_attrs[] = { - &dev_attr_bmcr.attr, - &dev_attr_bmsr.attr, - &dev_attr_physid1.attr, - &dev_attr_physid2.attr, - &dev_attr_advertise.attr, - &dev_attr_lpa.attr, - &dev_attr_expansion.attr, - NULL, -}; - -static struct attribute_group macb_mii_group = { - .name = "mii", - .attrs = macb_mii_attrs, -}; - -static void macb_unregister_sysfs(struct net_device *net) -{ - struct device *_dev = &net->dev; + if (!phydev) + return -ENODEV; - sysfs_remove_group(&_dev->kobj, &macb_mii_group); + return phy_mii_ioctl(phydev, if_mii(rq), cmd); } -static int macb_register_sysfs(struct net_device *net) -{ - struct device *_dev = &net->dev; - int ret; - - ret = sysfs_create_group(&_dev->kobj, &macb_mii_group); - if (ret) - printk(KERN_WARNING - "%s: sysfs mii attribute registration failed: %d\n", - net->name, ret); - return ret; -} static int __devinit macb_probe(struct platform_device *pdev) { struct eth_platform_data *pdata; struct resource *regs; struct net_device *dev; struct macb *bp; + struct phy_device *phydev; unsigned long pclk_hz; u32 config; int err = -ENXIO; @@ -1080,10 +1032,6 @@ static int __devinit macb_probe(struct platform_device *pdev) dev->base_addr = regs->start; - INIT_DELAYED_WORK(&bp->periodic_task, macb_periodic_task); - mutex_init(&bp->mdio_mutex); - init_completion(&bp->mdio_complete); - /* Set MII management clock divider */ pclk_hz = clk_get_rate(bp->pclk); if (pclk_hz <= 20000000) @@ -1096,20 +1044,9 @@ static int __devinit macb_probe(struct platform_device *pdev) config = MACB_BF(CLK, MACB_CLK_DIV64); macb_writel(bp, NCFGR, config); - bp->mii.dev = dev; - bp->mii.mdio_read = macb_mdio_read; - bp->mii.mdio_write = macb_mdio_write; - bp->mii.phy_id_mask = 0x1f; - bp->mii.reg_num_mask = 0x1f; - macb_get_hwaddr(bp); - err = macb_phy_probe(bp); - if (err) { - dev_err(&pdev->dev, "Failed to detect PHY, aborting.\n"); - goto err_out_free_irq; - } - pdata = pdev->dev.platform_data; + if (pdata && pdata->is_rmii) #if defined(CONFIG_ARCH_AT91) macb_writel(bp, USRIO, (MACB_BIT(RMII) | MACB_BIT(CLKEN)) ); @@ -1131,9 +1068,11 @@ static int __devinit macb_probe(struct platform_device *pdev) goto err_out_free_irq; } - platform_set_drvdata(pdev, dev); + if (macb_mii_init(bp) != 0) { + goto err_out_unregister_netdev; + } - macb_register_sysfs(dev); + platform_set_drvdata(pdev, dev); printk(KERN_INFO "%s: Atmel MACB at 0x%08lx irq %d " "(%02x:%02x:%02x:%02x:%02x:%02x)\n", @@ -1141,8 +1080,15 @@ static int __devinit macb_probe(struct platform_device *pdev) dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); + phydev = bp->phy_dev; + printk(KERN_INFO "%s: attached PHY driver [%s] " + "(mii_bus:phy_addr=%s, irq=%d)\n", + dev->name, phydev->drv->name, phydev->dev.bus_id, phydev->irq); + return 0; +err_out_unregister_netdev: + unregister_netdev(dev); err_out_free_irq: free_irq(dev->irq, dev); err_out_iounmap: @@ -1153,7 +1099,9 @@ err_out_disable_clocks: clk_put(bp->hclk); #endif clk_disable(bp->pclk); +#ifndef CONFIG_ARCH_AT91 err_out_put_pclk: +#endif clk_put(bp->pclk); err_out_free_dev: free_netdev(dev); @@ -1171,7 +1119,8 @@ static int __devexit macb_remove(struct platform_device *pdev) if (dev) { bp = netdev_priv(dev); - macb_unregister_sysfs(dev); + mdiobus_unregister(&bp->mii_bus); + kfree(bp->mii_bus.irq); unregister_netdev(dev); free_irq(dev->irq, dev); iounmap(bp->regs); -- cgit v1.2.3 From 446ebd0118d8e82791652bd17dd8db08ab993c0e Mon Sep 17 00:00:00 2001 From: Patrice Vilchez Date: Thu, 12 Jul 2007 19:07:25 +0200 Subject: macb: Add multicast capability Add multicast capability to Atmel ethernet macb driver. Signed-off-by: Patrice Vilchez Signed-off-by: Haavard Skinnemoen Signed-off-by: Jeff Garzik --- drivers/net/macb.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) (limited to 'drivers/net/macb.c') diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 83c35fd91e0..a4bb0264180 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -796,6 +796,125 @@ static void macb_init_hw(struct macb *bp) } +/* + * The hash address register is 64 bits long and takes up two + * locations in the memory map. The least significant bits are stored + * in EMAC_HSL and the most significant bits in EMAC_HSH. + * + * The unicast hash enable and the multicast hash enable bits in the + * network configuration register enable the reception of hash matched + * frames. The destination address is reduced to a 6 bit index into + * the 64 bit hash register using the following hash function. The + * hash function is an exclusive or of every sixth bit of the + * destination address. + * + * hi[5] = da[5] ^ da[11] ^ da[17] ^ da[23] ^ da[29] ^ da[35] ^ da[41] ^ da[47] + * hi[4] = da[4] ^ da[10] ^ da[16] ^ da[22] ^ da[28] ^ da[34] ^ da[40] ^ da[46] + * hi[3] = da[3] ^ da[09] ^ da[15] ^ da[21] ^ da[27] ^ da[33] ^ da[39] ^ da[45] + * hi[2] = da[2] ^ da[08] ^ da[14] ^ da[20] ^ da[26] ^ da[32] ^ da[38] ^ da[44] + * hi[1] = da[1] ^ da[07] ^ da[13] ^ da[19] ^ da[25] ^ da[31] ^ da[37] ^ da[43] + * hi[0] = da[0] ^ da[06] ^ da[12] ^ da[18] ^ da[24] ^ da[30] ^ da[36] ^ da[42] + * + * da[0] represents the least significant bit of the first byte + * received, that is, the multicast/unicast indicator, and da[47] + * represents the most significant bit of the last byte received. If + * the hash index, hi[n], points to a bit that is set in the hash + * register then the frame will be matched according to whether the + * frame is multicast or unicast. A multicast match will be signalled + * if the multicast hash enable bit is set, da[0] is 1 and the hash + * index points to a bit set in the hash register. A unicast match + * will be signalled if the unicast hash enable bit is set, da[0] is 0 + * and the hash index points to a bit set in the hash register. To + * receive all multicast frames, the hash register should be set with + * all ones and the multicast hash enable bit should be set in the + * network configuration register. + */ + +static inline int hash_bit_value(int bitnr, __u8 *addr) +{ + if (addr[bitnr / 8] & (1 << (bitnr % 8))) + return 1; + return 0; +} + +/* + * Return the hash index value for the specified address. + */ +static int hash_get_index(__u8 *addr) +{ + int i, j, bitval; + int hash_index = 0; + + for (j = 0; j < 6; j++) { + for (i = 0, bitval = 0; i < 8; i++) + bitval ^= hash_bit_value(i*6 + j, addr); + + hash_index |= (bitval << j); + } + + return hash_index; +} + +/* + * Add multicast addresses to the internal multicast-hash table. + */ +static void macb_sethashtable(struct net_device *dev) +{ + struct dev_mc_list *curr; + unsigned long mc_filter[2]; + unsigned int i, bitnr; + struct macb *bp = netdev_priv(dev); + + mc_filter[0] = mc_filter[1] = 0; + + curr = dev->mc_list; + for (i = 0; i < dev->mc_count; i++, curr = curr->next) { + if (!curr) break; /* unexpected end of list */ + + bitnr = hash_get_index(curr->dmi_addr); + mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); + } + + macb_writel(bp, HRB, mc_filter[0]); + macb_writel(bp, HRT, mc_filter[1]); +} + +/* + * Enable/Disable promiscuous and multicast modes. + */ +static void macb_set_rx_mode(struct net_device *dev) +{ + unsigned long cfg; + struct macb *bp = netdev_priv(dev); + + cfg = macb_readl(bp, NCFGR); + + if (dev->flags & IFF_PROMISC) + /* Enable promiscuous mode */ + cfg |= MACB_BIT(CAF); + else if (dev->flags & (~IFF_PROMISC)) + /* Disable promiscuous mode */ + cfg &= ~MACB_BIT(CAF); + + if (dev->flags & IFF_ALLMULTI) { + /* Enable all multicast mode */ + macb_writel(bp, HRB, -1); + macb_writel(bp, HRT, -1); + cfg |= MACB_BIT(NCFGR_MTI); + } else if (dev->mc_count > 0) { + /* Enable specific multicasts */ + macb_sethashtable(dev); + cfg |= MACB_BIT(NCFGR_MTI); + } else if (dev->flags & (~IFF_ALLMULTI)) { + /* Disable all multicast mode */ + macb_writel(bp, HRB, 0); + macb_writel(bp, HRT, 0); + cfg &= ~MACB_BIT(NCFGR_MTI); + } + + macb_writel(bp, NCFGR, cfg); +} + static int macb_open(struct net_device *dev) { struct macb *bp = netdev_priv(dev); @@ -1025,6 +1144,7 @@ static int __devinit macb_probe(struct platform_device *pdev) dev->stop = macb_close; dev->hard_start_xmit = macb_start_xmit; dev->get_stats = macb_get_stats; + dev->set_multicast_list = macb_set_rx_mode; dev->do_ioctl = macb_ioctl; dev->poll = macb_poll; dev->weight = 64; -- cgit v1.2.3