ath9k: add stability fixes for long standing hang issues (FS#13, #34, #373, #383)

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:
Felix Fietkau 2017-01-25 15:32:18 +01:00
parent acd1795a60
commit f9022964cf
16 changed files with 523 additions and 57 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}

View File

@ -1,6 +1,6 @@
--- a/drivers/net/wireless/ath/ath9k/init.c --- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/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) }, BIT(NL80211_IFTYPE_AP) },
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) }, BIT(NL80211_IFTYPE_P2P_GO) },

View File

@ -1,6 +1,6 @@
--- a/drivers/net/wireless/ath/ath9k/debug.c --- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/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); ath9k_cmn_spectral_deinit_debug(&sc->spec_priv);
} }
@ -54,7 +54,7 @@
int ath9k_init_debug(struct ath_hw *ah) int ath9k_init_debug(struct ath_hw *ah)
{ {
struct ath_common *common = ath9k_hw_common(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_tx99_init_debug(sc);
ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy); ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy);

View File

@ -1,6 +1,6 @@
--- a/drivers/net/wireless/ath/ath9k/init.c --- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/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; int error;

View File

@ -1,6 +1,6 @@
--- a/drivers/net/wireless/ath/ath9k/debug.c --- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/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 .owner = THIS_MODULE
}; };
@ -53,7 +53,7 @@
int ath9k_init_debug(struct ath_hw *ah) int ath9k_init_debug(struct ath_hw *ah)
{ {
struct ath_common *common = ath9k_hw_common(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, debugfs_create_file("eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_eeprom); &fops_eeprom);

View File

@ -24,7 +24,7 @@
struct ath_softc { struct ath_softc {
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct device *dev; struct device *dev;
@@ -1045,9 +1055,8 @@ struct ath_softc { @@ -1047,9 +1057,8 @@ struct ath_softc {
spinlock_t chan_lock; spinlock_t chan_lock;
#ifdef CPTCFG_MAC80211_LEDS #ifdef CPTCFG_MAC80211_LEDS
@ -103,7 +103,8 @@
+ GFP_KERNEL); + GFP_KERNEL);
+ if (!led) + if (!led)
+ return -ENOMEM; + return -ENOMEM;
+
- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
+ led->gpio = gpio = (struct gpio_led *) (led + 1); + led->gpio = gpio = (struct gpio_led *) (led + 1);
+ _name = (char *) (led->gpio + 1); + _name = (char *) (led->gpio + 1);
+ +
@ -116,8 +117,7 @@
+ ret = ath_add_led(sc, led); + ret = ath_add_led(sc, led);
+ if (unlikely(ret < 0)) + if (unlikely(ret < 0))
+ kfree(led); + kfree(led);
+
- ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, val);
+ return ret; + return ret;
} }
@ -125,11 +125,11 @@
{ {
- if (!sc->led_registered) - if (!sc->led_registered)
- return; - return;
+ struct ath_led *led; -
- ath_led_brightness(&sc->led_cdev, LED_OFF); - ath_led_brightness(&sc->led_cdev, LED_OFF);
- led_classdev_unregister(&sc->led_cdev); - led_classdev_unregister(&sc->led_cdev);
- + struct ath_led *led;
- ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin); - ath9k_hw_gpio_free(sc->sc_ah, sc->sc_ah->led_pin);
+ while (!list_empty(&sc->leds)) { + while (!list_empty(&sc->leds)) {
+ led = list_first_entry(&sc->leds, struct ath_led, list); + led = list_first_entry(&sc->leds, struct ath_led, list);
@ -181,7 +181,7 @@
--- a/drivers/net/wireless/ath/ath9k/init.c --- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/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 #ifdef CPTCFG_MAC80211_LEDS
/* must be initialized before ieee80211_register_hw */ /* must be initialized before ieee80211_register_hw */
@ -192,7 +192,7 @@
#endif #endif
--- a/drivers/net/wireless/ath/ath9k/debug.c --- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/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, .llseek = default_llseek,
}; };
@ -254,7 +254,7 @@
int ath9k_init_debug(struct ath_hw *ah) 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); &fops_eeprom);
debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, debugfs_create_file("chanbw", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
sc, &fops_chanbw); sc, &fops_chanbw);

View File

@ -1,6 +1,6 @@
--- a/drivers/net/wireless/ath/ath9k/debug.c --- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/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 #endif
@ -51,7 +51,7 @@
int ath9k_init_debug(struct ath_hw *ah) int ath9k_init_debug(struct ath_hw *ah)
{ {
struct ath_common *common = ath9k_hw_common(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, debugfs_create_file("gpio_led", S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_gpio_led); sc->debug.debugfs_phy, sc, &fops_gpio_led);
#endif #endif
@ -94,7 +94,7 @@
struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
--- a/drivers/net/wireless/ath/ath9k/hw.c --- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/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); EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
@ -115,7 +115,7 @@
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
struct ath9k_hw_cal_data *caldata, bool fastcc) 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); ar9003_hw_disable_phy_restart(ah);
ath9k_hw_apply_gpio_override(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); REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
--- a/drivers/net/wireless/ath/ath9k/main.c --- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/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)) if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
return IRQ_HANDLED; return IRQ_HANDLED;

View File

@ -55,7 +55,7 @@
ops->spectral_scan_config = ar9003_hw_spectral_scan_config; ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
--- a/drivers/net/wireless/ath/ath9k/init.c --- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/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) if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
ath9k_init_band_txpower(sc, NL80211_BAND_5GHZ); ath9k_init_band_txpower(sc, NL80211_BAND_5GHZ);
@ -65,7 +65,7 @@
} }
static const struct ieee80211_iface_limit if_limits[] = { 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); SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
} }
@ -84,7 +84,7 @@
int ath9k_init_device(u16 devid, struct ath_softc *sc, int ath9k_init_device(u16 devid, struct ath_softc *sc,
const struct ath_bus_ops *bus_ops) 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)); ARRAY_SIZE(ath9k_tpt_blink));
#endif #endif

View File

@ -40,7 +40,7 @@
return true; 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)) if (AR_SREV_9271(ah))
ar9002_hw_load_ani_reg(ah, chan); ar9002_hw_load_ani_reg(ah, chan);
@ -55,7 +55,7 @@
return -EINVAL; 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); ath9k_hw_set_radar_params(ah);
} }

View File

@ -33,7 +33,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
struct ath_softc { struct ath_softc {
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct device *dev; struct device *dev;
@@ -1057,6 +1066,9 @@ struct ath_softc { @@ -1059,6 +1068,9 @@ struct ath_softc {
#ifdef CPTCFG_MAC80211_LEDS #ifdef CPTCFG_MAC80211_LEDS
const char *led_default_trigger; const char *led_default_trigger;
struct list_head leds; struct list_head leds;

View File

@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau <nbd@nbd.name>
--- ---
--- a/drivers/net/wireless/ath/ath9k/ath9k.h --- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/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; struct list_head leds;
#ifdef CONFIG_GPIOLIB #ifdef CONFIG_GPIOLIB
struct ath9k_gpio_chip *gpiochip; struct ath9k_gpio_chip *gpiochip;

View File

@ -349,16 +349,6 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- -
- integer = swab32(eep->modalHeader.antCtrlCommon); - integer = swab32(eep->modalHeader.antCtrlCommon);
- eep->modalHeader.antCtrlCommon = integer; - 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.length);
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.checksum); + EEPROM_FIELD_SWAB16(eep->baseEepHeader.checksum);
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.version); + 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.blueToothOptions);
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.deviceCap); + EEPROM_FIELD_SWAB16(eep->baseEepHeader.deviceCap);
+ EEPROM_FIELD_SWAB32(eep->modalHeader.antCtrlCommon); + 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++) + for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++)
+ EEPROM_FIELD_SWAB32(eep->modalHeader.antCtrlChain[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++) + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++)
+ EEPROM_FIELD_SWAB16( + EEPROM_FIELD_SWAB16(
+ eep->modalHeader.spurChans[i].spurChan); + eep->modalHeader.spurChans[i].spurChan);
@ -542,16 +540,6 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- -
- integer = swab32(eep->modalHeader.antCtrlCommon); - integer = swab32(eep->modalHeader.antCtrlCommon);
- eep->modalHeader.antCtrlCommon = integer; - 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.length);
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.checksum); + EEPROM_FIELD_SWAB16(eep->baseEepHeader.checksum);
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.version); + 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.blueToothOptions);
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.deviceCap); + EEPROM_FIELD_SWAB16(eep->baseEepHeader.deviceCap);
+ EEPROM_FIELD_SWAB32(eep->modalHeader.antCtrlCommon); + 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++) + for (i = 0; i < AR9287_MAX_CHAINS; i++)
+ EEPROM_FIELD_SWAB32(eep->modalHeader.antCtrlChain[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++) + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++)
+ EEPROM_FIELD_SWAB16( + EEPROM_FIELD_SWAB16(
+ eep->modalHeader.spurChans[i].spurChan); + eep->modalHeader.spurChans[i].spurChan);
@ -716,8 +712,7 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
if (need_swap) { if (need_swap) {
- u32 integer, j; - u32 integer, j;
- u16 word; - u16 word;
+ u32 j; -
- word = swab16(eep->baseEepHeader.length); - word = swab16(eep->baseEepHeader.length);
- eep->baseEepHeader.length = word; - eep->baseEepHeader.length = word;
- -
@ -738,7 +733,8 @@ Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- -
- word = swab16(eep->baseEepHeader.blueToothOptions); - word = swab16(eep->baseEepHeader.blueToothOptions);
- eep->baseEepHeader.blueToothOptions = word; - eep->baseEepHeader.blueToothOptions = word;
- + u32 j;
- word = swab16(eep->baseEepHeader.deviceCap); - word = swab16(eep->baseEepHeader.deviceCap);
- eep->baseEepHeader.deviceCap = word; - eep->baseEepHeader.deviceCap = word;
+ EEPROM_FIELD_SWAB16(eep->baseEepHeader.length); + EEPROM_FIELD_SWAB16(eep->baseEepHeader.length);

View File

@ -343,7 +343,7 @@
u8 ath9k_parse_mpdudensity(u8 mpdudensity) 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) static int ath9k_start(struct ieee80211_hw *hw)
{ {
struct ath_softc *sc = hw->priv; struct ath_softc *sc = hw->priv;
@ -351,7 +351,7 @@
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan; 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); AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
} }