| From: Joseph CHANG <josright123@gmail.com> |
| Date: Thu, 28 Mar 2013 23:13:42 +0000 |
| Subject: DM9000B: driver initialization upgrade |
| |
| [ Upstream commit 6741f40d198c6a5feb23653a1efd4ca47f93d83d ] |
| |
| Fix bug for DM9000 revision B which contain a DSP PHY |
| |
| DM9000B use DSP PHY instead previouse DM9000 revisions' analog PHY, |
| So need extra change in initialization, For |
| explicity PHY Reset and PHY init parameter, and |
| first DM9000_NCR reset need NCR_MAC_LBK bit by dm9000_probe(). |
| |
| Following DM9000_NCR reset cause by dm9000_open() clear the |
| NCR_MAC_LBK bit. |
| |
| Without this fix, Power-up FIFO pointers error happen around 2% |
| rate among Davicom's customers' boards. With this fix, All above |
| cases can be solved. |
| |
| Signed-off-by: Joseph CHANG <josright123@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/net/ethernet/davicom/dm9000.c | 214 +++++++++++++++++----------------- |
| drivers/net/ethernet/davicom/dm9000.h | 11 +- |
| 2 files changed, 120 insertions(+), 105 deletions(-) |
| |
| diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c |
| index 2a22f52..2f2e98b 100644 |
| --- a/drivers/net/ethernet/davicom/dm9000.c |
| +++ b/drivers/net/ethernet/davicom/dm9000.c |
| @@ -257,6 +257,107 @@ 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) |
| + 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); |
| + 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); |
| + mutex_unlock(&db->addr_lock); |
| +} |
| + |
| /* dm9000_set_io |
| * |
| * select the specified set of io routines to use with the |
| @@ -793,6 +894,9 @@ dm9000_init_dm9000(struct net_device *dev) |
| |
| iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */ |
| |
| + dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET */ |
| + dm9000_phy_write(dev, 0, MII_DM_DSPCR, DSPCR_INIT_PARAM); /* Init */ |
| + |
| ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0; |
| |
| /* if wol is needed, then always set NCR_WAKEEN otherwise we end |
| @@ -1199,109 +1303,6 @@ dm9000_open(struct net_device *dev) |
| 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 */ |
| - |
| - 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); |
| -} |
| - |
| static void |
| dm9000_shutdown(struct net_device *dev) |
| { |
| @@ -1502,7 +1503,12 @@ dm9000_probe(struct platform_device *pdev) |
| db->flags |= DM9000_PLATF_SIMPLE_PHY; |
| #endif |
| |
| - dm9000_reset(db); |
| + /* Fixing bug on dm9000_probe, takeover dm9000_reset(db), |
| + * Need 'NCR_MAC_LBK' bit to indeed stable our DM9000 fifo |
| + * while probe stage. |
| + */ |
| + |
| + iow(db, DM9000_NCR, NCR_MAC_LBK | NCR_RST); |
| |
| /* try multiple times, DM9000 sometimes gets the read wrong */ |
| for (i = 0; i < 8; i++) { |
| diff --git a/drivers/net/ethernet/davicom/dm9000.h b/drivers/net/ethernet/davicom/dm9000.h |
| index 55688bd..9ce058a 100644 |
| --- a/drivers/net/ethernet/davicom/dm9000.h |
| +++ b/drivers/net/ethernet/davicom/dm9000.h |
| @@ -69,7 +69,9 @@ |
| #define NCR_WAKEEN (1<<6) |
| #define NCR_FCOL (1<<4) |
| #define NCR_FDX (1<<3) |
| -#define NCR_LBK (3<<1) |
| + |
| +#define NCR_RESERVED (3<<1) |
| +#define NCR_MAC_LBK (1<<1) |
| #define NCR_RST (1<<0) |
| |
| #define NSR_SPEED (1<<7) |
| @@ -167,5 +169,12 @@ |
| #define ISR_LNKCHNG (1<<5) |
| #define ISR_UNDERRUN (1<<4) |
| |
| +/* Davicom MII registers. |
| + */ |
| + |
| +#define MII_DM_DSPCR 0x1b /* DSP Control Register */ |
| + |
| +#define DSPCR_INIT_PARAM 0xE100 /* DSP init parameter */ |
| + |
| #endif /* _DM9000X_H_ */ |
| |