diff options
author | 2016-05-28 08:13:16 +0000 | |
---|---|---|
committer | 2016-05-28 08:13:16 +0000 | |
commit | 0e4c2833224af80a8de229a9194714ebad9d6143 (patch) | |
tree | 3189aed54e8b94a239166c3f123fd7219cdee366 | |
parent | Fix file block size rounding and ensure it's large enough to store a (diff) | |
download | wireguard-openbsd-0e4c2833224af80a8de229a9194714ebad9d6143.tar.xz wireguard-openbsd-0e4c2833224af80a8de229a9194714ebad9d6143.zip |
Add support for Intel Wireless 8260 devices to iwm(4).
Firmware has been available in fw_update(1) for some time (thanks sthen!).
Tested by robert, reyk, Imre Vadasz, Bryan Vyhmeister.
Thank you, Emmanuel Grumbach, for helping me diagnose issues during development.
ok kettenis
-rw-r--r-- | sys/dev/pci/if_iwm.c | 766 | ||||
-rw-r--r-- | sys/dev/pci/if_iwmreg.h | 346 | ||||
-rw-r--r-- | sys/dev/pci/if_iwmvar.h | 12 |
3 files changed, 1069 insertions, 55 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c index 177f22f626b..e009641cbf7 100644 --- a/sys/dev/pci/if_iwm.c +++ b/sys/dev/pci/if_iwm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwm.c,v 1.82 2016/05/25 13:35:12 stsp Exp $ */ +/* $OpenBSD: if_iwm.c,v 1.83 2016/05/28 08:13:16 stsp Exp $ */ /* * Copyright (c) 2014, 2016 genua gmbh <info@genua.de> @@ -168,6 +168,16 @@ const uint8_t iwm_nvm_channels[] = { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165 }; + +const uint8_t iwm_nvm_channels_8000[] = { + /* 2.4 GHz */ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + /* 5 GHz */ + 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, + 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, + 149, 153, 157, 161, 165, 169, 173, 177, 181 +}; + #define IWM_NUM_2GHZ_CHANNELS 14 const struct iwm_rate { @@ -319,6 +329,8 @@ int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, const uint16_t *, const uint16_t *, const uint16_t *, const uint16_t *, const uint16_t *); +void iwm_set_hw_address_8000(struct iwm_softc *, struct iwm_nvm_data *, + const uint16_t *, const uint16_t *); int iwm_parse_nvm_sections(struct iwm_softc *, struct iwm_nvm_section *); int iwm_nvm_init(struct iwm_softc *); int iwm_firmware_load_sect(struct iwm_softc *, uint32_t, const uint8_t *, @@ -326,6 +338,9 @@ int iwm_firmware_load_sect(struct iwm_softc *, uint32_t, const uint8_t *, int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, const uint8_t *, uint32_t); int iwm_load_firmware_7000(struct iwm_softc *, enum iwm_ucode_type); +int iwm_load_cpu_sections_8000(struct iwm_softc *, struct iwm_fw_sects *, + int , int *); +int iwm_load_firmware_8000(struct iwm_softc *, enum iwm_ucode_type); int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); @@ -408,6 +423,8 @@ uint8_t iwm_mvm_lmac_scan_fill_channels(struct iwm_softc *, struct iwm_scan_channel_cfg_lmac *, int); int iwm_mvm_fill_probe_req(struct iwm_softc *, struct iwm_scan_probe_req *); int iwm_mvm_lmac_scan(struct iwm_softc *); +int iwm_mvm_config_umac_scan(struct iwm_softc *); +int iwm_mvm_umac_scan(struct iwm_softc *); void iwm_mvm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, int *); void iwm_mvm_mac_ctxt_cmd_common(struct iwm_softc *, struct iwm_node *, struct iwm_mac_ctx_cmd *, uint32_t); @@ -445,6 +462,7 @@ int iwm_ioctl(struct ifnet *, u_long, caddr_t); #ifdef IWM_DEBUG const char *iwm_desc_lookup(uint32_t); void iwm_nic_error(struct iwm_softc *); +void iwm_nic_umac_error(struct iwm_softc *); #endif void iwm_notif_intr(struct iwm_softc *); int iwm_intr(void *); @@ -670,18 +688,19 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) tlv_data, tlv_len)) != 0) goto parse_out; break; - case IWM_UCODE_TLV_NUM_OF_CPU: + case IWM_UCODE_TLV_NUM_OF_CPU: { + uint32_t num_cpu; if (tlv_len != sizeof(uint32_t)) { error = EINVAL; goto parse_out; } - if (le32toh(*(uint32_t*)tlv_data) != 1) { - DPRINTF(("%s: driver supports " - "only TLV_NUM_OF_CPU == 1", DEVNAME(sc))); + num_cpu = le32toh(*(uint32_t *)tlv_data); + if (num_cpu < 1 || num_cpu > 2) { error = EINVAL; goto parse_out; } break; + } case IWM_UCODE_TLV_SEC_RT: if ((error = iwm_firmware_store_section(sc, IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len)) != 0) @@ -912,6 +931,9 @@ iwm_nic_lock(struct iwm_softc *sc) IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) + DELAY(2); + if (iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY @@ -919,6 +941,7 @@ iwm_nic_lock(struct iwm_softc *sc) rv = 1; } else { /* jolt */ + DPRINTF(("%s: resetting device via NMI\n", __func__)); IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_FORCE_NMI); } @@ -1469,8 +1492,9 @@ iwm_apm_init(struct iwm_softc *sc) DPRINTF(("iwm apm start\n")); /* Disable L0S exit timer (platform NMI Work/Around) */ - IWM_SETBITS(sc, IWM_CSR_GIO_CHICKEN_BITS, - IWM_CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) + IWM_SETBITS(sc, IWM_CSR_GIO_CHICKEN_BITS, + IWM_CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); /* * Disable L0s without affecting L1; @@ -1491,7 +1515,7 @@ iwm_apm_init(struct iwm_softc *sc) iwm_apm_config(sc); -#if 0 /* not for 7k */ +#if 0 /* not for 7k/8k */ /* Configure analog phase-lock-loop before activating to D0A */ if (trans->cfg->base_params->pll_cfg_val) IWM_SETBITS(trans, IWM_CSR_ANA_PLL_CFG, @@ -1547,18 +1571,19 @@ iwm_apm_init(struct iwm_softc *sc) * do not disable clocks. This preserves any hardware bits already * set by default in "CLK_CTRL_REG" after reset. */ - iwm_write_prph(sc, IWM_APMG_CLK_EN_REG, IWM_APMG_CLK_VAL_DMA_CLK_RQT); - //kpause("iwmapm", 0, mstohz(20), NULL); - DELAY(20); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { + iwm_write_prph(sc, IWM_APMG_CLK_EN_REG, + IWM_APMG_CLK_VAL_DMA_CLK_RQT); + DELAY(20); - /* Disable L1-Active */ - iwm_set_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, - IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - - /* Clear the interrupt in APMG if the NIC is in RFKILL */ - iwm_write_prph(sc, IWM_APMG_RTC_INT_STT_REG, - IWM_APMG_RTC_INT_STT_RFKILL); + /* Disable L1-Active */ + iwm_set_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, + IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + /* Clear the interrupt in APMG if the NIC is in RFKILL */ + iwm_write_prph(sc, IWM_APMG_RTC_INT_STT_REG, + IWM_APMG_RTC_INT_STT_RFKILL); + } out: if (error) printf("%s: apm init error %d\n", DEVNAME(sc), error); @@ -1711,9 +1736,10 @@ iwm_mvm_nic_config(struct iwm_softc *sc) * (PCIe power is lost before PERST# is asserted), causing ME FW * to lose ownership and not being able to obtain it back. */ - iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG, - IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, - ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) + iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG, + IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, + ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); } int @@ -1809,7 +1835,8 @@ iwm_nic_init(struct iwm_softc *sc) int error; iwm_apm_init(sc); - iwm_set_pwr(sc); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) + iwm_set_pwr(sc); iwm_mvm_nic_config(sc); @@ -1960,8 +1987,9 @@ iwm_post_alive(struct iwm_softc *sc) IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); /* Enable L1-Active */ - iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, - IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) + iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, + IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); out: iwm_nic_unlock(sc); @@ -2370,6 +2398,9 @@ const int nvm_to_read[] = { IWM_NVM_SECTION_TYPE_REGULATORY, IWM_NVM_SECTION_TYPE_CALIBRATION, IWM_NVM_SECTION_TYPE_PRODUCTION, + IWM_NVM_SECTION_TYPE_HW_8000, + IWM_NVM_SECTION_TYPE_MAC_OVERRIDE, + IWM_NVM_SECTION_TYPE_PHY_SKU, }; /* Default NVM size to read */ @@ -2545,6 +2576,32 @@ enum iwm_nvm_offsets { IWM_XTAL_CALIB = 0x316 - IWM_NVM_CALIB_SECTION }; +enum iwm_8000_nvm_offsets { + /* NVM HW-Section offset (in words) definitions */ + IWM_HW_ADDR0_WFPM_8000 = 0x12, + IWM_HW_ADDR1_WFPM_8000 = 0x16, + IWM_HW_ADDR0_PCIE_8000 = 0x8A, + IWM_HW_ADDR1_PCIE_8000 = 0x8E, + IWM_MAC_ADDRESS_OVERRIDE_8000 = 1, + + /* NVM SW-Section offset (in words) definitions */ + IWM_NVM_SW_SECTION_8000 = 0x1C0, + IWM_NVM_VERSION_8000 = 0, + IWM_RADIO_CFG_8000 = 0, + IWM_SKU_8000 = 2, + IWM_N_HW_ADDRS_8000 = 3, + + /* NVM REGULATORY -Section offset (in words) definitions */ + IWM_NVM_CHANNELS_8000 = 0, + IWM_NVM_LAR_OFFSET_8000_OLD = 0x4C7, + IWM_NVM_LAR_OFFSET_8000 = 0x507, + IWM_NVM_LAR_ENABLED_8000 = 0x7, + + /* NVM calibration section offset (in words) definitions */ + IWM_NVM_CALIB_SECTION_8000 = 0x2B8, + IWM_XTAL_CALIB_8000 = 0x316 - IWM_NVM_CALIB_SECTION_8000 +}; + /* SKU Capabilities (actual values from NVM definition) */ enum nvm_sku_bits { IWM_NVM_SKU_CAP_BAND_24GHZ = (1 << 0), @@ -2562,6 +2619,13 @@ enum nvm_sku_bits { #define IWM_NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ #define IWM_NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ +#define IWM_NVM_RF_CFG_PNUM_MSK_8000(x) (x & 0xF) +#define IWM_NVM_RF_CFG_DASH_MSK_8000(x) ((x >> 4) & 0xF) +#define IWM_NVM_RF_CFG_STEP_MSK_8000(x) ((x >> 8) & 0xF) +#define IWM_NVM_RF_CFG_TYPE_MSK_8000(x) ((x >> 12) & 0xFFF) +#define IWM_NVM_RF_CFG_TX_ANT_MSK_8000(x) ((x >> 24) & 0xF) +#define IWM_NVM_RF_CFG_RX_ANT_MSK_8000(x) ((x >> 28) & 0xF) + #define DEFAULT_MAX_TX_POWER 16 /** @@ -2800,6 +2864,66 @@ iwm_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, task_add(systq, &sc->ba_task); } +void +iwm_set_hw_address_8000(struct iwm_softc *sc, struct iwm_nvm_data *data, + const uint16_t *mac_override, const uint16_t *nvm_hw) +{ + const uint8_t *hw_addr; + + if (mac_override) { + static const uint8_t reserved_mac[] = { + 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00 + }; + + hw_addr = (const uint8_t *)(mac_override + + IWM_MAC_ADDRESS_OVERRIDE_8000); + + /* + * Store the MAC address from MAO section. + * No byte swapping is required in MAO section + */ + memcpy(data->hw_addr, hw_addr, ETHER_ADDR_LEN); + + /* + * Force the use of the OTP MAC address in case of reserved MAC + * address in the NVM, or if address is given but invalid. + */ + if (memcmp(reserved_mac, hw_addr, ETHER_ADDR_LEN) != 0 && + (memcmp(etherbroadcastaddr, data->hw_addr, + sizeof(etherbroadcastaddr)) != 0) && + (memcmp(etheranyaddr, data->hw_addr, + sizeof(etheranyaddr)) != 0) && + !ETHER_IS_MULTICAST(data->hw_addr)) + return; + + DPRINTF(("%s: mac address from nvm override section invalid\n", + DEVNAME(sc))); + } + + if (nvm_hw) { + /* read the mac address from WFMP registers */ + uint32_t mac_addr0 = + htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_0)); + uint32_t mac_addr1 = + htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_1)); + + hw_addr = (const uint8_t *)&mac_addr0; + data->hw_addr[0] = hw_addr[3]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[2] = hw_addr[1]; + data->hw_addr[3] = hw_addr[0]; + + hw_addr = (const uint8_t *)&mac_addr1; + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[5] = hw_addr[0]; + + return; + } + + printf("%s: mac address not found\n", DEVNAME(sc)); + memset(data->hw_addr, 0, sizeof(data->hw_addr)); +} + int iwm_parse_nvm_data(struct iwm_softc *sc, const uint16_t *nvm_hw, const uint16_t *nvm_sw, @@ -2808,20 +2932,33 @@ iwm_parse_nvm_data(struct iwm_softc *sc, { struct iwm_nvm_data *data = &sc->sc_nvm; uint8_t hw_addr[ETHER_ADDR_LEN]; - uint16_t radio_cfg; uint32_t sku; data->nvm_version = le16_to_cpup(nvm_sw + IWM_NVM_VERSION); - radio_cfg = le16_to_cpup(nvm_sw + IWM_RADIO_CFG); - data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg); - data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg); - data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg); - data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg); - data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK(radio_cfg); - data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK(radio_cfg); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { + uint16_t radio_cfg = le16_to_cpup(nvm_sw + IWM_RADIO_CFG); + data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg); + data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg); + data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg); + data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg); + data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK(radio_cfg); + data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK(radio_cfg); + + sku = le16_to_cpup(nvm_sw + IWM_SKU); + } else { + uint32_t radio_cfg = + le32_to_cpup((uint32_t *)(phy_sku + IWM_RADIO_CFG_8000)); + data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK_8000(radio_cfg); + data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK_8000(radio_cfg); + data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK_8000(radio_cfg); + data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK_8000(radio_cfg); + data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK_8000(radio_cfg); + data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK_8000(radio_cfg); + + sku = le32_to_cpup((uint32_t *)(phy_sku + IWM_SKU_8000)); + } - sku = le16_to_cpup(nvm_sw + IWM_SKU); data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ; data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ; data->sku_cap_11n_enable = sku & IWM_NVM_SKU_CAP_11N_ENABLE; @@ -2837,16 +2974,23 @@ iwm_parse_nvm_data(struct iwm_softc *sc, data->n_hw_addrs = le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS); /* The byte order is little endian 16 bit, meaning 214365 */ - memcpy(hw_addr, nvm_hw + IWM_HW_ADDR, ETHER_ADDR_LEN); - data->hw_addr[0] = hw_addr[1]; - data->hw_addr[1] = hw_addr[0]; - data->hw_addr[2] = hw_addr[3]; - data->hw_addr[3] = hw_addr[2]; - data->hw_addr[4] = hw_addr[5]; - data->hw_addr[5] = hw_addr[4]; + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { + memcpy(hw_addr, nvm_hw + IWM_HW_ADDR, ETHER_ADDR_LEN); + data->hw_addr[0] = hw_addr[1]; + data->hw_addr[1] = hw_addr[0]; + data->hw_addr[2] = hw_addr[3]; + data->hw_addr[3] = hw_addr[2]; + data->hw_addr[4] = hw_addr[5]; + data->hw_addr[5] = hw_addr[4]; + } else + iwm_set_hw_address_8000(sc, data, mac_override, nvm_hw); - iwm_init_channel_map(sc, &nvm_sw[IWM_NVM_CHANNELS], - iwm_nvm_channels, nitems(iwm_nvm_channels)); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) + iwm_init_channel_map(sc, &nvm_sw[IWM_NVM_CHANNELS], + iwm_nvm_channels, nitems(iwm_nvm_channels)); + else + iwm_init_channel_map(sc, ®ulatory[IWM_NVM_CHANNELS_8000], + iwm_nvm_channels_8000, nitems(iwm_nvm_channels_8000)); data->calib_version = 255; /* TODO: this value will prevent some checks from @@ -2864,14 +3008,51 @@ iwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections) const uint16_t *regulatory = NULL; /* Checking for required sections */ - if (!sections[IWM_NVM_SECTION_TYPE_SW].data || - !sections[IWM_NVM_SECTION_TYPE_HW].data) { - DPRINTF(("%s: Can't parse empty OTP/NVM sections\n", - DEVNAME(sc))); - return ENOENT; + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { + if (!sections[IWM_NVM_SECTION_TYPE_SW].data || + !sections[IWM_NVM_SECTION_TYPE_HW].data) { + DPRINTF(("%s: Can't parse empty OTP/NVM sections\n", + DEVNAME(sc))); + return ENOENT; + } + + hw = (const uint16_t *) sections[IWM_NVM_SECTION_TYPE_HW].data; + } else if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { + /* SW and REGULATORY sections are mandatory */ + if (!sections[IWM_NVM_SECTION_TYPE_SW].data || + !sections[IWM_NVM_SECTION_TYPE_REGULATORY].data) { + DPRINTF(("%s: Can't parse empty OTP/NVM sections\n", + DEVNAME(sc))); + return ENOENT; + } + /* MAC_OVERRIDE or at least HW section must exist */ + if (!sections[IWM_NVM_SECTION_TYPE_HW_8000].data && + !sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data) { + DPRINTF(("%s: Can't parse mac_address, empty " + "sections\n", DEVNAME(sc))); + return ENOENT; + } + + /* PHY_SKU section is mandatory in B0 */ + if (!sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data) { + DPRINTF(("%s: Can't parse phy_sku in B0, empty " + "sections\n", DEVNAME(sc))); + return ENOENT; + } + + regulatory = (const uint16_t *) + sections[IWM_NVM_SECTION_TYPE_REGULATORY].data; + hw = (const uint16_t *) + sections[IWM_NVM_SECTION_TYPE_HW_8000].data; + mac_override = + (const uint16_t *) + sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data; + phy_sku = (const uint16_t *) + sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data; + } else { + panic("unknown device family %d\n", sc->sc_device_family); } - hw = (const uint16_t *) sections[IWM_NVM_SECTION_TYPE_HW].data; sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data; calib = (const uint16_t *) sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data; @@ -3046,11 +3227,116 @@ iwm_load_firmware_7000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) } int +iwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws, + int cpu, int *first_ucode_section) +{ + int shift_param; + int i, error = 0, sec_num = 0x1; + uint32_t val, last_read_idx = 0; + void *data; + uint32_t dlen; + uint32_t offset; + + if (cpu == 1) { + shift_param = 0; + *first_ucode_section = 0; + } else { + shift_param = 16; + (*first_ucode_section)++; + } + + for (i = *first_ucode_section; i < IWM_UCODE_SECT_MAX; i++) { + last_read_idx = i; + data = fws->fw_sect[i].fws_data; + dlen = fws->fw_sect[i].fws_len; + offset = fws->fw_sect[i].fws_devoff; + + /* + * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between + * CPU1 to CPU2. + * PAGING_SEPARATOR_SECTION delimiter - separate between + * CPU2 non paged to CPU2 paging sec. + */ + if (!data || offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || + offset == IWM_PAGING_SEPARATOR_SECTION) + break; + + DPRINTFN(4, ("LOAD FIRMWARE chunk %d offset 0x%x len %d " + "for cpu %d\n", i, offset, dlen, cpu)); + + if (dlen > sc->sc_fwdmasegsz) { + DPRINTF(("chunk %d too large (%d bytes)\n", i, dlen)); + error = EFBIG; + } else + error = iwm_firmware_load_sect(sc, offset, data, dlen); + if (error) { + printf("%s: could not load firmware chunk %d " + "(error %d)\n", DEVNAME(sc), i, error); + return error; + } + + /* Notify the ucode of the loaded section number and status */ + if (iwm_nic_lock(sc)) { + val = IWM_READ(sc, IWM_FH_UCODE_LOAD_STATUS); + val = val | (sec_num << shift_param); + IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, val); + sec_num = (sec_num << 1) | 0x1; + iwm_nic_unlock(sc); + + /* + * The firmware won't load correctly without this delay. + */ + DELAY(8000); + } + } + + *first_ucode_section = last_read_idx; + + if (iwm_nic_lock(sc)) { + if (cpu == 1) + IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFF); + else + IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFFFFFF); + iwm_nic_unlock(sc); + } + + return 0; +} + +int +iwm_load_firmware_8000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +{ + struct iwm_fw_sects *fws; + int error = 0; + int first_ucode_section; + + DPRINTF(("%s: loading ucode type %d\n", DEVNAME(sc), ucode_type)); + + fws = &sc->sc_fw.fw_sects[ucode_type]; + + /* configure the ucode to be ready to get the secured image */ + /* release CPU reset */ + iwm_write_prph(sc, IWM_RELEASE_CPU_RESET, IWM_RELEASE_CPU_RESET_BIT); + + /* load to FW the binary Secured sections of CPU1 */ + error = iwm_load_cpu_sections_8000(sc, fws, 1, &first_ucode_section); + if (error) + return error; + + /* load to FW the binary sections of CPU2 */ + return iwm_load_cpu_sections_8000(sc, fws, 2, &first_ucode_section); +} + +int iwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) { int error, w; - error = iwm_load_firmware_7000(sc, ucode_type); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) + error = iwm_load_firmware_8000(sc, ucode_type); + else + error = iwm_load_firmware_7000(sc, ucode_type); + if (error) return error; @@ -3060,6 +3346,12 @@ iwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) } if (error || !sc->sc_uc.uc_ok) { printf("%s: could not load firmware\n", DEVNAME(sc)); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { + DPRINTF(("%s: cpu1 status: 0x%x\n", DEVNAME(sc), + iwm_read_prph(sc, IWM_SB_CPU_1_STATUS))); + DPRINTF(("%s: cpu2 status: 0x%x\n", DEVNAME(sc), + iwm_read_prph(sc, IWM_SB_CPU_2_STATUS))); + } } /* @@ -4864,6 +5156,35 @@ iwm_mvm_lmac_scan_fill_channels(struct iwm_softc *sc, return nchan; } +uint8_t +iwm_mvm_umac_scan_fill_channels(struct iwm_softc *sc, + struct iwm_scan_channel_cfg_umac *chan, int n_ssids) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *c; + uint8_t nchan; + + for (nchan = 0, c = &ic->ic_channels[1]; + c <= &ic->ic_channels[IEEE80211_CHAN_MAX] && + nchan < sc->sc_capa_n_scan_channels; + c++) { + if (c->ic_flags == 0) + continue; + + chan->channel_num = ieee80211_mhz2ieee(c->ic_freq, 0); + chan->iter_count = 1; + chan->iter_interval = htole16(0); +#if 0 /* makes scanning while associated less useful */ + if (n_ssids != 0) + chan->flags = htole32(1 << 0); /* select SSID 0 */ +#endif + chan++; + nchan++; + } + + return nchan; +} + int iwm_mvm_fill_probe_req(struct iwm_softc *sc, struct iwm_scan_probe_req *preq) { @@ -5067,6 +5388,172 @@ iwm_mvm_lmac_scan(struct iwm_softc *sc) return ret; } +int +iwm_mvm_config_umac_scan(struct iwm_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct iwm_scan_config *scan_config; + int ret, nchan; + size_t cmd_size; + struct ieee80211_channel *c; + struct iwm_host_cmd hcmd = { + .id = iwm_cmd_id(IWM_SCAN_CFG_CMD, IWM_ALWAYS_LONG_GROUP, 0), + .flags = IWM_CMD_SYNC, + }; + static const uint32_t rates = (IWM_SCAN_CONFIG_RATE_1M | + IWM_SCAN_CONFIG_RATE_2M | IWM_SCAN_CONFIG_RATE_5M | + IWM_SCAN_CONFIG_RATE_11M | IWM_SCAN_CONFIG_RATE_6M | + IWM_SCAN_CONFIG_RATE_9M | IWM_SCAN_CONFIG_RATE_12M | + IWM_SCAN_CONFIG_RATE_18M | IWM_SCAN_CONFIG_RATE_24M | + IWM_SCAN_CONFIG_RATE_36M | IWM_SCAN_CONFIG_RATE_48M | + IWM_SCAN_CONFIG_RATE_54M); + + cmd_size = sizeof(*scan_config) + sc->sc_capa_n_scan_channels; + + scan_config = malloc(cmd_size, M_DEVBUF, M_WAIT | M_CANFAIL | M_ZERO); + if (scan_config == NULL) + return ENOMEM; + + scan_config->tx_chains = htole32(iwm_fw_valid_tx_ant(sc)); + scan_config->rx_chains = htole32(iwm_fw_valid_rx_ant(sc)); + scan_config->legacy_rates = htole32(rates | + IWM_SCAN_CONFIG_SUPPORTED_RATE(rates)); + + /* These timings correspond to iwlwifi's UNASSOC scan. */ + scan_config->dwell_active = 10; + scan_config->dwell_passive = 110; + scan_config->dwell_fragmented = 44; + scan_config->dwell_extended = 90; + scan_config->out_of_channel_time = htole32(0); + scan_config->suspend_time = htole32(0); + + IEEE80211_ADDR_COPY(scan_config->mac_addr, sc->sc_ic.ic_myaddr); + + scan_config->bcast_sta_id = sc->sc_aux_sta.sta_id; + scan_config->channel_flags = IWM_CHANNEL_FLAG_EBS | + IWM_CHANNEL_FLAG_ACCURATE_EBS | IWM_CHANNEL_FLAG_EBS_ADD | + IWM_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE; + + for (c = &ic->ic_channels[1], nchan = 0; + c <= &ic->ic_channels[IEEE80211_CHAN_MAX] && + nchan < sc->sc_capa_n_scan_channels; c++) { + if (c->ic_flags == 0) + continue; + scan_config->channel_array[nchan++] = + ieee80211_mhz2ieee(c->ic_freq, 0); + } + + scan_config->flags = htole32(IWM_SCAN_CONFIG_FLAG_ACTIVATE | + IWM_SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS | + IWM_SCAN_CONFIG_FLAG_SET_TX_CHAINS | + IWM_SCAN_CONFIG_FLAG_SET_RX_CHAINS | + IWM_SCAN_CONFIG_FLAG_SET_AUX_STA_ID | + IWM_SCAN_CONFIG_FLAG_SET_ALL_TIMES | + IWM_SCAN_CONFIG_FLAG_SET_LEGACY_RATES | + IWM_SCAN_CONFIG_FLAG_SET_MAC_ADDR | + IWM_SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS| + IWM_SCAN_CONFIG_N_CHANNELS(nchan) | + IWM_SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED); + + hcmd.data[0] = scan_config; + hcmd.len[0] = cmd_size; + + DPRINTF(("Sending UMAC scan config\n")); + + ret = iwm_send_cmd(sc, &hcmd); + if (!ret) + DPRINTF(("UMAC scan config was sent successfully\n")); + + free(scan_config, M_DEVBUF, cmd_size); + return ret; +} + +int +iwm_mvm_umac_scan(struct iwm_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct iwm_host_cmd hcmd = { + .id = iwm_cmd_id(IWM_SCAN_REQ_UMAC, IWM_ALWAYS_LONG_GROUP, 0), + .len = { 0, }, + .data = { NULL, }, + .flags = IWM_CMD_SYNC, + }; + struct iwm_scan_req_umac *req; + struct iwm_scan_req_umac_tail *tail; + size_t req_len; + int ret; + + req_len = sizeof(struct iwm_scan_req_umac) + + (sizeof(struct iwm_scan_channel_cfg_umac) * + sc->sc_capa_n_scan_channels) + + sizeof(struct iwm_scan_req_umac_tail); + if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE) + return ENOMEM; + req = malloc(req_len, M_DEVBUF, M_WAIT | M_CANFAIL | M_ZERO); + if (req == NULL) + return ENOMEM; + + hcmd.len[0] = (uint16_t)req_len; + hcmd.data[0] = (void *)req; + + DPRINTF(("Handling ieee80211 scan request\n")); + + /* These timings correspond to iwlwifi's UNASSOC scan. */ + req->active_dwell = 10; + req->passive_dwell = 110; + req->fragmented_dwell = 44; + req->extended_dwell = 90; + req->max_out_time = 0; + req->suspend_time = 0; + + req->scan_priority = htole32(IWM_SCAN_PRIORITY_HIGH); + req->ooc_priority = htole32(IWM_SCAN_PRIORITY_HIGH); + + req->n_channels = iwm_mvm_umac_scan_fill_channels(sc, + (struct iwm_scan_channel_cfg_umac *)req->data, + ic->ic_des_esslen != 0); + + req->general_flags = htole32(IWM_UMAC_SCAN_GEN_FLAGS_PASS_ALL | + IWM_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE | + IWM_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL); + + tail = (void *)&req->data + + sizeof(struct iwm_scan_channel_cfg_umac) * + sc->sc_capa_n_scan_channels; + + /* Check if we're doing an active directed scan. */ + if (ic->ic_des_esslen != 0) { + tail->direct_scan[0].id = IEEE80211_ELEMID_SSID; + tail->direct_scan[0].len = ic->ic_des_esslen; + memcpy(tail->direct_scan[0].ssid, ic->ic_des_essid, + ic->ic_des_esslen); + req->general_flags |= + htole32(IWM_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT); + } else + req->general_flags |= htole32(IWM_UMAC_SCAN_GEN_FLAGS_PASSIVE); + + if (isset(sc->sc_enabled_capa, + IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) + req->general_flags |= + htole32(IWM_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED); + + ret = iwm_mvm_fill_probe_req(sc, &tail->preq); + if (ret) { + free(req, M_DEVBUF, req_len); + return ret; + } + + /* Specify the scan plan: We'll do one iteration. */ + tail->schedule[0].interval = 0; + tail->schedule[0].iter_count = 1; + + ret = iwm_send_cmd(sc, &hcmd); + if (!ret) + DPRINTF(("Scan request was sent successfully\n")); + free(req, M_DEVBUF, req_len); + return ret; +} + void iwm_mvm_ack_rates(struct iwm_softc *sc, struct iwm_node *in, int *cck_rates, int *ofdm_rates) @@ -5803,7 +6290,10 @@ iwm_newstate_task(void *psc) if (ic->ic_state == nstate && (sc->sc_flags & IWM_FLAG_SCANNING)) return; - error = iwm_mvm_lmac_scan(sc); + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) + error = iwm_mvm_umac_scan(sc); + else + error = iwm_mvm_lmac_scan(sc); if (error != 0) { printf("%s: could not initiate scan\n", DEVNAME(sc)); return; @@ -5996,6 +6486,9 @@ iwm_mvm_sf_config(struct iwm_softc *sc, enum iwm_sf_state new_state) }; int ret = 0; + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) + sf_cmd.state |= htole32(IWM_SF_CFG_DUMMY_NOTIF_OFF); + switch (new_state) { case IWM_SF_UNINIT: case IWM_SF_INIT_OFF: @@ -6170,7 +6663,8 @@ iwm_init_hw(struct iwm_softc *sc) } /* Initialize tx backoffs to the minimum. */ - iwm_mvm_tt_tx_backoff(sc, 0); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) + iwm_mvm_tt_tx_backoff(sc, 0); error = iwm_mvm_power_update_device(sc); if (error) @@ -6181,6 +6675,11 @@ iwm_init_hw(struct iwm_softc *sc) goto error; } + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) { + if ((error = iwm_mvm_config_umac_scan(sc)) != 0) + goto error; + } + /* Enable Tx queues. */ for (ac = 0; ac < EDCA_NUM_AC; ac++) { error = iwm_enable_txq(sc, IWM_STATION_ID, ac, @@ -6512,11 +7011,81 @@ struct iwm_error_event_table { uint32_t u_timestamp; /* indicate when the date and time of the * compilation */ uint32_t flow_handler; /* FH read/write pointers, RX credit */ +} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */; + +/* + * UMAC error struct - relevant starting from family 8000 chip. + * Note: This structure is read from the device with IO accesses, + * and the reading already does the endian conversion. As it is + * read with u32-sized accesses, any members with a different size + * need to be ordered correctly though! + */ +struct iwm_umac_error_event_table { + uint32_t valid; /* (nonzero) valid, (0) log is empty */ + uint32_t error_id; /* type of error */ + uint32_t blink1; /* branch link */ + uint32_t blink2; /* branch link */ + uint32_t ilink1; /* interrupt link */ + uint32_t ilink2; /* interrupt link */ + uint32_t data1; /* error-specific data */ + uint32_t data2; /* error-specific data */ + uint32_t data3; /* error-specific data */ + uint32_t umac_major; + uint32_t umac_minor; + uint32_t frame_pointer; /* core register 27*/ + uint32_t stack_pointer; /* core register 28 */ + uint32_t cmd_header; /* latest host cmd sent to UMAC */ + uint32_t nic_isr_pref; /* ISR status register */ } __packed; #define ERROR_START_OFFSET (1 * sizeof(uint32_t)) #define ERROR_ELEM_SIZE (7 * sizeof(uint32_t)) +void +iwm_nic_umac_error(struct iwm_softc *sc) +{ + struct iwm_umac_error_event_table table; + uint32_t base; + + base = sc->sc_uc.uc_umac_error_event_table; + + if (base < 0x800000) { + printf("%s: Invalid error log pointer 0x%08x\n", + DEVNAME(sc), base); + return; + } + + if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) { + printf("%s: reading errlog failed\n", DEVNAME(sc)); + return; + } + + if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { + printf("%s: Start UMAC Error Log Dump:\n", DEVNAME(sc)); + printf("%s: Status: 0x%x, count: %d\n", DEVNAME(sc), + sc->sc_flags, table.valid); + } + + printf("%s: 0x%08X | %s\n", DEVNAME(sc), table.error_id, + iwm_desc_lookup(table.error_id)); + printf("%s: 0x%08X | umac branchlink1\n", DEVNAME(sc), table.blink1); + printf("%s: 0x%08X | umac branchlink2\n", DEVNAME(sc), table.blink2); + printf("%s: 0x%08X | umac interruptlink1\n", DEVNAME(sc), table.ilink1); + printf("%s: 0x%08X | umac interruptlink2\n", DEVNAME(sc), table.ilink2); + printf("%s: 0x%08X | umac data1\n", DEVNAME(sc), table.data1); + printf("%s: 0x%08X | umac data2\n", DEVNAME(sc), table.data2); + printf("%s: 0x%08X | umac data3\n", DEVNAME(sc), table.data3); + printf("%s: 0x%08X | umac major\n", DEVNAME(sc), table.umac_major); + printf("%s: 0x%08X | umac minor\n", DEVNAME(sc), table.umac_minor); + printf("%s: 0x%08X | frame pointer\n", DEVNAME(sc), + table.frame_pointer); + printf("%s: 0x%08X | stack pointer\n", DEVNAME(sc), + table.stack_pointer); + printf("%s: 0x%08X | last host cmd\n", DEVNAME(sc), table.cmd_header); + printf("%s: 0x%08X | isr status reg\n", DEVNAME(sc), + table.nic_isr_pref); +} + struct { const char *name; uint8_t num; @@ -6629,6 +7198,9 @@ iwm_nic_error(struct iwm_softc *sc) printf("%s: %08X | lmpm_pmg_sel\n", DEVNAME(sc), table.lmpm_pmg_sel); printf("%s: %08X | timestamp\n", DEVNAME(sc), table.u_timestamp); printf("%s: %08X | flow_handler\n", DEVNAME(sc), table.flow_handler); + + if (sc->sc_uc.uc_umac_error_event_table) + iwm_nic_umac_error(sc); } #endif @@ -6733,6 +7305,8 @@ iwm_notif_intr(struct iwm_softc *sc) sc->sc_uc.uc_log_event_table = le32toh(resp2->log_event_table_ptr); sc->sched_base = le32toh(resp2->scd_base_ptr); + sc->sc_uc.uc_umac_error_event_table + = le32toh(resp2->error_info_addr); if (resp2->status == IWM_ALIVE_STATUS_OK) sc->sc_uc.uc_ok = 1; else @@ -6746,6 +7320,8 @@ iwm_notif_intr(struct iwm_softc *sc) sc->sc_uc.uc_log_event_table = le32toh(resp3->log_event_table_ptr); sc->sched_base = le32toh(resp3->scd_base_ptr); + sc->sc_uc.uc_umac_error_event_table + = le32toh(resp3->error_info_addr); if (resp3->status == IWM_ALIVE_STATUS_OK) sc->sc_uc.uc_ok = 1; else @@ -6806,6 +7382,8 @@ iwm_notif_intr(struct iwm_softc *sc) case IWM_BINDING_CONTEXT_CMD: case IWM_TIME_EVENT_CMD: case IWM_SCAN_REQUEST_CMD: + case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD): + case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC): case IWM_SCAN_OFFLOAD_REQUEST_CMD: case IWM_REPLY_BEACON_FILTERING_CMD: case IWM_MAC_PM_POWER_TABLE: @@ -6844,6 +7422,27 @@ iwm_notif_intr(struct iwm_softc *sc) task_add(sc->sc_eswq, &sc->sc_eswk); break; } + case IWM_SCAN_COMPLETE_UMAC: { + struct iwm_umac_scan_complete *notif; + SYNC_RESP_STRUCT(notif, pkt); + + DPRINTF(("UMAC scan complete, status=0x%x\n", + notif->status)); + task_add(sc->sc_eswq, &sc->sc_eswk); + break; + } + + case IWM_SCAN_ITERATION_COMPLETE_UMAC: { + struct iwm_umac_scan_iter_complete_notif *notif; + SYNC_RESP_STRUCT(notif, pkt); + + DPRINTF(("UMAC scan iteration complete, status=0x%x, " + "%d channels scanned\n", + notif->status, notif->scanned_channels)); + task_add(sc->sc_eswq, &sc->sc_eswk); + break; + } + case IWM_REPLY_ERROR: { struct iwm_error_resp *resp; SYNC_RESP_STRUCT(resp, pkt); @@ -7073,6 +7672,8 @@ static const struct pci_matchid iwm_devices[] = { { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WL_7260_2 }, { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WL_7265_1 }, { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WL_7265_2 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WL_8260_1 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_WL_8260_2 }, }; int @@ -7224,38 +7825,97 @@ iwm_attach(struct device *parent, struct device *self, void *aux) sc->sc_wantresp = IWM_CMD_RESP_IDLE; + sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV); switch (PCI_PRODUCT(pa->pa_id)) { case PCI_PRODUCT_INTEL_WL_3160_1: case PCI_PRODUCT_INTEL_WL_3160_2: sc->sc_fwname = "iwm-3160-16"; sc->host_interrupt_operation_mode = 1; + sc->sc_device_family = IWM_DEVICE_FAMILY_7000; + sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; break; case PCI_PRODUCT_INTEL_WL_7260_1: case PCI_PRODUCT_INTEL_WL_7260_2: sc->sc_fwname = "iwm-7260-16"; sc->host_interrupt_operation_mode = 1; + sc->sc_device_family = IWM_DEVICE_FAMILY_7000; + sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; break; case PCI_PRODUCT_INTEL_WL_7265_1: case PCI_PRODUCT_INTEL_WL_7265_2: sc->sc_fwname = "iwm-7265-16"; sc->host_interrupt_operation_mode = 0; + sc->sc_device_family = IWM_DEVICE_FAMILY_7000; + sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; + break; + case PCI_PRODUCT_INTEL_WL_8260_1: + case PCI_PRODUCT_INTEL_WL_8260_2: + sc->sc_fwname = "iwm-8000C-16"; + sc->host_interrupt_operation_mode = 0; + sc->sc_device_family = IWM_DEVICE_FAMILY_8000; + sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000; break; default: printf("%s: unknown adapter type\n", DEVNAME(sc)); return; } - sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; /* * We now start fiddling with the hardware */ - sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV); + /* + * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have + * changed, and now the revision step also includes bit 0-1 (no more + * "dash" value). To keep hw_rev backwards compatible - we'll store it + * in the old format. + */ + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) + sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) | + (IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2); + if (iwm_prepare_card_hw(sc) != 0) { printf("%s: could not initialize hardware\n", DEVNAME(sc)); return; } + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { + int ret; + uint32_t hw_step; + + /* + * In order to recognize C step the driver should read the + * chip version id located at the AUX bus MISC address. + */ + IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, + IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + DELAY(2); + + ret = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, + IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (ret < 0) { + printf("%s: Failed to wake up the nic\n", DEVNAME(sc)); + return; + } + + if (iwm_nic_lock(sc)) { + hw_step = iwm_read_prph(sc, IWM_WFPM_CTRL_REG); + hw_step |= IWM_ENABLE_WFPM; + iwm_write_prph(sc, IWM_WFPM_CTRL_REG, hw_step); + hw_step = iwm_read_prph(sc, IWM_AUX_MISC_REG); + hw_step = (hw_step >> IWM_HW_STEP_LOCATION_BITS) & 0xF; + if (hw_step == 0x3) + sc->sc_hw_rev = (sc->sc_hw_rev & 0xFFFFFFF3) | + (IWM_SILICON_C_STEP << 2); + iwm_nic_unlock(sc); + } else { + printf("%s: Failed to lock the nic\n", DEVNAME(sc)); + return; + } + } + /* Allocate DMA memory for firmware transfers. */ if ((error = iwm_alloc_fwmem(sc)) != 0) { printf("%s: could not allocate memory for firmware\n", diff --git a/sys/dev/pci/if_iwmreg.h b/sys/dev/pci/if_iwmreg.h index 872b95f10f4..db4cf8c54ef 100644 --- a/sys/dev/pci/if_iwmreg.h +++ b/sys/dev/pci/if_iwmreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwmreg.h,v 1.12 2016/05/25 13:35:12 stsp Exp $ */ +/* $OpenBSD: if_iwmreg.h,v 1.13 2016/05/28 08:13:16 stsp Exp $ */ /****************************************************************************** * @@ -1029,6 +1029,16 @@ struct iwm_tlv_ucode_header { #define IWM_DEVICE_SET_NMI_REG 0x00a01c30 #define IWM_DEVICE_SET_NMI_VAL_HW 0x01 #define IWM_DEVICE_SET_NMI_VAL_DRV 0x80 +#define IWM_DEVICE_SET_NMI_8000_REG 0x00a01c24 +#define IWM_DEVICE_SET_NMI_8000_VAL 0x1000000 + +/* + * Device reset for family 8000 + * write to bit 24 in order to reset the CPU +*/ +#define IWM_RELEASE_CPU_RESET 0x300c +#define IWM_RELEASE_CPU_RESET_BIT 0x1000000 + /***************************************************************************** * 7000/3000 series SHR DTS addresses * @@ -1712,6 +1722,13 @@ enum { IWM_PHY_CONTEXT_CMD = 0x8, IWM_DBG_CFG = 0x9, + /* UMAC scan commands */ + IWM_SCAN_ITERATION_COMPLETE_UMAC = 0xb5, + IWM_SCAN_CFG_CMD = 0xc, + IWM_SCAN_REQ_UMAC = 0xd, + IWM_SCAN_ABORT_UMAC = 0xe, + IWM_SCAN_COMPLETE_UMAC = 0xf, + /* station table */ IWM_ADD_STA_KEY = 0x17, IWM_ADD_STA = 0x18, @@ -1990,6 +2007,10 @@ enum { IWM_NVM_SECTION_TYPE_CALIBRATION, IWM_NVM_SECTION_TYPE_PRODUCTION, IWM_NVM_SECTION_TYPE_POST_FCS_CALIB, + /* 7, 8, 9 unknown */ + IWM_NVM_SECTION_TYPE_HW_8000 = 10, + IWM_NVM_SECTION_TYPE_MAC_OVERRIDE, + IWM_NVM_SECTION_TYPE_PHY_SKU, IWM_NVM_NUM_OF_SECTIONS, }; @@ -5399,6 +5420,329 @@ struct iwm_sched_scan_results { uint8_t reserved; }; +/* UMAC Scan API */ + +/* The maximum of either of these cannot exceed 8, because we use an + * 8-bit mask (see IWM_MVM_SCAN_MASK). + */ +#define IWM_MVM_MAX_UMAC_SCANS 8 +#define IWM_MVM_MAX_LMAC_SCANS 1 + +enum iwm_scan_config_flags { + IWM_SCAN_CONFIG_FLAG_ACTIVATE = (1 << 0), + IWM_SCAN_CONFIG_FLAG_DEACTIVATE = (1 << 1), + IWM_SCAN_CONFIG_FLAG_FORBID_CHUB_REQS = (1 << 2), + IWM_SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS = (1 << 3), + IWM_SCAN_CONFIG_FLAG_SET_TX_CHAINS = (1 << 8), + IWM_SCAN_CONFIG_FLAG_SET_RX_CHAINS = (1 << 9), + IWM_SCAN_CONFIG_FLAG_SET_AUX_STA_ID = (1 << 10), + IWM_SCAN_CONFIG_FLAG_SET_ALL_TIMES = (1 << 11), + IWM_SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES = (1 << 12), + IWM_SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS = (1 << 13), + IWM_SCAN_CONFIG_FLAG_SET_LEGACY_RATES = (1 << 14), + IWM_SCAN_CONFIG_FLAG_SET_MAC_ADDR = (1 << 15), + IWM_SCAN_CONFIG_FLAG_SET_FRAGMENTED = (1 << 16), + IWM_SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED = (1 << 17), + IWM_SCAN_CONFIG_FLAG_SET_CAM_MODE = (1 << 18), + IWM_SCAN_CONFIG_FLAG_CLEAR_CAM_MODE = (1 << 19), + IWM_SCAN_CONFIG_FLAG_SET_PROMISC_MODE = (1 << 20), + IWM_SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE = (1 << 21), + + /* Bits 26-31 are for num of channels in channel_array */ +#define IWM_SCAN_CONFIG_N_CHANNELS(n) ((n) << 26) +}; + +enum iwm_scan_config_rates { + /* OFDM basic rates */ + IWM_SCAN_CONFIG_RATE_6M = (1 << 0), + IWM_SCAN_CONFIG_RATE_9M = (1 << 1), + IWM_SCAN_CONFIG_RATE_12M = (1 << 2), + IWM_SCAN_CONFIG_RATE_18M = (1 << 3), + IWM_SCAN_CONFIG_RATE_24M = (1 << 4), + IWM_SCAN_CONFIG_RATE_36M = (1 << 5), + IWM_SCAN_CONFIG_RATE_48M = (1 << 6), + IWM_SCAN_CONFIG_RATE_54M = (1 << 7), + /* CCK basic rates */ + IWM_SCAN_CONFIG_RATE_1M = (1 << 8), + IWM_SCAN_CONFIG_RATE_2M = (1 << 9), + IWM_SCAN_CONFIG_RATE_5M = (1 << 10), + IWM_SCAN_CONFIG_RATE_11M = (1 << 11), + + /* Bits 16-27 are for supported rates */ +#define IWM_SCAN_CONFIG_SUPPORTED_RATE(rate) ((rate) << 16) +}; + +enum iwm_channel_flags { + IWM_CHANNEL_FLAG_EBS = (1 << 0), + IWM_CHANNEL_FLAG_ACCURATE_EBS = (1 << 1), + IWM_CHANNEL_FLAG_EBS_ADD = (1 << 2), + IWM_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE = (1 << 3), +}; + +/** + * struct iwm_scan_config + * @flags: enum scan_config_flags + * @tx_chains: valid_tx antenna - ANT_* definitions + * @rx_chains: valid_rx antenna - ANT_* definitions + * @legacy_rates: default legacy rates - enum scan_config_rates + * @out_of_channel_time: default max out of serving channel time + * @suspend_time: default max suspend time + * @dwell_active: default dwell time for active scan + * @dwell_passive: default dwell time for passive scan + * @dwell_fragmented: default dwell time for fragmented scan + * @dwell_extended: default dwell time for channels 1, 6 and 11 + * @mac_addr: default mac address to be used in probes + * @bcast_sta_id: the index of the station in the fw + * @channel_flags: default channel flags - enum iwm_channel_flags + * scan_config_channel_flag + * @channel_array: default supported channels + */ +struct iwm_scan_config { + uint32_t flags; + uint32_t tx_chains; + uint32_t rx_chains; + uint32_t legacy_rates; + uint32_t out_of_channel_time; + uint32_t suspend_time; + uint8_t dwell_active; + uint8_t dwell_passive; + uint8_t dwell_fragmented; + uint8_t dwell_extended; + uint8_t mac_addr[ETHER_ADDR_LEN]; + uint8_t bcast_sta_id; + uint8_t channel_flags; + uint8_t channel_array[]; +} __packed; /* SCAN_CONFIG_DB_CMD_API_S */ + +/** + * iwm_umac_scan_flags + *@IWM_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request + * can be preempted by other scan requests with higher priority. + * The low priority scan will be resumed when the higher proirity scan is + * completed. + *@IWM_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver + * when scan starts. + */ +enum iwm_umac_scan_flags { + IWM_UMAC_SCAN_FLAG_PREEMPTIVE = (1 << 0), + IWM_UMAC_SCAN_FLAG_START_NOTIF = (1 << 1), +}; + +enum iwm_umac_scan_uid_offsets { + IWM_UMAC_SCAN_UID_TYPE_OFFSET = 0, + IWM_UMAC_SCAN_UID_SEQ_OFFSET = 8, +}; + +enum iwm_umac_scan_general_flags { + IWM_UMAC_SCAN_GEN_FLAGS_PERIODIC = (1 << 0), + IWM_UMAC_SCAN_GEN_FLAGS_OVER_BT = (1 << 1), + IWM_UMAC_SCAN_GEN_FLAGS_PASS_ALL = (1 << 2), + IWM_UMAC_SCAN_GEN_FLAGS_PASSIVE = (1 << 3), + IWM_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT = (1 << 4), + IWM_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE = (1 << 5), + IWM_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = (1 << 6), + IWM_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = (1 << 7), + IWM_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = (1 << 8), + IWM_UMAC_SCAN_GEN_FLAGS_MATCH = (1 << 9), + IWM_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = (1 << 10), +}; + +/** + * struct iwm_scan_channel_cfg_umac + * @flags: bitmap - 0-19: directed scan to i'th ssid. + * @channel_num: channel number 1-13 etc. + * @iter_count: repetition count for the channel. + * @iter_interval: interval between two scan iterations on one channel. + */ +struct iwm_scan_channel_cfg_umac { + uint32_t flags; + uint8_t channel_num; + uint8_t iter_count; + uint16_t iter_interval; +} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */ + +/** + * struct iwm_scan_umac_schedule + * @interval: interval in seconds between scan iterations + * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop + * @reserved: for alignment and future use + */ +struct iwm_scan_umac_schedule { + uint16_t interval; + uint8_t iter_count; + uint8_t reserved; +} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */ + +/** + * struct iwm_scan_req_umac_tail - the rest of the UMAC scan request command + * parameters following channels configuration array. + * @schedule: two scheduling plans. + * @delay: delay in TUs before starting the first scan iteration + * @reserved: for future use and alignment + * @preq: probe request with IEs blocks + * @direct_scan: list of SSIDs for directed active scan + */ +struct iwm_scan_req_umac_tail { + /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */ + struct iwm_scan_umac_schedule schedule[IWM_MAX_SCHED_SCAN_PLANS]; + uint16_t delay; + uint16_t reserved; + /* SCAN_PROBE_PARAMS_API_S_VER_1 */ + struct iwm_scan_probe_req preq; + struct iwm_ssid_ie direct_scan[IWM_PROBE_OPTION_MAX]; +} __packed; + +/** + * struct iwm_scan_req_umac + * @flags: &enum iwm_umac_scan_flags + * @uid: scan id, &enum iwm_umac_scan_uid_offsets + * @ooc_priority: out of channel priority - &enum iwm_scan_priority + * @general_flags: &enum iwm_umac_scan_general_flags + * @extended_dwell: dwell time for channels 1, 6 and 11 + * @active_dwell: dwell time for active scan + * @passive_dwell: dwell time for passive scan + * @fragmented_dwell: dwell time for fragmented passive scan + * @max_out_time: max out of serving channel time + * @suspend_time: max suspend time + * @scan_priority: scan internal prioritization &enum iwm_scan_priority + * @channel_flags: &enum iwm_scan_channel_flags + * @n_channels: num of channels in scan request + * @reserved: for future use and alignment + * @data: &struct iwm_scan_channel_cfg_umac and + * &struct iwm_scan_req_umac_tail + */ +struct iwm_scan_req_umac { + uint32_t flags; + uint32_t uid; + uint32_t ooc_priority; + /* SCAN_GENERAL_PARAMS_API_S_VER_1 */ + uint32_t general_flags; + uint8_t extended_dwell; + uint8_t active_dwell; + uint8_t passive_dwell; + uint8_t fragmented_dwell; + uint32_t max_out_time; + uint32_t suspend_time; + uint32_t scan_priority; + /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */ + uint8_t channel_flags; + uint8_t n_channels; + uint16_t reserved; + uint8_t data[]; +} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ + +/** + * struct iwm_umac_scan_abort + * @uid: scan id, &enum iwm_umac_scan_uid_offsets + * @flags: reserved + */ +struct iwm_umac_scan_abort { + uint32_t uid; + uint32_t flags; +} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */ + +/** + * struct iwm_umac_scan_complete + * @uid: scan id, &enum iwm_umac_scan_uid_offsets + * @last_schedule: last scheduling line + * @last_iter: last scan iteration number + * @scan status: &enum iwm_scan_offload_complete_status + * @ebs_status: &enum iwm_scan_ebs_status + * @time_from_last_iter: time elapsed from last iteration + * @reserved: for future use + */ +struct iwm_umac_scan_complete { + uint32_t uid; + uint8_t last_schedule; + uint8_t last_iter; + uint8_t status; + uint8_t ebs_status; + uint32_t time_from_last_iter; + uint32_t reserved; +} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */ + +#define IWM_SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5 +/** + * struct iwm_scan_offload_profile_match - match information + * @bssid: matched bssid + * @channel: channel where the match occurred + * @energy: + * @matching_feature: + * @matching_channels: bitmap of channels that matched, referencing + * the channels passed in tue scan offload request + */ +struct iwm_scan_offload_profile_match { + uint8_t bssid[ETHER_ADDR_LEN]; + uint16_t reserved; + uint8_t channel; + uint8_t energy; + uint8_t matching_feature; + uint8_t matching_channels[IWM_SCAN_OFFLOAD_MATCHING_CHANNELS_LEN]; +} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */ + +/** + * struct iwm_scan_offload_profiles_query - match results query response + * @matched_profiles: bitmap of matched profiles, referencing the + * matches passed in the scan offload request + * @last_scan_age: age of the last offloaded scan + * @n_scans_done: number of offloaded scans done + * @gp2_d0u: GP2 when D0U occurred + * @gp2_invoked: GP2 when scan offload was invoked + * @resume_while_scanning: not used + * @self_recovery: obsolete + * @reserved: reserved + * @matches: array of match information, one for each match + */ +struct iwm_scan_offload_profiles_query { + uint32_t matched_profiles; + uint32_t last_scan_age; + uint32_t n_scans_done; + uint32_t gp2_d0u; + uint32_t gp2_invoked; + uint8_t resume_while_scanning; + uint8_t self_recovery; + uint16_t reserved; + struct iwm_scan_offload_profile_match matches[IWM_SCAN_MAX_PROFILES]; +} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */ + +/** + * struct iwm_umac_scan_iter_complete_notif - notifies end of scanning iteration + * @uid: scan id, &enum iwm_umac_scan_uid_offsets + * @scanned_channels: number of channels scanned and number of valid elements in + * results array + * @status: one of SCAN_COMP_STATUS_* + * @bt_status: BT on/off status + * @last_channel: last channel that was scanned + * @tsf_low: TSF timer (lower half) in usecs + * @tsf_high: TSF timer (higher half) in usecs + * @results: array of scan results, only "scanned_channels" of them are valid + */ +struct iwm_umac_scan_iter_complete_notif { + uint32_t uid; + uint8_t scanned_channels; + uint8_t status; + uint8_t bt_status; + uint8_t last_channel; + uint32_t tsf_low; + uint32_t tsf_high; + struct iwm_scan_results_notif results[]; +} __packed; /* SCAN_ITER_COMPLETE_NTF_UMAC_API_S_VER_1 */ + +/* Please keep this enum *SORTED* by hex value. + * Needed for binary search, otherwise a warning will be triggered. + */ +enum iwm_scan_subcmd_ids { + IWM_GSCAN_START_CMD = 0x0, + IWM_GSCAN_STOP_CMD = 0x1, + IWM_GSCAN_SET_HOTLIST_CMD = 0x2, + IWM_GSCAN_RESET_HOTLIST_CMD = 0x3, + IWM_GSCAN_SET_SIGNIFICANT_CHANGE_CMD = 0x4, + IWM_GSCAN_RESET_SIGNIFICANT_CHANGE_CMD = 0x5, + IWM_GSCAN_SIGNIFICANT_CHANGE_EVENT = 0xFD, + IWM_GSCAN_HOTLIST_CHANGE_EVENT = 0xFE, + IWM_GSCAN_RESULTS_AVAILABLE_EVENT = 0xFF, +}; + /* STA API */ /** diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h index c8efd2cdc84..4cf9fde6f0b 100644 --- a/sys/dev/pci/if_iwmvar.h +++ b/sys/dev/pci/if_iwmvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwmvar.h,v 1.17 2016/05/25 13:35:12 stsp Exp $ */ +/* $OpenBSD: if_iwmvar.h,v 1.18 2016/05/28 08:13:16 stsp Exp $ */ /* * Copyright (c) 2014 genua mbh <info@genua.de> @@ -139,6 +139,7 @@ struct iwm_tx_radiotap_header { #define IWM_UCODE_SECT_MAX 16 #define IWM_FWDMASEGSZ (192*1024) +#define IWM_FWDMASEGSZ_8000 (320*1024) /* sanity check value */ #define IWM_FWMAXSIZE (2*1024*1024) @@ -295,6 +296,7 @@ struct iwm_rx_ring { struct iwm_ucode_status { uint32_t uc_error_event_table; + uint32_t uc_umac_error_event_table; uint32_t uc_log_event_table; int uc_ok; @@ -308,6 +310,7 @@ struct iwm_ucode_status { /* lower blocks contain EEPROM image and calibration data */ #define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_7000 16384 +#define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_8000 32768 #define IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 1000 #define IWM_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400 @@ -406,7 +409,14 @@ struct iwm_softc { int ict_cur; int sc_hw_rev; +#define IWM_SILICON_A_STEP 0 +#define IWM_SILICON_B_STEP 1 +#define IWM_SILICON_C_STEP 2 +#define IWM_SILICON_D_STEP 3 int sc_hw_id; + int sc_device_family; +#define IWM_DEVICE_FAMILY_7000 1 +#define IWM_DEVICE_FAMILY_8000 2 struct iwm_dma_info kw_dma; struct iwm_dma_info fw_dma; |