Implement the power curve interpolation, which is required for proper tx on 2413 and newer RF designs. Signed-off-by: Felix Fietkau --- a/drivers/net/wireless/ath5k/phy.c +++ b/drivers/net/wireless/ath5k/phy.c @@ -4,6 +4,7 @@ * Copyright (c) 2004-2007 Reyk Floeter * Copyright (c) 2006-2007 Nick Kossifidis * Copyright (c) 2007-2008 Jiri Slaby + * Copyright (c) 2008-2009 Felix Fietkau * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -2383,31 +2384,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; + + if (pcdacL[vpd_size - 1] > pcdacL[vpd_size - 2]) + vpd_step = pcdacL[vpd_size - 1] - pcdacL[vpd_size - 2]; + else + vpd_step = 1; + + 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++; + } + } + + 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); - txpower = AR5K_TUNE_DEFAULT_TXPOWER * 2; - if (max_power > txpower) - txpower = max_power > AR5K_TUNE_MAX_TXPOWER ? - AR5K_TUNE_MAX_TXPOWER : max_power; + /* + * Write TX power values + */ + reg = AR5K_PHY_PCDAC_TXPOWER_BASE_2413; + 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; - for (i = 0; i < AR5K_MAX_RATES; i++) - rates[i] = txpower; + return 0; +} - /* XXX setup target powers by rate */ +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; @@ -2418,51 +2837,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) | @@ -2481,12 +2913,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 @@ -207,7 +207,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 @@ -1115,11 +1115,23 @@ struct ath5k_hw { struct ath5k_gain ah_gain; u32 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 @@ -1549,6 +1549,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 @@ -2578,6 +2587,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));