From 9a55d82883b5379493daac1f79242a1cb2d493c1 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 24 Feb 2006 18:04:12 -0800 Subject: [PATCH] skge bug fixes from shemminger --- queue/series | 4 + queue/skge-fix-napi-irq-race.patch | 63 +++++++++++ queue/skge-fix-smp-race.patch | 106 ++++++++++++++++++ .../skge-genesis-phy-initialization-fix.patch | 96 ++++++++++++++++ queue/skge-speed-setting.patch | 53 +++++++++ 5 files changed, 322 insertions(+) create mode 100644 queue/skge-fix-napi-irq-race.patch create mode 100644 queue/skge-fix-smp-race.patch create mode 100644 queue/skge-genesis-phy-initialization-fix.patch create mode 100644 queue/skge-speed-setting.patch diff --git a/queue/series b/queue/series index 23d0a4a1c27..5726953723a 100644 --- a/queue/series +++ b/queue/series @@ -26,3 +26,7 @@ dm-missing-bdput-thaw_bdev-at-removal.patch dm-free-minor-after-unlink-gendisk.patch ramfs-update-dir-mtime-and-ctime.patch gbefb-ip32-gbefb-depth-change-fix.patch +skge-speed-setting.patch +skge-fix-napi-irq-race.patch +skge-genesis-phy-initialization-fix.patch +skge-fix-smp-race.patch diff --git a/queue/skge-fix-napi-irq-race.patch b/queue/skge-fix-napi-irq-race.patch new file mode 100644 index 00000000000..a06f464c607 --- /dev/null +++ b/queue/skge-fix-napi-irq-race.patch @@ -0,0 +1,63 @@ +From stable-bounces@linux.kernel.org Wed Feb 22 13:50:00 2006 +Date: Wed, 22 Feb 2006 13:52:33 -0800 +From: Stephen Hemminger +To: stable@kernel.org +Cc: +Subject: [PATCH] skge: fix NAPI/irq race + +Fix a race in the receive NAPI, irq handling. The interrupt clear and the +start need to be separated. Otherwise there is a window between the last +frame received and the NAPI done level handling. + +Signed-off-by: Stephen Hemminger +Signed-off-by: Chris Wright +--- + + drivers/net/skge.c | 17 +++++------------ + 1 files changed, 5 insertions(+), 12 deletions(-) + +--- linux-2.6.15.4.orig/drivers/net/skge.c ++++ linux-2.6.15.4/drivers/net/skge.c +@@ -2675,8 +2675,7 @@ static int skge_poll(struct net_device * + + /* restart receiver */ + wmb(); +- skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR), +- CSR_START | CSR_IRQ_CL_F); ++ skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR), CSR_START); + + *budget -= work_done; + dev->quota -= work_done; +@@ -2853,14 +2852,6 @@ static void skge_extirq(unsigned long da + local_irq_enable(); + } + +-static inline void skge_wakeup(struct net_device *dev) +-{ +- struct skge_port *skge = netdev_priv(dev); +- +- prefetch(skge->rx_ring.to_clean); +- netif_rx_schedule(dev); +-} +- + static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs) + { + struct skge_hw *hw = dev_id; +@@ -2871,13 +2862,15 @@ static irqreturn_t skge_intr(int irq, vo + + status &= hw->intr_mask; + if (status & IS_R1_F) { ++ skge_write8(hw, Q_ADDR(Q_R1, Q_CSR), CSR_IRQ_CL_F); + hw->intr_mask &= ~IS_R1_F; +- skge_wakeup(hw->dev[0]); ++ netif_rx_schedule(hw->dev[0]); + } + + if (status & IS_R2_F) { ++ skge_write8(hw, Q_ADDR(Q_R2, Q_CSR), CSR_IRQ_CL_F); + hw->intr_mask &= ~IS_R2_F; +- skge_wakeup(hw->dev[1]); ++ netif_rx_schedule(hw->dev[1]); + } + + if (status & IS_XA1_F) diff --git a/queue/skge-fix-smp-race.patch b/queue/skge-fix-smp-race.patch new file mode 100644 index 00000000000..a5675646aa9 --- /dev/null +++ b/queue/skge-fix-smp-race.patch @@ -0,0 +1,106 @@ +From stable-bounces@linux.kernel.org Wed Feb 22 13:50:02 2006 +Date: Wed, 22 Feb 2006 13:52:35 -0800 +From: Stephen Hemminger +To: stable@kernel.org +Cc: +Subject: [PATCH] skge: fix SMP race + +If skge is attached to a bad cable, that goes up/down. +It exposes an SMP race with the management of IRQ mask + +Signed-off-by: Stephen Hemminger +Signed-off-by: Chris Wright +--- + + drivers/net/skge.c | 21 ++++++++++++++------- + drivers/net/skge.h | 1 + + 2 files changed, 15 insertions(+), 7 deletions(-) + +--- linux-2.6.15.4.orig/drivers/net/skge.c ++++ linux-2.6.15.4/drivers/net/skge.c +@@ -2182,8 +2182,10 @@ static int skge_up(struct net_device *de + skge->tx_avail = skge->tx_ring.count - 1; + + /* Enable IRQ from port */ ++ spin_lock_irq(&hw->hw_lock); + hw->intr_mask |= portirqmask[port]; + skge_write32(hw, B0_IMSK, hw->intr_mask); ++ spin_unlock_irq(&hw->hw_lock); + + /* Initialize MAC */ + spin_lock_bh(&hw->phy_lock); +@@ -2241,8 +2243,10 @@ static int skge_down(struct net_device * + else + yukon_stop(skge); + ++ spin_lock_irq(&hw->hw_lock); + hw->intr_mask &= ~portirqmask[skge->port]; + skge_write32(hw, B0_IMSK, hw->intr_mask); ++ spin_unlock_irq(&hw->hw_lock); + + /* Stop transmitter */ + skge_write8(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_STOP); +@@ -2698,10 +2702,11 @@ static int skge_poll(struct net_device * + if (work_done >= to_do) + return 1; /* not done */ + +- netif_rx_complete(dev); +- hw->intr_mask |= portirqmask[skge->port]; +- skge_write32(hw, B0_IMSK, hw->intr_mask); +- skge_read32(hw, B0_IMSK); ++ spin_lock_irq(&hw->hw_lock); ++ __netif_rx_complete(dev); ++ hw->intr_mask |= portirqmask[skge->port]; ++ skge_write32(hw, B0_IMSK, hw->intr_mask); ++ spin_unlock_irq(&hw->hw_lock); + + return 0; + } +@@ -2861,10 +2866,10 @@ static void skge_extirq(unsigned long da + } + spin_unlock(&hw->phy_lock); + +- local_irq_disable(); ++ spin_lock_irq(&hw->hw_lock); + hw->intr_mask |= IS_EXT_REG; + skge_write32(hw, B0_IMSK, hw->intr_mask); +- local_irq_enable(); ++ spin_unlock_irq(&hw->hw_lock); + } + + static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs) +@@ -2875,7 +2880,7 @@ static irqreturn_t skge_intr(int irq, vo + if (status == 0 || status == ~0) /* hotplug or shared irq */ + return IRQ_NONE; + +- status &= hw->intr_mask; ++ spin_lock(&hw->hw_lock); + if (status & IS_R1_F) { + skge_write8(hw, Q_ADDR(Q_R1, Q_CSR), CSR_IRQ_CL_F); + hw->intr_mask &= ~IS_R1_F; +@@ -2927,6 +2932,7 @@ static irqreturn_t skge_intr(int irq, vo + } + + skge_write32(hw, B0_IMSK, hw->intr_mask); ++ spin_unlock(&hw->hw_lock); + + return IRQ_HANDLED; + } +@@ -3285,6 +3291,7 @@ static int __devinit skge_probe(struct p + + hw->pdev = pdev; + spin_lock_init(&hw->phy_lock); ++ spin_lock_init(&hw->hw_lock); + tasklet_init(&hw->ext_tasklet, skge_extirq, (unsigned long) hw); + + hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); +--- linux-2.6.15.4.orig/drivers/net/skge.h ++++ linux-2.6.15.4/drivers/net/skge.h +@@ -2473,6 +2473,7 @@ struct skge_hw { + + struct tasklet_struct ext_tasklet; + spinlock_t phy_lock; ++ spinlock_t hw_lock; + }; + + enum { diff --git a/queue/skge-genesis-phy-initialization-fix.patch b/queue/skge-genesis-phy-initialization-fix.patch new file mode 100644 index 00000000000..cb9b56662e7 --- /dev/null +++ b/queue/skge-genesis-phy-initialization-fix.patch @@ -0,0 +1,96 @@ +From stable-bounces@linux.kernel.org Wed Feb 22 13:50:00 2006 +Date: Wed, 22 Feb 2006 13:52:34 -0800 +From: Stephen Hemminger +To: stable@kernel.org +Cc: +Subject: [PATCH] skge: genesis phy initialization fix + +The SysKonnect Genesis based board would fail on initialization +with phy_read errors caused by not waiting for last phy write. + +Signed-off-by: Stephen Hemminger +Signed-off-by: Chris Wright +--- + + drivers/net/skge.c | 37 ++++++++++++++++++++++++++----------- + 1 files changed, 26 insertions(+), 11 deletions(-) + +--- linux-2.6.15.4.orig/drivers/net/skge.c ++++ linux-2.6.15.4/drivers/net/skge.c +@@ -880,13 +880,12 @@ static int __xm_phy_read(struct skge_hw + int i; + + xm_write16(hw, port, XM_PHY_ADDR, reg | hw->phy_addr); +- xm_read16(hw, port, XM_PHY_DATA); ++ *val = xm_read16(hw, port, XM_PHY_DATA); + +- /* Need to wait for external PHY */ + for (i = 0; i < PHY_RETRIES; i++) { +- udelay(1); + if (xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_RDY) + goto ready; ++ udelay(1); + } + + return -ETIMEDOUT; +@@ -919,7 +918,12 @@ static int xm_phy_write(struct skge_hw * + + ready: + xm_write16(hw, port, XM_PHY_DATA, val); +- return 0; ++ for (i = 0; i < PHY_RETRIES; i++) { ++ if (!(xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_BUSY)) ++ return 0; ++ udelay(1); ++ } ++ return -ETIMEDOUT; + } + + static void genesis_init(struct skge_hw *hw) +@@ -1169,13 +1173,17 @@ static void genesis_mac_init(struct skge + u32 r; + const u8 zero[6] = { 0 }; + +- /* Clear MIB counters */ +- xm_write16(hw, port, XM_STAT_CMD, +- XM_SC_CLR_RXC | XM_SC_CLR_TXC); +- /* Clear two times according to Errata #3 */ +- xm_write16(hw, port, XM_STAT_CMD, +- XM_SC_CLR_RXC | XM_SC_CLR_TXC); ++ for (i = 0; i < 10; i++) { ++ skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), ++ MFF_SET_MAC_RST); ++ if (skge_read16(hw, SK_REG(port, TX_MFF_CTRL1)) & MFF_SET_MAC_RST) ++ goto reset_ok; ++ udelay(1); ++ } + ++ printk(KERN_WARNING PFX "%s: genesis reset failed\n", dev->name); ++ ++ reset_ok: + /* Unreset the XMAC. */ + skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_CLR_MAC_RST); + +@@ -1192,7 +1200,7 @@ static void genesis_mac_init(struct skge + r |= GP_DIR_2|GP_IO_2; + + skge_write32(hw, B2_GP_IO, r); +- skge_read32(hw, B2_GP_IO); ++ + + /* Enable GMII interface */ + xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD); +@@ -1206,6 +1214,13 @@ static void genesis_mac_init(struct skge + for (i = 1; i < 16; i++) + xm_outaddr(hw, port, XM_EXM(i), zero); + ++ /* Clear MIB counters */ ++ xm_write16(hw, port, XM_STAT_CMD, ++ XM_SC_CLR_RXC | XM_SC_CLR_TXC); ++ /* Clear two times according to Errata #3 */ ++ xm_write16(hw, port, XM_STAT_CMD, ++ XM_SC_CLR_RXC | XM_SC_CLR_TXC); ++ + /* configure Rx High Water Mark (XM_RX_HI_WM) */ + xm_write16(hw, port, XM_RX_HI_WM, 1450); + diff --git a/queue/skge-speed-setting.patch b/queue/skge-speed-setting.patch new file mode 100644 index 00000000000..c27cd499cb3 --- /dev/null +++ b/queue/skge-speed-setting.patch @@ -0,0 +1,53 @@ +From stable-bounces@linux.kernel.org Wed Feb 22 13:50:07 2006 +Date: Wed, 22 Feb 2006 13:52:32 -0800 +From: Stephen Hemminger +To: stable@kernel.org +Cc: +Subject: [PATCH] skge: speed setting + +This is a clone of John Linville's fixed for speed setting on sky2 driver. +The skge driver has the same code (and bug). It would not allow manually forcing +100 and 10 mbit. + +Signed-off-by: Stephen Hemminger +Signed-off-by: Chris Wright +--- + + drivers/net/skge.c | 10 +++++++++- + 1 files changed, 9 insertions(+), 1 deletion(-) + +--- linux-2.6.15.4.orig/drivers/net/skge.c ++++ linux-2.6.15.4/drivers/net/skge.c +@@ -1698,6 +1698,7 @@ static void yukon_mac_init(struct skge_h + skge_write32(hw, SK_REG(port, GPHY_CTRL), reg | GPC_RST_SET); + skge_write32(hw, SK_REG(port, GPHY_CTRL), reg | GPC_RST_CLR); + skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON | GMC_RST_CLR); ++ + if (skge->autoneg == AUTONEG_DISABLE) { + reg = GM_GPCR_AU_ALL_DIS; + gma_write16(hw, port, GM_GP_CTRL, +@@ -1705,16 +1706,23 @@ static void yukon_mac_init(struct skge_h + + switch (skge->speed) { + case SPEED_1000: ++ reg &= ~GM_GPCR_SPEED_100; + reg |= GM_GPCR_SPEED_1000; +- /* fallthru */ ++ break; + case SPEED_100: ++ reg &= ~GM_GPCR_SPEED_1000; + reg |= GM_GPCR_SPEED_100; ++ break; ++ case SPEED_10: ++ reg &= ~(GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100); ++ break; + } + + if (skge->duplex == DUPLEX_FULL) + reg |= GM_GPCR_DUP_FULL; + } else + reg = GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100 | GM_GPCR_DUP_FULL; ++ + switch (skge->flow_control) { + case FLOW_MODE_NONE: + skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); -- 2.47.3