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);
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
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);
}
}
+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);
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;
}
/* 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;
}
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;
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;
}
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);
}
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)
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.
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);
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 */
err = bcm43xx_disable_interrupts_sync(bcm);
assert(!err);
bcm43xx_free_board(bcm);
+ flush_scheduled_work();
return 0;
}
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);
(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 */
/* 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",
/* 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);
}
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);