mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Stanislaw Gruszka <stf_xl@wp.pl>
To: Ho Jie Feng <hjf3108@gmail.com>
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH wireless-next 2/8] wifi: rt2800: calibrate the crystal of RT3593 with the beacons
Date: Sat, 31 Oct 2026 11:52:17 +0100	[thread overview]
Message-ID: <20261031105217.GB31143@wp.pl> (raw)
In-Reply-To: <20261029134752.1299130-3-hjf3108@gmail.com>

On Thu, Oct 29, 2026 at 09:47:43PM +0800, Ho Jie Feng wrote:
> On a Netgear WNDA4100 (RT3593), as the adapter warms up, the
> frequency offset drifts enough to affect the link quality.
> In certain cases it can cause the link speed to drop
> to 1Mbit/s or below.
> 
> Add a frequency calibration step function to RT3593 to use
> the frequency offset obtained from the beacon frames of the AP
> for tuning.

Presumably this is based on vendor driver? Could you point
the sources i.e. a link for some Github repo where the
correspond code is?

Regards
Stanislaw

> Test results:
>    before after
> TX 27.7   28.5
> RX 31.6   69.7
> 
> Signed-off-by: Ho Jie Feng <hjf3108@gmail.com>
> ---
>  drivers/net/wireless/ralink/rt2x00/rt2800.h   |   1 +
>  .../net/wireless/ralink/rt2x00/rt2800lib.c    | 101 ++++++++++++++++++
>  .../net/wireless/ralink/rt2x00/rt2800lib.h    |   4 +
>  .../net/wireless/ralink/rt2x00/rt2800usb.c    |   1 +
>  drivers/net/wireless/ralink/rt2x00/rt2x00.h   |   6 ++
>  .../net/wireless/ralink/rt2x00/rt2x00lib.h    |   2 +
>  .../net/wireless/ralink/rt2x00/rt2x00link.c   |   9 ++
>  7 files changed, 124 insertions(+)
> 
> diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800.h b/drivers/net/wireless/ralink/rt2x00/rt2800.h
> index 8930589b4967d..b9f6de3a462e3 100644
> --- a/drivers/net/wireless/ralink/rt2x00/rt2800.h
> +++ b/drivers/net/wireless/ralink/rt2x00/rt2800.h
> @@ -3163,6 +3163,7 @@ enum rt2800_eeprom_word {
>   */
>  #define RXWI_W3_SNR0			FIELD32(0x000000ff)
>  #define RXWI_W3_SNR1			FIELD32(0x0000ff00)
> +#define RXWI_W3_FOFFSET			FIELD32(0xff000000)
>  
>  /*
>   * Macros for converting txpower from EEPROM to mac80211 value
> diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
> index 2e2e382b62475..e7b6edf485099 100644
> --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
> +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
> @@ -897,6 +897,28 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, u32 rxwi_w2)
>  	return (int)max(rssi0, rssi2);
>  }
>  
> +static void rt2800_rx_freq_offset(struct queue_entry *entry,
> +				  struct rxdone_entry_desc *rxdesc)
> +{
> +	struct rt2800_drv_data *drv_data = entry->queue->rt2x00dev->drv_data;
> +	__le32 *rxwi = (__le32 *) entry->skb->data;
> +	struct ieee80211_hdr *hdr =
> +	    (void *)(entry->skb->data + entry->queue->winfo_size);
> +	u32 word;
> +
> +	if (!(rxdesc->dev_flags & RXDONE_MY_BSS) ||
> +	    (rxdesc->flags & RX_FLAG_FAILED_FCS_CRC) ||
> +	    !ieee80211_is_beacon(hdr->frame_control))
> +		return;
> +
> +	word = rt2x00_desc_read(rxwi, 3);
> +
> +	spin_lock_bh(&drv_data->bcn_lock);
> +	drv_data->bcn_freq_offset = rt2x00_get_field32(word, RXWI_W3_FOFFSET);
> +	drv_data->bcn_rate_mode = rxdesc->rate_mode;
> +	spin_unlock_bh(&drv_data->bcn_lock);
> +}
> +
>  void rt2800_process_rxwi(struct queue_entry *entry,
>  			 struct rxdone_entry_desc *rxdesc)
>  {
> @@ -935,6 +957,14 @@ void rt2800_process_rxwi(struct queue_entry *entry,
>  	 * Convert descriptor AGC value to RSSI value.
>  	 */
>  	rxdesc->rssi = rt2800_agc_to_rssi(entry->queue->rt2x00dev, word);
> +
> +	/*
> +	 * The frequency offset of the beacons is used for the frequency
> +	 * calibration.
> +	 */
> +	if (rt2x00_rt(entry->queue->rt2x00dev, RT3593))
> +		rt2800_rx_freq_offset(entry, rxdesc);
> +
>  	/*
>  	 * Remove RXWI descriptor from start of the buffer.
>  	 */
> @@ -2443,6 +2473,7 @@ static inline bool rt2800_clk_is_20mhz(struct rt2x00_dev *rt2x00dev)
>  }
>  
>  #define FREQ_OFFSET_BOUND	0x5f
> +#define FREQ_OFFSET_INVALID	-128
>  
>  static void rt2800_freq_cal_mode1(struct rt2x00_dev *rt2x00dev)
>  {
> @@ -5633,6 +5664,73 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev)
>  }
>  EXPORT_SYMBOL_GPL(rt2800_vco_calibration);
>  
> +void rt2800_freq_calibration(struct rt2x00_dev *rt2x00dev)
> +{
> +	struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
> +	struct link *link = &rt2x00dev->link;
> +	u8 activate_threshold, deactivate_threshold;
> +	u8 freq_offset, rate_mode;
> +	u16 eeprom;
> +	s8 offset;
> +
> +	if (!rt2x00_rt(rt2x00dev, RT3593))
> +		return;
> +
> +	if (!rt2x00dev->intf_associated) {
> +		eeprom = rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ);
> +		rt2x00dev->freq_offset = rt2x00_get_field16(eeprom,
> +							    EEPROM_FREQ_OFFSET);
> +		link->freq_cal_adjusting = false;
> +		return;
> +	}
> +
> +	spin_lock_bh(&drv_data->bcn_lock);
> +	offset = drv_data->bcn_freq_offset;
> +	rate_mode = drv_data->bcn_rate_mode;
> +	drv_data->bcn_freq_offset = FREQ_OFFSET_INVALID;
> +	spin_unlock_bh(&drv_data->bcn_lock);
> +
> +	/* No beacons received */
> +	if (offset == FREQ_OFFSET_INVALID)
> +		return;
> +
> +	if (rate_mode == RATE_MODE_CCK) {
> +		activate_threshold = 4;
> +		deactivate_threshold = 2;
> +	} else {
> +		activate_threshold = 20;
> +		deactivate_threshold = 10;
> +	}
> +
> +	/* Hysteresis */
> +	if (abs(offset) >= activate_threshold)
> +		link->freq_cal_adjusting = true;
> +	else if (abs(offset) <= deactivate_threshold)
> +		link->freq_cal_adjusting = false;
> +
> +	if (!link->freq_cal_adjusting)
> +		return;
> +
> +	freq_offset = rt2x00_get_field8(rt2x00dev->freq_offset, RFCSR17_CODE);
> +	freq_offset = min_t(u8, freq_offset, FREQ_OFFSET_BOUND);
> +
> +	if (offset > 0) {
> +		if (freq_offset > 0)
> +			freq_offset--;
> +		else
> +			link->freq_cal_adjusting = false;
> +	} else {
> +		if (freq_offset < FREQ_OFFSET_BOUND)
> +			freq_offset++;
> +		else
> +			link->freq_cal_adjusting = false;
> +	}
> +
> +	rt2x00dev->freq_offset = freq_offset;
> +	rt2800_freq_cal_mode1(rt2x00dev);
> +}
> +EXPORT_SYMBOL_GPL(rt2800_freq_calibration);
> +
>  static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev,
>  				      struct rt2x00lib_conf *libconf)
>  {
> @@ -12032,6 +12130,7 @@ static int rt2800_probe_rt(struct rt2x00_dev *rt2x00dev)
>  
>  int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev)
>  {
> +	struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
>  	int retval;
>  	u32 reg;
>  
> @@ -12105,6 +12204,8 @@ int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev)
>  	 */
>  	rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET;
>  
> +	spin_lock_init(&drv_data->bcn_lock);
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(rt2800_probe_hw);
> diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
> index a3c3a751f57e3..9a04a0fba113f 100644
> --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
> +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
> @@ -45,6 +45,9 @@ struct rt2800_drv_data {
>  	unsigned int ampdu_factor_cnt[4];
>  	DECLARE_BITMAP(sta_ids, STA_IDS_SIZE);
>  	struct ieee80211_sta *wcid_to_sta[STA_IDS_SIZE];
> +	spinlock_t bcn_lock; /* protects bcn_freq_offset, bcn_rate_mode */
> +	s8 bcn_freq_offset;
> +	u8 bcn_rate_mode;
>  };
>  
>  struct rt2800_ops {
> @@ -241,6 +244,7 @@ void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
>  		       const u32 count);
>  void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev);
>  void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev);
> +void rt2800_freq_calibration(struct rt2x00_dev *rt2x00dev);
>  
>  int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev);
>  void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev);
> diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
> index 15de3db124298..387c07e2251b7 100644
> --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
> +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
> @@ -693,6 +693,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
>  	.link_tuner		= rt2800_link_tuner,
>  	.gain_calibration	= rt2800_gain_calibration,
>  	.vco_calibration	= rt2800_vco_calibration,
> +	.freq_calibration	= rt2800_freq_calibration,
>  	.watchdog		= rt2800_watchdog,
>  	.start_queue		= rt2800usb_start_queue,
>  	.kick_queue		= rt2x00usb_kick_queue,
> diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
> index 7d313e86d3f20..a80508db32d9c 100644
> --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
> +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
> @@ -319,6 +319,11 @@ struct link {
>  	 */
>  	struct ewma_rssi avg_rssi;
>  
> +	/*
> +	 * Controls interval of frequency calibration
> +	 */
> +	bool freq_cal_adjusting;
> +
>  	/*
>  	 * Work structure for scheduling periodic link tuning.
>  	 */
> @@ -559,6 +564,7 @@ struct rt2x00lib_ops {
>  			    struct link_qual *qual, const u32 count);
>  	void (*gain_calibration) (struct rt2x00_dev *rt2x00dev);
>  	void (*vco_calibration) (struct rt2x00_dev *rt2x00dev);
> +	void (*freq_calibration) (struct rt2x00_dev *rt2x00dev);
>  
>  	/*
>  	 * Data queue handlers.
> diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
> index 2f1385baa7ae9..a6f516ebdb2db 100644
> --- a/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
> +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00lib.h
> @@ -22,6 +22,8 @@
>  #define LINK_TUNE_INTERVAL	round_jiffies_relative(LINK_TUNE_SECONDS * HZ)
>  #define AGC_SECONDS		4
>  #define VCO_SECONDS		10
> +#define FREQ_SECONDS		10
> +#define FREQ_ADJUST_SECONDS	1
>  
>  /*
>   * rt2x00_rate: Per rate device information
> diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00link.c b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
> index fb23d409fba87..2dc4edc12834f 100644
> --- a/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
> +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00link.c
> @@ -343,6 +343,7 @@ static void rt2x00link_tuner(struct work_struct *work)
>  	struct rt2x00_dev *rt2x00dev =
>  	    container_of(work, struct rt2x00_dev, link.work.work);
>  	struct link *link = &rt2x00dev->link;
> +	unsigned int freq_interval;
>  
>  	/*
>  	 * When the radio is shutting down we should
> @@ -362,6 +363,14 @@ static void rt2x00link_tuner(struct work_struct *work)
>  	    (link->count % (AGC_SECONDS / LINK_TUNE_SECONDS)) == 0)
>  		rt2x00dev->ops->lib->gain_calibration(rt2x00dev);
>  
> +	freq_interval = link->freq_cal_adjusting ?
> +			FREQ_ADJUST_SECONDS : FREQ_SECONDS;
> +
> +	/* VCO calibration should be done after frequency calibration */
> +	if (rt2x00dev->ops->lib->freq_calibration &&
> +	    (link->count % (freq_interval / LINK_TUNE_SECONDS)) == 0)
> +		rt2x00dev->ops->lib->freq_calibration(rt2x00dev);
> +
>  	if (rt2x00dev->ops->lib->vco_calibration &&
>  	    rt2x00_has_cap_vco_recalibration(rt2x00dev) &&
>  	    (link->count % (VCO_SECONDS / LINK_TUNE_SECONDS)) == 0)
> -- 
> 2.53.0
> 

  reply	other threads:[~2026-10-31 10:52 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20261029134752.1299130-1-hjf3108@gmail.com>
2026-10-29 13:47 ` [PATCH wireless-next 1/8] wifi: rt2x00: start the link tuner when an interface is added Ho Jie Feng
2026-10-31 10:51   ` Stanislaw Gruszka
2026-10-29 13:47 ` [PATCH wireless-next 2/8] wifi: rt2800: calibrate the crystal of RT3593 with the beacons Ho Jie Feng
2026-10-31 10:52   ` Stanislaw Gruszka [this message]
2026-10-31 16:49     ` Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 3/8] wifi: rt2800: use the 3SS fallback table of RT3883 on RT3593 Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 4/8] wifi: rt2800: report the MCS16 to MCS8 fallback of RT3593 in TX status Ho Jie Feng
2026-10-31 10:54   ` Stanislaw Gruszka
2026-10-29 13:47 ` [PATCH wireless-next 5/8] wifi: rt2800: don't report an A-MPDU length to minstrel_ht Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 6/8] wifi: rt2800: set the mac80211 fast-xmit and aggregation flags Ho Jie Feng
2026-10-30  9:17   ` Stanislaw Gruszka
2026-10-30 11:33     ` Jie Feng
2026-10-31 11:06       ` Stanislaw Gruszka
2026-10-31 17:18         ` Jie Feng
2026-10-31 17:23         ` Johannes Berg
2026-10-29 13:47 ` [PATCH wireless-next 7/8] wifi: rt2800usb: enable RX bulk aggregation Ho Jie Feng
2026-10-29 13:47 ` [PATCH wireless-next 8/8] wifi: rt2x00usb: use a threaded NAPI with GRO for RX Ho Jie Feng

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261031105217.GB31143@wp.pl \
    --to=stf_xl@wp.pl \
    --cc=hjf3108@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®