* [PATCH 01/10] carl9170: mac80211: enable Short Guard Interval for 20 MHz
[not found] <cover.1773277728.git.mas-i@hotmail.de>
@ 2026-03-12 10:37 ` Masi Osmani
2026-03-21 18:41 ` Christian Lamparter
2026-03-12 10:37 ` [PATCH 02/10] carl9170: mac80211: advertise RX STBC capability Masi Osmani
` (8 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Masi Osmani @ 2026-03-12 10:37 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Masi Osmani, ath9k-devel
The AR9170 hardware supports Short Guard Interval (400ns) for both
20 MHz and 40 MHz channel widths. SGI_40 was already advertised in
the HT capabilities, but SGI_20 was missing. This reduces the OFDM
symbol duration from 800ns to 400ns on 20 MHz channels, increasing
the maximum PHY rate from 130 Mbps to 144.4 Mbps (MCS 15, 2SS).
ath9k (the PCI sibling for the same AR9xxx chipset family) has
always advertised both SGI_20 and SGI_40.
Signed-off-by: Masi Osmani <mas-i@hotmail.de>
---
drivers/net/wireless/ath/carl9170/main.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index a7a9345..aa7e481 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -154,6 +154,7 @@ static struct ieee80211_channel carl9170_5ghz_chantable[] = {
.cap = IEEE80211_HT_CAP_MAX_AMSDU | \
IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
IEEE80211_HT_CAP_SGI_40 | \
+ IEEE80211_HT_CAP_SGI_20 | \
IEEE80211_HT_CAP_DSSSCCK40 | \
IEEE80211_HT_CAP_SM_PS, \
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \
--
2.51.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 02/10] carl9170: mac80211: advertise RX STBC capability
[not found] <cover.1773277728.git.mas-i@hotmail.de>
2026-03-12 10:37 ` [PATCH 01/10] carl9170: mac80211: enable Short Guard Interval for 20 MHz Masi Osmani
@ 2026-03-12 10:37 ` Masi Osmani
2026-03-21 18:47 ` Christian Lamparter
2026-03-12 10:37 ` [PATCH 03/10] carl9170: mac80211: document spatial multiplexing power save handler Masi Osmani
` (7 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Masi Osmani @ 2026-03-12 10:37 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Masi Osmani, ath9k-devel
The AR9170 baseband supports receiving Space-Time Block Coded
streams (1 spatial stream). The capability was not advertised
in the HT capabilities, causing peers to never use STBC when
transmitting to us. Enabling RX STBC improves receive diversity
gain and link reliability, especially in multipath environments.
The ath9k driver for the same chipset family advertises RX STBC
based on the number of RX chains. With 2 RX chains, the AR9170
can decode 1 STBC spatial stream.
Signed-off-by: Masi Osmani <mas-i@hotmail.de>
---
drivers/net/wireless/ath/carl9170/main.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index aa7e481..6324b38 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -156,7 +156,8 @@ static struct ieee80211_channel carl9170_5ghz_chantable[] = {
IEEE80211_HT_CAP_SGI_40 | \
IEEE80211_HT_CAP_SGI_20 | \
IEEE80211_HT_CAP_DSSSCCK40 | \
- IEEE80211_HT_CAP_SM_PS, \
+ IEEE80211_HT_CAP_SM_PS | \
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
.mcs = { \
--
2.51.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 03/10] carl9170: mac80211: document spatial multiplexing power save handler
[not found] <cover.1773277728.git.mas-i@hotmail.de>
2026-03-12 10:37 ` [PATCH 01/10] carl9170: mac80211: enable Short Guard Interval for 20 MHz Masi Osmani
2026-03-12 10:37 ` [PATCH 02/10] carl9170: mac80211: advertise RX STBC capability Masi Osmani
@ 2026-03-12 10:37 ` Masi Osmani
2026-03-21 18:57 ` Christian Lamparter
2026-03-12 10:37 ` [PATCH 04/10] carl9170: rx: wire up dropped frame counter Masi Osmani
` (6 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Masi Osmani @ 2026-03-12 10:37 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Masi Osmani, ath9k-devel
Replace the bare TODO comment in the SMPS configuration handler
with documentation explaining why the driver accepts but does not
act on SMPS mode changes.
The AR9170 advertises SM_PS disabled (both chains always active)
in its HT capabilities. While mac80211 may still send SMPS
configuration requests, implementing static or dynamic SMPS would
require firmware support for per-chain enable/disable that the
AR9170 firmware (v1.9.9) does not provide.
Signed-off-by: Masi Osmani <mas-i@hotmail.de>
---
drivers/net/wireless/ath/carl9170/main.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index 6324b38..d75688c 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -910,7 +910,13 @@ static int carl9170_op_config(struct ieee80211_hw *hw, int radio_idx, u32 change
}
if (changed & IEEE80211_CONF_CHANGE_SMPS) {
- /* TODO */
+ /*
+ * We advertise SM_PS disabled (all chains active).
+ * mac80211 may still request mode changes, which we
+ * accept but only support OFF (both chains active).
+ * Static/dynamic SMPS would require firmware support
+ * for chain control that the AR9170 does not provide.
+ */
err = 0;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 04/10] carl9170: rx: wire up dropped frame counter
[not found] <cover.1773277728.git.mas-i@hotmail.de>
` (2 preceding siblings ...)
2026-03-12 10:37 ` [PATCH 03/10] carl9170: mac80211: document spatial multiplexing power save handler Masi Osmani
@ 2026-03-12 10:37 ` Masi Osmani
2026-03-21 19:03 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 05/10] carl9170: rx: track PHY errors via debugfs Masi Osmani
` (5 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Masi Osmani @ 2026-03-12 10:37 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Masi Osmani, ath9k-devel
Increment ar->rx_dropped when frames with unrecognized error
codes are dropped in carl9170_rx_mac_status(). The counter
was already defined in the ar9170 struct and exported via
debugfs, but the actual increment was missing -- only a TODO
comment was in place.
This provides visibility into receive-path frame drops through
the existing debugfs interface.
Signed-off-by: Masi Osmani <mas-i@hotmail.de>
---
drivers/net/wireless/ath/carl9170/rx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
index 6833430..c664014 100644
--- a/drivers/net/wireless/ath/carl9170/rx.c
+++ b/drivers/net/wireless/ath/carl9170/rx.c
@@ -340,7 +340,7 @@ static int carl9170_rx_mac_status(struct ar9170 *ar,
/* drop any other error frames */
if (unlikely(error)) {
- /* TODO: update netdevice's RX dropped/errors statistics */
+ ar->rx_dropped++;
if (net_ratelimit())
wiphy_dbg(ar->hw->wiphy, "received frame with "
--
2.51.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 05/10] carl9170: rx: track PHY errors via debugfs
[not found] <cover.1773277728.git.mas-i@hotmail.de>
` (3 preceding siblings ...)
2026-03-12 10:37 ` [PATCH 04/10] carl9170: rx: wire up dropped frame counter Masi Osmani
@ 2026-03-12 10:38 ` Masi Osmani
2026-03-21 20:29 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 06/10] carl9170: phy: populate per-channel TX power from EEPROM Masi Osmani
` (4 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Masi Osmani @ 2026-03-12 10:38 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Masi Osmani, ath9k-devel
Count PHY errors reported by the hardware in the RX status and
expose the counter through debugfs as rx_phy_errors. Previously,
PHY errors from ar9170_rx_phystatus were silently ignored (marked
with a TODO comment).
The counter helps diagnose RF environment issues (interference,
multipath, low SNR) without requiring monitor mode or additional
tooling.
Signed-off-by: Masi Osmani <mas-i@hotmail.de>
---
drivers/net/wireless/ath/carl9170/carl9170.h | 1 +
drivers/net/wireless/ath/carl9170/debug.c | 2 ++
drivers/net/wireless/ath/carl9170/rx.c | 4 +++-
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
index ba29b4a..eaac859 100644
--- a/drivers/net/wireless/ath/carl9170/carl9170.h
+++ b/drivers/net/wireless/ath/carl9170/carl9170.h
@@ -386,6 +386,7 @@ struct ar9170 {
unsigned int tx_ack_failures;
unsigned int tx_fcs_errors;
unsigned int rx_dropped;
+ unsigned int rx_phy_errors;
/* EEPROM */
struct ar9170_eeprom eeprom;
diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c
index 2d73456..0498df2 100644
--- a/drivers/net/wireless/ath/carl9170/debug.c
+++ b/drivers/net/wireless/ath/carl9170/debug.c
@@ -794,6 +794,7 @@ DEBUGFS_READONLY_FILE(tx_janitor_last_run, 64, "last run:%d ms ago",
DEBUGFS_READONLY_FILE(tx_dropped, 20, "%d", ar->tx_dropped);
DEBUGFS_READONLY_FILE(rx_dropped, 20, "%d", ar->rx_dropped);
+DEBUGFS_READONLY_FILE(rx_phy_errors, 20, "%d", ar->rx_phy_errors);
DEBUGFS_READONLY_FILE(sniffer_enabled, 20, "%d", ar->sniffer_enabled);
DEBUGFS_READONLY_FILE(rx_software_decryption, 20, "%d",
@@ -830,6 +831,7 @@ void carl9170_debugfs_register(struct ar9170 *ar)
DEBUGFS_ADD(tx_ampdu_list_len);
DEBUGFS_ADD(rx_dropped);
+ DEBUGFS_ADD(rx_phy_errors);
DEBUGFS_ADD(sniffer_enabled);
DEBUGFS_ADD(rx_software_decryption);
diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
index c664014..414d499 100644
--- a/drivers/net/wireless/ath/carl9170/rx.c
+++ b/drivers/net/wireless/ath/carl9170/rx.c
@@ -455,7 +455,9 @@ static void carl9170_rx_phy_status(struct ar9170 *ar,
if (phy->rssi[i] & 0x80)
phy->rssi[i] = ((~phy->rssi[i] & 0x7f) + 1) & 0x7f;
- /* TODO: we could do something with phy_errors */
+ if (phy->phy_err)
+ ar->rx_phy_errors++;
+
status->signal = ar->noise[0] + phy->rssi_combined;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 06/10] carl9170: phy: populate per-channel TX power from EEPROM
[not found] <cover.1773277728.git.mas-i@hotmail.de>
` (4 preceding siblings ...)
2026-03-12 10:38 ` [PATCH 05/10] carl9170: rx: track PHY errors via debugfs Masi Osmani
@ 2026-03-12 10:38 ` Masi Osmani
2026-03-21 19:24 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 07/10] carl9170: main: add exponential restart backoff Masi Osmani
` (3 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Masi Osmani @ 2026-03-12 10:38 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Masi Osmani, ath9k-devel
Replace the hardcoded max_power = 18 dBm (marked XXX) in the
channel definitions with actual per-channel values derived from
the EEPROM calibration target power tables.
The new carl9170_get_max_tgt_power() function interpolates the
maximum target power for a given frequency from the EEPROM's
legacy OFDM and CCK target power tables, using the same frequency
encoding and interpolation helpers already used by the power
calibration code. carl9170_update_channel_maxpower() iterates
all registered channels and updates their max_power fields.
This is called during EEPROM parsing, so mac80211 and userspace
see correct per-channel power limits from the start. The CHAN
macro default of 18 dBm remains as a safe fallback for channels
where EEPROM data is missing.
Signed-off-by: Masi Osmani <mas-i@hotmail.de>
---
drivers/net/wireless/ath/carl9170/carl9170.h | 1 +
drivers/net/wireless/ath/carl9170/main.c | 4 +-
drivers/net/wireless/ath/carl9170/phy.c | 88 ++++++++++++++++++++
3 files changed, 92 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
index eaac859..a2ffa62 100644
--- a/drivers/net/wireless/ath/carl9170/carl9170.h
+++ b/drivers/net/wireless/ath/carl9170/carl9170.h
@@ -602,6 +602,7 @@ int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state);
int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
enum nl80211_channel_type bw);
int carl9170_get_noisefloor(struct ar9170 *ar);
+void carl9170_update_channel_maxpower(struct ar9170 *ar);
/* FW */
int carl9170_parse_firmware(struct ar9170 *ar);
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index d75688c..dcedcb1 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -89,7 +89,7 @@ struct ieee80211_rate __carl9170_ratetable[] = {
#define CHAN(_freq, _idx) { \
.center_freq = (_freq), \
.hw_value = (_idx), \
- .max_power = 18, /* XXX */ \
+ .max_power = 18, \
}
static struct ieee80211_channel carl9170_2ghz_chantable[] = {
@@ -1930,6 +1930,8 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
if (!bands)
return -EINVAL;
+ carl9170_update_channel_maxpower(ar);
+
ar->survey = devm_kcalloc(&ar->udev->dev, chans,
sizeof(struct survey_info), GFP_KERNEL);
if (!ar->survey)
diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
index 34d9fd7..290c336 100644
--- a/drivers/net/wireless/ath/carl9170/phy.c
+++ b/drivers/net/wireless/ath/carl9170/phy.c
@@ -1524,6 +1524,94 @@ static void carl9170_set_power_cal(struct ar9170 *ar, u32 freq,
carl9170_calc_ctl(ar, freq, bw);
}
+static u8 carl9170_get_max_tgt_power(struct ar9170 *ar, u32 freq)
+{
+ struct ar9170_calibration_target_power_legacy *ctpl;
+ int ntargets, idx, n, i;
+ u8 f, max_power = 0;
+ u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS];
+
+ if (freq < 3000)
+ f = freq - 2300;
+ else
+ f = (freq - 4800) / 5;
+
+ /* check legacy target powers (OFDM for 2G, 5G leg) */
+ for (i = 0; i < 2; i++) {
+ switch (i) {
+ case 0:
+ if (freq >= 3000) {
+ ctpl = &ar->eeprom.cal_tgt_pwr_5G[0];
+ ntargets = AR5416_NUM_5G_TARGET_PWRS;
+ } else {
+ ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0];
+ ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
+ }
+ break;
+ case 1:
+ if (freq < 3000) {
+ ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0];
+ ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS;
+ } else {
+ continue;
+ }
+ break;
+ default:
+ continue;
+ }
+
+ for (n = 0; n < ntargets; n++) {
+ if (ctpl[n].freq == 0xff)
+ break;
+ pwr_freqs[n] = ctpl[n].freq;
+ }
+ ntargets = n;
+ if (ntargets < 2)
+ continue;
+
+ idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f);
+ for (n = 0; n < 4; n++) {
+ u8 pwr;
+
+ pwr = carl9170_interpolate_u8(f,
+ ctpl[idx + 0].freq,
+ ctpl[idx + 0].power[n],
+ ctpl[idx + 1].freq,
+ ctpl[idx + 1].power[n]);
+ max_power = max(max_power, pwr);
+ }
+ }
+
+ /* target power is in half-dBm, max_power is in dBm */
+ return max_power / 2;
+}
+
+void carl9170_update_channel_maxpower(struct ar9170 *ar)
+{
+ struct ieee80211_supported_band *band;
+ int i;
+
+ band = ar->hw->wiphy->bands[NL80211_BAND_2GHZ];
+ if (band) {
+ for (i = 0; i < band->n_channels; i++) {
+ u8 pwr = carl9170_get_max_tgt_power(ar,
+ band->channels[i].center_freq);
+ if (pwr)
+ band->channels[i].max_power = pwr;
+ }
+ }
+
+ band = ar->hw->wiphy->bands[NL80211_BAND_5GHZ];
+ if (band) {
+ for (i = 0; i < band->n_channels; i++) {
+ u8 pwr = carl9170_get_max_tgt_power(ar,
+ band->channels[i].center_freq);
+ if (pwr)
+ band->channels[i].max_power = pwr;
+ }
+ }
+}
+
int carl9170_get_noisefloor(struct ar9170 *ar)
{
static const u32 phy_regs[] = {
--
2.51.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 07/10] carl9170: main: add exponential restart backoff
[not found] <cover.1773277728.git.mas-i@hotmail.de>
` (5 preceding siblings ...)
2026-03-12 10:38 ` [PATCH 06/10] carl9170: phy: populate per-channel TX power from EEPROM Masi Osmani
@ 2026-03-12 10:38 ` Masi Osmani
2026-03-21 20:42 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 08/10] carl9170: phy: enable antenna diversity for 2-chain devices Masi Osmani
` (2 subsequent siblings)
9 siblings, 1 reply; 20+ messages in thread
From: Masi Osmani @ 2026-03-12 10:38 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Masi Osmani, ath9k-devel
When the AR9170 enters a bad state (firmware errors, command
timeouts, TX queue stalls), the driver can trigger rapid-fire
restarts that prevent the device from ever stabilizing.
Add exponential backoff to carl9170_restart(): if a restart
request arrives before the current backoff window has elapsed,
the request is throttled. The backoff starts at 500 ms and
doubles on each restart, capping at 30 seconds. A successful
restart resets the backoff to zero.
Additionally, use named constants for the firmware error
threshold (CARL9170_FW_ERROR_THRESHOLD) instead of a magic
number, and add a window-based counting approach to avoid
accumulating sporadic errors over long uptimes.
Signed-off-by: Masi Osmani <mas-i@hotmail.de>
---
drivers/net/wireless/ath/carl9170/carl9170.h | 2 ++
drivers/net/wireless/ath/carl9170/main.c | 27 ++++++++++++++++++++
drivers/net/wireless/ath/carl9170/rx.c | 10 +++++++-
3 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
index a2ffa62..2eedb2f 100644
--- a/drivers/net/wireless/ath/carl9170/carl9170.h
+++ b/drivers/net/wireless/ath/carl9170/carl9170.h
@@ -301,6 +301,8 @@ struct ar9170 {
bool needs_full_reset;
bool force_usb_reset;
atomic_t pending_restarts;
+ unsigned long last_restart_jiffies;
+ unsigned int restart_backoff_ms;
/* interface mode settings */
struct list_head vif_list;
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index dcedcb1..ebf9fa9 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -492,6 +492,7 @@ static void carl9170_restart_work(struct work_struct *work)
if (!err && !ar->force_usb_reset) {
ar->restart_counter++;
atomic_set(&ar->pending_restarts, 0);
+ ar->restart_backoff_ms = 0;
ieee80211_restart_hw(ar->hw);
} else {
@@ -505,6 +506,9 @@ static void carl9170_restart_work(struct work_struct *work)
}
}
+#define CARL9170_RESTART_BACKOFF_INIT_MS 500
+#define CARL9170_RESTART_BACKOFF_MAX_MS 30000
+
void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r)
{
carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE);
@@ -519,6 +523,29 @@ void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r)
return;
}
+ /*
+ * Exponential backoff: if restarts are happening too frequently,
+ * increase the delay before accepting the next one. This prevents
+ * restart storms when the device is in a bad state.
+ */
+ if (ar->last_restart_jiffies &&
+ time_before(jiffies, ar->last_restart_jiffies +
+ msecs_to_jiffies(ar->restart_backoff_ms))) {
+ dev_warn(&ar->udev->dev,
+ "restart (%d) throttled (backoff %u ms)\n",
+ r, ar->restart_backoff_ms);
+ atomic_dec(&ar->pending_restarts);
+ return;
+ }
+
+ ar->last_restart_jiffies = jiffies;
+ if (ar->restart_backoff_ms == 0)
+ ar->restart_backoff_ms = CARL9170_RESTART_BACKOFF_INIT_MS;
+ else
+ ar->restart_backoff_ms = min(ar->restart_backoff_ms * 2,
+ (unsigned int)
+ CARL9170_RESTART_BACKOFF_MAX_MS);
+
ieee80211_stop_queues(ar->hw);
dev_err(&ar->udev->dev, "restart device (%d)\n", r);
diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
index 414d499..bb909b5 100644
--- a/drivers/net/wireless/ath/carl9170/rx.c
+++ b/drivers/net/wireless/ath/carl9170/rx.c
@@ -46,6 +46,14 @@
#include "hw.h"
#include "cmd.h"
+/*
+ * Time window for firmware error counting. Sporadic errors are
+ * normal over long uptimes; only a burst of errors within a short
+ * window warrants a restart.
+ */
+#define CARL9170_FW_ERROR_WINDOW_MS 10000
+#define CARL9170_FW_ERROR_THRESHOLD 3
+
static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len)
{
bool restart = false;
@@ -54,7 +62,7 @@ static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len)
if (len > 3) {
if (memcmp(buf, CARL9170_ERR_MAGIC, 3) == 0) {
ar->fw.err_counter++;
- if (ar->fw.err_counter > 3) {
+ if (ar->fw.err_counter >= CARL9170_FW_ERROR_THRESHOLD) {
restart = true;
reason = CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS;
}
--
2.51.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 08/10] carl9170: phy: enable antenna diversity for 2-chain devices
[not found] <cover.1773277728.git.mas-i@hotmail.de>
` (6 preceding siblings ...)
2026-03-12 10:38 ` [PATCH 07/10] carl9170: main: add exponential restart backoff Masi Osmani
@ 2026-03-12 10:38 ` Masi Osmani
2026-03-21 19:53 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 09/10] carl9170: fw: enable DFS radar detection Masi Osmani
2026-03-12 10:38 ` [PATCH 10/10] carl9170: phy: add periodic runtime IQ calibration Masi Osmani
9 siblings, 1 reply; 20+ messages in thread
From: Masi Osmani @ 2026-03-12 10:38 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Masi Osmani, ath9k-devel
Enable fast antenna diversity on devices with two RX chains
(rx_mask == 3). This programs the MULTICHAIN_GAIN_CTL register
to configure main/alternate LNA settings and enables the
CCK fast antenna diversity bit.
The AR9170 hardware has antenna diversity registers inherited
from the AR9285/AR9287 family, but the carl9170 driver never
programmed them. For dual-chain devices this improves receive
performance in multipath environments by allowing the baseband
to quickly switch between antenna paths.
The diversity configuration mirrors what ath9k uses for the
AR9285 single-chip design:
- Main LNA: LNA1
- Alt LNA: LNA1+LNA2 (combined)
- Gain table: table 0 for both paths
Signed-off-by: Masi Osmani <mas-i@hotmail.de>
---
drivers/net/wireless/ath/carl9170/phy.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
index 290c336..bcd9066 100644
--- a/drivers/net/wireless/ath/carl9170/phy.c
+++ b/drivers/net/wireless/ath/carl9170/phy.c
@@ -536,6 +536,31 @@ static int carl9170_init_phy_from_eeprom(struct ar9170 *ar,
carl9170_regwrite(AR9170_PHY_REG_RX_CHAINMASK, ar->eeprom.rx_mask);
carl9170_regwrite(AR9170_PHY_REG_CAL_CHAINMASK, ar->eeprom.rx_mask);
+ /*
+ * Enable fast antenna diversity for 2-chain devices.
+ * Configure main/alt LNA with both chains for best
+ * multipath performance.
+ */
+ if (ar->eeprom.rx_mask == 3) {
+ val = carl9170_def_val(AR9170_PHY_REG_MULTICHAIN_GAIN_CTL,
+ is_2ghz, is_40mhz);
+ val |= AR9170_PHY_9285_ANT_DIV_CTL;
+ SET_VAL(AR9170_PHY_9285_ANT_DIV_ALT_LNACONF, val,
+ AR9170_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2);
+ SET_VAL(AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF, val,
+ AR9170_PHY_9285_ANT_DIV_LNA1);
+ SET_VAL(AR9170_PHY_9285_ANT_DIV_ALT_GAINTB, val,
+ AR9170_PHY_9285_ANT_DIV_GAINTB_0);
+ SET_VAL(AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB, val,
+ AR9170_PHY_9285_ANT_DIV_GAINTB_0);
+ carl9170_regwrite(AR9170_PHY_REG_MULTICHAIN_GAIN_CTL, val);
+
+ val = carl9170_def_val(AR9170_PHY_REG_CCK_DETECT,
+ is_2ghz, is_40mhz);
+ val |= AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
+ carl9170_regwrite(AR9170_PHY_REG_CCK_DETECT, val);
+ }
+
carl9170_regwrite_finish();
return carl9170_regwrite_result();
}
--
2.51.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 09/10] carl9170: fw: enable DFS radar detection
[not found] <cover.1773277728.git.mas-i@hotmail.de>
` (7 preceding siblings ...)
2026-03-12 10:38 ` [PATCH 08/10] carl9170: phy: enable antenna diversity for 2-chain devices Masi Osmani
@ 2026-03-12 10:38 ` Masi Osmani
2026-03-21 20:11 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 10/10] carl9170: phy: add periodic runtime IQ calibration Masi Osmani
9 siblings, 1 reply; 20+ messages in thread
From: Masi Osmani @ 2026-03-12 10:38 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Masi Osmani, ath9k-devel
Enable DFS (Dynamic Frequency Selection) radar detection on the
AR9170. The hardware has radar detection registers (RADAR_0,
RADAR_1, RADAR_EXT) and the firmware already sends
CARL9170_RSP_RADAR events, but the driver never programmed the
detection parameters and only logged a "please report" message.
Changes:
- Program radar detection pulse parameters in phy.c when the
current channel has IEEE80211_CHAN_RADAR set. Values are
based on ath9k defaults for FCC/ETSI compliance.
- Advertise radar_detect_widths in the interface combination
(fw.c) for 20 MHz noHT, 20 MHz HT, and 40 MHz HT.
- Replace the old "please report" message with a call to
ieee80211_radar_detected() so mac80211 can trigger the
proper DFS state machine (channel switch / CAC).
Signed-off-by: Masi Osmani <mas-i@hotmail.de>
---
drivers/net/wireless/ath/carl9170/fw.c | 3 ++
drivers/net/wireless/ath/carl9170/phy.c | 45 +++++++++++++++++++++++++
drivers/net/wireless/ath/carl9170/rx.c | 7 ++--
3 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c
index 419f553..a730593 100644
--- a/drivers/net/wireless/ath/carl9170/fw.c
+++ b/drivers/net/wireless/ath/carl9170/fw.c
@@ -215,6 +215,9 @@ static void carl9170_fw_set_if_combinations(struct ar9170 *ar,
ar->if_combs[0].max_interfaces = ar->fw.vif_num;
ar->if_combs[0].limits = ar->if_comb_limits;
ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits);
+ ar->if_combs[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+ BIT(NL80211_CHAN_WIDTH_20) |
+ BIT(NL80211_CHAN_WIDTH_40);
ar->hw->wiphy->iface_combinations = ar->if_combs;
ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs);
diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
index bcd9066..c294df7 100644
--- a/drivers/net/wireless/ath/carl9170/phy.c
+++ b/drivers/net/wireless/ath/carl9170/phy.c
@@ -1637,6 +1637,47 @@ void carl9170_update_channel_maxpower(struct ar9170 *ar)
}
}
+static int carl9170_set_radar_detection(struct ar9170 *ar,
+ struct ieee80211_channel *channel)
+{
+ bool enable = channel->flags & IEEE80211_CHAN_RADAR;
+
+ carl9170_regwrite_begin(ar);
+
+ if (enable) {
+ /*
+ * Configure radar detection pulse parameters.
+ * Values based on ath9k's defaults for FCC/ETSI.
+ */
+ carl9170_regwrite(AR9170_PHY_REG_RADAR_0,
+ AR9170_PHY_RADAR_0_ENA |
+ AR9170_PHY_RADAR_0_FFT_ENA |
+ SET_CONSTVAL(AR9170_PHY_RADAR_0_INBAND, 5) |
+ SET_CONSTVAL(AR9170_PHY_RADAR_0_PRSSI, 1) |
+ SET_CONSTVAL(AR9170_PHY_RADAR_0_HEIGHT, 6) |
+ SET_CONSTVAL(AR9170_PHY_RADAR_0_RRSSI, 12) |
+ SET_CONSTVAL(AR9170_PHY_RADAR_0_FIRPWR, 33));
+
+ carl9170_regwrite(AR9170_PHY_REG_RADAR_1,
+ AR9170_PHY_RADAR_1_MAX_RRSSI |
+ AR9170_PHY_RADAR_1_BLOCK_CHECK |
+ AR9170_PHY_RADAR_1_RELSTEP_CHECK |
+ SET_CONSTVAL(AR9170_PHY_RADAR_1_RELSTEP_THRESH, 8) |
+ SET_CONSTVAL(AR9170_PHY_RADAR_1_RELPWR_THRESH, 12) |
+ SET_CONSTVAL(AR9170_PHY_RADAR_1_MAXLEN, 255));
+
+ carl9170_regwrite(AR9170_PHY_REG_RADAR_EXT,
+ AR9170_PHY_RADAR_EXT_ENA);
+ } else {
+ carl9170_regwrite(AR9170_PHY_REG_RADAR_0, 0);
+ carl9170_regwrite(AR9170_PHY_REG_RADAR_1, 0);
+ carl9170_regwrite(AR9170_PHY_REG_RADAR_EXT, 0);
+ }
+
+ carl9170_regwrite_finish();
+ return carl9170_regwrite_result();
+}
+
int carl9170_get_noisefloor(struct ar9170 *ar)
{
static const u32 phy_regs[] = {
@@ -1739,6 +1780,10 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
if (err)
return err;
+ err = carl9170_set_radar_detection(ar, channel);
+ if (err)
+ return err;
+
tmp = AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1 |
AR9170_PHY_TURBO_FC_HT_EN;
diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
index bb909b5..1fe727c 100644
--- a/drivers/net/wireless/ath/carl9170/rx.c
+++ b/drivers/net/wireless/ath/carl9170/rx.c
@@ -259,11 +259,8 @@ void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len)
break;
case CARL9170_RSP_RADAR:
- if (!net_ratelimit())
- break;
-
- wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this "
- "incident to linux-wireless@vger.kernel.org !\n");
+ wiphy_info(ar->hw->wiphy, "FW: radar pulse detected\n");
+ ieee80211_radar_detected(ar->hw, NULL);
break;
case CARL9170_RSP_GPIO:
--
2.51.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH 10/10] carl9170: phy: add periodic runtime IQ calibration
[not found] <cover.1773277728.git.mas-i@hotmail.de>
` (8 preceding siblings ...)
2026-03-12 10:38 ` [PATCH 09/10] carl9170: fw: enable DFS radar detection Masi Osmani
@ 2026-03-12 10:38 ` Masi Osmani
2026-03-21 21:25 ` Christian Lamparter
9 siblings, 1 reply; 20+ messages in thread
From: Masi Osmani @ 2026-03-12 10:38 UTC (permalink / raw)
To: Christian Lamparter; +Cc: linux-wireless, Masi Osmani, ath9k-devel
Add periodic runtime I/Q calibration triggered from the existing
survey statistics work handler (carl9170_stat_work). The AR9170
hardware performs initial IQ calibration during channel setup, but
I/Q imbalance drifts with temperature over time, degrading EVM
and increasing packet error rate.
The new carl9170_run_iq_calibration() function sets the DO_IQCAL
bit in PHY_TIMING_CTRL4 for both chains, which triggers the
hardware to re-measure I/Q imbalance and update the correction
coefficients automatically. This is a non-blocking operation --
the hardware runs the calibration in the background without
interrupting normal traffic.
The ath9k driver performs similar periodic calibration via its
longcal timer (every 30s). carl9170_stat_work runs at a
comparable interval, making it a natural trigger point.
Signed-off-by: Masi Osmani <mas-i@hotmail.de>
---
drivers/net/wireless/ath/carl9170/carl9170.h | 1 +
drivers/net/wireless/ath/carl9170/main.c | 2 ++
drivers/net/wireless/ath/carl9170/phy.c | 36 ++++++++++++++++++++
3 files changed, 39 insertions(+)
diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
index 2eedb2f..0175f8e 100644
--- a/drivers/net/wireless/ath/carl9170/carl9170.h
+++ b/drivers/net/wireless/ath/carl9170/carl9170.h
@@ -605,6 +605,7 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
enum nl80211_channel_type bw);
int carl9170_get_noisefloor(struct ar9170 *ar);
void carl9170_update_channel_maxpower(struct ar9170 *ar);
+int carl9170_run_iq_calibration(struct ar9170 *ar);
/* FW */
int carl9170_parse_firmware(struct ar9170 *ar);
diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
index ebf9fa9..50c0922 100644
--- a/drivers/net/wireless/ath/carl9170/main.c
+++ b/drivers/net/wireless/ath/carl9170/main.c
@@ -910,6 +910,8 @@ static void carl9170_stat_work(struct work_struct *work)
mutex_lock(&ar->mutex);
err = carl9170_update_survey(ar, false, true);
+ if (!err)
+ carl9170_run_iq_calibration(ar);
mutex_unlock(&ar->mutex);
if (err)
diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
index c294df7..b145e9e 100644
--- a/drivers/net/wireless/ath/carl9170/phy.c
+++ b/drivers/net/wireless/ath/carl9170/phy.c
@@ -1637,6 +1637,42 @@ void carl9170_update_channel_maxpower(struct ar9170 *ar)
}
}
+int carl9170_run_iq_calibration(struct ar9170 *ar)
+{
+ u32 val;
+ int err;
+
+ if (!ar->channel)
+ return 0;
+
+ /*
+ * Trigger runtime IQ calibration. The hardware measures
+ * I/Q imbalance and updates the correction coefficients
+ * automatically when DO_IQCAL is set. We trigger on both
+ * chains and re-enable the IQ correction afterwards.
+ */
+ err = carl9170_read_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(0), &val);
+ if (err)
+ return err;
+
+ val |= AR9170_PHY_TIMING_CTRL4_DO_IQCAL;
+ err = carl9170_write_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(0), val);
+ if (err)
+ return err;
+
+ /* chain 2 */
+ err = carl9170_read_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(2), &val);
+ if (err)
+ return err;
+
+ val |= AR9170_PHY_TIMING_CTRL4_DO_IQCAL;
+ err = carl9170_write_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(2), val);
+ if (err)
+ return err;
+
+ return 0;
+}
+
static int carl9170_set_radar_detection(struct ar9170 *ar,
struct ieee80211_channel *channel)
{
--
2.51.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH 01/10] carl9170: mac80211: enable Short Guard Interval for 20 MHz
2026-03-12 10:37 ` [PATCH 01/10] carl9170: mac80211: enable Short Guard Interval for 20 MHz Masi Osmani
@ 2026-03-21 18:41 ` Christian Lamparter
0 siblings, 0 replies; 20+ messages in thread
From: Christian Lamparter @ 2026-03-21 18:41 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
On 3/12/26 11:37 AM, Masi Osmani wrote:
> The AR9170 hardware supports Short Guard Interval (400ns) for both
> 20 MHz and 40 MHz channel widths. SGI_40 was already advertised in
> the HT capabilities, but SGI_20 was missing. This reduces the OFDM
> symbol duration from 800ns to 400ns on 20 MHz channels, increasing
> the maximum PHY rate from 130 Mbps to 144.4 Mbps (MCS 15, 2SS).
>
> ath9k (the PCI sibling for the same AR9xxx chipset family) has
> always advertised both SGI_20 and SGI_40.
>
Are you really, really sure about that?
Because based on what's in ath9k:
<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/wireless/ath/ath9k/hw.c?h=v7.0-rc4#n2622>
| if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah))
| pCap->hw_caps |= ATH9K_HW_CAP_SGI_20;
Whereas AR_SREV_9287_11_OR_LATER and AR_SREV_9271 are defined in reg.h @
<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/wireless/ath/ath9k/reg.h?h=v7.0-rc4#n861>
|#define AR_SREV_9287_11_OR_LATER(_ah) \
| (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9287))
| [...]
|#define AR_SREV_9271(_ah) \
| (((_ah))->hw_version.macVersion == AR_SREV_VERSION_9271)
What's strange here is that this isn't a PHY check, but MAC check...
And the AR9170 uses a modified ZyDAS MAC which is completely different
than what any of the ath9k siblings uses.
So based on that: No. I can't ACK that. The feature might work or not,
but that commit message is wrong.
That said, if you say have been successfully been using this and rewrite the
commit message to not include wrong information and add a module_parameter
like experimental that enables it, this would be OK!
Cheers,
Christian
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/main.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index a7a9345..aa7e481 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -154,6 +154,7 @@ static struct ieee80211_channel carl9170_5ghz_chantable[] = {
> .cap = IEEE80211_HT_CAP_MAX_AMSDU | \
> IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
> IEEE80211_HT_CAP_SGI_40 | \
> + IEEE80211_HT_CAP_SGI_20 | \
> IEEE80211_HT_CAP_DSSSCCK40 | \
> IEEE80211_HT_CAP_SM_PS, \
> .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 02/10] carl9170: mac80211: advertise RX STBC capability
2026-03-12 10:37 ` [PATCH 02/10] carl9170: mac80211: advertise RX STBC capability Masi Osmani
@ 2026-03-21 18:47 ` Christian Lamparter
0 siblings, 0 replies; 20+ messages in thread
From: Christian Lamparter @ 2026-03-21 18:47 UTC (permalink / raw)
To: Masi Osmani, Christian Lamparter; +Cc: linux-wireless, ath9k-devel
On 3/12/26 11:37 AM, Masi Osmani wrote:
> The AR9170 baseband supports receiving Space-Time Block Coded
> streams (1 spatial stream). The capability was not advertised
> in the HT capabilities, causing peers to never use STBC when
> transmitting to us. Enabling RX STBC improves receive diversity
> gain and link reliability, especially in multipath environments.
>
> The ath9k driver for the same chipset family advertises RX STBC
> based on the number of RX chains. With 2 RX chains, the AR9170
> can decode 1 STBC spatial stream.
No, not that I can tell.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/wireless/ath/ath9k/common-init.c?h=v7.0-rc4#n204
| if (AR_SREV_9280_20_OR_LATER(ah)) {
| if (max_streams >= 2)
| ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
| ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
| }
the ath9k driver supports this for generation after the AR9160, the AR9280
The commit message is wrong again. If you are sure this is working though,
please rewrite the commit message and put this feature after a experimental
module parameter (like for the STBC patch you proposed).
Cheers,
Christian
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/main.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index aa7e481..6324b38 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -156,7 +156,8 @@ static struct ieee80211_channel carl9170_5ghz_chantable[] = {
> IEEE80211_HT_CAP_SGI_40 | \
> IEEE80211_HT_CAP_SGI_20 | \
> IEEE80211_HT_CAP_DSSSCCK40 | \
> - IEEE80211_HT_CAP_SM_PS, \
> + IEEE80211_HT_CAP_SM_PS | \
> + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \
> .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \
> .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
> .mcs = { \
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 03/10] carl9170: mac80211: document spatial multiplexing power save handler
2026-03-12 10:37 ` [PATCH 03/10] carl9170: mac80211: document spatial multiplexing power save handler Masi Osmani
@ 2026-03-21 18:57 ` Christian Lamparter
0 siblings, 0 replies; 20+ messages in thread
From: Christian Lamparter @ 2026-03-21 18:57 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
On 3/12/26 11:37 AM, Masi Osmani wrote:
> Replace the bare TODO comment in the SMPS configuration handler
> with documentation explaining why the driver accepts but does not
> act on SMPS mode changes.
>
> The AR9170 advertises SM_PS disabled (both chains always active)
> in its HT capabilities. While mac80211 may still send SMPS
> configuration requests, implementing static or dynamic SMPS would
> require firmware support for per-chain enable/disable that the
> AR9170 firmware (v1.9.9) does not provide.
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
Sure, this patch is reasonable.
Acked-by: Christian Lamparter <chunkeey@gmail.com>
> ---
> drivers/net/wireless/ath/carl9170/main.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index 6324b38..d75688c 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -910,7 +910,13 @@ static int carl9170_op_config(struct ieee80211_hw *hw, int radio_idx, u32 change
> }
>
> if (changed & IEEE80211_CONF_CHANGE_SMPS) {
> - /* TODO */
> + /*
> + * We advertise SM_PS disabled (all chains active).
> + * mac80211 may still request mode changes, which we
> + * accept but only support OFF (both chains active).
> + * Static/dynamic SMPS would require firmware support
> + * for chain control that the AR9170 does not provide.
> + */
> err = 0;
> }
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 04/10] carl9170: rx: wire up dropped frame counter
2026-03-12 10:37 ` [PATCH 04/10] carl9170: rx: wire up dropped frame counter Masi Osmani
@ 2026-03-21 19:03 ` Christian Lamparter
0 siblings, 0 replies; 20+ messages in thread
From: Christian Lamparter @ 2026-03-21 19:03 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
On 3/12/26 11:37 AM, Masi Osmani wrote:
> Increment ar->rx_dropped when frames with unrecognized error
> codes are dropped in carl9170_rx_mac_status(). The counter
> was already defined in the ar9170 struct and exported via
> debugfs, but the actual increment was missing -- only a TODO
> comment was in place.
>
> This provides visibility into receive-path frame drops through
> the existing debugfs interface.
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/rx.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
> index 6833430..c664014 100644
> --- a/drivers/net/wireless/ath/carl9170/rx.c
> +++ b/drivers/net/wireless/ath/carl9170/rx.c
> @@ -340,7 +340,7 @@ static int carl9170_rx_mac_status(struct ar9170 *ar,
>
> /* drop any other error frames */
> if (unlikely(error)) {
> - /* TODO: update netdevice's RX dropped/errors statistics */
> + ar->rx_dropped++;
No, that would cause it to be counted twice.
This is because in the parent function carl9170_rx_untie_data() (yes, carl9170_rx_mac_status() is only
called from there.) already has the "ar->rx_dropped++;" in the "drop:" error code path.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 06/10] carl9170: phy: populate per-channel TX power from EEPROM
2026-03-12 10:38 ` [PATCH 06/10] carl9170: phy: populate per-channel TX power from EEPROM Masi Osmani
@ 2026-03-21 19:24 ` Christian Lamparter
0 siblings, 0 replies; 20+ messages in thread
From: Christian Lamparter @ 2026-03-21 19:24 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
On 3/12/26 11:38 AM, Masi Osmani wrote:
> Replace the hardcoded max_power = 18 dBm (marked XXX) in the
> channel definitions with actual per-channel values derived from
> the EEPROM calibration target power tables.
>
> The new carl9170_get_max_tgt_power() function interpolates the
> maximum target power for a given frequency from the EEPROM's
> legacy OFDM and CCK target power tables, using the same frequency
> encoding and interpolation helpers already used by the power
> calibration code. carl9170_update_channel_maxpower() iterates
> all registered channels and updates their max_power fields.
Why the need for interpolation here? Don't you just need to look
for the max(ctpl[idx].power, previous_value) within the band?
I'm not aware of any high-powered AR9170 devices. Were/are there any?
Cheers,
Christian
>
> This is called during EEPROM parsing, so mac80211 and userspace
> see correct per-channel power limits from the start. The CHAN
> macro default of 18 dBm remains as a safe fallback for channels
> where EEPROM data is missing.
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/carl9170.h | 1 +
> drivers/net/wireless/ath/carl9170/main.c | 4 +-
> drivers/net/wireless/ath/carl9170/phy.c | 88 ++++++++++++++++++++
> 3 files changed, 92 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
> index eaac859..a2ffa62 100644
> --- a/drivers/net/wireless/ath/carl9170/carl9170.h
> +++ b/drivers/net/wireless/ath/carl9170/carl9170.h
> @@ -602,6 +602,7 @@ int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state);
> int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
> enum nl80211_channel_type bw);
> int carl9170_get_noisefloor(struct ar9170 *ar);
> +void carl9170_update_channel_maxpower(struct ar9170 *ar);
>
> /* FW */
> int carl9170_parse_firmware(struct ar9170 *ar);
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index d75688c..dcedcb1 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -89,7 +89,7 @@ struct ieee80211_rate __carl9170_ratetable[] = {
> #define CHAN(_freq, _idx) { \
> .center_freq = (_freq), \
> .hw_value = (_idx), \
> - .max_power = 18, /* XXX */ \
> + .max_power = 18, \
> }
>
> static struct ieee80211_channel carl9170_2ghz_chantable[] = {
> @@ -1930,6 +1930,8 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
> if (!bands)
> return -EINVAL;
>
> + carl9170_update_channel_maxpower(ar);
> +
> ar->survey = devm_kcalloc(&ar->udev->dev, chans,
> sizeof(struct survey_info), GFP_KERNEL);
> if (!ar->survey)
> diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
> index 34d9fd7..290c336 100644
> --- a/drivers/net/wireless/ath/carl9170/phy.c
> +++ b/drivers/net/wireless/ath/carl9170/phy.c
> @@ -1524,6 +1524,94 @@ static void carl9170_set_power_cal(struct ar9170 *ar, u32 freq,
> carl9170_calc_ctl(ar, freq, bw);
> }
>
> +static u8 carl9170_get_max_tgt_power(struct ar9170 *ar, u32 freq)
> +{
> + struct ar9170_calibration_target_power_legacy *ctpl;
> + int ntargets, idx, n, i;
> + u8 f, max_power = 0;
> + u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS];
> +
> + if (freq < 3000)
> + f = freq - 2300;
> + else
> + f = (freq - 4800) / 5;
> +
> + /* check legacy target powers (OFDM for 2G, 5G leg) */
> + for (i = 0; i < 2; i++) {
> + switch (i) {
> + case 0:
> + if (freq >= 3000) {
> + ctpl = &ar->eeprom.cal_tgt_pwr_5G[0];
> + ntargets = AR5416_NUM_5G_TARGET_PWRS;
> + } else {
> + ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0];
> + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
> + }
> + break;
> + case 1:
> + if (freq < 3000) {
> + ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0];
> + ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS;
> + } else {
> + continue;
> + }
> + break;
> + default:
> + continue;
> + }
> +
> + for (n = 0; n < ntargets; n++) {
> + if (ctpl[n].freq == 0xff)
> + break;
> + pwr_freqs[n] = ctpl[n].freq;
> + }
> + ntargets = n;
> + if (ntargets < 2)
> + continue;
> +
> + idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f);
> + for (n = 0; n < 4; n++) {
> + u8 pwr;
> +
> + pwr = carl9170_interpolate_u8(f,
> + ctpl[idx + 0].freq,
> + ctpl[idx + 0].power[n],
> + ctpl[idx + 1].freq,
> + ctpl[idx + 1].power[n]);
> + max_power = max(max_power, pwr);
> + }
> + }
> +
> + /* target power is in half-dBm, max_power is in dBm */
> + return max_power / 2;
> +}
> +
> +void carl9170_update_channel_maxpower(struct ar9170 *ar)
> +{
> + struct ieee80211_supported_band *band;
> + int i;
> +
> + band = ar->hw->wiphy->bands[NL80211_BAND_2GHZ];
> + if (band) {
> + for (i = 0; i < band->n_channels; i++) {
> + u8 pwr = carl9170_get_max_tgt_power(ar,
> + band->channels[i].center_freq);
> + if (pwr)
> + band->channels[i].max_power = pwr;
> + }
> + }
> +
> + band = ar->hw->wiphy->bands[NL80211_BAND_5GHZ];
> + if (band) {
> + for (i = 0; i < band->n_channels; i++) {
> + u8 pwr = carl9170_get_max_tgt_power(ar,
> + band->channels[i].center_freq);
> + if (pwr)
> + band->channels[i].max_power = pwr;
> + }
> + }
> +}
> +
> int carl9170_get_noisefloor(struct ar9170 *ar)
> {
> static const u32 phy_regs[] = {
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 08/10] carl9170: phy: enable antenna diversity for 2-chain devices
2026-03-12 10:38 ` [PATCH 08/10] carl9170: phy: enable antenna diversity for 2-chain devices Masi Osmani
@ 2026-03-21 19:53 ` Christian Lamparter
0 siblings, 0 replies; 20+ messages in thread
From: Christian Lamparter @ 2026-03-21 19:53 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
On 3/12/26 11:38 AM, Masi Osmani wrote:
> Enable fast antenna diversity on devices with two RX chains
> (rx_mask == 3). This programs the MULTICHAIN_GAIN_CTL register
> to configure main/alternate LNA settings and enables the
> CCK fast antenna diversity bit.
>
> The AR9170 hardware has antenna diversity registers inherited
> from the AR9285/AR9287 family, but the carl9170 driver never
> programmed them. For dual-chain devices this improves receive
> performance in multipath environments by allowing the baseband
> to quickly switch between antenna paths.
Oh, no. It does program them! It's part of the ar5416_phy_init array in phy.c.
So. From what I remember, this was the reason why I copied the definitions
over from the ath9k driver to carl9170. Because this register must be important
if they are part of the init values. But I don't know if these definitions
are the same for AR9170 and AR9285. That's why the AR9285 is there.
As for ar5416_phy_init values. They came from the original OTUS driver.
Hope this helps.
If possible, this feature should be governed by something like the experimental
module parameter?
>
> The diversity configuration mirrors what ath9k uses for the
> AR9285 single-chip design:
> - Main LNA: LNA1
> - Alt LNA: LNA1+LNA2 (combined)
> - Gain table: table 0 for both paths
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/phy.c | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
> index 290c336..bcd9066 100644
> --- a/drivers/net/wireless/ath/carl9170/phy.c
> +++ b/drivers/net/wireless/ath/carl9170/phy.c
> @@ -536,6 +536,31 @@ static int carl9170_init_phy_from_eeprom(struct ar9170 *ar,
> carl9170_regwrite(AR9170_PHY_REG_RX_CHAINMASK, ar->eeprom.rx_mask);
> carl9170_regwrite(AR9170_PHY_REG_CAL_CHAINMASK, ar->eeprom.rx_mask);
>
> + /*
> + * Enable fast antenna diversity for 2-chain devices.
> + * Configure main/alt LNA with both chains for best
> + * multipath performance.
> + */
> + if (ar->eeprom.rx_mask == 3) {
> + val = carl9170_def_val(AR9170_PHY_REG_MULTICHAIN_GAIN_CTL,
> + is_2ghz, is_40mhz);
> + val |= AR9170_PHY_9285_ANT_DIV_CTL;
> + SET_VAL(AR9170_PHY_9285_ANT_DIV_ALT_LNACONF, val,
> + AR9170_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2);
> + SET_VAL(AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF, val,
> + AR9170_PHY_9285_ANT_DIV_LNA1);
> + SET_VAL(AR9170_PHY_9285_ANT_DIV_ALT_GAINTB, val,
> + AR9170_PHY_9285_ANT_DIV_GAINTB_0);
> + SET_VAL(AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB, val,
> + AR9170_PHY_9285_ANT_DIV_GAINTB_0);
> + carl9170_regwrite(AR9170_PHY_REG_MULTICHAIN_GAIN_CTL, val);
> +
> + val = carl9170_def_val(AR9170_PHY_REG_CCK_DETECT,
> + is_2ghz, is_40mhz);
> + val |= AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
> + carl9170_regwrite(AR9170_PHY_REG_CCK_DETECT, val);
> + }
> +
> carl9170_regwrite_finish();
> return carl9170_regwrite_result();
> }
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 09/10] carl9170: fw: enable DFS radar detection
2026-03-12 10:38 ` [PATCH 09/10] carl9170: fw: enable DFS radar detection Masi Osmani
@ 2026-03-21 20:11 ` Christian Lamparter
0 siblings, 0 replies; 20+ messages in thread
From: Christian Lamparter @ 2026-03-21 20:11 UTC (permalink / raw)
To: Masi Osmani, Christian Lamparter; +Cc: linux-wireless, ath9k-devel
On 3/12/26 11:38 AM, Masi Osmani wrote:
> Enable DFS (Dynamic Frequency Selection) radar detection on the
> AR9170. The hardware has radar detection registers (RADAR_0,
> RADAR_1, RADAR_EXT) and the firmware already sends
> CARL9170_RSP_RADAR events, but the driver never programmed the
> detection parameters and only logged a "please report" message.
>
> Changes:
> - Program radar detection pulse parameters in phy.c when the
> current channel has IEEE80211_CHAN_RADAR set. Values are
> based on ath9k defaults for FCC/ETSI compliance.
Which values did you use? The best I can find are in ar5008_phy.c
<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/wireless/ath/ath9k/ar5008_phy.c?h=v7.0-rc4#n1268>
| conf->fir_power = -33;
| conf->radar_rssi = 20;
| conf->pulse_height = 10;
| conf->pulse_rssi = 15;
| conf->pulse_inband = 15;
| conf->pulse_maxlen = 255;
| conf->pulse_inband_step = 12;
| conf->radar_inband = 8;
the one in the patch look a little bit different.
Can I ask, how did you tune them? Do you have a setup to mimic
those air-port or weather radars? Or are you living close to one?
> - Advertise radar_detect_widths in the interface combination
> (fw.c) for 20 MHz noHT, 20 MHz HT, and 40 MHz HT.
> - Replace the old "please report" message with a call to
> ieee80211_radar_detected() so mac80211 can trigger the
> proper DFS state machine (channel switch / CAC).
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/fw.c | 3 ++
> drivers/net/wireless/ath/carl9170/phy.c | 45 +++++++++++++++++++++++++
> drivers/net/wireless/ath/carl9170/rx.c | 7 ++--
> 3 files changed, 50 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c
> index 419f553..a730593 100644
> --- a/drivers/net/wireless/ath/carl9170/fw.c
> +++ b/drivers/net/wireless/ath/carl9170/fw.c
> @@ -215,6 +215,9 @@ static void carl9170_fw_set_if_combinations(struct ar9170 *ar,
> ar->if_combs[0].max_interfaces = ar->fw.vif_num;
> ar->if_combs[0].limits = ar->if_comb_limits;
> ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits);
> + ar->if_combs[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
> + BIT(NL80211_CHAN_WIDTH_20) |
> + BIT(NL80211_CHAN_WIDTH_40);
>
> ar->hw->wiphy->iface_combinations = ar->if_combs;
> ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs);
> diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
> index bcd9066..c294df7 100644
> --- a/drivers/net/wireless/ath/carl9170/phy.c
> +++ b/drivers/net/wireless/ath/carl9170/phy.c
> @@ -1637,6 +1637,47 @@ void carl9170_update_channel_maxpower(struct ar9170 *ar)
> }
> }
>
> +static int carl9170_set_radar_detection(struct ar9170 *ar,
> + struct ieee80211_channel *channel)
> +{
> + bool enable = channel->flags & IEEE80211_CHAN_RADAR;
> +
> + carl9170_regwrite_begin(ar);
> +
> + if (enable) {
> + /*
> + * Configure radar detection pulse parameters.
> + * Values based on ath9k's defaults for FCC/ETSI.
> + */
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_0,
> + AR9170_PHY_RADAR_0_ENA |
> + AR9170_PHY_RADAR_0_FFT_ENA |
> + SET_CONSTVAL(AR9170_PHY_RADAR_0_INBAND, 5) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_0_PRSSI, 1) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_0_HEIGHT, 6) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_0_RRSSI, 12) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_0_FIRPWR, 33));
> +
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_1,
> + AR9170_PHY_RADAR_1_MAX_RRSSI |
> + AR9170_PHY_RADAR_1_BLOCK_CHECK |
> + AR9170_PHY_RADAR_1_RELSTEP_CHECK |
> + SET_CONSTVAL(AR9170_PHY_RADAR_1_RELSTEP_THRESH, 8) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_1_RELPWR_THRESH, 12) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_1_MAXLEN, 255));
> +
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_EXT,
> + AR9170_PHY_RADAR_EXT_ENA);
This seems to be based on ar5008_hw_set_radar_params except that:
| if (conf->ext_channel)
| REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
| else
| REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
you always set the EXT_ENA bit?
> + } else {
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_0, 0);
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_1, 0);
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_EXT, 0);
> + }
> +
> + carl9170_regwrite_finish();
> + return carl9170_regwrite_result();
> +}
> +
> int carl9170_get_noisefloor(struct ar9170 *ar)
> {
> static const u32 phy_regs[] = {
> @@ -1739,6 +1780,10 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
> if (err)
> return err;
>
> + err = carl9170_set_radar_detection(ar, channel);
> + if (err)
> + return err;
> +
> tmp = AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1 |
> AR9170_PHY_TURBO_FC_HT_EN;
>
> diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
> index bb909b5..1fe727c 100644
> --- a/drivers/net/wireless/ath/carl9170/rx.c
> +++ b/drivers/net/wireless/ath/carl9170/rx.c
> @@ -259,11 +259,8 @@ void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len)
> break;
>
> case CARL9170_RSP_RADAR:
> - if (!net_ratelimit())
> - break;
> -
> - wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this "
> - "incident to linux-wireless@vger.kernel.org !\n");
> + wiphy_info(ar->hw->wiphy, "FW: radar pulse detected\n");
> + ieee80211_radar_detected(ar->hw, NULL);
> break;
>
> case CARL9170_RSP_GPIO:
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 05/10] carl9170: rx: track PHY errors via debugfs
2026-03-12 10:38 ` [PATCH 05/10] carl9170: rx: track PHY errors via debugfs Masi Osmani
@ 2026-03-21 20:29 ` Christian Lamparter
0 siblings, 0 replies; 20+ messages in thread
From: Christian Lamparter @ 2026-03-21 20:29 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
On 3/12/26 11:38 AM, Masi Osmani wrote:
> Count PHY errors reported by the hardware in the RX status and
> expose the counter through debugfs as rx_phy_errors. Previously,
> PHY errors from ar9170_rx_phystatus were silently ignored (marked
> with a TODO comment).
>
> The counter helps diagnose RF environment issues (interference,
> multipath, low SNR) without requiring monitor mode or additional
> tooling.
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
Sure!
Acked-by: Christian Lamparter <chunkeey@gmail.com>
> ---
> drivers/net/wireless/ath/carl9170/carl9170.h | 1 +
> drivers/net/wireless/ath/carl9170/debug.c | 2 ++
> drivers/net/wireless/ath/carl9170/rx.c | 4 +++-
> 3 files changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
> index ba29b4a..eaac859 100644
> --- a/drivers/net/wireless/ath/carl9170/carl9170.h
> +++ b/drivers/net/wireless/ath/carl9170/carl9170.h
> @@ -386,6 +386,7 @@ struct ar9170 {
> unsigned int tx_ack_failures;
> unsigned int tx_fcs_errors;
> unsigned int rx_dropped;
> + unsigned int rx_phy_errors;
>
> /* EEPROM */
> struct ar9170_eeprom eeprom;
> diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c
> index 2d73456..0498df2 100644
> --- a/drivers/net/wireless/ath/carl9170/debug.c
> +++ b/drivers/net/wireless/ath/carl9170/debug.c
> @@ -794,6 +794,7 @@ DEBUGFS_READONLY_FILE(tx_janitor_last_run, 64, "last run:%d ms ago",
> DEBUGFS_READONLY_FILE(tx_dropped, 20, "%d", ar->tx_dropped);
>
> DEBUGFS_READONLY_FILE(rx_dropped, 20, "%d", ar->rx_dropped);
> +DEBUGFS_READONLY_FILE(rx_phy_errors, 20, "%d", ar->rx_phy_errors);
>
> DEBUGFS_READONLY_FILE(sniffer_enabled, 20, "%d", ar->sniffer_enabled);
> DEBUGFS_READONLY_FILE(rx_software_decryption, 20, "%d",
> @@ -830,6 +831,7 @@ void carl9170_debugfs_register(struct ar9170 *ar)
> DEBUGFS_ADD(tx_ampdu_list_len);
>
> DEBUGFS_ADD(rx_dropped);
> + DEBUGFS_ADD(rx_phy_errors);
> DEBUGFS_ADD(sniffer_enabled);
> DEBUGFS_ADD(rx_software_decryption);
>
> diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
> index c664014..414d499 100644
> --- a/drivers/net/wireless/ath/carl9170/rx.c
> +++ b/drivers/net/wireless/ath/carl9170/rx.c
> @@ -455,7 +455,9 @@ static void carl9170_rx_phy_status(struct ar9170 *ar,
> if (phy->rssi[i] & 0x80)
> phy->rssi[i] = ((~phy->rssi[i] & 0x7f) + 1) & 0x7f;
>
> - /* TODO: we could do something with phy_errors */
> + if (phy->phy_err)
> + ar->rx_phy_errors++;
> +
> status->signal = ar->noise[0] + phy->rssi_combined;
> }
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 07/10] carl9170: main: add exponential restart backoff
2026-03-12 10:38 ` [PATCH 07/10] carl9170: main: add exponential restart backoff Masi Osmani
@ 2026-03-21 20:42 ` Christian Lamparter
0 siblings, 0 replies; 20+ messages in thread
From: Christian Lamparter @ 2026-03-21 20:42 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
On 3/12/26 11:38 AM, Masi Osmani wrote:
> When the AR9170 enters a bad state (firmware errors, command
> timeouts, TX queue stalls), the driver can trigger rapid-fire
> restarts that prevent the device from ever stabilizing.
>
> Add exponential backoff to carl9170_restart(): if a restart
> request arrives before the current backoff window has elapsed,
> the request is throttled. The backoff starts at 500 ms and
> doubles on each restart, capping at 30 seconds. A successful
> restart resets the backoff to zero.
If this helps? It would be great if the device could get to "cool down"
instead of the restart request being throttled, after all something must
have happend for the restart request to be issued.
>
> Additionally, use named constants for the firmware error
> threshold (CARL9170_FW_ERROR_THRESHOLD) instead of a magic
> number, and add a window-based counting approach to avoid
> accumulating sporadic errors over long uptimes.
I guess you are refering with "window-based counting" to the
CARL9170_FW_ERROR_WINDOW_MS ? But is it used anywhere?
Maybe part of the patch is missing, or is there another patch?
Otherwise, I would be inclined to test this out (in other words: ack it).
Cheers,
Christian
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/carl9170.h | 2 ++
> drivers/net/wireless/ath/carl9170/main.c | 27 ++++++++++++++++++++
> drivers/net/wireless/ath/carl9170/rx.c | 10 +++++++-
> 3 files changed, 38 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
> index a2ffa62..2eedb2f 100644
> --- a/drivers/net/wireless/ath/carl9170/carl9170.h
> +++ b/drivers/net/wireless/ath/carl9170/carl9170.h
> @@ -301,6 +301,8 @@ struct ar9170 {
> bool needs_full_reset;
> bool force_usb_reset;
> atomic_t pending_restarts;
> + unsigned long last_restart_jiffies;
> + unsigned int restart_backoff_ms;
>
> /* interface mode settings */
> struct list_head vif_list;
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index dcedcb1..ebf9fa9 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -492,6 +492,7 @@ static void carl9170_restart_work(struct work_struct *work)
> if (!err && !ar->force_usb_reset) {
> ar->restart_counter++;
> atomic_set(&ar->pending_restarts, 0);
> + ar->restart_backoff_ms = 0;
>
> ieee80211_restart_hw(ar->hw);
> } else {
> @@ -505,6 +506,9 @@ static void carl9170_restart_work(struct work_struct *work)
> }
> }
>
> +#define CARL9170_RESTART_BACKOFF_INIT_MS 500
> +#define CARL9170_RESTART_BACKOFF_MAX_MS 30000
> +
> void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r)
> {
> carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE);
> @@ -519,6 +523,29 @@ void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r)
> return;
> }
>
> + /*
> + * Exponential backoff: if restarts are happening too frequently,
> + * increase the delay before accepting the next one. This prevents
> + * restart storms when the device is in a bad state.
> + */
> + if (ar->last_restart_jiffies &&
> + time_before(jiffies, ar->last_restart_jiffies +
> + msecs_to_jiffies(ar->restart_backoff_ms))) {
> + dev_warn(&ar->udev->dev,
> + "restart (%d) throttled (backoff %u ms)\n",
> + r, ar->restart_backoff_ms);
> + atomic_dec(&ar->pending_restarts);
> + return;
> + }
> +
> + ar->last_restart_jiffies = jiffies;
> + if (ar->restart_backoff_ms == 0)
> + ar->restart_backoff_ms = CARL9170_RESTART_BACKOFF_INIT_MS;
> + else
> + ar->restart_backoff_ms = min(ar->restart_backoff_ms * 2,
> + (unsigned int)
> + CARL9170_RESTART_BACKOFF_MAX_MS);
> +
> ieee80211_stop_queues(ar->hw);
>
> dev_err(&ar->udev->dev, "restart device (%d)\n", r);
> diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
> index 414d499..bb909b5 100644
> --- a/drivers/net/wireless/ath/carl9170/rx.c
> +++ b/drivers/net/wireless/ath/carl9170/rx.c
> @@ -46,6 +46,14 @@
> #include "hw.h"
> #include "cmd.h"
>
> +/*
> + * Time window for firmware error counting. Sporadic errors are
> + * normal over long uptimes; only a burst of errors within a short
> + * window warrants a restart.
> + */
> +#define CARL9170_FW_ERROR_WINDOW_MS 10000
This CARL9170_FW_ERROR_WINDOW_MS isn't used anywhere?
> +#define CARL9170_FW_ERROR_THRESHOLD 3
> +
> static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len)
> {
> bool restart = false;
> @@ -54,7 +62,7 @@ static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len)
> if (len > 3) {
> if (memcmp(buf, CARL9170_ERR_MAGIC, 3) == 0) {
> ar->fw.err_counter++;
> - if (ar->fw.err_counter > 3) {
> + if (ar->fw.err_counter >= CARL9170_FW_ERROR_THRESHOLD) {
> then vs >= now. You could update CARL9170_FW_ERROR_THRESHOLD to 4 to match the "previous" behavior.
But no, this is neither here nor there.
> restart = true;
> reason = CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS;
> }
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 10/10] carl9170: phy: add periodic runtime IQ calibration
2026-03-12 10:38 ` [PATCH 10/10] carl9170: phy: add periodic runtime IQ calibration Masi Osmani
@ 2026-03-21 21:25 ` Christian Lamparter
0 siblings, 0 replies; 20+ messages in thread
From: Christian Lamparter @ 2026-03-21 21:25 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
On 3/12/26 11:38 AM, Masi Osmani wrote:
> Add periodic runtime I/Q calibration triggered from the existing
> survey statistics work handler (carl9170_stat_work). The AR9170
> hardware performs initial IQ calibration during channel setup, but
> I/Q imbalance drifts with temperature over time, degrading EVM
> and increasing packet error rate.
>
> The new carl9170_run_iq_calibration() function sets the DO_IQCAL
> bit in PHY_TIMING_CTRL4 for both chains, which triggers the
> hardware to re-measure I/Q imbalance and update the correction
> coefficients automatically. This is a non-blocking operation --
> the hardware runs the calibration in the background without
> interrupting normal traffic.
>
> The ath9k driver performs similar periodic calibration via its
> longcal timer (every 30s). carl9170_stat_work runs at a
> comparable interval, making it a natural trigger point.
This is interesting.
I checked OTUS and unlike the antenna diversity these definitions, the IQCAL are present in
HalPlus/OTUS_FB50/hpani.h:
| #define AR_PHY_TIMING_CTRL4 0x1C5920 /* timing control */
| #define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF 0x01F /* Mask for kcos_theta-1 for q correction */
| #define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF_S 0 /* shift for Q_COFF */
| #define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF 0x7E0 /* Mask for sin_theta for i correction */
| #define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S 5 /* Shift for sin_theta for i correction */
| #define AR_PHY_TIMING_CTRL4_IQCORR_ENABLE 0x800 /* enable IQ correction */
| #define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX 0xF000 /* Mask for max number of samples (logarithmic) */
| #define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S 12 /* Shift for max number of samples */
| #define AR_PHY_TIMING_CTRL4_DO_IQCAL 0x10000 /* perform IQ calibration */
There's not a 100% match with what ath9k ar9002 is doing though. It looks like ar9002 can do more
than just IQCAL (as the matching value for AR_PHY_TIMING_CTRL4_DO_IQCAL is called AR_PHY_TIMING_CTRL4_DO_CAL.
And you need to set a separate AR_PHY_CALMODE to tell it what of the available CALs (IQ, GAIN, DC)
There's a chance that there's a benifit, if this is working.
One question about this though:
In HalPlus/OTUS_FB50/hpani.h:
|
| /* PHY IQ calibration results */
| #define AR_PHY_IQCAL_RES_PWR_MEAS_I 0x1C5C10 /* power measurement for I */
| #define AR_PHY_IQCAL_RES_PWR_MEAS_Q 0x1C5C14 /* power measurement for Q */
| #define AR_PHY_IQCAL_RES_IQ_CORR_MEAS 0x1C5C18 /* IQ correlation measurement */
This makes it look like we need to read back the results after the calibration
and put them into AR_PHY_TIMING_CTRL4's IQCORR_Q_Q and IQCORR_Q_I ... At least that's
what ath9k with the ar9002_calib seems to be doing in ar9002_hw_iqcalibrate().
In other words: Do you have some credible information that just setting
AR9170_PHY_TIMING_CTRL4_DO_IQCAL is enough?
Cheers,
Christian
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/carl9170.h | 1 +
> drivers/net/wireless/ath/carl9170/main.c | 2 ++
> drivers/net/wireless/ath/carl9170/phy.c | 36 ++++++++++++++++++++
> 3 files changed, 39 insertions(+)
>
> diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
> index 2eedb2f..0175f8e 100644
> --- a/drivers/net/wireless/ath/carl9170/carl9170.h
> +++ b/drivers/net/wireless/ath/carl9170/carl9170.h
> @@ -605,6 +605,7 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
> enum nl80211_channel_type bw);
> int carl9170_get_noisefloor(struct ar9170 *ar);
> void carl9170_update_channel_maxpower(struct ar9170 *ar);
> +int carl9170_run_iq_calibration(struct ar9170 *ar);
>
> /* FW */
> int carl9170_parse_firmware(struct ar9170 *ar);
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index ebf9fa9..50c0922 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -910,6 +910,8 @@ static void carl9170_stat_work(struct work_struct *work)
>
> mutex_lock(&ar->mutex);
> err = carl9170_update_survey(ar, false, true);
> + if (!err)
> + carl9170_run_iq_calibration(ar);
> mutex_unlock(&ar->mutex);
>
> if (err)
> diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
> index c294df7..b145e9e 100644
> --- a/drivers/net/wireless/ath/carl9170/phy.c
> +++ b/drivers/net/wireless/ath/carl9170/phy.c
> @@ -1637,6 +1637,42 @@ void carl9170_update_channel_maxpower(struct ar9170 *ar)
> }
> }
>
> +int carl9170_run_iq_calibration(struct ar9170 *ar)
> +{
> + u32 val;
> + int err;
> +
> + if (!ar->channel)
> + return 0;
> +
> + /*
> + * Trigger runtime IQ calibration. The hardware measures
> + * I/Q imbalance and updates the correction coefficients
> + * automatically when DO_IQCAL is set. We trigger on both
> + * chains and re-enable the IQ correction afterwards.
> + */
> + err = carl9170_read_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(0), &val);
> + if (err)
> + return err;
> +
> + val |= AR9170_PHY_TIMING_CTRL4_DO_IQCAL;
> + err = carl9170_write_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(0), val);
> + if (err)
> + return err;
> +
> + /* chain 2 */
> + err = carl9170_read_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(2), &val);
> + if (err)
> + return err;
> +
> + val |= AR9170_PHY_TIMING_CTRL4_DO_IQCAL;
> + err = carl9170_write_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(2), val);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> static int carl9170_set_radar_detection(struct ar9170 *ar,
> struct ieee80211_channel *channel)
> {
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-03-21 21:25 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <cover.1773277728.git.mas-i@hotmail.de>
2026-03-12 10:37 ` [PATCH 01/10] carl9170: mac80211: enable Short Guard Interval for 20 MHz Masi Osmani
2026-03-21 18:41 ` Christian Lamparter
2026-03-12 10:37 ` [PATCH 02/10] carl9170: mac80211: advertise RX STBC capability Masi Osmani
2026-03-21 18:47 ` Christian Lamparter
2026-03-12 10:37 ` [PATCH 03/10] carl9170: mac80211: document spatial multiplexing power save handler Masi Osmani
2026-03-21 18:57 ` Christian Lamparter
2026-03-12 10:37 ` [PATCH 04/10] carl9170: rx: wire up dropped frame counter Masi Osmani
2026-03-21 19:03 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 05/10] carl9170: rx: track PHY errors via debugfs Masi Osmani
2026-03-21 20:29 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 06/10] carl9170: phy: populate per-channel TX power from EEPROM Masi Osmani
2026-03-21 19:24 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 07/10] carl9170: main: add exponential restart backoff Masi Osmani
2026-03-21 20:42 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 08/10] carl9170: phy: enable antenna diversity for 2-chain devices Masi Osmani
2026-03-21 19:53 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 09/10] carl9170: fw: enable DFS radar detection Masi Osmani
2026-03-21 20:11 ` Christian Lamparter
2026-03-12 10:38 ` [PATCH 10/10] carl9170: phy: add periodic runtime IQ calibration Masi Osmani
2026-03-21 21:25 ` Christian Lamparter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox