diff options
author | Joerg Albert <jal2@gmx.de> | 2009-09-05 10:07:47 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-09-08 16:31:10 -0400 |
commit | fea6734a0e444c31c99a1271154bb0281d7908d8 (patch) | |
tree | aa6af0ecb5ddafd7ac35e3f70e11d11c83edcd83 /drivers | |
parent | 7f42c37aa676825fea329a7bec2fefe51033b3e9 (diff) |
ath,ar9170: implemented conformance test limit calc. for tx power
apply the conformance test limits (CTL) stored in the eeprom upon
the values calculated for the tx power (ar->power_*).
This is based on the implementation in the vendor driver
(hal/hpmain.c, line 3700 ff.) with one difference:
If any ctl mode isn't found in the eeprom, we fall back to the "lower",
legacy modes (5GHT20,11A or 2GHT20,11G,11B). Otus only did 5GHT20->11A.
Currently CTL are applied for the FCC group only.
Signed-off-by: Joerg Albert <jal2@gmx.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath/ar9170/phy.c | 165 |
1 files changed, 164 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/ar9170/phy.c b/drivers/net/wireless/ath/ar9170/phy.c index 108ebfe21fcf..b3e5cf3735b0 100644 --- a/drivers/net/wireless/ath/ar9170/phy.c +++ b/drivers/net/wireless/ath/ar9170/phy.c | |||
@@ -556,7 +556,6 @@ int ar9170_init_phy(struct ar9170 *ar, enum ieee80211_band band) | |||
556 | if (err) | 556 | if (err) |
557 | return err; | 557 | return err; |
558 | 558 | ||
559 | /* TODO: (heavy clip) regulatory domain power level fine-tuning. */ | ||
560 | err = ar9170_init_phy_from_eeprom(ar, is_2ghz, is_40mhz); | 559 | err = ar9170_init_phy_from_eeprom(ar, is_2ghz, is_40mhz); |
561 | if (err) | 560 | if (err) |
562 | return err; | 561 | return err; |
@@ -1238,6 +1237,164 @@ static int ar9170_set_freq_cal_data(struct ar9170 *ar, | |||
1238 | return ar9170_regwrite_result(); | 1237 | return ar9170_regwrite_result(); |
1239 | } | 1238 | } |
1240 | 1239 | ||
1240 | static u8 ar9170_get_max_edge_power(struct ar9170 *ar, | ||
1241 | struct ar9170_calctl_edges edges[], | ||
1242 | u32 freq) | ||
1243 | { | ||
1244 | /* TODO: move somewhere else */ | ||
1245 | #define AR5416_MAX_RATE_POWER 63 | ||
1246 | |||
1247 | int i; | ||
1248 | u8 rc = AR5416_MAX_RATE_POWER; | ||
1249 | u8 f; | ||
1250 | if (freq < 3000) | ||
1251 | f = freq - 2300; | ||
1252 | else | ||
1253 | f = (freq - 4800) / 5; | ||
1254 | |||
1255 | for (i = 0; i < AR5416_NUM_BAND_EDGES; i++) { | ||
1256 | if (edges[i].channel == 0xff) | ||
1257 | break; | ||
1258 | if (f == edges[i].channel) { | ||
1259 | /* exact freq match */ | ||
1260 | rc = edges[i].power_flags & ~AR9170_CALCTL_EDGE_FLAGS; | ||
1261 | break; | ||
1262 | } | ||
1263 | if (i > 0 && f < edges[i].channel) { | ||
1264 | if (f > edges[i-1].channel && | ||
1265 | edges[i-1].power_flags & AR9170_CALCTL_EDGE_FLAGS) { | ||
1266 | /* lower channel has the inband flag set */ | ||
1267 | rc = edges[i-1].power_flags & | ||
1268 | ~AR9170_CALCTL_EDGE_FLAGS; | ||
1269 | } | ||
1270 | break; | ||
1271 | } | ||
1272 | } | ||
1273 | |||
1274 | if (i == AR5416_NUM_BAND_EDGES) { | ||
1275 | if (f > edges[i-1].channel && | ||
1276 | edges[i-1].power_flags & AR9170_CALCTL_EDGE_FLAGS) { | ||
1277 | /* lower channel has the inband flag set */ | ||
1278 | rc = edges[i-1].power_flags & | ||
1279 | ~AR9170_CALCTL_EDGE_FLAGS; | ||
1280 | } | ||
1281 | } | ||
1282 | return rc; | ||
1283 | } | ||
1284 | |||
1285 | /* calculate the conformance test limits and apply them to ar->power* | ||
1286 | * (derived from otus hal/hpmain.c, line 3706 ff.) | ||
1287 | */ | ||
1288 | static void ar9170_calc_ctl(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) | ||
1289 | { | ||
1290 | u8 ctl_grp; /* CTL group */ | ||
1291 | u8 ctl_idx; /* CTL index */ | ||
1292 | int i, j; | ||
1293 | struct ctl_modes { | ||
1294 | u8 ctl_mode; | ||
1295 | u8 max_power; | ||
1296 | u8 *pwr_cal_data; | ||
1297 | int pwr_cal_len; | ||
1298 | } *modes; | ||
1299 | |||
1300 | /* order is relevant in the mode_list_*: we fall back to the | ||
1301 | * lower indices if any mode is missed in the EEPROM. | ||
1302 | */ | ||
1303 | struct ctl_modes mode_list_2ghz[] = { | ||
1304 | { CTL_11B, 0, ar->power_2G_cck, 4 }, | ||
1305 | { CTL_11G, 0, ar->power_2G_ofdm, 4 }, | ||
1306 | { CTL_2GHT20, 0, ar->power_2G_ht20, 8 }, | ||
1307 | { CTL_2GHT40, 0, ar->power_2G_ht40, 8 }, | ||
1308 | }; | ||
1309 | struct ctl_modes mode_list_5ghz[] = { | ||
1310 | { CTL_11A, 0, ar->power_5G_leg, 4 }, | ||
1311 | { CTL_5GHT20, 0, ar->power_5G_ht20, 8 }, | ||
1312 | { CTL_5GHT40, 0, ar->power_5G_ht40, 8 }, | ||
1313 | }; | ||
1314 | int nr_modes; | ||
1315 | |||
1316 | #define EDGES(c, n) (ar->eeprom.ctl_data[c].control_edges[n]) | ||
1317 | |||
1318 | /* TODO: investigate the differences between OTUS' | ||
1319 | * hpreg.c::zfHpGetRegulatoryDomain() and | ||
1320 | * ath/regd.c::ath_regd_get_band_ctl() - | ||
1321 | * e.g. for FCC3_WORLD the OTUS procedure | ||
1322 | * always returns CTL_FCC, while the one in ath/ delivers | ||
1323 | * CTL_ETSI for 2GHz and CTL_FCC for 5GHz. | ||
1324 | */ | ||
1325 | ctl_grp = ath_regd_get_band_ctl(&ar->common.regulatory, | ||
1326 | ar->hw->conf.channel->band); | ||
1327 | |||
1328 | /* ctl group not found - either invalid band (NO_CTL) or ww roaming */ | ||
1329 | if (ctl_grp == NO_CTL || ctl_grp == SD_NO_CTL) | ||
1330 | ctl_grp = CTL_FCC; | ||
1331 | |||
1332 | if (ctl_grp != CTL_FCC) | ||
1333 | /* skip CTL and heavy clip for CTL_MKK and CTL_ETSI */ | ||
1334 | return; | ||
1335 | |||
1336 | if (ar->hw->conf.channel->band == IEEE80211_BAND_2GHZ) { | ||
1337 | modes = mode_list_2ghz; | ||
1338 | nr_modes = ARRAY_SIZE(mode_list_2ghz); | ||
1339 | } else { | ||
1340 | modes = mode_list_5ghz; | ||
1341 | nr_modes = ARRAY_SIZE(mode_list_5ghz); | ||
1342 | } | ||
1343 | |||
1344 | for (i = 0; i < nr_modes; i++) { | ||
1345 | u8 c = ctl_grp | modes[i].ctl_mode; | ||
1346 | for (ctl_idx = 0; ctl_idx < AR5416_NUM_CTLS; ctl_idx++) | ||
1347 | if (c == ar->eeprom.ctl_index[ctl_idx]) | ||
1348 | break; | ||
1349 | if (ctl_idx < AR5416_NUM_CTLS) { | ||
1350 | int f_off = 0; | ||
1351 | |||
1352 | /* adjust freq for 40MHz */ | ||
1353 | if (modes[i].ctl_mode == CTL_2GHT40 || | ||
1354 | modes[i].ctl_mode == CTL_5GHT40) { | ||
1355 | if (bw == AR9170_BW_40_BELOW) | ||
1356 | f_off = -10; | ||
1357 | else | ||
1358 | f_off = 10; | ||
1359 | } | ||
1360 | |||
1361 | modes[i].max_power = | ||
1362 | ar9170_get_max_edge_power(ar, EDGES(ctl_idx, 1), | ||
1363 | freq+f_off); | ||
1364 | |||
1365 | /* TODO: check if the regulatory max. power is | ||
1366 | * controlled by cfg80211 for DFS | ||
1367 | * (hpmain applies it to max_power itself for DFS freq) | ||
1368 | */ | ||
1369 | |||
1370 | } else { | ||
1371 | /* Workaround in otus driver, hpmain.c, line 3906: | ||
1372 | * if no data for 5GHT20 are found, take the | ||
1373 | * legacy 5G value. | ||
1374 | * We extend this here to fallback from any other *HT or | ||
1375 | * 11G, too. | ||
1376 | */ | ||
1377 | int k = i; | ||
1378 | |||
1379 | modes[i].max_power = AR5416_MAX_RATE_POWER; | ||
1380 | while (k-- > 0) { | ||
1381 | if (modes[k].max_power != | ||
1382 | AR5416_MAX_RATE_POWER) { | ||
1383 | modes[i].max_power = modes[k].max_power; | ||
1384 | break; | ||
1385 | } | ||
1386 | } | ||
1387 | } | ||
1388 | |||
1389 | /* apply max power to pwr_cal_data (ar->power_*) */ | ||
1390 | for (j = 0; j < modes[i].pwr_cal_len; j++) { | ||
1391 | modes[i].pwr_cal_data[j] = min(modes[i].pwr_cal_data[j], | ||
1392 | modes[i].max_power); | ||
1393 | } | ||
1394 | } | ||
1395 | #undef EDGES | ||
1396 | } | ||
1397 | |||
1241 | static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) | 1398 | static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) |
1242 | { | 1399 | { |
1243 | struct ar9170_calibration_target_power_legacy *ctpl; | 1400 | struct ar9170_calibration_target_power_legacy *ctpl; |
@@ -1340,6 +1497,12 @@ static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) | |||
1340 | ctph[idx + 1].power[n]); | 1497 | ctph[idx + 1].power[n]); |
1341 | } | 1498 | } |
1342 | 1499 | ||
1500 | |||
1501 | /* calc. conformance test limits and apply to ar->power*[] */ | ||
1502 | ar9170_calc_ctl(ar, freq, bw); | ||
1503 | |||
1504 | /* TODO: (heavy clip) regulatory domain power level fine-tuning. */ | ||
1505 | |||
1343 | /* set ACK/CTS TX power */ | 1506 | /* set ACK/CTS TX power */ |
1344 | ar9170_regwrite_begin(ar); | 1507 | ar9170_regwrite_begin(ar); |
1345 | 1508 | ||