[PATCH] bcm43xx: workaround init_board vs. IRQ race
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / bcm43xx / bcm43xx_main.c
index 085d7857fe31aff4abd20c8a05563ce41a852798..673ab11d92abc658bd758214ffd6df6500b93540 100644 (file)
@@ -1885,6 +1885,15 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_re
 
        spin_lock(&bcm->irq_lock);
 
+       /* Only accept IRQs, if we are initialized properly.
+        * This avoids an RX race while initializing.
+        * We should probably not enable IRQs before we are initialized
+        * completely, but some careful work is needed to fix this. I think it
+        * is best to stay with this cheap workaround for now... .
+        */
+       if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED))
+               goto out;
+
        reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
        if (reason == 0xffffffff) {
                /* irq not for us (shared irq) */
@@ -1906,19 +1915,11 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_re
 
        bcm43xx_interrupt_ack(bcm, reason);
 
-       /* Only accept IRQs, if we are initialized properly.
-        * This avoids an RX race while initializing.
-        * We should probably not enable IRQs before we are initialized
-        * completely, but some careful work is needed to fix this. I think it
-        * is best to stay with this cheap workaround for now... .
-        */
-       if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) {
-               /* disable all IRQs. They are enabled again in the bottom half. */
-               bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
-               /* save the reason code and call our bottom half. */
-               bcm->irq_reason = reason;
-               tasklet_schedule(&bcm->isr_tasklet);
-       }
+       /* disable all IRQs. They are enabled again in the bottom half. */
+       bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+       /* save the reason code and call our bottom half. */
+       bcm->irq_reason = reason;
+       tasklet_schedule(&bcm->isr_tasklet);
 
 out:
        mmiowb();
@@ -2175,7 +2176,7 @@ static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm)
        }
 #endif
        res = request_irq(bcm->irq, bcm43xx_interrupt_handler,
-                         SA_SHIRQ, KBUILD_MODNAME, bcm);
+                         IRQF_SHARED, KBUILD_MODNAME, bcm);
        if (res) {
                printk(KERN_ERR PFX "Cannot register IRQ%d\n", bcm->irq);
                return -ENODEV;
@@ -3237,6 +3238,39 @@ static void bcm43xx_security_init(struct bcm43xx_private *bcm)
        bcm43xx_clear_keys(bcm);
 }
 
+static int bcm43xx_rng_read(struct hwrng *rng, u32 *data)
+{
+       struct bcm43xx_private *bcm = (struct bcm43xx_private *)rng->priv;
+       unsigned long flags;
+
+       bcm43xx_lock_irqonly(bcm, flags);
+       *data = bcm43xx_read16(bcm, BCM43xx_MMIO_RNG);
+       bcm43xx_unlock_irqonly(bcm, flags);
+
+       return (sizeof(u16));
+}
+
+static void bcm43xx_rng_exit(struct bcm43xx_private *bcm)
+{
+       hwrng_unregister(&bcm->rng);
+}
+
+static int bcm43xx_rng_init(struct bcm43xx_private *bcm)
+{
+       int err;
+
+       snprintf(bcm->rng_name, ARRAY_SIZE(bcm->rng_name),
+                "%s_%s", KBUILD_MODNAME, bcm->net_dev->name);
+       bcm->rng.name = bcm->rng_name;
+       bcm->rng.data_read = bcm43xx_rng_read;
+       bcm->rng.priv = (unsigned long)bcm;
+       err = hwrng_register(&bcm->rng);
+       if (err)
+               printk(KERN_ERR PFX "RNG init failed (%d)\n", err);
+
+       return err;
+}
+
 /* This is the opposite of bcm43xx_init_board() */
 static void bcm43xx_free_board(struct bcm43xx_private *bcm)
 {
@@ -3248,6 +3282,7 @@ static void bcm43xx_free_board(struct bcm43xx_private *bcm)
 
        bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN);
 
+       bcm43xx_rng_exit(bcm);
        for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {
                if (!bcm->core_80211[i].available)
                        continue;
@@ -3325,6 +3360,9 @@ static int bcm43xx_init_board(struct bcm43xx_private *bcm)
                bcm43xx_switch_core(bcm, &bcm->core_80211[0]);
                bcm43xx_mac_enable(bcm);
        }
+       err = bcm43xx_rng_init(bcm);
+       if (err)
+               goto err_80211_unwind;
        bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC);
        bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_SELF, (u8 *)(bcm->net_dev->dev_addr));
        dprintk(KERN_INFO PFX "80211 cores initialized\n");