Commit 8e218fb2 authored by Nick Kossifidis's avatar Nick Kossifidis Committed by John W. Linville

ath5k: Convert chip specific calibration data to a generic format

* Convert chip specific calibration data to a generic format common
for all chips

Note: We scale up power to be in 0.25dB units for all chips for
compatibility with RF5112

v2: Address Bob's and Jiri's comments
Signed-off-by: default avatarNick Kossifidis <mickflemm@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 504f3655
/* /*
* Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
* Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org> * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
...@@ -98,11 +98,6 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah) ...@@ -98,11 +98,6 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah)
int ret; int ret;
u16 val; u16 val;
/* Initial TX thermal adjustment values */
ee->ee_tx_clip = 4;
ee->ee_pwd_84 = ee->ee_pwd_90 = 1;
ee->ee_gain_select = 1;
/* /*
* Read values from EEPROM and store them in the capability structure * Read values from EEPROM and store them in the capability structure
*/ */
...@@ -241,22 +236,22 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, ...@@ -241,22 +236,22 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset,
ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff);
switch(mode) { switch(mode) {
case AR5K_EEPROM_MODE_11A: case AR5K_EEPROM_MODE_11A:
ee->ee_ob[mode][3] = (val >> 5) & 0x7; ee->ee_ob[mode][3] = (val >> 5) & 0x7;
ee->ee_db[mode][3] = (val >> 2) & 0x7; ee->ee_db[mode][3] = (val >> 2) & 0x7;
ee->ee_ob[mode][2] = (val << 1) & 0x7; ee->ee_ob[mode][2] = (val << 1) & 0x7;
AR5K_EEPROM_READ(o++, val); AR5K_EEPROM_READ(o++, val);
ee->ee_ob[mode][2] |= (val >> 15) & 0x1; ee->ee_ob[mode][2] |= (val >> 15) & 0x1;
ee->ee_db[mode][2] = (val >> 12) & 0x7; ee->ee_db[mode][2] = (val >> 12) & 0x7;
ee->ee_ob[mode][1] = (val >> 9) & 0x7; ee->ee_ob[mode][1] = (val >> 9) & 0x7;
ee->ee_db[mode][1] = (val >> 6) & 0x7; ee->ee_db[mode][1] = (val >> 6) & 0x7;
ee->ee_ob[mode][0] = (val >> 3) & 0x7; ee->ee_ob[mode][0] = (val >> 3) & 0x7;
ee->ee_db[mode][0] = val & 0x7; ee->ee_db[mode][0] = val & 0x7;
break; break;
case AR5K_EEPROM_MODE_11G: case AR5K_EEPROM_MODE_11G:
case AR5K_EEPROM_MODE_11B: case AR5K_EEPROM_MODE_11B:
ee->ee_ob[mode][1] = (val >> 4) & 0x7; ee->ee_ob[mode][1] = (val >> 4) & 0x7;
ee->ee_db[mode][1] = val & 0x7; ee->ee_db[mode][1] = val & 0x7;
break; break;
} }
...@@ -504,35 +499,6 @@ ath5k_eeprom_init_modes(struct ath5k_hw *ah) ...@@ -504,35 +499,6 @@ 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
ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
{
static const u16 intercepts3[] =
{ 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 };
static const u16 intercepts3_2[] =
{ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
const u16 *ip;
int i;
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2)
ip = intercepts3_2;
else
ip = intercepts3;
for (i = 0; i < ARRAY_SIZE(intercepts3); i++)
*vp++ = (ip[i] * max + (100 - ip[i]) * min) / 100;
}
/* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff /* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff
* frequency mask) */ * frequency mask) */
static inline int static inline int
...@@ -546,26 +512,25 @@ ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max, ...@@ -546,26 +512,25 @@ ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max,
int ret; int ret;
u16 val; u16 val;
ee->ee_n_piers[mode] = 0;
while(i < max) { while(i < max) {
AR5K_EEPROM_READ(o++, val); AR5K_EEPROM_READ(o++, val);
freq1 = (val >> 8) & 0xff; freq1 = val & 0xff;
freq2 = val & 0xff; if (!freq1)
break;
if (freq1) {
pc[i++].freq = ath5k_eeprom_bin2freq(ee,
freq1, mode);
ee->ee_n_piers[mode]++;
}
if (freq2) { pc[i++].freq = ath5k_eeprom_bin2freq(ee,
pc[i++].freq = ath5k_eeprom_bin2freq(ee, freq1, mode);
freq2, mode); ee->ee_n_piers[mode]++;
ee->ee_n_piers[mode]++;
}
if (!freq1 || !freq2) freq2 = (val >> 8) & 0xff;
if (!freq2)
break; break;
pc[i++].freq = ath5k_eeprom_bin2freq(ee,
freq2, mode);
ee->ee_n_piers[mode]++;
} }
/* return new offset */ /* return new offset */
...@@ -652,13 +617,122 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset) ...@@ -652,13 +617,122 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
return 0; return 0;
} }
/* Read power calibration for RF5111 chips /*
* Read power calibration for RF5111 chips
*
* For RF5111 we have an XPD -eXternal Power Detector- curve * For RF5111 we have an XPD -eXternal Power Detector- curve
* for each calibrated channel. Each curve has PCDAC steps on * for each calibrated channel. Each curve has 0,5dB Power steps
* x axis and power on y axis and looks like a logarithmic * on x axis and PCDAC steps (offsets) on y axis and looks like an
* function. To recreate the curve and pass the power values * exponential function. To recreate the curve we read 11 points
* on the pcdac table, we read 10 points here and interpolate later. * here and interpolate later.
*/ */
/* Used to match PCDAC steps with power values on RF5111 chips
* (eeprom versions < 4). For RF5111 we have 11 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-
* (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2)
* these 11 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
ath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp)
{
const static u16 intercepts3[] =
{ 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 };
const static u16 intercepts3_2[] =
{ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
const u16 *ip;
int i;
if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2)
ip = intercepts3_2;
else
ip = intercepts3;
for (i = 0; i < ARRAY_SIZE(intercepts3); i++)
vp[i] = (ip[i] * max + (100 - ip[i]) * min) / 100;
}
/* Convert RF5111 specific data to generic raw data
* used by interpolation code */
static int
ath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode,
struct ath5k_chan_pcal_info *chinfo)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
struct ath5k_chan_pcal_info_rf5111 *pcinfo;
struct ath5k_pdgain_info *pd;
u8 pier, point, idx;
u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
/* Fill raw data for each calibration pier */
for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
pcinfo = &chinfo[pier].rf5111_info;
/* Allocate pd_curves for this cal pier */
chinfo[pier].pd_curves =
kcalloc(AR5K_EEPROM_N_PD_CURVES,
sizeof(struct ath5k_pdgain_info),
GFP_KERNEL);
if (!chinfo[pier].pd_curves)
return -ENOMEM;
/* Only one curve for RF5111
* find out which one and place
* in in pd_curves.
* Note: ee_x_gain is reversed here */
for (idx = 0; idx < AR5K_EEPROM_N_PD_CURVES; idx++) {
if (!((ee->ee_x_gain[mode] >> idx) & 0x1)) {
pdgain_idx[0] = idx;
break;
}
}
ee->ee_pd_gains[mode] = 1;
pd = &chinfo[pier].pd_curves[idx];
pd->pd_points = AR5K_EEPROM_N_PWR_POINTS_5111;
/* Allocate pd points for this curve */
pd->pd_step = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111,
sizeof(u8), GFP_KERNEL);
if (!pd->pd_step)
return -ENOMEM;
pd->pd_pwr = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111,
sizeof(s16), GFP_KERNEL);
if (!pd->pd_pwr)
return -ENOMEM;
/* Fill raw dataset
* (convert power to 0.25dB units
* for RF5112 combatibility) */
for (point = 0; point < pd->pd_points; point++) {
/* Absolute values */
pd->pd_pwr[point] = 2 * pcinfo->pwr[point];
/* Already sorted */
pd->pd_step[point] = pcinfo->pcdac[point];
}
/* Set min/max pwr */
chinfo[pier].min_pwr = pd->pd_pwr[0];
chinfo[pier].max_pwr = pd->pd_pwr[10];
}
return 0;
}
/* Parse EEPROM data */
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)
{ {
...@@ -747,30 +821,165 @@ ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode) ...@@ -747,30 +821,165 @@ ath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode)
cdata->pcdac_max, cdata->pcdac); cdata->pcdac_max, cdata->pcdac);
} }
return 0; return ath5k_eeprom_convert_pcal_info_5111(ah, mode, pcal);
} }
/* Read power calibration for RF5112 chips
/*
* Read power calibration for RF5112 chips
*
* For RF5112 we have 4 XPD -eXternal Power Detector- curves * For RF5112 we have 4 XPD -eXternal Power Detector- curves
* for each calibrated channel on 0, -6, -12 and -18dbm but we only * 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 * use the higher (3) and the lower (0) curves. Each curve has 0.5dB
* steps on x axis and power on y axis and looks like a linear * power steps on x axis and PCDAC steps on y axis and looks like a
* function. To recreate the curve and pass the power values * 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 * on hw, we read 4 points for xpd 0 (lower gain -> max power)
* for xpd 3 here and interpolate later. * and 3 points for xpd 3 (higher gain -> lower power) here and
* interpolate later.
* *
* Note: Many vendors just use xpd 0 so xpd 3 is zeroed. * Note: Many vendors just use xpd 0 so xpd 3 is zeroed.
*/ */
/* Convert RF5112 specific data to generic raw data
* used by interpolation code */
static int
ath5k_eeprom_convert_pcal_info_5112(struct ath5k_hw *ah, int mode,
struct ath5k_chan_pcal_info *chinfo)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
struct ath5k_chan_pcal_info_rf5112 *pcinfo;
u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
unsigned int pier, pdg, point;
/* Fill raw data for each calibration pier */
for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
pcinfo = &chinfo[pier].rf5112_info;
/* Allocate pd_curves for this cal pier */
chinfo[pier].pd_curves =
kcalloc(AR5K_EEPROM_N_PD_CURVES,
sizeof(struct ath5k_pdgain_info),
GFP_KERNEL);
if (!chinfo[pier].pd_curves)
return -ENOMEM;
/* Fill pd_curves */
for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
u8 idx = pdgain_idx[pdg];
struct ath5k_pdgain_info *pd =
&chinfo[pier].pd_curves[idx];
/* Lowest gain curve (max power) */
if (pdg == 0) {
/* One more point for better accuracy */
pd->pd_points = AR5K_EEPROM_N_XPD0_POINTS;
/* Allocate pd points for this curve */
pd->pd_step = kcalloc(pd->pd_points,
sizeof(u8), GFP_KERNEL);
if (!pd->pd_step)
return -ENOMEM;
pd->pd_pwr = kcalloc(pd->pd_points,
sizeof(s16), GFP_KERNEL);
if (!pd->pd_pwr)
return -ENOMEM;
/* Fill raw dataset
* (all power levels are in 0.25dB units) */
pd->pd_step[0] = pcinfo->pcdac_x0[0];
pd->pd_pwr[0] = pcinfo->pwr_x0[0];
for (point = 1; point < pd->pd_points;
point++) {
/* Absolute values */
pd->pd_pwr[point] =
pcinfo->pwr_x0[point];
/* Deltas */
pd->pd_step[point] =
pd->pd_step[point - 1] +
pcinfo->pcdac_x0[point];
}
/* Set min power for this frequency */
chinfo[pier].min_pwr = pd->pd_pwr[0];
/* Highest gain curve (min power) */
} else if (pdg == 1) {
pd->pd_points = AR5K_EEPROM_N_XPD3_POINTS;
/* Allocate pd points for this curve */
pd->pd_step = kcalloc(pd->pd_points,
sizeof(u8), GFP_KERNEL);
if (!pd->pd_step)
return -ENOMEM;
pd->pd_pwr = kcalloc(pd->pd_points,
sizeof(s16), GFP_KERNEL);
if (!pd->pd_pwr)
return -ENOMEM;
/* Fill raw dataset
* (all power levels are in 0.25dB units) */
for (point = 0; point < pd->pd_points;
point++) {
/* Absolute values */
pd->pd_pwr[point] =
pcinfo->pwr_x3[point];
/* Fixed points */
pd->pd_step[point] =
pcinfo->pcdac_x3[point];
}
/* Since we have a higher gain curve
* override min power */
chinfo[pier].min_pwr = pd->pd_pwr[0];
}
}
}
return 0;
}
/* Parse EEPROM data */
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)
{ {
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_rf5112 *chan_pcal_info; struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info;
struct ath5k_chan_pcal_info *gen_chan_info; struct ath5k_chan_pcal_info *gen_chan_info;
u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
u32 offset; u32 offset;
unsigned int i, c; u8 i, c;
u16 val; u16 val;
int ret; int ret;
u8 pd_gains = 0;
/* Count how many curves we have and
* identify them (which one of the 4
* available curves we have on each count).
* Curves are stored from lower (x0) to
* higher (x3) gain */
for (i = 0; i < AR5K_EEPROM_N_PD_CURVES; i++) {
/* ee_x_gain[mode] is x gain mask */
if ((ee->ee_x_gain[mode] >> i) & 0x1)
pdgain_idx[pd_gains++] = i;
}
ee->ee_pd_gains[mode] = pd_gains;
if (pd_gains == 0 || pd_gains > 2)
return -EINVAL;
switch (mode) { switch (mode) {
case AR5K_EEPROM_MODE_11A: case AR5K_EEPROM_MODE_11A:
...@@ -808,13 +1017,13 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) ...@@ -808,13 +1017,13 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
for (i = 0; i < ee->ee_n_piers[mode]; i++) { for (i = 0; i < ee->ee_n_piers[mode]; i++) {
chan_pcal_info = &gen_chan_info[i].rf5112_info; chan_pcal_info = &gen_chan_info[i].rf5112_info;
/* Power values in dBm * 4 /* Power values in quarter dB
* for the lower xpd gain curve * for the lower xpd gain curve
* (0 dBm -> higher output power) */ * (0 dBm -> higher output power) */
for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) { for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) {
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pwr_x0[c] = (val & 0xff); chan_pcal_info->pwr_x0[c] = (s8) (val & 0xff);
chan_pcal_info->pwr_x0[++c] = ((val >> 8) & 0xff); chan_pcal_info->pwr_x0[++c] = (s8) ((val >> 8) & 0xff);
} }
/* PCDAC steps /* PCDAC steps
...@@ -825,12 +1034,12 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) ...@@ -825,12 +1034,12 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f); chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f);
chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f); chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f);
/* Power values in dBm * 4 /* Power values in quarter dB
* for the higher xpd gain curve * for the higher xpd gain curve
* (18 dBm -> lower output power) */ * (18 dBm -> lower output power) */
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pwr_x3[0] = (val & 0xff); chan_pcal_info->pwr_x3[0] = (s8) (val & 0xff);
chan_pcal_info->pwr_x3[1] = ((val >> 8) & 0xff); chan_pcal_info->pwr_x3[1] = (s8) ((val >> 8) & 0xff);
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pwr_x3[2] = (val & 0xff); chan_pcal_info->pwr_x3[2] = (val & 0xff);
...@@ -843,24 +1052,36 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) ...@@ -843,24 +1052,36 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
chan_pcal_info->pcdac_x3[2] = 63; chan_pcal_info->pcdac_x3[2] = 63;
if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) { if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) {
chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0xff); chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0x3f);
/* Last xpd0 power level is also channel maximum */ /* Last xpd0 power level is also channel maximum */
gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3]; gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3];
} else { } else {
chan_pcal_info->pcdac_x0[0] = 1; chan_pcal_info->pcdac_x0[0] = 1;
gen_chan_info[i].max_pwr = ((val >> 8) & 0xff); gen_chan_info[i].max_pwr = (s8) ((val >> 8) & 0xff);
} }
/* Recreate pcdac_x0 table for this channel using pcdac steps */
chan_pcal_info->pcdac_x0[1] += chan_pcal_info->pcdac_x0[0];
chan_pcal_info->pcdac_x0[2] += chan_pcal_info->pcdac_x0[1];
chan_pcal_info->pcdac_x0[3] += chan_pcal_info->pcdac_x0[2];
} }
return 0; return ath5k_eeprom_convert_pcal_info_5112(ah, mode, gen_chan_info);
} }
/*
* Read power calibration for RF2413 chips
*
* For RF2413 we have a Power to PDDAC table (Power Detector)
* instead of a PCDAC and 4 pd gain curves for each calibrated channel.
* Each curve has power on x axis in 0.5 db steps and PDDADC steps on y
* axis and looks like an exponential function like the RF5111 curve.
*
* To recreate the curves we read here the points and interpolate
* later. Note that in most cases only 2 (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.
*/
/* For RF2413 power calibration data doesn't start on a fixed location and /* 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-. * 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 * So we need to calculate the starting offset for each section by using
...@@ -890,13 +1111,15 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) ...@@ -890,13 +1111,15 @@ 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) + offset += ath5k_pdgains_size_2413(ee,
AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; 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) + offset += ath5k_pdgains_size_2413(ee,
AR5K_EEPROM_N_5GHZ_CHAN / 2; 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;
...@@ -907,37 +1130,118 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) ...@@ -907,37 +1130,118 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
return offset; return offset;
} }
/* Read power calibration for RF2413 chips /* Convert RF2413 specific data to generic raw data
* For RF2413 we have a PDDAC table (Power Detector) instead * used by interpolation code */
* of a PCDAC and 4 pd gain curves for each calibrated channel. static int
* Each curve has PDDAC steps on x axis and power on y axis and ath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode,
* looks like an exponential function. To recreate the curves struct ath5k_chan_pcal_info *chinfo)
* we read here the points and interpolate later. Note that {
* in most cases only higher and lower curves are used (like struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
* RF5112) but vendors have the oportunity to include all 4 struct ath5k_chan_pcal_info_rf2413 *pcinfo;
* curves on eeprom. The final curve (higher power) has an extra u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
* point for better accuracy like RF5112. unsigned int pier, pdg, point;
*/
/* Fill raw data for each calibration pier */
for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
pcinfo = &chinfo[pier].rf2413_info;
/* Allocate pd_curves for this cal pier */
chinfo[pier].pd_curves =
kcalloc(AR5K_EEPROM_N_PD_CURVES,
sizeof(struct ath5k_pdgain_info),
GFP_KERNEL);
if (!chinfo[pier].pd_curves)
return -ENOMEM;
/* Fill pd_curves */
for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
u8 idx = pdgain_idx[pdg];
struct ath5k_pdgain_info *pd =
&chinfo[pier].pd_curves[idx];
/* One more point for the highest power
* curve (lowest gain) */
if (pdg == ee->ee_pd_gains[mode] - 1)
pd->pd_points = AR5K_EEPROM_N_PD_POINTS;
else
pd->pd_points = AR5K_EEPROM_N_PD_POINTS - 1;
/* Allocate pd points for this curve */
pd->pd_step = kcalloc(pd->pd_points,
sizeof(u8), GFP_KERNEL);
if (!pd->pd_step)
return -ENOMEM;
pd->pd_pwr = kcalloc(pd->pd_points,
sizeof(s16), GFP_KERNEL);
if (!pd->pd_pwr)
return -ENOMEM;
/* Fill raw dataset
* convert all pwr levels to
* quarter dB for RF5112 combatibility */
pd->pd_step[0] = pcinfo->pddac_i[pdg];
pd->pd_pwr[0] = 4 * pcinfo->pwr_i[pdg];
for (point = 1; point < pd->pd_points; point++) {
pd->pd_pwr[point] = pd->pd_pwr[point - 1] +
2 * pcinfo->pwr[pdg][point - 1];
pd->pd_step[point] = pd->pd_step[point - 1] +
pcinfo->pddac[pdg][point - 1];
}
/* Highest gain curve -> min power */
if (pdg == 0)
chinfo[pier].min_pwr = pd->pd_pwr[0];
/* Lowest gain curve -> max power */
if (pdg == ee->ee_pd_gains[mode] - 1)
chinfo[pier].max_pwr =
pd->pd_pwr[pd->pd_points - 1];
}
}
return 0;
}
/* Parse EEPROM data */
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)
{ {
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_rf2413 *chan_pcal_info; struct ath5k_chan_pcal_info_rf2413 *pcinfo;
struct ath5k_chan_pcal_info *gen_chan_info; struct ath5k_chan_pcal_info *chinfo;
unsigned int i, c; u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
u32 offset; u32 offset;
int ret; int idx, i, ret;
u16 val; u16 val;
u8 pd_gains = 0; u8 pd_gains = 0;
if (ee->ee_x_gain[mode] & 0x1) pd_gains++; /* Count how many curves we have and
if ((ee->ee_x_gain[mode] >> 1) & 0x1) pd_gains++; * identify them (which one of the 4
if ((ee->ee_x_gain[mode] >> 2) & 0x1) pd_gains++; * available curves we have on each count).
if ((ee->ee_x_gain[mode] >> 3) & 0x1) pd_gains++; * Curves are stored from higher to
* lower gain so we go backwards */
for (idx = AR5K_EEPROM_N_PD_CURVES - 1; idx >= 0; idx--) {
/* ee_x_gain[mode] is x gain mask */
if ((ee->ee_x_gain[mode] >> idx) & 0x1)
pdgain_idx[pd_gains++] = idx;
}
ee->ee_pd_gains[mode] = pd_gains; ee->ee_pd_gains[mode] = pd_gains;
if (pd_gains == 0)
return -EINVAL;
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))
...@@ -945,7 +1249,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ...@@ -945,7 +1249,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
ath5k_eeprom_init_11a_pcal_freq(ah, offset); ath5k_eeprom_init_11a_pcal_freq(ah, offset);
offset += AR5K_EEPROM_N_5GHZ_CHAN / 2; offset += AR5K_EEPROM_N_5GHZ_CHAN / 2;
gen_chan_info = ee->ee_pwr_cal_a; chinfo = ee->ee_pwr_cal_a;
break; break;
case AR5K_EEPROM_MODE_11B: case AR5K_EEPROM_MODE_11B:
if (!AR5K_EEPROM_HDR_11B(ee->ee_header)) if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
...@@ -953,7 +1257,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ...@@ -953,7 +1257,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
ath5k_eeprom_init_11bg_2413(ah, mode, offset); ath5k_eeprom_init_11bg_2413(ah, mode, offset);
offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
gen_chan_info = ee->ee_pwr_cal_b; chinfo = ee->ee_pwr_cal_b;
break; break;
case AR5K_EEPROM_MODE_11G: case AR5K_EEPROM_MODE_11G:
if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
...@@ -961,41 +1265,35 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ...@@ -961,41 +1265,35 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
ath5k_eeprom_init_11bg_2413(ah, mode, offset); ath5k_eeprom_init_11bg_2413(ah, mode, offset);
offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
gen_chan_info = ee->ee_pwr_cal_g; chinfo = ee->ee_pwr_cal_g;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
if (pd_gains == 0)
return 0;
for (i = 0; i < ee->ee_n_piers[mode]; i++) { 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 * Read pwr_i, pddac_i and the first
* 2 pd points (pwr, pddac) * 2 pd points (pwr, pddac)
*/ */
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pwr_i[0] = val & 0x1f; pcinfo->pwr_i[0] = val & 0x1f;
chan_pcal_info->pddac_i[0] = (val >> 5) & 0x7f; pcinfo->pddac_i[0] = (val >> 5) & 0x7f;
chan_pcal_info->pwr[0][0] = pcinfo->pwr[0][0] = (val >> 12) & 0xf;
(val >> 12) & 0xf;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pddac[0][0] = val & 0x3f; pcinfo->pddac[0][0] = val & 0x3f;
chan_pcal_info->pwr[0][1] = (val >> 6) & 0xf; pcinfo->pwr[0][1] = (val >> 6) & 0xf;
chan_pcal_info->pddac[0][1] = pcinfo->pddac[0][1] = (val >> 10) & 0x3f;
(val >> 10) & 0x3f;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pwr[0][2] = val & 0xf; pcinfo->pwr[0][2] = val & 0xf;
chan_pcal_info->pddac[0][2] = pcinfo->pddac[0][2] = (val >> 4) & 0x3f;
(val >> 4) & 0x3f;
chan_pcal_info->pwr[0][3] = 0; pcinfo->pwr[0][3] = 0;
chan_pcal_info->pddac[0][3] = 0; pcinfo->pddac[0][3] = 0;
if (pd_gains > 1) { if (pd_gains > 1) {
/* /*
...@@ -1003,44 +1301,36 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ...@@ -1003,44 +1301,36 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
* so it only has 2 pd points. * so it only has 2 pd points.
* Continue wih pd gain 1. * 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); 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; pcinfo->pwr[1][0] = (val >> 6) & 0xf;
chan_pcal_info->pddac[1][0] = pcinfo->pddac[1][0] = (val >> 10) & 0x3f;
(val >> 10) & 0x3f;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pwr[1][1] = val & 0xf; pcinfo->pwr[1][1] = val & 0xf;
chan_pcal_info->pddac[1][1] = pcinfo->pddac[1][1] = (val >> 4) & 0x3f;
(val >> 4) & 0x3f; pcinfo->pwr[1][2] = (val >> 10) & 0xf;
chan_pcal_info->pwr[1][2] =
(val >> 10) & 0xf; pcinfo->pddac[1][2] = (val >> 14) & 0x3;
chan_pcal_info->pddac[1][2] =
(val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pddac[1][2] |= pcinfo->pddac[1][2] |= (val & 0xF) << 2;
(val & 0xF) << 2;
chan_pcal_info->pwr[1][3] = 0; pcinfo->pwr[1][3] = 0;
chan_pcal_info->pddac[1][3] = 0; pcinfo->pddac[1][3] = 0;
} else if (pd_gains == 1) { } else if (pd_gains == 1) {
/* /*
* Pd gain 0 is the last one so * Pd gain 0 is the last one so
* read the extra point. * read the extra point.
*/ */
chan_pcal_info->pwr[0][3] = pcinfo->pwr[0][3] = (val >> 10) & 0xf;
(val >> 10) & 0xf;
chan_pcal_info->pddac[0][3] = pcinfo->pddac[0][3] = (val >> 14) & 0x3;
(val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pddac[0][3] |= pcinfo->pddac[0][3] |= (val & 0xF) << 2;
(val & 0xF) << 2;
} }
/* /*
...@@ -1048,105 +1338,65 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ...@@ -1048,105 +1338,65 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
* as above. * as above.
*/ */
if (pd_gains > 2) { if (pd_gains > 2) {
chan_pcal_info->pwr_i[2] = (val >> 4) & 0x1f; pcinfo->pwr_i[2] = (val >> 4) & 0x1f;
chan_pcal_info->pddac_i[2] = (val >> 9) & 0x7f; pcinfo->pddac_i[2] = (val >> 9) & 0x7f;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pwr[2][0] = pcinfo->pwr[2][0] = (val >> 0) & 0xf;
(val >> 0) & 0xf; pcinfo->pddac[2][0] = (val >> 4) & 0x3f;
chan_pcal_info->pddac[2][0] = pcinfo->pwr[2][1] = (val >> 10) & 0xf;
(val >> 4) & 0x3f;
chan_pcal_info->pwr[2][1] = pcinfo->pddac[2][1] = (val >> 14) & 0x3;
(val >> 10) & 0xf;
chan_pcal_info->pddac[2][1] =
(val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pddac[2][1] |= pcinfo->pddac[2][1] |= (val & 0xF) << 2;
(val & 0xF) << 2;
chan_pcal_info->pwr[2][2] = pcinfo->pwr[2][2] = (val >> 4) & 0xf;
(val >> 4) & 0xf; pcinfo->pddac[2][2] = (val >> 8) & 0x3f;
chan_pcal_info->pddac[2][2] =
(val >> 8) & 0x3f;
chan_pcal_info->pwr[2][3] = 0; pcinfo->pwr[2][3] = 0;
chan_pcal_info->pddac[2][3] = 0; pcinfo->pddac[2][3] = 0;
} else if (pd_gains == 2) { } else if (pd_gains == 2) {
chan_pcal_info->pwr[1][3] = pcinfo->pwr[1][3] = (val >> 4) & 0xf;
(val >> 4) & 0xf; pcinfo->pddac[1][3] = (val >> 8) & 0x3f;
chan_pcal_info->pddac[1][3] =
(val >> 8) & 0x3f;
} }
if (pd_gains > 3) { 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); 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; pcinfo->pddac_i[3] = (val >> 3) & 0x7f;
chan_pcal_info->pwr[3][0] = pcinfo->pwr[3][0] = (val >> 10) & 0xf;
(val >> 10) & 0xf; pcinfo->pddac[3][0] = (val >> 14) & 0x3;
chan_pcal_info->pddac[3][0] =
(val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pddac[3][0] |= pcinfo->pddac[3][0] |= (val & 0xF) << 2;
(val & 0xF) << 2; pcinfo->pwr[3][1] = (val >> 4) & 0xf;
chan_pcal_info->pwr[3][1] = pcinfo->pddac[3][1] = (val >> 8) & 0x3f;
(val >> 4) & 0xf;
chan_pcal_info->pddac[3][1] = pcinfo->pwr[3][2] = (val >> 14) & 0x3;
(val >> 8) & 0x3f;
chan_pcal_info->pwr[3][2] =
(val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pwr[3][2] |= pcinfo->pwr[3][2] |= ((val >> 0) & 0x3) << 2;
((val >> 0) & 0x3) << 2;
chan_pcal_info->pddac[3][2] = pcinfo->pddac[3][2] = (val >> 2) & 0x3f;
(val >> 2) & 0x3f; pcinfo->pwr[3][3] = (val >> 8) & 0xf;
chan_pcal_info->pwr[3][3] =
(val >> 8) & 0xf;
chan_pcal_info->pddac[3][3] = pcinfo->pddac[3][3] = (val >> 12) & 0xF;
(val >> 12) & 0xF;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pddac[3][3] |= pcinfo->pddac[3][3] |= ((val >> 0) & 0x3) << 4;
((val >> 0) & 0x3) << 4;
} else if (pd_gains == 3) { } else if (pd_gains == 3) {
chan_pcal_info->pwr[2][3] = pcinfo->pwr[2][3] = (val >> 14) & 0x3;
(val >> 14) & 0x3;
AR5K_EEPROM_READ(offset++, val); AR5K_EEPROM_READ(offset++, val);
chan_pcal_info->pwr[2][3] |= pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2;
((val >> 0) & 0x3) << 2;
chan_pcal_info->pddac[2][3] =
(val >> 2) & 0x3f;
}
for (c = 0; c < pd_gains; c++) { pcinfo->pddac[2][3] = (val >> 2) & 0x3f;
/* 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;
} }
} }
return 0; return ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo);
} }
/* /*
* Read per rate target power (this is the maximum tx power * Read per rate target power (this is the maximum tx power
* supported by the card). This info is used when setting * supported by the card). This info is used when setting
...@@ -1154,11 +1404,12 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) ...@@ -1154,11 +1404,12 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
* *
* This also works for v5 EEPROMs. * This also works for v5 EEPROMs.
*/ */
static int ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode) static int
ath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode)
{ {
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
struct ath5k_rate_pcal_info *rate_pcal_info; struct ath5k_rate_pcal_info *rate_pcal_info;
u16 *rate_target_pwr_num; u8 *rate_target_pwr_num;
u32 offset; u32 offset;
u16 val; u16 val;
int ret, i; int ret, i;
...@@ -1264,7 +1515,9 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah) ...@@ -1264,7 +1515,9 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
else else
read_pcal = ath5k_eeprom_read_pcal_info_5111; read_pcal = ath5k_eeprom_read_pcal_info_5111;
for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) {
for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G;
mode++) {
err = read_pcal(ah, mode); err = read_pcal(ah, mode);
if (err) if (err)
return err; return err;
...@@ -1277,6 +1530,62 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah) ...@@ -1277,6 +1530,62 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
return 0; return 0;
} }
static int
ath5k_eeprom_free_pcal_info(struct ath5k_hw *ah, int mode)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
struct ath5k_chan_pcal_info *chinfo;
u8 pier, pdg;
switch (mode) {
case AR5K_EEPROM_MODE_11A:
if (!AR5K_EEPROM_HDR_11A(ee->ee_header))
return 0;
chinfo = ee->ee_pwr_cal_a;
break;
case AR5K_EEPROM_MODE_11B:
if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
return 0;
chinfo = ee->ee_pwr_cal_b;
break;
case AR5K_EEPROM_MODE_11G:
if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
return 0;
chinfo = ee->ee_pwr_cal_g;
break;
default:
return -EINVAL;
}
for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) {
if (!chinfo[pier].pd_curves)
continue;
for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) {
struct ath5k_pdgain_info *pd =
&chinfo[pier].pd_curves[pdg];
if (pd != NULL) {
kfree(pd->pd_step);
kfree(pd->pd_pwr);
}
}
kfree(chinfo[pier].pd_curves);
}
return 0;
}
void
ath5k_eeprom_detach(struct ath5k_hw *ah)
{
u8 mode;
for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++)
ath5k_eeprom_free_pcal_info(ah, mode);
}
/* Read conformance test limits used for regulatory control */ /* 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)
...@@ -1457,3 +1766,4 @@ bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah) ...@@ -1457,3 +1766,4 @@ bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah)
else else
return false; return false;
} }
...@@ -173,6 +173,7 @@ ...@@ -173,6 +173,7 @@
#define AR5K_EEPROM_N_5GHZ_CHAN 10 #define AR5K_EEPROM_N_5GHZ_CHAN 10
#define AR5K_EEPROM_N_2GHZ_CHAN 3 #define AR5K_EEPROM_N_2GHZ_CHAN 3
#define AR5K_EEPROM_N_2GHZ_CHAN_2413 4 #define AR5K_EEPROM_N_2GHZ_CHAN_2413 4
#define AR5K_EEPROM_N_2GHZ_CHAN_MAX 4
#define AR5K_EEPROM_MAX_CHAN 10 #define AR5K_EEPROM_MAX_CHAN 10
#define AR5K_EEPROM_N_PWR_POINTS_5111 11 #define AR5K_EEPROM_N_PWR_POINTS_5111 11
#define AR5K_EEPROM_N_PCDAC 11 #define AR5K_EEPROM_N_PCDAC 11
...@@ -193,7 +194,7 @@ ...@@ -193,7 +194,7 @@
#define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10) #define AR5K_EEPROM_SCALE_OC_DELTA(_x) (((_x) * 2) / 10)
#define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32) #define AR5K_EEPROM_N_CTLS(_v) AR5K_EEPROM_OFF(_v, 16, 32)
#define AR5K_EEPROM_MAX_CTLS 32 #define AR5K_EEPROM_MAX_CTLS 32
#define AR5K_EEPROM_N_XPD_PER_CHANNEL 4 #define AR5K_EEPROM_N_PD_CURVES 4
#define AR5K_EEPROM_N_XPD0_POINTS 4 #define AR5K_EEPROM_N_XPD0_POINTS 4
#define AR5K_EEPROM_N_XPD3_POINTS 3 #define AR5K_EEPROM_N_XPD3_POINTS 3
#define AR5K_EEPROM_N_PD_GAINS 4 #define AR5K_EEPROM_N_PD_GAINS 4
...@@ -232,7 +233,7 @@ enum ath5k_ctl_mode { ...@@ -232,7 +233,7 @@ enum ath5k_ctl_mode {
AR5K_CTL_11B = 1, AR5K_CTL_11B = 1,
AR5K_CTL_11G = 2, AR5K_CTL_11G = 2,
AR5K_CTL_TURBO = 3, AR5K_CTL_TURBO = 3,
AR5K_CTL_108G = 4, AR5K_CTL_TURBOG = 4,
AR5K_CTL_2GHT20 = 5, AR5K_CTL_2GHT20 = 5,
AR5K_CTL_5GHT20 = 6, AR5K_CTL_5GHT20 = 6,
AR5K_CTL_2GHT40 = 7, AR5K_CTL_2GHT40 = 7,
...@@ -240,65 +241,114 @@ enum ath5k_ctl_mode { ...@@ -240,65 +241,114 @@ enum ath5k_ctl_mode {
AR5K_CTL_MODE_M = 15, AR5K_CTL_MODE_M = 15,
}; };
/* Default CTL ids for the 3 main reg domains.
* Atheros only uses these by default but vendors
* can have up to 32 different CTLs for different
* scenarios. Note that theese values are ORed with
* the mode id (above) so we can have up to 24 CTL
* datasets out of these 3 main regdomains. That leaves
* 8 ids that can be used by vendors and since 0x20 is
* missing from HAL sources i guess this is the set of
* custom CTLs vendors can use. */
#define AR5K_CTL_FCC 0x10
#define AR5K_CTL_CUSTOM 0x20
#define AR5K_CTL_ETSI 0x30
#define AR5K_CTL_MKK 0x40
/* Indicates a CTL with only mode set and
* no reg domain mapping, such CTLs are used
* for world roaming domains or simply when
* a reg domain is not set */
#define AR5K_CTL_NO_REGDOMAIN 0xf0
/* Indicates an empty (invalid) CTL */
#define AR5K_CTL_NO_CTL 0xff
/* Per channel calibration data, used for power table setup */ /* Per channel calibration data, used for power table setup */
struct ath5k_chan_pcal_info_rf5111 { struct ath5k_chan_pcal_info_rf5111 {
/* Power levels in half dbm units /* Power levels in half dbm units
* for one power curve. */ * for one power curve. */
u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111]; u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
/* PCDAC table steps /* PCDAC table steps
* for the above values */ * for the above values */
u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111]; u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
/* Starting PCDAC step */ /* Starting PCDAC step */
u8 pcdac_min; u8 pcdac_min;
/* Final PCDAC step */ /* Final PCDAC step */
u8 pcdac_max; u8 pcdac_max;
}; };
struct ath5k_chan_pcal_info_rf5112 { struct ath5k_chan_pcal_info_rf5112 {
/* Power levels in quarter dBm units /* Power levels in quarter dBm units
* for lower (0) and higher (3) * for lower (0) and higher (3)
* level curves */ * level curves in 0.25dB units */
s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS]; s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS]; s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
/* PCDAC table steps /* PCDAC table steps
* for the above values */ * for the above values */
u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS]; u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS]; u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
}; };
struct ath5k_chan_pcal_info_rf2413 { struct ath5k_chan_pcal_info_rf2413 {
/* Starting pwr/pddac values */ /* Starting pwr/pddac values */
s8 pwr_i[AR5K_EEPROM_N_PD_GAINS]; s8 pwr_i[AR5K_EEPROM_N_PD_GAINS];
u8 pddac_i[AR5K_EEPROM_N_PD_GAINS]; u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
/* (pwr,pddac) points */ /* (pwr,pddac) points
s8 pwr[AR5K_EEPROM_N_PD_GAINS] * power levels in 0.5dB units */
[AR5K_EEPROM_N_PD_POINTS]; s8 pwr[AR5K_EEPROM_N_PD_GAINS]
u8 pddac[AR5K_EEPROM_N_PD_GAINS] [AR5K_EEPROM_N_PD_POINTS];
[AR5K_EEPROM_N_PD_POINTS]; u8 pddac[AR5K_EEPROM_N_PD_GAINS]
[AR5K_EEPROM_N_PD_POINTS];
};
enum ath5k_powertable_type {
AR5K_PWRTABLE_PWR_TO_PCDAC = 0,
AR5K_PWRTABLE_LINEAR_PCDAC = 1,
AR5K_PWRTABLE_PWR_TO_PDADC = 2,
};
struct ath5k_pdgain_info {
u8 pd_points;
u8 *pd_step;
/* Power values are in
* 0.25dB units */
s16 *pd_pwr;
}; };
struct ath5k_chan_pcal_info { struct ath5k_chan_pcal_info {
/* Frequency */ /* Frequency */
u16 freq; u16 freq;
/* Max available power */ /* Tx power boundaries */
s8 max_pwr; s16 max_pwr;
s16 min_pwr;
union { union {
struct ath5k_chan_pcal_info_rf5111 rf5111_info; struct ath5k_chan_pcal_info_rf5111 rf5111_info;
struct ath5k_chan_pcal_info_rf5112 rf5112_info; struct ath5k_chan_pcal_info_rf5112 rf5112_info;
struct ath5k_chan_pcal_info_rf2413 rf2413_info; struct ath5k_chan_pcal_info_rf2413 rf2413_info;
}; };
/* Raw values used by phy code
* Curves are stored in order from lower
* gain to higher gain (max txpower -> min txpower) */
struct ath5k_pdgain_info *pd_curves;
}; };
/* Per rate calibration data for each mode, used for power table setup */ /* Per rate calibration data for each mode,
* used for rate power table setup.
* Note: Values in 0.5dB units */
struct ath5k_rate_pcal_info { struct ath5k_rate_pcal_info {
u16 freq; /* Frequency */ u16 freq; /* Frequency */
/* Power level for 6-24Mbit/s rates */ /* Power level for 6-24Mbit/s rates or
* 1Mb rate */
u16 target_power_6to24; u16 target_power_6to24;
/* Power level for 36Mbit rate */ /* Power level for 36Mbit rate or
* 2Mb rate */
u16 target_power_36; u16 target_power_36;
/* Power level for 48Mbit rate */ /* Power level for 48Mbit rate or
* 5.5Mbit rate */
u16 target_power_48; u16 target_power_48;
/* Power level for 54Mbit rate */ /* Power level for 54Mbit rate or
* 11Mbit rate */
u16 target_power_54; u16 target_power_54;
}; };
...@@ -330,12 +380,6 @@ struct ath5k_eeprom_info { ...@@ -330,12 +380,6 @@ struct ath5k_eeprom_info {
u16 ee_cck_ofdm_power_delta; u16 ee_cck_ofdm_power_delta;
u16 ee_scaled_cck_delta; u16 ee_scaled_cck_delta;
/* Used for tx thermal adjustment (eeprom_init, rfregs) */
u16 ee_tx_clip;
u16 ee_pwd_84;
u16 ee_pwd_90;
u16 ee_gain_select;
/* RF Calibration settings (reset, rfregs) */ /* RF Calibration settings (reset, rfregs) */
u16 ee_i_cal[AR5K_EEPROM_N_MODES]; u16 ee_i_cal[AR5K_EEPROM_N_MODES];
u16 ee_q_cal[AR5K_EEPROM_N_MODES]; u16 ee_q_cal[AR5K_EEPROM_N_MODES];
...@@ -363,23 +407,25 @@ struct ath5k_eeprom_info { ...@@ -363,23 +407,25 @@ struct ath5k_eeprom_info {
/* Power calibration data */ /* Power calibration data */
u16 ee_false_detect[AR5K_EEPROM_N_MODES]; u16 ee_false_detect[AR5K_EEPROM_N_MODES];
/* Number of pd gain curves per mode (RF2413) */ /* Number of pd gain curves per mode */
u8 ee_pd_gains[AR5K_EEPROM_N_MODES]; u8 ee_pd_gains[AR5K_EEPROM_N_MODES];
/* Back mapping pdcurve number -> pdcurve index in pd->pd_curves */
u8 ee_pdc_to_idx[AR5K_EEPROM_N_MODES][AR5K_EEPROM_N_PD_GAINS];
u8 ee_n_piers[AR5K_EEPROM_N_MODES]; u8 ee_n_piers[AR5K_EEPROM_N_MODES];
struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN]; struct ath5k_chan_pcal_info ee_pwr_cal_a[AR5K_EEPROM_N_5GHZ_CHAN];
struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN]; struct ath5k_chan_pcal_info ee_pwr_cal_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN]; struct ath5k_chan_pcal_info ee_pwr_cal_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
/* Per rate target power levels */ /* Per rate target power levels */
u16 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES]; u8 ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN]; struct ath5k_rate_pcal_info ee_rate_tpwr_a[AR5K_EEPROM_N_5GHZ_CHAN];
struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN]; struct ath5k_rate_pcal_info ee_rate_tpwr_b[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN]; struct ath5k_rate_pcal_info ee_rate_tpwr_g[AR5K_EEPROM_N_2GHZ_CHAN_MAX];
/* Conformance test limits (Unused) */ /* Conformance test limits (Unused) */
u16 ee_ctls; u8 ee_ctls;
u16 ee_ctl[AR5K_EEPROM_MAX_CTLS]; u8 ee_ctl[AR5K_EEPROM_MAX_CTLS];
struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS]; struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS];
/* Noise Floor Calibration settings */ /* Noise Floor Calibration settings */
......
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