diff options
author | Christian Lamparter <chunkeey@googlemail.com> | 2009-09-03 14:25:31 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-09-08 16:31:05 -0400 |
commit | d9c35a506ed7770301f705a9070e05f0c5fae4bd (patch) | |
tree | 3303836f0b9e938da37c68a3a2b5bfd96a3dd8b9 /drivers/net/wireless/ath | |
parent | 8813262ea79acf9daa0e03901bdfe93db4dc4ca5 (diff) |
ar9170: implement frequency calibration for one-stage/openfw
This patch ports some code from the vendor driver, which is
supposed to upload the right calibration values for the
chosen frequency.
In theory, this should give a better range and throughput
for all users with the open, or one-stage firmware.
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath')
-rw-r--r-- | drivers/net/wireless/ath/ar9170/phy.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ar9170/phy.c b/drivers/net/wireless/ath/ar9170/phy.c index 3ace58ab40c8..108ebfe21fcf 100644 --- a/drivers/net/wireless/ath/ar9170/phy.c +++ b/drivers/net/wireless/ath/ar9170/phy.c | |||
@@ -1120,6 +1120,124 @@ static u8 ar9170_interpolate_u8(u8 x, u8 x1, u8 y1, u8 x2, u8 y2) | |||
1120 | #undef SHIFT | 1120 | #undef SHIFT |
1121 | } | 1121 | } |
1122 | 1122 | ||
1123 | static u8 ar9170_interpolate_val(u8 x, u8 *x_array, u8 *y_array) | ||
1124 | { | ||
1125 | int i; | ||
1126 | |||
1127 | for (i = 0; i < 3; i++) | ||
1128 | if (x <= x_array[i + 1]) | ||
1129 | break; | ||
1130 | |||
1131 | return ar9170_interpolate_u8(x, | ||
1132 | x_array[i], | ||
1133 | y_array[i], | ||
1134 | x_array[i + 1], | ||
1135 | y_array[i + 1]); | ||
1136 | } | ||
1137 | |||
1138 | static int ar9170_set_freq_cal_data(struct ar9170 *ar, | ||
1139 | struct ieee80211_channel *channel) | ||
1140 | { | ||
1141 | u8 *cal_freq_pier; | ||
1142 | u8 vpds[2][AR5416_PD_GAIN_ICEPTS]; | ||
1143 | u8 pwrs[2][AR5416_PD_GAIN_ICEPTS]; | ||
1144 | int chain, idx, i; | ||
1145 | u8 f; | ||
1146 | |||
1147 | switch (channel->band) { | ||
1148 | case IEEE80211_BAND_2GHZ: | ||
1149 | f = channel->center_freq - 2300; | ||
1150 | cal_freq_pier = ar->eeprom.cal_freq_pier_2G; | ||
1151 | i = AR5416_NUM_2G_CAL_PIERS - 1; | ||
1152 | break; | ||
1153 | |||
1154 | case IEEE80211_BAND_5GHZ: | ||
1155 | f = (channel->center_freq - 4800) / 5; | ||
1156 | cal_freq_pier = ar->eeprom.cal_freq_pier_5G; | ||
1157 | i = AR5416_NUM_5G_CAL_PIERS - 1; | ||
1158 | break; | ||
1159 | |||
1160 | default: | ||
1161 | return -EINVAL; | ||
1162 | break; | ||
1163 | } | ||
1164 | |||
1165 | for (; i >= 0; i--) { | ||
1166 | if (cal_freq_pier[i] != 0xff) | ||
1167 | break; | ||
1168 | } | ||
1169 | if (i < 0) | ||
1170 | return -EINVAL; | ||
1171 | |||
1172 | idx = ar9170_find_freq_idx(i, cal_freq_pier, f); | ||
1173 | |||
1174 | ar9170_regwrite_begin(ar); | ||
1175 | |||
1176 | for (chain = 0; chain < AR5416_MAX_CHAINS; chain++) { | ||
1177 | for (i = 0; i < AR5416_PD_GAIN_ICEPTS; i++) { | ||
1178 | struct ar9170_calibration_data_per_freq *cal_pier_data; | ||
1179 | int j; | ||
1180 | |||
1181 | switch (channel->band) { | ||
1182 | case IEEE80211_BAND_2GHZ: | ||
1183 | cal_pier_data = &ar->eeprom. | ||
1184 | cal_pier_data_2G[chain][idx]; | ||
1185 | break; | ||
1186 | |||
1187 | case IEEE80211_BAND_5GHZ: | ||
1188 | cal_pier_data = &ar->eeprom. | ||
1189 | cal_pier_data_5G[chain][idx]; | ||
1190 | break; | ||
1191 | |||
1192 | default: | ||
1193 | return -EINVAL; | ||
1194 | } | ||
1195 | |||
1196 | for (j = 0; j < 2; j++) { | ||
1197 | vpds[j][i] = ar9170_interpolate_u8(f, | ||
1198 | cal_freq_pier[idx], | ||
1199 | cal_pier_data->vpd_pdg[j][i], | ||
1200 | cal_freq_pier[idx + 1], | ||
1201 | cal_pier_data[1].vpd_pdg[j][i]); | ||
1202 | |||
1203 | pwrs[j][i] = ar9170_interpolate_u8(f, | ||
1204 | cal_freq_pier[idx], | ||
1205 | cal_pier_data->pwr_pdg[j][i], | ||
1206 | cal_freq_pier[idx + 1], | ||
1207 | cal_pier_data[1].pwr_pdg[j][i]) / 2; | ||
1208 | } | ||
1209 | } | ||
1210 | |||
1211 | for (i = 0; i < 76; i++) { | ||
1212 | u32 phy_data; | ||
1213 | u8 tmp; | ||
1214 | |||
1215 | if (i < 25) { | ||
1216 | tmp = ar9170_interpolate_val(i, &pwrs[0][0], | ||
1217 | &vpds[0][0]); | ||
1218 | } else { | ||
1219 | tmp = ar9170_interpolate_val(i - 12, | ||
1220 | &pwrs[1][0], | ||
1221 | &vpds[1][0]); | ||
1222 | } | ||
1223 | |||
1224 | phy_data |= tmp << ((i & 3) << 3); | ||
1225 | if ((i & 3) == 3) { | ||
1226 | ar9170_regwrite(0x1c6280 + chain * 0x1000 + | ||
1227 | (i & ~3), phy_data); | ||
1228 | phy_data = 0; | ||
1229 | } | ||
1230 | } | ||
1231 | |||
1232 | for (i = 19; i < 32; i++) | ||
1233 | ar9170_regwrite(0x1c6280 + chain * 0x1000 + (i << 2), | ||
1234 | 0x0); | ||
1235 | } | ||
1236 | |||
1237 | ar9170_regwrite_finish(); | ||
1238 | return ar9170_regwrite_result(); | ||
1239 | } | ||
1240 | |||
1123 | static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) | 1241 | static int ar9170_set_power_cal(struct ar9170 *ar, u32 freq, enum ar9170_bw bw) |
1124 | { | 1242 | { |
1125 | struct ar9170_calibration_target_power_legacy *ctpl; | 1243 | struct ar9170_calibration_target_power_legacy *ctpl; |
@@ -1340,6 +1458,10 @@ int ar9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel, | |||
1340 | if (err) | 1458 | if (err) |
1341 | return err; | 1459 | return err; |
1342 | 1460 | ||
1461 | err = ar9170_set_freq_cal_data(ar, channel); | ||
1462 | if (err) | ||
1463 | return err; | ||
1464 | |||
1343 | err = ar9170_set_power_cal(ar, channel->center_freq, bw); | 1465 | err = ar9170_set_power_cal(ar, channel->center_freq, bw); |
1344 | if (err) | 1466 | if (err) |
1345 | return err; | 1467 | return err; |