diff options
Diffstat (limited to 'package/mac80211/patches/202-ath5k_txpower_2413.patch')
-rw-r--r-- | package/mac80211/patches/202-ath5k_txpower_2413.patch | 672 |
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)); - |