mac80211: update compat-wireless to 2009-02-19
[oweals/openwrt.git] / package / mac80211 / patches / 202-ath5k_txpower_2413.patch
1 Implement the power curve interpolation, which is required for
2 proper tx on 2413 and newer RF designs.
3
4 Signed-off-by: Felix Fietkau <nbd@openwrt.org>
5
6 --- a/drivers/net/wireless/ath5k/phy.c
7 +++ b/drivers/net/wireless/ath5k/phy.c
8 @@ -4,6 +4,7 @@
9   * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
10   * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
11   * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
12 + * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org>
13   *
14   * Permission to use, copy, modify, and distribute this software for any
15   * purpose with or without fee is hereby granted, provided that the above
16 @@ -1438,31 +1439,449 @@ unsigned int ath5k_hw_get_def_antenna(st
17   */
18  
19  /*
20 - * Initialize the tx power table (not fully implemented)
21 + * find the lower and upper index of the values in the table surrounding the target value
22   */
23 -static void ath5k_txpower_table(struct ath5k_hw *ah,
24 -               struct ieee80211_channel *channel, s16 max_power)
25 +static void
26 +ath5k_get_table_index(const u16 *tbl, unsigned int tbl_sz, u16 target,
27 +                      unsigned int idx[2])
28  {
29 -       unsigned int i, min, max, n;
30 -       u16 txpower, *rates;
31 +       const u16 *ti;
32  
33 -       rates = ah->ah_txpower.txp_rates;
34 +       if (target < tbl[0]) {
35 +               idx[0] = idx[1] = 0;
36 +               return;
37 +       }
38 +
39 +       if (target > tbl[tbl_sz - 1]) {
40 +               idx[0] = idx[1] = tbl_sz - 1;
41 +               return;
42 +       }
43 +
44 +       /* look for the surrounding values */
45 +       for (ti = tbl; ti < &tbl[tbl_sz - 1]; ti++) {
46 +
47 +               /* if the value is equal to the target, set lo = hi = index */
48 +               if (*ti == target) {
49 +                       idx[0] = idx[1] = ti - tbl;
50 +                       return;
51 +               }
52 +
53 +               /* if the target is between the current value and the next one,
54 +                * set lo = cur, hi = lo + 1 */
55 +               if (target < ti[1]) {
56 +                       idx[0] = ti - tbl;
57 +                       idx[1] = idx[0] + 1;
58 +                       return;
59 +               }
60 +       }
61 +}
62 +
63 +/* find the lower and upper frequency info */
64 +static void
65 +ath5k_get_freq_tables(struct ath5k_hw *ah, struct ieee80211_channel *channel,
66 +                     struct ath5k_chan_pcal_info **pcinfo_l,
67 +                     struct ath5k_chan_pcal_info **pcinfo_r,
68 +                     struct ath5k_rate_pcal_info *rates)
69 +{
70 +       struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
71 +       struct ath5k_chan_pcal_info *pcinfo;
72 +       unsigned int idx_l, idx_r;
73 +       int mode, max, i;
74 +       unsigned int target = channel->center_freq;
75 +       struct ath5k_rate_pcal_info *rpinfo;
76 +
77 +       if (!(channel->hw_value & CHANNEL_OFDM)) {
78 +               pcinfo = ee->ee_pwr_cal_b;
79 +               rpinfo = ee->ee_rate_tpwr_b;
80 +               mode = AR5K_EEPROM_MODE_11B;
81 +       } else if (channel->hw_value & CHANNEL_2GHZ) {
82 +               pcinfo = ee->ee_pwr_cal_g;
83 +               rpinfo = ee->ee_rate_tpwr_g;
84 +               mode = AR5K_EEPROM_MODE_11G;
85 +       } else {
86 +               pcinfo = ee->ee_pwr_cal_a;
87 +               rpinfo = ee->ee_rate_tpwr_a;
88 +               mode = AR5K_EEPROM_MODE_11A;
89 +       }
90 +       max = ee->ee_n_piers[mode] - 1;
91 +
92 +       if (target < pcinfo[0].freq) {
93 +               idx_l = idx_r = 0;
94 +               goto done;
95 +       }
96 +
97 +       if (target > pcinfo[max].freq) {
98 +               idx_l = idx_r = max;
99 +               goto done;
100 +       }
101 +
102 +       /* look for the surrounding values */
103 +       for (i = 0; i <= max; i++) {
104 +
105 +               /* if the value is equal to the target, set lo = hi = index */
106 +               if (pcinfo[i].freq == target) {
107 +                       idx_l = idx_r = i;
108 +                       goto done;
109 +               }
110 +
111 +               /* if the target is between the current value and the next one,
112 +                * set lo = cur, hi = lo + 1 */
113 +               if (target < pcinfo[i].freq) {
114 +                       idx_l = i;
115 +                       idx_r = idx_l + 1;
116 +                       goto done;
117 +               }
118 +       }
119 +
120 +done:
121 +       *pcinfo_l = &pcinfo[idx_l];
122 +       *pcinfo_r = &pcinfo[idx_r];
123 +
124 +       if (!rates)
125 +               return;
126 +
127 +       /* rate info minimum values */
128 +       rates->freq = channel->center_freq;
129 +       rates->target_power_6to24 =
130 +               min(rpinfo[idx_l].target_power_6to24,
131 +                   rpinfo[idx_r].target_power_6to24);
132 +       rates->target_power_36 =
133 +               min(rpinfo[idx_l].target_power_36,
134 +                   rpinfo[idx_r].target_power_36);
135 +       rates->target_power_48 =
136 +               min(rpinfo[idx_l].target_power_48,
137 +                   rpinfo[idx_r].target_power_48);
138 +       rates->target_power_54 =
139 +               min(rpinfo[idx_l].target_power_54,
140 +                   rpinfo[idx_r].target_power_54);
141 +}
142 +
143 +
144 +/* Fill the VPD table for all indices between pmin and pmax */
145 +static void
146 +ath5k_fill_vpdtable(s16 pmin, s16 pmax, const s16 *pwr,
147 +                    const u16 *vpd, unsigned int intercepts,
148 +                    u16 vpdtable[AR5K_EEPROM_POWER_TABLE_SIZE])
149 +{
150 +       unsigned int idx[2] = { 0, 0 };
151 +       s16 cur_pwr = 2 * pmin;
152 +       int i;
153 +
154 +       if (intercepts < 2)
155 +               return;
156 +
157 +       for(i = 0; i <= (pmax - pmin); i++) {
158 +               ath5k_get_table_index(pwr, intercepts, cur_pwr, idx);
159 +
160 +               if (!idx[1])
161 +                       idx[1] = 1;
162 +
163 +               if (idx[0] == intercepts - 1)
164 +                       idx[0] = intercepts - 2;
165 +
166 +               if (pwr[idx[0]] == pwr[idx[1]])
167 +                       vpdtable[i] = vpd[idx[0]];
168 +               else
169 +                       vpdtable[i] = (((cur_pwr - pwr[idx[0]]) * vpd[idx[1]] +
170 +                                       (pwr[idx[1]] - cur_pwr) * vpd[idx[0]]) /
171 +                                          (pwr[idx[1]] - pwr[idx[0]]));
172 +
173 +               cur_pwr += 2;
174 +       }
175 +}
176 +
177 +static inline s16
178 +ath5k_interpolate_signed(u16 ref, u16 ref_l, u16 ref_r, s16 val_l, s16 val_r)
179 +{
180 +       if (ref_l == ref_r)
181 +               return val_l;
182 +
183 +       return ((ref - ref_l)*val_r + (ref_r - ref)*val_l) / (ref_r - ref_l);
184 +}
185 +
186 +static inline s16
187 +ath5k_get_min_power_2413(struct ath5k_chan_pcal_info *pcinfo)
188 +{
189 +       struct ath5k_pdgain_info *pd;
190 +       int i;
191 +
192 +       /* backwards - highest pdgain == lowest power */
193 +       for (i = AR5K_EEPROM_N_PD_GAINS - 1; i >= 0; i--) {
194 +               pd = &pcinfo->rf2413_info.pdgains[i];
195 +               if (!pd->n_vpd)
196 +                       continue;
197 +
198 +               return pd->pwr_t4[0];
199 +       }
200 +       return 0;
201 +}
202 +
203 +static inline s16
204 +ath5k_get_max_power_2413(struct ath5k_chan_pcal_info *pcinfo)
205 +{
206 +       struct ath5k_pdgain_info *pd;
207 +       int i;
208 +
209 +       /* forwards: lowest pdgain == highest power */
210 +       for (i = 0; i < AR5K_EEPROM_N_PD_GAINS; i++) {
211 +               pd = &pcinfo->rf2413_info.pdgains[i];
212 +               if (!pd->n_vpd)
213 +                       continue;
214 +
215 +               return pd->pwr_t4[pd->n_vpd];
216 +       }
217 +       return 0;
218 +}
219 +
220 +
221 +
222 +static int
223 +ath5k_txpower_table_2413(struct ath5k_hw *ah, struct ieee80211_channel *ch,
224 +                         struct ath5k_chan_pcal_info *pcinfo_l,
225 +                         struct ath5k_chan_pcal_info *pcinfo_r)
226 +{
227 +       struct ath5k_pdgain_info *pd_l, *pd_r;
228 +       u16 gain_boundaries[4];
229 +       u16 *xpd = ah->ah_txpower.txp_xpd;
230 +       int n_xpd = 0;
231 +       s16 pmin_t2[AR5K_EEPROM_N_PD_GAINS];
232 +       s16 pmax_t2[AR5K_EEPROM_N_PD_GAINS];
233 +       u16 *pdadc_out = ah->ah_txpower.txp_pcdac;
234 +       unsigned int gain_overlap;
235 +       unsigned int vpd_size, target_idx, max_idx;
236 +       unsigned int n_pdadc = 0;
237 +       u16 vpd_step;
238 +       u16 *pcdacL;
239 +       u16 *pcdacR;
240 +       int i, j, s;
241 +       u32 reg;
242 +       s16 ch_pmin, ch_pmax;
243 +
244 +       gain_overlap = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) &
245 +               AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP;
246 +
247 +       /* loop backwards over pdgains (highest pdgain == lowest power) */
248 +       for (i = AR5K_EEPROM_N_PD_GAINS - 1; i >= 0; i--) {
249 +               pd_l = &pcinfo_l->rf2413_info.pdgains[i];
250 +               pd_r = &pcinfo_r->rf2413_info.pdgains[i];
251 +               pcdacL = ah->ah_txpower.txp_rfdata.rf2413.pcdacL[n_xpd];
252 +               pcdacR = ah->ah_txpower.txp_rfdata.rf2413.pcdacR[n_xpd];
253 +
254 +               if (!pd_l->n_vpd)
255 +                       continue;
256 +
257 +               xpd[n_xpd] = i;
258 +
259 +               pmin_t2[n_xpd] = min(pd_l->pwr_t4[0], pd_r->pwr_t4[0]) / 2;
260 +               pmax_t2[n_xpd] = min(pd_l->pwr_t4[pd_l->n_vpd - 1],
261 +                       pd_r->pwr_t4[pd_r->n_vpd - 1]) / 2;
262 +
263 +               if ((u16) (pmax_t2[n_xpd] - pmin_t2[n_xpd]) > 64)
264 +                       continue;
265 +
266 +               /* fill vpd tables for left and right frequency info */
267 +               ath5k_fill_vpdtable(pmin_t2[n_xpd], pmax_t2[n_xpd],
268 +                       pd_l->pwr_t4, pd_l->vpd, pd_l->n_vpd, pcdacL);
269 +
270 +               /* check if interpolation is necessary */
271 +               if (pcinfo_l == pcinfo_r)
272 +                       continue;
273 +
274 +               ath5k_fill_vpdtable(pmin_t2[n_xpd], pmax_t2[n_xpd],
275 +                       pd_r->pwr_t4, pd_r->vpd, pd_r->n_vpd, pcdacR);
276 +
277 +               /* interpolate pcdac values,
278 +                * reuse pcdacL table for interpolation output */
279 +               for (j = 0; j < (u16) (pmax_t2[n_xpd] - pmin_t2[n_xpd]); j++) {
280 +                       pcdacL[j] = ath5k_interpolate_signed(ch->center_freq,
281 +                                       pcinfo_l->freq, pcinfo_r->freq,
282 +                                       (s16) pcdacL[j], (s16) pcdacR[j]);
283 +               }
284 +               n_xpd++;
285 +       }
286 +
287 +       if (!n_xpd)
288 +               return 0;
289 +
290 +       /* create final table */
291 +       for (i = 0, n_pdadc = 0; i < n_xpd; i++) {
292 +               pcdacL = ah->ah_txpower.txp_rfdata.rf2413.pcdacL[i];
293 +
294 +               if (i == n_xpd - 1) {
295 +                       /* 2 db boundary stretch */
296 +                       gain_boundaries[i] = pmax_t2[i] + 4;
297 +               } else {
298 +                       gain_boundaries[i] = (pmax_t2[i] + pmin_t2[i + 1]) / 2;
299 +               }
300 +
301 +               if (gain_boundaries[i] > AR5K_TUNE_MAX_TXPOWER)
302 +                       gain_boundaries[i] = AR5K_TUNE_MAX_TXPOWER;
303 +
304 +               /* find starting index */
305 +               if (i == 0)
306 +                       s = 0;
307 +               else
308 +                       s = (gain_boundaries[i - 1] - pmin_t2[i]) -
309 +                               gain_overlap;
310 +
311 +               if (pcdacL[1] > pcdacL[0])
312 +                       vpd_step = pcdacL[1] - pcdacL[0];
313 +               else
314 +                       vpd_step = 1;
315 +
316 +               /* if s is below 0, we need to extrapolate below this pdgain */
317 +               while ((s < 0) && (n_pdadc < 128)) {
318 +                       s16 tmp = pcdacL[0] + s * vpd_step;
319 +                       pdadc_out[n_pdadc++] = (u16) ((tmp < 0) ? 0 : tmp);
320 +                       s++;
321 +               }
322 +
323 +               vpd_size = pmax_t2[i] - pmin_t2[i];
324 +               target_idx = gain_boundaries[i] + gain_overlap - pmin_t2[i];
325 +               max_idx = (target_idx < vpd_size) ? target_idx : vpd_size;
326 +
327 +               while ((s < (s16) max_idx) && (n_pdadc < 128))
328 +                       pdadc_out[n_pdadc++] = pcdacL[s++];
329 +
330 +               /* need to extrapolate above this pdgain? */
331 +               if (target_idx <= max_idx)
332 +                       continue;
333  
334 -       txpower = AR5K_TUNE_DEFAULT_TXPOWER * 2;
335 -       if (max_power > txpower)
336 -               txpower = max_power > AR5K_TUNE_MAX_TXPOWER ?
337 -                   AR5K_TUNE_MAX_TXPOWER : max_power;
338 +               if (pcdacL[vpd_size - 1] > pcdacL[vpd_size - 2])
339 +                       vpd_step = pcdacL[vpd_size - 1] - pcdacL[vpd_size - 2];
340 +               else
341 +                       vpd_step = 1;
342  
343 -       for (i = 0; i < AR5K_MAX_RATES; i++)
344 -               rates[i] = txpower;
345 +               while ((s < (s16) target_idx) && (n_pdadc < 128)) {
346 +                       int tmp = pcdacL[vpd_size - 1] +
347 +                               (s - max_idx) * vpd_step;
348 +                       pdadc_out[n_pdadc++] = (tmp > 127) ? 127 : tmp;
349 +                       s++;
350 +               }
351 +       }
352  
353 -       /* XXX setup target powers by rate */
354 +       while (i < AR5K_EEPROM_N_PD_GAINS) {
355 +               gain_boundaries[i] = gain_boundaries[i - 1];
356 +               i++;
357 +       }
358 +
359 +       while (n_pdadc < 128) {
360 +               pdadc_out[n_pdadc] = pdadc_out[n_pdadc - 1];
361 +               n_pdadc++;
362 +       }
363 +
364 +       /* select the right xpdgain curves */
365 +       reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1);
366 +       reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 |
367 +                AR5K_PHY_TPC_RG1_PDGAIN_2 |
368 +                AR5K_PHY_TPC_RG1_PDGAIN_3 |
369 +                AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
370 +       reg |= AR5K_REG_SM(n_xpd, AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
371 +       switch(n_xpd) {
372 +       case 3:
373 +               reg |= AR5K_REG_SM(xpd[2], AR5K_PHY_TPC_RG1_PDGAIN_3);
374 +               /* fall through */
375 +       case 2:
376 +               reg |= AR5K_REG_SM(xpd[1], AR5K_PHY_TPC_RG1_PDGAIN_2);
377 +               /* fall through */
378 +       case 1:
379 +               reg |= AR5K_REG_SM(xpd[0], AR5K_PHY_TPC_RG1_PDGAIN_1);
380 +               break;
381 +       }
382 +       ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1);
383  
384 +       /*
385 +        * Write TX power values
386 +        */
387 +       reg = AR5K_PHY_PDADC_TXPOWER_BASE;
388 +       for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
389 +               ath5k_hw_reg_write(ah,
390 +                       ((pdadc_out[4*i + 0] & 0xff) << 0) |
391 +                       ((pdadc_out[4*i + 1] & 0xff) << 8) |
392 +                       ((pdadc_out[4*i + 2] & 0xff) << 16) |
393 +                       ((pdadc_out[4*i + 3] & 0xff) << 24), reg);
394 +               reg += 4;
395 +       }
396 +
397 +       ath5k_hw_reg_write(ah,
398 +               AR5K_REG_SM(gain_overlap,
399 +                       AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) |
400 +               AR5K_REG_SM(gain_boundaries[0],
401 +                       AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) |
402 +               AR5K_REG_SM(gain_boundaries[1],
403 +                       AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) |
404 +               AR5K_REG_SM(gain_boundaries[2],
405 +                       AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) |
406 +               AR5K_REG_SM(gain_boundaries[3],
407 +                       AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4),
408 +               AR5K_PHY_TPC_RG5);
409 +
410 +       ah->ah_txpower.txp_offset = pmin_t2[0];
411 +
412 +       /* look up power boundaries for this channel */
413 +       ch_pmin = ath5k_get_min_power_2413(pcinfo_l);
414 +       ch_pmax = ath5k_get_max_power_2413(pcinfo_l);
415 +
416 +       if (pcinfo_l != pcinfo_r) {
417 +               s16 pwr_r;
418 +
419 +               pwr_r = ath5k_get_min_power_2413(pcinfo_r);
420 +               ch_pmin = ath5k_interpolate_signed(ch->center_freq,
421 +                                       pcinfo_l->freq, pcinfo_r->freq,
422 +                                       ch_pmin, pwr_r);
423 +
424 +               pwr_r = ath5k_get_max_power_2413(pcinfo_r);
425 +               ch_pmax = ath5k_interpolate_signed(ch->center_freq,
426 +                                       pcinfo_l->freq, pcinfo_r->freq,
427 +                                       ch_pmax, pwr_r);
428 +       }
429 +       ah->ah_txpower.txp_min = ch_pmin;
430 +       ah->ah_txpower.txp_max = ch_pmax;
431 +
432 +       return 0;
433 +}
434 +
435 +static void
436 +ath5k_setup_rate_table(struct ath5k_hw *ah, u16 max_pwr,
437 +                       struct ath5k_rate_pcal_info *rate_info)
438 +{
439 +       unsigned int i;
440 +       u16 *rates;
441 +
442 +       max_pwr *= 2;
443 +       max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max);
444 +
445 +       /* apply rate limits */
446 +       rates = ah->ah_txpower.txp_rates;
447 +       for (i = 0; i < 5; i++) {
448 +               rates[i] = min(max_pwr, rate_info->target_power_6to24);
449 +       }
450 +       rates[5] = min(rates[0], rate_info->target_power_36);
451 +       rates[6] = min(rates[0], rate_info->target_power_48);
452 +       rates[7] = min(rates[0], rate_info->target_power_54);
453 +       rates[8] = min(rates[0], rate_info->target_power_6to24);
454 +       rates[9] = min(rates[0], rate_info->target_power_36);
455 +       rates[10] = min(rates[0], rate_info->target_power_36);
456 +       rates[11] = min(rates[0], rate_info->target_power_48);
457 +       rates[12] = min(rates[0], rate_info->target_power_48);
458 +       rates[13] = min(rates[0], rate_info->target_power_54);
459 +       rates[14] = min(rates[0], rate_info->target_power_54);
460 +
461 +       ah->ah_txpower.txp_tpc = max_pwr;
462         ah->ah_txpower.txp_min = rates[7];
463 -       ah->ah_txpower.txp_max = rates[0];
464 -       ah->ah_txpower.txp_ofdm = rates[0];
465 +       ah->ah_txpower.txp_max = min(ah->ah_txpower.txp_max,
466 +               (s16) rate_info->target_power_36);
467 +       ah->ah_txpower.txp_ofdm = ah->ah_txpower.txp_max;
468 +}
469 +
470 +static int
471 +ath5k_txpower_table(struct ath5k_hw *ah, struct ieee80211_channel *ch,
472 +                    struct ath5k_chan_pcal_info *pcinfo_l,
473 +                    struct ath5k_chan_pcal_info *pcinfo_r,
474 +                                       u16 max_pwr)
475 +{
476 +       unsigned int i, min, max, n;
477  
478 -       /* Calculate the power table */
479         n = ARRAY_SIZE(ah->ah_txpower.txp_pcdac);
480         min = AR5K_EEPROM_PCDAC_START;
481         max = AR5K_EEPROM_PCDAC_STOP;
482 @@ -1473,51 +1892,64 @@ static void ath5k_txpower_table(struct a
483  #else
484                 min;
485  #endif
486 +
487 +       /*
488 +        * Write TX power values
489 +        */
490 +       for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
491 +               ath5k_hw_reg_write(ah,
492 +                       ((((ah->ah_txpower.txp_pcdac[(i << 1) + 1] << 8) |
493 +                               0xff) & 0xffff) << 16) |
494 +                        (((ah->ah_txpower.txp_pcdac[(i << 1)    ] << 8) |
495 +                               0xff) & 0xffff),
496 +                       AR5K_PHY_PCDAC_TXPOWER(i));
497 +       }
498 +       return 0;
499  }
500  
501 +
502  /*
503   * Set transmition power
504   */
505 -int /*O.K. - txpower_table is unimplemented so this doesn't work*/
506 +int
507  ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
508                 unsigned int txpower)
509  {
510 +       struct ath5k_chan_pcal_info *pcinfo_l, *pcinfo_r;
511 +       struct ath5k_rate_pcal_info rate_info;
512         bool tpc = ah->ah_txpower.txp_tpc;
513 -       unsigned int i;
514  
515         ATH5K_TRACE(ah->ah_sc);
516         if (txpower > AR5K_TUNE_MAX_TXPOWER) {
517                 ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower);
518                 return -EINVAL;
519         }
520 -
521 -       /*
522 -        * RF2413 for some reason can't
523 -        * transmit anything if we call
524 -        * this funtion, so we skip it
525 -        * until we fix txpower.
526 -        *
527 -        * XXX: Assume same for RF2425
528 -        * to be safe.
529 -        */
530 -       if ((ah->ah_radio == AR5K_RF2413) || (ah->ah_radio == AR5K_RF2425))
531 -               return 0;
532 +       if (txpower == 0)
533 +               txpower = AR5K_TUNE_MAX_TXPOWER;
534  
535         /* Reset TX power values */
536         memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
537         ah->ah_txpower.txp_tpc = tpc;
538 +       ah->ah_txpower.txp_min = 0;
539 +       ah->ah_txpower.txp_max = AR5K_TUNE_MAX_TXPOWER;
540  
541 -       /* Initialize TX power table */
542 -       ath5k_txpower_table(ah, channel, txpower);
543 +       /* find matching frequency info */
544 +       ath5k_get_freq_tables(ah, channel, &pcinfo_l, &pcinfo_r, &rate_info);
545 +       ath5k_setup_rate_table(ah, txpower, &rate_info);
546  
547 -       /*
548 -        * Write TX power values
549 -        */
550 -       for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
551 -               ath5k_hw_reg_write(ah,
552 -                       ((((ah->ah_txpower.txp_pcdac[(i << 1) + 1] << 8) | 0xff) & 0xffff) << 16) |
553 -                       (((ah->ah_txpower.txp_pcdac[(i << 1)    ] << 8) | 0xff) & 0xffff),
554 -                       AR5K_PHY_PCDAC_TXPOWER(i));
555 +       /* Initialize TX power table */
556 +       switch(ah->ah_radio) {
557 +       case AR5K_RF2413:
558 +       case AR5K_RF5413:
559 +               ath5k_txpower_table_2413(ah, channel, pcinfo_l, pcinfo_r);
560 +               break;
561 +       case AR5K_RF2425:
562 +               /* unimplemented */
563 +               return 0;
564 +       default:
565 +               /* Default power table */
566 +               ath5k_txpower_table(ah, channel, pcinfo_l, pcinfo_r, txpower);
567 +               break;
568         }
569  
570         ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) |
571 @@ -1536,12 +1968,19 @@ ath5k_hw_txpower(struct ath5k_hw *ah, st
572                 AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) |
573                 AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4);
574  
575 -       if (ah->ah_txpower.txp_tpc)
576 +       if (ah->ah_txpower.txp_tpc) {
577                 ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE |
578                         AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
579 -       else
580 +
581 +               ath5k_hw_reg_write(ah,
582 +                       AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) |
583 +                       AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) |
584 +                       AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
585 +                       AR5K_TPC);
586 +       } else {
587                 ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX |
588                         AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
589 +       }
590  
591         return 0;
592  }
593 --- a/drivers/net/wireless/ath5k/ath5k.h
594 +++ b/drivers/net/wireless/ath5k/ath5k.h
595 @@ -204,7 +204,7 @@
596  #define AR5K_TUNE_CWMAX_11B                    1023
597  #define AR5K_TUNE_CWMAX_XR                     7
598  #define AR5K_TUNE_NOISE_FLOOR                  -72
599 -#define AR5K_TUNE_MAX_TXPOWER                  60
600 +#define AR5K_TUNE_MAX_TXPOWER                  63
601  #define AR5K_TUNE_DEFAULT_TXPOWER              30
602  #define AR5K_TUNE_TPC_TXPOWER                  true
603  #define AR5K_TUNE_ANT_DIVERSITY                        true
604 @@ -1085,11 +1085,23 @@ struct ath5k_hw {
605         struct ath5k_gain       ah_gain;
606         u8                      ah_offset[AR5K_MAX_RF_BANKS];
607  
608 +
609         struct {
610 -               u16             txp_pcdac[AR5K_EEPROM_POWER_TABLE_SIZE];
611 +               union {
612 +                       struct {
613 +                               /* Temporary PCDAC tables for interpolation */
614 +                               u16 pcdacL[AR5K_EEPROM_N_PD_GAINS]
615 +                                       [AR5K_EEPROM_POWER_TABLE_SIZE];
616 +                               u16 pcdacR[AR5K_EEPROM_N_PD_GAINS]
617 +                                       [AR5K_EEPROM_POWER_TABLE_SIZE];
618 +                       } rf2413;
619 +               } txp_rfdata;
620 +               u16             txp_xpd[AR5K_EEPROM_N_XPD_PER_CHANNEL];
621 +               u16             txp_pcdac[AR5K_EEPROM_POWER_TABLE_SIZE * 2];
622                 u16             txp_rates[AR5K_MAX_RATES];
623                 s16             txp_min;
624                 s16             txp_max;
625 +               s16             txp_offset;
626                 bool            txp_tpc;
627                 s16             txp_ofdm;
628         } ah_txpower;
629 --- a/drivers/net/wireless/ath5k/reg.h
630 +++ b/drivers/net/wireless/ath5k/reg.h
631 @@ -1552,6 +1552,15 @@
632  
633  
634  /*===5212 Specific PCU registers===*/
635 +#define AR5K_TPC                       0x80e8
636 +#define AR5K_TPC_ACK                   0x0000003f      /* ack frames */
637 +#define AR5K_TPC_ACK_S                 0
638 +#define AR5K_TPC_CTS                   0x00003f00      /* cts frames */
639 +#define AR5K_TPC_CTS_S                 8
640 +#define AR5K_TPC_CHIRP                 0x003f0000      /* chirp frames */
641 +#define AR5K_TPC_CHIRP_S               16
642 +#define AR5K_TPC_DOPPLER               0x0f000000      /* doppler chirp span */
643 +#define AR5K_TPC_DOPPLER_S             24
644  
645  /*
646   * XR (eXtended Range) mode register
647 @@ -2550,6 +2559,12 @@
648  #define        AR5K_PHY_TPC_RG1                0xa258
649  #define        AR5K_PHY_TPC_RG1_NUM_PD_GAIN    0x0000c000
650  #define        AR5K_PHY_TPC_RG1_NUM_PD_GAIN_S  14
651 +#define AR5K_PHY_TPC_RG1_PDGAIN_1      0x00030000
652 +#define AR5K_PHY_TPC_RG1_PDGAIN_1_S    16
653 +#define AR5K_PHY_TPC_RG1_PDGAIN_2      0x000c0000
654 +#define AR5K_PHY_TPC_RG1_PDGAIN_2_S    18
655 +#define AR5K_PHY_TPC_RG1_PDGAIN_3      0x00300000
656 +#define AR5K_PHY_TPC_RG1_PDGAIN_3_S    20
657  
658  #define        AR5K_PHY_TPC_RG5                        0xa26C
659  #define        AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP        0x0000000F
660 --- a/drivers/net/wireless/ath5k/desc.c
661 +++ b/drivers/net/wireless/ath5k/desc.c
662 @@ -194,6 +194,10 @@ static int ath5k_hw_setup_4word_tx_desc(
663                 return -EINVAL;
664         }
665  
666 +       tx_power += ah->ah_txpower.txp_offset;
667 +       if (tx_power > AR5K_TUNE_MAX_TXPOWER)
668 +               tx_power = AR5K_TUNE_MAX_TXPOWER;
669 +
670         /* Clear descriptor */
671         memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc));
672