1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-10-04 04:32:00 +03:00

add some work-in-progress patches for ath5k stability and performance

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@14277 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
nbd 2009-01-30 02:52:21 +00:00
parent 7eba7887af
commit 53f985729d
3 changed files with 1203 additions and 0 deletions

View File

@ -0,0 +1,37 @@
When freeing rx dma descriptors, use the right buffer size.
Fixes kernel oopses on module unload on ixp4xx and most likely
other platforms as well.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
--- a/drivers/net/wireless/ath5k/base.c
+++ b/drivers/net/wireless/ath5k/base.c
@@ -308,6 +308,19 @@ static inline void ath5k_txbuf_free(stru
bf->skb = NULL;
}
+static inline void ath5k_rxbuf_free(struct ath5k_softc *sc,
+ struct ath5k_buf *bf)
+{
+ BUG_ON(!bf);
+ if (!bf->skb)
+ return;
+ pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb_any(bf->skb);
+ bf->skb = NULL;
+}
+
+
/* Queues setup */
static struct ath5k_txq *ath5k_txq_setup(struct ath5k_softc *sc,
int qtype, int subtype);
@@ -1341,7 +1354,7 @@ ath5k_desc_free(struct ath5k_softc *sc,
list_for_each_entry(bf, &sc->txbuf, list)
ath5k_txbuf_free(sc, bf);
list_for_each_entry(bf, &sc->rxbuf, list)
- ath5k_txbuf_free(sc, bf);
+ ath5k_rxbuf_free(sc, bf);
/* Free memory associated with all descriptors */
pci_free_consistent(pdev, sc->desc_len, sc->desc, sc->desc_daddr);

View File

@ -0,0 +1,494 @@
Clean up the eeprom parsing code and prepare the pdgain
data for 2413, which will be required for power calibration code.
Also clean up some ugly line wrapping to make the code easier on
the eyes.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
--- a/drivers/net/wireless/ath5k/eeprom.c
+++ b/drivers/net/wireless/ath5k/eeprom.c
@@ -541,31 +541,30 @@ ath5k_eeprom_read_freq_list(struct ath5k
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
int o = *offset;
- int i = 0;
+ int i;
u8 freq1, freq2;
int ret;
u16 val;
+ ee->ee_n_piers[mode] = 0;
while(i < max) {
AR5K_EEPROM_READ(o++, val);
- freq1 = (val >> 8) & 0xff;
- freq2 = val & 0xff;
-
- if (freq1) {
- pc[i++].freq = ath5k_eeprom_bin2freq(ee,
- freq1, mode);
- ee->ee_n_piers[mode]++;
- }
+ freq1 = val & 0xff;
+ if (!freq1)
+ break;
- if (freq2) {
- pc[i++].freq = ath5k_eeprom_bin2freq(ee,
- freq2, mode);
- ee->ee_n_piers[mode]++;
- }
+ pc[i++].freq = ath5k_eeprom_bin2freq(ee,
+ freq1, mode);
+ ee->ee_n_piers[mode]++;
- if (!freq1 || !freq2)
+ freq2 = (val >> 8) & 0xff;
+ if (!freq2)
break;
+
+ pc[i++].freq = ath5k_eeprom_bin2freq(ee,
+ freq2, mode);
+ ee->ee_n_piers[mode]++;
}
/* return new offset */
@@ -918,84 +917,46 @@ ath5k_cal_data_offset_2413(struct ath5k_
* curves on eeprom. The final curve (higher power) has an extra
* point for better accuracy like RF5112.
*/
+
static int
-ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
+ath5k_eeprom_parse_pcal_info_2413(struct ath5k_hw *ah, int mode, u32 offset,
+ struct ath5k_chan_pcal_info *chinfo)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
- struct ath5k_chan_pcal_info_rf2413 *chan_pcal_info;
- struct ath5k_chan_pcal_info *gen_chan_info;
- unsigned int i, c;
- u32 offset;
+ struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+ unsigned int i;
int ret;
u16 val;
- u8 pd_gains = 0;
-
- if (ee->ee_x_gain[mode] & 0x1) pd_gains++;
- if ((ee->ee_x_gain[mode] >> 1) & 0x1) pd_gains++;
- if ((ee->ee_x_gain[mode] >> 2) & 0x1) pd_gains++;
- if ((ee->ee_x_gain[mode] >> 3) & 0x1) pd_gains++;
- ee->ee_pd_gains[mode] = pd_gains;
+ u8 pd_gains;
- offset = ath5k_cal_data_offset_2413(ee, mode);
- ee->ee_n_piers[mode] = 0;
- switch (mode) {
- case AR5K_EEPROM_MODE_11A:
- if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
- return 0;
-
- ath5k_eeprom_init_11a_pcal_freq(ah, offset);
- offset += AR5K_EEPROM_N_5GHZ_CHAN / 2;
- gen_chan_info = ee->ee_pwr_cal_a;
- break;
- case AR5K_EEPROM_MODE_11B:
- if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
- return 0;
-
- ath5k_eeprom_init_11bg_2413(ah, mode, offset);
- offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
- gen_chan_info = ee->ee_pwr_cal_b;
- break;
- case AR5K_EEPROM_MODE_11G:
- if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
- return 0;
-
- ath5k_eeprom_init_11bg_2413(ah, mode, offset);
- offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
- gen_chan_info = ee->ee_pwr_cal_g;
- break;
- default:
- return -EINVAL;
- }
+ pd_gains = ee->ee_pd_gains[mode];
if (pd_gains == 0)
return 0;
for (i = 0; i < ee->ee_n_piers[mode]; i++) {
- chan_pcal_info = &gen_chan_info[i].rf2413_info;
+ pcinfo = &chinfo[i].rf2413_info;
/*
* Read pwr_i, pddac_i and the first
* 2 pd points (pwr, pddac)
*/
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr_i[0] = val & 0x1f;
- chan_pcal_info->pddac_i[0] = (val >> 5) & 0x7f;
- chan_pcal_info->pwr[0][0] =
- (val >> 12) & 0xf;
+ pcinfo->pwr_i[0] = val & 0x1f;
+ pcinfo->pddac_i[0] = (val >> 5) & 0x7f;
+ pcinfo->pwr[0][0] = (val >> 12) & 0xf;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[0][0] = val & 0x3f;
- chan_pcal_info->pwr[0][1] = (val >> 6) & 0xf;
- chan_pcal_info->pddac[0][1] =
- (val >> 10) & 0x3f;
+ pcinfo->pddac[0][0] = val & 0x3f;
+ pcinfo->pwr[0][1] = (val >> 6) & 0xf;
+ pcinfo->pddac[0][1] = (val >> 10) & 0x3f;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr[0][2] = val & 0xf;
- chan_pcal_info->pddac[0][2] =
- (val >> 4) & 0x3f;
+ pcinfo->pwr[0][2] = val & 0xf;
+ pcinfo->pddac[0][2] = (val >> 4) & 0x3f;
- chan_pcal_info->pwr[0][3] = 0;
- chan_pcal_info->pddac[0][3] = 0;
+ pcinfo->pwr[0][3] = 0;
+ pcinfo->pddac[0][3] = 0;
if (pd_gains > 1) {
/*
@@ -1003,44 +964,36 @@ ath5k_eeprom_read_pcal_info_2413(struct
* so it only has 2 pd points.
* Continue wih pd gain 1.
*/
- chan_pcal_info->pwr_i[1] = (val >> 10) & 0x1f;
+ pcinfo->pwr_i[1] = (val >> 10) & 0x1f;
- chan_pcal_info->pddac_i[1] = (val >> 15) & 0x1;
+ pcinfo->pddac_i[1] = (val >> 15) & 0x1;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac_i[1] |= (val & 0x3F) << 1;
+ pcinfo->pddac_i[1] |= (val & 0x3F) << 1;
- chan_pcal_info->pwr[1][0] = (val >> 6) & 0xf;
- chan_pcal_info->pddac[1][0] =
- (val >> 10) & 0x3f;
+ pcinfo->pwr[1][0] = (val >> 6) & 0xf;
+ pcinfo->pddac[1][0] = (val >> 10) & 0x3f;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr[1][1] = val & 0xf;
- chan_pcal_info->pddac[1][1] =
- (val >> 4) & 0x3f;
- chan_pcal_info->pwr[1][2] =
- (val >> 10) & 0xf;
+ pcinfo->pwr[1][1] = val & 0xf;
+ pcinfo->pddac[1][1] = (val >> 4) & 0x3f;
+ pcinfo->pwr[1][2] = (val >> 10) & 0xf;
- chan_pcal_info->pddac[1][2] =
- (val >> 14) & 0x3;
+ pcinfo->pddac[1][2] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[1][2] |=
- (val & 0xF) << 2;
+ pcinfo->pddac[1][2] |= (val & 0xF) << 2;
- chan_pcal_info->pwr[1][3] = 0;
- chan_pcal_info->pddac[1][3] = 0;
+ pcinfo->pwr[1][3] = 0;
+ pcinfo->pddac[1][3] = 0;
} else if (pd_gains == 1) {
/*
* Pd gain 0 is the last one so
* read the extra point.
*/
- chan_pcal_info->pwr[0][3] =
- (val >> 10) & 0xf;
+ pcinfo->pwr[0][3] = (val >> 10) & 0xf;
- chan_pcal_info->pddac[0][3] =
- (val >> 14) & 0x3;
+ pcinfo->pddac[0][3] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[0][3] |=
- (val & 0xF) << 2;
+ pcinfo->pddac[0][3] |= (val & 0xF) << 2;
}
/*
@@ -1048,105 +1001,159 @@ ath5k_eeprom_read_pcal_info_2413(struct
* as above.
*/
if (pd_gains > 2) {
- chan_pcal_info->pwr_i[2] = (val >> 4) & 0x1f;
- chan_pcal_info->pddac_i[2] = (val >> 9) & 0x7f;
+ pcinfo->pwr_i[2] = (val >> 4) & 0x1f;
+ pcinfo->pddac_i[2] = (val >> 9) & 0x7f;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr[2][0] =
- (val >> 0) & 0xf;
- chan_pcal_info->pddac[2][0] =
- (val >> 4) & 0x3f;
- chan_pcal_info->pwr[2][1] =
- (val >> 10) & 0xf;
-
- chan_pcal_info->pddac[2][1] =
- (val >> 14) & 0x3;
- AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[2][1] |=
- (val & 0xF) << 2;
-
- chan_pcal_info->pwr[2][2] =
- (val >> 4) & 0xf;
- chan_pcal_info->pddac[2][2] =
- (val >> 8) & 0x3f;
+ pcinfo->pwr[2][0] = (val >> 0) & 0xf;
+ pcinfo->pddac[2][0] = (val >> 4) & 0x3f;
+ pcinfo->pwr[2][1] = (val >> 10) & 0xf;
- chan_pcal_info->pwr[2][3] = 0;
- chan_pcal_info->pddac[2][3] = 0;
+ pcinfo->pddac[2][1] = (val >> 14) & 0x3;
+ AR5K_EEPROM_READ(offset++, val);
+ pcinfo->pddac[2][1] |= (val & 0xF) << 2;
+
+ pcinfo->pwr[2][2] = (val >> 4) & 0xf;
+ pcinfo->pddac[2][2] = (val >> 8) & 0x3f;
+
+ pcinfo->pwr[2][3] = 0;
+ pcinfo->pddac[2][3] = 0;
} else if (pd_gains == 2) {
- chan_pcal_info->pwr[1][3] =
- (val >> 4) & 0xf;
- chan_pcal_info->pddac[1][3] =
- (val >> 8) & 0x3f;
+ pcinfo->pwr[1][3] = (val >> 4) & 0xf;
+ pcinfo->pddac[1][3] = (val >> 8) & 0x3f;
}
if (pd_gains > 3) {
- chan_pcal_info->pwr_i[3] = (val >> 14) & 0x3;
+ pcinfo->pwr_i[3] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr_i[3] |= ((val >> 0) & 0x7) << 2;
+ pcinfo->pwr_i[3] |= ((val >> 0) & 0x7) << 2;
- chan_pcal_info->pddac_i[3] = (val >> 3) & 0x7f;
- chan_pcal_info->pwr[3][0] =
- (val >> 10) & 0xf;
- chan_pcal_info->pddac[3][0] =
- (val >> 14) & 0x3;
+ pcinfo->pddac_i[3] = (val >> 3) & 0x7f;
+ pcinfo->pwr[3][0] = (val >> 10) & 0xf;
+ pcinfo->pddac[3][0] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[3][0] |=
- (val & 0xF) << 2;
- chan_pcal_info->pwr[3][1] =
- (val >> 4) & 0xf;
- chan_pcal_info->pddac[3][1] =
- (val >> 8) & 0x3f;
+ pcinfo->pddac[3][0] |= (val & 0xF) << 2;
+ pcinfo->pwr[3][1] = (val >> 4) & 0xf;
+ pcinfo->pddac[3][1] = (val >> 8) & 0x3f;
- chan_pcal_info->pwr[3][2] =
- (val >> 14) & 0x3;
+ pcinfo->pwr[3][2] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr[3][2] |=
- ((val >> 0) & 0x3) << 2;
+ pcinfo->pwr[3][2] |= ((val >> 0) & 0x3) << 2;
- chan_pcal_info->pddac[3][2] =
- (val >> 2) & 0x3f;
- chan_pcal_info->pwr[3][3] =
- (val >> 8) & 0xf;
+ pcinfo->pddac[3][2] = (val >> 2) & 0x3f;
+ pcinfo->pwr[3][3] = (val >> 8) & 0xf;
- chan_pcal_info->pddac[3][3] =
- (val >> 12) & 0xF;
+ pcinfo->pddac[3][3] = (val >> 12) & 0xF;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pddac[3][3] |=
- ((val >> 0) & 0x3) << 4;
+ pcinfo->pddac[3][3] |= ((val >> 0) & 0x3) << 4;
} else if (pd_gains == 3) {
- chan_pcal_info->pwr[2][3] =
- (val >> 14) & 0x3;
+ pcinfo->pwr[2][3] = (val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val);
- chan_pcal_info->pwr[2][3] |=
- ((val >> 0) & 0x3) << 2;
+ pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2;
- chan_pcal_info->pddac[2][3] =
- (val >> 2) & 0x3f;
+ pcinfo->pddac[2][3] = (val >> 2) & 0x3f;
}
+ }
+ return 0;
+}
+
+static int
+ath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode,
+ struct ath5k_chan_pcal_info *chinfo,
+ unsigned int *xgains)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+ unsigned int i, j, k;
- for (c = 0; c < pd_gains; c++) {
- /* Recreate pwr table for this channel using pwr steps */
- chan_pcal_info->pwr[c][0] += chan_pcal_info->pwr_i[c] * 2;
- chan_pcal_info->pwr[c][1] += chan_pcal_info->pwr[c][0];
- chan_pcal_info->pwr[c][2] += chan_pcal_info->pwr[c][1];
- chan_pcal_info->pwr[c][3] += chan_pcal_info->pwr[c][2];
- if (chan_pcal_info->pwr[c][3] == chan_pcal_info->pwr[c][2])
- chan_pcal_info->pwr[c][3] = 0;
-
- /* Recreate pddac table for this channel using pddac steps */
- chan_pcal_info->pddac[c][0] += chan_pcal_info->pddac_i[c];
- chan_pcal_info->pddac[c][1] += chan_pcal_info->pddac[c][0];
- chan_pcal_info->pddac[c][2] += chan_pcal_info->pddac[c][1];
- chan_pcal_info->pddac[c][3] += chan_pcal_info->pddac[c][2];
- if (chan_pcal_info->pddac[c][3] == chan_pcal_info->pddac[c][2])
- chan_pcal_info->pddac[c][3] = 0;
+ /* prepare the raw values */
+ for (i = 0; i < ee->ee_n_piers[mode]; i++) {
+ pcinfo = &chinfo[i].rf2413_info;
+ for (j = 0; j < ee->ee_pd_gains[mode]; j++) {
+ unsigned int idx = xgains[j];
+ struct ath5k_pdgain_info *pd = &pcinfo->pdgains[idx];
+
+ /* one more point for the highest power (lowest gain) */
+ if (j == ee->ee_pd_gains[mode] - 1) {
+ pd->n_vpd = AR5K_EEPROM_N_PD_POINTS;
+ } else {
+ pd->n_vpd = AR5K_EEPROM_N_PD_POINTS - 1;
+ }
+
+ pd->vpd[0] = pcinfo->pddac_i[j];
+ pd->pwr_t4[0] = 4 * pcinfo->pwr_i[j];
+ for (k = 1; k < pd->n_vpd; k++) {
+ pd->pwr_t4[k] = pd->pwr_t4[k - 1] + 2 * pcinfo->pwr[j][k - 1];
+ pd->vpd[k] = pd->vpd[k - 1] + pcinfo->pddac[j][k - 1];
+ }
}
}
return 0;
}
+static int
+ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
+{
+ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
+ struct ath5k_chan_pcal_info *chinfo;
+ unsigned int xgains[AR5K_EEPROM_N_PD_GAINS];
+ u32 offset;
+ u8 pd_gains = 0;
+ int i, ret;
+
+ memset(xgains, 0, sizeof(xgains));
+ for (i = 0; i < AR5K_EEPROM_N_PD_GAINS; i++) {
+ int idx = AR5K_EEPROM_N_PD_GAINS - i - 1;
+
+ if ((ee->ee_x_gain[mode] >> idx) & 0x1)
+ xgains[pd_gains++] = idx;
+ }
+ ee->ee_pd_gains[mode] = pd_gains;
+
+ offset = ath5k_cal_data_offset_2413(ee, mode);
+ switch (mode) {
+ case AR5K_EEPROM_MODE_11A:
+ if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
+ return 0;
+
+ ath5k_eeprom_init_11a_pcal_freq(ah, offset);
+ offset += AR5K_EEPROM_N_5GHZ_CHAN / 2;
+ chinfo = ee->ee_pwr_cal_a;
+ break;
+ case AR5K_EEPROM_MODE_11B:
+ if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
+ return 0;
+
+ ath5k_eeprom_init_11bg_2413(ah, mode, offset);
+ offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+ chinfo = ee->ee_pwr_cal_b;
+ break;
+ case AR5K_EEPROM_MODE_11G:
+ if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
+ return 0;
+
+ ath5k_eeprom_init_11bg_2413(ah, mode, offset);
+ offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+ chinfo = ee->ee_pwr_cal_g;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ ret = ath5k_eeprom_parse_pcal_info_2413(ah, mode, offset, chinfo);
+ if (ret)
+ return ret;
+
+ ret = ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo, xgains);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
/*
* Read per rate target power (this is the maximum tx power
* supported by the card). This info is used when setting
@@ -1264,6 +1271,7 @@ ath5k_eeprom_read_pcal_info(struct ath5k
else
read_pcal = ath5k_eeprom_read_pcal_info_5111;
+
for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) {
err = read_pcal(ah, mode);
if (err)
--- a/drivers/net/wireless/ath5k/eeprom.h
+++ b/drivers/net/wireless/ath5k/eeprom.h
@@ -265,15 +265,27 @@ struct ath5k_chan_pcal_info_rf5112 {
u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
};
+
+struct ath5k_pdgain_info {
+ u16 n_vpd;
+ u16 vpd[AR5K_EEPROM_N_PD_POINTS];
+ s16 pwr_t4[AR5K_EEPROM_N_PD_POINTS];
+};
+
struct ath5k_chan_pcal_info_rf2413 {
+ /* --- EEPROM VALUES --- */
/* Starting pwr/pddac values */
- s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
- u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
+ s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
+ u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
/* (pwr,pddac) points */
- s8 pwr[AR5K_EEPROM_N_PD_GAINS]
- [AR5K_EEPROM_N_PD_POINTS];
- u8 pddac[AR5K_EEPROM_N_PD_GAINS]
- [AR5K_EEPROM_N_PD_POINTS];
+ s8 pwr[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_N_PD_POINTS];
+ u8 pddac[AR5K_EEPROM_N_PD_GAINS]
+ [AR5K_EEPROM_N_PD_POINTS];
+
+ /* --- RAW VALUES --- */
+ struct ath5k_pdgain_info pdgains
+ [AR5K_EEPROM_N_PD_GAINS];
};
struct ath5k_chan_pcal_info {

View File

@ -0,0 +1,672 @@
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));