diff options
Diffstat (limited to 'drivers/net/ethernet/davicom/dm9000.c')
| -rw-r--r-- | drivers/net/ethernet/davicom/dm9000.c | 408 |
1 files changed, 232 insertions, 176 deletions
diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index 8cdf02503d1..13723c96d1a 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -23,12 +23,13 @@ #include <linux/ioport.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/skbuff.h> #include <linux/spinlock.h> #include <linux/crc32.h> #include <linux/mii.h> +#include <linux/of.h> +#include <linux/of_net.h> #include <linux/ethtool.h> #include <linux/dm9000.h> #include <linux/delay.h> @@ -108,8 +109,9 @@ typedef struct board_info { u8 imr_all; unsigned int flags; - unsigned int in_suspend :1; - unsigned int wake_supported :1; + unsigned int in_timeout:1; + unsigned int in_suspend:1; + unsigned int wake_supported:1; enum dm9000_type type; @@ -156,23 +158,11 @@ static inline board_info_t *to_dm9000_board(struct net_device *dev) /* DM9000 network board routine ---------------------------- */ -static void -dm9000_reset(board_info_t * db) -{ - dev_dbg(db->dev, "resetting device\n"); - - /* RESET device */ - writeb(DM9000_NCR, db->io_addr); - udelay(200); - writeb(NCR_RST, db->io_data); - udelay(200); -} - /* * Read a byte from I/O port */ static u8 -ior(board_info_t * db, int reg) +ior(board_info_t *db, int reg) { writeb(reg, db->io_addr); return readb(db->io_data); @@ -183,12 +173,33 @@ ior(board_info_t * db, int reg) */ static void -iow(board_info_t * db, int reg, int value) +iow(board_info_t *db, int reg, int value) { writeb(reg, db->io_addr); writeb(value, db->io_data); } +static void +dm9000_reset(board_info_t *db) +{ + dev_dbg(db->dev, "resetting device\n"); + + /* Reset DM9000, see DM9000 Application Notes V1.22 Jun 11, 2004 page 29 + * The essential point is that we have to do a double reset, and the + * instruction is to set LBK into MAC internal loopback mode. + */ + iow(db, DM9000_NCR, NCR_RST | NCR_MAC_LBK); + udelay(100); /* Application note says at least 20 us */ + if (ior(db, DM9000_NCR) & 1) + dev_err(db->dev, "dm9000 did not respond to first reset\n"); + + iow(db, DM9000_NCR, 0); + iow(db, DM9000_NCR, NCR_RST | NCR_MAC_LBK); + udelay(100); + if (ior(db, DM9000_NCR) & 1) + dev_err(db->dev, "dm9000 did not respond to second reset\n"); +} + /* routines for sending block to chip */ static void dm9000_outblk_8bit(void __iomem *reg, void *data, int count) @@ -257,6 +268,109 @@ static void dm9000_dumpblk_32bit(void __iomem *reg, int count) tmp = readl(reg); } +/* + * Sleep, either by using msleep() or if we are suspending, then + * use mdelay() to sleep. + */ +static void dm9000_msleep(board_info_t *db, unsigned int ms) +{ + if (db->in_suspend || db->in_timeout) + mdelay(ms); + else + msleep(ms); +} + +/* Read a word from phyxcer */ +static int +dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg) +{ + board_info_t *db = netdev_priv(dev); + unsigned long flags; + unsigned int reg_save; + int ret; + + mutex_lock(&db->addr_lock); + + spin_lock_irqsave(&db->lock, flags); + + /* Save previous register address */ + reg_save = readb(db->io_addr); + + /* Fill the phyxcer register into REG_0C */ + iow(db, DM9000_EPAR, DM9000_PHY | reg); + + /* Issue phyxcer read command */ + iow(db, DM9000_EPCR, EPCR_ERPRR | EPCR_EPOS); + + writeb(reg_save, db->io_addr); + spin_unlock_irqrestore(&db->lock, flags); + + dm9000_msleep(db, 1); /* Wait read complete */ + + spin_lock_irqsave(&db->lock, flags); + reg_save = readb(db->io_addr); + + iow(db, DM9000_EPCR, 0x0); /* Clear phyxcer read command */ + + /* The read data keeps on REG_0D & REG_0E */ + ret = (ior(db, DM9000_EPDRH) << 8) | ior(db, DM9000_EPDRL); + + /* restore the previous address */ + writeb(reg_save, db->io_addr); + spin_unlock_irqrestore(&db->lock, flags); + + mutex_unlock(&db->addr_lock); + + dm9000_dbg(db, 5, "phy_read[%02x] -> %04x\n", reg, ret); + return ret; +} + +/* Write a word to phyxcer */ +static void +dm9000_phy_write(struct net_device *dev, + int phyaddr_unused, int reg, int value) +{ + board_info_t *db = netdev_priv(dev); + unsigned long flags; + unsigned long reg_save; + + dm9000_dbg(db, 5, "phy_write[%02x] = %04x\n", reg, value); + if (!db->in_timeout) + mutex_lock(&db->addr_lock); + + spin_lock_irqsave(&db->lock, flags); + + /* Save previous register address */ + reg_save = readb(db->io_addr); + + /* Fill the phyxcer register into REG_0C */ + iow(db, DM9000_EPAR, DM9000_PHY | reg); + + /* Fill the written data into REG_0D & REG_0E */ + iow(db, DM9000_EPDRL, value); + iow(db, DM9000_EPDRH, value >> 8); + + /* Issue phyxcer write command */ + iow(db, DM9000_EPCR, EPCR_EPOS | EPCR_ERPRW); + + writeb(reg_save, db->io_addr); + spin_unlock_irqrestore(&db->lock, flags); + + dm9000_msleep(db, 1); /* Wait write complete */ + + spin_lock_irqsave(&db->lock, flags); + reg_save = readb(db->io_addr); + + iow(db, DM9000_EPCR, 0x0); /* Clear phyxcer write command */ + + /* restore the previous address */ + writeb(reg_save, db->io_addr); + + spin_unlock_irqrestore(&db->lock, flags); + if (!db->in_timeout) + mutex_unlock(&db->addr_lock); +} + /* dm9000_set_io * * select the specified set of io routines to use with the @@ -633,23 +747,28 @@ static const struct ethtool_ops dm9000_ethtool_ops = { .get_link = dm9000_get_link, .get_wol = dm9000_get_wol, .set_wol = dm9000_set_wol, - .get_eeprom_len = dm9000_get_eeprom_len, - .get_eeprom = dm9000_get_eeprom, - .set_eeprom = dm9000_set_eeprom, + .get_eeprom_len = dm9000_get_eeprom_len, + .get_eeprom = dm9000_get_eeprom, + .set_eeprom = dm9000_set_eeprom, }; static void dm9000_show_carrier(board_info_t *db, unsigned carrier, unsigned nsr) { + int lpa; struct net_device *ndev = db->ndev; + struct mii_if_info *mii = &db->mii; unsigned ncr = dm9000_read_locked(db, DM9000_NCR); - if (carrier) - dev_info(db->dev, "%s: link up, %dMbps, %s-duplex, no LPA\n", + if (carrier) { + lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA); + dev_info(db->dev, + "%s: link up, %dMbps, %s-duplex, lpa 0x%04X\n", ndev->name, (nsr & NSR_SPEED) ? 10 : 100, - (ncr & NCR_FDX) ? "full" : "half"); - else + (ncr & NCR_FDX) ? "full" : "half", lpa); + } else { dev_info(db->dev, "%s: link down\n", ndev->name); + } } static void @@ -678,7 +797,7 @@ dm9000_poll_work(struct work_struct *w) } } else mii_check_media(&db->mii, netif_msg_link(db), 0); - + if (netif_running(ndev)) dm9000_schedule_poll(db); } @@ -726,7 +845,7 @@ dm9000_hash_table_unlocked(struct net_device *dev) struct netdev_hw_addr *ha; int i, oft; u32 hash_val; - u16 hash_table[4]; + u16 hash_table[4] = { 0, 0, 0, 0x8000 }; /* broadcast address */ u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN; dm9000_dbg(db, 1, "entering %s\n", __func__); @@ -734,13 +853,6 @@ dm9000_hash_table_unlocked(struct net_device *dev) for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++) iow(db, oft, dev->dev_addr[i]); - /* Clear Hash Table */ - for (i = 0; i < 4; i++) - hash_table[i] = 0x0; - - /* broadcast address */ - hash_table[3] = 0x8000; - if (dev->flags & IFF_PROMISC) rcr |= RCR_PRMSC; @@ -773,6 +885,18 @@ dm9000_hash_table(struct net_device *dev) spin_unlock_irqrestore(&db->lock, flags); } +static void +dm9000_mask_interrupts(board_info_t *db) +{ + iow(db, DM9000_IMR, IMR_PAR); +} + +static void +dm9000_unmask_interrupts(board_info_t *db) +{ + iow(db, DM9000_IMR, db->imr_all); +} + /* * Initialize dm9000 board */ @@ -785,6 +909,9 @@ dm9000_init_dm9000(struct net_device *dev) dm9000_dbg(db, 1, "entering %s\n", __func__); + dm9000_reset(db); + dm9000_mask_interrupts(db); + /* I/O mode */ db->io_mode = ior(db, DM9000_ISR) >> 6; /* ISR bit7:6 keeps I/O mode */ @@ -794,6 +921,15 @@ dm9000_init_dm9000(struct net_device *dev) (dev->features & NETIF_F_RXCSUM) ? RCSR_CSUM : 0); iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */ + iow(db, DM9000_GPR, 0); + + /* If we are dealing with DM9000B, some extra steps are required: a + * manual phy reset, and setting init params. + */ + if (db->type == TYPE_DM9000B) { + dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); + dm9000_phy_write(dev, 0, MII_DM_DSPCR, DSPCR_INIT_PARAM); + } ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0; @@ -823,9 +959,6 @@ dm9000_init_dm9000(struct net_device *dev) db->imr_all = imr; - /* Enable TX/RX interrupt mask */ - iow(db, DM9000_IMR, imr); - /* Init Driver variable */ db->tx_pkt_cnt = 0; db->queue_pkt_len = 0; @@ -841,17 +974,19 @@ static void dm9000_timeout(struct net_device *dev) /* Save previous register address */ spin_lock_irqsave(&db->lock, flags); + db->in_timeout = 1; reg_save = readb(db->io_addr); netif_stop_queue(dev); - dm9000_reset(db); dm9000_init_dm9000(dev); + dm9000_unmask_interrupts(db); /* We can accept TX packets again */ dev->trans_start = jiffies; /* prevent tx timeout */ netif_wake_queue(dev); /* Restore previous register address */ writeb(reg_save, db->io_addr); + db->in_timeout = 0; spin_unlock_irqrestore(&db->lock, flags); } @@ -915,7 +1050,7 @@ dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev) spin_unlock_irqrestore(&db->lock, flags); /* free this SKB */ - dev_kfree_skb(skb); + dev_consume_skb_any(skb); return NETDEV_TX_OK; } @@ -975,7 +1110,6 @@ dm9000_rx(struct net_device *dev) if (rxbyte & DM9000_PKT_ERR) { dev_warn(db->dev, "status check fail: %d\n", rxbyte); iow(db, DM9000_RCR, 0x00); /* Stop Device */ - iow(db, DM9000_ISR, IMR_PAR); /* Stop INT request */ return; } @@ -1075,9 +1209,7 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id) /* Save previous register address */ reg_save = readb(db->io_addr); - /* Disable all interrupts */ - iow(db, DM9000_IMR, IMR_PAR); - + dm9000_mask_interrupts(db); /* Got DM9000 interrupt status */ int_status = ior(db, DM9000_ISR); /* Got ISR */ iow(db, DM9000_ISR, int_status); /* Clear ISR status */ @@ -1100,9 +1232,7 @@ static irqreturn_t dm9000_interrupt(int irq, void *dev_id) } } - /* Re-enable interrupt mask */ - iow(db, DM9000_IMR, db->imr_all); - + dm9000_unmask_interrupts(db); /* Restore previous register address */ writeb(reg_save, db->io_addr); @@ -1133,12 +1263,11 @@ static irqreturn_t dm9000_wol_interrupt(int irq, void *dev_id) dev_info(db->dev, "wake by link status change\n"); if (wcr & WCR_SAMPLEST) dev_info(db->dev, "wake by sample packet\n"); - if (wcr & WCR_MAGICST ) + if (wcr & WCR_MAGICST) dev_info(db->dev, "wake by magic packet\n"); if (!(wcr & (WCR_LINKST | WCR_SAMPLEST | WCR_MAGICST))) dev_err(db->dev, "wake signalled with no reason? " "NSR=0x%02x, WSR=0x%02x\n", nsr, wcr); - } spin_unlock_irqrestore(&db->lock, flags); @@ -1175,6 +1304,9 @@ dm9000_open(struct net_device *dev) * may work, and tell the user that this is a problem */ if (irqflags == IRQF_TRIGGER_NONE) + irqflags = irq_get_trigger_type(dev->irq); + + if (irqflags == IRQF_TRIGGER_NONE) dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n"); irqflags |= IRQF_SHARED; @@ -1184,124 +1316,25 @@ dm9000_open(struct net_device *dev) mdelay(1); /* delay needs by DM9000B */ /* Initialize DM9000 board */ - dm9000_reset(db); dm9000_init_dm9000(dev); if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev)) return -EAGAIN; + /* Now that we have an interrupt handler hooked up we can unmask + * our interrupts + */ + dm9000_unmask_interrupts(db); /* Init driver variable */ db->dbug_cnt = 0; mii_check_media(&db->mii, netif_msg_link(db), 1); netif_start_queue(dev); - - dm9000_schedule_poll(db); - - return 0; -} - -/* - * Sleep, either by using msleep() or if we are suspending, then - * use mdelay() to sleep. - */ -static void dm9000_msleep(board_info_t *db, unsigned int ms) -{ - if (db->in_suspend) - mdelay(ms); - else - msleep(ms); -} - -/* - * Read a word from phyxcer - */ -static int -dm9000_phy_read(struct net_device *dev, int phy_reg_unused, int reg) -{ - board_info_t *db = netdev_priv(dev); - unsigned long flags; - unsigned int reg_save; - int ret; - - mutex_lock(&db->addr_lock); - - spin_lock_irqsave(&db->lock,flags); - - /* Save previous register address */ - reg_save = readb(db->io_addr); - - /* Fill the phyxcer register into REG_0C */ - iow(db, DM9000_EPAR, DM9000_PHY | reg); - - iow(db, DM9000_EPCR, EPCR_ERPRR | EPCR_EPOS); /* Issue phyxcer read command */ - - writeb(reg_save, db->io_addr); - spin_unlock_irqrestore(&db->lock,flags); - - dm9000_msleep(db, 1); /* Wait read complete */ - - spin_lock_irqsave(&db->lock,flags); - reg_save = readb(db->io_addr); - - iow(db, DM9000_EPCR, 0x0); /* Clear phyxcer read command */ - - /* The read data keeps on REG_0D & REG_0E */ - ret = (ior(db, DM9000_EPDRH) << 8) | ior(db, DM9000_EPDRL); - - /* restore the previous address */ - writeb(reg_save, db->io_addr); - spin_unlock_irqrestore(&db->lock,flags); - - mutex_unlock(&db->addr_lock); - - dm9000_dbg(db, 5, "phy_read[%02x] -> %04x\n", reg, ret); - return ret; -} - -/* - * Write a word to phyxcer - */ -static void -dm9000_phy_write(struct net_device *dev, - int phyaddr_unused, int reg, int value) -{ - board_info_t *db = netdev_priv(dev); - unsigned long flags; - unsigned long reg_save; - - dm9000_dbg(db, 5, "phy_write[%02x] = %04x\n", reg, value); - mutex_lock(&db->addr_lock); - - spin_lock_irqsave(&db->lock,flags); - /* Save previous register address */ - reg_save = readb(db->io_addr); - - /* Fill the phyxcer register into REG_0C */ - iow(db, DM9000_EPAR, DM9000_PHY | reg); - - /* Fill the written data into REG_0D & REG_0E */ - iow(db, DM9000_EPDRL, value); - iow(db, DM9000_EPDRH, value >> 8); - - iow(db, DM9000_EPCR, EPCR_EPOS | EPCR_ERPRW); /* Issue phyxcer write command */ - - writeb(reg_save, db->io_addr); - spin_unlock_irqrestore(&db->lock, flags); - - dm9000_msleep(db, 1); /* Wait write complete */ + /* Poll initial link status */ + schedule_delayed_work(&db->phy_poll, 1); - spin_lock_irqsave(&db->lock,flags); - reg_save = readb(db->io_addr); - - iow(db, DM9000_EPCR, 0x0); /* Clear phyxcer write command */ - - /* restore the previous address */ - writeb(reg_save, db->io_addr); - - spin_unlock_irqrestore(&db->lock, flags); - mutex_unlock(&db->addr_lock); + return 0; } static void @@ -1312,7 +1345,7 @@ dm9000_shutdown(struct net_device *dev) /* RESET device */ dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */ iow(db, DM9000_GPR, 0x01); /* Power-Down PHY */ - iow(db, DM9000_IMR, IMR_PAR); /* Disable all interrupt */ + dm9000_mask_interrupts(db); iow(db, DM9000_RCR, 0x00); /* Disable RX */ } @@ -1357,13 +1390,38 @@ static const struct net_device_ops dm9000_netdev_ops = { #endif }; +static struct dm9000_plat_data *dm9000_parse_dt(struct device *dev) +{ + struct dm9000_plat_data *pdata; + struct device_node *np = dev->of_node; + const void *mac_addr; + + if (!IS_ENABLED(CONFIG_OF) || !np) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + if (of_find_property(np, "davicom,ext-phy", NULL)) + pdata->flags |= DM9000_PLATF_EXT_PHY; + if (of_find_property(np, "davicom,no-eeprom", NULL)) + pdata->flags |= DM9000_PLATF_NO_EEPROM; + + mac_addr = of_get_mac_address(np); + if (mac_addr) + memcpy(pdata->dev_addr, mac_addr, sizeof(pdata->dev_addr)); + + return pdata; +} + /* * Search DM9000 board, allocate space and register it */ static int dm9000_probe(struct platform_device *pdev) { - struct dm9000_plat_data *pdata = pdev->dev.platform_data; + struct dm9000_plat_data *pdata = dev_get_platdata(&pdev->dev); struct board_info *db; /* Point a board information structure */ struct net_device *ndev; const unsigned char *mac_src; @@ -1372,6 +1430,12 @@ dm9000_probe(struct platform_device *pdev) int i; u32 id_val; + if (!pdata) { + pdata = dm9000_parse_dt(&pdev->dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + /* Init network device */ ndev = alloc_etherdev(sizeof(struct board_info)); if (!ndev) @@ -1571,12 +1635,12 @@ dm9000_probe(struct platform_device *pdev) if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) { mac_src = "platform data"; - memcpy(ndev->dev_addr, pdata->dev_addr, 6); + memcpy(ndev->dev_addr, pdata->dev_addr, ETH_ALEN); } if (!is_valid_ether_addr(ndev->dev_addr)) { /* try reading from mac */ - + mac_src = "chip"; for (i = 0; i < 6; i++) ndev->dev_addr[i] = ior(db, i+DM9000_PAR); @@ -1645,8 +1709,8 @@ dm9000_drv_resume(struct device *dev) /* reset if we were not in wake mode to ensure if * the device was powered off it is in a known state */ if (!db->wake_state) { - dm9000_reset(db); dm9000_init_dm9000(ndev); + dm9000_unmask_interrupts(db); } netif_device_attach(ndev); @@ -1667,8 +1731,6 @@ dm9000_drv_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - unregister_netdev(ndev); dm9000_release_board(pdev, netdev_priv(ndev)); free_netdev(ndev); /* free device structure */ @@ -1677,32 +1739,26 @@ dm9000_drv_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id dm9000_of_matches[] = { + { .compatible = "davicom,dm9000", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dm9000_of_matches); +#endif + static struct platform_driver dm9000_driver = { .driver = { .name = "dm9000", .owner = THIS_MODULE, .pm = &dm9000_drv_pm_ops, + .of_match_table = of_match_ptr(dm9000_of_matches), }, .probe = dm9000_probe, .remove = dm9000_drv_remove, }; -static int __init -dm9000_init(void) -{ - printk(KERN_INFO "%s Ethernet Driver, V%s\n", CARDNAME, DRV_VERSION); - - return platform_driver_register(&dm9000_driver); -} - -static void __exit -dm9000_cleanup(void) -{ - platform_driver_unregister(&dm9000_driver); -} - -module_init(dm9000_init); -module_exit(dm9000_cleanup); +module_platform_driver(dm9000_driver); MODULE_AUTHOR("Sascha Hauer, Ben Dooks"); MODULE_DESCRIPTION("Davicom DM9000 network driver"); |
