[iwm] Implement apmg_wake_up_wa workaround properly for 7000 family.

* Add iwm_pcie_set_cmd_in_flight() and iwm_pcie_clear_cmd_in_flight()
  helper methods.

* Use ring->queued tracking in the command queue to set/clear the
  cmd_hold_nic_awake bit at the right points.

Taken-From: Linux iwlwifi

Obtained from:	DragonflyBSD commit ce43f57f5308b579ea21e8a5a29969114ba2247d
This commit is contained in:
adrian 2017-02-08 06:53:23 +00:00
parent 0fae13e67a
commit 5b92aa7f37
5 changed files with 86 additions and 14 deletions

View File

@ -182,7 +182,8 @@ __FBSDID("$FreeBSD$");
#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
.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,
@ -1251,6 +1252,9 @@ iwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring)
sc->qfullmsk &= ~(1 << ring->qid);
ring->queued = 0;
ring->cur = 0;
if (ring->qid == IWM_MVM_CMD_QUEUE && sc->cmd_hold_nic_awake)
iwm_pcie_clear_cmd_in_flight(sc);
}
static void
@ -3338,6 +3342,18 @@ iwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt)
data->m = NULL;
}
wakeup(&ring->desc[pkt->hdr.idx]);
if (((pkt->hdr.idx + ring->queued) % IWM_TX_RING_COUNT) != ring->cur) {
device_printf(sc->sc_dev,
"%s: Some HCMDs skipped?: idx=%d queued=%d cur=%d\n",
__func__, pkt->hdr.idx, ring->queued, ring->cur);
/* XXX call iwm_force_nmi() */
}
KASSERT(ring->queued > 0, ("ring->queued is empty?"));
ring->queued--;
if (ring->queued == 0)
iwm_pcie_clear_cmd_in_flight(sc);
}
#if 0
@ -5580,9 +5596,6 @@ iwm_notif_intr(struct iwm_softc *sc)
ADVANCE_RXQ(sc);
}
IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
/*
* Tell the firmware what we have processed.
* Seems like the hardware gets upset unless we align

View File

@ -253,6 +253,9 @@ iwm_nic_lock(struct iwm_softc *sc)
{
int rv = 0;
if (sc->cmd_hold_nic_awake)
return 1;
IWM_SETBITS(sc, IWM_CSR_GP_CNTRL,
IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
@ -277,6 +280,9 @@ iwm_nic_lock(struct iwm_softc *sc)
void
iwm_nic_unlock(struct iwm_softc *sc)
{
if (sc->cmd_hold_nic_awake)
return;
IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
}
@ -583,3 +589,55 @@ iwm_pcie_rx_stop(struct iwm_softc *sc)
}
return ret;
}
void
iwm_pcie_clear_cmd_in_flight(struct iwm_softc *sc)
{
if (!sc->cfg->apmg_wake_up_wa)
return;
if (!sc->cmd_hold_nic_awake) {
device_printf(sc->sc_dev,
"%s: cmd_hold_nic_awake not set\n", __func__);
return;
}
sc->cmd_hold_nic_awake = 0;
IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
}
int
iwm_pcie_set_cmd_in_flight(struct iwm_softc *sc)
{
int ret;
/*
* wake up the NIC to make sure that the firmware will see the host
* command - we will let the NIC sleep once all the host commands
* returned. This needs to be done only on NICs that have
* apmg_wake_up_wa set.
*/
if (sc->cfg->apmg_wake_up_wa &&
!sc->cmd_hold_nic_awake) {
IWM_SETBITS(sc, IWM_CSR_GP_CNTRL,
IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
ret = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL,
IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
(IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
15000);
if (ret == 0) {
IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL,
IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
device_printf(sc->sc_dev,
"%s: Failed to wake NIC for hcmd\n", __func__);
return EIO;
}
sc->cmd_hold_nic_awake = 1;
}
return 0;
}

View File

@ -129,4 +129,7 @@ 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);
extern int iwm_pcie_set_cmd_in_flight(struct iwm_softc *sc);
extern void iwm_pcie_clear_cmd_in_flight(struct iwm_softc *sc);
#endif

View File

@ -305,17 +305,10 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd)
bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map,
BUS_DMASYNC_PREWRITE);
IWM_SETBITS(sc, IWM_CSR_GP_CNTRL,
IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
if (!iwm_poll_bit(sc, IWM_CSR_GP_CNTRL,
IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
(IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000)) {
device_printf(sc->sc_dev,
"%s: acquiring device failed\n", __func__);
error = EBUSY;
error = iwm_pcie_set_cmd_in_flight(sc);
if (error)
goto out;
}
ring->queued++;
#if 0
iwm_update_sched(sc, ring->qid, ring->cur, 0, 0);

View File

@ -389,6 +389,8 @@ enum iwm_device_family {
* @host_interrupt_operation_mode: device needs host interrupt operation
* mode set
* @nvm_hw_section_num: the ID of the HW NVM section
* @apmg_wake_up_wa: should the MAC access REQ be asserted when a command
* is in flight. This is due to a HW bug in 7260, 3160 and 7265.
*/
struct iwm_cfg {
const char *fw_name;
@ -396,6 +398,7 @@ struct iwm_cfg {
enum iwm_device_family device_family;
int host_interrupt_operation_mode;
uint8_t nvm_hw_section_num;
int apmg_wake_up_wa;
};
struct iwm_softc {
@ -521,6 +524,8 @@ struct iwm_softc {
int sc_max_rssi;
struct iwm_notif_wait_data *sc_notif_wait;
int cmd_hold_nic_awake;
};
#define IWM_LOCK_INIT(_sc) \