aboutsummaryrefslogtreecommitdiffstats
path: root/package/mac80211/patches/202-ath5k_txpower_2413.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/mac80211/patches/202-ath5k_txpower_2413.patch')
-rw-r--r--package/mac80211/patches/202-ath5k_txpower_2413.patch672
1 files changed, 0 insertions, 672 deletions
diff --git a/package/mac80211/patches/202-ath5k_txpower_2413.patch b/package/mac80211/patches/202-ath5k_txpower_2413.patch
deleted file mode 100644
index ddecd9e071..0000000000
--- a/package/mac80211/patches/202-ath5k_txpower_2413.patch
+++ /dev/null
@@ -1,672 +0,0 @@
-Implement the power curve interpolation, which is required for
-proper tx on 2413 and newer RF designs.
-
-Signed-off-by: Felix Fietkau <nbd@openwrt.org>
-
---- a/drivers/net/wireless/ath5k/phy.c
-+++ b/drivers/net/wireless/ath5k/phy.c
-@@ -4,6 +4,7 @@
- * Copyright (c) 2004-2007 Reyk Floeter <reyk@openbsd.org>
- * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
- * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com>
-+ * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
-@@ -1438,31 +1439,449 @@ unsigned int ath5k_hw_get_def_antenna(st
- */
-
- /*
-- * Initialize the tx power table (not fully implemented)
-+ * find the lower and upper index of the values in the table surrounding the target value
- */
--static void ath5k_txpower_table(struct ath5k_hw *ah,
-- struct ieee80211_channel *channel, s16 max_power)
-+static void
-+ath5k_get_table_index(const u16 *tbl, unsigned int tbl_sz, u16 target,
-+ unsigned int idx[2])
- {
-- unsigned int i, min, max, n;
-- u16 txpower, *rates;
-+ const u16 *ti;
-
-- rates = ah->ah_txpower.txp_rates;
-+ if (target < tbl[0]) {
-+ idx[0] = idx[1] = 0;
-+ return;
-+ }
-+
-+ if (target > tbl[tbl_sz - 1]) {
-+ idx[0] = idx[1] = tbl_sz - 1;
-+ return;
-+ }
-+
-+ /* look for the surrounding values */
-+ for (ti = tbl; ti < &tbl[tbl_sz - 1]; ti++) {
-+
-+ /* if the value is equal to the target, set lo = hi = index */
-+ if (*ti == target) {
-+ idx[0] = idx[1] = ti - tbl;
-+ return;
-+ }
-+
-+ /* if the target is between the current value and the next one,
-+ * set lo = cur, hi = lo + 1 */
-+ if (target < ti[1]) {
-+ idx[0] = ti - tbl;
-+ idx[1] = idx[0] + 1;
-+ return;
-+ }
-+ }
-+}
-+
-+/* find the lower and upper frequency info */
-+static void
-+ath5k_get_freq_tables(struct ath5k_hw *ah, struct ieee80211_channel *channel,
-+ struct ath5k_chan_pcal_info **pcinfo_l,
-+ struct ath5k_chan_pcal_info **pcinfo_r,
-+ struct ath5k_rate_pcal_info *rates)
-+{
-+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
-+ struct ath5k_chan_pcal_info *pcinfo;
-+ unsigned int idx_l, idx_r;
-+ int mode, max, i;
-+ unsigned int target = channel->center_freq;
-+ struct ath5k_rate_pcal_info *rpinfo;
-+
-+ if (!(channel->hw_value & CHANNEL_OFDM)) {
-+ pcinfo = ee->ee_pwr_cal_b;
-+ rpinfo = ee->ee_rate_tpwr_b;
-+ mode = AR5K_EEPROM_MODE_11B;
-+ } else if (channel->hw_value & CHANNEL_2GHZ) {
-+ pcinfo = ee->ee_pwr_cal_g;
-+ rpinfo = ee->ee_rate_tpwr_g;
-+ mode = AR5K_EEPROM_MODE_11G;
-+ } else {
-+ pcinfo = ee->ee_pwr_cal_a;
-+ rpinfo = ee->ee_rate_tpwr_a;
-+ mode = AR5K_EEPROM_MODE_11A;
-+ }
-+ max = ee->ee_n_piers[mode] - 1;
-+
-+ if (target < pcinfo[0].freq) {
-+ idx_l = idx_r = 0;
-+ goto done;
-+ }
-+
-+ if (target > pcinfo[max].freq) {
-+ idx_l = idx_r = max;
-+ goto done;
-+ }
-+
-+ /* look for the surrounding values */
-+ for (i = 0; i <= max; i++) {
-+
-+ /* if the value is equal to the target, set lo = hi = index */
-+ if (pcinfo[i].freq == target) {
-+ idx_l = idx_r = i;
-+ goto done;
-+ }
-+
-+ /* if the target is between the current value and the next one,
-+ * set lo = cur, hi = lo + 1 */
-+ if (target < pcinfo[i].freq) {
-+ idx_l = i;
-+ idx_r = idx_l + 1;
-+ goto done;
-+ }
-+ }
-+
-+done:
-+ *pcinfo_l = &pcinfo[idx_l];
-+ *pcinfo_r = &pcinfo[idx_r];
-+
-+ if (!rates)
-+ return;
-+
-+ /* rate info minimum values */
-+ rates->freq = channel->center_freq;
-+ rates->target_power_6to24 =
-+ min(rpinfo[idx_l].target_power_6to24,
-+ rpinfo[idx_r].target_power_6to24);
-+ rates->target_power_36 =
-+ min(rpinfo[idx_l].target_power_36,
-+ rpinfo[idx_r].target_power_36);
-+ rates->target_power_48 =
-+ min(rpinfo[idx_l].target_power_48,
-+ rpinfo[idx_r].target_power_48);
-+ rates->target_power_54 =
-+ min(rpinfo[idx_l].target_power_54,
-+ rpinfo[idx_r].target_power_54);
-+}
-+
-+
-+/* Fill the VPD table for all indices between pmin and pmax */
-+static void
-+ath5k_fill_vpdtable(s16 pmin, s16 pmax, const s16 *pwr,
-+ const u16 *vpd, unsigned int intercepts,
-+ u16 vpdtable[AR5K_EEPROM_POWER_TABLE_SIZE])
-+{
-+ unsigned int idx[2] = { 0, 0 };
-+ s16 cur_pwr = 2 * pmin;
-+ int i;
-+
-+ if (intercepts < 2)
-+ return;
-+
-+ for(i = 0; i <= (pmax - pmin); i++) {
-+ ath5k_get_table_index(pwr, intercepts, cur_pwr, idx);
-+
-+ if (!idx[1])
-+ idx[1] = 1;
-+
-+ if (idx[0] == intercepts - 1)
-+ idx[0] = intercepts - 2;
-+
-+ if (pwr[idx[0]] == pwr[idx[1]])
-+ vpdtable[i] = vpd[idx[0]];
-+ else
-+ vpdtable[i] = (((cur_pwr - pwr[idx[0]]) * vpd[idx[1]] +
-+ (pwr[idx[1]] - cur_pwr) * vpd[idx[0]]) /
-+ (pwr[idx[1]] - pwr[idx[0]]));
-+
-+ cur_pwr += 2;
-+ }
-+}
-+
-+static inline s16
-+ath5k_interpolate_signed(u16 ref, u16 ref_l, u16 ref_r, s16 val_l, s16 val_r)
-+{
-+ if (ref_l == ref_r)
-+ return val_l;
-+
-+ return ((ref - ref_l)*val_r + (ref_r - ref)*val_l) / (ref_r - ref_l);
-+}
-+
-+static inline s16
-+ath5k_get_min_power_2413(struct ath5k_chan_pcal_info *pcinfo)
-+{
-+ struct ath5k_pdgain_info *pd;
-+ int i;
-+
-+ /* backwards - highest pdgain == lowest power */
-+ for (i = AR5K_EEPROM_N_PD_GAINS - 1; i >= 0; i--) {
-+ pd = &pcinfo->rf2413_info.pdgains[i];
-+ if (!pd->n_vpd)
-+ continue;
-+
-+ return pd->pwr_t4[0];
-+ }
-+ return 0;
-+}
-+
-+static inline s16
-+ath5k_get_max_power_2413(struct ath5k_chan_pcal_info *pcinfo)
-+{
-+ struct ath5k_pdgain_info *pd;
-+ int i;
-+
-+ /* forwards: lowest pdgain == highest power */
-+ for (i = 0; i < AR5K_EEPROM_N_PD_GAINS; i++) {
-+ pd = &pcinfo->rf2413_info.pdgains[i];
-+ if (!pd->n_vpd)
-+ continue;
-+
-+ return pd->pwr_t4[pd->n_vpd];
-+ }
-+ return 0;
-+}
-+
-+
-+
-+static int
-+ath5k_txpower_table_2413(struct ath5k_hw *ah, struct ieee80211_channel *ch,
-+ struct ath5k_chan_pcal_info *pcinfo_l,
-+ struct ath5k_chan_pcal_info *pcinfo_r)
-+{
-+ struct ath5k_pdgain_info *pd_l, *pd_r;
-+ u16 gain_boundaries[4];
-+ u16 *xpd = ah->ah_txpower.txp_xpd;
-+ int n_xpd = 0;
-+ s16 pmin_t2[AR5K_EEPROM_N_PD_GAINS];
-+ s16 pmax_t2[AR5K_EEPROM_N_PD_GAINS];
-+ u16 *pdadc_out = ah->ah_txpower.txp_pcdac;
-+ unsigned int gain_overlap;
-+ unsigned int vpd_size, target_idx, max_idx;
-+ unsigned int n_pdadc = 0;
-+ u16 vpd_step;
-+ u16 *pcdacL;
-+ u16 *pcdacR;
-+ int i, j, s;
-+ u32 reg;
-+ s16 ch_pmin, ch_pmax;
-+
-+ gain_overlap = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG5) &
-+ AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP;
-+
-+ /* loop backwards over pdgains (highest pdgain == lowest power) */
-+ for (i = AR5K_EEPROM_N_PD_GAINS - 1; i >= 0; i--) {
-+ pd_l = &pcinfo_l->rf2413_info.pdgains[i];
-+ pd_r = &pcinfo_r->rf2413_info.pdgains[i];
-+ pcdacL = ah->ah_txpower.txp_rfdata.rf2413.pcdacL[n_xpd];
-+ pcdacR = ah->ah_txpower.txp_rfdata.rf2413.pcdacR[n_xpd];
-+
-+ if (!pd_l->n_vpd)
-+ continue;
-+
-+ xpd[n_xpd] = i;
-+
-+ pmin_t2[n_xpd] = min(pd_l->pwr_t4[0], pd_r->pwr_t4[0]) / 2;
-+ pmax_t2[n_xpd] = min(pd_l->pwr_t4[pd_l->n_vpd - 1],
-+ pd_r->pwr_t4[pd_r->n_vpd - 1]) / 2;
-+
-+ if ((u16) (pmax_t2[n_xpd] - pmin_t2[n_xpd]) > 64)
-+ continue;
-+
-+ /* fill vpd tables for left and right frequency info */
-+ ath5k_fill_vpdtable(pmin_t2[n_xpd], pmax_t2[n_xpd],
-+ pd_l->pwr_t4, pd_l->vpd, pd_l->n_vpd, pcdacL);
-+
-+ /* check if interpolation is necessary */
-+ if (pcinfo_l == pcinfo_r)
-+ continue;
-+
-+ ath5k_fill_vpdtable(pmin_t2[n_xpd], pmax_t2[n_xpd],
-+ pd_r->pwr_t4, pd_r->vpd, pd_r->n_vpd, pcdacR);
-+
-+ /* interpolate pcdac values,
-+ * reuse pcdacL table for interpolation output */
-+ for (j = 0; j < (u16) (pmax_t2[n_xpd] - pmin_t2[n_xpd]); j++) {
-+ pcdacL[j] = ath5k_interpolate_signed(ch->center_freq,
-+ pcinfo_l->freq, pcinfo_r->freq,
-+ (s16) pcdacL[j], (s16) pcdacR[j]);
-+ }
-+ n_xpd++;
-+ }
-+
-+ if (!n_xpd)
-+ return 0;
-+
-+ /* create final table */
-+ for (i = 0, n_pdadc = 0; i < n_xpd; i++) {
-+ pcdacL = ah->ah_txpower.txp_rfdata.rf2413.pcdacL[i];
-+
-+ if (i == n_xpd - 1) {
-+ /* 2 db boundary stretch */
-+ gain_boundaries[i] = pmax_t2[i] + 4;
-+ } else {
-+ gain_boundaries[i] = (pmax_t2[i] + pmin_t2[i + 1]) / 2;
-+ }
-+
-+ if (gain_boundaries[i] > AR5K_TUNE_MAX_TXPOWER)
-+ gain_boundaries[i] = AR5K_TUNE_MAX_TXPOWER;
-+
-+ /* find starting index */
-+ if (i == 0)
-+ s = 0;
-+ else
-+ s = (gain_boundaries[i - 1] - pmin_t2[i]) -
-+ gain_overlap;
-+
-+ if (pcdacL[1] > pcdacL[0])
-+ vpd_step = pcdacL[1] - pcdacL[0];
-+ else
-+ vpd_step = 1;
-+
-+ /* if s is below 0, we need to extrapolate below this pdgain */
-+ while ((s < 0) && (n_pdadc < 128)) {
-+ s16 tmp = pcdacL[0] + s * vpd_step;
-+ pdadc_out[n_pdadc++] = (u16) ((tmp < 0) ? 0 : tmp);
-+ s++;
-+ }
-+
-+ vpd_size = pmax_t2[i] - pmin_t2[i];
-+ target_idx = gain_boundaries[i] + gain_overlap - pmin_t2[i];
-+ max_idx = (target_idx < vpd_size) ? target_idx : vpd_size;
-+
-+ while ((s < (s16) max_idx) && (n_pdadc < 128))
-+ pdadc_out[n_pdadc++] = pcdacL[s++];
-+
-+ /* need to extrapolate above this pdgain? */
-+ if (target_idx <= max_idx)
-+ continue;
-
-- txpower = AR5K_TUNE_DEFAULT_TXPOWER * 2;
-- if (max_power > txpower)
-- txpower = max_power > AR5K_TUNE_MAX_TXPOWER ?
-- AR5K_TUNE_MAX_TXPOWER : max_power;
-+ if (pcdacL[vpd_size - 1] > pcdacL[vpd_size - 2])
-+ vpd_step = pcdacL[vpd_size - 1] - pcdacL[vpd_size - 2];
-+ else
-+ vpd_step = 1;
-
-- for (i = 0; i < AR5K_MAX_RATES; i++)
-- rates[i] = txpower;
-+ while ((s < (s16) target_idx) && (n_pdadc < 128)) {
-+ int tmp = pcdacL[vpd_size - 1] +
-+ (s - max_idx) * vpd_step;
-+ pdadc_out[n_pdadc++] = (tmp > 127) ? 127 : tmp;
-+ s++;
-+ }
-+ }
-
-- /* XXX setup target powers by rate */
-+ while (i < AR5K_EEPROM_N_PD_GAINS) {
-+ gain_boundaries[i] = gain_boundaries[i - 1];
-+ i++;
-+ }
-+
-+ while (n_pdadc < 128) {
-+ pdadc_out[n_pdadc] = pdadc_out[n_pdadc - 1];
-+ n_pdadc++;
-+ }
-+
-+ /* select the right xpdgain curves */
-+ reg = ath5k_hw_reg_read(ah, AR5K_PHY_TPC_RG1);
-+ reg &= ~(AR5K_PHY_TPC_RG1_PDGAIN_1 |
-+ AR5K_PHY_TPC_RG1_PDGAIN_2 |
-+ AR5K_PHY_TPC_RG1_PDGAIN_3 |
-+ AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
-+ reg |= AR5K_REG_SM(n_xpd, AR5K_PHY_TPC_RG1_NUM_PD_GAIN);
-+ switch(n_xpd) {
-+ case 3:
-+ reg |= AR5K_REG_SM(xpd[2], AR5K_PHY_TPC_RG1_PDGAIN_3);
-+ /* fall through */
-+ case 2:
-+ reg |= AR5K_REG_SM(xpd[1], AR5K_PHY_TPC_RG1_PDGAIN_2);
-+ /* fall through */
-+ case 1:
-+ reg |= AR5K_REG_SM(xpd[0], AR5K_PHY_TPC_RG1_PDGAIN_1);
-+ break;
-+ }
-+ ath5k_hw_reg_write(ah, reg, AR5K_PHY_TPC_RG1);
-
-+ /*
-+ * Write TX power values
-+ */
-+ reg = AR5K_PHY_PDADC_TXPOWER_BASE;
-+ for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
-+ ath5k_hw_reg_write(ah,
-+ ((pdadc_out[4*i + 0] & 0xff) << 0) |
-+ ((pdadc_out[4*i + 1] & 0xff) << 8) |
-+ ((pdadc_out[4*i + 2] & 0xff) << 16) |
-+ ((pdadc_out[4*i + 3] & 0xff) << 24), reg);
-+ reg += 4;
-+ }
-+
-+ ath5k_hw_reg_write(ah,
-+ AR5K_REG_SM(gain_overlap,
-+ AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP) |
-+ AR5K_REG_SM(gain_boundaries[0],
-+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_1) |
-+ AR5K_REG_SM(gain_boundaries[1],
-+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_2) |
-+ AR5K_REG_SM(gain_boundaries[2],
-+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_3) |
-+ AR5K_REG_SM(gain_boundaries[3],
-+ AR5K_PHY_TPC_RG5_PD_GAIN_BOUNDARY_4),
-+ AR5K_PHY_TPC_RG5);
-+
-+ ah->ah_txpower.txp_offset = pmin_t2[0];
-+
-+ /* look up power boundaries for this channel */
-+ ch_pmin = ath5k_get_min_power_2413(pcinfo_l);
-+ ch_pmax = ath5k_get_max_power_2413(pcinfo_l);
-+
-+ if (pcinfo_l != pcinfo_r) {
-+ s16 pwr_r;
-+
-+ pwr_r = ath5k_get_min_power_2413(pcinfo_r);
-+ ch_pmin = ath5k_interpolate_signed(ch->center_freq,
-+ pcinfo_l->freq, pcinfo_r->freq,
-+ ch_pmin, pwr_r);
-+
-+ pwr_r = ath5k_get_max_power_2413(pcinfo_r);
-+ ch_pmax = ath5k_interpolate_signed(ch->center_freq,
-+ pcinfo_l->freq, pcinfo_r->freq,
-+ ch_pmax, pwr_r);
-+ }
-+ ah->ah_txpower.txp_min = ch_pmin;
-+ ah->ah_txpower.txp_max = ch_pmax;
-+
-+ return 0;
-+}
-+
-+static void
-+ath5k_setup_rate_table(struct ath5k_hw *ah, u16 max_pwr,
-+ struct ath5k_rate_pcal_info *rate_info)
-+{
-+ unsigned int i;
-+ u16 *rates;
-+
-+ max_pwr *= 2;
-+ max_pwr = min(max_pwr, (u16) ah->ah_txpower.txp_max);
-+
-+ /* apply rate limits */
-+ rates = ah->ah_txpower.txp_rates;
-+ for (i = 0; i < 5; i++) {
-+ rates[i] = min(max_pwr, rate_info->target_power_6to24);
-+ }
-+ rates[5] = min(rates[0], rate_info->target_power_36);
-+ rates[6] = min(rates[0], rate_info->target_power_48);
-+ rates[7] = min(rates[0], rate_info->target_power_54);
-+ rates[8] = min(rates[0], rate_info->target_power_6to24);
-+ rates[9] = min(rates[0], rate_info->target_power_36);
-+ rates[10] = min(rates[0], rate_info->target_power_36);
-+ rates[11] = min(rates[0], rate_info->target_power_48);
-+ rates[12] = min(rates[0], rate_info->target_power_48);
-+ rates[13] = min(rates[0], rate_info->target_power_54);
-+ rates[14] = min(rates[0], rate_info->target_power_54);
-+
-+ ah->ah_txpower.txp_tpc = max_pwr;
- ah->ah_txpower.txp_min = rates[7];
-- ah->ah_txpower.txp_max = rates[0];
-- ah->ah_txpower.txp_ofdm = rates[0];
-+ ah->ah_txpower.txp_max = min(ah->ah_txpower.txp_max,
-+ (s16) rate_info->target_power_36);
-+ ah->ah_txpower.txp_ofdm = ah->ah_txpower.txp_max;
-+}
-+
-+static int
-+ath5k_txpower_table(struct ath5k_hw *ah, struct ieee80211_channel *ch,
-+ struct ath5k_chan_pcal_info *pcinfo_l,
-+ struct ath5k_chan_pcal_info *pcinfo_r,
-+ u16 max_pwr)
-+{
-+ unsigned int i, min, max, n;
-
-- /* Calculate the power table */
- n = ARRAY_SIZE(ah->ah_txpower.txp_pcdac);
- min = AR5K_EEPROM_PCDAC_START;
- max = AR5K_EEPROM_PCDAC_STOP;
-@@ -1473,51 +1892,64 @@ static void ath5k_txpower_table(struct a
- #else
- min;
- #endif
-+
-+ /*
-+ * Write TX power values
-+ */
-+ for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
-+ ath5k_hw_reg_write(ah,
-+ ((((ah->ah_txpower.txp_pcdac[(i << 1) + 1] << 8) |
-+ 0xff) & 0xffff) << 16) |
-+ (((ah->ah_txpower.txp_pcdac[(i << 1) ] << 8) |
-+ 0xff) & 0xffff),
-+ AR5K_PHY_PCDAC_TXPOWER(i));
-+ }
-+ return 0;
- }
-
-+
- /*
- * Set transmition power
- */
--int /*O.K. - txpower_table is unimplemented so this doesn't work*/
-+int
- ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel,
- unsigned int txpower)
- {
-+ struct ath5k_chan_pcal_info *pcinfo_l, *pcinfo_r;
-+ struct ath5k_rate_pcal_info rate_info;
- bool tpc = ah->ah_txpower.txp_tpc;
-- unsigned int i;
-
- ATH5K_TRACE(ah->ah_sc);
- if (txpower > AR5K_TUNE_MAX_TXPOWER) {
- ATH5K_ERR(ah->ah_sc, "invalid tx power: %u\n", txpower);
- return -EINVAL;
- }
--
-- /*
-- * RF2413 for some reason can't
-- * transmit anything if we call
-- * this funtion, so we skip it
-- * until we fix txpower.
-- *
-- * XXX: Assume same for RF2425
-- * to be safe.
-- */
-- if ((ah->ah_radio == AR5K_RF2413) || (ah->ah_radio == AR5K_RF2425))
-- return 0;
-+ if (txpower == 0)
-+ txpower = AR5K_TUNE_MAX_TXPOWER;
-
- /* Reset TX power values */
- memset(&ah->ah_txpower, 0, sizeof(ah->ah_txpower));
- ah->ah_txpower.txp_tpc = tpc;
-+ ah->ah_txpower.txp_min = 0;
-+ ah->ah_txpower.txp_max = AR5K_TUNE_MAX_TXPOWER;
-
-- /* Initialize TX power table */
-- ath5k_txpower_table(ah, channel, txpower);
-+ /* find matching frequency info */
-+ ath5k_get_freq_tables(ah, channel, &pcinfo_l, &pcinfo_r, &rate_info);
-+ ath5k_setup_rate_table(ah, txpower, &rate_info);
-
-- /*
-- * Write TX power values
-- */
-- for (i = 0; i < (AR5K_EEPROM_POWER_TABLE_SIZE / 2); i++) {
-- ath5k_hw_reg_write(ah,
-- ((((ah->ah_txpower.txp_pcdac[(i << 1) + 1] << 8) | 0xff) & 0xffff) << 16) |
-- (((ah->ah_txpower.txp_pcdac[(i << 1) ] << 8) | 0xff) & 0xffff),
-- AR5K_PHY_PCDAC_TXPOWER(i));
-+ /* Initialize TX power table */
-+ switch(ah->ah_radio) {
-+ case AR5K_RF2413:
-+ case AR5K_RF5413:
-+ ath5k_txpower_table_2413(ah, channel, pcinfo_l, pcinfo_r);
-+ break;
-+ case AR5K_RF2425:
-+ /* unimplemented */
-+ return 0;
-+ default:
-+ /* Default power table */
-+ ath5k_txpower_table(ah, channel, pcinfo_l, pcinfo_r, txpower);
-+ break;
- }
-
- ath5k_hw_reg_write(ah, AR5K_TXPOWER_OFDM(3, 24) |
-@@ -1536,12 +1968,19 @@ ath5k_hw_txpower(struct ath5k_hw *ah, st
- AR5K_TXPOWER_CCK(13, 16) | AR5K_TXPOWER_CCK(12, 8) |
- AR5K_TXPOWER_CCK(11, 0), AR5K_PHY_TXPOWER_RATE4);
-
-- if (ah->ah_txpower.txp_tpc)
-+ if (ah->ah_txpower.txp_tpc) {
- ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX_TPC_ENABLE |
- AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
-- else
-+
-+ ath5k_hw_reg_write(ah,
-+ AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_ACK) |
-+ AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CTS) |
-+ AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP),
-+ AR5K_TPC);
-+ } else {
- ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX |
- AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX);
-+ }
-
- return 0;
- }
---- a/drivers/net/wireless/ath5k/ath5k.h
-+++ b/drivers/net/wireless/ath5k/ath5k.h
-@@ -204,7 +204,7 @@
- #define AR5K_TUNE_CWMAX_11B 1023
- #define AR5K_TUNE_CWMAX_XR 7
- #define AR5K_TUNE_NOISE_FLOOR -72
--#define AR5K_TUNE_MAX_TXPOWER 60
-+#define AR5K_TUNE_MAX_TXPOWER 63
- #define AR5K_TUNE_DEFAULT_TXPOWER 30
- #define AR5K_TUNE_TPC_TXPOWER true
- #define AR5K_TUNE_ANT_DIVERSITY true
-@@ -1085,11 +1085,23 @@ struct ath5k_hw {
- struct ath5k_gain ah_gain;
- u8 ah_offset[AR5K_MAX_RF_BANKS];
-
-+
- struct {
-- u16 txp_pcdac[AR5K_EEPROM_POWER_TABLE_SIZE];
-+ union {
-+ struct {
-+ /* Temporary PCDAC tables for interpolation */
-+ u16 pcdacL[AR5K_EEPROM_N_PD_GAINS]
-+ [AR5K_EEPROM_POWER_TABLE_SIZE];
-+ u16 pcdacR[AR5K_EEPROM_N_PD_GAINS]
-+ [AR5K_EEPROM_POWER_TABLE_SIZE];
-+ } rf2413;
-+ } txp_rfdata;
-+ u16 txp_xpd[AR5K_EEPROM_N_XPD_PER_CHANNEL];
-+ u16 txp_pcdac[AR5K_EEPROM_POWER_TABLE_SIZE * 2];
- u16 txp_rates[AR5K_MAX_RATES];
- s16 txp_min;
- s16 txp_max;
-+ s16 txp_offset;
- bool txp_tpc;
- s16 txp_ofdm;
- } ah_txpower;
---- a/drivers/net/wireless/ath5k/reg.h
-+++ b/drivers/net/wireless/ath5k/reg.h
-@@ -1552,6 +1552,15 @@
-
-
- /*===5212 Specific PCU registers===*/
-+#define AR5K_TPC 0x80e8
-+#define AR5K_TPC_ACK 0x0000003f /* ack frames */
-+#define AR5K_TPC_ACK_S 0
-+#define AR5K_TPC_CTS 0x00003f00 /* cts frames */
-+#define AR5K_TPC_CTS_S 8
-+#define AR5K_TPC_CHIRP 0x003f0000 /* chirp frames */
-+#define AR5K_TPC_CHIRP_S 16
-+#define AR5K_TPC_DOPPLER 0x0f000000 /* doppler chirp span */
-+#define AR5K_TPC_DOPPLER_S 24
-
- /*
- * XR (eXtended Range) mode register
-@@ -2550,6 +2559,12 @@
- #define AR5K_PHY_TPC_RG1 0xa258
- #define AR5K_PHY_TPC_RG1_NUM_PD_GAIN 0x0000c000
- #define AR5K_PHY_TPC_RG1_NUM_PD_GAIN_S 14
-+#define AR5K_PHY_TPC_RG1_PDGAIN_1 0x00030000
-+#define AR5K_PHY_TPC_RG1_PDGAIN_1_S 16
-+#define AR5K_PHY_TPC_RG1_PDGAIN_2 0x000c0000
-+#define AR5K_PHY_TPC_RG1_PDGAIN_2_S 18
-+#define AR5K_PHY_TPC_RG1_PDGAIN_3 0x00300000
-+#define AR5K_PHY_TPC_RG1_PDGAIN_3_S 20
-
- #define AR5K_PHY_TPC_RG5 0xa26C
- #define AR5K_PHY_TPC_RG5_PD_GAIN_OVERLAP 0x0000000F
---- a/drivers/net/wireless/ath5k/desc.c
-+++ b/drivers/net/wireless/ath5k/desc.c
-@@ -194,6 +194,10 @@ static int ath5k_hw_setup_4word_tx_desc(
- return -EINVAL;
- }
-
-+ tx_power += ah->ah_txpower.txp_offset;
-+ if (tx_power > AR5K_TUNE_MAX_TXPOWER)
-+ tx_power = AR5K_TUNE_MAX_TXPOWER;
-+
- /* Clear descriptor */
- memset(&desc->ud.ds_tx5212, 0, sizeof(struct ath5k_hw_5212_tx_desc));
-