Subject: [PATCH 13/32] rt2x00: Tune link depending on link quality

From: Ivo van Doorn <[email-masked]>
Date: Fri, 28 Apr 2006 00:03:04 +0200

From: Ivo van Doorn <[email-masked]>

Add link tuning capabilities, and call this function
every time the rxdone handler has finished.

Signed-off-by: Ivo van Doorn <[email-masked]>

diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2400pci.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2400pci.c	2006-04-27 21:42:29.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2400pci.c	2006-04-27 21:45:08.000000000 +0200
@@ -618,6 +618,41 @@ rt2400pci_config_rate(struct rt2x00_pci 
 }
 
 /*
+ * Link tuning
+ */
+static void
+rt2400pci_link_tuner(struct rt2x00_pci *rt2x00pci)
+{
+	u8			reg;
+	char			false_cca_delta;
+
+	/*
+	 * Read false CCA counter.
+	 */
+	rt2x00_bbp_read(rt2x00pci, 39, &reg);
+
+	/*
+	 * Determine difference with previous CCA counter.
+	 */
+	false_cca_delta = reg - rt2x00pci->false_cca;
+	rt2x00pci->false_cca = reg;
+
+	/*
+	 * Check if the difference is higher than the
+	 * threshold and if so, tune the link.
+	 */
+	if (false_cca_delta >= 8) {
+		/*
+		 * Read and update RX AGC VGC.
+		 */
+		rt2x00_bbp_read(rt2x00pci, 13, &reg);
+		reg += 2;
+		if (reg < 0x20)
+			rt2x00_bbp_write(rt2x00pci, 13, reg);
+	}
+}
+
+/*
  * TX descriptor initialization
  */
 static void
@@ -779,6 +814,9 @@ rt2400pci_rxdone(void *data)
 
 			memcpy(skb_put(skb, size), entry->data_addr, size);
 
+			ring->params.rx.ssi =
+				rt2x00_get_field32(rxd->word2, RXD_W2_RSSI);
+
 			__ieee80211_rx(net_dev, skb, &ring->params.rx);
 		}
 
@@ -786,6 +824,11 @@ rt2400pci_rxdone(void *data)
 
 		rt2x00_ring_index_inc(&rt2x00pci->rx);
 	}
+
+	/*
+	 * Tune link for optimal performance.
+	 */
+	rt2400pci_link_tuner(rt2x00pci);
 }
 
 static void
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2400pci.h wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2400pci.h
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2400pci.h	2006-04-27 21:36:19.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2400pci.h	2006-04-27 21:45:08.000000000 +0200
@@ -926,6 +926,11 @@ struct rt2x00_pci{
 	struct workqueue_struct		*workqueue;
 
 	/*
+	 * False CCA count.
+	 */
+	int					false_cca;
+
+	/*
 	 * EEPROM bus width.
 	 */
 	u8					eeprom_width;
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500pci.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500pci.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500pci.c	2006-04-27 21:42:29.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500pci.c	2006-04-27 21:45:08.000000000 +0200
@@ -661,6 +661,51 @@ rt2500pci_config_rate(struct rt2x00_pci 
 }
 
 /*
+ * Link tuning
+ */
+static void
+rt2500pci_link_tuner(struct rt2x00_pci *rt2x00pci, char rssi)
+{
+	u32			reg;
+	u8			reg_r17;
+
+	/*
+	 * Don't perform any tuning during scan.
+	 */
+	if (rt2x00pci->scan)
+		return;
+
+	rt2x00_register_read(rt2x00pci, CSR0, &reg);
+	rt2x00_bbp_read(rt2x00pci, 17, &reg_r17);
+
+	if (reg < RT2560_VERSION_D)
+		goto dynamic_cca_tune;
+
+	if (rssi < 40) {
+		if (reg_r17 >= 0x41)
+			rt2x00_bbp_write(rt2x00pci, 17, reg_r17);
+		return;
+	} else if (rssi >= 62) {
+		if (reg_r17 != 0x50)
+			rt2x00_bbp_write(rt2x00pci, 17, 0x50);
+		return;
+	} else if (reg_r17 >= 0x41) {
+		rt2x00_bbp_write(rt2x00pci, 17, reg_r17);
+		return;
+	}
+
+dynamic_cca_tune:
+	rt2x00_register_read(rt2x00pci, CNT3, &reg);
+
+	reg = rt2x00_get_field32(reg, CNT3_FALSE_CCA);
+
+	if (reg > 512 && reg_r17 < 0x40)
+		rt2x00_bbp_write(rt2x00pci, 17, ++reg_r17);
+	else if (reg < 100 && reg_r17 > 0x32)
+		rt2x00_bbp_write(rt2x00pci, 17, --reg_r17);
+}
+
+/*
  * TX descriptor initialization
  */
 static void
@@ -819,6 +864,14 @@ rt2500pci_rxdone(void *data)
 	struct sk_buff		*skb;
 	struct rxd		*rxd;
 	u16			size;
+	u8			rssi_count;
+	char			total_rssi;
+
+	/*
+	 * Initialize variable for average RSSI calculation.
+	 */
+	rssi_count = 0;
+	total_rssi = 0;
 
 	while (1) {
 		entry = rt2x00_get_data_entry(ring);
@@ -843,12 +896,24 @@ rt2500pci_rxdone(void *data)
 
 			memcpy(skb_put(skb, size), entry->data_addr, size);
 
+			ring->params.rx.ssi =
+				rt2x00_get_field32(rxd->word2, RXD_W2_RSSI);
+
 			__ieee80211_rx(net_dev, skb, &ring->params.rx);
+
+			rssi_count++;
+			total_rssi += ring->params.rx.ssi;
 		}
 		rt2x00_set_field32(&rxd->word0, RXD_W0_OWNER_NIC, 1);
 
 		rt2x00_ring_index_inc(&rt2x00pci->rx);
 	}
+
+	/*
+	 * Tune link for optimal performance.
+	 */
+	if (total_rssi && rssi_count)
+		rt2500pci_link_tuner(rt2x00pci, total_rssi / rssi_count);
 }
 
 static void
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500usb.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500usb.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500usb.c	2006-04-27 21:44:33.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500usb.c	2006-04-27 21:45:08.000000000 +0200
@@ -485,6 +485,68 @@ rt2500usb_config_rate(struct rt2x00_usb 
 }
 
 /*
+ * Link tuning
+ */
+static void
+rt2500usb_link_tuner(struct rt2x00_usb *rt2x00usb, char rssi)
+{
+	u16			reg;
+	u8			reg_r17;
+	u8			up_bound;
+	u8			low_bound;
+
+	/*
+	 * Don't perform any tuning during scan.
+	 */
+	if (rt2x00usb->scan)
+		return;
+
+	low_bound = 0x32;
+	if (rssi >= 43)
+		up_bound = 0x40;
+	else
+		up_bound = 0x40 - (43 - rssi);
+	if (up_bound < low_bound)
+		up_bound = low_bound;
+
+	if (rssi > 75) {
+		rt2x00_bbp_write(rt2x00usb, 24, 0x70);
+		rt2x00_bbp_write(rt2x00usb, 25, 0x40);
+		rt2x00_bbp_write(rt2x00usb, 61, 0x6d);
+	} else {
+		rt2x00_bbp_write(rt2x00usb, 24, 0x80);
+		rt2x00_bbp_write(rt2x00usb, 25, 0x50);
+		rt2x00_bbp_write(rt2x00usb, 61, 0x60);
+	}
+
+	rt2x00_bbp_read(rt2x00usb, 17, &reg_r17);
+
+	if (rssi > 80) {
+		if (reg_r17 != 0x60)
+			rt2x00_bbp_write(rt2x00usb, 17, 0x60);
+		return;
+	} else if (rssi >= 62) {
+		if (reg_r17 != 0x48)
+			rt2x00_bbp_write(rt2x00usb, 17, 0x48);
+		return;
+	} else if (rssi >= 46) {
+		if (reg_r17 != 0x41)
+			rt2x00_bbp_write(rt2x00usb, 17, 0x41);
+		return;
+	} else if (reg_r17 > up_bound) {
+		rt2x00_bbp_write(rt2x00usb, 17, up_bound);
+		return;
+	}
+
+	rt2x00_register_read(rt2x00usb, STA_CSR3, &reg);
+
+	if (reg > 512 && reg_r17 < up_bound)
+		rt2x00_bbp_write(rt2x00usb, 17, ++reg_r17);
+	else if (reg < 100 && reg_r17 > low_bound)
+		rt2x00_bbp_write(rt2x00usb, 17, --reg_r17);
+}
+
+/*
  * TX descriptor initialization
  */
 static void
@@ -636,6 +698,14 @@ rt2500usb_rxdone(void *data)
 	struct sk_buff		*skb;
 	struct rxd		*rxd;
 	u16			size;
+	u8			rssi_count;
+	char			total_rssi;
+
+	/*
+	 * Initialize variable for average RSSI calculation.
+	 */
+	rssi_count = 0;
+	total_rssi = 0;
 
 	while (1) {
 		entry = rt2x00_get_data_entry(ring);
@@ -670,13 +740,25 @@ rt2500usb_rxdone(void *data)
 
 			memcpy(skb_put(skb, size), entry->data_addr, size);
 
+			ring->params.rx.ssi =
+				rt2x00_get_field32(rxd->word1, RXD_W1_RSSI);
+
 			__ieee80211_rx(net_dev, skb, &ring->params.rx);
+
+			rssi_count++;
+			total_rssi += ring->params.rx.ssi;
 		}
 
 		usb_submit_urb(entry->urb, GFP_ATOMIC);
 
 		rt2x00_ring_index_inc(ring);
 	}
+
+	/*
+	 * Tune link for optimal performance.
+	 */
+	if (total_rssi && rssi_count)
+		rt2500usb_link_tuner(rt2x00usb, total_rssi / rssi_count);
 }
 
 static void