Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F173724638
D60079.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
D60079.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D60079: iwm: add MSI-X support
Attached
Detach File
Event Timeline
Log In to Comment