diff options
author | Damien Bergamini <damien@cvs.openbsd.org> | 2009-10-24 18:14:58 +0000 |
---|---|---|
committer | Damien Bergamini <damien@cvs.openbsd.org> | 2009-10-24 18:14:58 +0000 |
commit | 45b6f313411871b5464b71e69dc2c0a1e116f86b (patch) | |
tree | 1cf80667e31b7c1320ccdfda18ca1cd88f0c8891 /sys | |
parent | 8e49b4a81933339e615f2d1b6b141cc1ec842939 (diff) |
huge diff introducing many of the recent changes made by intel to iwlwifi:
- ICT interrupts for >=5000 series (avoids reading IWN_INT which is slow)
- support v2 firmware header (including build number)
- switch to v2 firmware api (requires a firmware package upgrade)
- initial support for 1000 series and initial bits for upcoming 6000
series (untested as hardware is not available to the general public)
- many bug fixes, including workarounds for hardware bugs
make sure to update your iwn-firmware package to iwn-firmware-5.2.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/pci/if_iwn.c | 665 | ||||
-rw-r--r-- | sys/dev/pci/if_iwnreg.h | 101 | ||||
-rw-r--r-- | sys/dev/pci/if_iwnvar.h | 21 |
3 files changed, 525 insertions, 262 deletions
diff --git a/sys/dev/pci/if_iwn.c b/sys/dev/pci/if_iwn.c index 200a8f431d5..a9aec53e00a 100644 --- a/sys/dev/pci/if_iwn.c +++ b/sys/dev/pci/if_iwn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwn.c,v 1.65 2009/10/24 18:06:16 damien Exp $ */ +/* $OpenBSD: if_iwn.c,v 1.66 2009/10/24 18:14:57 damien Exp $ */ /*- * Copyright (c) 2007-2009 Damien Bergamini <damien.bergamini@free.fr> @@ -17,8 +17,8 @@ */ /* - * Driver for Intel Wireless WiFi Link 4965 and Intel WiFi Link 5000 Series - * 802.11 network adapters. + * Driver for Intel WiFi Link 4965 and 1000/5000/6000 Series 802.11 network + * adapters. */ #include "bpfilter.h" @@ -82,6 +82,7 @@ static const struct pci_matchid iwn_devices[] = { { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WIFI_LINK_6000_4 }, { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WIFI_LINK_6000_5 }, { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WIFI_LINK_6000_6 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WIFI_LINK_6000_7 }, { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WIFI_LINK_6050_1 }, { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WIFI_LINK_6050_2 }, { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WIFI_LINK_6050_3 }, @@ -112,6 +113,8 @@ int iwn_alloc_sched(struct iwn_softc *); void iwn_free_sched(struct iwn_softc *); int iwn_alloc_kw(struct iwn_softc *); void iwn_free_kw(struct iwn_softc *); +int iwn_alloc_ict(struct iwn_softc *); +void iwn_free_ict(struct iwn_softc *); int iwn_alloc_fwmem(struct iwn_softc *); void iwn_free_fwmem(struct iwn_softc *); int iwn_alloc_rx_ring(struct iwn_softc *, struct iwn_rx_ring *); @@ -121,11 +124,13 @@ int iwn_alloc_tx_ring(struct iwn_softc *, struct iwn_tx_ring *, int); void iwn_reset_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); void iwn_free_tx_ring(struct iwn_softc *, struct iwn_tx_ring *); +void iwn5000_ict_reset(struct iwn_softc *); int iwn_read_eeprom(struct iwn_softc *); void iwn4965_read_eeprom(struct iwn_softc *); void iwn4965_print_power_group(struct iwn_softc *, int); void iwn5000_read_eeprom(struct iwn_softc *); void iwn_read_eeprom_channels(struct iwn_softc *, int, uint32_t); +void iwn_read_eeprom_enhinfo(struct iwn_softc *); struct ieee80211_node *iwn_node_alloc(struct ieee80211com *); void iwn_newassoc(struct ieee80211com *, struct ieee80211_node *, int); @@ -233,8 +238,7 @@ int iwn5000_load_firmware_section(struct iwn_softc *, uint32_t, int iwn5000_load_firmware(struct iwn_softc *); int iwn_read_firmware(struct iwn_softc *); int iwn_clock_wait(struct iwn_softc *); -int iwn4965_apm_init(struct iwn_softc *); -int iwn5000_apm_init(struct iwn_softc *); +int iwn_apm_init(struct iwn_softc *); void iwn_apm_stop_master(struct iwn_softc *); void iwn_apm_stop(struct iwn_softc *); int iwn4965_nic_config(struct iwn_softc *); @@ -258,7 +262,6 @@ static const struct iwn_hal iwn4965_hal = { iwn4965_load_firmware, iwn4965_read_eeprom, iwn4965_post_alive, - iwn4965_apm_init, iwn4965_nic_config, iwn4965_update_sched, iwn4965_get_temperature, @@ -288,7 +291,6 @@ static const struct iwn_hal iwn5000_hal = { iwn5000_load_firmware, iwn5000_read_eeprom, iwn5000_post_alive, - iwn5000_apm_init, iwn5000_nic_config, iwn5000_update_sched, iwn5000_get_temperature, @@ -342,6 +344,7 @@ iwn_attach(struct device *parent, struct device *self, void *aux) pcireg_t memtype, reg; int i, error; + sc->sc_id = pa->pa_id; sc->sc_pct = pa->pa_pc; sc->sc_pcitag = pa->pa_tag; sc->sc_dmat = pa->pa_dmat; @@ -405,12 +408,6 @@ iwn_attach(struct device *parent, struct device *self, void *aux) return; } - /* Power ON adapter. */ - if ((error = hal->apm_init(sc)) != 0) { - printf(": could not power ON adapter\n"); - return; - } - /* Read MAC address, channels, etc from EEPROM. */ if ((error = iwn_read_eeprom(sc)) != 0) { printf(": could not read EEPROM\n"); @@ -429,37 +426,48 @@ iwn_attach(struct device *parent, struct device *self, void *aux) goto fail1; } + /* Allocate ICT table for 5000 Series. */ + if (sc->hw_type != IWN_HW_REV_TYPE_4965 && + (error = iwn_alloc_ict(sc)) != 0) { + printf(": could not allocate ICT table\n"); + goto fail2; + } + /* Allocate TX scheduler "rings". */ if ((error = iwn_alloc_sched(sc)) != 0) { printf(": could not allocate TX scheduler rings\n"); - goto fail2; + goto fail3; } /* Allocate TX rings (16 on 4965AGN, 20 on 5000.) */ for (i = 0; i < hal->ntxqs; i++) { if ((error = iwn_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { printf(": could not allocate TX ring %d\n", i); - goto fail3; + goto fail4; } } /* Allocate RX ring. */ if ((error = iwn_alloc_rx_ring(sc, &sc->rxq)) != 0) { printf(": could not allocate RX ring\n"); - goto fail3; + goto fail4; } - /* Power OFF adapter. */ - iwn_apm_stop(sc); /* Clear pending interrupts. */ IWN_WRITE(sc, IWN_INT, 0xffffffff); + /* Count the number of available chains. */ + sc->ntxchains = + ((sc->txchainmask >> 2) & 1) + + ((sc->txchainmask >> 1) & 1) + + ((sc->txchainmask >> 0) & 1); + sc->nrxchains = + ((sc->rxchainmask >> 2) & 1) + + ((sc->rxchainmask >> 1) & 1) + + ((sc->rxchainmask >> 0) & 1); printf(", MIMO %dT%dR, %.4s, address %s\n", sc->ntxchains, sc->nrxchains, sc->eeprom_domain, ether_sprintf(ic->ic_myaddr)); - /* Initialization firmware has not been loaded yet. */ - sc->sc_flags |= IWN_FLAG_FIRST_BOOT; - ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ ic->ic_state = IEEE80211_S_INIT; @@ -528,9 +536,11 @@ iwn_attach(struct device *parent, struct device *self, void *aux) return; /* Free allocated memory if something failed during attachment. */ -fail3: while (--i >= 0) +fail4: while (--i >= 0) iwn_free_tx_ring(sc, &sc->txq[i]); iwn_free_sched(sc); +fail3: if (sc->ict != NULL) + iwn_free_ict(sc); fail2: iwn_free_kw(sc); fail1: iwn_free_fwmem(sc); } @@ -545,63 +555,45 @@ iwn_hal_attach(struct iwn_softc *sc) sc->sc_hal = &iwn4965_hal; sc->fwname = "iwn-4965"; sc->critical_temp = IWN_CTOK(110); - sc->txantmsk = IWN_ANT_A | IWN_ANT_B; - sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = 2; - sc->nrxchains = 3; + sc->txchainmask = IWN_ANT_A | IWN_ANT_B; + sc->rxchainmask = IWN_ANT_ABC; break; case IWN_HW_REV_TYPE_5100: sc->sc_hal = &iwn5000_hal; sc->fwname = "iwn-5000"; sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_B; - sc->rxantmsk = IWN_ANT_A | IWN_ANT_B; - sc->ntxchains = 1; - sc->nrxchains = 2; + sc->txchainmask = IWN_ANT_B; + sc->rxchainmask = IWN_ANT_A | IWN_ANT_B; break; case IWN_HW_REV_TYPE_5150: sc->sc_hal = &iwn5000_hal; sc->fwname = "iwn-5150"; /* NB: critical temperature will be read from EEPROM. */ - sc->txantmsk = IWN_ANT_A; - sc->rxantmsk = IWN_ANT_A | IWN_ANT_B; - sc->ntxchains = 1; - sc->nrxchains = 2; + sc->txchainmask = IWN_ANT_A; + sc->rxchainmask = IWN_ANT_A | IWN_ANT_B; break; case IWN_HW_REV_TYPE_5300: case IWN_HW_REV_TYPE_5350: sc->sc_hal = &iwn5000_hal; sc->fwname = "iwn-5000"; sc->critical_temp = 110; - sc->txantmsk = sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = sc->nrxchains = 3; + sc->txchainmask = IWN_ANT_ABC; + sc->rxchainmask = IWN_ANT_ABC; break; case IWN_HW_REV_TYPE_1000: sc->sc_hal = &iwn5000_hal; sc->fwname = "iwn-1000"; sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_A; - sc->rxantmsk = IWN_ANT_A | IWN_ANT_B; - sc->ntxchains = 1; - sc->nrxchains = 2; + sc->txchainmask = IWN_ANT_A; + sc->rxchainmask = IWN_ANT_A | IWN_ANT_B; break; case IWN_HW_REV_TYPE_6000: - sc->sc_hal = &iwn5000_hal; - sc->fwname = "iwn-6000"; - sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_ABC; - sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = 3; - sc->nrxchains = 3; - break; case IWN_HW_REV_TYPE_6050: sc->sc_hal = &iwn5000_hal; - sc->fwname = "iwn-6050"; + sc->fwname = "iwn-6000"; sc->critical_temp = 110; - sc->txantmsk = IWN_ANT_ABC; - sc->rxantmsk = IWN_ANT_ABC; - sc->ntxchains = 3; - sc->nrxchains = 3; + sc->txchainmask = IWN_ANT_ABC; + sc->rxchainmask = IWN_ANT_ABC; break; default: printf(": adapter type %d not supported\n", sc->hw_type); @@ -612,7 +604,7 @@ iwn_hal_attach(struct iwn_softc *sc) #ifndef SMALL_KERNEL /* - * Attach the adapter's on-board thermal sensor to the sensors framework. + * Attach the adapter on-board thermal sensor to the sensors framework. */ void iwn_sensor_attach(struct iwn_softc *sc) @@ -671,6 +663,8 @@ iwn_detach(struct device *self, int flags) iwn_free_tx_ring(sc, &sc->txq[qid]); iwn_free_sched(sc); iwn_free_kw(sc); + if (sc->ict != NULL) + iwn_free_ict(sc); iwn_free_fwmem(sc); #ifndef SMALL_KERNEL @@ -849,8 +843,11 @@ iwn_eeprom_unlock(struct iwn_softc *sc) int iwn_init_otprom(struct iwn_softc *sc) { - int error; + uint32_t base; + uint16_t next; + int count, error; + /* Wait for clock stabilization before accessing prph. */ if ((error = iwn_clock_wait(sc)) != 0) return error; @@ -861,11 +858,37 @@ iwn_init_otprom(struct iwn_softc *sc) iwn_prph_clrbits(sc, IWN_APMG_PS, IWN_APMG_PS_RESET_REQ); iwn_nic_unlock(sc); + /* Set auto clock gate disable bit for HW with OTP shadow RAM. */ + if (sc->hw_type != IWN_HW_REV_TYPE_1000) { + IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT, + IWN_RESET_LINK_PWR_MGMT_DIS); + } IWN_CLRBITS(sc, IWN_EEPROM_GP, IWN_EEPROM_GP_IF_OWNER); /* Clear ECC status. */ IWN_SETBITS(sc, IWN_OTP_GP, IWN_OTP_GP_ECC_CORR_STTS | IWN_OTP_GP_ECC_UNCORR_STTS); + /* + * Find last valid OTP block (contains the EEPROM image) for HW + * without OTP shadow RAM. + */ + if (sc->hw_type == IWN_HW_REV_TYPE_1000) { + /* Switch to absolute addressing mode. */ + IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS); + base = 0; + for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) { + error = iwn_read_prom_data(sc, base, &next, 2); + if (error != 0) + return error; + if (next == 0) /* End of linked-list. */ + break; + base = letoh16(next); + } + if (base == 0 || count == IWN1000_OTP_NBLOCKS) + return EIO; + /* Skip "next" word. */ + sc->prom_base = base + 1; + } return 0; } @@ -876,6 +899,7 @@ iwn_read_prom_data(struct iwn_softc *sc, uint32_t addr, void *data, int count) uint32_t val, tmp; int ntries; + addr += sc->prom_base; for (; count > 0; count -= 2, addr++) { IWN_WRITE(sc, IWN_EEPROM, addr << 2); for (ntries = 0; ntries < 10; ntries++) { @@ -997,6 +1021,20 @@ iwn_free_kw(struct iwn_softc *sc) } int +iwn_alloc_ict(struct iwn_softc *sc) +{ + /* ICT table must be aligned on a 4KB boundary. */ + return iwn_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, + (void **)&sc->ict, IWN_ICT_SIZE, 4096); +} + +void +iwn_free_ict(struct iwn_softc *sc) +{ + iwn_dma_contig_free(&sc->ict_dma); +} + +int iwn_alloc_fwmem(struct iwn_softc *sc) { /* Must be aligned on a 16-byte boundary. */ @@ -1044,7 +1082,8 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) struct iwn_rx_data *data = &ring->data[i]; error = bus_dmamap_create(sc->sc_dmat, IWN_RBUF_SIZE, 1, - IWN_RBUF_SIZE, 0, BUS_DMA_NOWAIT, &data->map); + IWN_RBUF_SIZE, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, + &data->map); if (error != 0) { printf("%s: could not create RX buf DMA map\n", sc->sc_dev.dv_xname); @@ -1229,6 +1268,31 @@ iwn_free_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) } } +void +iwn5000_ict_reset(struct iwn_softc *sc) +{ + /* Disable interrupts. */ + IWN_WRITE(sc, IWN_INT_MASK, 0); + + /* Reset ICT table. */ + memset(sc->ict, 0, IWN_ICT_SIZE); + sc->ict_cur = 0; + + /* Set physical address of ICT table (4KB aligned.) */ + DPRINTF(("enabling ICT\n")); + IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE | + IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12); + + /* Enable periodic RX interrupt. */ + sc->int_mask |= IWN_INT_RX_PERIODIC; + /* Switch to ICT interrupt mode in driver. */ + sc->sc_flags |= IWN_FLAG_USE_ICT; + + /* Re-enable interrupts. */ + IWN_WRITE(sc, IWN_INT, 0xffffffff); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); +} + int iwn_read_eeprom(struct iwn_softc *sc) { @@ -1254,11 +1318,18 @@ iwn_read_eeprom(struct iwn_softc *sc) return error; } - if ((sc->sc_flags & IWN_FLAG_HAS_OTPROM) && - ((error = iwn_init_otprom(sc)) != 0)) { - printf("%s: could not initialize OTPROM (error=%d)\n", - sc->sc_dev.dv_xname, error); - return error; + if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) { + /* Adapter has to be powered on for OTPROM access to work. */ + if ((error = iwn_apm_init(sc)) != 0) { + printf("%s: could not power ON adapter\n", + sc->sc_dev.dv_xname); + return error; + } + if ((error = iwn_init_otprom(sc)) != 0) { + printf("%s: could not initialize OTPROM\n", + sc->sc_dev.dv_xname); + return error; + } } iwn_read_prom_data(sc, IWN_EEPROM_RFCFG, &val, 2); @@ -1271,6 +1342,9 @@ iwn_read_eeprom(struct iwn_softc *sc) /* Read adapter-specific information from EEPROM. */ hal->read_eeprom(sc); + if (sc->sc_flags & IWN_FLAG_HAS_OTPROM) + iwn_apm_stop(sc); /* Power OFF adapter. */ + iwn_eeprom_unlock(sc); return 0; } @@ -1358,7 +1432,7 @@ iwn4965_print_power_group(struct iwn_softc *sc, int i) void iwn5000_read_eeprom(struct iwn_softc *sc) { - int32_t temp, volt, delta; + int32_t temp, volt; uint32_t base, addr; uint16_t val; int i; @@ -1375,6 +1449,10 @@ iwn5000_read_eeprom(struct iwn_softc *sc) iwn_read_eeprom_channels(sc, i, addr); } + /* Read enhanced TX power information for 6000 Series. */ + if (sc->hw_type >= IWN_HW_REV_TYPE_6000) + iwn_read_eeprom_enhinfo(sc); + iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2); base = letoh16(val); if (sc->hw_type == IWN_HW_REV_TYPE_5150) { @@ -1383,10 +1461,10 @@ iwn5000_read_eeprom(struct iwn_softc *sc) temp = letoh16(val); iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2); volt = letoh16(val); - delta = temp - (volt / -5); - sc->critical_temp = (IWN_CTOK(110) - delta) * -5; - DPRINTF(("temp=%d volt=%d delta=%dK\n", - temp, volt, delta)); + sc->temp_off = temp - (volt / -5); + sc->critical_temp = (IWN_CTOK(110) - sc->temp_off) * -5; + DPRINTF(("temp=%d volt=%d offset=%dK\n", + temp, volt, sc->temp_off)); } else { /* Read crystal calibration. */ iwn_read_prom_data(sc, base + IWN5000_EEPROM_CRYSTAL, @@ -1452,6 +1530,38 @@ iwn_read_eeprom_channels(struct iwn_softc *sc, int n, uint32_t addr) } } +void +iwn_read_eeprom_enhinfo(struct iwn_softc *sc) +{ + struct iwn_eeprom_enhinfo enhinfo[35]; + uint16_t val, base; + int8_t maxpwr; + int i; + + iwn_read_prom_data(sc, IWN5000_EEPROM_REG, &val, 2); + base = letoh16(val); + iwn_read_prom_data(sc, base + IWN6000_EEPROM_ENHINFO, + enhinfo, sizeof enhinfo); + + for (i = 0; i < nitems(enhinfo); i++) { + maxpwr = 0; + if (sc->txchainmask & IWN_ANT_A) + maxpwr = MAX(maxpwr, enhinfo[i].chain[0]); + if (sc->txchainmask & IWN_ANT_B) + maxpwr = MAX(maxpwr, enhinfo[i].chain[1]); + if (sc->txchainmask & IWN_ANT_C) + maxpwr = MAX(maxpwr, enhinfo[i].chain[2]); + if (sc->ntxchains == 2) + maxpwr = MAX(maxpwr, enhinfo[i].mimo2); + else if (sc->ntxchains == 3) + maxpwr = MAX(maxpwr, enhinfo[i].mimo3); + maxpwr /= 2; /* Convert half-dBm to dBm. */ + + DPRINTF(("enhinfo %d, maxpwr=%d\n", i, maxpwr)); + /* XXX apply to sc->maxpwr[] */ + } +} + struct ieee80211_node * iwn_node_alloc(struct ieee80211com *ic) { @@ -1885,7 +1995,7 @@ iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc, int len, idx = -1; /* Runtime firmware should not send such a notification. */ - if (!(sc->sc_flags & IWN_FLAG_FIRST_BOOT)) + if (sc->sc_flags & IWN_FLAG_CALIB_DONE) return; len = (letoh32(desc->len) & 0x3fff) - 4; @@ -2007,8 +2117,10 @@ iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, { struct iwn5000_tx_stat *stat = (struct iwn5000_tx_stat *)(desc + 1); +#ifdef notyet /* Reset TX scheduler slot. */ iwn5000_reset_sched(sc, desc->qid & 0xf, desc->idx); +#endif bus_dmamap_sync(sc->sc_dmat, data->map, sizeof (*desc), sizeof (*stat), BUS_DMASYNC_POSTREAD); @@ -2168,7 +2280,7 @@ iwn_notif_intr(struct iwn_softc *sc) break; } if (uc->subtype == IWN_UCODE_INIT) { - /* Save microcontroller's report. */ + /* Save microcontroller report. */ memcpy(&sc->ucode_info, uc, sizeof (*uc)); } /* Save the address of the error log in SRAM. */ @@ -2236,6 +2348,7 @@ iwn_notif_intr(struct iwn_softc *sc) break; case IWN5000_CALIBRATION_DONE: + sc->sc_flags |= IWN_FLAG_CALIB_DONE; wakeup(sc); break; } @@ -2261,7 +2374,7 @@ iwn_wakeup_intr(struct iwn_softc *sc) /* Wakeup RX and TX rings. */ IWN_WRITE(sc, IWN_FH_RX_WPTR, sc->rxq.cur & ~7); - for (qid = 0; qid < 6; qid++) { + for (qid = 0; qid < sc->sc_hal->ntxqs; qid++) { struct iwn_tx_ring *ring = &sc->txq[qid]; IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, qid << 8 | ring->cur); } @@ -2279,6 +2392,9 @@ iwn_fatal_intr(struct iwn_softc *sc) struct iwn_fw_dump dump; int i; + /* Force a complete recalibration on next init. */ + sc->sc_flags &= ~IWN_FLAG_CALIB_DONE; + /* Check that the error log address is valid. */ if (sc->errptr < IWN_FW_DATA_BASE || sc->errptr + sizeof (dump) > @@ -2333,35 +2449,49 @@ iwn_intr(void *arg) { struct iwn_softc *sc = arg; struct ifnet *ifp = &sc->sc_ic.ic_if; - uint32_t r1, r2; + uint32_t r1, r2, tmp; /* Disable interrupts. */ - IWN_WRITE(sc, IWN_MASK, 0); - - r1 = IWN_READ(sc, IWN_INT); - r2 = IWN_READ(sc, IWN_FH_INT); - + IWN_WRITE(sc, IWN_INT_MASK, 0); + + /* Read interrupts from ICT (fast) or from registers (slow). */ + if (sc->sc_flags & IWN_FLAG_USE_ICT) { + tmp = 0; + while (sc->ict[sc->ict_cur] != 0) { + tmp |= sc->ict[sc->ict_cur]; + sc->ict[sc->ict_cur] = 0; /* Acknowledge. */ + sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT; + } + tmp = letoh32(tmp); + if (tmp == 0xffffffff) + tmp = 0; /* Shouldn't happen. */ + r1 = (tmp & 0xff00) << 16 | (tmp & 0xff); + r2 = 0; /* Unused. */ + } else { + r1 = IWN_READ(sc, IWN_INT); + if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) + return 0; /* Hardware gone! */ + r2 = IWN_READ(sc, IWN_FH_INT); + } if (r1 == 0 && r2 == 0) { if (ifp->if_flags & IFF_UP) - IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); return 0; /* Interrupt not for us. */ } - if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) - return 0; /* Hardware gone! */ /* Acknowledge interrupts. */ IWN_WRITE(sc, IWN_INT, r1); - IWN_WRITE(sc, IWN_FH_INT, r2); + if (!(sc->sc_flags & IWN_FLAG_USE_ICT)) + IWN_WRITE(sc, IWN_FH_INT, r2); if (r1 & IWN_INT_RF_TOGGLED) { - uint32_t tmp = IWN_READ(sc, IWN_GP_CNTRL); + tmp = IWN_READ(sc, IWN_GP_CNTRL); printf("%s: RF switch: radio %s\n", sc->sc_dev.dv_xname, (tmp & IWN_GP_CNTRL_RFKILL) ? "enabled" : "disabled"); } if (r1 & IWN_INT_CT_REACHED) { printf("%s: critical temperature reached!\n", sc->sc_dev.dv_xname); - /* XXX Reduce TX power? */ } if (r1 & (IWN_INT_SW_ERR | IWN_INT_HW_ERR)) { printf("%s: fatal firmware error\n", sc->sc_dev.dv_xname); @@ -2371,12 +2501,26 @@ iwn_intr(void *arg) iwn_stop(ifp, 1); return 1; } - if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) || - (r2 & IWN_FH_INT_RX)) - iwn_notif_intr(sc); + if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) || + (r2 & IWN_FH_INT_RX)) { + if (sc->sc_flags & IWN_FLAG_USE_ICT) { + if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) + IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_RX); + IWN_WRITE(sc, IWN_INT_PERIODIC, IWN_INT_PERIODIC_DIS); + iwn_notif_intr(sc); + if (r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX)) { + IWN_WRITE(sc, IWN_INT_PERIODIC, + IWN_INT_PERIODIC_ENA); + } + } else + iwn_notif_intr(sc); + } - if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) + if ((r1 & IWN_INT_FH_TX) || (r2 & IWN_FH_INT_TX)) { + if (sc->sc_flags & IWN_FLAG_USE_ICT) + IWN_WRITE(sc, IWN_FH_INT, IWN_FH_INT_TX); wakeup(sc); /* FH DMA transfer completed. */ + } if (r1 & IWN_INT_ALIVE) wakeup(sc); /* Firmware is alive. */ @@ -2386,7 +2530,7 @@ iwn_intr(void *arg) /* Re-enable interrupts. */ if (ifp->if_flags & IFF_UP) - IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); return 1; } @@ -2613,7 +2757,7 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) tx->timeout = htole16(0); if (hdrlen & 3) { - /* First segment's length must be a multiple of 4. */ + /* First segment length must be a multiple of 4. */ flags |= IWN_TX_NEED_PADDING; pad = 4 - (hdrlen & 3); } else @@ -2630,7 +2774,7 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) /* Group or management frame. */ tx->linkq = 0; /* XXX Alternate between antenna A and B? */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); tx->rflags |= IWN_RFLAG_ANT(txant); } else { tx->linkq = ni->ni_rates.rs_nrates - ni->ni_txrate - 1; @@ -2739,8 +2883,10 @@ iwn_tx(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) (caddr_t)desc - ring->desc_dma.vaddr, sizeof (*desc), BUS_DMASYNC_PREWRITE); +#ifdef notyet /* Update TX scheduler. */ hal->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); +#endif /* Kick TX ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; @@ -2904,7 +3050,6 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) int iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) { - const struct iwn_hal *hal = sc->sc_hal; struct iwn_tx_ring *ring = &sc->txq[4]; struct iwn_tx_desc *desc; struct iwn_tx_data *data; @@ -2967,8 +3112,10 @@ iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) (caddr_t)desc - ring->desc_dma.vaddr, sizeof (*desc), BUS_DMASYNC_PREWRITE); +#ifdef notyet /* Update TX scheduler. */ - hal->update_sched(sc, ring->qid, ring->cur, 0, 0); + sc->sc_hal->update_sched(sc, ring->qid, ring->cur, 0, 0); +#endif /* Kick command ring. */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; @@ -3014,13 +3161,13 @@ iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni) int i, txrate; /* Use the first valid TX antenna. */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); memset(&linkq, 0, sizeof linkq); linkq.id = wn->id; linkq.antmsk_1stream = txant; linkq.antmsk_2stream = IWN_ANT_A | IWN_ANT_B; - linkq.ampdu_max = 64; + linkq.ampdu_max = 31; linkq.ampdu_threshold = 3; linkq.ampdu_limit = htole16(4000); /* 4ms */ @@ -3059,7 +3206,7 @@ iwn_add_broadcast_node(struct iwn_softc *sc, int async) return error; /* Use the first valid TX antenna. */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); memset(&linkq, 0, sizeof linkq); linkq.id = hal->broadcast_id; @@ -3122,7 +3269,8 @@ iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) } /* - * Set the critical temperature at which the firmware will notify us. + * Set the critical temperature at which the firmware will stop the radio + * and notify us. */ int iwn_set_critical_temp(struct iwn_softc *sc) @@ -3225,7 +3373,7 @@ iwn4965_set_txpower(struct iwn_softc *sc, int async) DPRINTF(("voltage compensation=%d (UCODE=%d, EEPROM=%d)\n", vdiff, letoh32(uc->volt), sc->eeprom_voltage)); - /* Get channel's attenuation group. */ + /* Get channel attenuation group. */ if (chan <= 20) /* 1-20 */ grp = 4; else if (chan <= 43) /* 34-43 */ @@ -3238,7 +3386,7 @@ iwn4965_set_txpower(struct iwn_softc *sc, int async) grp = 3; DPRINTF(("chan %d, attenuation group=%d\n", chan, grp)); - /* Get channel's sub-band. */ + /* Get channel sub-band. */ for (i = 0; i < IWN_NBANDS; i++) if (sc->bands[i].lo != 0 && sc->bands[i].lo <= chan && chan <= sc->bands[i].hi) @@ -3286,7 +3434,7 @@ iwn4965_set_txpower(struct iwn_softc *sc, int async) else pwr -= 10; /* Others: -5dB */ - /* Do not exceed channel's max TX power. */ + /* Do not exceed channel max TX power. */ if (pwr > maxchpwr) pwr = maxchpwr; @@ -3346,7 +3494,7 @@ iwn4965_get_rssi(const struct iwn_rx_stat *stat) uint8_t mask, agc; int rssi; - mask = (letoh16(phy->antenna) >> 4) & 0x7; + mask = (letoh16(phy->antenna) >> 4) & IWN_ANT_ABC; agc = (letoh16(phy->agc) >> 7) & 0x7f; rssi = 0; @@ -3425,12 +3573,19 @@ iwn4965_get_temperature(struct iwn_softc *sc) int iwn5000_get_temperature(struct iwn_softc *sc) { + int32_t temp; + /* * Temperature is not used by the driver for 5000 Series because * TX power calibration is handled by firmware. We export it to * users through the sensor framework though. */ - return letoh32(sc->rawtemp); + temp = letoh32(sc->rawtemp); + if (sc->hw_type == IWN_HW_REV_TYPE_5150) { + temp = (temp / -5) + sc->temp_off; + temp = IWN_KTOC(temp); + } + return temp; } /* @@ -3499,21 +3654,21 @@ iwn_collect_noise(struct iwn_softc *sc, val = MAX(calib->rssi[2], val); /* Determine which antennas are connected. */ - sc->antmsk = 0; + sc->chainmask = 0; for (i = 0; i < 3; i++) if (val - calib->rssi[i] <= 15 * 20) - sc->antmsk |= 1 << i; + sc->chainmask |= 1 << i; /* If none of the TX antennas are connected, keep at least one. */ - if ((sc->antmsk & sc->txantmsk) == 0) - sc->antmsk |= IWN_LSB(sc->txantmsk); + if ((sc->chainmask & sc->txchainmask) == 0) + sc->chainmask |= IWN_LSB(sc->txchainmask); (void)hal->set_gains(sc); calib->state = IWN_CALIB_STATE_RUN; #ifdef notyet /* XXX Disable RX chains with no antennas connected. */ - sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->antmsk)); - (void)iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 1); + sc->rxon.rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); + (void)iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); #endif /* Enable power-saving mode if requested by user. */ @@ -3538,10 +3693,9 @@ iwn5000_init_gains(struct iwn_softc *sc) { struct iwn_phy_calib cmd; - if (sc->hw_type == IWN_HW_REV_TYPE_6000 || - sc->hw_type == IWN_HW_REV_TYPE_6050) + if (sc->hw_type == IWN_HW_REV_TYPE_6050) return 0; - + memset(&cmd, 0, sizeof cmd); cmd.code = IWN5000_PHY_CALIB_RESET_NOISE_GAIN; cmd.ngroups = 1; @@ -3560,14 +3714,14 @@ iwn4965_set_gains(struct iwn_softc *sc) /* Get minimal noise among connected antennas. */ noise = INT_MAX; /* NB: There's at least one antenna. */ for (i = 0; i < 3; i++) - if (sc->antmsk & (1 << i)) + if (sc->chainmask & (1 << i)) noise = MIN(calib->noise[i], noise); memset(&cmd, 0, sizeof cmd); cmd.code = IWN4965_PHY_CALIB_DIFF_GAIN; /* Set differential gains for connected antennas. */ for (i = 0; i < 3; i++) { - if (sc->antmsk & (1 << i)) { + if (sc->chainmask & (1 << i)) { /* Compute attenuation (in unit of 1.5dB). */ delta = (noise - (int32_t)calib->noise[i]) / 30; /* NB: delta <= 0 */ @@ -3578,7 +3732,7 @@ iwn4965_set_gains(struct iwn_softc *sc) } } DPRINTF(("setting differential gains Ant A/B/C: %x/%x/%x (%x)\n", - cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->antmsk)); + cmd.gain[0], cmd.gain[1], cmd.gain[2], sc->chainmask)); return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); } @@ -3587,21 +3741,22 @@ iwn5000_set_gains(struct iwn_softc *sc) { struct iwn_calib_state *calib = &sc->calib; struct iwn_phy_calib_gain cmd; - int i, delta; + int i, ant, delta; - if (sc->hw_type == IWN_HW_REV_TYPE_6000 || - sc->hw_type == IWN_HW_REV_TYPE_6050) + if (sc->hw_type == IWN_HW_REV_TYPE_6050) return 0; memset(&cmd, 0, sizeof cmd); cmd.code = IWN5000_PHY_CALIB_NOISE_GAIN; cmd.ngroups = 1; cmd.isvalid = 1; - /* Set differential gains for antennas B and C. */ - for (i = 1; i < 3; i++) { - if (sc->antmsk & (1 << i)) { - /* The delta is relative to antenna A. */ - delta = ((int32_t)calib->noise[0] - + /* Get first available RX antenna as referential. */ + ant = IWN_LSB(sc->rxchainmask); + /* Set differential gains for other antennas. */ + for (i = ant + 1; i < 3; i++) { + if (sc->chainmask & (1 << i)) { + /* The delta is relative to antenna "ant". */ + delta = ((int32_t)calib->noise[ant] - (int32_t)calib->noise[i]) / 30; /* Limit to [-4.5dB,+4.5dB]. */ cmd.gain[i - 1] = MIN(abs(delta), 3); @@ -3609,8 +3764,8 @@ iwn5000_set_gains(struct iwn_softc *sc) cmd.gain[i - 1] |= 1 << 2; /* sign bit */ } } - DPRINTF(("setting differential gains Ant B/C: %x/%x (%x)\n", - cmd.gain[0], cmd.gain[1], sc->antmsk)); + DPRINTF(("setting differential gains: %x/%x (%x)\n", + cmd.gain[0], cmd.gain[1], sc->chainmask)); return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); } @@ -3858,21 +4013,28 @@ iwn_config(struct iwn_softc *sc) struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; struct iwn_bluetooth bluetooth; + uint32_t txmask; uint16_t rxchain; int error; - /* Set power saving level to CAM during initialization. */ - if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) { - printf("%s: could not set power saving level\n", - sc->sc_dev.dv_xname); - return error; + /* Configure valid TX chains for 5000 Series. */ + if (sc->hw_type != IWN_HW_REV_TYPE_4965) { + txmask = htole32(sc->txchainmask); + DPRINTF(("configuring valid TX chains 0x%x\n", txmask)); + error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask, + sizeof txmask, 0); + if (error != 0) { + printf("%s: could not configure valid TX chains\n", + sc->sc_dev.dv_xname); + return error; + } } /* Configure bluetooth coexistence. */ memset(&bluetooth, 0, sizeof bluetooth); - bluetooth.flags = 3; - bluetooth.lead = 0xaa; - bluetooth.kill = 1; + bluetooth.flags = IWN_BT_COEX_MODE_4WIRE; + bluetooth.lead_time = IWN_BT_LEAD_TIME_DEF; + bluetooth.max_kill = IWN_BT_MAX_KILL_DEF; DPRINTF(("configuring bluetooth coexistence\n")); error = iwn_cmd(sc, IWN_CMD_BT_COEX, &bluetooth, sizeof bluetooth, 0); if (error != 0) { @@ -3881,12 +4043,11 @@ iwn_config(struct iwn_softc *sc) return error; } - /* Configure adapter. */ + /* Set mode, channel, RX filter and enable RX. */ memset(&sc->rxon, 0, sizeof (struct iwn_rxon)); IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); IEEE80211_ADDR_COPY(sc->rxon.myaddr, ic->ic_myaddr); IEEE80211_ADDR_COPY(sc->rxon.wlap, ic->ic_myaddr); - /* Set default channel. */ sc->rxon.chan = ieee80211_chan2ieee(ic, ic->ic_ibss_chan); sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); if (IEEE80211_IS_CHAN_2GHZ(ic->ic_ibss_chan)) @@ -3909,13 +4070,22 @@ iwn_config(struct iwn_softc *sc) sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ sc->rxon.ht_single_mask = 0xff; sc->rxon.ht_dual_mask = 0xff; - rxchain = IWN_RXCHAIN_VALID(IWN_ANT_ABC) | IWN_RXCHAIN_IDLE_COUNT(2) | - IWN_RXCHAIN_MIMO_COUNT(2); + sc->rxon.ht_triple_mask = 0xff; + rxchain = + IWN_RXCHAIN_VALID(sc->rxchainmask) | + IWN_RXCHAIN_MIMO_COUNT(2) | + IWN_RXCHAIN_IDLE_COUNT(2); sc->rxon.rxchain = htole16(rxchain); DPRINTF(("setting configuration\n")); - error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 0); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 0); if (error != 0) { - printf("%s: configure command failed\n", sc->sc_dev.dv_xname); + printf("%s: RXON command failed\n", sc->sc_dev.dv_xname); + return error; + } + + if ((error = iwn_add_broadcast_node(sc, 0)) != 0) { + printf("%s: could not add broadcast node\n", + sc->sc_dev.dv_xname); return error; } @@ -3925,14 +4095,15 @@ iwn_config(struct iwn_softc *sc) return error; } - if ((error = iwn_add_broadcast_node(sc, 0)) != 0) { - printf("%s: could not add broadcast node\n", + if ((error = iwn_set_critical_temp(sc)) != 0) { + printf("%s: could not set critical temperature\n", sc->sc_dev.dv_xname); return error; } - if ((error = iwn_set_critical_temp(sc)) != 0) { - printf("%s: could not set critical temperature\n", + /* Set power saving level to CAM during initialization. */ + if ((error = iwn_set_pslevel(sc, 0, 0, 0)) != 0) { + printf("%s: could not set power saving level\n", sc->sc_dev.dv_xname); return error; } @@ -3999,7 +4170,7 @@ iwn_scan(struct iwn_softc *sc, uint16_t flags) rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; } /* Use the first valid TX antenna. */ - txant = IWN_LSB(sc->txantmsk); + txant = IWN_LSB(sc->txchainmask); tx->rflags |= IWN_RFLAG_ANT(txant); essid = (struct iwn_scan_essid *)(tx + 1); @@ -4075,7 +4246,7 @@ iwn_auth(struct iwn_softc *sc) struct ieee80211_node *ni = ic->ic_bss; int error; - /* Update adapter's configuration. */ + /* Update adapter configuration. */ IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); sc->rxon.chan = ieee80211_chan2ieee(ic, ni->ni_chan); sc->rxon.flags = htole32(IWN_RXON_TSF | IWN_RXON_CTS_TO_SELF); @@ -4100,9 +4271,9 @@ iwn_auth(struct iwn_softc *sc) } DPRINTF(("rxon chan %d flags %x cck %x ofdm %x\n", sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask, sc->rxon.ofdm_mask)); - error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 1); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); if (error != 0) { - printf("%s: could not configure\n", sc->sc_dev.dv_xname); + printf("%s: RXON command failed\n", sc->sc_dev.dv_xname); return error; } @@ -4112,7 +4283,7 @@ iwn_auth(struct iwn_softc *sc) return error; } /* - * Reconfiguring RXON clears the firmware's nodes table so we must + * Reconfiguring RXON clears the firmware nodes table so we must * add the broadcast node again. */ if ((error = iwn_add_broadcast_node(sc, 1)) != 0) { @@ -4142,7 +4313,7 @@ iwn_run(struct iwn_softc *sc) return error; } - /* Update adapter's configuration. */ + /* Update adapter configuration. */ sc->rxon.associd = htole16(IEEE80211_AID(ni->ni_associd)); /* Short preamble and slot time are negotiated when associating. */ sc->rxon.flags &= ~htole32(IWN_RXON_SHPREAMBLE | IWN_RXON_SHSLOT); @@ -4152,7 +4323,7 @@ iwn_run(struct iwn_softc *sc) sc->rxon.flags |= htole32(IWN_RXON_SHPREAMBLE); sc->rxon.filter |= htole32(IWN_FILTER_BSS); DPRINTF(("rxon chan %d flags %x\n", sc->rxon.chan, sc->rxon.flags)); - error = iwn_cmd(sc, IWN_CMD_CONFIGURE, &sc->rxon, hal->rxonsz, 1); + error = iwn_cmd(sc, IWN_CMD_RXON, &sc->rxon, hal->rxonsz, 1); if (error != 0) { printf("%s: could not update configuration\n", sc->sc_dev.dv_xname); @@ -4501,7 +4672,9 @@ iwn5000_query_calibration(struct iwn_softc *sc) return error; /* Wait at most two seconds for calibration to complete. */ - return tsleep(sc, PCATCH, "iwncal", 2 * hz); + if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) + error = tsleep(sc, PCATCH, "iwncal", 2 * hz); + return error; } /* @@ -4541,7 +4714,7 @@ iwn4965_post_alive(struct iwn_softc *sc) if ((error = iwn_nic_lock(sc)) != 0) return error; - /* Clear TX scheduler's state in SRAM. */ + /* Clear TX scheduler state in SRAM. */ sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); iwn_mem_set_region_4(sc, sc->sched_base + IWN4965_SCHED_CTX_OFF, 0, IWN4965_SCHED_CTX_LEN / sizeof (uint32_t)); @@ -4592,10 +4765,13 @@ iwn5000_post_alive(struct iwn_softc *sc) struct iwn5000_wimax_coex wimax; int error, qid; + /* Switch to using ICT interrupt mode. */ + iwn5000_ict_reset(sc); + if ((error = iwn_nic_lock(sc)) != 0) return error; - /* Clear TX scheduler's state in SRAM. */ + /* Clear TX scheduler state in SRAM. */ sc->sched_base = iwn_prph_read(sc, IWN_SCHED_SRAM_ADDR); iwn_mem_set_region_4(sc, sc->sched_base + IWN5000_SCHED_CTX_OFF, 0, IWN5000_SCHED_CTX_LEN / sizeof (uint32_t)); @@ -4663,7 +4839,7 @@ iwn5000_post_alive(struct iwn_softc *sc) return error; } } - if (sc->sc_flags & IWN_FLAG_FIRST_BOOT) { + if (!(sc->sc_flags & IWN_FLAG_CALIB_DONE)) { /* Query calibration from the initialization firmware. */ if ((error = iwn5000_query_calibration(sc)) != 0) { printf("%s: could not query calibration\n", @@ -4671,12 +4847,9 @@ iwn5000_post_alive(struct iwn_softc *sc) return error; } /* - * We have the calibration results now so we can skip - * loading the initialization firmware next time. + * We have the calibration results now, reboot with the + * runtime firmware (call ourselves recursively!) */ - sc->sc_flags &= ~IWN_FLAG_FIRST_BOOT; - - /* Reboot (call ourselves recursively!) */ iwn_hw_stop(sc); error = iwn_hw_init(sc); } else { @@ -4846,8 +5019,8 @@ iwn5000_load_firmware(struct iwn_softc *sc) int error; /* Load the initialization firmware on first boot only. */ - fw = (sc->sc_flags & IWN_FLAG_FIRST_BOOT) ? - &sc->fw.init : &sc->fw.main; + fw = (sc->sc_flags & IWN_FLAG_CALIB_DONE) ? + &sc->fw.main : &sc->fw.init; error = iwn5000_load_firmware_section(sc, IWN_FW_TEXT_BASE, fw->text, fw->textsz); @@ -4874,7 +5047,8 @@ iwn_read_firmware(struct iwn_softc *sc) { const struct iwn_hal *hal = sc->sc_hal; struct iwn_fw_info *fw = &sc->fw; - const struct iwn_firmware_hdr *hdr; + const uint32_t *ptr; + uint32_t rev; size_t size; int error; @@ -4884,20 +5058,34 @@ iwn_read_firmware(struct iwn_softc *sc) sc->sc_dev.dv_xname, error, sc->fwname); return error; } - if (size < sizeof (*hdr)) { + if (size < 28) { printf("%s: truncated firmware header: %d bytes\n", sc->sc_dev.dv_xname, size); free(fw->data, M_DEVBUF); return EINVAL; } - /* Extract firmware header information. */ - hdr = (struct iwn_firmware_hdr *)fw->data; - fw->main.textsz = letoh32(hdr->main_textsz); - fw->main.datasz = letoh32(hdr->main_datasz); - fw->init.textsz = letoh32(hdr->init_textsz); - fw->init.datasz = letoh32(hdr->init_datasz); - fw->boot.textsz = letoh32(hdr->boot_textsz); - fw->boot.datasz = 0; + + /* Process firmware header. */ + ptr = (const uint32_t *)fw->data; + rev = letoh32(*ptr++); + /* Check firmware API version. */ + if (IWN_FW_API(rev) <= 1) { + printf("%s: bad firmware, need API version >=2\n", + sc->sc_dev.dv_xname); + free(fw->data, M_DEVBUF); + return EINVAL; + } + if (IWN_FW_API(rev) >= 3) { + /* Skip build number (version 2 header). */ + size -= 4; + ptr++; + } + fw->main.textsz = letoh32(*ptr++); + fw->main.datasz = letoh32(*ptr++); + fw->init.textsz = letoh32(*ptr++); + fw->init.datasz = letoh32(*ptr++); + fw->boot.textsz = letoh32(*ptr++); + size -= 24; /* Sanity-check firmware header. */ if (fw->main.textsz > hal->fw_text_maxsz || @@ -4912,8 +5100,8 @@ iwn_read_firmware(struct iwn_softc *sc) } /* Check that all firmware sections fit. */ - if (size < sizeof (*hdr) + fw->main.textsz + fw->main.datasz + - fw->init.textsz + fw->init.datasz + fw->boot.textsz) { + if (fw->main.textsz + fw->main.datasz + fw->init.textsz + + fw->init.datasz + fw->boot.textsz > size) { printf("%s: firmware file too short: %d bytes\n", sc->sc_dev.dv_xname, size); free(fw->data, M_DEVBUF); @@ -4921,7 +5109,7 @@ iwn_read_firmware(struct iwn_softc *sc) } /* Get pointers to firmware sections. */ - fw->main.text = (const uint8_t *)(hdr + 1); + fw->main.text = (const uint8_t *)ptr; fw->main.data = fw->main.text + fw->main.textsz; fw->init.text = fw->main.data + fw->main.datasz; fw->init.data = fw->init.text + fw->init.textsz; @@ -4939,10 +5127,10 @@ iwn_clock_wait(struct iwn_softc *sc) IWN_SETBITS(sc, IWN_GP_CNTRL, IWN_GP_CNTRL_INIT_DONE); /* Wait for clock stabilization. */ - for (ntries = 0; ntries < 25000; ntries++) { + for (ntries = 0; ntries < 2500; ntries++) { if (IWN_READ(sc, IWN_GP_CNTRL) & IWN_GP_CNTRL_MAC_CLOCK_READY) return 0; - DELAY(100); + DELAY(10); } printf("%s: timeout waiting for clock stabilization\n", sc->sc_dev.dv_xname); @@ -4950,58 +5138,54 @@ iwn_clock_wait(struct iwn_softc *sc) } int -iwn4965_apm_init(struct iwn_softc *sc) -{ - int error; - - /* Disable L0s. */ - IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER); - IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX); - - if ((error = iwn_clock_wait(sc)) != 0) - return error; - - if ((error = iwn_nic_lock(sc)) != 0) - return error; - /* Enable DMA. */ - iwn_prph_write(sc, IWN_APMG_CLK_CTRL, - IWN_APMG_CLK_CTRL_DMA_CLK_RQT | IWN_APMG_CLK_CTRL_BSM_CLK_RQT); - DELAY(20); - /* Disable L1. */ - iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS); - iwn_nic_unlock(sc); - - return 0; -} - -int -iwn5000_apm_init(struct iwn_softc *sc) +iwn_apm_init(struct iwn_softc *sc) { + pcireg_t reg; int error; - /* Disable L0s. */ + /* Disable L0s exit timer (NMI bug workaround.) */ IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_DIS_L0S_TIMER); + /* Don't wait for ICH L0s (ICH bug workaround.) */ IWN_SETBITS(sc, IWN_GIO_CHICKEN, IWN_GIO_CHICKEN_L1A_NO_L0S_RX); - /* Set Flow Handler wait threshold to the maximum. */ + /* Set FH wait threshold to max (HW bug under stress workaround.) */ IWN_SETBITS(sc, IWN_DBG_HPET_MEM, 0xffff0000); - /* Enable HAP to move adapter from L1a to L0s. */ + /* Enable HAP INTA to move adapter from L1a to L0s. */ IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_HAP_WAKE_L1A); - if (sc->hw_type != IWN_HW_REV_TYPE_6000 && + /* Retrieve PCIe Active State Power Management (ASPM). */ + reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, + sc->sc_cap_off + PCI_PCIE_LCSR); + /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ + if (reg & PCI_PCIE_LCSR_ASPM_L1) /* L1 Entry enabled. */ + IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); + else + IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); + + if (sc->hw_type != IWN_HW_REV_TYPE_4965 && + sc->hw_type != IWN_HW_REV_TYPE_6000 && sc->hw_type != IWN_HW_REV_TYPE_6050) IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT); + /* Wait for clock stabilization before accessing prph. */ if ((error = iwn_clock_wait(sc)) != 0) return error; if ((error = iwn_nic_lock(sc)) != 0) return error; - /* Enable DMA. */ - iwn_prph_write(sc, IWN_APMG_CLK_CTRL, IWN_APMG_CLK_CTRL_DMA_CLK_RQT); + if (sc->hw_type == IWN_HW_REV_TYPE_4965) { + /* Enable DMA and BSM (Bootstrap State Machine.) */ + iwn_prph_write(sc, IWN_APMG_CLK_EN, + IWN_APMG_CLK_CTRL_DMA_CLK_RQT | + IWN_APMG_CLK_CTRL_BSM_CLK_RQT); + } else { + /* Enable DMA. */ + iwn_prph_write(sc, IWN_APMG_CLK_EN, + IWN_APMG_CLK_CTRL_DMA_CLK_RQT); + } DELAY(20); - /* Disable L1. */ + /* Disable L1-Active. */ iwn_prph_setbits(sc, IWN_APMG_PCI_STT, IWN_APMG_PCI_STT_L1A_DIS); iwn_nic_unlock(sc); @@ -5013,6 +5197,7 @@ iwn_apm_stop_master(struct iwn_softc *sc) { int ntries; + /* Stop busmaster DMA activity. */ IWN_SETBITS(sc, IWN_RESET, IWN_RESET_STOP_MASTER); for (ntries = 0; ntries < 100; ntries++) { if (IWN_READ(sc, IWN_RESET) & IWN_RESET_MASTER_DISABLED) @@ -5027,6 +5212,7 @@ iwn_apm_stop(struct iwn_softc *sc) { iwn_apm_stop_master(sc); + /* Reset the entire device. */ IWN_SETBITS(sc, IWN_RESET, IWN_RESET_SW); DELAY(10); /* Clear "initialization complete" bit. */ @@ -5036,16 +5222,6 @@ iwn_apm_stop(struct iwn_softc *sc) int iwn4965_nic_config(struct iwn_softc *sc) { - pcireg_t reg; - - /* Retrieve PCIe Active State Power Management (ASPM). */ - reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, - sc->sc_cap_off + PCI_PCIE_LCSR); - if (reg & PCI_PCIE_LCSR_ASPM_L1) /* L1 Entry enabled. */ - IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); - else - IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); - if (IWN_RFCFG_TYPE(sc->rfcfg) == 1) { /* * I don't believe this to be correct but this is what the @@ -5065,17 +5241,10 @@ iwn4965_nic_config(struct iwn_softc *sc) int iwn5000_nic_config(struct iwn_softc *sc) { - pcireg_t reg; + pci_product_id_t id; + uint32_t tmp; int error; - /* Retrieve PCIe Active State Power Management (ASPM). */ - reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, - sc->sc_cap_off + PCI_PCIE_LCSR); - if (reg & PCI_PCIE_LCSR_ASPM_L1) /* L1 Entry enabled. */ - IWN_SETBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); - else - IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); - if (IWN_RFCFG_TYPE(sc->rfcfg) < 3) { IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_RFCFG_TYPE(sc->rfcfg) | @@ -5088,7 +5257,30 @@ iwn5000_nic_config(struct iwn_softc *sc) if ((error = iwn_nic_lock(sc)) != 0) return error; iwn_prph_setbits(sc, IWN_APMG_PS, IWN_APMG_PS_EARLY_PWROFF_DIS); + + if (sc->hw_type == IWN_HW_REV_TYPE_1000) { + /* + * Select first Switching Voltage Regulator (1.32V) to + * solve a stability issue related to noisy DC2DC line + * in the silicon of 1000 Series. + */ + tmp = iwn_prph_read(sc, IWN_APMG_DIGITAL_SVR); + tmp &= ~IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK; + tmp |= IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32; + iwn_prph_write(sc, IWN_APMG_DIGITAL_SVR, tmp); + } iwn_nic_unlock(sc); + + id = PCI_PRODUCT(sc->sc_id); + if (id == PCI_PRODUCT_INTEL_WIFI_LINK_6000_1 || + id == PCI_PRODUCT_INTEL_WIFI_LINK_6000_2 || + id == PCI_PRODUCT_INTEL_WIFI_LINK_6000_7) { + /* Use internal and external power amplifiers. */ + IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_HYB); + } else if (id == PCI_PRODUCT_INTEL_WIFI_LINK_6000_5) { + /* Use internal power amplifier only. */ + IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA); + } return 0; } @@ -5100,6 +5292,16 @@ iwn_hw_prepare(struct iwn_softc *sc) { int ntries; + /* Check if hardware is ready. */ + IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); + for (ntries = 0; ntries < 5; ntries++) { + if (IWN_READ(sc, IWN_HW_IF_CONFIG) & + IWN_HW_IF_CONFIG_NIC_READY) + return 0; + DELAY(10); + } + + /* Hardware not ready, force into ready state. */ IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_PREPARE); for (ntries = 0; ntries < 15000; ntries++) { if (!(IWN_READ(sc, IWN_HW_IF_CONFIG) & @@ -5110,6 +5312,7 @@ iwn_hw_prepare(struct iwn_softc *sc) if (ntries == 15000) return ETIMEDOUT; + /* Hardware should be ready now. */ IWN_SETBITS(sc, IWN_HW_IF_CONFIG, IWN_HW_IF_CONFIG_NIC_READY); for (ntries = 0; ntries < 5; ntries++) { if (IWN_READ(sc, IWN_HW_IF_CONFIG) & @@ -5129,7 +5332,7 @@ iwn_hw_init(struct iwn_softc *sc) /* Clear pending interrupts. */ IWN_WRITE(sc, IWN_INT, 0xffffffff); - if ((error = hal->apm_init(sc)) != 0) { + if ((error = iwn_apm_init(sc)) != 0) { printf("%s: could not power ON adapter\n", sc->sc_dev.dv_xname); return error; @@ -5200,7 +5403,7 @@ iwn_hw_init(struct iwn_softc *sc) /* Enable interrupt coalescing. */ IWN_WRITE(sc, IWN_INT_COALESCING, 512 / 8); /* Enable interrupts. */ - IWN_WRITE(sc, IWN_MASK, IWN_INT_MASK); + IWN_WRITE(sc, IWN_INT_MASK, sc->int_mask); /* _Really_ make sure "radio off" bit is cleared! */ IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); @@ -5230,9 +5433,10 @@ iwn_hw_stop(struct iwn_softc *sc) IWN_WRITE(sc, IWN_RESET, IWN_RESET_NEVO); /* Disable interrupts. */ - IWN_WRITE(sc, IWN_MASK, 0); + IWN_WRITE(sc, IWN_INT_MASK, 0); IWN_WRITE(sc, IWN_INT, 0xffffffff); IWN_WRITE(sc, IWN_FH_INT, 0xffffffff); + sc->sc_flags &= ~IWN_FLAG_USE_ICT; /* Make sure we no longer hold the NIC lock. */ iwn_nic_unlock(sc); @@ -5263,7 +5467,8 @@ iwn_hw_stop(struct iwn_softc *sc) iwn_reset_tx_ring(sc, &sc->txq[qid]); if (iwn_nic_lock(sc) == 0) { - iwn_prph_write(sc, IWN_APMG_CLK_DIS, IWN_APMG_CLK_DMA_RQT); + iwn_prph_write(sc, IWN_APMG_CLK_DIS, + IWN_APMG_CLK_CTRL_DMA_CLK_RQT); iwn_nic_unlock(sc); } DELAY(5); @@ -5297,6 +5502,10 @@ iwn_init(struct ifnet *ifp) goto fail; } + /* Initialize interrupt mask to default value. */ + sc->int_mask = IWN_INT_MASK_DEF; + sc->sc_flags &= ~IWN_FLAG_USE_ICT; + /* Initialize hardware and upload firmware. */ error = iwn_hw_init(sc); free(sc->fw.data, M_DEVBUF); diff --git a/sys/dev/pci/if_iwnreg.h b/sys/dev/pci/if_iwnreg.h index 8a746917be6..10c3de89065 100644 --- a/sys/dev/pci/if_iwnreg.h +++ b/sys/dev/pci/if_iwnreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwnreg.h,v 1.26 2009/05/29 08:25:45 damien Exp $ */ +/* $OpenBSD: if_iwnreg.h,v 1.27 2009/10/24 18:14:57 damien Exp $ */ /*- * Copyright (c) 2007, 2008 @@ -31,6 +31,9 @@ #define IWN_SRVC_DMACHNL 9 +#define IWN_ICT_SIZE 4096 +#define IWN_ICT_COUNT (IWN_ICT_SIZE / sizeof (uint32_t)) + /* Maximum number of DMA segments for TX. */ #define IWN_MAX_SCATTER 20 @@ -54,8 +57,9 @@ */ #define IWN_HW_IF_CONFIG 0x000 #define IWN_INT_COALESCING 0x004 +#define IWN_INT_PERIODIC 0x005 /* XXX fixme */ #define IWN_INT 0x008 -#define IWN_MASK 0x00c +#define IWN_INT_MASK 0x00c #define IWN_FH_INT 0x010 #define IWN_RESET 0x020 #define IWN_GP_CNTRL 0x024 @@ -64,11 +68,15 @@ #define IWN_EEPROM_GP 0x030 #define IWN_OTP_GP 0x034 #define IWN_GIO 0x03c +#define IWN_GP_DRIVER 0x050 #define IWN_UCODE_GP1_CLR 0x05c #define IWN_LED 0x094 +#define IWN_DRAM_INT_TBL 0x0a0 #define IWN_GIO_CHICKEN 0x100 #define IWN_ANA_PLL 0x20c +#define IWN_HW_REV_WA 0x22c #define IWN_DBG_HPET_MEM 0x240 +#define IWN_DBG_LINK_PWR_MGMT 0x250 #define IWN_MEM_RADDR 0x40c #define IWN_MEM_WADDR 0x410 #define IWN_MEM_WDATA 0x418 @@ -131,10 +139,12 @@ /* * NIC internal memory offsets. */ -#define IWN_CLOCK_CTL 0x3000 -#define IWN_APMG_CLK_CTRL 0x3004 +#define IWN_APMG_CLK_CTRL 0x3000 +#define IWN_APMG_CLK_EN 0x3004 #define IWN_APMG_CLK_DIS 0x3008 #define IWN_APMG_PS 0x300c +#define IWN_APMG_DIGITAL_SVR 0x3058 +#define IWN_APMG_ANALOG_SVR 0x306c #define IWN_APMG_PCI_STT 0x3010 #define IWN_BSM_WR_CTRL 0x3400 #define IWN_BSM_WR_MEM_SRC 0x3404 @@ -146,9 +156,6 @@ #define IWN_BSM_DRAM_DATA_SIZE 0x349c #define IWN_BSM_SRAM_BASE 0x3800 -/* Possible values for IWN_APMG_CLK_DIS. */ -#define IWN_APMG_CLK_DMA_RQT (1 << 9) - /* Possible flags for register IWN_HW_IF_CONFIG. */ #define IWN_HW_IF_CONFIG_4965_R (1 << 4) #define IWN_HW_IF_CONFIG_MAC_SI (1 << 8) @@ -159,6 +166,10 @@ #define IWN_HW_IF_CONFIG_PREPARE_DONE (1 << 25) #define IWN_HW_IF_CONFIG_PREPARE (1 << 27) +/* Possible values for register IWN_INT_PERIODIC. */ +#define IWN_INT_PERIODIC_DIS 0x00 +#define IWN_INT_PERIODIC_ENA 0xff + /* Possible flags for registers IWN_PRPH_RADDR/IWN_PRPH_WADDR. */ #define IWN_PRPH_DWORD ((sizeof (uint32_t) - 1) << 24) @@ -171,6 +182,7 @@ #define IWN_RESET_SW (1 << 7) #define IWN_RESET_MASTER_DISABLED (1 << 8) #define IWN_RESET_STOP_MASTER (1 << 9) +#define IWN_RESET_LINK_PWR_MGMT_DIS (1 << 31) /* Possible flags for register IWN_GP_CNTRL. */ #define IWN_GP_CNTRL_MAC_ACCESS_ENA (1 << 0) @@ -199,6 +211,11 @@ /* Possible flags for register IWN_GIO. */ #define IWN_GIO_L0S_ENA (1 << 1) +/* Possible flags for register IWN_GP_DRIVER. */ +#define IWN_GP_DRIVER_RADIO_3X3_HYB (0 << 0) +#define IWN_GP_DRIVER_RADIO_2X2_HYB (1 << 0) +#define IWN_GP_DRIVER_RADIO_2X2_IPA (2 << 0) + /* Possible flags for register IWN_UCODE_GP1_CLR. */ #define IWN_UCODE_GP1_RFKILL (1 << 1) #define IWN_UCODE_GP1_CMD_BLOCKED (1 << 2) @@ -209,6 +226,10 @@ #define IWN_LED_OFF 0x00000038 #define IWN_LED_ON 0x00000078 +/* Possible flags for register IWN_DRAM_INT_TBL. */ +#define IWN_DRAM_INT_TBL_WRAP_CHECK (1 << 27) +#define IWN_DRAM_INT_TBL_ENABLE (1 << 31) + /* Possible values for register IWN_ANA_PLL. */ #define IWN_ANA_PLL_INIT 0x00880300 @@ -226,12 +247,14 @@ #define IWN_INT_CT_REACHED (1 << 6) #define IWN_INT_RF_TOGGLED (1 << 7) #define IWN_INT_SW_ERR (1 << 25) +#define IWN_INT_SCHED (1 << 26) #define IWN_INT_FH_TX (1 << 27) +#define IWN_INT_RX_PERIODIC (1 << 28) #define IWN_INT_HW_ERR (1 << 29) #define IWN_INT_FH_RX (1 << 31) /* Shortcut. */ -#define IWN_INT_MASK \ +#define IWN_INT_MASK_DEF \ (IWN_INT_SW_ERR | IWN_INT_HW_ERR | IWN_INT_FH_TX | \ IWN_INT_FH_RX | IWN_INT_ALIVE | IWN_INT_WAKEUP | \ IWN_INT_SW_RX | IWN_INT_CT_REACHED | IWN_INT_RF_TOGGLED) @@ -298,7 +321,7 @@ #define IWN5000_TXQ_STATUS_INACTIVE 0x00ff0010 #define IWN5000_TXQ_STATUS_CHGACT (1 << 19) -/* Possible flags for register IWN_APMG_CLK_CTRL. */ +/* Possible flags for registers IWN_APMG_CLK_*. */ #define IWN_APMG_CLK_CTRL_DMA_CLK_RQT (1 << 9) #define IWN_APMG_CLK_CTRL_BSM_CLK_RQT (1 << 11) @@ -310,6 +333,13 @@ #define IWN_APMG_PS_PWR_SRC_MASK IWN_APMG_PS_PWR_SRC(3) #define IWN_APMG_PS_RESET_REQ (1 << 26) +/* Possible flags for register IWN_APMG_DIGITAL_SVR. */ +#define IWN_APMG_DIGITAL_SVR_VOLTAGE(x) (((x) & 0xf) << 5) +#define IWN_APMG_DIGITAL_SVR_VOLTAGE_MASK \ + IWN_APMG_DIGITAL_SVR_VOLTAGE(0xf) +#define IWN_APMG_DIGITAL_SVR_VOLTAGE_1_32 \ + IWN_APMG_DIGITAL_SVR_VOLTAGE(3) + /* Possible flags for IWN_APMG_PCI_STT. */ #define IWN_APMG_PCI_STT_L1A_DIS (1 << 11) @@ -379,8 +409,8 @@ struct iwn_rx_desc { struct iwn_tx_cmd { uint8_t code; -#define IWN_CMD_CONFIGURE 16 -#define IWN_CMD_ASSOCIATE 17 +#define IWN_CMD_RXON 16 +#define IWN_CMD_RXON_ASSOC 17 #define IWN_CMD_EDCA_PARAMS 19 #define IWN_CMD_TIMING 20 #define IWN_CMD_ADD_NODE 24 @@ -391,8 +421,9 @@ struct iwn_tx_cmd { #define IWN5000_CMD_CALIB_CONFIG 101 #define IWN_CMD_SET_POWER_MODE 119 #define IWN_CMD_SCAN 128 +#define IWN_CMD_TXPOWER_DBM 149 #define IWN_CMD_TXPOWER 151 -#define IWN_CMD_TXPOWER_DBM 152 +#define IWN5000_CMD_TX_ANT_CONFIG 152 #define IWN_CMD_BT_COEX 155 #define IWN_CMD_GET_STATISTICS 156 #define IWN_CMD_SET_CRITICAL_TEMP 164 @@ -412,7 +443,7 @@ struct iwn_tx_cmd { /* Shortcut. */ #define IWN_ANT_ABC (IWN_ANT_A | IWN_ANT_B | IWN_ANT_C) -/* Structure for command IWN_CMD_CONFIGURE. */ +/* Structure for command IWN_CMD_RXON. */ struct iwn_rxon { uint8_t myaddr[IEEE80211_ADDR_LEN]; uint16_t reserved1; @@ -463,7 +494,7 @@ struct iwn_rxon { uint8_t reserved4; uint8_t ht_single_mask; uint8_t ht_dual_mask; - /* The following fields are for 5000 Series only. */ + /* The following fields are for >=5000 Series only. */ uint8_t ht_triple_mask; uint8_t reserved5; uint16_t acquisition; @@ -789,11 +820,20 @@ struct iwn5000_cmd_txpower { /* Structure for command IWN_CMD_BLUETOOTH. */ struct iwn_bluetooth { uint8_t flags; - uint8_t lead; - uint8_t kill; +#define IWN_BT_COEX_DISABLE 0 +#define IWN_BT_COEX_MODE_2WIRE 1 +#define IWN_BT_COEX_MODE_3WIRE 2 +#define IWN_BT_COEX_MODE_4WIRE 3 + + uint8_t lead_time; +#define IWN_BT_LEAD_TIME_DEF 30 + + uint8_t max_kill; +#define IWN_BT_MAX_KILL_DEF 5 + uint8_t reserved; - uint32_t ack; - uint32_t cts; + uint32_t kill_ack; + uint32_t kill_cts; } __packed; /* Structure for command IWN_CMD_SET_CRITICAL_TEMP. */ @@ -1185,16 +1225,6 @@ struct iwn_fw_dump { uint32_t time[2]; } __packed; -/* Firmware image file header. */ -struct iwn_firmware_hdr { - uint32_t version; - uint32_t main_textsz; - uint32_t main_datasz; - uint32_t init_textsz; - uint32_t init_datasz; - uint32_t boot_textsz; -} __packed; - #define IWN4965_FW_TEXT_MAXSZ ( 96 * 1024) #define IWN4965_FW_DATA_MAXSZ ( 40 * 1024) #define IWN5000_FW_TEXT_MAXSZ (256 * 1024) @@ -1203,6 +1233,8 @@ struct iwn_firmware_hdr { #define IWN4965_FWSZ (IWN4965_FW_TEXT_MAXSZ + IWN4965_FW_DATA_MAXSZ) #define IWN5000_FWSZ IWN5000_FW_TEXT_MAXSZ +#define IWN_FW_API(x) (((x) >> 8) & 0xff) + /* * Offsets into EEPROM. */ @@ -1230,6 +1262,7 @@ struct iwn_firmware_hdr { #define IWN5000_EEPROM_BAND5 0x03a #define IWN5000_EEPROM_BAND6 0x041 #define IWN5000_EEPROM_BAND7 0x049 +#define IWN6000_EEPROM_ENHINFO 0x054 #define IWN5000_EEPROM_CRYSTAL 0x128 #define IWN5000_EEPROM_TEMP 0x12a #define IWN5000_EEPROM_VOLT 0x12b @@ -1251,6 +1284,14 @@ struct iwn_eeprom_chan { int8_t maxpwr; } __packed; +struct iwn_eeprom_enhinfo { + uint16_t reserved1; + int8_t chain[3]; /* max power in half-dBm */ + uint8_t reserved2; + int8_t mimo2; /* max power in half-dBm */ + int8_t mimo3; /* max power in half-dBm */ +} __packed; + #define IWN_NSAMPLES 3 struct iwn4965_eeprom_chan_samples { uint8_t num; @@ -1311,6 +1352,10 @@ static const struct iwn_chan_band { { 11, { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 } } }; +#define IWN1000_OTP_NBLOCKS 3 +#define IWN6000_OTP_NBLOCKS 4 +#define IWN6050_OTP_NBLOCKS 7 + /* HW rate indices. */ #define IWN_RIDX_CCK1 0 #define IWN_RIDX_OFDM6 4 diff --git a/sys/dev/pci/if_iwnvar.h b/sys/dev/pci/if_iwnvar.h index d321892c364..17a28331704 100644 --- a/sys/dev/pci/if_iwnvar.h +++ b/sys/dev/pci/if_iwnvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwnvar.h,v 1.12 2009/05/29 08:25:45 damien Exp $ */ +/* $OpenBSD: if_iwnvar.h,v 1.13 2009/10/24 18:14:57 damien Exp $ */ /*- * Copyright (c) 2007, 2008 @@ -159,7 +159,6 @@ struct iwn_hal { int (*load_firmware)(struct iwn_softc *); void (*read_eeprom)(struct iwn_softc *); int (*post_alive)(struct iwn_softc *); - int (*apm_init)(struct iwn_softc *); int (*nic_config)(struct iwn_softc *); void (*update_sched)(struct iwn_softc *, int, int, uint8_t, uint16_t); @@ -201,11 +200,13 @@ struct iwn_softc { uint8_t fixed_ridx; bus_dma_tag_t sc_dmat; + pcireg_t sc_id; u_int sc_flags; #define IWN_FLAG_HAS_5GHZ (1 << 0) #define IWN_FLAG_HAS_OTPROM (1 << 1) -#define IWN_FLAG_FIRST_BOOT (1 << 2) +#define IWN_FLAG_CALIB_DONE (1 << 2) +#define IWN_FLAG_USE_ICT (1 << 3) uint8_t hw_type; const struct iwn_hal *sc_hal; @@ -222,6 +223,11 @@ struct iwn_softc { /* Firmware DMA transfer. */ struct iwn_dma_info fw_dma; + /* ICT table. */ + struct iwn_dma_info ict_dma; + uint32_t *ict; + int ict_cur; + /* TX/RX rings. */ struct iwn_tx_ring txq[IWN5000_NTXQUEUES]; struct iwn_rx_ring rxq; @@ -253,6 +259,7 @@ struct iwn_softc { int noise; uint32_t qfullmsk; + uint32_t prom_base; struct iwn4965_eeprom_band bands[IWN_NBANDS]; uint16_t rfcfg; @@ -264,11 +271,13 @@ struct iwn_softc { int8_t maxpwr[IEEE80211_CHAN_MAX]; uint32_t critical_temp; + int32_t temp_off; + uint32_t int_mask; uint8_t ntxchains; uint8_t nrxchains; - uint8_t txantmsk; - uint8_t rxantmsk; - uint8_t antmsk; + uint8_t txchainmask; + uint8_t rxchainmask; + uint8_t chainmask; int sc_tx_timer; void *powerhook; |