aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/iwm/if_iwm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/iwm/if_iwm.c')
-rw-r--r--sys/dev/iwm/if_iwm.c921
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);