commit 8b73fb8e29e9ae0458d36cc0dc25e2717587dfd4
Author: John W. Linville <linville@tuxdriver.com>
Date:   Wed Jul 21 16:26:40 2010 -0400

    rtl8180: improve signal reporting for actual rtl8180 hardware
    
    Adapted from Realtek-provided driver...
    
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Tested-by: Pauli Nieminen <suokkos@gmail.com>

diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c
index 31808f9..d8b186a 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c
@@ -103,7 +103,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
 {
 	struct rtl8180_priv *priv = dev->priv;
 	unsigned int count = 32;
-	u8 signal;
+	u8 signal, agc, sq;
 
 	while (count--) {
 		struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx];
@@ -132,12 +132,16 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
 
 			rx_status.antenna = (flags2 >> 15) & 1;
 			rx_status.rate_idx = (flags >> 20) & 0xF;
-			/* TODO: improve signal/rssi reporting for !rtl8185 */
-			signal = (flags2 >> 17) & 0x7F;
-			if (rx_status.rate_idx > 3)
-				signal = 90 - clamp_t(u8, signal, 25, 90);
-			else
-				signal = 95 - clamp_t(u8, signal, 30, 95);
+			agc = (flags2 >> 17) & 0x7F;
+			if (priv->r8185) {
+				if (rx_status.rate_idx > 3)
+					signal = 90 - clamp_t(u8, agc, 25, 90);
+				else
+					signal = 95 - clamp_t(u8, agc, 30, 95);
+			} else {
+				sq = flags2 & 0xff;
+				signal = priv->rf->calc_rssi(agc, sq);
+			}
 			rx_status.signal = signal;
 			rx_status.freq = dev->conf.channel->center_freq;
 			rx_status.band = dev->conf.channel->band;
diff --git a/drivers/net/wireless/rtl818x/rtl8180_grf5101.c b/drivers/net/wireless/rtl818x/rtl8180_grf5101.c
index 947ee55..5cab9df 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_grf5101.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_grf5101.c
@@ -69,6 +69,15 @@ static void grf5101_write_phy_antenna(struct ieee80211_hw *dev, short chan)
 	rtl8180_write_phy(dev, 0x10, ant);
 }
 
+static u8 grf5101_rf_calc_rssi(u8 agc, u8 sq)
+{
+	if (agc > 60)
+		return 65;
+
+	/* TODO(?): just return agc (or agc + 5) to avoid mult / div */
+	return 65 * agc / 60;
+}
+
 static void grf5101_rf_set_channel(struct ieee80211_hw *dev,
 				   struct ieee80211_conf *conf)
 {
@@ -176,5 +185,6 @@ const struct rtl818x_rf_ops grf5101_rf_ops = {
 	.name		= "GCT",
 	.init		= grf5101_rf_init,
 	.stop		= grf5101_rf_stop,
-	.set_chan	= grf5101_rf_set_channel
+	.set_chan	= grf5101_rf_set_channel,
+	.calc_rssi	= grf5101_rf_calc_rssi,
 };
diff --git a/drivers/net/wireless/rtl818x/rtl8180_max2820.c b/drivers/net/wireless/rtl818x/rtl8180_max2820.c
index 6c825fd..16c4655 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_max2820.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_max2820.c
@@ -74,6 +74,22 @@ static void max2820_write_phy_antenna(struct ieee80211_hw *dev, short chan)
 	rtl8180_write_phy(dev, 0x10, ant);
 }
 
+static u8 max2820_rf_calc_rssi(u8 agc, u8 sq)
+{
+	bool odd;
+
+	odd = !!(agc & 1);
+
+	agc >>= 1;
+	if (odd)
+		agc += 76;
+	else
+		agc += 66;
+
+	/* TODO: change addends above to avoid mult / div below */
+	return 65 * agc / 100;
+}
+
 static void max2820_rf_set_channel(struct ieee80211_hw *dev,
 				   struct ieee80211_conf *conf)
 {
@@ -148,5 +164,6 @@ const struct rtl818x_rf_ops max2820_rf_ops = {
 	.name		= "Maxim",
 	.init		= max2820_rf_init,
 	.stop		= max2820_rf_stop,
-	.set_chan	= max2820_rf_set_channel
+	.set_chan	= max2820_rf_set_channel,
+	.calc_rssi	= max2820_rf_calc_rssi,
 };
diff --git a/drivers/net/wireless/rtl818x/rtl8180_sa2400.c b/drivers/net/wireless/rtl818x/rtl8180_sa2400.c
index cea4e0c..d064fcc 100644
--- a/drivers/net/wireless/rtl818x/rtl8180_sa2400.c
+++ b/drivers/net/wireless/rtl818x/rtl8180_sa2400.c
@@ -76,6 +76,31 @@ static void sa2400_write_phy_antenna(struct ieee80211_hw *dev, short chan)
 
 }
 
+static u8 sa2400_rf_rssi_map[] = {
+	0x64, 0x64, 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e,
+	0x5d, 0x5c, 0x5b, 0x5a, 0x57, 0x54, 0x52, 0x50,
+	0x4e, 0x4c, 0x4a, 0x48, 0x46, 0x44, 0x41, 0x3f,
+	0x3c, 0x3a, 0x37, 0x36, 0x36, 0x1c, 0x1c, 0x1b,
+	0x1b, 0x1a, 0x1a, 0x19, 0x19, 0x18, 0x18, 0x17,
+	0x17, 0x16, 0x16, 0x15, 0x15, 0x14, 0x14, 0x13,
+	0x13, 0x12, 0x12, 0x11, 0x11, 0x10, 0x10, 0x0f,
+	0x0f, 0x0e, 0x0e, 0x0d, 0x0d, 0x0c, 0x0c, 0x0b,
+	0x0b, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x07,
+	0x07, 0x06, 0x06, 0x05, 0x04, 0x03, 0x02,
+};
+
+static u8 sa2400_rf_calc_rssi(u8 agc, u8 sq)
+{
+	if (sq == 0x80)
+		return 1;
+
+	if (sq > 78)
+		return 32;
+
+	/* TODO: recalc sa2400_rf_rssi_map to avoid mult / div */
+	return 65 * sa2400_rf_rssi_map[sq] / 100;
+}
+
 static void sa2400_rf_set_channel(struct ieee80211_hw *dev,
 				  struct ieee80211_conf *conf)
 {
@@ -198,5 +223,6 @@ const struct rtl818x_rf_ops sa2400_rf_ops = {
 	.name		= "Philips",
 	.init		= sa2400_rf_init,
 	.stop		= sa2400_rf_stop,
-	.set_chan	= sa2400_rf_set_channel
+	.set_chan	= sa2400_rf_set_channel,
+	.calc_rssi	= sa2400_rf_calc_rssi,
 };
diff --git a/drivers/net/wireless/rtl818x/rtl818x.h b/drivers/net/wireless/rtl818x/rtl818x.h
index 8522490..22d9384 100644
--- a/drivers/net/wireless/rtl818x/rtl818x.h
+++ b/drivers/net/wireless/rtl818x/rtl818x.h
@@ -193,6 +193,7 @@ struct rtl818x_rf_ops {
 	void (*stop)(struct ieee80211_hw *);
 	void (*set_chan)(struct ieee80211_hw *, struct ieee80211_conf *);
 	void (*conf_erp)(struct ieee80211_hw *, struct ieee80211_bss_conf *);
+	u8 (*calc_rssi)(u8 agc, u8 sq);
 };
 
 /**
