diff options
Diffstat (limited to 'drivers/net/wireless/bcm43xx/bcm43xx_main.c')
-rw-r--r-- | drivers/net/wireless/bcm43xx/bcm43xx_main.c | 900 |
1 files changed, 530 insertions, 370 deletions
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index df317c1e12a8..cb9a3ae8463a 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -509,23 +509,20 @@ static void bcm43xx_synchronize_irq(struct bcm43xx_private *bcm) } /* Make sure we don't receive more data from the device. */ -static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate) +static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm) { unsigned long flags; - u32 old; - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) { - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); return -EBUSY; } - old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - bcm43xx_unlock_irqonly(bcm, flags); + 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 (oldstate) - *oldstate = old; - return 0; } @@ -537,7 +534,6 @@ static int bcm43xx_read_radioinfo(struct bcm43xx_private *bcm) u16 manufact; u16 version; u8 revision; - s8 i; if (bcm->chip_id == 0x4317) { if (bcm->chip_rev == 0x00) @@ -580,20 +576,11 @@ static int bcm43xx_read_radioinfo(struct bcm43xx_private *bcm) radio->version = version; radio->revision = revision; - /* Set default attenuation values. */ - radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm); - radio->radio_atten = bcm43xx_default_radio_attenuation(bcm); - radio->txctl1 = bcm43xx_default_txctl1(bcm); - radio->txctl2 = 0xFFFF; if (phy->type == BCM43xx_PHYTYPE_A) radio->txpower_desired = bcm->sprom.maxpower_aphy; else radio->txpower_desired = bcm->sprom.maxpower_bgphy; - /* Initialize the in-memory nrssi Lookup Table. */ - for (i = 0; i < 64; i++) - radio->nrssi_lt[i] = i; - return 0; err_unsupported_radio: @@ -1250,10 +1237,6 @@ int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *ne goto out; bcm->current_core = new_core; - bcm->current_80211_core_idx = -1; - if (new_core->id == BCM43xx_COREID_80211) - bcm->current_80211_core_idx = (int)(new_core - &(bcm->core_80211[0])); - out: return err; } @@ -1389,6 +1372,7 @@ void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy) if ((bcm43xx_core_enabled(bcm)) && !bcm43xx_using_pio(bcm)) { //FIXME: Do we _really_ want #ifndef CONFIG_BCM947XX here? +#if 0 #ifndef CONFIG_BCM947XX /* reset all used DMA controllers. */ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA1_BASE); @@ -1399,6 +1383,7 @@ void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy) if (bcm->current_core->rev < 5) bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA4_BASE); #endif +#endif } if (bcm43xx_status(bcm) == BCM43xx_STAT_SHUTTINGDOWN) { bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, @@ -1423,43 +1408,23 @@ static void bcm43xx_wireless_core_disable(struct bcm43xx_private *bcm) bcm43xx_core_disable(bcm, 0); } -/* Mark the current 80211 core inactive. - * "active_80211_core" is the other 80211 core, which is used. - */ -static int bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm, - struct bcm43xx_coreinfo *active_80211_core) +/* Mark the current 80211 core inactive. */ +static void bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm) { u32 sbtmstatelow; - struct bcm43xx_coreinfo *old_core; - int err = 0; bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); bcm43xx_radio_turn_off(bcm); sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - sbtmstatelow &= ~0x200a0000; - sbtmstatelow |= 0xa0000; + sbtmstatelow &= 0xDFF5FFFF; + sbtmstatelow |= 0x000A0000; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); udelay(1); sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - sbtmstatelow &= ~0xa0000; - sbtmstatelow |= 0x80000; + sbtmstatelow &= 0xFFF5FFFF; + sbtmstatelow |= 0x00080000; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); udelay(1); - - if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) { - old_core = bcm->current_core; - err = bcm43xx_switch_core(bcm, active_80211_core); - if (err) - goto out; - sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - sbtmstatelow &= ~0x20000000; - sbtmstatelow |= 0x20000000; - bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); - err = bcm43xx_switch_core(bcm, old_core); - } - -out: - return err; } static void handle_irq_transmit_status(struct bcm43xx_private *bcm) @@ -1581,17 +1546,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; @@ -1709,8 +1664,9 @@ static void handle_irq_beacon(struct bcm43xx_private *bcm) static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) { u32 reason; - u32 dma_reason[4]; - int activity = 0; + u32 dma_reason[6]; + u32 merged_dma_reason = 0; + int i, activity = 0; unsigned long flags; #ifdef CONFIG_BCM43XX_DEBUG @@ -1720,12 +1676,12 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) # define bcmirq_handled(irq) do { /* nothing */ } while (0) #endif /* CONFIG_BCM43XX_DEBUG*/ - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); reason = bcm->irq_reason; - dma_reason[0] = bcm->dma_reason[0]; - dma_reason[1] = bcm->dma_reason[1]; - dma_reason[2] = bcm->dma_reason[2]; - dma_reason[3] = bcm->dma_reason[3]; + for (i = 5; i >= 0; i--) { + dma_reason[i] = bcm->dma_reason[i]; + merged_dma_reason |= dma_reason[i]; + } if (unlikely(reason & BCM43xx_IRQ_XMIT_ERROR)) { /* TX error. We get this when Template Ram is written in wrong endianess @@ -1736,27 +1692,25 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) printkl(KERN_ERR PFX "FATAL ERROR: BCM43xx_IRQ_XMIT_ERROR\n"); bcmirq_handled(BCM43xx_IRQ_XMIT_ERROR); } - if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_FATALMASK) | - (dma_reason[1] & BCM43xx_DMAIRQ_FATALMASK) | - (dma_reason[2] & BCM43xx_DMAIRQ_FATALMASK) | - (dma_reason[3] & BCM43xx_DMAIRQ_FATALMASK))) { + if (unlikely(merged_dma_reason & BCM43xx_DMAIRQ_FATALMASK)) { printkl(KERN_ERR PFX "FATAL ERROR: Fatal DMA error: " - "0x%08X, 0x%08X, 0x%08X, 0x%08X\n", + "0x%08X, 0x%08X, 0x%08X, " + "0x%08X, 0x%08X, 0x%08X\n", dma_reason[0], dma_reason[1], - dma_reason[2], dma_reason[3]); + dma_reason[2], dma_reason[3], + dma_reason[4], dma_reason[5]); bcm43xx_controller_restart(bcm, "DMA error"); mmiowb(); - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); return; } - if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_NONFATALMASK) | - (dma_reason[1] & BCM43xx_DMAIRQ_NONFATALMASK) | - (dma_reason[2] & BCM43xx_DMAIRQ_NONFATALMASK) | - (dma_reason[3] & BCM43xx_DMAIRQ_NONFATALMASK))) { + if (unlikely(merged_dma_reason & BCM43xx_DMAIRQ_NONFATALMASK)) { printkl(KERN_ERR PFX "DMA error: " - "0x%08X, 0x%08X, 0x%08X, 0x%08X\n", + "0x%08X, 0x%08X, 0x%08X, " + "0x%08X, 0x%08X, 0x%08X\n", dma_reason[0], dma_reason[1], - dma_reason[2], dma_reason[3]); + dma_reason[2], dma_reason[3], + dma_reason[4], dma_reason[5]); } if (reason & BCM43xx_IRQ_PS) { @@ -1791,8 +1745,6 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) } /* Check the DMA reason registers for received data. */ - assert(!(dma_reason[1] & BCM43xx_DMAIRQ_RX_DONE)); - assert(!(dma_reason[2] & BCM43xx_DMAIRQ_RX_DONE)); if (dma_reason[0] & BCM43xx_DMAIRQ_RX_DONE) { if (bcm43xx_using_pio(bcm)) bcm43xx_pio_rx(bcm43xx_current_pio(bcm)->queue0); @@ -1800,13 +1752,17 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring0); /* We intentionally don't set "activity" to 1, here. */ } + assert(!(dma_reason[1] & BCM43xx_DMAIRQ_RX_DONE)); + assert(!(dma_reason[2] & BCM43xx_DMAIRQ_RX_DONE)); if (dma_reason[3] & BCM43xx_DMAIRQ_RX_DONE) { if (bcm43xx_using_pio(bcm)) bcm43xx_pio_rx(bcm43xx_current_pio(bcm)->queue3); else - bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring1); + bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring3); activity = 1; } + assert(!(dma_reason[4] & BCM43xx_DMAIRQ_RX_DONE)); + assert(!(dma_reason[5] & BCM43xx_DMAIRQ_RX_DONE)); bcmirq_handled(BCM43xx_IRQ_RX); if (reason & BCM43xx_IRQ_XMIT_STATUS) { @@ -1834,7 +1790,7 @@ static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) bcm43xx_leds_update(bcm, activity); bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); mmiowb(); - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); } static void pio_irq_workaround(struct bcm43xx_private *bcm, @@ -1863,14 +1819,18 @@ static void bcm43xx_interrupt_ack(struct bcm43xx_private *bcm, u32 reason) bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, reason); - bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_REASON, + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA0_REASON, bcm->dma_reason[0]); - bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_REASON, + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_REASON, bcm->dma_reason[1]); - bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_REASON, + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_REASON, bcm->dma_reason[2]); - bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_REASON, + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_REASON, bcm->dma_reason[3]); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_REASON, + bcm->dma_reason[4]); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA5_REASON, + bcm->dma_reason[5]); } /* Interrupt handler top-half */ @@ -1885,14 +1845,8 @@ 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; + assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); + assert(bcm->current_core->id == BCM43xx_COREID_80211); reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); if (reason == 0xffffffff) { @@ -1904,14 +1858,18 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_re if (!reason) goto out; - bcm->dma_reason[0] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA1_REASON) - & 0x0001dc00; - bcm->dma_reason[1] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA2_REASON) - & 0x0000dc00; - bcm->dma_reason[2] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA3_REASON) - & 0x0000dc00; - bcm->dma_reason[3] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA4_REASON) - & 0x0001dc00; + bcm->dma_reason[0] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA0_REASON) + & 0x0001DC00; + bcm->dma_reason[1] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA1_REASON) + & 0x0000DC00; + bcm->dma_reason[2] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA2_REASON) + & 0x0000DC00; + bcm->dma_reason[3] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA3_REASON) + & 0x0001DC00; + bcm->dma_reason[4] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA4_REASON) + & 0x0000DC00; + bcm->dma_reason[5] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA5_REASON) + & 0x0000DC00; bcm43xx_interrupt_ack(bcm, reason); @@ -1930,16 +1888,18 @@ out: static void bcm43xx_release_firmware(struct bcm43xx_private *bcm, int force) { + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + if (bcm->firmware_norelease && !force) return; /* Suspending or controller reset. */ - release_firmware(bcm->ucode); - bcm->ucode = NULL; - release_firmware(bcm->pcm); - bcm->pcm = NULL; - release_firmware(bcm->initvals0); - bcm->initvals0 = NULL; - release_firmware(bcm->initvals1); - bcm->initvals1 = NULL; + release_firmware(phy->ucode); + phy->ucode = NULL; + release_firmware(phy->pcm); + phy->pcm = NULL; + release_firmware(phy->initvals0); + phy->initvals0 = NULL; + release_firmware(phy->initvals1); + phy->initvals1 = NULL; } static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) @@ -1950,11 +1910,11 @@ static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) int nr; char buf[22 + sizeof(modparam_fwpostfix) - 1] = { 0 }; - if (!bcm->ucode) { + if (!phy->ucode) { snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_microcode%d%s.fw", (rev >= 5 ? 5 : rev), modparam_fwpostfix); - err = request_firmware(&bcm->ucode, buf, &bcm->pci_dev->dev); + err = request_firmware(&phy->ucode, buf, &bcm->pci_dev->dev); if (err) { printk(KERN_ERR PFX "Error: Microcode \"%s\" not available or load failed.\n", @@ -1963,12 +1923,12 @@ static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) } } - if (!bcm->pcm) { + if (!phy->pcm) { snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_pcm%d%s.fw", (rev < 5 ? 4 : 5), modparam_fwpostfix); - err = request_firmware(&bcm->pcm, buf, &bcm->pci_dev->dev); + err = request_firmware(&phy->pcm, buf, &bcm->pci_dev->dev); if (err) { printk(KERN_ERR PFX "Error: PCM \"%s\" not available or load failed.\n", @@ -1977,7 +1937,7 @@ static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) } } - if (!bcm->initvals0) { + if (!phy->initvals0) { if (rev == 2 || rev == 4) { switch (phy->type) { case BCM43xx_PHYTYPE_A: @@ -2008,20 +1968,20 @@ static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw", nr, modparam_fwpostfix); - err = request_firmware(&bcm->initvals0, buf, &bcm->pci_dev->dev); + err = request_firmware(&phy->initvals0, buf, &bcm->pci_dev->dev); if (err) { printk(KERN_ERR PFX "Error: InitVals \"%s\" not available or load failed.\n", buf); goto error; } - if (bcm->initvals0->size % sizeof(struct bcm43xx_initval)) { + if (phy->initvals0->size % sizeof(struct bcm43xx_initval)) { printk(KERN_ERR PFX "InitVals fileformat error.\n"); goto error; } } - if (!bcm->initvals1) { + if (!phy->initvals1) { if (rev >= 5) { u32 sbtmstatehigh; @@ -2043,14 +2003,14 @@ static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw", nr, modparam_fwpostfix); - err = request_firmware(&bcm->initvals1, buf, &bcm->pci_dev->dev); + err = request_firmware(&phy->initvals1, buf, &bcm->pci_dev->dev); if (err) { printk(KERN_ERR PFX "Error: InitVals \"%s\" not available or load failed.\n", buf); goto error; } - if (bcm->initvals1->size % sizeof(struct bcm43xx_initval)) { + if (phy->initvals1->size % sizeof(struct bcm43xx_initval)) { printk(KERN_ERR PFX "InitVals fileformat error.\n"); goto error; } @@ -2070,12 +2030,13 @@ err_noinitval: static void bcm43xx_upload_microcode(struct bcm43xx_private *bcm) { + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); const u32 *data; unsigned int i, len; /* Upload Microcode. */ - data = (u32 *)(bcm->ucode->data); - len = bcm->ucode->size / sizeof(u32); + data = (u32 *)(phy->ucode->data); + len = phy->ucode->size / sizeof(u32); bcm43xx_shm_control_word(bcm, BCM43xx_SHM_UCODE, 0x0000); for (i = 0; i < len; i++) { bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, @@ -2084,8 +2045,8 @@ static void bcm43xx_upload_microcode(struct bcm43xx_private *bcm) } /* Upload PCM data. */ - data = (u32 *)(bcm->pcm->data); - len = bcm->pcm->size / sizeof(u32); + data = (u32 *)(phy->pcm->data); + len = phy->pcm->size / sizeof(u32); bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01ea); bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, 0x00004000); bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01eb); @@ -2131,15 +2092,16 @@ err_format: static int bcm43xx_upload_initvals(struct bcm43xx_private *bcm) { + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); int err; - err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals0->data, - bcm->initvals0->size / sizeof(struct bcm43xx_initval)); + err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)phy->initvals0->data, + phy->initvals0->size / sizeof(struct bcm43xx_initval)); if (err) goto out; - if (bcm->initvals1) { - err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals1->data, - bcm->initvals1->size / sizeof(struct bcm43xx_initval)); + if (phy->initvals1) { + err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)phy->initvals1->data, + phy->initvals1->size / sizeof(struct bcm43xx_initval)); if (err) goto out; } @@ -2156,9 +2118,7 @@ static struct pci_device_id bcm43xx_47xx_ids[] = { static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm) { - int res; - unsigned int i; - u32 data; + int err; bcm->irq = bcm->pci_dev->irq; #ifdef CONFIG_BCM947XX @@ -2175,32 +2135,12 @@ static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm) } } #endif - res = request_irq(bcm->irq, bcm43xx_interrupt_handler, + err = request_irq(bcm->irq, bcm43xx_interrupt_handler, IRQF_SHARED, KBUILD_MODNAME, bcm); - if (res) { + if (err) printk(KERN_ERR PFX "Cannot register IRQ%d\n", bcm->irq); - return -ENODEV; - } - bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xffffffff); - bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402); - i = 0; - while (1) { - data = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); - if (data == BCM43xx_IRQ_READY) - break; - i++; - if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) { - printk(KERN_ERR PFX "Card IRQ register not responding. " - "Giving up.\n"); - free_irq(bcm->irq, bcm); - return -ENODEV; - } - udelay(10); - } - // dummy read - bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); - return 0; + return err; } /* Switch to the core used to write the GPIO register. @@ -2298,13 +2238,17 @@ static int bcm43xx_gpio_cleanup(struct bcm43xx_private *bcm) /* http://bcm-specs.sipsolutions.net/EnableMac */ void bcm43xx_mac_enable(struct bcm43xx_private *bcm) { - bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, - bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) - | BCM43xx_SBF_MAC_ENABLED); - bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, BCM43xx_IRQ_READY); - bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ - bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ - bcm43xx_power_saving_ctl_bits(bcm, -1, -1); + bcm->mac_suspended--; + assert(bcm->mac_suspended >= 0); + if (bcm->mac_suspended == 0) { + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) + | BCM43xx_SBF_MAC_ENABLED); + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, BCM43xx_IRQ_READY); + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ + bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + bcm43xx_power_saving_ctl_bits(bcm, -1, -1); + } } /* http://bcm-specs.sipsolutions.net/SuspendMAC */ @@ -2313,18 +2257,23 @@ void bcm43xx_mac_suspend(struct bcm43xx_private *bcm) int i; u32 tmp; - bcm43xx_power_saving_ctl_bits(bcm, -1, 1); - bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, - bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) - & ~BCM43xx_SBF_MAC_ENABLED); - bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ - for (i = 100000; i; i--) { - tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); - if (tmp & BCM43xx_IRQ_READY) - return; - udelay(10); + assert(bcm->mac_suspended >= 0); + if (bcm->mac_suspended == 0) { + bcm43xx_power_saving_ctl_bits(bcm, -1, 1); + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) + & ~BCM43xx_SBF_MAC_ENABLED); + bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + for (i = 10000; i; i--) { + tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); + if (tmp & BCM43xx_IRQ_READY) + goto out; + udelay(1); + } + printkl(KERN_ERR PFX "MAC suspend failed\n"); } - printkl(KERN_ERR PFX "MAC suspend failed\n"); +out: + bcm->mac_suspended++; } void bcm43xx_set_iwmode(struct bcm43xx_private *bcm, @@ -2394,7 +2343,6 @@ static void bcm43xx_chip_cleanup(struct bcm43xx_private *bcm) if (!modparam_noleds) bcm43xx_leds_exit(bcm); bcm43xx_gpio_cleanup(bcm); - free_irq(bcm->irq, bcm); bcm43xx_release_firmware(bcm, 0); } @@ -2406,7 +2354,7 @@ static int bcm43xx_chip_init(struct bcm43xx_private *bcm) struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); int err; - int tmp; + int i, tmp; u32 value32; u16 value16; @@ -2419,13 +2367,53 @@ static int bcm43xx_chip_init(struct bcm43xx_private *bcm) goto out; bcm43xx_upload_microcode(bcm); - err = bcm43xx_initialize_irq(bcm); - if (err) + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xFFFFFFFF); + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402); + i = 0; + while (1) { + value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); + if (value32 == BCM43xx_IRQ_READY) + break; + i++; + if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) { + printk(KERN_ERR PFX "IRQ_READY timeout\n"); + err = -ENODEV; + goto err_release_fw; + } + udelay(10); + } + 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 ) { + dprintk(KERN_ERR PFX + "Firmware: no support for microcode rev > 0x128\n"); + err = -1; goto err_release_fw; + } err = bcm43xx_gpio_init(bcm); if (err) - goto err_free_irq; + goto err_release_fw; err = bcm43xx_upload_initvals(bcm); if (err) @@ -2489,10 +2477,12 @@ static int bcm43xx_chip_init(struct bcm43xx_private *bcm) bcm43xx_write32(bcm, 0x018C, 0x02000000); } bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0x00004000); - bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_IRQ_MASK, 0x0001DC00); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA0_IRQ_MASK, 0x0001DC00); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_IRQ_MASK, 0x0000DC00); bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_IRQ_MASK, 0x0000DC00); - bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_IRQ_MASK, 0x0000DC00); - bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_IRQ_MASK, 0x0001DC00); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_IRQ_MASK, 0x0001DC00); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_IRQ_MASK, 0x0000DC00); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA5_IRQ_MASK, 0x0000DC00); value32 = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); value32 |= 0x00100000; @@ -2509,8 +2499,6 @@ err_radio_off: bcm43xx_radio_turn_off(bcm); err_gpio_cleanup: bcm43xx_gpio_cleanup(bcm); -err_free_irq: - free_irq(bcm->irq, bcm); err_release_fw: bcm43xx_release_firmware(bcm, 1); goto out; @@ -2550,11 +2538,9 @@ static void bcm43xx_init_struct_phyinfo(struct bcm43xx_phyinfo *phy) { /* Initialize a "phyinfo" structure. The structure is already * zeroed out. + * This is called on insmod time to initialize members. */ - phy->antenna_diversity = 0xFFFF; phy->savedpctlreg = 0xFFFF; - phy->minlowsig[0] = 0xFFFF; - phy->minlowsig[1] = 0xFFFF; spin_lock_init(&phy->lock); } @@ -2562,14 +2548,11 @@ static void bcm43xx_init_struct_radioinfo(struct bcm43xx_radioinfo *radio) { /* Initialize a "radioinfo" structure. The structure is already * zeroed out. + * This is called on insmod time to initialize members. */ radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE; radio->channel = 0xFF; radio->initial_channel = 0xFF; - radio->lofcal = 0xFFFF; - radio->initval = 0xFFFF; - radio->nrssi[0] = -1000; - radio->nrssi[1] = -1000; } static int bcm43xx_probe_cores(struct bcm43xx_private *bcm) @@ -2587,7 +2570,6 @@ static int bcm43xx_probe_cores(struct bcm43xx_private *bcm) * BCM43xx_MAX_80211_CORES); memset(&bcm->core_80211_ext, 0, sizeof(struct bcm43xx_coreinfo_80211) * BCM43xx_MAX_80211_CORES); - bcm->current_80211_core_idx = -1; bcm->nr_80211_available = 0; bcm->current_core = NULL; bcm->active_80211_core = NULL; @@ -2757,6 +2739,7 @@ static int bcm43xx_probe_cores(struct bcm43xx_private *bcm) goto out; } bcm->nr_80211_available++; + core->priv = ext_80211; bcm43xx_init_struct_phyinfo(&ext_80211->phy); bcm43xx_init_struct_radioinfo(&ext_80211->radio); break; @@ -2857,7 +2840,8 @@ static void bcm43xx_wireless_core_cleanup(struct bcm43xx_private *bcm) } /* http://bcm-specs.sipsolutions.net/80211Init */ -static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm) +static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm, + int active_wlcore) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); @@ -2939,19 +2923,26 @@ static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm) if (bcm->current_core->rev >= 5) bcm43xx_write16(bcm, 0x043C, 0x000C); - if (bcm43xx_using_pio(bcm)) - err = bcm43xx_pio_init(bcm); - else - err = bcm43xx_dma_init(bcm); - if (err) - goto err_chip_cleanup; + if (active_wlcore) { + if (bcm43xx_using_pio(bcm)) + err = bcm43xx_pio_init(bcm); + else + err = bcm43xx_dma_init(bcm); + if (err) + goto err_chip_cleanup; + } bcm43xx_write16(bcm, 0x0612, 0x0050); bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0416, 0x0050); bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0414, 0x01F4); - bcm43xx_mac_enable(bcm); - bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); + if (active_wlcore) { + if (radio->initial_channel != 0xFF) + bcm43xx_radio_selectchannel(bcm, radio->initial_channel, 0); + } + /* Don't enable MAC/IRQ here, as it will race with the IRQ handler. + * We enable it later. + */ bcm->current_core->initialized = 1; out: return err; @@ -3066,11 +3057,6 @@ out: return err; } -static void bcm43xx_softmac_init(struct bcm43xx_private *bcm) -{ - ieee80211softmac_start(bcm->net_dev); -} - static void bcm43xx_periodic_every120sec(struct bcm43xx_private *bcm) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); @@ -3182,47 +3168,46 @@ static void bcm43xx_periodic_work_handler(void *d) /* Periodic work will take a long time, so we want it to * be preemtible. */ - bcm43xx_lock_irqonly(bcm, flags); + mutex_lock(&bcm->mutex); netif_stop_queue(bcm->net_dev); + synchronize_net(); + 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); - bcm43xx_unlock_irqonly(bcm, flags); - bcm43xx_lock_noirq(bcm); + spin_unlock_irqrestore(&bcm->irq_lock, flags); bcm43xx_synchronize_irq(bcm); } else { /* Periodic work should take short time, so we want low * locking overhead. */ - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); } do_periodic_work(bcm); if (badness > BADNESS_LIMIT) { - bcm43xx_lock_irqonly(bcm, 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); - } + spin_lock_irqsave(&bcm->irq_lock, flags); + 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); - mmiowb(); - bcm43xx_unlock_irqonly(bcm, flags); - bcm43xx_unlock_noirq(bcm); - } else { - mmiowb(); - bcm43xx_unlock_irqsafe(bcm, flags); } + 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); @@ -3243,9 +3228,9 @@ 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); + spin_lock_irqsave(&(bcm)->irq_lock, flags); *data = bcm43xx_read16(bcm, BCM43xx_MMIO_RNG); - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&(bcm)->irq_lock, flags); return (sizeof(u16)); } @@ -3271,139 +3256,329 @@ static int bcm43xx_rng_init(struct bcm43xx_private *bcm) return err; } -/* This is the opposite of bcm43xx_init_board() */ -static void bcm43xx_free_board(struct bcm43xx_private *bcm) +static int bcm43xx_shutdown_all_wireless_cores(struct bcm43xx_private *bcm) { + int ret = 0; int i, err; + struct bcm43xx_coreinfo *core; - bcm43xx_lock_noirq(bcm); + bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN); + for (i = 0; i < bcm->nr_80211_available; i++) { + core = &(bcm->core_80211[i]); + assert(core->available); + if (!core->initialized) + continue; + err = bcm43xx_switch_core(bcm, core); + if (err) { + dprintk(KERN_ERR PFX "shutdown_all_wireless_cores " + "switch_core failed (%d)\n", err); + ret = err; + continue; + } + bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + bcm43xx_wireless_core_cleanup(bcm); + if (core == bcm->active_80211_core) + bcm->active_80211_core = NULL; + } + free_irq(bcm->irq, bcm); + bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT); + + return ret; +} + +/* This is the opposite of bcm43xx_init_board() */ +static void bcm43xx_free_board(struct bcm43xx_private *bcm) +{ + bcm43xx_rng_exit(bcm); bcm43xx_sysfs_unregister(bcm); bcm43xx_periodic_tasks_delete(bcm); - bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN); + mutex_lock(&(bcm)->mutex); + bcm43xx_shutdown_all_wireless_cores(bcm); + bcm43xx_pctl_set_crystal(bcm, 0); + mutex_unlock(&(bcm)->mutex); +} - bcm43xx_rng_exit(bcm); +static void prepare_phydata_for_init(struct bcm43xx_phyinfo *phy) +{ + phy->antenna_diversity = 0xFFFF; + memset(phy->minlowsig, 0xFF, sizeof(phy->minlowsig)); + memset(phy->minlowsigpos, 0, sizeof(phy->minlowsigpos)); + + /* Flags */ + phy->calibrated = 0; + phy->is_locked = 0; + + if (phy->_lo_pairs) { + memset(phy->_lo_pairs, 0, + sizeof(struct bcm43xx_lopair) * BCM43xx_LO_COUNT); + } + memset(phy->loopback_gain, 0, sizeof(phy->loopback_gain)); +} + +static void prepare_radiodata_for_init(struct bcm43xx_private *bcm, + struct bcm43xx_radioinfo *radio) +{ + int i; + + /* Set default attenuation values. */ + radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm); + radio->radio_atten = bcm43xx_default_radio_attenuation(bcm); + radio->txctl1 = bcm43xx_default_txctl1(bcm); + radio->txctl2 = 0xFFFF; + radio->txpwr_offset = 0; + + /* NRSSI */ + radio->nrssislope = 0; + for (i = 0; i < ARRAY_SIZE(radio->nrssi); i++) + radio->nrssi[i] = -1000; + for (i = 0; i < ARRAY_SIZE(radio->nrssi_lt); i++) + radio->nrssi_lt[i] = i; + + radio->lofcal = 0xFFFF; + radio->initval = 0xFFFF; + + radio->aci_enable = 0; + radio->aci_wlan_automatic = 0; + radio->aci_hw_rssi = 0; +} + +static void prepare_priv_for_init(struct bcm43xx_private *bcm) +{ + int i; + struct bcm43xx_coreinfo *core; + struct bcm43xx_coreinfo_80211 *wlext; + + assert(!bcm->active_80211_core); + + bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING); + + /* Flags */ + bcm->was_initialized = 0; + bcm->reg124_set_0x4 = 0; + + /* Stats */ + memset(&bcm->stats, 0, sizeof(bcm->stats)); + + /* Wireless core data */ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { - if (!bcm->core_80211[i].available) - continue; - if (!bcm->core_80211[i].initialized) + core = &(bcm->core_80211[i]); + wlext = core->priv; + + if (!core->available) continue; + assert(wlext == &(bcm->core_80211_ext[i])); - err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]); - assert(err == 0); - bcm43xx_wireless_core_cleanup(bcm); + prepare_phydata_for_init(&wlext->phy); + prepare_radiodata_for_init(bcm, &wlext->radio); } - bcm43xx_pctl_set_crystal(bcm, 0); + /* IRQ related flags */ + bcm->irq_reason = 0; + memset(bcm->dma_reason, 0, sizeof(bcm->dma_reason)); + bcm->irq_savedstate = BCM43xx_IRQ_INITIAL; - bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT); - bcm43xx_unlock_noirq(bcm); + bcm->mac_suspended = 1; + + /* Noise calculation context */ + memset(&bcm->noisecalc, 0, sizeof(bcm->noisecalc)); + + /* Periodic work context */ + bcm->periodic_state = 0; } -static int bcm43xx_init_board(struct bcm43xx_private *bcm) +static int wireless_core_up(struct bcm43xx_private *bcm, + int active_wlcore) +{ + int err; + + if (!bcm43xx_core_enabled(bcm)) + bcm43xx_wireless_core_reset(bcm, 1); + if (!active_wlcore) + bcm43xx_wireless_core_mark_inactive(bcm); + err = bcm43xx_wireless_core_init(bcm, active_wlcore); + if (err) + goto out; + if (!active_wlcore) + bcm43xx_radio_turn_off(bcm); +out: + return err; +} + +/* Select and enable the "to be used" wireless core. + * Locking: bcm->mutex must be aquired before calling this. + * bcm->irq_lock must not be aquired. + */ +int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm, + int phytype) { int i, err; - int connect_phy; + struct bcm43xx_coreinfo *active_core = NULL; + struct bcm43xx_coreinfo_80211 *active_wlext = NULL; + struct bcm43xx_coreinfo *core; + struct bcm43xx_coreinfo_80211 *wlext; + int adjust_active_sbtmstatelow = 0; might_sleep(); - bcm43xx_lock_noirq(bcm); - bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING); + if (phytype < 0) { + /* If no phytype is requested, select the first core. */ + assert(bcm->core_80211[0].available); + wlext = bcm->core_80211[0].priv; + phytype = wlext->phy.type; + } + /* Find the requested core. */ + for (i = 0; i < bcm->nr_80211_available; i++) { + core = &(bcm->core_80211[i]); + wlext = core->priv; + if (wlext->phy.type == phytype) { + active_core = core; + active_wlext = wlext; + break; + } + } + if (!active_core) + return -ESRCH; /* No such PHYTYPE on this board. */ + + if (bcm->active_80211_core) { + /* We already selected a wl core in the past. + * So first clean up everything. + */ + dprintk(KERN_INFO PFX "select_wireless_core: cleanup\n"); + ieee80211softmac_stop(bcm->net_dev); + bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED); + err = bcm43xx_disable_interrupts_sync(bcm); + assert(!err); + tasklet_enable(&bcm->isr_tasklet); + err = bcm43xx_shutdown_all_wireless_cores(bcm); + if (err) + goto error; + /* Ok, everything down, continue to re-initialize. */ + bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZING); + } + + /* Reset all data structures. */ + prepare_priv_for_init(bcm); - err = bcm43xx_pctl_set_crystal(bcm, 1); - if (err) - goto out; - err = bcm43xx_pctl_init(bcm); - if (err) - goto err_crystal_off; err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_FAST); if (err) - goto err_crystal_off; + goto error; - tasklet_enable(&bcm->isr_tasklet); + /* Mark all unused cores "inactive". */ for (i = 0; i < bcm->nr_80211_available; i++) { - err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]); - assert(err != -ENODEV); - if (err) - goto err_80211_unwind; + core = &(bcm->core_80211[i]); + wlext = core->priv; - /* Enable the selected wireless core. - * Connect PHY only on the first core. - */ - if (!bcm43xx_core_enabled(bcm)) { - if (bcm->nr_80211_available == 1) { - connect_phy = bcm43xx_current_phy(bcm)->connected; - } else { - if (i == 0) - connect_phy = 1; - else - connect_phy = 0; - } - bcm43xx_wireless_core_reset(bcm, connect_phy); + if (core == active_core) + continue; + err = bcm43xx_switch_core(bcm, core); + if (err) { + dprintk(KERN_ERR PFX "Could not switch to inactive " + "802.11 core (%d)\n", err); + goto error; } + err = wireless_core_up(bcm, 0); + if (err) { + dprintk(KERN_ERR PFX "core_up for inactive 802.11 core " + "failed (%d)\n", err); + goto error; + } + adjust_active_sbtmstatelow = 1; + } - if (i != 0) - bcm43xx_wireless_core_mark_inactive(bcm, &bcm->core_80211[0]); - - err = bcm43xx_wireless_core_init(bcm); - if (err) - goto err_80211_unwind; + /* Now initialize the active 802.11 core. */ + err = bcm43xx_switch_core(bcm, active_core); + if (err) { + dprintk(KERN_ERR PFX "Could not switch to active " + "802.11 core (%d)\n", err); + goto error; + } + if (adjust_active_sbtmstatelow && + active_wlext->phy.type == BCM43xx_PHYTYPE_G) { + u32 sbtmstatelow; - if (i != 0) { - bcm43xx_mac_suspend(bcm); - bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - bcm43xx_radio_turn_off(bcm); - } + sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + sbtmstatelow |= 0x20000000; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); } - bcm->active_80211_core = &bcm->core_80211[0]; - if (bcm->nr_80211_available >= 2) { - bcm43xx_switch_core(bcm, &bcm->core_80211[0]); - bcm43xx_mac_enable(bcm); + err = wireless_core_up(bcm, 1); + if (err) { + dprintk(KERN_ERR PFX "core_up for active 802.11 core " + "failed (%d)\n", err); + goto error; } - err = bcm43xx_rng_init(bcm); + err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC); if (err) - goto err_80211_unwind; + goto error; + bcm->active_80211_core = active_core; + 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"); bcm43xx_security_init(bcm); - bcm43xx_softmac_init(bcm); + ieee80211softmac_start(bcm->net_dev); - bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC); + /* Let's go! Be careful after enabling the IRQs. + * Don't switch cores, for example. + */ + bcm43xx_mac_enable(bcm); + bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED); + err = bcm43xx_initialize_irq(bcm); + if (err) + goto error; + bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); - if (bcm43xx_current_radio(bcm)->initial_channel != 0xFF) { - bcm43xx_mac_suspend(bcm); - bcm43xx_radio_selectchannel(bcm, bcm43xx_current_radio(bcm)->initial_channel, 0); - bcm43xx_mac_enable(bcm); - } + dprintk(KERN_INFO PFX "Selected 802.11 core (phytype %d)\n", + active_wlext->phy.type); - /* Initialization of the board is done. Flag it as such. */ - bcm43xx_set_status(bcm, BCM43xx_STAT_INITIALIZED); + return 0; + +error: + bcm43xx_set_status(bcm, BCM43xx_STAT_UNINIT); + bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW); + return err; +} +static int bcm43xx_init_board(struct bcm43xx_private *bcm) +{ + int err; + + mutex_lock(&(bcm)->mutex); + + tasklet_enable(&bcm->isr_tasklet); + err = bcm43xx_pctl_set_crystal(bcm, 1); + if (err) + goto err_tasklet; + err = bcm43xx_pctl_init(bcm); + if (err) + goto err_crystal_off; + err = bcm43xx_select_wireless_core(bcm, -1); + if (err) + goto err_crystal_off; + 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); - bcm43xx_sysfs_register(bcm); - //FIXME: check for bcm43xx_sysfs_register failure. This function is a bit messy regarding unwinding, though... /*FIXME: This should be handled by softmac instead. */ schedule_work(&bcm->softmac->associnfo.work); - assert(err == 0); out: - bcm43xx_unlock_noirq(bcm); + mutex_unlock(&(bcm)->mutex); return err; -err_80211_unwind: - tasklet_disable(&bcm->isr_tasklet); - /* unwind all 80211 initialization */ - for (i = 0; i < bcm->nr_80211_available; i++) { - if (!bcm->core_80211[i].initialized) - continue; - bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - bcm43xx_wireless_core_cleanup(bcm); - } +err_sysfs_unreg: + bcm43xx_sysfs_unregister(bcm); +err_wlshutdown: + bcm43xx_shutdown_all_wireless_cores(bcm); err_crystal_off: bcm43xx_pctl_set_crystal(bcm, 0); +err_tasklet: + tasklet_disable(&bcm->isr_tasklet); goto out; } @@ -3647,7 +3822,8 @@ static void bcm43xx_ieee80211_set_chan(struct net_device *net_dev, struct bcm43xx_radioinfo *radio; unsigned long flags; - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { bcm43xx_mac_suspend(bcm); bcm43xx_radio_selectchannel(bcm, channel, 0); @@ -3656,7 +3832,8 @@ static void bcm43xx_ieee80211_set_chan(struct net_device *net_dev, radio = bcm43xx_current_radio(bcm); radio->initial_channel = channel; } - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); } /* set_security() callback in struct ieee80211_device */ @@ -3670,7 +3847,8 @@ static void bcm43xx_ieee80211_set_security(struct net_device *net_dev, dprintk(KERN_INFO PFX "set security called"); - bcm43xx_lock_irqsafe(bcm, flags); + mutex_lock(&bcm->mutex); + spin_lock_irqsave(&bcm->irq_lock, flags); for (keyidx = 0; keyidx<WEP_KEYS; keyidx++) if (sec->flags & (1<<keyidx)) { @@ -3739,7 +3917,8 @@ static void bcm43xx_ieee80211_set_security(struct net_device *net_dev, } else bcm43xx_clear_keys(bcm); } - bcm43xx_unlock_irqsafe(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); + mutex_unlock(&bcm->mutex); } /* hard_start_xmit() callback in struct ieee80211_device */ @@ -3751,12 +3930,14 @@ static int bcm43xx_ieee80211_hard_start_xmit(struct ieee80211_txb *txb, int err = -ENODEV; unsigned long flags; - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) err = bcm43xx_tx(bcm, txb); - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); - return err; + if (unlikely(err)) + return NETDEV_TX_BUSY; + return NETDEV_TX_OK; } static struct net_device_stats * bcm43xx_net_get_stats(struct net_device *net_dev) @@ -3769,9 +3950,9 @@ static void bcm43xx_net_tx_timeout(struct net_device *net_dev) struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); unsigned long flags; - bcm43xx_lock_irqonly(bcm, flags); + spin_lock_irqsave(&bcm->irq_lock, flags); bcm43xx_controller_restart(bcm, "TX timeout"); - bcm43xx_unlock_irqonly(bcm, flags); + spin_unlock_irqrestore(&bcm->irq_lock, flags); } #ifdef CONFIG_NET_POLL_CONTROLLER @@ -3781,7 +3962,8 @@ static void bcm43xx_net_poll_controller(struct net_device *net_dev) unsigned long flags; local_irq_save(flags); - bcm43xx_interrupt_handler(bcm->irq, bcm, NULL); + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) + bcm43xx_interrupt_handler(bcm->irq, bcm, NULL); local_irq_restore(flags); } #endif /* CONFIG_NET_POLL_CONTROLLER */ @@ -3799,9 +3981,10 @@ static int bcm43xx_net_stop(struct net_device *net_dev) int err; ieee80211softmac_stop(net_dev); - err = bcm43xx_disable_interrupts_sync(bcm, NULL); + err = bcm43xx_disable_interrupts_sync(bcm); assert(!err); bcm43xx_free_board(bcm); + flush_scheduled_work(); return 0; } @@ -3818,10 +4001,12 @@ static int bcm43xx_init_private(struct bcm43xx_private *bcm, bcm->softmac->set_channel = bcm43xx_ieee80211_set_chan; bcm->irq_savedstate = BCM43xx_IRQ_INITIAL; + bcm->mac_suspended = 1; bcm->pci_dev = pci_dev; bcm->net_dev = net_dev; bcm->bad_frames_preempt = modparam_bad_frames_preempt; spin_lock_init(&bcm->irq_lock); + spin_lock_init(&bcm->leds_lock); mutex_init(&bcm->mutex); tasklet_init(&bcm->isr_tasklet, (void (*)(unsigned long))bcm43xx_interrupt_tasklet, @@ -3940,7 +4125,6 @@ static void __devexit bcm43xx_remove_one(struct pci_dev *pdev) bcm43xx_debugfs_remove_device(bcm); unregister_netdev(net_dev); bcm43xx_detach_board(bcm); - assert(bcm->ucode == NULL); free_ieee80211softmac(net_dev); } @@ -3950,47 +4134,31 @@ static void __devexit bcm43xx_remove_one(struct pci_dev *pdev) static void bcm43xx_chip_reset(void *_bcm) { struct bcm43xx_private *bcm = _bcm; - struct net_device *net_dev = bcm->net_dev; - struct pci_dev *pci_dev = bcm->pci_dev; - int err; - int was_initialized = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); - - netif_stop_queue(bcm->net_dev); - tasklet_disable(&bcm->isr_tasklet); + struct bcm43xx_phyinfo *phy; + int err = -ENODEV; - bcm->firmware_norelease = 1; - if (was_initialized) - bcm43xx_free_board(bcm); - bcm->firmware_norelease = 0; - bcm43xx_detach_board(bcm); - err = bcm43xx_init_private(bcm, net_dev, pci_dev); - if (err) - goto failure; - err = bcm43xx_attach_board(bcm); - if (err) - goto failure; - if (was_initialized) { - err = bcm43xx_init_board(bcm); - if (err) - goto failure; + mutex_lock(&(bcm)->mutex); + 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); } - netif_wake_queue(bcm->net_dev); - printk(KERN_INFO PFX "Controller restarted\n"); + mutex_unlock(&(bcm)->mutex); - return; -failure: - printk(KERN_ERR PFX "Controller restart failed\n"); + printk(KERN_ERR PFX "Controller restart%s\n", + (err == 0) ? "ed" : " failed"); } /* Hard-reset the chip. * This can be called from interrupt or process context. - * Make sure to _not_ re-enable device interrupts after this has been called. -*/ + * bcm->irq_lock must be locked. + */ void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason) { - bcm43xx_set_status(bcm, BCM43xx_STAT_RESTARTING); - bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ + 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); schedule_work(&bcm->restart_work); @@ -4002,21 +4170,16 @@ static int bcm43xx_suspend(struct pci_dev *pdev, pm_message_t state) { struct net_device *net_dev = pci_get_drvdata(pdev); struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); - unsigned long flags; - int try_to_shutdown = 0, err; + int err; dprintk(KERN_INFO PFX "Suspending...\n"); - bcm43xx_lock_irqsafe(bcm, flags); - bcm->was_initialized = (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); - if (bcm->was_initialized) - try_to_shutdown = 1; - bcm43xx_unlock_irqsafe(bcm, flags); - netif_device_detach(net_dev); - if (try_to_shutdown) { + bcm->was_initialized = 0; + if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { + bcm->was_initialized = 1; ieee80211softmac_stop(net_dev); - err = bcm43xx_disable_interrupts_sync(bcm, &bcm->irq_savedstate); + err = bcm43xx_disable_interrupts_sync(bcm); if (unlikely(err)) { dprintk(KERN_ERR PFX "Suspend failed.\n"); return -EAGAIN; @@ -4049,17 +4212,14 @@ static int bcm43xx_resume(struct pci_dev *pdev) pci_restore_state(pdev); bcm43xx_chipset_attach(bcm); - if (bcm->was_initialized) { - bcm->irq_savedstate = BCM43xx_IRQ_INITIAL; + if (bcm->was_initialized) err = bcm43xx_init_board(bcm); - } if (err) { printk(KERN_ERR PFX "Resume failed!\n"); return err; } - netif_device_attach(net_dev); - + dprintk(KERN_INFO PFX "Device resumed.\n"); return 0; |