aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHelmut Schaa <helmut.schaa@googlemail.com>2011-03-28 07:33:40 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-04-04 16:20:03 -0400
commit9e33a3553821418b2c4f53d09311476c55176b13 (patch)
treefa0538ca1775be5b598949b2418721268e0ece53
parent2f2bb7e8bdc977c94cdaaf84328526555eba89b1 (diff)
rt2x00: Implement tx power temperature compensation
rt2800 devices should adjust their tx power in accordance with the eeproms temperature calibration values. Add a new driver callback gain_calibration that is called every 4 seconds. The rt2800 gain calibration routine simply runs the tx power configuration that takes care of calculating the temperature compensation delta. We don't need to synchronize the calls to rt2800_config_txpower as they should all happen from mac80211's single threaded workqueue. Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com> Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/rt2x00/rt2800.h106
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c133
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.h1
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c1
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c1
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h6
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c2
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00lib.h13
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00link.c38
9 files changed, 296 insertions, 5 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index 70b9abbdeb9..3b3d851fe26 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -2104,6 +2104,59 @@ struct mac_iveiv_entry {
2104#define EEPROM_TXPOWER_BG_2 FIELD16(0xff00) 2104#define EEPROM_TXPOWER_BG_2 FIELD16(0xff00)
2105 2105
2106/* 2106/*
2107 * EEPROM temperature compensation boundaries 802.11BG
2108 * MINUS4: If the actual TSSI is below this boundary, tx power needs to be
2109 * reduced by (agc_step * -4)
2110 * MINUS3: If the actual TSSI is below this boundary, tx power needs to be
2111 * reduced by (agc_step * -3)
2112 */
2113#define EEPROM_TSSI_BOUND_BG1 0x0037
2114#define EEPROM_TSSI_BOUND_BG1_MINUS4 FIELD16(0x00ff)
2115#define EEPROM_TSSI_BOUND_BG1_MINUS3 FIELD16(0xff00)
2116
2117/*
2118 * EEPROM temperature compensation boundaries 802.11BG
2119 * MINUS2: If the actual TSSI is below this boundary, tx power needs to be
2120 * reduced by (agc_step * -2)
2121 * MINUS1: If the actual TSSI is below this boundary, tx power needs to be
2122 * reduced by (agc_step * -1)
2123 */
2124#define EEPROM_TSSI_BOUND_BG2 0x0038
2125#define EEPROM_TSSI_BOUND_BG2_MINUS2 FIELD16(0x00ff)
2126#define EEPROM_TSSI_BOUND_BG2_MINUS1 FIELD16(0xff00)
2127
2128/*
2129 * EEPROM temperature compensation boundaries 802.11BG
2130 * REF: Reference TSSI value, no tx power changes needed
2131 * PLUS1: If the actual TSSI is above this boundary, tx power needs to be
2132 * increased by (agc_step * 1)
2133 */
2134#define EEPROM_TSSI_BOUND_BG3 0x0039
2135#define EEPROM_TSSI_BOUND_BG3_REF FIELD16(0x00ff)
2136#define EEPROM_TSSI_BOUND_BG3_PLUS1 FIELD16(0xff00)
2137
2138/*
2139 * EEPROM temperature compensation boundaries 802.11BG
2140 * PLUS2: If the actual TSSI is above this boundary, tx power needs to be
2141 * increased by (agc_step * 2)
2142 * PLUS3: If the actual TSSI is above this boundary, tx power needs to be
2143 * increased by (agc_step * 3)
2144 */
2145#define EEPROM_TSSI_BOUND_BG4 0x003a
2146#define EEPROM_TSSI_BOUND_BG4_PLUS2 FIELD16(0x00ff)
2147#define EEPROM_TSSI_BOUND_BG4_PLUS3 FIELD16(0xff00)
2148
2149/*
2150 * EEPROM temperature compensation boundaries 802.11BG
2151 * PLUS4: If the actual TSSI is above this boundary, tx power needs to be
2152 * increased by (agc_step * 4)
2153 * AGC_STEP: Temperature compensation step.
2154 */
2155#define EEPROM_TSSI_BOUND_BG5 0x003b
2156#define EEPROM_TSSI_BOUND_BG5_PLUS4 FIELD16(0x00ff)
2157#define EEPROM_TSSI_BOUND_BG5_AGC_STEP FIELD16(0xff00)
2158
2159/*
2107 * EEPROM TXPOWER 802.11A 2160 * EEPROM TXPOWER 802.11A
2108 */ 2161 */
2109#define EEPROM_TXPOWER_A1 0x003c 2162#define EEPROM_TXPOWER_A1 0x003c
@@ -2113,6 +2166,59 @@ struct mac_iveiv_entry {
2113#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) 2166#define EEPROM_TXPOWER_A_2 FIELD16(0xff00)
2114 2167
2115/* 2168/*
2169 * EEPROM temperature compensation boundaries 802.11A
2170 * MINUS4: If the actual TSSI is below this boundary, tx power needs to be
2171 * reduced by (agc_step * -4)
2172 * MINUS3: If the actual TSSI is below this boundary, tx power needs to be
2173 * reduced by (agc_step * -3)
2174 */
2175#define EEPROM_TSSI_BOUND_A1 0x006a
2176#define EEPROM_TSSI_BOUND_A1_MINUS4 FIELD16(0x00ff)
2177#define EEPROM_TSSI_BOUND_A1_MINUS3 FIELD16(0xff00)
2178
2179/*
2180 * EEPROM temperature compensation boundaries 802.11A
2181 * MINUS2: If the actual TSSI is below this boundary, tx power needs to be
2182 * reduced by (agc_step * -2)
2183 * MINUS1: If the actual TSSI is below this boundary, tx power needs to be
2184 * reduced by (agc_step * -1)
2185 */
2186#define EEPROM_TSSI_BOUND_A2 0x006b
2187#define EEPROM_TSSI_BOUND_A2_MINUS2 FIELD16(0x00ff)
2188#define EEPROM_TSSI_BOUND_A2_MINUS1 FIELD16(0xff00)
2189
2190/*
2191 * EEPROM temperature compensation boundaries 802.11A
2192 * REF: Reference TSSI value, no tx power changes needed
2193 * PLUS1: If the actual TSSI is above this boundary, tx power needs to be
2194 * increased by (agc_step * 1)
2195 */
2196#define EEPROM_TSSI_BOUND_A3 0x006c
2197#define EEPROM_TSSI_BOUND_A3_REF FIELD16(0x00ff)
2198#define EEPROM_TSSI_BOUND_A3_PLUS1 FIELD16(0xff00)
2199
2200/*
2201 * EEPROM temperature compensation boundaries 802.11A
2202 * PLUS2: If the actual TSSI is above this boundary, tx power needs to be
2203 * increased by (agc_step * 2)
2204 * PLUS3: If the actual TSSI is above this boundary, tx power needs to be
2205 * increased by (agc_step * 3)
2206 */
2207#define EEPROM_TSSI_BOUND_A4 0x006d
2208#define EEPROM_TSSI_BOUND_A4_PLUS2 FIELD16(0x00ff)
2209#define EEPROM_TSSI_BOUND_A4_PLUS3 FIELD16(0xff00)
2210
2211/*
2212 * EEPROM temperature compensation boundaries 802.11A
2213 * PLUS4: If the actual TSSI is above this boundary, tx power needs to be
2214 * increased by (agc_step * 4)
2215 * AGC_STEP: Temperature compensation step.
2216 */
2217#define EEPROM_TSSI_BOUND_A5 0x006e
2218#define EEPROM_TSSI_BOUND_A5_PLUS4 FIELD16(0x00ff)
2219#define EEPROM_TSSI_BOUND_A5_AGC_STEP FIELD16(0xff00)
2220
2221/*
2116 * EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode 2222 * EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode
2117 */ 2223 */
2118#define EEPROM_TXPOWER_BYRATE 0x006f 2224#define EEPROM_TXPOWER_BYRATE 0x006f
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 02651334937..59302faec39 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -1813,6 +1813,116 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
1813 rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, &reg); 1813 rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, &reg);
1814} 1814}
1815 1815
1816static int rt2800_get_gain_calibration_delta(struct rt2x00_dev *rt2x00dev)
1817{
1818 u8 tssi_bounds[9];
1819 u8 current_tssi;
1820 u16 eeprom;
1821 u8 step;
1822 int i;
1823
1824 /*
1825 * Read TSSI boundaries for temperature compensation from
1826 * the EEPROM.
1827 *
1828 * Array idx 0 1 2 3 4 5 6 7 8
1829 * Matching Delta value -4 -3 -2 -1 0 +1 +2 +3 +4
1830 * Example TSSI bounds 0xF0 0xD0 0xB5 0xA0 0x88 0x45 0x25 0x15 0x00
1831 */
1832 if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
1833 rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom);
1834 tssi_bounds[0] = rt2x00_get_field16(eeprom,
1835 EEPROM_TSSI_BOUND_BG1_MINUS4);
1836 tssi_bounds[1] = rt2x00_get_field16(eeprom,
1837 EEPROM_TSSI_BOUND_BG1_MINUS3);
1838
1839 rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom);
1840 tssi_bounds[2] = rt2x00_get_field16(eeprom,
1841 EEPROM_TSSI_BOUND_BG2_MINUS2);
1842 tssi_bounds[3] = rt2x00_get_field16(eeprom,
1843 EEPROM_TSSI_BOUND_BG2_MINUS1);
1844
1845 rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom);
1846 tssi_bounds[4] = rt2x00_get_field16(eeprom,
1847 EEPROM_TSSI_BOUND_BG3_REF);
1848 tssi_bounds[5] = rt2x00_get_field16(eeprom,
1849 EEPROM_TSSI_BOUND_BG3_PLUS1);
1850
1851 rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom);
1852 tssi_bounds[6] = rt2x00_get_field16(eeprom,
1853 EEPROM_TSSI_BOUND_BG4_PLUS2);
1854 tssi_bounds[7] = rt2x00_get_field16(eeprom,
1855 EEPROM_TSSI_BOUND_BG4_PLUS3);
1856
1857 rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom);
1858 tssi_bounds[8] = rt2x00_get_field16(eeprom,
1859 EEPROM_TSSI_BOUND_BG5_PLUS4);
1860
1861 step = rt2x00_get_field16(eeprom,
1862 EEPROM_TSSI_BOUND_BG5_AGC_STEP);
1863 } else {
1864 rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom);
1865 tssi_bounds[0] = rt2x00_get_field16(eeprom,
1866 EEPROM_TSSI_BOUND_A1_MINUS4);
1867 tssi_bounds[1] = rt2x00_get_field16(eeprom,
1868 EEPROM_TSSI_BOUND_A1_MINUS3);
1869
1870 rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom);
1871 tssi_bounds[2] = rt2x00_get_field16(eeprom,
1872 EEPROM_TSSI_BOUND_A2_MINUS2);
1873 tssi_bounds[3] = rt2x00_get_field16(eeprom,
1874 EEPROM_TSSI_BOUND_A2_MINUS1);
1875
1876 rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom);
1877 tssi_bounds[4] = rt2x00_get_field16(eeprom,
1878 EEPROM_TSSI_BOUND_A3_REF);
1879 tssi_bounds[5] = rt2x00_get_field16(eeprom,
1880 EEPROM_TSSI_BOUND_A3_PLUS1);
1881
1882 rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom);
1883 tssi_bounds[6] = rt2x00_get_field16(eeprom,
1884 EEPROM_TSSI_BOUND_A4_PLUS2);
1885 tssi_bounds[7] = rt2x00_get_field16(eeprom,
1886 EEPROM_TSSI_BOUND_A4_PLUS3);
1887
1888 rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom);
1889 tssi_bounds[8] = rt2x00_get_field16(eeprom,
1890 EEPROM_TSSI_BOUND_A5_PLUS4);
1891
1892 step = rt2x00_get_field16(eeprom,
1893 EEPROM_TSSI_BOUND_A5_AGC_STEP);
1894 }
1895
1896 /*
1897 * Check if temperature compensation is supported.
1898 */
1899 if (tssi_bounds[4] == 0xff)
1900 return 0;
1901
1902 /*
1903 * Read current TSSI (BBP 49).
1904 */
1905 rt2800_bbp_read(rt2x00dev, 49, &current_tssi);
1906
1907 /*
1908 * Compare TSSI value (BBP49) with the compensation boundaries
1909 * from the EEPROM and increase or decrease tx power.
1910 */
1911 for (i = 0; i <= 3; i++) {
1912 if (current_tssi > tssi_bounds[i])
1913 break;
1914 }
1915
1916 if (i == 4) {
1917 for (i = 8; i >= 5; i--) {
1918 if (current_tssi < tssi_bounds[i])
1919 break;
1920 }
1921 }
1922
1923 return (i - 4) * step;
1924}
1925
1816static int rt2800_get_txpower_bw_comp(struct rt2x00_dev *rt2x00dev, 1926static int rt2800_get_txpower_bw_comp(struct rt2x00_dev *rt2x00dev,
1817 enum ieee80211_band band) 1927 enum ieee80211_band band)
1818{ 1928{
@@ -1904,7 +2014,8 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
1904} 2014}
1905 2015
1906static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, 2016static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
1907 struct ieee80211_conf *conf) 2017 enum ieee80211_band band,
2018 int power_level)
1908{ 2019{
1909 u8 txpower; 2020 u8 txpower;
1910 u16 eeprom; 2021 u16 eeprom;
@@ -1912,8 +2023,6 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
1912 u32 reg; 2023 u32 reg;
1913 u8 r1; 2024 u8 r1;
1914 u32 offset; 2025 u32 offset;
1915 enum ieee80211_band band = conf->channel->band;
1916 int power_level = conf->power_level;
1917 int delta; 2026 int delta;
1918 2027
1919 /* 2028 /*
@@ -1922,6 +2031,11 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
1922 delta = rt2800_get_txpower_bw_comp(rt2x00dev, band); 2031 delta = rt2800_get_txpower_bw_comp(rt2x00dev, band);
1923 2032
1924 /* 2033 /*
2034 * calculate temperature compensation delta
2035 */
2036 delta += rt2800_get_gain_calibration_delta(rt2x00dev);
2037
2038 /*
1925 * set to normal bbp tx power control mode: +/- 0dBm 2039 * set to normal bbp tx power control mode: +/- 0dBm
1926 */ 2040 */
1927 rt2800_bbp_read(rt2x00dev, 1, &r1); 2041 rt2800_bbp_read(rt2x00dev, 1, &r1);
@@ -2041,6 +2155,13 @@ static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
2041 } 2155 }
2042} 2156}
2043 2157
2158void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev)
2159{
2160 rt2800_config_txpower(rt2x00dev, rt2x00dev->curr_band,
2161 rt2x00dev->tx_power);
2162}
2163EXPORT_SYMBOL_GPL(rt2800_gain_calibration);
2164
2044static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev, 2165static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev,
2045 struct rt2x00lib_conf *libconf) 2166 struct rt2x00lib_conf *libconf)
2046{ 2167{
@@ -2094,10 +2215,12 @@ void rt2800_config(struct rt2x00_dev *rt2x00dev,
2094 if (flags & IEEE80211_CONF_CHANGE_CHANNEL) { 2215 if (flags & IEEE80211_CONF_CHANGE_CHANNEL) {
2095 rt2800_config_channel(rt2x00dev, libconf->conf, 2216 rt2800_config_channel(rt2x00dev, libconf->conf,
2096 &libconf->rf, &libconf->channel); 2217 &libconf->rf, &libconf->channel);
2097 rt2800_config_txpower(rt2x00dev, libconf->conf); 2218 rt2800_config_txpower(rt2x00dev, libconf->conf->channel->band,
2219 libconf->conf->power_level);
2098 } 2220 }
2099 if (flags & IEEE80211_CONF_CHANGE_POWER) 2221 if (flags & IEEE80211_CONF_CHANGE_POWER)
2100 rt2800_config_txpower(rt2x00dev, libconf->conf); 2222 rt2800_config_txpower(rt2x00dev, libconf->conf->channel->band,
2223 libconf->conf->power_level);
2101 if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS) 2224 if (flags & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
2102 rt2800_config_retry_limit(rt2x00dev, libconf); 2225 rt2800_config_retry_limit(rt2x00dev, libconf);
2103 if (flags & IEEE80211_CONF_CHANGE_PS) 2226 if (flags & IEEE80211_CONF_CHANGE_PS)
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h
index 0c92d86a36f..f2d15941c71 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/rt2x00/rt2800lib.h
@@ -181,6 +181,7 @@ void rt2800_link_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual);
181void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual); 181void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual);
182void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, 182void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
183 const u32 count); 183 const u32 count);
184void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev);
184 185
185int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev); 186int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev);
186void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev); 187void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index d3055147ddf..adc3534254d 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -1053,6 +1053,7 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
1053 .link_stats = rt2800_link_stats, 1053 .link_stats = rt2800_link_stats,
1054 .reset_tuner = rt2800_reset_tuner, 1054 .reset_tuner = rt2800_reset_tuner,
1055 .link_tuner = rt2800_link_tuner, 1055 .link_tuner = rt2800_link_tuner,
1056 .gain_calibration = rt2800_gain_calibration,
1056 .start_queue = rt2800pci_start_queue, 1057 .start_queue = rt2800pci_start_queue,
1057 .kick_queue = rt2800pci_kick_queue, 1058 .kick_queue = rt2800pci_kick_queue,
1058 .stop_queue = rt2800pci_stop_queue, 1059 .stop_queue = rt2800pci_stop_queue,
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 1c99a4f449f..8b3ab3f17eb 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -629,6 +629,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
629 .link_stats = rt2800_link_stats, 629 .link_stats = rt2800_link_stats,
630 .reset_tuner = rt2800_reset_tuner, 630 .reset_tuner = rt2800_reset_tuner,
631 .link_tuner = rt2800_link_tuner, 631 .link_tuner = rt2800_link_tuner,
632 .gain_calibration = rt2800_gain_calibration,
632 .watchdog = rt2800usb_watchdog, 633 .watchdog = rt2800usb_watchdog,
633 .start_queue = rt2800usb_start_queue, 634 .start_queue = rt2800usb_start_queue,
634 .kick_queue = rt2x00usb_kick_queue, 635 .kick_queue = rt2x00usb_kick_queue,
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 60b1cb05a70..dd0f66ade6e 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -348,6 +348,11 @@ struct link {
348 * to bring the device/driver back into the desired state. 348 * to bring the device/driver back into the desired state.
349 */ 349 */
350 struct delayed_work watchdog_work; 350 struct delayed_work watchdog_work;
351
352 /*
353 * Work structure for scheduling periodic AGC adjustments.
354 */
355 struct delayed_work agc_work;
351}; 356};
352 357
353enum rt2x00_delayed_flags { 358enum rt2x00_delayed_flags {
@@ -556,6 +561,7 @@ struct rt2x00lib_ops {
556 struct link_qual *qual); 561 struct link_qual *qual);
557 void (*link_tuner) (struct rt2x00_dev *rt2x00dev, 562 void (*link_tuner) (struct rt2x00_dev *rt2x00dev,
558 struct link_qual *qual, const u32 count); 563 struct link_qual *qual, const u32 count);
564 void (*gain_calibration) (struct rt2x00_dev *rt2x00dev);
559 565
560 /* 566 /*
561 * Data queue handlers. 567 * Data queue handlers.
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 55c1d0332b2..a98c4348523 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -71,6 +71,7 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
71 */ 71 */
72 rt2x00queue_start_queues(rt2x00dev); 72 rt2x00queue_start_queues(rt2x00dev);
73 rt2x00link_start_tuner(rt2x00dev); 73 rt2x00link_start_tuner(rt2x00dev);
74 rt2x00link_start_agc(rt2x00dev);
74 75
75 /* 76 /*
76 * Start watchdog monitoring. 77 * Start watchdog monitoring.
@@ -93,6 +94,7 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
93 /* 94 /*
94 * Stop all queues 95 * Stop all queues
95 */ 96 */
97 rt2x00link_stop_agc(rt2x00dev);
96 rt2x00link_stop_tuner(rt2x00dev); 98 rt2x00link_stop_tuner(rt2x00dev);
97 rt2x00queue_stop_queues(rt2x00dev); 99 rt2x00queue_stop_queues(rt2x00dev);
98 rt2x00queue_flush_queues(rt2x00dev, true); 100 rt2x00queue_flush_queues(rt2x00dev, true);
diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h
index 63c40d45724..88f2f927552 100644
--- a/drivers/net/wireless/rt2x00/rt2x00lib.h
+++ b/drivers/net/wireless/rt2x00/rt2x00lib.h
@@ -32,6 +32,7 @@
32 */ 32 */
33#define WATCHDOG_INTERVAL round_jiffies_relative(HZ) 33#define WATCHDOG_INTERVAL round_jiffies_relative(HZ)
34#define LINK_TUNE_INTERVAL round_jiffies_relative(HZ) 34#define LINK_TUNE_INTERVAL round_jiffies_relative(HZ)
35#define AGC_INTERVAL round_jiffies_relative(4 * HZ)
35 36
36/* 37/*
37 * rt2x00_rate: Per rate device information 38 * rt2x00_rate: Per rate device information
@@ -271,6 +272,18 @@ void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev);
271void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev); 272void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev);
272 273
273/** 274/**
275 * rt2x00link_start_agc - Start periodic gain calibration
276 * @rt2x00dev: Pointer to &struct rt2x00_dev.
277 */
278void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev);
279
280/**
281 * rt2x00link_stop_agc - Stop periodic gain calibration
282 * @rt2x00dev: Pointer to &struct rt2x00_dev.
283 */
284void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev);
285
286/**
274 * rt2x00link_register - Initialize link tuning & watchdog functionality 287 * rt2x00link_register - Initialize link tuning & watchdog functionality
275 * @rt2x00dev: Pointer to &struct rt2x00_dev. 288 * @rt2x00dev: Pointer to &struct rt2x00_dev.
276 * 289 *
diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c
index fc8cee91b54..128b3615c08 100644
--- a/drivers/net/wireless/rt2x00/rt2x00link.c
+++ b/drivers/net/wireless/rt2x00/rt2x00link.c
@@ -446,8 +446,46 @@ static void rt2x00link_watchdog(struct work_struct *work)
446 WATCHDOG_INTERVAL); 446 WATCHDOG_INTERVAL);
447} 447}
448 448
449void rt2x00link_start_agc(struct rt2x00_dev *rt2x00dev)
450{
451 struct link *link = &rt2x00dev->link;
452
453 if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
454 rt2x00dev->ops->lib->gain_calibration)
455 ieee80211_queue_delayed_work(rt2x00dev->hw,
456 &link->agc_work,
457 AGC_INTERVAL);
458}
459
460void rt2x00link_stop_agc(struct rt2x00_dev *rt2x00dev)
461{
462 cancel_delayed_work_sync(&rt2x00dev->link.agc_work);
463}
464
465static void rt2x00link_agc(struct work_struct *work)
466{
467 struct rt2x00_dev *rt2x00dev =
468 container_of(work, struct rt2x00_dev, link.agc_work.work);
469 struct link *link = &rt2x00dev->link;
470
471 /*
472 * When the radio is shutting down we should
473 * immediately cease the watchdog monitoring.
474 */
475 if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
476 return;
477
478 rt2x00dev->ops->lib->gain_calibration(rt2x00dev);
479
480 if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
481 ieee80211_queue_delayed_work(rt2x00dev->hw,
482 &link->agc_work,
483 AGC_INTERVAL);
484}
485
449void rt2x00link_register(struct rt2x00_dev *rt2x00dev) 486void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
450{ 487{
488 INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc);
451 INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog); 489 INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
452 INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); 490 INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);
453} 491}