2009-01-30 02:45:50

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH,RFC 0/2] ath5k: implement tx power calibration on ar2413/5413

Hi,

I got my AR2413 patches to the point where ath5k properly
initializes the PDGAIN curves (register dumps verified against
what the madwifi HAL does), and it does Tx at much better
power level and with 54 Mbit (tested on an AR5413 card).
Power still isn't as good as on madwifi yet, but much closer
than before, and I actually get >20Mbit/s throughput now.

Enjoy!

- Felix


2009-01-30 02:46:51

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH,RFC 1/2] ath5k: extract more data from the eeprom for 2413 power calibration

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 <[email protected]>

--- 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 {


2009-01-30 08:17:53

by Nick Kossifidis

[permalink] [raw]
Subject: Re: [PATCH,RFC 0/2] ath5k: implement tx power calibration on ar2413/5413

2009/1/30 Felix Fietkau <[email protected]>:
> Hi,
>
> I got my AR2413 patches to the point where ath5k properly
> initializes the PDGAIN curves (register dumps verified against
> what the madwifi HAL does), and it does Tx at much better
> power level and with 54 Mbit (tested on an AR5413 card).
> Power still isn't as good as on madwifi yet, but much closer
> than before, and I actually get >20Mbit/s throughput now.
>
> Enjoy!
>
> - Felix
>

You are the man !

I'm sending some patches that update phy.c, initvals.c and reset.c etc
today and i'll merge this one with RF5111/RF5112 pcdac stuff and tpc
support asap to have power table support for all chips. We also need
to work out regulatory stuff before we fully enable tx power support
so i have to test this on a spectrum analyzer.

Just give me some time and i'll come back with code ;-)

--
GPG ID: 0xD21DB2DB
As you read this post global entropy rises. Have Fun ;-)
Nick

2009-01-30 02:48:09

by Felix Fietkau

[permalink] [raw]
Subject: Re: [PATCH,RFC 2/2] ath5k: implement tx power calibration on ar2413/5413

Implement the power curve interpolation, which is required for
proper tx on 2413 and newer RF designs.

Signed-off-by: Felix Fietkau <[email protected]>

--- a/drivers/net/wireless/ath5k/phy.c
+++ b/drivers/net/wireless/ath5k/phy.c
@@ -4,6 +4,7 @@
* Copyright (c) 2004-2007 Reyk Floeter <[email protected]>
* Copyright (c) 2006-2007 Nick Kossifidis <[email protected]>
* Copyright (c) 2007-2008 Jiri Slaby <[email protected]>
+ * Copyright (c) 2008-2009 Felix Fietkau <[email protected]>
*
* 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));


2009-01-30 14:47:47

by Bob Copeland

[permalink] [raw]
Subject: Re: [PATCH,RFC 0/2] ath5k: implement tx power calibration on ar2413/5413

On Fri, Jan 30, 2009 at 3:17 AM, Nick Kossifidis <[email protected]> wrote:
> I'm sending some patches that update phy.c, initvals.c and reset.c etc
> today and i'll merge this one with RF5111/RF5112 pcdac stuff and tpc
> support asap to have power table support for all chips. We also need
> to work out regulatory stuff before we fully enable tx power support
> so i have to test this on a spectrum analyzer.

Speaking of regulatory, I had started adding the 802.11d stuff from
ath9k already, but I haven't gotten to the point of even compiling it
yet. Want me to continue on it? Probably can have something to show
this weekend.

--
Bob Copeland %% http://www.bobcopeland.com

2009-01-30 15:08:08

by Nick Kossifidis

[permalink] [raw]
Subject: Re: [PATCH,RFC 0/2] ath5k: implement tx power calibration on ar2413/5413

2009/1/30 Bob Copeland <[email protected]>:
> On Fri, Jan 30, 2009 at 3:17 AM, Nick Kossifidis <[email protected]> wrote:
>> I'm sending some patches that update phy.c, initvals.c and reset.c etc
>> today and i'll merge this one with RF5111/RF5112 pcdac stuff and tpc
>> support asap to have power table support for all chips. We also need
>> to work out regulatory stuff before we fully enable tx power support
>> so i have to test this on a spectrum analyzer.
>
> Speaking of regulatory, I had started adding the 802.11d stuff from
> ath9k already, but I haven't gotten to the point of even compiling it
> yet. Want me to continue on it? Probably can have something to show
> this weekend.
>

That 'll be great !!


--
GPG ID: 0xD21DB2DB
As you read this post global entropy rises. Have Fun ;-)
Nick

2009-01-30 13:39:32

by Bob Copeland

[permalink] [raw]
Subject: Re: [PATCH,RFC 0/2] ath5k: implement tx power calibration on ar2413/5413

On Fri, Jan 30, 2009 at 03:45:41AM +0100, Felix Fietkau wrote:
> Hi,
>
> I got my AR2413 patches to the point where ath5k properly
> initializes the PDGAIN curves (register dumps verified against
> what the madwifi HAL does), and it does Tx at much better
> power level and with 54 Mbit (tested on an AR5413 card).
> Power still isn't as good as on madwifi yet, but much closer
> than before, and I actually get >20Mbit/s throughput now.

Very cool! I didn't notice huge change in my environment with
a few iperf runs, however it is at least as good as before
(around 14 Mbit/s for tcp). And it looks like you fixed all
the warnings, so that is good :)

--
Bob Copeland %% http://www.bobcopeland.com