Date: Tue, 26 Jul 2016 00:02:17 +0000 (UTC) From: Sean Bruno <sbruno@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r303327 - head/sys/dev/iwm Message-ID: <201607260002.u6Q02HKn066810@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: sbruno Date: Tue Jul 26 00:02:17 2016 New Revision: 303327 URL: https://svnweb.freebsd.org/changeset/base/303327 Log: iwm(4) synchronize driver to DragonFlyBSD version and recent f/w update. Submitted by: Kevin Bowling (kevin.bowling@kev009.com) MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D6967 Modified: head/sys/dev/iwm/if_iwm.c head/sys/dev/iwm/if_iwm_led.c head/sys/dev/iwm/if_iwm_led.h head/sys/dev/iwm/if_iwm_mac_ctxt.c head/sys/dev/iwm/if_iwm_pcie_trans.c head/sys/dev/iwm/if_iwm_phy_ctxt.c head/sys/dev/iwm/if_iwm_phy_db.c head/sys/dev/iwm/if_iwm_power.c head/sys/dev/iwm/if_iwm_scan.c head/sys/dev/iwm/if_iwm_scan.h head/sys/dev/iwm/if_iwm_time_event.c head/sys/dev/iwm/if_iwm_util.c head/sys/dev/iwm/if_iwm_util.h head/sys/dev/iwm/if_iwmreg.h head/sys/dev/iwm/if_iwmvar.h Modified: head/sys/dev/iwm/if_iwm.c ============================================================================== --- head/sys/dev/iwm/if_iwm.c Mon Jul 25 23:44:44 2016 (r303326) +++ head/sys/dev/iwm/if_iwm.c Tue Jul 26 00:02:17 2016 (r303327) @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ +/* $OpenBSD: if_iwm.c,v 1.42 2015/05/30 02:49:23 deraadt Exp $ */ /* * Copyright (c) 2014 genua mbh <info@genua.de> @@ -173,11 +173,23 @@ const uint8_t iwm_nvm_channels[] = { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165 }; -#define IWM_NUM_2GHZ_CHANNELS 14 - _Static_assert(nitems(iwm_nvm_channels) <= IWM_NUM_CHANNELS, "IWM_NUM_CHANNELS is too small"); +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 +}; +_Static_assert(nitems(iwm_nvm_channels_8000) <= IWM_NUM_CHANNELS_8000, + "IWM_NUM_CHANNELS_8000 is too small"); + +#define IWM_NUM_2GHZ_CHANNELS 14 +#define IWM_N_HW_ADDR_MASK 0xF + /* * XXX For now, there's simply a fixed set of rate table entries * that are populated. @@ -205,6 +217,11 @@ const struct iwm_rate { #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) +struct iwm_nvm_section { + uint16_t length; + uint8_t *data; +}; + 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, @@ -242,27 +259,45 @@ static void iwm_mvm_nic_config(struct iw 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 void iwm_enable_txq(struct iwm_softc *, int, int); +static int iwm_enable_txq(struct iwm_softc *, int, int, int); static int iwm_post_alive(struct iwm_softc *); 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 *, - uint16_t *); + uint16_t *, size_t); static uint32_t iwm_eeprom_channel_flags(uint16_t); static void iwm_add_channel_band(struct iwm_softc *, - struct ieee80211_channel[], int, int *, int, int, + struct ieee80211_channel[], int, int *, int, size_t, const uint8_t[]); static void iwm_init_channel_map(struct ieee80211com *, int, int *, struct ieee80211_channel[]); static int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, - const uint16_t *, const uint16_t *, uint8_t, - uint8_t); -struct iwm_nvm_section; + const uint16_t *, const uint16_t *, + const uint16_t *, const uint16_t *, + const uint16_t *); +static void iwm_set_hw_address_8000(struct iwm_softc *, + struct iwm_nvm_data *, + const uint16_t *, const uint16_t *); +static int iwm_get_sku(const struct iwm_softc *, const uint16_t *, + const uint16_t *); +static int iwm_get_nvm_version(const struct iwm_softc *, const uint16_t *); +static int iwm_get_radio_cfg(const struct iwm_softc *, const uint16_t *, + const uint16_t *); +static int iwm_get_n_hw_addrs(const struct iwm_softc *, + const uint16_t *); +static void iwm_set_radio_cfg(const struct iwm_softc *, + struct iwm_nvm_data *, uint32_t); static int 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_send_tx_ant_cfg(struct iwm_softc *, uint8_t); @@ -297,10 +332,8 @@ static int iwm_tx(struct iwm_softc *, st struct ieee80211_node *, int); static int iwm_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); -static void iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *, - struct iwm_mvm_add_sta_cmd_v5 *); static int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *, - struct iwm_mvm_add_sta_cmd_v6 *, + struct iwm_mvm_add_sta_cmd_v7 *, int *); static int iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *, int); @@ -321,6 +354,13 @@ static void iwm_setrates(struct iwm_soft static int iwm_media_change(struct ifnet *); static int iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void iwm_endscan_cb(void *, int); +static void iwm_mvm_fill_sf_command(struct iwm_softc *, + struct iwm_sf_cfg_cmd *, + struct ieee80211_node *); +static int iwm_mvm_sf_config(struct iwm_softc *, enum iwm_sf_state); +static int iwm_send_bt_init_conf(struct iwm_softc *); +static int iwm_send_update_mcc_cmd(struct iwm_softc *, const char *); +static void iwm_mvm_tt_tx_backoff(struct iwm_softc *, uint32_t); static int iwm_init_hw(struct iwm_softc *); static void iwm_init(struct iwm_softc *); static void iwm_start(struct iwm_softc *); @@ -331,10 +371,12 @@ static void iwm_parent(struct ieee80211c static const char * iwm_desc_lookup(uint32_t); static void iwm_nic_error(struct iwm_softc *); +static void iwm_nic_umac_error(struct iwm_softc *); #endif static void iwm_notif_intr(struct iwm_softc *); static void iwm_intr(void *); static int iwm_attach(device_t); +static int iwm_is_valid_ether_addr(uint8_t *); static void iwm_preinit(void *); static int iwm_detach_local(struct iwm_softc *sc, int); static void iwm_init_task(void *); @@ -477,6 +519,12 @@ iwm_read_firmware(struct iwm_softc *sc, } fw->fw_fp = fwp; + /* (Re-)Initialize default values. */ + sc->sc_capaflags = 0; + sc->sc_capa_n_scan_channels = IWM_MAX_NUM_SCAN_CHANNELS; + memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa)); + memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc)); + /* * Parse firmware contents */ @@ -490,7 +538,10 @@ iwm_read_firmware(struct iwm_softc *sc, goto out; } - sc->sc_fwver = le32toh(uhdr->ver); + snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)", + IWM_UCODE_MAJOR(le32toh(uhdr->ver)), + IWM_UCODE_MINOR(le32toh(uhdr->ver)), + IWM_UCODE_API(le32toh(uhdr->ver))); data = uhdr->data; len = fw->fw_fp->datasize - sizeof(*uhdr); @@ -527,7 +578,8 @@ iwm_read_firmware(struct iwm_softc *sc, sc->sc_capa_max_probe_len = le32toh(*(const uint32_t *)tlv_data); /* limit it to something sensible */ - if (sc->sc_capa_max_probe_len > (1<<16)) { + if (sc->sc_capa_max_probe_len > + IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) { IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, "%s: IWM_UCODE_TLV_PROBE_MAX_LEN " "ridiculous\n", __func__); @@ -578,7 +630,8 @@ iwm_read_firmware(struct iwm_softc *sc, 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)) { device_printf(sc->sc_dev, "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) < sizeof(uint32_t)\n", @@ -587,15 +640,16 @@ iwm_read_firmware(struct iwm_softc *sc, error = EINVAL; goto parse_out; } - if (le32toh(*(const uint32_t*)tlv_data) != 1) { + num_cpu = le32toh(*(const uint32_t *)tlv_data); + if (num_cpu < 1 || num_cpu > 2) { device_printf(sc->sc_dev, - "%s: driver supports " - "only TLV_NUM_OF_CPU == 1", + "%s: Driver supports only 1 or 2 CPUs\n", __func__); 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) { @@ -657,11 +711,80 @@ iwm_read_firmware(struct iwm_softc *sc, le32toh(*(const uint32_t *)tlv_data); break; - case IWM_UCODE_TLV_API_CHANGES_SET: - case IWM_UCODE_TLV_ENABLED_CAPABILITIES: + case IWM_UCODE_TLV_API_CHANGES_SET: { + const struct iwm_ucode_api *api; + if (tlv_len != sizeof(*api)) { + error = EINVAL; + goto parse_out; + } + api = (const struct iwm_ucode_api *)tlv_data; + /* Flags may exceed 32 bits in future firmware. */ + if (le32toh(api->api_index) > 0) { + device_printf(sc->sc_dev, + "unsupported API index %d\n", + le32toh(api->api_index)); + goto parse_out; + } + sc->sc_ucode_api = le32toh(api->api_flags); + break; + } + + case IWM_UCODE_TLV_ENABLED_CAPABILITIES: { + const struct iwm_ucode_capa *capa; + int idx, i; + if (tlv_len != sizeof(*capa)) { + error = EINVAL; + goto parse_out; + } + capa = (const struct iwm_ucode_capa *)tlv_data; + idx = le32toh(capa->api_index); + if (idx > howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) { + device_printf(sc->sc_dev, + "unsupported API index %d\n", idx); + goto parse_out; + } + for (i = 0; i < 32; i++) { + if ((le32toh(capa->api_capa) & (1U << i)) == 0) + continue; + setbit(sc->sc_enabled_capa, i + (32 * idx)); + } + break; + } + + case 48: /* undocumented TLV */ + case IWM_UCODE_TLV_SDIO_ADMA_ADDR: + case IWM_UCODE_TLV_FW_GSCAN_CAPA: /* ignore, not used by current driver */ break; + case IWM_UCODE_TLV_SEC_RT_USNIFFER: + if ((error = iwm_firmware_store_section(sc, + IWM_UCODE_TYPE_REGULAR_USNIFFER, tlv_data, + tlv_len)) != 0) + goto parse_out; + break; + + case IWM_UCODE_TLV_N_SCAN_CHANNELS: + if (tlv_len != sizeof(uint32_t)) { + error = EINVAL; + goto parse_out; + } + sc->sc_capa_n_scan_channels = + le32toh(*(const uint32_t *)tlv_data); + break; + + case IWM_UCODE_TLV_FW_VERSION: + if (tlv_len != sizeof(uint32_t) * 3) { + error = EINVAL; + goto parse_out; + } + snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), + "%d.%d.%d", + le32toh(((const uint32_t *)tlv_data)[0]), + le32toh(((const uint32_t *)tlv_data)[1]), + le32toh(((const uint32_t *)tlv_data)[2])); + break; + default: device_printf(sc->sc_dev, "%s: unknown firmware section %d, abort\n", @@ -710,7 +833,7 @@ iwm_dma_map_addr(void *arg, bus_dma_segm if (error != 0) return; KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); - *(bus_addr_t *)arg = segs[0].ds_addr; + *(bus_addr_t *)arg = segs[0].ds_addr; } static int @@ -720,6 +843,7 @@ iwm_dma_contig_alloc(bus_dma_tag_t tag, int error; dma->tag = NULL; + dma->map = NULL; dma->size = size; dma->vaddr = NULL; @@ -739,14 +863,16 @@ iwm_dma_contig_alloc(bus_dma_tag_t tag, if (error != 0) { bus_dmamem_free(dma->tag, dma->vaddr, dma->map); dma->vaddr = NULL; - goto fail; + goto fail; } bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); return 0; -fail: iwm_dma_contig_free(dma); +fail: + iwm_dma_contig_free(dma); + return error; } @@ -764,7 +890,6 @@ iwm_dma_contig_free(struct iwm_dma_info bus_dma_tag_destroy(dma->tag); dma->tag = NULL; } - } /* fwmem is used to load firmware onto the card */ @@ -901,7 +1026,7 @@ fail: iwm_free_rx_ring(sc, ring); static void iwm_disable_rx_dma(struct iwm_softc *sc) { - + /* XXX conditional nic locks are stupid */ /* XXX print out if we can't lock the NIC? */ if (iwm_nic_lock(sc)) { /* XXX handle if RX stop doesn't finish? */ @@ -915,6 +1040,11 @@ iwm_reset_rx_ring(struct iwm_softc *sc, { /* Reset the ring state */ ring->cur = 0; + + /* + * The hw rx ring index in shared memory must also be cleared, + * otherwise the discrepancy can cause reprocessing chaos. + */ memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); } @@ -1125,6 +1255,7 @@ iwm_ict_reset(struct iwm_softc *sc) /* Set physical address of ICT table (4KB aligned). */ IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG, IWM_CSR_DRAM_INT_TBL_ENABLE + | IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT); @@ -1217,7 +1348,7 @@ iwm_stop_device(struct iwm_softc *sc) */ iwm_disable_interrupts(sc); /* stop and reset the on-board processor */ - IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_NEVO_RESET); + IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET); /* * Even if we stop the HW, we still want the RF kill @@ -1263,9 +1394,11 @@ 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); + } } static int @@ -1295,16 +1428,12 @@ iwm_nic_rx_init(struct iwm_softc *sc) IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4); /* Enable RX. */ - /* - * Note: Linux driver also sets this: - * (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | - * - * It causes weird behavior. YMMV. - */ IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG, IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | /* HW bug */ IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | + IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | + (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K | IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS); @@ -1318,7 +1447,7 @@ iwm_nic_rx_init(struct iwm_softc *sc) * Thus sayeth el jefe (iwlwifi) via a comment: * * This value should initially be 0 (before preparing any - * RBs), should be 8 after preparing the first 8 RBs (for example) + * RBs), should be 8 after preparing the first 8 RBs (for example) */ IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, 8); @@ -1354,6 +1483,9 @@ iwm_nic_tx_init(struct iwm_softc *sc) qid, txq->desc, (unsigned long) (txq->desc_dma.paddr >> 8)); } + + iwm_write_prph(sc, IWM_SCD_GP_CTRL, IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE); + iwm_nic_unlock(sc); return 0; @@ -1365,7 +1497,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); @@ -1392,52 +1525,79 @@ const uint8_t iwm_mvm_ac_to_tx_fifo[] = IWM_MVM_TX_FIFO_BK, }; -static void -iwm_enable_txq(struct iwm_softc *sc, int qid, int fifo) +static int +iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo) { if (!iwm_nic_lock(sc)) { device_printf(sc->sc_dev, "%s: cannot enable txq %d\n", __func__, qid); - return; /* XXX return EBUSY */ + return EBUSY; } - /* unactivate before configuration */ - iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), - (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) - | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); + IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); - if (qid != IWM_MVM_CMD_QUEUE) { - iwm_set_bits_prph(sc, IWM_SCD_QUEUECHAIN_SEL, (1 << qid)); - } + if (qid == IWM_MVM_CMD_QUEUE) { + /* unactivate before configuration */ + iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), + (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) + | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); + + iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); + + iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); + + iwm_write_mem32(sc, sc->sched_base + 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) + + 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)); + + 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) | + (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | + IWM_SCD_QUEUE_STTS_REG_MSK); + } else { + struct iwm_scd_txq_cfg_cmd cmd; + int error; - iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); + iwm_nic_unlock(sc); - IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); - iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); + memset(&cmd, 0, sizeof(cmd)); + cmd.scd_queue = qid; + cmd.enable = 1; + cmd.sta_id = sta_id; + cmd.tx_fifo = fifo; + cmd.aggregate = 0; + cmd.window = IWM_FRAME_LIMIT; + + error = iwm_mvm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, IWM_CMD_SYNC, + sizeof(cmd), &cmd); + if (error) { + device_printf(sc->sc_dev, + "cannot enable txq %d\n", qid); + return error; + } + + if (!iwm_nic_lock(sc)) + return EBUSY; + } - iwm_write_mem32(sc, sc->sched_base + 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) + - 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)); - - 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) | - (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | - IWM_SCD_QUEUE_STTS_REG_MSK); + iwm_write_prph(sc, IWM_SCD_EN_CTRL, + iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qid); iwm_nic_unlock(sc); - IWM_DPRINTF(sc, IWM_DEBUG_XMIT, - "%s: enabled txq %d FIFO %d\n", + IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: enabled txq %d FIFO %d\n", __func__, qid, fifo); + + return 0; } static int @@ -1445,16 +1605,16 @@ iwm_post_alive(struct iwm_softc *sc) { int nwords; int error, chnl; + uint32_t base; if (!iwm_nic_lock(sc)) return EBUSY; - if (sc->sched_base != iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR)) { + base = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR); + if (sc->sched_base != base) { device_printf(sc->sc_dev, - "%s: sched addr mismatch", - __func__); - error = EINVAL; - goto out; + "%s: sched addr mismatch: alive: 0x%x prph: 0x%x\n", + __func__, sc->sched_base, base); } iwm_ict_reset(sc); @@ -1474,8 +1634,15 @@ iwm_post_alive(struct iwm_softc *sc) iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0); + iwm_nic_unlock(sc); + /* enable command channel */ - iwm_enable_txq(sc, IWM_MVM_CMD_QUEUE, 7); + error = iwm_enable_txq(sc, 0 /* unused */, IWM_MVM_CMD_QUEUE, 7); + if (error) + return error; + + if (!iwm_nic_lock(sc)) + return EBUSY; iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff); @@ -1490,11 +1657,13 @@ 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); + iwm_nic_unlock(sc); return error; } @@ -1508,17 +1677,25 @@ iwm_post_alive(struct iwm_softc *sc) const int nvm_to_read[] = { IWM_NVM_SECTION_TYPE_HW, IWM_NVM_SECTION_TYPE_SW, + 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 */ -#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWM_MAX_NVM_SECTION_SIZE 7000 +#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024) +#define IWM_MAX_NVM_SECTION_SIZE 8192 #define IWM_NVM_WRITE_OPCODE 1 #define IWM_NVM_READ_OPCODE 0 +/* load nvm chunk response */ +#define IWM_READ_NVM_CHUNK_SUCCEED 0 +#define IWM_READ_NVM_CHUNK_INVALID_ADDRESS 1 + static int iwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section, uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len) @@ -1538,20 +1715,24 @@ iwm_nvm_read_chunk(struct iwm_softc *sc, IWM_CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, }, }; - int ret, bytes_read, offset_read; + int ret, offset_read; + size_t bytes_read; uint8_t *resp_data; cmd.len[0] = sizeof(struct iwm_nvm_access_cmd); ret = iwm_send_cmd(sc, &cmd); - if (ret) + if (ret) { + device_printf(sc->sc_dev, + "Could not send NVM_ACCESS command (error=%d)\n", ret); return ret; + } pkt = cmd.resp_pkt; if (pkt->hdr.flags & IWM_CMD_FAILED_MSK) { device_printf(sc->sc_dev, - "%s: Bad return from IWM_NVM_ACCES_COMMAND (0x%08X)\n", - __func__, pkt->hdr.flags); + "Bad return from IWM_NVM_ACCES_COMMAND (0x%08X)\n", + pkt->hdr.flags); ret = EIO; goto exit; } @@ -1564,17 +1745,25 @@ iwm_nvm_read_chunk(struct iwm_softc *sc, offset_read = le16toh(nvm_resp->offset); resp_data = nvm_resp->data; if (ret) { - device_printf(sc->sc_dev, - "%s: NVM access command failed with status %d\n", - __func__, ret); + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "NVM access command failed with status %d\n", ret); ret = EINVAL; goto exit; } if (offset_read != offset) { device_printf(sc->sc_dev, - "%s: NVM ACCESS response with invalid offset %d\n", - __func__, offset_read); + "NVM ACCESS response with invalid offset %d\n", + offset_read); + ret = EINVAL; + goto exit; + } + + if (bytes_read > length) { + device_printf(sc->sc_dev, + "NVM ACCESS response with too much data " + "(%d bytes requested, %zd bytes received)\n", + length, bytes_read); ret = EINVAL; goto exit; } @@ -1589,7 +1778,7 @@ iwm_nvm_read_chunk(struct iwm_softc *sc, /* * Reads an NVM section completely. - * NICs prior to 7000 family doesn't have a real NVM, but just read + * NICs prior to 7000 family don't have a real NVM, but just read * section 0 which is the EEPROM. Because the EEPROM reading is unlimited * by uCode, we need to manually check in this case that we don't * overflow and try to read more than the EEPROM size. @@ -1599,32 +1788,34 @@ iwm_nvm_read_chunk(struct iwm_softc *sc, */ static int iwm_nvm_read_section(struct iwm_softc *sc, - uint16_t section, uint8_t *data, uint16_t *len) + uint16_t section, uint8_t *data, uint16_t *len, size_t max_len) { - uint16_t length, seglen; - int error; + uint16_t chunklen, seglen; + int error = 0; + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "reading NVM section %d\n", section); - /* Set nvm section read length */ - length = seglen = IWM_NVM_DEFAULT_CHUNK_SIZE; + chunklen = seglen = IWM_NVM_DEFAULT_CHUNK_SIZE; *len = 0; - /* Read the NVM until exhausted (reading less than requested) */ - while (seglen == length) { + /* Read NVM chunks until exhausted (reading less than requested) */ + while (seglen == chunklen && *len < max_len) { error = iwm_nvm_read_chunk(sc, - section, *len, length, data, &seglen); + section, *len, chunklen, data, &seglen); if (error) { - device_printf(sc->sc_dev, - "Cannot read NVM from section " - "%d offset %d, length %d\n", - section, *len, length); + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "Cannot read from NVM section " + "%d at offset %d\n", section, *len); return error; } *len += seglen; } IWM_DPRINTF(sc, IWM_DEBUG_RESET, - "NVM section %d read completed\n", section); - return 0; + "NVM section %d read completed (%d bytes, error=%d)\n", + section, *len, error); + return error; } /* @@ -1634,7 +1825,7 @@ iwm_nvm_read_section(struct iwm_softc *s /* iwlwifi/iwl-nvm-parse.c */ /* NVM offsets (in words) definitions */ -enum wkp_nvm_offsets { +enum iwm_nvm_offsets { /* NVM HW-Section offset (in words) definitions */ IWM_HW_ADDR = 0x15, @@ -1651,6 +1842,32 @@ enum wkp_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), @@ -1667,6 +1884,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_FLAVOR_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 /** @@ -1718,7 +1942,8 @@ iwm_eeprom_channel_flags(uint16_t ch_fla static void iwm_add_channel_band(struct iwm_softc *sc, struct ieee80211_channel chans[], - int maxchans, int *nchans, int ch_idx, int ch_num, const uint8_t bands[]) + int maxchans, int *nchans, int ch_idx, size_t ch_num, + const uint8_t bands[]) { const uint16_t * const nvm_ch_flags = sc->sc_nvm.nvm_ch_flags; uint32_t nflags; @@ -1728,7 +1953,10 @@ iwm_add_channel_band(struct iwm_softc *s for (; ch_idx < ch_num; ch_idx++) { ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx); - ieee = iwm_nvm_channels[ch_idx]; + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) + ieee = iwm_nvm_channels[ch_idx]; + else + ieee = iwm_nvm_channels_8000[ch_idx]; if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) { IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, @@ -1760,6 +1988,7 @@ iwm_init_channel_map(struct ieee80211com struct iwm_softc *sc = ic->ic_softc; struct iwm_nvm_data *data = &sc->sc_nvm; uint8_t bands[IEEE80211_MODE_BYTES]; + size_t ch_num; memset(bands, 0, sizeof(bands)); /* 1-13: 11b/g channels. */ @@ -1774,51 +2003,182 @@ iwm_init_channel_map(struct ieee80211com IWM_NUM_2GHZ_CHANNELS - 1, IWM_NUM_2GHZ_CHANNELS, bands); if (data->sku_cap_band_52GHz_enable) { + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) + ch_num = nitems(iwm_nvm_channels); + else + ch_num = nitems(iwm_nvm_channels_8000); memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11A); iwm_add_channel_band(sc, chans, maxchans, nchans, - IWM_NUM_2GHZ_CHANNELS, nitems(iwm_nvm_channels), bands); + IWM_NUM_2GHZ_CHANNELS, ch_num, bands); } } +static 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 + */ + IEEE80211_ADDR_COPY(data->hw_addr, hw_addr); + + /* + * 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 (!IEEE80211_ADDR_EQ(reserved_mac, hw_addr) && + !IEEE80211_ADDR_EQ(ieee80211broadcastaddr, data->hw_addr) && + iwm_is_valid_ether_addr(data->hw_addr) && + !IEEE80211_IS_MULTICAST(data->hw_addr)) + return; + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "%s: mac address from nvm override section invalid\n", + __func__); + } + + 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; + } + + device_printf(sc->sc_dev, "%s: mac address not found\n", __func__); + memset(data->hw_addr, 0, sizeof(data->hw_addr)); +} + +static int +iwm_get_sku(const struct iwm_softc *sc, const uint16_t *nvm_sw, + const uint16_t *phy_sku) +{ + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + IWM_SKU); + + return le32_to_cpup((const uint32_t *)(phy_sku + IWM_SKU_8000)); +} + +static int +iwm_get_nvm_version(const struct iwm_softc *sc, const uint16_t *nvm_sw) +{ + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + IWM_NVM_VERSION); + else + return le32_to_cpup((const uint32_t *)(nvm_sw + + IWM_NVM_VERSION_8000)); +} + +static int +iwm_get_radio_cfg(const struct iwm_softc *sc, const uint16_t *nvm_sw, + const uint16_t *phy_sku) +{ + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + IWM_RADIO_CFG); + + return le32_to_cpup((const uint32_t *)(phy_sku + IWM_RADIO_CFG_8000)); +} + +static int +iwm_get_n_hw_addrs(const struct iwm_softc *sc, const uint16_t *nvm_sw) +{ + int n_hw_addr; + + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS); + + n_hw_addr = le32_to_cpup((const uint32_t *)(nvm_sw + IWM_N_HW_ADDRS_8000)); + + return n_hw_addr & IWM_N_HW_ADDR_MASK; +} + +static void +iwm_set_radio_cfg(const struct iwm_softc *sc, struct iwm_nvm_data *data, + uint32_t radio_cfg) +{ + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) { + 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); + return; + } + + /* set the radio configuration for family 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_FLAVOR_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); +} + static int iwm_parse_nvm_data(struct iwm_softc *sc, - const uint16_t *nvm_hw, const uint16_t *nvm_sw, - const uint16_t *nvm_calib, uint8_t tx_chains, uint8_t rx_chains) + const uint16_t *nvm_hw, const uint16_t *nvm_sw, + const uint16_t *nvm_calib, const uint16_t *mac_override, + const uint16_t *phy_sku, const uint16_t *regulatory) { struct iwm_nvm_data *data = &sc->sc_nvm; uint8_t hw_addr[IEEE80211_ADDR_LEN]; - uint16_t radio_cfg, sku; + uint32_t sku, radio_cfg; - data->nvm_version = le16_to_cpup(nvm_sw + IWM_NVM_VERSION); + data->nvm_version = iwm_get_nvm_version(sc, nvm_sw); - 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); + radio_cfg = iwm_get_radio_cfg(sc, nvm_sw, phy_sku); + iwm_set_radio_cfg(sc, data, radio_cfg); - sku = le16_to_cpup(nvm_sw + IWM_SKU); + sku = iwm_get_sku(sc, nvm_sw, phy_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 = 0; - data->n_hw_addrs = le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS); - - data->xtal_calib[0] = *(nvm_calib + IWM_XTAL_CALIB); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201607260002.u6Q02HKn066810>