WorkStruct: make allyesconfig
[safe/jmp/linux-2.6] / drivers / net / wireless / bcm43xx / bcm43xx_main.c
index 468cd8a..728a9b7 100644 (file)
@@ -519,6 +519,7 @@ static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm)
                return -EBUSY;
        }
        bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+       bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK); /* flush */
        spin_unlock_irqrestore(&bcm->irq_lock, flags);
        bcm43xx_synchronize_irq(bcm);
 
@@ -745,7 +746,7 @@ int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom)
        if (err)
                goto err_ctlreg;
        spromctl |= 0x10; /* SPROM WRITE enable. */
-       bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl);
+       err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl);
        if (err)
                goto err_ctlreg;
        /* We must burn lots of CPU cycles here, but that does not
@@ -767,7 +768,7 @@ int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom)
                mdelay(20);
        }
        spromctl &= ~0x10; /* SPROM WRITE enable. */
-       bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl);
+       err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl);
        if (err)
                goto err_ctlreg;
        mdelay(500);
@@ -1462,6 +1463,23 @@ static void handle_irq_transmit_status(struct bcm43xx_private *bcm)
        }
 }
 
+static void drain_txstatus_queue(struct bcm43xx_private *bcm)
+{
+       u32 dummy;
+
+       if (bcm->current_core->rev < 5)
+               return;
+       /* Read all entries from the microcode TXstatus FIFO
+        * and throw them away.
+        */
+       while (1) {
+               dummy = bcm43xx_read32(bcm, BCM43xx_MMIO_XMITSTAT_0);
+               if (!dummy)
+                       break;
+               dummy = bcm43xx_read32(bcm, BCM43xx_MMIO_XMITSTAT_1);
+       }
+}
+
 static void bcm43xx_generate_noise_sample(struct bcm43xx_private *bcm)
 {
        bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x408, 0x7F7F);
@@ -1545,17 +1563,7 @@ static void handle_irq_noise(struct bcm43xx_private *bcm)
                else
                        average -= 48;
 
-/* FIXME: This is wrong, but people want fancy stats. well... */
-bcm->stats.noise = average;
-               if (average > -65)
-                       bcm->stats.link_quality = 0;
-               else if (average > -75)
-                       bcm->stats.link_quality = 1;
-               else if (average > -85)
-                       bcm->stats.link_quality = 2;
-               else
-                       bcm->stats.link_quality = 3;
-//             dprintk(KERN_INFO PFX "Link Quality: %u (avg was %d)\n", bcm->stats.link_quality, average);
+               bcm->stats.noise = average;
 drop_calculation:
                bcm->noisecalc.calculation_running = 0;
                return;
@@ -1843,7 +1851,7 @@ static void bcm43xx_interrupt_ack(struct bcm43xx_private *bcm, u32 reason)
 }
 
 /* Interrupt handler top-half */
-static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id)
 {
        irqreturn_t ret = IRQ_HANDLED;
        struct bcm43xx_private *bcm = dev_id;
@@ -2393,6 +2401,34 @@ static int bcm43xx_chip_init(struct bcm43xx_private *bcm)
        }
        bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */
 
+       value16 = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
+                                    BCM43xx_UCODE_REVISION);
+
+       dprintk(KERN_INFO PFX "Microcode rev 0x%x, pl 0x%x "
+               "(20%.2i-%.2i-%.2i  %.2i:%.2i:%.2i)\n", value16,
+               bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
+                                  BCM43xx_UCODE_PATCHLEVEL),
+               (bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
+                                   BCM43xx_UCODE_DATE) >> 12) & 0xf,
+               (bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
+                                   BCM43xx_UCODE_DATE) >> 8) & 0xf,
+               bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
+                                  BCM43xx_UCODE_DATE) & 0xff,
+               (bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
+                                  BCM43xx_UCODE_TIME) >> 11) & 0x1f,
+               (bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
+                                  BCM43xx_UCODE_TIME) >> 5) & 0x3f,
+               bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
+                                  BCM43xx_UCODE_TIME) & 0x1f);
+
+       if ( value16 > 0x128 ) {
+               printk(KERN_ERR PFX
+                       "Firmware: no support for microcode extracted "
+                       "from version 4.x binary drivers.\n");
+               err = -EOPNOTSUPP;
+               goto err_release_fw;
+       }
+
        err = bcm43xx_gpio_init(bcm);
        if (err)
                goto err_release_fw;
@@ -2906,10 +2942,13 @@ static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm,
                bcm43xx_write16(bcm, 0x043C, 0x000C);
 
        if (active_wlcore) {
-               if (bcm43xx_using_pio(bcm))
+               if (bcm43xx_using_pio(bcm)) {
                        err = bcm43xx_pio_init(bcm);
-               else
+               } else {
                        err = bcm43xx_dma_init(bcm);
+                       if (err == -ENOSYS)
+                               err = bcm43xx_pio_init(bcm);
+               }
                if (err)
                        goto err_chip_cleanup;
        }
@@ -3138,33 +3177,45 @@ static int estimate_periodic_work_badness(unsigned int state)
        return badness;
 }
 
-static void bcm43xx_periodic_work_handler(void *d)
+static void bcm43xx_periodic_work_handler(struct work_struct *work)
 {
-       struct bcm43xx_private *bcm = d;
+       struct bcm43xx_private *bcm =
+               container_of(work, struct bcm43xx_private, periodic_work.work);
+       struct net_device *net_dev = bcm->net_dev;
        unsigned long flags;
        u32 savedirqs = 0;
        int badness;
+       unsigned long orig_trans_start = 0;
 
+       mutex_lock(&bcm->mutex);
        badness = estimate_periodic_work_badness(bcm->periodic_state);
        if (badness > BADNESS_LIMIT) {
                /* Periodic work will take a long time, so we want it to
                 * be preemtible.
                 */
-               netif_stop_queue(bcm->net_dev);
-               synchronize_net();
+
+               netif_tx_lock_bh(net_dev);
+               /* We must fake a started transmission here, as we are going to
+                * disable TX. If we wouldn't fake a TX, it would be possible to
+                * trigger the netdev watchdog, if the last real TX is already
+                * some time on the past (slightly less than 5secs)
+                */
+               orig_trans_start = net_dev->trans_start;
+               net_dev->trans_start = jiffies;
+               netif_stop_queue(net_dev);
+               netif_tx_unlock_bh(net_dev);
+
                spin_lock_irqsave(&bcm->irq_lock, flags);
                bcm43xx_mac_suspend(bcm);
                if (bcm43xx_using_pio(bcm))
                        bcm43xx_pio_freeze_txqueues(bcm);
                savedirqs = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
                spin_unlock_irqrestore(&bcm->irq_lock, flags);
-               mutex_lock(&bcm->mutex);
                bcm43xx_synchronize_irq(bcm);
        } else {
                /* Periodic work should take short time, so we want low
                 * locking overhead.
                 */
-               mutex_lock(&bcm->mutex);
                spin_lock_irqsave(&bcm->irq_lock, flags);
        }
 
@@ -3172,32 +3223,31 @@ static void bcm43xx_periodic_work_handler(void *d)
 
        if (badness > BADNESS_LIMIT) {
                spin_lock_irqsave(&bcm->irq_lock, flags);
-               if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) {
-                       tasklet_enable(&bcm->isr_tasklet);
-                       bcm43xx_interrupt_enable(bcm, savedirqs);
-                       if (bcm43xx_using_pio(bcm))
-                               bcm43xx_pio_thaw_txqueues(bcm);
-                       bcm43xx_mac_enable(bcm);
-               }
+               tasklet_enable(&bcm->isr_tasklet);
+               bcm43xx_interrupt_enable(bcm, savedirqs);
+               if (bcm43xx_using_pio(bcm))
+                       bcm43xx_pio_thaw_txqueues(bcm);
+               bcm43xx_mac_enable(bcm);
                netif_wake_queue(bcm->net_dev);
+               net_dev->trans_start = orig_trans_start;
        }
        mmiowb();
        spin_unlock_irqrestore(&bcm->irq_lock, flags);
        mutex_unlock(&bcm->mutex);
 }
 
-static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)
+void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)
 {
        cancel_rearming_delayed_work(&bcm->periodic_work);
 }
 
-static void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm)
+void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm)
 {
-       struct work_struct *work = &(bcm->periodic_work);
+       struct delayed_work *work = &bcm->periodic_work;
 
        assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
-       INIT_WORK(work, bcm43xx_periodic_work_handler, bcm);
-       schedule_work(work);
+       INIT_DELAYED_WORK(work, bcm43xx_periodic_work_handler);
+       schedule_delayed_work(work, 0);
 }
 
 static void bcm43xx_security_init(struct bcm43xx_private *bcm)
@@ -3363,6 +3413,8 @@ static void prepare_priv_for_init(struct bcm43xx_private *bcm)
        memset(bcm->dma_reason, 0, sizeof(bcm->dma_reason));
        bcm->irq_savedstate = BCM43xx_IRQ_INITIAL;
 
+       bcm->mac_suspended = 1;
+
        /* Noise calculation context */
        memset(&bcm->noisecalc, 0, sizeof(bcm->noisecalc));
 
@@ -3498,6 +3550,7 @@ int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm,
        bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC);
        bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_SELF, (u8 *)(bcm->net_dev->dev_addr));
        bcm43xx_security_init(bcm);
+       drain_txstatus_queue(bcm);
        ieee80211softmac_start(bcm->net_dev);
 
        /* Let's go! Be careful after enabling the IRQs.
@@ -3537,17 +3590,16 @@ static int bcm43xx_init_board(struct bcm43xx_private *bcm)
        err = bcm43xx_select_wireless_core(bcm, -1);
        if (err)
                goto err_crystal_off;
-
-       bcm43xx_periodic_tasks_setup(bcm);
        err = bcm43xx_sysfs_register(bcm);
        if (err)
                goto err_wlshutdown;
        err = bcm43xx_rng_init(bcm);
        if (err)
                goto err_sysfs_unreg;
+       bcm43xx_periodic_tasks_setup(bcm);
 
        /*FIXME: This should be handled by softmac instead. */
-       schedule_work(&bcm->softmac->associnfo.work);
+       schedule_delayed_work(&bcm->softmac->associnfo.work, 0);
 
 out:
        mutex_unlock(&(bcm)->mutex);
@@ -3946,7 +3998,7 @@ static void bcm43xx_net_poll_controller(struct net_device *net_dev)
 
        local_irq_save(flags);
        if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)
-               bcm43xx_interrupt_handler(bcm->irq, bcm, NULL);
+               bcm43xx_interrupt_handler(bcm->irq, bcm);
        local_irq_restore(flags);
 }
 #endif /* CONFIG_NET_POLL_CONTROLLER */
@@ -3967,6 +4019,7 @@ static int bcm43xx_net_stop(struct net_device *net_dev)
        err = bcm43xx_disable_interrupts_sync(bcm);
        assert(!err);
        bcm43xx_free_board(bcm);
+       flush_scheduled_work();
 
        return 0;
 }
@@ -3975,8 +4028,6 @@ static int bcm43xx_init_private(struct bcm43xx_private *bcm,
                                struct net_device *net_dev,
                                struct pci_dev *pci_dev)
 {
-       int err;
-
        bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT);
        bcm->ieee = netdev_priv(net_dev);
        bcm->softmac = ieee80211_priv(net_dev);
@@ -3994,22 +4045,8 @@ static int bcm43xx_init_private(struct bcm43xx_private *bcm,
                     (void (*)(unsigned long))bcm43xx_interrupt_tasklet,
                     (unsigned long)bcm);
        tasklet_disable_nosync(&bcm->isr_tasklet);
-       if (modparam_pio) {
+       if (modparam_pio)
                bcm->__using_pio = 1;
-       } else {
-               err = pci_set_dma_mask(pci_dev, DMA_30BIT_MASK);
-               err |= pci_set_consistent_dma_mask(pci_dev, DMA_30BIT_MASK);
-               if (err) {
-#ifdef CONFIG_BCM43XX_PIO
-                       printk(KERN_WARNING PFX "DMA not supported. Falling back to PIO.\n");
-                       bcm->__using_pio = 1;
-#else
-                       printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. "
-                                           "Recompile the driver with PIO support, please.\n");
-                       return -ENODEV;
-#endif /* CONFIG_BCM43XX_PIO */
-               }
-       }
        bcm->rts_threshold = BCM43xx_DEFAULT_RTS_THRESHOLD;
 
        /* default to sw encryption for now */
@@ -4113,15 +4150,21 @@ static void __devexit bcm43xx_remove_one(struct pci_dev *pdev)
 /* Hard-reset the chip. Do not call this directly.
  * Use bcm43xx_controller_restart()
  */
-static void bcm43xx_chip_reset(void *_bcm)
+static void bcm43xx_chip_reset(struct work_struct *work)
 {
-       struct bcm43xx_private *bcm = _bcm;
+       struct bcm43xx_private *bcm =
+               container_of(work, struct bcm43xx_private, restart_work);
        struct bcm43xx_phyinfo *phy;
-       int err;
+       int err = -ENODEV;
 
        mutex_lock(&(bcm)->mutex);
-       phy = bcm43xx_current_phy(bcm);
-       err = bcm43xx_select_wireless_core(bcm, phy->type);
+       if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) {
+               bcm43xx_periodic_tasks_delete(bcm);
+               phy = bcm43xx_current_phy(bcm);
+               err = bcm43xx_select_wireless_core(bcm, phy->type);
+               if (!err)
+                       bcm43xx_periodic_tasks_setup(bcm);
+       }
        mutex_unlock(&(bcm)->mutex);
 
        printk(KERN_ERR PFX "Controller restart%s\n",
@@ -4130,13 +4173,14 @@ static void bcm43xx_chip_reset(void *_bcm)
 
 /* Hard-reset the chip.
  * This can be called from interrupt or process context.
+ * bcm->irq_lock must be locked.
  */
 void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason)
 {
-       assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED);
-       bcm43xx_set_status(bcm, BCM43xx_STAT_RESTARTING);
+       if (bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)
+               return;
        printk(KERN_ERR PFX "Controller RESET (%s) ...\n", reason);
-       INIT_WORK(&bcm->restart_work, bcm43xx_chip_reset, bcm);
+       INIT_WORK(&bcm->restart_work, bcm43xx_chip_reset);
        schedule_work(&bcm->restart_work);
 }
 
@@ -4184,7 +4228,11 @@ static int bcm43xx_resume(struct pci_dev *pdev)
        dprintk(KERN_INFO PFX "Resuming...\n");
 
        pci_set_power_state(pdev, 0);
-       pci_enable_device(pdev);
+       err = pci_enable_device(pdev);
+       if (err) {
+               printk(KERN_ERR PFX "Failure with pci_enable_device!\n");
+               return err;
+       }
        pci_restore_state(pdev);
 
        bcm43xx_chipset_attach(bcm);