From: Felix Fietkau <nbd@openwrt.org>
To: linux-wireless <linux-wireless@vger.kernel.org>
Cc: Nick Kossifidis <mickflemm@gmail.com>, Bob Copeland <me@bobcopeland.com>
Subject: Re: [PATCH,RFC 2/2] ath5k: implement tx power calibration on ar2413/5413
Date: Fri, 30 Jan 2009 03:48:05 +0100 [thread overview]
Message-ID: <49826A65.30106@openwrt.org> (raw)
In-Reply-To: <498269D5.7080204@openwrt.org>
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-2007 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
@@ -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));
next prev parent reply other threads:[~2009-01-30 2:48 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-01-30 2:45 [PATCH,RFC 0/2] ath5k: implement tx power calibration on ar2413/5413 Felix Fietkau
2009-01-30 2:46 ` [PATCH,RFC 1/2] ath5k: extract more data from the eeprom for 2413 power calibration Felix Fietkau
2009-01-30 2:48 ` Felix Fietkau [this message]
2009-01-30 8:17 ` [PATCH,RFC 0/2] ath5k: implement tx power calibration on ar2413/5413 Nick Kossifidis
2009-01-30 14:47 ` Bob Copeland
2009-01-30 15:08 ` Nick Kossifidis
2009-01-30 13:39 ` Bob Copeland
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=49826A65.30106@openwrt.org \
--to=nbd@openwrt.org \
--cc=linux-wireless@vger.kernel.org \
--cc=me@bobcopeland.com \
--cc=mickflemm@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.