]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
net/sonic: Fix CAM initialization
authorFinn Thain <fthain@telegraphics.com.au>
Wed, 22 Jan 2020 22:07:26 +0000 (09:07 +1100)
committerKhalid Elmously <khalid.elmously@canonical.com>
Fri, 6 Mar 2020 07:13:20 +0000 (02:13 -0500)
BugLink: https://bugs.launchpad.net/bugs/1864261
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>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
drivers/net/ethernet/natsemi/sonic.c

index e488d6026c35d38f98dd51935ca2d2d857c97b5c..15c2c6c133634b6e36f77fbf49f7e840dd743740 100644 (file)
@@ -626,6 +626,8 @@ static void sonic_multicast_list(struct net_device *dev)
                    (netdev_mc_count(dev) > 15)) {
                        rcr |= SONIC_RCR_AMC;
                } else {
+                       unsigned long flags;
+
                        if (sonic_debug > 2)
                                printk("sonic_multicast_list: mc_count %d\n",
                                       netdev_mc_count(dev));
@@ -640,9 +642,14 @@ static void sonic_multicast_list(struct net_device *dev)
                                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);
                }
        }
 
@@ -669,6 +676,9 @@ static int sonic_init(struct net_device *dev)
        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
@@ -778,16 +788,7 @@ static int sonic_init(struct net_device *dev)
         * load the CAM
         */
        SONIC_WRITE(SONIC_CMD, SONIC_CR_LCAM);
-
-       i = 0;
-       while (i++ < 100) {
-               if (SONIC_READ(SONIC_ISR) & SONIC_INT_LCD)
-                       break;
-       }
-       if (sonic_debug > 2) {
-               printk("sonic_init: CMD=%x, ISR=%x\n, i=%d",
-                      SONIC_READ(SONIC_CMD), SONIC_READ(SONIC_ISR), i);
-       }
+       sonic_quiesce(dev, SONIC_CR_LCAM);
 
        /*
         * enable receiver, disable loopback