diff options
Diffstat (limited to 'sys/dev/iwm/if_iwm.c')
-rw-r--r-- | sys/dev/iwm/if_iwm.c | 921 |
1 files changed, 469 insertions, 452 deletions
diff --git a/sys/dev/iwm/if_iwm.c b/sys/dev/iwm/if_iwm.c index 54f325e7031f..9a9ce9242a9d 100644 --- a/sys/dev/iwm/if_iwm.c +++ b/sys/dev/iwm/if_iwm.c @@ -152,6 +152,7 @@ __FBSDID("$FreeBSD$"); #include <dev/iwm/if_iwmreg.h> #include <dev/iwm/if_iwmvar.h> +#include <dev/iwm/if_iwm_config.h> #include <dev/iwm/if_iwm_debug.h> #include <dev/iwm/if_iwm_notif_wait.h> #include <dev/iwm/if_iwm_util.h> @@ -166,68 +167,6 @@ __FBSDID("$FreeBSD$"); #include <dev/iwm/if_iwm_pcie_trans.h> #include <dev/iwm/if_iwm_led.h> -#define IWM_NVM_HW_SECTION_NUM_FAMILY_7000 0 -#define IWM_NVM_HW_SECTION_NUM_FAMILY_8000 10 - -/* lower blocks contain EEPROM image and calibration data */ -#define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(uint16_t)) /* 16 KB */ -#define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(uint16_t)) /* 32 KB */ - -#define IWM7260_FW "iwm7260fw" -#define IWM3160_FW "iwm3160fw" -#define IWM7265_FW "iwm7265fw" -#define IWM7265D_FW "iwm7265Dfw" -#define IWM8000_FW "iwm8000Cfw" - -#define IWM_DEVICE_7000_COMMON \ - .device_family = IWM_DEVICE_FAMILY_7000, \ - .eeprom_size = IWM_OTP_LOW_IMAGE_SIZE_FAMILY_7000, \ - .nvm_hw_section_num = IWM_NVM_HW_SECTION_NUM_FAMILY_7000, \ - .apmg_wake_up_wa = 1 - -const struct iwm_cfg iwm7260_cfg = { - .fw_name = IWM7260_FW, - IWM_DEVICE_7000_COMMON, - .host_interrupt_operation_mode = 1, -}; - -const struct iwm_cfg iwm3160_cfg = { - .fw_name = IWM3160_FW, - IWM_DEVICE_7000_COMMON, - .host_interrupt_operation_mode = 1, -}; - -const struct iwm_cfg iwm3165_cfg = { - /* XXX IWM7265D_FW doesn't seem to work properly yet */ - .fw_name = IWM7265_FW, - IWM_DEVICE_7000_COMMON, - .host_interrupt_operation_mode = 0, -}; - -const struct iwm_cfg iwm7265_cfg = { - .fw_name = IWM7265_FW, - IWM_DEVICE_7000_COMMON, - .host_interrupt_operation_mode = 0, -}; - -const struct iwm_cfg iwm7265d_cfg = { - /* XXX IWM7265D_FW doesn't seem to work properly yet */ - .fw_name = IWM7265_FW, - IWM_DEVICE_7000_COMMON, - .host_interrupt_operation_mode = 0, -}; - -#define IWM_DEVICE_8000_COMMON \ - .device_family = IWM_DEVICE_FAMILY_8000, \ - .eeprom_size = IWM_OTP_LOW_IMAGE_SIZE_FAMILY_8000, \ - .nvm_hw_section_num = IWM_NVM_HW_SECTION_NUM_FAMILY_8000 - -const struct iwm_cfg iwm8260_cfg = { - .fw_name = IWM8000_FW, - IWM_DEVICE_8000_COMMON, - .host_interrupt_operation_mode = 0, -}; - const uint8_t iwm_nvm_channels[] = { /* 2.4 GHz */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, @@ -285,8 +224,14 @@ struct iwm_nvm_section { uint8_t *data; }; +#define IWM_MVM_UCODE_ALIVE_TIMEOUT hz #define IWM_MVM_UCODE_CALIB_TIMEOUT (2*hz) +struct iwm_mvm_alive_data { + int valid; + uint32_t scd_base_addr; +}; + static int iwm_store_cscheme(struct iwm_softc *, const uint8_t *, size_t); static int iwm_firmware_store_section(struct iwm_softc *, enum iwm_ucode_type, @@ -294,10 +239,6 @@ static int iwm_firmware_store_section(struct iwm_softc *, static int iwm_set_default_calib(struct iwm_softc *, const void *); static void iwm_fw_info_free(struct iwm_fw_info *); static int iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type); -static void iwm_dma_map_addr(void *, bus_dma_segment_t *, int, int); -static int iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *, - bus_size_t, bus_size_t); -static void iwm_dma_contig_free(struct iwm_dma_info *); static int iwm_alloc_fwmem(struct iwm_softc *); static int iwm_alloc_sched(struct iwm_softc *); static int iwm_alloc_kw(struct iwm_softc *); @@ -320,7 +261,7 @@ static int iwm_nic_rx_init(struct iwm_softc *); static int iwm_nic_tx_init(struct iwm_softc *); static int iwm_nic_init(struct iwm_softc *); static int iwm_enable_txq(struct iwm_softc *, int, int, int); -static int iwm_post_alive(struct iwm_softc *); +static int iwm_trans_pcie_fw_alive(struct iwm_softc *, uint32_t); static int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, uint16_t, uint8_t *, uint16_t *); static int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, @@ -353,16 +294,21 @@ static void iwm_set_radio_cfg(const struct iwm_softc *, static struct iwm_nvm_data * iwm_parse_nvm_sections(struct iwm_softc *, struct iwm_nvm_section *); static int iwm_nvm_init(struct iwm_softc *); -static int iwm_firmware_load_sect(struct iwm_softc *, uint32_t, - const uint8_t *, uint32_t); -static int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, - const uint8_t *, uint32_t); -static int iwm_load_firmware_7000(struct iwm_softc *, enum iwm_ucode_type); -static int iwm_load_cpu_sections_8000(struct iwm_softc *, - struct iwm_fw_sects *, int , int *); -static int iwm_load_firmware_8000(struct iwm_softc *, enum iwm_ucode_type); -static int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); -static int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); +static int iwm_pcie_load_section(struct iwm_softc *, uint8_t, + const struct iwm_fw_desc *); +static int iwm_pcie_load_firmware_chunk(struct iwm_softc *, uint32_t, + bus_addr_t, uint32_t); +static int iwm_pcie_load_cpu_sections_8000(struct iwm_softc *sc, + const struct iwm_fw_sects *, + int, int *); +static int iwm_pcie_load_cpu_sections(struct iwm_softc *, + const struct iwm_fw_sects *, + int, int *); +static int iwm_pcie_load_given_ucode_8000(struct iwm_softc *, + const struct iwm_fw_sects *); +static int iwm_pcie_load_given_ucode(struct iwm_softc *, + const struct iwm_fw_sects *); +static int iwm_start_fw(struct iwm_softc *, const struct iwm_fw_sects *); static int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); static int iwm_send_phy_cfg_cmd(struct iwm_softc *); static int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *, @@ -485,7 +431,7 @@ iwm_firmware_store_section(struct iwm_softc *sc, enum iwm_ucode_type type, const uint8_t *data, size_t dlen) { struct iwm_fw_sects *fws; - struct iwm_fw_onesect *fwone; + struct iwm_fw_desc *fwone; if (type >= IWM_UCODE_TYPE_MAX) return EINVAL; @@ -499,11 +445,11 @@ iwm_firmware_store_section(struct iwm_softc *sc, fwone = &fws->fw_sect[fws->fw_count]; /* first 32bit are device load offset */ - memcpy(&fwone->fws_devoff, data, sizeof(uint32_t)); + memcpy(&fwone->offset, data, sizeof(uint32_t)); /* rest is data */ - fwone->fws_data = data + sizeof(uint32_t); - fwone->fws_len = dlen - sizeof(uint32_t); + fwone->data = data + sizeof(uint32_t); + fwone->len = dlen - sizeof(uint32_t); fws->fw_count++; @@ -559,6 +505,7 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) const uint8_t *data; uint32_t usniffer_img; uint32_t paging_mem_size; + int num_of_cpus; int error = 0; size_t len; @@ -699,18 +646,24 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) goto parse_out; } break; - case IWM_UCODE_TLV_NUM_OF_CPU: { - uint32_t num_cpu; + case IWM_UCODE_TLV_NUM_OF_CPU: if (tlv_len != sizeof(uint32_t)) { device_printf(sc->sc_dev, - "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) < sizeof(uint32_t)\n", + "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) != sizeof(uint32_t)\n", __func__, (int) tlv_len); error = EINVAL; goto parse_out; } - num_cpu = le32toh(*(const uint32_t *)tlv_data); - if (num_cpu < 1 || num_cpu > 2) { + num_of_cpus = le32toh(*(const uint32_t *)tlv_data); + if (num_of_cpus == 2) { + fw->fw_sects[IWM_UCODE_REGULAR].is_dual_cpus = + TRUE; + fw->fw_sects[IWM_UCODE_INIT].is_dual_cpus = + TRUE; + fw->fw_sects[IWM_UCODE_WOWLAN].is_dual_cpus = + TRUE; + } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) { device_printf(sc->sc_dev, "%s: Driver supports only 1 or 2 CPUs\n", __func__); @@ -718,7 +671,6 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) goto parse_out; } break; - } case IWM_UCODE_TLV_SEC_RT: if ((error = iwm_firmware_store_section(sc, IWM_UCODE_REGULAR, tlv_data, tlv_len)) != 0) { @@ -937,78 +889,13 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) * DMA resource routines */ -static void -iwm_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) -{ - if (error != 0) - return; - KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); - *(bus_addr_t *)arg = segs[0].ds_addr; -} - -static int -iwm_dma_contig_alloc(bus_dma_tag_t tag, struct iwm_dma_info *dma, - bus_size_t size, bus_size_t alignment) -{ - int error; - - dma->tag = NULL; - dma->map = NULL; - dma->size = size; - dma->vaddr = NULL; - - error = bus_dma_tag_create(tag, alignment, - 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, - 1, size, 0, NULL, NULL, &dma->tag); - if (error != 0) - goto fail; - - error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, - BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); - if (error != 0) - goto fail; - - error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, - iwm_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); - if (error != 0) { - bus_dmamem_free(dma->tag, dma->vaddr, dma->map); - dma->vaddr = NULL; - goto fail; - } - - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - - return 0; - -fail: - iwm_dma_contig_free(dma); - - return error; -} - -static void -iwm_dma_contig_free(struct iwm_dma_info *dma) -{ - if (dma->vaddr != NULL) { - bus_dmamap_sync(dma->tag, dma->map, - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(dma->tag, dma->map); - bus_dmamem_free(dma->tag, dma->vaddr, dma->map); - dma->vaddr = NULL; - } - if (dma->tag != NULL) { - bus_dma_tag_destroy(dma->tag); - dma->tag = NULL; - } -} - /* fwmem is used to load firmware onto the card */ static int iwm_alloc_fwmem(struct iwm_softc *sc) { /* Must be aligned on a 16-byte boundary. */ return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, - sc->sc_fwdmasegsz, 16); + IWM_FH_MEM_TB_MAX_LENGTH, 16); } /* tx scheduler rings. not used? */ @@ -1622,20 +1509,33 @@ iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo) (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); + iwm_nic_unlock(sc); + iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); + if (!iwm_nic_lock(sc)) { + device_printf(sc->sc_dev, + "%s: cannot enable txq %d\n", __func__, qid); + return EBUSY; + } iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); + iwm_nic_unlock(sc); - iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); + iwm_write_mem32(sc, sc->scd_base_addr + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); /* Set scheduler window size and frame limit. */ iwm_write_mem32(sc, - sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + + sc->scd_base_addr + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + sizeof(uint32_t), ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); + if (!iwm_nic_lock(sc)) { + device_printf(sc->sc_dev, + "%s: cannot enable txq %d\n", __func__, qid); + return EBUSY; + } iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | @@ -1679,33 +1579,37 @@ iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo) } static int -iwm_post_alive(struct iwm_softc *sc) +iwm_trans_pcie_fw_alive(struct iwm_softc *sc, uint32_t scd_base_addr) { - int nwords; int error, chnl; - uint32_t base; + + int clear_dwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND - + IWM_SCD_CONTEXT_MEM_LOWER_BOUND) / sizeof(uint32_t); if (!iwm_nic_lock(sc)) return EBUSY; - base = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR); - if (sc->sched_base != base) { + iwm_ict_reset(sc); + + iwm_nic_unlock(sc); + + sc->scd_base_addr = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR); + if (scd_base_addr != 0 && + scd_base_addr != sc->scd_base_addr) { device_printf(sc->sc_dev, "%s: sched addr mismatch: alive: 0x%x prph: 0x%x\n", - __func__, sc->sched_base, base); + __func__, sc->scd_base_addr, scd_base_addr); } - iwm_ict_reset(sc); - - /* Clear TX scheduler state in SRAM. */ - nwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND - - IWM_SCD_CONTEXT_MEM_LOWER_BOUND) - / sizeof(uint32_t); + /* reset context data, TX status and translation data */ error = iwm_write_mem(sc, - sc->sched_base + IWM_SCD_CONTEXT_MEM_LOWER_BOUND, - NULL, nwords); + sc->scd_base_addr + IWM_SCD_CONTEXT_MEM_LOWER_BOUND, + NULL, clear_dwords); if (error) - goto out; + return EBUSY; + + if (!iwm_nic_lock(sc)) + return EBUSY; /* Set physical address of TX scheduler rings (1KB aligned). */ iwm_write_prph(sc, IWM_SCD_DRAM_BASE_ADDR, sc->sched_dma.paddr >> 10); @@ -1734,14 +1638,14 @@ iwm_post_alive(struct iwm_softc *sc) IWM_SETBITS(sc, IWM_FH_TX_CHICKEN_BITS_REG, IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); + iwm_nic_unlock(sc); + /* Enable L1-Active */ if (sc->cfg->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); return error; } @@ -2414,52 +2318,67 @@ iwm_nvm_init(struct iwm_softc *sc) return 0; } -/* - * Firmware loading gunk. This is kind of a weird hybrid between the - * iwn driver and the Linux iwlwifi driver. - */ - static int -iwm_firmware_load_sect(struct iwm_softc *sc, uint32_t dst_addr, - const uint8_t *section, uint32_t byte_cnt) +iwm_pcie_load_section(struct iwm_softc *sc, uint8_t section_num, + const struct iwm_fw_desc *section) { - int error = EINVAL; - uint32_t chunk_sz, offset; + struct iwm_dma_info *dma = &sc->fw_dma; + uint8_t *v_addr; + bus_addr_t p_addr; + uint32_t offset, chunk_sz = MIN(IWM_FH_MEM_TB_MAX_LENGTH, section->len); + int ret = 0; + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "%s: [%d] uCode section being loaded...\n", + __func__, section_num); - chunk_sz = MIN(IWM_FH_MEM_TB_MAX_LENGTH, byte_cnt); + v_addr = dma->vaddr; + p_addr = dma->paddr; - for (offset = 0; offset < byte_cnt; offset += chunk_sz) { - uint32_t addr, len; - const uint8_t *data; + for (offset = 0; offset < section->len; offset += chunk_sz) { + uint32_t copy_size, dst_addr; + int extended_addr = FALSE; - addr = dst_addr + offset; - len = MIN(chunk_sz, byte_cnt - offset); - data = section + offset; + copy_size = MIN(chunk_sz, section->len - offset); + dst_addr = section->offset + offset; - error = iwm_firmware_load_chunk(sc, addr, data, len); - if (error) + if (dst_addr >= IWM_FW_MEM_EXTENDED_START && + dst_addr <= IWM_FW_MEM_EXTENDED_END) + extended_addr = TRUE; + + if (extended_addr) + iwm_set_bits_prph(sc, IWM_LMPM_CHICK, + IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); + + memcpy(v_addr, (const uint8_t *)section->data + offset, + copy_size); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); + ret = iwm_pcie_load_firmware_chunk(sc, dst_addr, p_addr, + copy_size); + + if (extended_addr) + iwm_clear_bits_prph(sc, IWM_LMPM_CHICK, + IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); + + if (ret) { + device_printf(sc->sc_dev, + "%s: Could not load the [%d] uCode section\n", + __func__, section_num); break; + } } - return error; + return ret; } +/* + * ucode + */ static int -iwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, - const uint8_t *chunk, uint32_t byte_cnt) +iwm_pcie_load_firmware_chunk(struct iwm_softc *sc, uint32_t dst_addr, + bus_addr_t phy_addr, uint32_t byte_cnt) { - struct iwm_dma_info *dma = &sc->fw_dma; - int error; - - /* Copy firmware chunk into pre-allocated DMA-safe memory. */ - memcpy(dma->vaddr, chunk, byte_cnt); - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - - if (dst_addr >= IWM_FW_MEM_EXTENDED_START && - dst_addr <= IWM_FW_MEM_EXTENDED_END) { - iwm_set_bits_prph(sc, IWM_LMPM_CHICK, - IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); - } + int ret; sc->sc_fw_chunk_done = 0; @@ -2468,17 +2387,22 @@ iwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); + IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL), dst_addr); + IWM_WRITE(sc, IWM_FH_TFDIB_CTRL0_REG(IWM_FH_SRVC_CHNL), - dma->paddr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); + phy_addr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); + IWM_WRITE(sc, IWM_FH_TFDIB_CTRL1_REG(IWM_FH_SRVC_CHNL), - (iwm_get_dma_hi_addr(dma->paddr) - << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); + (iwm_get_dma_hi_addr(phy_addr) + << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); + IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(IWM_FH_SRVC_CHNL), 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); + IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | @@ -2486,37 +2410,31 @@ iwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, iwm_nic_unlock(sc); - /* wait 1s for this segment to load */ - while (!sc->sc_fw_chunk_done) - if ((error = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz)) != 0) + /* wait up to 5s for this segment to load */ + ret = 0; + while (!sc->sc_fw_chunk_done) { + ret = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz); + if (ret) break; + } - if (!sc->sc_fw_chunk_done) { + if (ret != 0) { device_printf(sc->sc_dev, "fw chunk addr 0x%x len %d failed to load\n", dst_addr, byte_cnt); + return ETIMEDOUT; } - if (dst_addr >= IWM_FW_MEM_EXTENDED_START && - dst_addr <= IWM_FW_MEM_EXTENDED_END && iwm_nic_lock(sc)) { - iwm_clear_bits_prph(sc, IWM_LMPM_CHICK, - IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); - iwm_nic_unlock(sc); - } - - return error; + return 0; } -int -iwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws, - int cpu, int *first_ucode_section) +static int +iwm_pcie_load_cpu_sections_8000(struct iwm_softc *sc, + const struct iwm_fw_sects *image, int cpu, int *first_ucode_section) { int shift_param; - int i, error = 0, sec_num = 0x1; + int i, ret = 0, sec_num = 0x1; uint32_t val, last_read_idx = 0; - const void *data; - uint32_t dlen; - uint32_t offset; if (cpu == 1) { shift_param = 0; @@ -2528,9 +2446,6 @@ iwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws, for (i = *first_ucode_section; i < IWM_UCODE_SECTION_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 @@ -2538,27 +2453,17 @@ iwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws, * 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; - - IWM_DPRINTF(sc, IWM_DEBUG_RESET, - "LOAD FIRMWARE chunk %d offset 0x%x len %d for cpu %d\n", - i, offset, dlen, cpu); - - if (dlen > sc->sc_fwdmasegsz) { + if (!image->fw_sect[i].data || + image->fw_sect[i].offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || + image->fw_sect[i].offset == IWM_PAGING_SEPARATOR_SECTION) { IWM_DPRINTF(sc, IWM_DEBUG_RESET, - "chunk %d too large (%d bytes)\n", i, dlen); - error = EFBIG; - } else { - error = iwm_firmware_load_sect(sc, offset, data, dlen); - } - if (error) { - device_printf(sc->sc_dev, - "could not load firmware chunk %d (error %d)\n", - i, error); - return error; + "Break since Data not valid or Empty section, sec = %d\n", + i); + break; } + ret = iwm_pcie_load_section(sc, i, &image->fw_sect[i]); + if (ret) + return ret; /* Notify the ucode of the loaded section number and status */ if (iwm_nic_lock(sc)) { @@ -2567,16 +2472,13 @@ iwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws, 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; + iwm_enable_interrupts(sc); + if (iwm_nic_lock(sc)) { if (cpu == 1) IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFF); @@ -2588,120 +2490,170 @@ iwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws, return 0; } -int -iwm_load_firmware_8000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +static int +iwm_pcie_load_cpu_sections(struct iwm_softc *sc, + const struct iwm_fw_sects *image, int cpu, int *first_ucode_section) { - struct iwm_fw_sects *fws; - int error = 0; - int first_ucode_section; + int shift_param; + int i, ret = 0; + uint32_t last_read_idx = 0; - IWM_DPRINTF(sc, IWM_DEBUG_RESET, "loading ucode type %d\n", - ucode_type); + if (cpu == 1) { + shift_param = 0; + *first_ucode_section = 0; + } else { + shift_param = 16; + (*first_ucode_section)++; + } - fws = &sc->sc_fw.fw_sects[ucode_type]; + for (i = *first_ucode_section; i < IWM_UCODE_SECTION_MAX; i++) { + last_read_idx = i; - /* 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); + /* + * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between + * CPU1 to CPU2. + * PAGING_SEPARATOR_SECTION delimiter - separate between + * CPU2 non paged to CPU2 paging sec. + */ + if (!image->fw_sect[i].data || + image->fw_sect[i].offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || + image->fw_sect[i].offset == IWM_PAGING_SEPARATOR_SECTION) { + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "Break since Data not valid or Empty section, sec = %d\n", + i); + break; + } - /* 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; + ret = iwm_pcie_load_section(sc, i, &image->fw_sect[i]); + if (ret) + return ret; + } + + if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) + iwm_set_bits_prph(sc, + IWM_CSR_UCODE_LOAD_STATUS_ADDR, + (IWM_LMPM_CPU_UCODE_LOADING_COMPLETED | + IWM_LMPM_CPU_HDRS_LOADING_COMPLETED | + IWM_LMPM_CPU_UCODE_LOADING_STARTED) << + shift_param); + + *first_ucode_section = last_read_idx; + + return 0; - /* load to FW the binary sections of CPU2 */ - return iwm_load_cpu_sections_8000(sc, fws, 2, &first_ucode_section); } static int -iwm_load_firmware_7000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +iwm_pcie_load_given_ucode(struct iwm_softc *sc, + const struct iwm_fw_sects *image) { - struct iwm_fw_sects *fws; - int error, i; - const void *data; - uint32_t dlen; - uint32_t offset; - - sc->sc_uc.uc_intr = 0; - - fws = &sc->sc_fw.fw_sects[ucode_type]; - for (i = 0; i < fws->fw_count; i++) { - data = fws->fw_sect[i].fws_data; - dlen = fws->fw_sect[i].fws_len; - offset = fws->fw_sect[i].fws_devoff; - IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, - "LOAD FIRMWARE type %d offset %u len %d\n", - ucode_type, offset, dlen); - if (dlen > sc->sc_fwdmasegsz) { - IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, - "chunk %d too large (%d bytes)\n", i, dlen); - error = EFBIG; - } else { - error = iwm_firmware_load_sect(sc, offset, data, dlen); - } - if (error) { - device_printf(sc->sc_dev, - "could not load firmware chunk %u of %u " - "(error=%d)\n", i, fws->fw_count, error); - return error; - } + int ret = 0; + int first_ucode_section; + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, "working with %s CPU\n", + image->is_dual_cpus ? "Dual" : "Single"); + + /* load to FW the binary non secured sections of CPU1 */ + ret = iwm_pcie_load_cpu_sections(sc, image, 1, &first_ucode_section); + if (ret) + return ret; + + if (image->is_dual_cpus) { + /* set CPU2 header address */ + iwm_write_prph(sc, + IWM_LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR, + IWM_LMPM_SECURE_CPU2_HDR_MEM_SPACE); + + /* load to FW the binary sections of CPU2 */ + ret = iwm_pcie_load_cpu_sections(sc, image, 2, + &first_ucode_section); + if (ret) + return ret; } + iwm_enable_interrupts(sc); + + /* release CPU reset */ IWM_WRITE(sc, IWM_CSR_RESET, 0); return 0; } -static int -iwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +int +iwm_pcie_load_given_ucode_8000(struct iwm_softc *sc, + const struct iwm_fw_sects *image) { - int error, w; + int ret = 0; + int first_ucode_section; - if (sc->cfg->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; + IWM_DPRINTF(sc, IWM_DEBUG_RESET, "working with %s CPU\n", + image->is_dual_cpus ? "Dual" : "Single"); - /* wait for the firmware to load */ - for (w = 0; !sc->sc_uc.uc_intr && w < 10; w++) { - error = msleep(&sc->sc_uc, &sc->sc_mtx, 0, "iwmuc", hz/10); - } - if (error || !sc->sc_uc.uc_ok) { - device_printf(sc->sc_dev, "could not load firmware\n"); - if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) { - device_printf(sc->sc_dev, "cpu1 status: 0x%x\n", - iwm_read_prph(sc, IWM_SB_CPU_1_STATUS)); - device_printf(sc->sc_dev, "cpu2 status: 0x%x\n", - iwm_read_prph(sc, IWM_SB_CPU_2_STATUS)); - } - } + /* 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); - return error; + /* load to FW the binary Secured sections of CPU1 */ + ret = iwm_pcie_load_cpu_sections_8000(sc, image, 1, + &first_ucode_section); + if (ret) + return ret; + + /* load to FW the binary sections of CPU2 */ + return iwm_pcie_load_cpu_sections_8000(sc, image, 2, + &first_ucode_section); } -/* iwlwifi: pcie/trans.c */ -static int -iwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +/* XXX Get rid of this definition */ +static inline void +iwm_enable_fw_load_int(struct iwm_softc *sc) { - int error; + IWM_DPRINTF(sc, IWM_DEBUG_INTR, "Enabling FW load interrupt\n"); + sc->sc_intmask = IWM_CSR_INT_BIT_FH_TX; + IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); +} - IWM_WRITE(sc, IWM_CSR_INT, ~0); +/* XXX Add proper rfkill support code */ +static int +iwm_start_fw(struct iwm_softc *sc, + const struct iwm_fw_sects *fw) +{ + int ret; - if ((error = iwm_nic_init(sc)) != 0) { - device_printf(sc->sc_dev, "unable to init nic\n"); - return error; + /* This may fail if AMT took ownership of the device */ + if (iwm_prepare_card_hw(sc)) { + device_printf(sc->sc_dev, + "%s: Exit HW not ready\n", __func__); + ret = EIO; + goto out; } + IWM_WRITE(sc, IWM_CSR_INT, 0xFFFFFFFF); + + iwm_disable_interrupts(sc); + /* make sure rfkill handshake bits are cleared */ IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); /* clear (again), then enable host interrupts */ - IWM_WRITE(sc, IWM_CSR_INT, ~0); - iwm_enable_interrupts(sc); + IWM_WRITE(sc, IWM_CSR_INT, 0xFFFFFFFF); + + ret = iwm_nic_init(sc); + if (ret) { + device_printf(sc->sc_dev, "%s: Unable to init nic\n", __func__); + goto out; + } + + /* + * Now, we load the firmware and don't want to be interrupted, even + * by the RF-Kill interrupt (hence mask all the interrupt besides the + * FH_TX interrupt which is needed to load the firmware). If the + * RF-Kill switch is toggled, we will find out after having loaded + * the firmware and return the proper value to the caller. + */ + iwm_enable_fw_load_int(sc); /* really make sure rfkill handshake bits are cleared */ /* maybe we should write a few times more? just to make sure */ @@ -2709,7 +2661,15 @@ iwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); /* Load the given image to the HW */ - return iwm_load_firmware(sc, ucode_type); + if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) + ret = iwm_pcie_load_given_ucode_8000(sc, fw); + else + ret = iwm_pcie_load_given_ucode(sc, fw); + + /* XXX re-check RF-Kill state */ + +out: + return ret; } static int @@ -2728,7 +2688,7 @@ static int iwm_send_phy_cfg_cmd(struct iwm_softc *sc) { struct iwm_phy_cfg_cmd phy_cfg_cmd; - enum iwm_ucode_type ucode_type = sc->sc_uc_current; + enum iwm_ucode_type ucode_type = sc->cur_ucode; /* Set parameters */ phy_cfg_cmd.phy_cfg = htole32(iwm_mvm_get_phy_config(sc)); @@ -2744,6 +2704,83 @@ iwm_send_phy_cfg_cmd(struct iwm_softc *sc) } static int +iwm_alive_fn(struct iwm_softc *sc, struct iwm_rx_packet *pkt, void *data) +{ + struct iwm_mvm_alive_data *alive_data = data; + struct iwm_mvm_alive_resp_ver1 *palive1; + struct iwm_mvm_alive_resp_ver2 *palive2; + struct iwm_mvm_alive_resp *palive; + + if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive1)) { + palive1 = (void *)pkt->data; + + sc->support_umac_log = FALSE; + sc->error_event_table = + le32toh(palive1->error_event_table_ptr); + sc->log_event_table = + le32toh(palive1->log_event_table_ptr); + alive_data->scd_base_addr = le32toh(palive1->scd_base_ptr); + + alive_data->valid = le16toh(palive1->status) == + IWM_ALIVE_STATUS_OK; + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", + le16toh(palive1->status), palive1->ver_type, + palive1->ver_subtype, palive1->flags); + } else if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive2)) { + palive2 = (void *)pkt->data; + sc->error_event_table = + le32toh(palive2->error_event_table_ptr); + sc->log_event_table = + le32toh(palive2->log_event_table_ptr); + alive_data->scd_base_addr = le32toh(palive2->scd_base_ptr); + sc->umac_error_event_table = + le32toh(palive2->error_info_addr); + + alive_data->valid = le16toh(palive2->status) == + IWM_ALIVE_STATUS_OK; + if (sc->umac_error_event_table) + sc->support_umac_log = TRUE; + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", + le16toh(palive2->status), palive2->ver_type, + palive2->ver_subtype, palive2->flags); + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "UMAC version: Major - 0x%x, Minor - 0x%x\n", + palive2->umac_major, palive2->umac_minor); + } else if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive)) { + palive = (void *)pkt->data; + + sc->error_event_table = + le32toh(palive->error_event_table_ptr); + sc->log_event_table = + le32toh(palive->log_event_table_ptr); + alive_data->scd_base_addr = le32toh(palive->scd_base_ptr); + sc->umac_error_event_table = + le32toh(palive->error_info_addr); + + alive_data->valid = le16toh(palive->status) == + IWM_ALIVE_STATUS_OK; + if (sc->umac_error_event_table) + sc->support_umac_log = TRUE; + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", + le16toh(palive->status), palive->ver_type, + palive->ver_subtype, palive->flags); + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "UMAC version: Major - 0x%x, Minor - 0x%x\n", + le32toh(palive->umac_major), + le32toh(palive->umac_minor)); + } + + return TRUE; +} + +static int iwm_wait_phy_db_entry(struct iwm_softc *sc, struct iwm_rx_packet *pkt, void *data) { @@ -2769,27 +2806,76 @@ static int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) { - enum iwm_ucode_type old_type = sc->sc_uc_current; + struct iwm_notification_wait alive_wait; + struct iwm_mvm_alive_data alive_data; + const struct iwm_fw_sects *fw; + enum iwm_ucode_type old_type = sc->cur_ucode; int error; + static const uint16_t alive_cmd[] = { IWM_MVM_ALIVE }; if ((error = iwm_read_firmware(sc, ucode_type)) != 0) { device_printf(sc->sc_dev, "iwm_read_firmware: failed %d\n", error); return error; } + fw = &sc->sc_fw.fw_sects[ucode_type]; + sc->cur_ucode = ucode_type; + sc->ucode_loaded = FALSE; + + memset(&alive_data, 0, sizeof(alive_data)); + iwm_init_notification_wait(sc->sc_notif_wait, &alive_wait, + alive_cmd, nitems(alive_cmd), + iwm_alive_fn, &alive_data); - sc->sc_uc_current = ucode_type; - error = iwm_start_fw(sc, ucode_type); + error = iwm_start_fw(sc, fw); if (error) { device_printf(sc->sc_dev, "iwm_start_fw: failed %d\n", error); - sc->sc_uc_current = old_type; + sc->cur_ucode = old_type; + iwm_remove_notification(sc->sc_notif_wait, &alive_wait); return error; } - error = iwm_post_alive(sc); + /* + * Some things may run in the background now, but we + * just wait for the ALIVE notification here. + */ + IWM_UNLOCK(sc); + error = iwm_wait_notification(sc->sc_notif_wait, &alive_wait, + IWM_MVM_UCODE_ALIVE_TIMEOUT); + IWM_LOCK(sc); if (error) { - device_printf(sc->sc_dev, "iwm_fw_alive: failed %d\n", error); + if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) { + device_printf(sc->sc_dev, + "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", + iwm_read_prph(sc, IWM_SB_CPU_1_STATUS), + iwm_read_prph(sc, IWM_SB_CPU_2_STATUS)); + } + sc->cur_ucode = old_type; + return error; + } + + if (!alive_data.valid) { + device_printf(sc->sc_dev, "%s: Loaded ucode is not valid\n", + __func__); + sc->cur_ucode = old_type; + return EIO; } + + iwm_trans_pcie_fw_alive(sc, alive_data.scd_base_addr); + + /* + * configure and operate fw paging mechanism. + * driver configures the paging flow only once, CPU2 paging image + * included in the IWM_UCODE_INIT image. + */ + if (fw->paging_mem_size) { + /* XXX implement FW paging */ + device_printf(sc->sc_dev, + "%s: XXX FW paging not implemented yet\n", __func__); + } + + if (!error) + sc->ucode_loaded = TRUE; return error; } @@ -3295,11 +3381,6 @@ iwm_mvm_rx_tx_cmd(struct iwm_softc *sc, if (--ring->queued < IWM_TX_RING_LOMARK) { sc->qfullmsk &= ~(1 << ring->qid); if (sc->qfullmsk == 0) { - /* - * Well, we're in interrupt context, but then again - * I guess net80211 does all sorts of stunts in - * interrupt context, so maybe this is no biggie. - */ iwm_start(sc); } } @@ -5139,7 +5220,7 @@ 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; + base = sc->umac_error_event_table; if (base < 0x800000) { device_printf(sc->sc_dev, "Invalid error log pointer 0x%08x\n", @@ -5194,7 +5275,7 @@ iwm_nic_error(struct iwm_softc *sc) uint32_t base; device_printf(sc->sc_dev, "dumping device error log\n"); - base = sc->sc_uc.uc_error_event_table; + base = sc->error_event_table; if (base < 0x800000) { device_printf(sc->sc_dev, "Invalid error log pointer 0x%08x\n", base); @@ -5256,7 +5337,7 @@ iwm_nic_error(struct iwm_softc *sc) device_printf(sc->sc_dev, "%08X | timestamp\n", table.u_timestamp); device_printf(sc->sc_dev, "%08X | flow_handler\n", table.flow_handler); - if (sc->sc_uc.uc_umac_error_event_table) + if (sc->umac_error_event_table) iwm_nic_umac_error(sc); } #endif @@ -5366,57 +5447,8 @@ iwm_notif_intr(struct iwm_softc *sc) case IWM_MFUART_LOAD_NOTIFICATION: break; - case IWM_MVM_ALIVE: { - struct iwm_mvm_alive_resp_v1 *resp1; - struct iwm_mvm_alive_resp_v2 *resp2; - struct iwm_mvm_alive_resp_v3 *resp3; - - if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp1)) { - resp1 = (void *)pkt->data; - sc->sc_uc.uc_error_event_table - = le32toh(resp1->error_event_table_ptr); - sc->sc_uc.uc_log_event_table - = le32toh(resp1->log_event_table_ptr); - sc->sched_base = le32toh(resp1->scd_base_ptr); - if (resp1->status == IWM_ALIVE_STATUS_OK) - sc->sc_uc.uc_ok = 1; - else - sc->sc_uc.uc_ok = 0; - } - - if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp2)) { - resp2 = (void *)pkt->data; - sc->sc_uc.uc_error_event_table - = le32toh(resp2->error_event_table_ptr); - 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 - sc->sc_uc.uc_ok = 0; - } - - if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp3)) { - resp3 = (void *)pkt->data; - sc->sc_uc.uc_error_event_table - = le32toh(resp3->error_event_table_ptr); - 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 - sc->sc_uc.uc_ok = 0; - } - - sc->sc_uc.uc_intr = 1; - wakeup(&sc->sc_uc); - break; } + case IWM_MVM_ALIVE: + break; case IWM_CALIB_RES_NOTIF_PHY_DB: break; @@ -5668,8 +5700,8 @@ iwm_intr(void *arg) IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask); - /* ignored */ - handled |= (r1 & (IWM_CSR_INT_BIT_ALIVE /*| IWM_CSR_INT_BIT_SCD*/)); + /* Safely ignore these bits for debug checks below */ + r1 &= ~(IWM_CSR_INT_BIT_ALIVE | IWM_CSR_INT_BIT_SCD); if (r1 & IWM_CSR_INT_BIT_SW_ERR) { int i; @@ -5788,19 +5820,19 @@ iwm_intr(void *arg) #define PCI_PRODUCT_INTEL_WL_8260_2 0x24f4 static const struct iwm_devices { - uint16_t device; - const char *name; + uint16_t device; + const struct iwm_cfg *cfg; } iwm_devices[] = { - { PCI_PRODUCT_INTEL_WL_3160_1, "Intel Dual Band Wireless AC 3160" }, - { PCI_PRODUCT_INTEL_WL_3160_2, "Intel Dual Band Wireless AC 3160" }, - { PCI_PRODUCT_INTEL_WL_3165_1, "Intel Dual Band Wireless AC 3165" }, - { PCI_PRODUCT_INTEL_WL_3165_2, "Intel Dual Band Wireless AC 3165" }, - { PCI_PRODUCT_INTEL_WL_7260_1, "Intel Dual Band Wireless AC 7260" }, - { PCI_PRODUCT_INTEL_WL_7260_2, "Intel Dual Band Wireless AC 7260" }, - { PCI_PRODUCT_INTEL_WL_7265_1, "Intel Dual Band Wireless AC 7265" }, - { PCI_PRODUCT_INTEL_WL_7265_2, "Intel Dual Band Wireless AC 7265" }, - { PCI_PRODUCT_INTEL_WL_8260_1, "Intel Dual Band Wireless AC 8260" }, - { PCI_PRODUCT_INTEL_WL_8260_2, "Intel Dual Band Wireless AC 8260" }, + { PCI_PRODUCT_INTEL_WL_3160_1, &iwm3160_cfg }, + { PCI_PRODUCT_INTEL_WL_3160_2, &iwm3160_cfg }, + { PCI_PRODUCT_INTEL_WL_3165_1, &iwm3165_cfg }, + { PCI_PRODUCT_INTEL_WL_3165_2, &iwm3165_cfg }, + { PCI_PRODUCT_INTEL_WL_7260_1, &iwm7260_cfg }, + { PCI_PRODUCT_INTEL_WL_7260_2, &iwm7260_cfg }, + { PCI_PRODUCT_INTEL_WL_7265_1, &iwm7265_cfg }, + { PCI_PRODUCT_INTEL_WL_7265_2, &iwm7265_cfg }, + { PCI_PRODUCT_INTEL_WL_8260_1, &iwm8260_cfg }, + { PCI_PRODUCT_INTEL_WL_8260_2, &iwm8260_cfg }, }; static int @@ -5811,7 +5843,7 @@ iwm_probe(device_t dev) for (i = 0; i < nitems(iwm_devices); i++) { if (pci_get_vendor(dev) == PCI_VENDOR_INTEL && pci_get_device(dev) == iwm_devices[i].device) { - device_set_desc(dev, iwm_devices[i].name); + device_set_desc(dev, iwm_devices[i].cfg->name); return (BUS_PROBE_DEFAULT); } } @@ -5823,41 +5855,25 @@ static int iwm_dev_check(device_t dev) { struct iwm_softc *sc; + uint16_t devid; + int i; sc = device_get_softc(dev); - switch (pci_get_device(dev)) { - case PCI_PRODUCT_INTEL_WL_3160_1: - case PCI_PRODUCT_INTEL_WL_3160_2: - sc->cfg = &iwm3160_cfg; - sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; - return (0); - case PCI_PRODUCT_INTEL_WL_3165_1: - case PCI_PRODUCT_INTEL_WL_3165_2: - sc->cfg = &iwm3165_cfg; - sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; - return (0); - case PCI_PRODUCT_INTEL_WL_7260_1: - case PCI_PRODUCT_INTEL_WL_7260_2: - sc->cfg = &iwm7260_cfg; - sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; - return (0); - case PCI_PRODUCT_INTEL_WL_7265_1: - case PCI_PRODUCT_INTEL_WL_7265_2: - sc->cfg = &iwm7265_cfg; - sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; - return (0); - case PCI_PRODUCT_INTEL_WL_8260_1: - case PCI_PRODUCT_INTEL_WL_8260_2: - sc->cfg = &iwm8260_cfg; - sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000; - return (0); - default: - device_printf(dev, "unknown adapter type\n"); - return ENXIO; + devid = pci_get_device(dev); + for (i = 0; i < nitems(iwm_devices); i++) { + if (iwm_devices[i].device == devid) { + sc->cfg = iwm_devices[i].cfg; + return (0); + } } + device_printf(dev, "unknown adapter type\n"); + return ENXIO; } +/* PCI registers */ +#define PCI_CFG_RETRY_TIMEOUT 0x041 + static int iwm_pci_attach(device_t dev) { @@ -5867,9 +5883,9 @@ iwm_pci_attach(device_t dev) sc = device_get_softc(dev); - /* Clear device-specific "PCI retry timeout" register (41h). */ - reg = pci_read_config(dev, 0x40, sizeof(reg)); - pci_write_config(dev, 0x40, reg & ~0xff00, sizeof(reg)); + /* We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state */ + pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1); /* Enable bus-mastering and hardware bug workaround. */ pci_enable_busmaster(dev); @@ -6377,11 +6393,12 @@ iwm_resume(device_t dev) { struct iwm_softc *sc = device_get_softc(dev); int do_reinit = 0; - uint16_t reg; - /* Clear device-specific "PCI retry timeout" register (41h). */ - reg = pci_read_config(dev, 0x40, sizeof(reg)); - pci_write_config(dev, 0x40, reg & ~0xff00, sizeof(reg)); + /* + * We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1); iwm_init_task(device_get_softc(dev)); IWM_LOCK(sc); |