The radio would stop communicating completely. This issue was easiest to trigger on AR913x devices, e.g. the TP-Link TL-WR1043ND, but other hardware was occasionally affected as well. The most critical issue was a race condition in disabling/enabling IRQs between the IRQ handler and the IRQ processing tasklet Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
4cacc1c5f5
commit
b94177e10f
16 changed files with 523 additions and 57 deletions
|
@ -0,0 +1,175 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 25 Jan 2017 12:57:05 +0100
|
||||
Subject: [PATCH] ath9k: rename tx_complete_work to hw_check_work
|
||||
|
||||
Also include common MAC alive check. This should make the hang checks
|
||||
more reliable for modes where beacons are not sent and is used as a
|
||||
starting point for further hang check improvements
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
||||
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
||||
@@ -108,7 +108,7 @@ int ath_descdma_setup(struct ath_softc *
|
||||
#define ATH_AGGR_MIN_QDEPTH 2
|
||||
/* minimum h/w qdepth for non-aggregated traffic */
|
||||
#define ATH_NON_AGGR_MIN_QDEPTH 8
|
||||
-#define ATH_TX_COMPLETE_POLL_INT 1000
|
||||
+#define ATH_HW_CHECK_POLL_INT 1000
|
||||
#define ATH_TXFIFO_DEPTH 8
|
||||
#define ATH_TX_ERROR 0x01
|
||||
|
||||
@@ -745,7 +745,7 @@ void ath9k_csa_update(struct ath_softc *
|
||||
#define ATH_PAPRD_TIMEOUT 100 /* msecs */
|
||||
#define ATH_PLL_WORK_INTERVAL 100
|
||||
|
||||
-void ath_tx_complete_poll_work(struct work_struct *work);
|
||||
+void ath_hw_check_work(struct work_struct *work);
|
||||
void ath_reset_work(struct work_struct *work);
|
||||
bool ath_hw_check(struct ath_softc *sc);
|
||||
void ath_hw_pll_work(struct work_struct *work);
|
||||
@@ -1053,7 +1053,7 @@ struct ath_softc {
|
||||
#ifdef CPTCFG_ATH9K_DEBUGFS
|
||||
struct ath9k_debug debug;
|
||||
#endif
|
||||
- struct delayed_work tx_complete_work;
|
||||
+ struct delayed_work hw_check_work;
|
||||
struct delayed_work hw_pll_work;
|
||||
struct timer_list sleep_timer;
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath9k/init.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/init.c
|
||||
@@ -681,6 +681,7 @@ static int ath9k_init_softc(u16 devid, s
|
||||
INIT_WORK(&sc->hw_reset_work, ath_reset_work);
|
||||
INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
|
||||
INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
|
||||
+ INIT_DELAYED_WORK(&sc->hw_check_work, ath_hw_check_work);
|
||||
|
||||
ath9k_init_channel_context(sc);
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath9k/link.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/link.c
|
||||
@@ -20,20 +20,13 @@
|
||||
* TX polling - checks if the TX engine is stuck somewhere
|
||||
* and issues a chip reset if so.
|
||||
*/
|
||||
-void ath_tx_complete_poll_work(struct work_struct *work)
|
||||
+static bool ath_tx_complete_check(struct ath_softc *sc)
|
||||
{
|
||||
- struct ath_softc *sc = container_of(work, struct ath_softc,
|
||||
- tx_complete_work.work);
|
||||
struct ath_txq *txq;
|
||||
int i;
|
||||
- bool needreset = false;
|
||||
-
|
||||
|
||||
- if (sc->tx99_state) {
|
||||
- ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
|
||||
- "skip tx hung detection on tx99\n");
|
||||
- return;
|
||||
- }
|
||||
+ if (sc->tx99_state)
|
||||
+ return true;
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
|
||||
txq = sc->tx.txq_map[i];
|
||||
@@ -41,25 +34,36 @@ void ath_tx_complete_poll_work(struct wo
|
||||
ath_txq_lock(sc, txq);
|
||||
if (txq->axq_depth) {
|
||||
if (txq->axq_tx_inprogress) {
|
||||
- needreset = true;
|
||||
ath_txq_unlock(sc, txq);
|
||||
- break;
|
||||
- } else {
|
||||
- txq->axq_tx_inprogress = true;
|
||||
+ goto reset;
|
||||
}
|
||||
+
|
||||
+ txq->axq_tx_inprogress = true;
|
||||
}
|
||||
ath_txq_unlock(sc, txq);
|
||||
}
|
||||
|
||||
- if (needreset) {
|
||||
- ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
|
||||
- "tx hung, resetting the chip\n");
|
||||
- ath9k_queue_reset(sc, RESET_TYPE_TX_HANG);
|
||||
+ return true;
|
||||
+
|
||||
+reset:
|
||||
+ ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
|
||||
+ "tx hung, resetting the chip\n");
|
||||
+ ath9k_queue_reset(sc, RESET_TYPE_TX_HANG);
|
||||
+ return false;
|
||||
+
|
||||
+}
|
||||
+
|
||||
+void ath_hw_check_work(struct work_struct *work)
|
||||
+{
|
||||
+ struct ath_softc *sc = container_of(work, struct ath_softc,
|
||||
+ hw_check_work.work);
|
||||
+
|
||||
+ if (!ath_hw_check(sc) ||
|
||||
+ !ath_tx_complete_check(sc))
|
||||
return;
|
||||
- }
|
||||
|
||||
- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work,
|
||||
- msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT));
|
||||
+ ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work,
|
||||
+ msecs_to_jiffies(ATH_HW_CHECK_POLL_INT));
|
||||
}
|
||||
|
||||
/*
|
||||
--- a/drivers/net/wireless/ath/ath9k/main.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/main.c
|
||||
@@ -181,7 +181,7 @@ void ath9k_ps_restore(struct ath_softc *
|
||||
static void __ath_cancel_work(struct ath_softc *sc)
|
||||
{
|
||||
cancel_work_sync(&sc->paprd_work);
|
||||
- cancel_delayed_work_sync(&sc->tx_complete_work);
|
||||
+ cancel_delayed_work_sync(&sc->hw_check_work);
|
||||
cancel_delayed_work_sync(&sc->hw_pll_work);
|
||||
|
||||
#ifdef CPTCFG_ATH9K_BTCOEX_SUPPORT
|
||||
@@ -198,7 +198,8 @@ void ath_cancel_work(struct ath_softc *s
|
||||
|
||||
void ath_restart_work(struct ath_softc *sc)
|
||||
{
|
||||
- ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
|
||||
+ ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work,
|
||||
+ ATH_HW_CHECK_POLL_INT);
|
||||
|
||||
if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9330(sc->sc_ah))
|
||||
ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
|
||||
@@ -2091,7 +2092,7 @@ void __ath9k_flush(struct ieee80211_hw *
|
||||
int timeout;
|
||||
bool drain_txq;
|
||||
|
||||
- cancel_delayed_work_sync(&sc->tx_complete_work);
|
||||
+ cancel_delayed_work_sync(&sc->hw_check_work);
|
||||
|
||||
if (ah->ah_flags & AH_UNPLUGGED) {
|
||||
ath_dbg(common, ANY, "Device has been unplugged!\n");
|
||||
@@ -2129,7 +2130,8 @@ void __ath9k_flush(struct ieee80211_hw *
|
||||
ath9k_ps_restore(sc);
|
||||
}
|
||||
|
||||
- ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0);
|
||||
+ ieee80211_queue_delayed_work(hw, &sc->hw_check_work,
|
||||
+ ATH_HW_CHECK_POLL_INT);
|
||||
}
|
||||
|
||||
static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw)
|
||||
--- a/drivers/net/wireless/ath/ath9k/xmit.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
|
||||
@@ -2915,8 +2915,6 @@ int ath_tx_init(struct ath_softc *sc, in
|
||||
return error;
|
||||
}
|
||||
|
||||
- INIT_DELAYED_WORK(&sc->tx_complete_work, ath_tx_complete_poll_work);
|
||||
-
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
|
||||
error = ath_tx_edma_init(sc);
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 25 Jan 2017 12:58:17 +0100
|
||||
Subject: [PATCH] ath9k_hw: check if the chip failed to wake up
|
||||
|
||||
In an RFC patch, Sven Eckelmann and Simon Wunderlich reported:
|
||||
|
||||
"QCA 802.11n chips (especially AR9330/AR9340) sometimes end up in a
|
||||
state in which a read of AR_CFG always returns 0xdeadbeef.
|
||||
This should not happen when when the power_mode of the device is
|
||||
ATH9K_PM_AWAKE."
|
||||
|
||||
Include the check for the default register state in the existing MAC
|
||||
hang check.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath9k/hw.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/hw.c
|
||||
@@ -1624,6 +1624,10 @@ bool ath9k_hw_check_alive(struct ath_hw
|
||||
int count = 50;
|
||||
u32 reg, last_val;
|
||||
|
||||
+ /* Check if chip failed to wake up */
|
||||
+ if (REG_READ(ah, AR_CFG) == 0xdeadbeef)
|
||||
+ return false;
|
||||
+
|
||||
if (AR_SREV_9300(ah))
|
||||
return !ath9k_hw_detect_mac_hang(ah);
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 25 Jan 2017 13:00:58 +0100
|
||||
Subject: [PATCH] ath9k: check for deaf rx path state
|
||||
|
||||
Various chips occasionally run into a state where the tx path still
|
||||
appears to be working normally, but the rx path is deaf.
|
||||
|
||||
There is no known register signature to check for this state explicitly,
|
||||
so use the lack of rx interrupts as an indicator.
|
||||
|
||||
This detection is prone to false positives, since a device could also
|
||||
simply be in an environment where there are no frames on the air.
|
||||
However, in this case doing a reset should be harmless since it's
|
||||
obviously not interrupting any real activity. To avoid confusion, call
|
||||
the reset counters in this case "Rx path inactive" instead of something
|
||||
like "Rx path deaf", since it may not be an indication of a real
|
||||
hardware failure.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
||||
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
||||
@@ -1027,6 +1027,7 @@ struct ath_softc {
|
||||
|
||||
u8 gtt_cnt;
|
||||
u32 intrstatus;
|
||||
+ u32 rx_active;
|
||||
u16 ps_flags; /* PS_* */
|
||||
bool ps_enabled;
|
||||
bool ps_idle;
|
||||
--- a/drivers/net/wireless/ath/ath9k/debug.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/debug.c
|
||||
@@ -763,6 +763,7 @@ static int read_file_reset(struct seq_fi
|
||||
[RESET_TYPE_BEACON_STUCK] = "Stuck Beacon",
|
||||
[RESET_TYPE_MCI] = "MCI Reset",
|
||||
[RESET_TYPE_CALIBRATION] = "Calibration error",
|
||||
+ [RESET_TYPE_RX_INACTIVE] = "Rx path inactive",
|
||||
[RESET_TX_DMA_ERROR] = "Tx DMA stop error",
|
||||
[RESET_RX_DMA_ERROR] = "Rx DMA stop error",
|
||||
};
|
||||
--- a/drivers/net/wireless/ath/ath9k/debug.h
|
||||
+++ b/drivers/net/wireless/ath/ath9k/debug.h
|
||||
@@ -50,6 +50,7 @@ enum ath_reset_type {
|
||||
RESET_TYPE_BEACON_STUCK,
|
||||
RESET_TYPE_MCI,
|
||||
RESET_TYPE_CALIBRATION,
|
||||
+ RESET_TYPE_RX_INACTIVE,
|
||||
RESET_TX_DMA_ERROR,
|
||||
RESET_RX_DMA_ERROR,
|
||||
__RESET_TYPE_MAX
|
||||
--- a/drivers/net/wireless/ath/ath9k/link.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/link.c
|
||||
@@ -53,13 +53,27 @@ reset:
|
||||
|
||||
}
|
||||
|
||||
+static bool ath_rx_active_check(struct ath_softc *sc)
|
||||
+{
|
||||
+ if (sc->rx_active) {
|
||||
+ sc->rx_active = 0;
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
|
||||
+ "rx path inactive, resetting the chip\n");
|
||||
+ ath9k_queue_reset(sc, RESET_TYPE_RX_INACTIVE);
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
void ath_hw_check_work(struct work_struct *work)
|
||||
{
|
||||
struct ath_softc *sc = container_of(work, struct ath_softc,
|
||||
hw_check_work.work);
|
||||
|
||||
if (!ath_hw_check(sc) ||
|
||||
- !ath_tx_complete_check(sc))
|
||||
+ !ath_tx_complete_check(sc) ||
|
||||
+ !ath_rx_active_check(sc))
|
||||
return;
|
||||
|
||||
ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work,
|
||||
--- a/drivers/net/wireless/ath/ath9k/main.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/main.c
|
||||
@@ -269,6 +269,7 @@ static bool ath_complete_reset(struct at
|
||||
}
|
||||
|
||||
sc->gtt_cnt = 0;
|
||||
+ sc->rx_active = 1;
|
||||
|
||||
ath9k_hw_set_interrupts(ah);
|
||||
ath9k_hw_enable_interrupts(ah);
|
||||
@@ -452,6 +453,7 @@ void ath9k_tasklet(unsigned long data)
|
||||
ath_rx_tasklet(sc, 0, true);
|
||||
|
||||
ath_rx_tasklet(sc, 0, false);
|
||||
+ sc->rx_active = 1;
|
||||
}
|
||||
|
||||
if (status & ATH9K_INT_TX) {
|
|
@ -0,0 +1,165 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 25 Jan 2017 15:10:37 +0100
|
||||
Subject: [PATCH] ath9k: fix race condition in enabling/disabling IRQs
|
||||
|
||||
The code currently relies on refcounting to disable IRQs from within the
|
||||
IRQ handler and re-enabling them again after the tasklet has run.
|
||||
|
||||
However, due to race conditions sometimes the IRQ handler might be
|
||||
called twice, or the tasklet may not run at all (if interrupted in the
|
||||
middle of a reset).
|
||||
|
||||
This can cause nasty imbalances in the irq-disable refcount which will
|
||||
get the driver permanently stuck until the entire radio has been stopped
|
||||
and started again (ath_reset will not recover from this).
|
||||
|
||||
Instead of using this fragile logic, change the code to ensure that
|
||||
running the irq handler during tasklet processing is safe, and leave the
|
||||
refcount untouched.
|
||||
|
||||
Cc: stable@vger.kernel.org
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
||||
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
||||
@@ -998,6 +998,7 @@ struct ath_softc {
|
||||
struct survey_info *cur_survey;
|
||||
struct survey_info survey[ATH9K_NUM_CHANNELS];
|
||||
|
||||
+ spinlock_t intr_lock;
|
||||
struct tasklet_struct intr_tq;
|
||||
struct tasklet_struct bcon_tasklet;
|
||||
struct ath_hw *sc_ah;
|
||||
--- a/drivers/net/wireless/ath/ath9k/init.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/init.c
|
||||
@@ -669,6 +669,7 @@ static int ath9k_init_softc(u16 devid, s
|
||||
common->bt_ant_diversity = 1;
|
||||
|
||||
spin_lock_init(&common->cc_lock);
|
||||
+ spin_lock_init(&sc->intr_lock);
|
||||
spin_lock_init(&sc->sc_serial_rw);
|
||||
spin_lock_init(&sc->sc_pm_lock);
|
||||
spin_lock_init(&sc->chan_lock);
|
||||
--- a/drivers/net/wireless/ath/ath9k/mac.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/mac.c
|
||||
@@ -810,21 +810,12 @@ void ath9k_hw_disable_interrupts(struct
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_hw_disable_interrupts);
|
||||
|
||||
-void ath9k_hw_enable_interrupts(struct ath_hw *ah)
|
||||
+static void __ath9k_hw_enable_interrupts(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
u32 sync_default = AR_INTR_SYNC_DEFAULT;
|
||||
u32 async_mask;
|
||||
|
||||
- if (!(ah->imask & ATH9K_INT_GLOBAL))
|
||||
- return;
|
||||
-
|
||||
- if (!atomic_inc_and_test(&ah->intr_ref_cnt)) {
|
||||
- ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
|
||||
- atomic_read(&ah->intr_ref_cnt));
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
|
||||
AR_SREV_9561(ah))
|
||||
sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
|
||||
@@ -846,6 +837,39 @@ void ath9k_hw_enable_interrupts(struct a
|
||||
ath_dbg(common, INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
|
||||
REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
|
||||
}
|
||||
+
|
||||
+void ath9k_hw_resume_interrupts(struct ath_hw *ah)
|
||||
+{
|
||||
+ struct ath_common *common = ath9k_hw_common(ah);
|
||||
+
|
||||
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
|
||||
+ return;
|
||||
+
|
||||
+ if (atomic_read(&ah->intr_ref_cnt) != 0) {
|
||||
+ ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
|
||||
+ atomic_read(&ah->intr_ref_cnt));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ __ath9k_hw_enable_interrupts(ah);
|
||||
+}
|
||||
+EXPORT_SYMBOL(ath9k_hw_resume_interrupts);
|
||||
+
|
||||
+void ath9k_hw_enable_interrupts(struct ath_hw *ah)
|
||||
+{
|
||||
+ struct ath_common *common = ath9k_hw_common(ah);
|
||||
+
|
||||
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
|
||||
+ return;
|
||||
+
|
||||
+ if (!atomic_inc_and_test(&ah->intr_ref_cnt)) {
|
||||
+ ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
|
||||
+ atomic_read(&ah->intr_ref_cnt));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ __ath9k_hw_enable_interrupts(ah);
|
||||
+}
|
||||
EXPORT_SYMBOL(ath9k_hw_enable_interrupts);
|
||||
|
||||
void ath9k_hw_set_interrupts(struct ath_hw *ah)
|
||||
--- a/drivers/net/wireless/ath/ath9k/mac.h
|
||||
+++ b/drivers/net/wireless/ath/ath9k/mac.h
|
||||
@@ -744,6 +744,7 @@ void ath9k_hw_set_interrupts(struct ath_
|
||||
void ath9k_hw_enable_interrupts(struct ath_hw *ah);
|
||||
void ath9k_hw_disable_interrupts(struct ath_hw *ah);
|
||||
void ath9k_hw_kill_interrupts(struct ath_hw *ah);
|
||||
+void ath9k_hw_resume_interrupts(struct ath_hw *ah);
|
||||
|
||||
void ar9002_hw_attach_mac_ops(struct ath_hw *ah);
|
||||
|
||||
--- a/drivers/net/wireless/ath/ath9k/main.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/main.c
|
||||
@@ -375,9 +375,14 @@ void ath9k_tasklet(unsigned long data)
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
enum ath_reset_type type;
|
||||
unsigned long flags;
|
||||
- u32 status = sc->intrstatus;
|
||||
+ u32 status;
|
||||
u32 rxmask;
|
||||
|
||||
+ spin_lock_irqsave(&sc->intr_lock, flags);
|
||||
+ status = sc->intrstatus;
|
||||
+ sc->intrstatus = 0;
|
||||
+ spin_unlock_irqrestore(&sc->intr_lock, flags);
|
||||
+
|
||||
ath9k_ps_wakeup(sc);
|
||||
spin_lock(&sc->sc_pcu_lock);
|
||||
|
||||
@@ -480,7 +485,7 @@ void ath9k_tasklet(unsigned long data)
|
||||
ath9k_btcoex_handle_interrupt(sc, status);
|
||||
|
||||
/* re-enable hardware interrupt */
|
||||
- ath9k_hw_enable_interrupts(ah);
|
||||
+ ath9k_hw_resume_interrupts(ah);
|
||||
out:
|
||||
spin_unlock(&sc->sc_pcu_lock);
|
||||
ath9k_ps_restore(sc);
|
||||
@@ -544,7 +549,9 @@ irqreturn_t ath_isr(int irq, void *dev)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* Cache the status */
|
||||
- sc->intrstatus = status;
|
||||
+ spin_lock(&sc->intr_lock);
|
||||
+ sc->intrstatus |= status;
|
||||
+ spin_unlock(&sc->intr_lock);
|
||||
|
||||
if (status & SCHED_INTR)
|
||||
sched = true;
|
||||
@@ -590,7 +597,7 @@ chip_reset:
|
||||
|
||||
if (sched) {
|
||||
/* turn off every interrupt */
|
||||
- ath9k_hw_disable_interrupts(ah);
|
||||
+ ath9k_hw_kill_interrupts(ah);
|
||||
tasklet_schedule(&sc->intr_tq);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
--- a/drivers/net/wireless/ath/ath9k/init.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/init.c
|
||||
@@ -775,6 +775,7 @@ static const struct ieee80211_iface_limi
|
||||
@@ -777,6 +777,7 @@ static const struct ieee80211_iface_limi
|
||||
BIT(NL80211_IFTYPE_AP) },
|
||||
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
||||
BIT(NL80211_IFTYPE_P2P_GO) },
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
--- a/drivers/net/wireless/ath/ath9k/debug.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/debug.c
|
||||
@@ -1315,6 +1315,53 @@ void ath9k_deinit_debug(struct ath_softc
|
||||
@@ -1316,6 +1316,53 @@ void ath9k_deinit_debug(struct ath_softc
|
||||
ath9k_cmn_spectral_deinit_debug(&sc->spec_priv);
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@
|
|||
int ath9k_init_debug(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
@@ -1334,6 +1381,8 @@ int ath9k_init_debug(struct ath_hw *ah)
|
||||
@@ -1335,6 +1382,8 @@ int ath9k_init_debug(struct ath_hw *ah)
|
||||
ath9k_tx99_init_debug(sc);
|
||||
ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
--- a/drivers/net/wireless/ath/ath9k/init.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/init.c
|
||||
@@ -1078,23 +1078,23 @@ static int __init ath9k_init(void)
|
||||
@@ -1080,23 +1080,23 @@ static int __init ath9k_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
--- a/drivers/net/wireless/ath/ath9k/debug.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/debug.c
|
||||
@@ -1362,6 +1362,52 @@ static const struct file_operations fops
|
||||
@@ -1363,6 +1363,52 @@ static const struct file_operations fops
|
||||
.owner = THIS_MODULE
|
||||
};
|
||||
|
||||
|
@ -53,7 +53,7 @@
|
|||
int ath9k_init_debug(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
@@ -1383,6 +1429,8 @@ int ath9k_init_debug(struct ath_hw *ah)
|
||||
@@ -1384,6 +1430,8 @@ int ath9k_init_debug(struct ath_hw *ah)
|
||||
|
||||
debugfs_create_file("eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
|
||||
&fops_eeprom);
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
struct ath_softc {
|
||||
struct ieee80211_hw *hw;
|
||||
struct device *dev;
|
||||
@@ -1045,9 +1055,8 @@ struct ath_softc {
|
||||
@@ -1047,9 +1057,8 @@ struct ath_softc {
|
||||
spinlock_t chan_lock;
|
||||
|
||||
#ifdef CPTCFG_MAC80211_LEDS
|
||||
|
@ -103,7 +103,8 @@
|
|||
+ GFP_KERNEL);
|
||||
+ if (!led)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
|
||||
- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
|
||||
+ led->gpio = gpio = (struct gpio_led *) (led + 1);
|
||||
+ _name = (char *) (led->gpio + 1);
|
||||
+
|
||||
|
@ -116,8 +117,7 @@
|
|||
+ ret = ath_add_led(sc, led);
|
||||
+ if (unlikely(ret < 0))
|
||||
+ kfree(led);
|
||||
|
||||
- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
|
||||
+
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
|
@ -125,11 +125,11 @@
|
|||
{
|
||||
- if (!sc->led_registered)
|
||||
- return;
|
||||
+ struct ath_led *led;
|
||||
|
||||
-
|
||||
- ath_led_brightness(&sc->led_cdev, LED_OFF);
|
||||
- led_classdev_unregister(&sc->led_cdev);
|
||||
-
|
||||
+ struct ath_led *led;
|
||||
|
||||
- ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin);
|
||||
+ while (!list_empty(&sc->leds)) {
|
||||
+ led = list_first_entry(&sc->leds, struct ath_led, list);
|
||||
|
@ -181,7 +181,7 @@
|
|||
|
||||
--- a/drivers/net/wireless/ath/ath9k/init.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/init.c
|
||||
@@ -990,7 +990,7 @@ int ath9k_init_device(u16 devid, struct
|
||||
@@ -992,7 +992,7 @@ int ath9k_init_device(u16 devid, struct
|
||||
|
||||
#ifdef CPTCFG_MAC80211_LEDS
|
||||
/* must be initialized before ieee80211_register_hw */
|
||||
|
@ -192,7 +192,7 @@
|
|||
#endif
|
||||
--- a/drivers/net/wireless/ath/ath9k/debug.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/debug.c
|
||||
@@ -1407,6 +1407,61 @@ static const struct file_operations fops
|
||||
@@ -1408,6 +1408,61 @@ static const struct file_operations fops
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
|
@ -254,7 +254,7 @@
|
|||
|
||||
int ath9k_init_debug(struct ath_hw *ah)
|
||||
{
|
||||
@@ -1431,6 +1486,10 @@ int ath9k_init_debug(struct ath_hw *ah)
|
||||
@@ -1432,6 +1487,10 @@ int ath9k_init_debug(struct ath_hw *ah)
|
||||
&fops_eeprom);
|
||||
debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
|
||||
sc, &fops_chanbw);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
--- a/drivers/net/wireless/ath/ath9k/debug.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/debug.c
|
||||
@@ -1463,6 +1463,50 @@ static const struct file_operations fops
|
||||
@@ -1464,6 +1464,50 @@ static const struct file_operations fops
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -51,7 +51,7 @@
|
|||
int ath9k_init_debug(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
@@ -1490,6 +1534,8 @@ int ath9k_init_debug(struct ath_hw *ah)
|
||||
@@ -1491,6 +1535,8 @@ int ath9k_init_debug(struct ath_hw *ah)
|
||||
debugfs_create_file("gpio_led", S_IWUSR,
|
||||
sc->debug.debugfs_phy, sc, &fops_gpio_led);
|
||||
#endif
|
||||
|
@ -94,7 +94,7 @@
|
|||
struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
|
||||
--- a/drivers/net/wireless/ath/ath9k/hw.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/hw.c
|
||||
@@ -1838,6 +1838,20 @@ u32 ath9k_hw_get_tsf_offset(struct times
|
||||
@@ -1842,6 +1842,20 @@ u32 ath9k_hw_get_tsf_offset(struct times
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
|
||||
|
||||
|
@ -115,7 +115,7 @@
|
|||
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
|
||||
struct ath9k_hw_cal_data *caldata, bool fastcc)
|
||||
{
|
||||
@@ -2046,6 +2060,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st
|
||||
@@ -2050,6 +2064,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st
|
||||
ar9003_hw_disable_phy_restart(ah);
|
||||
|
||||
ath9k_hw_apply_gpio_override(ah);
|
||||
|
@ -125,7 +125,7 @@
|
|||
REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
|
||||
--- a/drivers/net/wireless/ath/ath9k/main.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/main.c
|
||||
@@ -533,6 +533,11 @@ irqreturn_t ath_isr(int irq, void *dev)
|
||||
@@ -541,6 +541,11 @@ irqreturn_t ath_isr(int irq, void *dev)
|
||||
if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
|
||||
--- a/drivers/net/wireless/ath/ath9k/init.c
|
||||
+++ b/drivers/net/wireless/ath/ath9k/init.c
|
||||
@@ -763,7 +763,8 @@ static void ath9k_init_txpower_limits(st
|
||||
@@ -765,7 +765,8 @@ static void ath9k_init_txpower_limits(st
|
||||
if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
|
||||
ath9k_init_band_txpower(sc, NL80211_BAND_5GHZ);
|
||||
|
||||
|
@ -65,7 +65,7 @@
|
|||
}
|
||||
|
||||
static const struct ieee80211_iface_limit if_limits[] = {
|
||||
@@ -950,6 +951,18 @@ static void ath9k_set_hw_capab(struct at
|
||||
@@ -952,6 +953,18 @@ static void ath9k_set_hw_capab(struct at
|
||||
SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@
|
|||
int ath9k_init_device(u16 devid, struct ath_softc *sc,
|
||||
const struct ath_bus_ops *bus_ops)
|
||||
{
|
||||
@@ -995,6 +1008,8 @@ int ath9k_init_device(u16 devid, struct
|
||||
@@ -997,6 +1010,8 @@ int ath9k_init_device(u16 devid, struct
|
||||
ARRAY_SIZE(ath9k_tpt_blink));
|
||||
#endif
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
return true;
|
||||
}
|
||||
|
||||
@@ -1816,8 +1835,14 @@ static int ath9k_hw_do_fastcc(struct ath
|
||||
@@ -1820,8 +1839,14 @@ static int ath9k_hw_do_fastcc(struct ath
|
||||
if (AR_SREV_9271(ah))
|
||||
ar9002_hw_load_ani_reg(ah, chan);
|
||||
|
||||
|
@ -55,7 +55,7 @@
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -2071,6 +2096,9 @@ int ath9k_hw_reset(struct ath_hw *ah, st
|
||||
@@ -2075,6 +2100,9 @@ int ath9k_hw_reset(struct ath_hw *ah, st
|
||||
ath9k_hw_set_radar_params(ah);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
struct ath_softc {
|
||||
struct ieee80211_hw *hw;
|
||||
struct device *dev;
|
||||
@@ -1057,6 +1066,9 @@ struct ath_softc {
|
||||
@@ -1059,6 +1068,9 @@ struct ath_softc {
|
||||
#ifdef CPTCFG_MAC80211_LEDS
|
||||
const char *led_default_trigger;
|
||||
struct list_head leds;
|
||||
|
|
|
@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
|||
---
|
||||
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
||||
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
||||
@@ -1068,6 +1068,7 @@ struct ath_softc {
|
||||
@@ -1070,6 +1070,7 @@ struct ath_softc {
|
||||
struct list_head leds;
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
struct ath9k_gpio_chip *gpiochip;
|
||||
|
|
|
@ -349,16 +349,6 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
|||
-
|
||||
- integer = swab32(eep->modalHeader.antCtrlCommon);
|
||||
- eep->modalHeader.antCtrlCommon = integer;
|
||||
-
|
||||
- for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) {
|
||||
- integer = swab32(eep->modalHeader.antCtrlChain[i]);
|
||||
- eep->modalHeader.antCtrlChain[i] = integer;
|
||||
- }
|
||||
-
|
||||
- for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
|
||||
- word = swab16(eep->modalHeader.spurChans[i].spurChan);
|
||||
- eep->modalHeader.spurChans[i].spurChan = word;
|
||||
- }
|
||||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.length);
|
||||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.checksum);
|
||||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.version);
|
||||
|
@ -368,10 +358,18 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
|||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.blueToothOptions);
|
||||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.deviceCap);
|
||||
+ EEPROM_FIELD_SWAB32(eep->modalHeader.antCtrlCommon);
|
||||
+
|
||||
|
||||
- for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) {
|
||||
- integer = swab32(eep->modalHeader.antCtrlChain[i]);
|
||||
- eep->modalHeader.antCtrlChain[i] = integer;
|
||||
- }
|
||||
+ for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++)
|
||||
+ EEPROM_FIELD_SWAB32(eep->modalHeader.antCtrlChain[i]);
|
||||
+
|
||||
|
||||
- for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
|
||||
- word = swab16(eep->modalHeader.spurChans[i].spurChan);
|
||||
- eep->modalHeader.spurChans[i].spurChan = word;
|
||||
- }
|
||||
+ for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++)
|
||||
+ EEPROM_FIELD_SWAB16(
|
||||
+ eep->modalHeader.spurChans[i].spurChan);
|
||||
|
@ -542,16 +540,6 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
|||
-
|
||||
- integer = swab32(eep->modalHeader.antCtrlCommon);
|
||||
- eep->modalHeader.antCtrlCommon = integer;
|
||||
-
|
||||
- for (i = 0; i < AR9287_MAX_CHAINS; i++) {
|
||||
- integer = swab32(eep->modalHeader.antCtrlChain[i]);
|
||||
- eep->modalHeader.antCtrlChain[i] = integer;
|
||||
- }
|
||||
-
|
||||
- for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
|
||||
- word = swab16(eep->modalHeader.spurChans[i].spurChan);
|
||||
- eep->modalHeader.spurChans[i].spurChan = word;
|
||||
- }
|
||||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.length);
|
||||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.checksum);
|
||||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.version);
|
||||
|
@ -561,10 +549,18 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
|||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.blueToothOptions);
|
||||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.deviceCap);
|
||||
+ EEPROM_FIELD_SWAB32(eep->modalHeader.antCtrlCommon);
|
||||
+
|
||||
|
||||
- for (i = 0; i < AR9287_MAX_CHAINS; i++) {
|
||||
- integer = swab32(eep->modalHeader.antCtrlChain[i]);
|
||||
- eep->modalHeader.antCtrlChain[i] = integer;
|
||||
- }
|
||||
+ for (i = 0; i < AR9287_MAX_CHAINS; i++)
|
||||
+ EEPROM_FIELD_SWAB32(eep->modalHeader.antCtrlChain[i]);
|
||||
+
|
||||
|
||||
- for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
|
||||
- word = swab16(eep->modalHeader.spurChans[i].spurChan);
|
||||
- eep->modalHeader.spurChans[i].spurChan = word;
|
||||
- }
|
||||
+ for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++)
|
||||
+ EEPROM_FIELD_SWAB16(
|
||||
+ eep->modalHeader.spurChans[i].spurChan);
|
||||
|
@ -716,8 +712,7 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
|||
if (need_swap) {
|
||||
- u32 integer, j;
|
||||
- u16 word;
|
||||
+ u32 j;
|
||||
|
||||
-
|
||||
- word = swab16(eep->baseEepHeader.length);
|
||||
- eep->baseEepHeader.length = word;
|
||||
-
|
||||
|
@ -738,7 +733,8 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
|||
-
|
||||
- word = swab16(eep->baseEepHeader.blueToothOptions);
|
||||
- eep->baseEepHeader.blueToothOptions = word;
|
||||
-
|
||||
+ u32 j;
|
||||
|
||||
- word = swab16(eep->baseEepHeader.deviceCap);
|
||||
- eep->baseEepHeader.deviceCap = word;
|
||||
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.length);
|
||||
|
|
|
@ -343,7 +343,7 @@
|
|||
|
||||
u8 ath9k_parse_mpdudensity(u8 mpdudensity)
|
||||
{
|
||||
@@ -652,6 +654,7 @@ void ath_reset_work(struct work_struct *
|
||||
@@ -662,6 +664,7 @@ void ath_reset_work(struct work_struct *
|
||||
static int ath9k_start(struct ieee80211_hw *hw)
|
||||
{
|
||||
struct ath_softc *sc = hw->priv;
|
||||
|
@ -351,7 +351,7 @@
|
|||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
|
||||
@@ -730,6 +733,11 @@ static int ath9k_start(struct ieee80211_
|
||||
@@ -740,6 +743,11 @@ static int ath9k_start(struct ieee80211_
|
||||
AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue