Commit 0ea9c00c authored by Nick Kossifidis's avatar Nick Kossifidis Committed by John W. Linville

ath5k: Update EEPROM code

*Read misc2...6 values from eeprom since we want to use them (fixes
 wrong power calibration info offset on RF2413+ chips)

*Initialize num_piers to 0 for RF2413 chips (note that we read 2GHz
 frequency piers while reading mode sections, we have to ignore them
 -usualy they are 0xff anyway but during my tests i got a 1 on b mode
 with no data- and use the newer eemap.

*Add some more comments (please forgive my poor English ;-( ) and
 some minor code cleanup

*Tested on 2425 and 2112 and has the same data with ath_info (i
 wrote some debug code on debug.c to print everything like ath_info
 but i haven't tested it yet on 5111 and it's full of > 80 col lines,
 if anyone wants to play with it let me know).
Signed-Off-by: default avatarNick Kossifidis <mickflemm@gmail.com>
Acked-by: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 754be309
...@@ -137,6 +137,18 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah) ...@@ -137,6 +137,18 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah)
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) {
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0); AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0);
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1); AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1);
/* XXX: Don't know which versions include these two */
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC2, ee_misc2);
if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3)
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC3, ee_misc3);
if (ee->ee_version >= AR5K_EEPROM_VERSION_5_0) {
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC4, ee_misc4);
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC5, ee_misc5);
AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC6, ee_misc6);
}
} }
if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) { if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) {
...@@ -213,7 +225,8 @@ static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset, ...@@ -213,7 +225,8 @@ static int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset,
} }
/* /*
* Read supported modes from eeprom * Read supported modes and some mode-specific calibration data
* from eeprom
*/ */
static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
unsigned int mode) unsigned int mode)
...@@ -315,6 +328,9 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, ...@@ -315,6 +328,9 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_0) if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_0)
goto done; goto done;
/* Note: >= v5 have bg freq piers on another location
* so these freq piers are ignored for >= v5 (should be 0xff
* anyway) */
switch(mode) { switch(mode) {
case AR5K_EEPROM_MODE_11A: case AR5K_EEPROM_MODE_11A:
if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_1) if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_1)
...@@ -442,7 +458,7 @@ ath5k_eeprom_read_turbo_modes(struct ath5k_hw *ah, ...@@ -442,7 +458,7 @@ ath5k_eeprom_read_turbo_modes(struct ath5k_hw *ah,
return 0; return 0;
} }
/* Read mode-specific data (except power calibration data) */
static int static int
ath5k_eeprom_init_modes(struct ath5k_hw *ah) ath5k_eeprom_init_modes(struct ath5k_hw *ah)
{ {
...@@ -488,6 +504,16 @@ ath5k_eeprom_init_modes(struct ath5k_hw *ah) ...@@ -488,6 +504,16 @@ ath5k_eeprom_init_modes(struct ath5k_hw *ah)
return 0; return 0;
} }
/* Used to match PCDAC steps with power values on RF5111 chips
* (eeprom versions < 4). For RF5111 we have 10 pre-defined PCDAC
* steps that match with the power values we read from eeprom. On
* older eeprom versions (< 3.2) these steps are equaly spaced at
* 10% of the pcdac curve -until the curve reaches it's maximum-
* (10 steps from 0 to 100%) but on newer eeprom versions (>= 3.2)
* these 10 steps are spaced in a different way. This function returns
* the pcdac steps based on eeprom version and curve min/max so that we
* can have pcdac/pwr points.
*/
static inline void static inline void
ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp) ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
{ {
...@@ -507,37 +533,48 @@ ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp) ...@@ -507,37 +533,48 @@ ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
*vp++ = (ip[i] * max + (100 - ip[i]) * min) / 100; *vp++ = (ip[i] * max + (100 - ip[i]) * min) / 100;
} }
/* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff
* frequency mask) */
static inline int static inline int
ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max, ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max,
struct ath5k_chan_pcal_info *pc, u8 *count) struct ath5k_chan_pcal_info *pc, unsigned int mode)
{ {
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
int o = *offset; int o = *offset;
int i = 0; int i = 0;
u8 f1, f2; u8 freq1, freq2;
int ret; int ret;
u16 val; u16 val;
while(i < max) { while(i < max) {
AR5K_EEPROM_READ(o++, val); AR5K_EEPROM_READ(o++, val);
f1 = (val >> 8) & 0xff; freq1 = (val >> 8) & 0xff;
f2 = val & 0xff; freq2 = val & 0xff;
if (f1) if (freq1) {
pc[i++].freq = f1; pc[i++].freq = ath5k_eeprom_bin2freq(ee,
freq1, mode);
ee->ee_n_piers[mode]++;
}
if (f2) if (freq2) {
pc[i++].freq = f2; pc[i++].freq = ath5k_eeprom_bin2freq(ee,
freq2, mode);
ee->ee_n_piers[mode]++;
}
if (!f1 || !f2) if (!freq1 || !freq2)
break; break;
} }
/* return new offset */
*offset = o; *offset = o;
*count = i;
return 0; return 0;
} }
/* Read frequency piers for 802.11a */
static int static int
ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset) ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset)
{ {
...@@ -550,7 +587,7 @@ ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset) ...@@ -550,7 +587,7 @@ ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset)
if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) { if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) {
ath5k_eeprom_read_freq_list(ah, &offset, ath5k_eeprom_read_freq_list(ah, &offset,
AR5K_EEPROM_N_5GHZ_CHAN, pcal, AR5K_EEPROM_N_5GHZ_CHAN, pcal,
&ee->ee_n_piers[AR5K_EEPROM_MODE_11A]); AR5K_EEPROM_MODE_11A);
} else { } else {
mask = AR5K_EEPROM_FREQ_M(ah->ah_ee_version); mask = AR5K_EEPROM_FREQ_M(ah->ah_ee_version);
...@@ -577,23 +614,25 @@ ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset) ...@@ -577,23 +614,25 @@ ath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset)
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
pcal[9].freq |= (val >> 10) & 0x3f; pcal[9].freq |= (val >> 10) & 0x3f;
/* Fixed number of piers */
ee->ee_n_piers[AR5K_EEPROM_MODE_11A] = 10; ee->ee_n_piers[AR5K_EEPROM_MODE_11A] = 10;
}
for(i = 0; i < AR5K_EEPROM_N_5GHZ_CHAN; i += 1) { for (i = 0; i < AR5K_EEPROM_N_5GHZ_CHAN; i++) {
pcal[i].freq = ath5k_eeprom_bin2freq(ee, pcal[i].freq = ath5k_eeprom_bin2freq(ee,
pcal[i].freq, AR5K_EEPROM_MODE_11A); pcal[i].freq, AR5K_EEPROM_MODE_11A);
}
} }
return 0; return 0;
} }
/* Read frequency piers for 802.11bg on eeprom versions >= 5 and eemap >= 2 */
static inline int static inline int
ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset) ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
{ {
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
struct ath5k_chan_pcal_info *pcal; struct ath5k_chan_pcal_info *pcal;
int i;
switch(mode) { switch(mode) {
case AR5K_EEPROM_MODE_11B: case AR5K_EEPROM_MODE_11B:
...@@ -608,16 +647,18 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset) ...@@ -608,16 +647,18 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
ath5k_eeprom_read_freq_list(ah, &offset, ath5k_eeprom_read_freq_list(ah, &offset,
AR5K_EEPROM_N_2GHZ_CHAN_2413, pcal, AR5K_EEPROM_N_2GHZ_CHAN_2413, pcal,
&ee->ee_n_piers[mode]); mode);
for(i = 0; i < AR5K_EEPROM_N_2GHZ_CHAN_2413; i += 1) {
pcal[i].freq = ath5k_eeprom_bin2freq(ee,
pcal[i].freq, mode);
}
return 0; return 0;
} }
/* Read power calibration for RF5111 chips
* For RF5111 we have an XPD -eXternal Power Detector- curve
* for each calibrated channel. Each curve has PCDAC steps on
* x axis and power on y axis and looks like a logarithmic
* function. To recreate the curve and pass the power values
* on the pcdac table, we read 10 points here and interpolate later.
*/
static int static int
ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode) ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
{ {
...@@ -714,6 +755,17 @@ ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode) ...@@ -714,6 +755,17 @@ ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
return 0; return 0;
} }
/* Read power calibration for RF5112 chips
* For RF5112 we have 4 XPD -eXternal Power Detector- curves
* for each calibrated channel on 0, -6, -12 and -18dbm but we only
* use the higher (3) and the lower (0) curves. Each curve has PCDAC
* steps on x axis and power on y axis and looks like a linear
* function. To recreate the curve and pass the power values
* on the pcdac table, we read 4 points for xpd 0 and 3 points
* for xpd 3 here and interpolate later.
*
* Note: Many vendors just use xpd 0 so xpd 3 is zeroed.
*/
static int static int
ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
{ {
...@@ -790,7 +842,7 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) ...@@ -790,7 +842,7 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
/* PCDAC steps /* PCDAC steps
* corresponding to the above power * corresponding to the above power
* measurements (static) */ * measurements (fixed) */
chan_pcal_info->pcdac_x3[0] = 20; chan_pcal_info->pcdac_x3[0] = 20;
chan_pcal_info->pcdac_x3[1] = 35; chan_pcal_info->pcdac_x3[1] = 35;
chan_pcal_info->pcdac_x3[2] = 63; chan_pcal_info->pcdac_x3[2] = 63;
...@@ -814,6 +866,13 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) ...@@ -814,6 +866,13 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
return 0; return 0;
} }
/* For RF2413 power calibration data doesn't start on a fixed location and
* if a mode is not supported, it's section is missing -not zeroed-.
* So we need to calculate the starting offset for each section by using
* these two functions */
/* Return the size of each section based on the mode and the number of pd
* gains available (maximum 4). */
static inline unsigned int static inline unsigned int
ath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode) ath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode)
{ {
...@@ -826,6 +885,8 @@ ath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode) ...@@ -826,6 +885,8 @@ ath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode)
return sz; return sz;
} }
/* Return the starting offset for a section based on the modes supported
* and each section's size. */
static unsigned int static unsigned int
ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
{ {
...@@ -834,11 +895,13 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) ...@@ -834,11 +895,13 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
switch(mode) { switch(mode) {
case AR5K_EEPROM_MODE_11G: case AR5K_EEPROM_MODE_11G:
if (AR5K_EEPROM_HDR_11B(ee->ee_header)) if (AR5K_EEPROM_HDR_11B(ee->ee_header))
offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11B) + 2; offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11B) +
AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
/* fall through */ /* fall through */
case AR5K_EEPROM_MODE_11B: case AR5K_EEPROM_MODE_11B:
if (AR5K_EEPROM_HDR_11A(ee->ee_header)) if (AR5K_EEPROM_HDR_11A(ee->ee_header))
offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11A) + 5; offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11A) +
AR5K_EEPROM_N_5GHZ_CHAN / 2;
/* fall through */ /* fall through */
case AR5K_EEPROM_MODE_11A: case AR5K_EEPROM_MODE_11A:
break; break;
...@@ -849,6 +912,17 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) ...@@ -849,6 +912,17 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
return offset; return offset;
} }
/* Read power calibration for RF2413 chips
* For RF2413 we have a PDDAC table (Power Detector) instead
* of a PCDAC and 4 pd gain curves for each calibrated channel.
* Each curve has PDDAC steps on x axis and power on y axis and
* looks like an exponential function. To recreate the curves
* we read here the points and interpolate later. Note that
* in most cases only higher and lower curves are used (like
* RF5112) but vendors have the oportunity to include all 4
* curves on eeprom. The final curve (higher power) has an extra
* point for better accuracy like RF5112.
*/
static int static int
ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
{ {
...@@ -868,6 +942,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ...@@ -868,6 +942,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
ee->ee_pd_gains[mode] = pd_gains; ee->ee_pd_gains[mode] = pd_gains;
offset = ath5k_cal_data_offset_2413(ee, mode); offset = ath5k_cal_data_offset_2413(ee, mode);
ee->ee_n_piers[mode] = 0;
switch (mode) { switch (mode) {
case AR5K_EEPROM_MODE_11A: case AR5K_EEPROM_MODE_11A:
if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
...@@ -1163,6 +1238,20 @@ static int ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned ...@@ -1163,6 +1238,20 @@ static int ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned
return 0; return 0;
} }
/*
* Read per channel calibration info from EEPROM
*
* This info is used to calibrate the baseband power table. Imagine
* that for each channel there is a power curve that's hw specific
* (depends on amplifier etc) and we try to "correct" this curve using
* offests we pass on to phy chip (baseband -> before amplifier) so that
* it can use accurate power values when setting tx power (takes amplifier's
* performance on each channel into account).
*
* EEPROM provides us with the offsets for some pre-calibrated channels
* and we have to interpolate to create the full table for these channels and
* also the table for any channel.
*/
static int static int
ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah) ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
{ {
...@@ -1193,7 +1282,7 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah) ...@@ -1193,7 +1282,7 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
return 0; return 0;
} }
/* Read conformance test limits */ /* Read conformance test limits used for regulatory control */
static int static int
ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah) ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment