Page MenuHomeFreeBSD

D60079.diff
No OneTemporary

D60079.diff

diff --git a/sys/dev/iwm/if_iwm.c b/sys/dev/iwm/if_iwm.c
--- a/sys/dev/iwm/if_iwm.c
+++ b/sys/dev/iwm/if_iwm.c
@@ -402,6 +402,7 @@
static void iwm_handle_rxb(struct iwm_softc *, struct mbuf *);
static void iwm_notif_intr(struct iwm_softc *);
static void iwm_intr(void *);
+static void iwm_intr_msix(void *);
static int iwm_attach(device_t);
static int iwm_is_valid_ether_addr(uint8_t *);
static void iwm_preinit(void *);
@@ -1266,8 +1267,21 @@
static void
iwm_enable_interrupts(struct iwm_softc *sc)
{
- sc->sc_intmask = IWM_CSR_INI_SET_MASK;
- IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
+ if (!sc->sc_msix) {
+ sc->sc_intmask = IWM_CSR_INI_SET_MASK;
+ IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
+ } else {
+ /*
+ * fh/hw_mask keeps all the unmasked causes.
+ * Unlike msi, in msix cause is enabled when it is unset.
+ */
+ sc->sc_hw_mask = sc->sc_hw_init_mask;
+ sc->sc_fh_mask = sc->sc_fh_init_mask;
+ IWM_WRITE(sc, IWM_CSR_MSIX_FH_INT_MASK_AD,
+ ~sc->sc_fh_mask);
+ IWM_WRITE(sc, IWM_CSR_MSIX_HW_INT_MASK_AD,
+ ~sc->sc_hw_mask);
+ }
}
static void
@@ -1279,12 +1293,19 @@
static void
iwm_disable_interrupts(struct iwm_softc *sc)
{
- /* disable interrupts */
- IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
+ if (!sc->sc_msix) {
+ /* disable interrupts */
+ IWM_WRITE(sc, IWM_CSR_INT_MASK, 0);
- /* acknowledge all interrupts */
- IWM_WRITE(sc, IWM_CSR_INT, ~0);
- IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0);
+ /* acknowledge all interrupts */
+ IWM_WRITE(sc, IWM_CSR_INT, ~0);
+ IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0);
+ } else {
+ IWM_WRITE(sc, IWM_CSR_MSIX_FH_INT_MASK_AD,
+ sc->sc_fh_init_mask);
+ IWM_WRITE(sc, IWM_CSR_MSIX_HW_INT_MASK_AD,
+ sc->sc_hw_init_mask);
+ }
}
static void
@@ -1398,8 +1419,18 @@
IWM_SETBITS(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET);
DELAY(5000);
+ /*
+ * Upon stop, the IVAR table gets erased, so msi-x won't
+ * work. This causes a bug in RF-KILL flows, since the interrupt
+ * that enables radio won't fire on the correct irq, and the
+ * driver won't be able to handle the interrupt.
+ * Configure the IVAR table again after reset.
+ */
+ iwm_conf_msix_hw(sc, 1);
+
/*
* Upon stop, the APM issues an interrupt if HW RF kill is set.
+ * Clear the interrupt again.
*/
iwm_disable_interrupts(sc);
@@ -2709,8 +2740,17 @@
iwm_enable_fw_load_int(struct iwm_softc *sc)
{
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);
+
+ if (!sc->sc_msix) {
+ sc->sc_intmask = IWM_CSR_INT_BIT_FH_TX;
+ IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
+ } else {
+ IWM_WRITE(sc, IWM_CSR_MSIX_HW_INT_MASK_AD,
+ sc->sc_hw_init_mask);
+ IWM_WRITE(sc, IWM_CSR_MSIX_FH_INT_MASK_AD,
+ ~IWM_MSIX_FH_INT_CAUSES_D2S_CH0_NUM);
+ sc->sc_fh_mask = IWM_MSIX_FH_INT_CAUSES_D2S_CH0_NUM;
+ }
}
/* XXX Add proper rfkill support code */
@@ -5955,6 +5995,95 @@
return;
}
+static void
+iwm_intr_msix(void *arg)
+{
+ struct iwm_softc *sc = arg;
+ uint32_t inta_fh, inta_hw;
+ int vector = 0;
+
+ IWM_LOCK(sc);
+
+ inta_fh = IWM_READ(sc, IWM_CSR_MSIX_FH_INT_CAUSES_AD);
+ inta_hw = IWM_READ(sc, IWM_CSR_MSIX_HW_INT_CAUSES_AD);
+ /* acknowledge all interrupts */
+ IWM_WRITE(sc, IWM_CSR_MSIX_FH_INT_CAUSES_AD, inta_fh);
+ IWM_WRITE(sc, IWM_CSR_MSIX_HW_INT_CAUSES_AD, inta_hw);
+ inta_fh &= sc->sc_fh_mask;
+ inta_hw &= sc->sc_hw_mask;
+
+ if (inta_fh & IWM_MSIX_FH_INT_CAUSES_Q0 ||
+ inta_fh & IWM_MSIX_FH_INT_CAUSES_Q1)
+ iwm_notif_intr(sc);
+
+ /* firmware chunk loaded */
+ if (inta_fh & IWM_MSIX_FH_INT_CAUSES_D2S_CH0_NUM) {
+ sc->sc_fw_chunk_done = 1;
+ wakeup(&sc->sc_fw);
+ }
+
+ if ((inta_fh & IWM_MSIX_FH_INT_CAUSES_FH_ERR) ||
+ (inta_hw & IWM_MSIX_HW_INT_CAUSES_REG_SW_ERR) ||
+ (inta_hw & IWM_MSIX_HW_INT_CAUSES_REG_SW_ERR_V2)) {
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211vap *vap;
+ int i;
+
+#ifdef IWM_DEBUG
+ iwm_nic_error(sc);
+#endif
+ /* Dump driver status (TX and RX rings) while we're here. */
+ device_printf(sc->sc_dev, "driver status:\n");
+ for (i = 0; i < IWM_MAX_QUEUES; i++) {
+ struct iwm_tx_ring *ring = &sc->txq[i];
+ device_printf(sc->sc_dev,
+ " tx ring %2d: qid=%-2d cur=%-3d "
+ "queued=%-3d\n",
+ i, ring->qid, ring->cur, ring->queued);
+ }
+ device_printf(sc->sc_dev, " rx ring: cur=%d\n", sc->rxq.cur);
+
+ /* Reset our firmware state tracking. */
+ sc->sc_firmware_state = 0;
+ IWM_UNLOCK(sc);
+
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+ if (vap == NULL) {
+ printf("%s: null vap\n", __func__);
+ return;
+ }
+
+ device_printf(sc->sc_dev,
+ "%s: controller panicked, iv_state = %d; restarting\n",
+ __func__, vap->iv_state);
+
+ ieee80211_restart_all(ic);
+ return;
+ }
+
+ if (inta_hw & IWM_MSIX_HW_INT_CAUSES_REG_RF_KILL)
+ taskqueue_enqueue(sc->sc_tq, &sc->sc_rftoggle_task);
+
+ if (inta_hw & IWM_MSIX_HW_INT_CAUSES_REG_HW_ERR) {
+ device_printf(sc->sc_dev, "hardware error, stopping device\n");
+ iwm_stop(sc);
+ goto out;
+ }
+
+ /*
+ * Before sending the interrupt the HW disables it to prevent
+ * a nested interrupt. This is done by writing 1 to the
+ * corresponding bit in the mask register. After handling the
+ * interrupt, it should be re-enabled by clearing this bit.
+ * This register is defined as a write 1 clear (W1C) register,
+ * meaning that it's being cleared by writing 1 to the bit.
+ */
+ IWM_WRITE(sc, IWM_CSR_MSIX_AUTOMASK_ST_AD, 1 << vector);
+
+ out:
+ IWM_UNLOCK(sc);
+}
+
/*
* Autoconf glue-sniffing
*/
@@ -6072,8 +6201,12 @@
/* Install interrupt handler. */
count = 1;
rid = 0;
- if (pci_alloc_msi(dev, &count) == 0)
+ if (pci_alloc_msix(dev, &count) == 0) {
rid = 1;
+ sc->sc_msix = 1;
+ } else if (pci_alloc_msi(dev, &count) == 0) {
+ rid = 1;
+ }
sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
(rid != 0 ? 0 : RF_SHAREABLE));
if (sc->sc_irq == NULL) {
@@ -6081,7 +6214,7 @@
return (ENXIO);
}
error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
- NULL, iwm_intr, sc, &sc->sc_ih);
+ NULL, sc->sc_msix ? iwm_intr_msix : iwm_intr, sc, &sc->sc_ih);
if (error != 0) {
device_printf(dev, "can't establish interrupt");
return (error);
diff --git a/sys/dev/iwm/if_iwm_pcie_trans.h b/sys/dev/iwm/if_iwm_pcie_trans.h
--- a/sys/dev/iwm/if_iwm_pcie_trans.h
+++ b/sys/dev/iwm/if_iwm_pcie_trans.h
@@ -129,6 +129,8 @@
extern int iwm_apm_init(struct iwm_softc *sc);
extern void iwm_apm_stop(struct iwm_softc *sc);
extern int iwm_clear_persistence_bit(struct iwm_softc *sc);
+extern void iwm_conf_msix_hw(struct iwm_softc *sc, int stopped);
+extern void iwm_init_msix_hw(struct iwm_softc *sc);
extern int iwm_start_hw(struct iwm_softc *sc);
extern void iwm_set_pwr(struct iwm_softc *sc);
extern int iwm_pcie_rx_stop(struct iwm_softc *sc);
diff --git a/sys/dev/iwm/if_iwm_pcie_trans.c b/sys/dev/iwm/if_iwm_pcie_trans.c
--- a/sys/dev/iwm/if_iwm_pcie_trans.c
+++ b/sys/dev/iwm/if_iwm_pcie_trans.c
@@ -342,10 +342,20 @@
void
iwm_enable_rfkill_int(struct iwm_softc *sc)
{
- sc->sc_intmask = IWM_CSR_INT_BIT_RF_KILL;
- IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
- IWM_SETBITS(sc, IWM_CSR_GP_CNTRL,
- IWM_CSR_GP_CNTRL_REG_FLAG_RFKILL_WAKE_L1A_EN);
+ if (!sc->sc_msix) {
+ sc->sc_intmask = IWM_CSR_INT_BIT_RF_KILL;
+ IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask);
+ } else {
+ IWM_WRITE(sc, IWM_CSR_MSIX_FH_INT_MASK_AD,
+ sc->sc_fh_init_mask);
+ IWM_WRITE(sc, IWM_CSR_MSIX_HW_INT_MASK_AD,
+ ~IWM_MSIX_HW_INT_CAUSES_REG_RF_KILL);
+ sc->sc_hw_mask = IWM_MSIX_HW_INT_CAUSES_REG_RF_KILL;
+ }
+
+ if (sc->cfg->device_family >= IWM_DEVICE_FAMILY_9000)
+ IWM_SETBITS(sc, IWM_CSR_GP_CNTRL,
+ IWM_CSR_GP_CNTRL_REG_FLAG_RFKILL_WAKE_L1A_EN);
}
int
@@ -640,6 +650,107 @@
return 0;
}
+void
+iwm_init_msix_hw(struct iwm_softc *sc)
+{
+ iwm_conf_msix_hw(sc, 0);
+
+ if (!sc->sc_msix)
+ return;
+
+ sc->sc_fh_init_mask = ~IWM_READ(sc, IWM_CSR_MSIX_FH_INT_MASK_AD);
+ sc->sc_fh_mask = sc->sc_fh_init_mask;
+ sc->sc_hw_init_mask = ~IWM_READ(sc, IWM_CSR_MSIX_HW_INT_MASK_AD);
+ sc->sc_hw_mask = sc->sc_hw_init_mask;
+}
+
+void
+iwm_conf_msix_hw(struct iwm_softc *sc, int stopped)
+{
+ int vector = 0;
+
+ if (!sc->sc_msix) {
+ /* Newer chips default to MSIX. */
+ if (sc->cfg->mqrx_supported && !stopped && iwm_nic_lock(sc)) {
+ iwm_write_prph(sc, IWM_UREG_CHICK,
+ IWM_UREG_CHICK_MSI_ENABLE);
+ iwm_nic_unlock(sc);
+ }
+ return;
+ }
+
+ if (!stopped && iwm_nic_lock(sc)) {
+ iwm_write_prph(sc, IWM_UREG_CHICK, IWM_UREG_CHICK_MSIX_ENABLE);
+ iwm_nic_unlock(sc);
+ }
+
+ /* Disable all interrupts */
+ IWM_WRITE(sc, IWM_CSR_MSIX_FH_INT_MASK_AD, ~0);
+ IWM_WRITE(sc, IWM_CSR_MSIX_HW_INT_MASK_AD, ~0);
+
+ /* Map fallback-queue (command/mgmt) to a single vector */
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_RX_IVAR(0),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ /* Map RSS queue (data) to the same vector */
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_RX_IVAR(1),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+
+ /* Enable the RX queues cause interrupts */
+ IWM_CLRBITS(sc, IWM_CSR_MSIX_FH_INT_MASK_AD,
+ IWM_MSIX_FH_INT_CAUSES_Q0 | IWM_MSIX_FH_INT_CAUSES_Q1);
+
+ /* Map non-RX causes to the same vector */
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_D2S_CH0_NUM),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_D2S_CH1_NUM),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_S2D),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_FH_ERR),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_ALIVE),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_WAKEUP),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_IML),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_CT_KILL),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_RF_KILL),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_PERIODIC),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_SW_ERR),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_SCD),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_FH_TX),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_HW_ERR),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+ IWM_WRITE_1(sc, IWM_CSR_MSIX_IVAR(IWM_MSIX_IVAR_CAUSE_REG_HAP),
+ vector | IWM_MSIX_NON_AUTO_CLEAR_CAUSE);
+
+ /* Enable non-RX causes interrupts */
+ IWM_CLRBITS(sc, IWM_CSR_MSIX_FH_INT_MASK_AD,
+ IWM_MSIX_FH_INT_CAUSES_D2S_CH0_NUM |
+ IWM_MSIX_FH_INT_CAUSES_D2S_CH1_NUM |
+ IWM_MSIX_FH_INT_CAUSES_S2D |
+ IWM_MSIX_FH_INT_CAUSES_FH_ERR);
+ IWM_CLRBITS(sc, IWM_CSR_MSIX_HW_INT_MASK_AD,
+ IWM_MSIX_HW_INT_CAUSES_REG_ALIVE |
+ IWM_MSIX_HW_INT_CAUSES_REG_WAKEUP |
+ IWM_MSIX_HW_INT_CAUSES_REG_IML |
+ IWM_MSIX_HW_INT_CAUSES_REG_CT_KILL |
+ IWM_MSIX_HW_INT_CAUSES_REG_RF_KILL |
+ IWM_MSIX_HW_INT_CAUSES_REG_PERIODIC |
+ IWM_MSIX_HW_INT_CAUSES_REG_SW_ERR |
+ IWM_MSIX_HW_INT_CAUSES_REG_SCD |
+ IWM_MSIX_HW_INT_CAUSES_REG_FH_TX |
+ IWM_MSIX_HW_INT_CAUSES_REG_HW_ERR |
+ IWM_MSIX_HW_INT_CAUSES_REG_HAP);
+}
+
/* iwlwifi pcie/trans.c */
int
iwm_start_hw(struct iwm_softc *sc)
@@ -661,9 +772,7 @@
if ((error = iwm_apm_init(sc)) != 0)
return error;
- /* On newer chipsets MSI is disabled by default. */
- if (sc->cfg->mqrx_supported)
- iwm_write_prph(sc, IWM_UREG_CHICK, IWM_UREG_CHICK_MSI_ENABLE);
+ iwm_init_msix_hw(sc);
iwm_enable_rfkill_int(sc);
iwm_check_rfkill(sc);
diff --git a/sys/dev/iwm/if_iwmvar.h b/sys/dev/iwm/if_iwmvar.h
--- a/sys/dev/iwm/if_iwmvar.h
+++ b/sys/dev/iwm/if_iwmvar.h
@@ -475,6 +475,13 @@
int sc_intmask;
+ /* MSIX support */
+ int sc_msix;
+ uint32_t sc_fh_init_mask;
+ uint32_t sc_fh_mask;
+ uint32_t sc_hw_init_mask;
+ uint32_t sc_hw_mask;
+
/*
* So why do we need a separate stopped flag and a generation?
* the former protects the device from issuing commands when it's

File Metadata

Mime Type
text/plain
Expires
Mon, Sep 28, 11:26 PM (3 h, 42 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39839341
Default Alt Text
D60079.diff (12 KB)

Event Timeline