| From 772f66421d5aa0b9f256056f513bbc38ac132271 Mon Sep 17 00:00:00 2001 |
| From: Finn Thain <fthain@telegraphics.com.au> |
| Date: Thu, 23 Jan 2020 09:07:26 +1100 |
| Subject: net/sonic: Fix CAM initialization |
| |
| From: Finn Thain <fthain@telegraphics.com.au> |
| |
| commit 772f66421d5aa0b9f256056f513bbc38ac132271 upstream. |
| |
| Section 4.3.1 of the datasheet says, |
| |
| This bit [TXP] must not be set if a Load CAM operation is in |
| progress (LCAM is set). The SONIC will lock up if both bits are |
| set simultaneously. |
| |
| Testing has shown that the driver sometimes attempts to set LCAM |
| while TXP is set. Avoid this by waiting for command completion |
| before and after giving the LCAM command. |
| |
| After issuing the Load CAM command, poll for !SONIC_CR_LCAM rather than |
| SONIC_INT_LCD, because the SONIC_CR_TXP bit can't be used until |
| !SONIC_CR_LCAM. |
| |
| When in reset mode, take the opportunity to reset the CAM Enable |
| register. |
| |
| Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") |
| Tested-by: Stan Johnson <userm57@yahoo.com> |
| Signed-off-by: Finn Thain <fthain@telegraphics.com.au> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/net/ethernet/natsemi/sonic.c | 21 ++++++++++++--------- |
| 1 file changed, 12 insertions(+), 9 deletions(-) |
| |
| --- a/drivers/net/ethernet/natsemi/sonic.c |
| +++ b/drivers/net/ethernet/natsemi/sonic.c |
| @@ -634,6 +634,8 @@ static void sonic_multicast_list(struct |
| (netdev_mc_count(dev) > 15)) { |
| rcr |= SONIC_RCR_AMC; |
| } else { |
| + unsigned long flags; |
| + |
| netif_dbg(lp, ifup, dev, "%s: mc_count %d\n", __func__, |
| netdev_mc_count(dev)); |
| sonic_set_cam_enable(dev, 1); /* always enable our own address */ |
| @@ -647,9 +649,14 @@ static void sonic_multicast_list(struct |
| i++; |
| } |
| SONIC_WRITE(SONIC_CDC, 16); |
| - /* issue Load CAM command */ |
| SONIC_WRITE(SONIC_CDP, lp->cda_laddr & 0xffff); |
| + |
| + /* LCAM and TXP commands can't be used simultaneously */ |
| + spin_lock_irqsave(&lp->lock, flags); |
| + sonic_quiesce(dev, SONIC_CR_TXP); |
| SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM); |
| + sonic_quiesce(dev, SONIC_CR_LCAM); |
| + spin_unlock_irqrestore(&lp->lock, flags); |
| } |
| } |
| |
| @@ -675,6 +682,9 @@ static int sonic_init(struct net_device |
| SONIC_WRITE(SONIC_ISR, 0x7fff); |
| SONIC_WRITE(SONIC_CMD, SONIC_CR_RST); |
| |
| + /* While in reset mode, clear CAM Enable register */ |
| + SONIC_WRITE(SONIC_CE, 0); |
| + |
| /* |
| * clear software reset flag, disable receiver, clear and |
| * enable interrupts, then completely initialize the SONIC |
| @@ -785,14 +795,7 @@ static int sonic_init(struct net_device |
| * load the CAM |
| */ |
| SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM); |
| - |
| - i = 0; |
| - while (i++ < 100) { |
| - if (SONIC_READ(SONIC_ISR) & SONIC_INT_LCD) |
| - break; |
| - } |
| - netif_dbg(lp, ifup, dev, "%s: CMD=%x, ISR=%x, i=%d\n", __func__, |
| - SONIC_READ(SONIC_CMD), SONIC_READ(SONIC_ISR), i); |
| + sonic_quiesce(dev, SONIC_CR_LCAM); |
| |
| /* |
| * enable receiver, disable loopback |