]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
skge bug fixes from shemminger
authorChris Wright <chrisw@sous-sol.org>
Sat, 25 Feb 2006 02:04:12 +0000 (18:04 -0800)
committerChris Wright <chrisw@sous-sol.org>
Sat, 25 Feb 2006 02:04:12 +0000 (18:04 -0800)
queue/series
queue/skge-fix-napi-irq-race.patch [new file with mode: 0644]
queue/skge-fix-smp-race.patch [new file with mode: 0644]
queue/skge-genesis-phy-initialization-fix.patch [new file with mode: 0644]
queue/skge-speed-setting.patch [new file with mode: 0644]

index 23d0a4a1c27540c93462ea275a412a937efcf6c5..5726953723a9743c238012a0b5396c4995194d67 100644 (file)
@@ -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 (file)
index 0000000..a06f464
--- /dev/null
@@ -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 <shemminger@osdl.org>
+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 <shemminger@osdl.org>
+Signed-off-by: Chris Wright <chrisw@sous-sol.org>
+---
+
+ 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 (file)
index 0000000..a567564
--- /dev/null
@@ -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 <shemminger@osdl.org>
+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 <shemminger@osdl.org>
+Signed-off-by: Chris Wright <chrisw@sous-sol.org>
+---
+
+ 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 (file)
index 0000000..cb9b566
--- /dev/null
@@ -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 <shemminger@osdl.org>
+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 <shemminger@osdl.org>
+Signed-off-by: Chris Wright <chrisw@sous-sol.org>
+---
+
+ 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 (file)
index 0000000..c27cd49
--- /dev/null
@@ -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 <shemminger@osdl.org>
+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 <shemminger@osdl.org>
+Signed-off-by: Chris Wright <chrisw@sous-sol.org>
+---
+
+ 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);