ath5k: Convert chip specific calibration data to a generic format
authorNick Kossifidis <mick@madwifi-project.org>
Sun, 15 Mar 2009 20:17:04 +0000 (22:17 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Sat, 28 Mar 2009 00:12:54 +0000 (20:12 -0400)
* 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: Nick Kossifidis <mickflemm@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath5k/eeprom.c
drivers/net/wireless/ath5k/eeprom.h

index ac45ca47ca87fa483850d0295dd4a3a73b1eaa96..c0fb3b09ba45f0e28b927f008f1d74736a683717 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org>
- * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com>
- * Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com>
+ * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -98,11 +98,6 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah)
        int ret;
        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
         */
@@ -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);
        switch(mode) {
        case AR5K_EEPROM_MODE_11A:
-               ee->ee_ob[mode][3]              = (val >> 5) & 0x7;
-               ee->ee_db[mode][3]              = (val >> 2) & 0x7;
-               ee->ee_ob[mode][2]              = (val << 1) & 0x7;
+               ee->ee_ob[mode][3]      = (val >> 5) & 0x7;
+               ee->ee_db[mode][3]      = (val >> 2) & 0x7;
+               ee->ee_ob[mode][2]      = (val << 1) & 0x7;
 
                AR5K_EEPROM_READ(o++, val);
-               ee->ee_ob[mode][2]              |= (val >> 15) & 0x1;
-               ee->ee_db[mode][2]              = (val >> 12) & 0x7;
-               ee->ee_ob[mode][1]              = (val >> 9) & 0x7;
-               ee->ee_db[mode][1]              = (val >> 6) & 0x7;
-               ee->ee_ob[mode][0]              = (val >> 3) & 0x7;
-               ee->ee_db[mode][0]              = val & 0x7;
+               ee->ee_ob[mode][2]      |= (val >> 15) & 0x1;
+               ee->ee_db[mode][2]      = (val >> 12) & 0x7;
+               ee->ee_ob[mode][1]      = (val >> 9) & 0x7;
+               ee->ee_db[mode][1]      = (val >> 6) & 0x7;
+               ee->ee_ob[mode][0]      = (val >> 3) & 0x7;
+               ee->ee_db[mode][0]      = val & 0x7;
                break;
        case AR5K_EEPROM_MODE_11G:
        case AR5K_EEPROM_MODE_11B:
-               ee->ee_ob[mode][1]              = (val >> 4) & 0x7;
-               ee->ee_db[mode][1]              = val & 0x7;
+               ee->ee_ob[mode][1]      = (val >> 4) & 0x7;
+               ee->ee_db[mode][1]      = val & 0x7;
                break;
        }
 
@@ -504,35 +499,6 @@ ath5k_eeprom_init_modes(struct ath5k_hw *ah)
        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
  * frequency mask) */
 static inline int
@@ -546,26 +512,25 @@ ath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max,
        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 */
@@ -652,13 +617,122 @@ ath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset)
        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 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.
+ * for each calibrated channel. Each curve has 0,5dB Power steps
+ * on x axis and PCDAC steps (offsets) on y axis and looks like an
+ * exponential function. To recreate the curve we read 11 points
+ * 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
 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);
        }
 
-       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 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.
+ * use the higher (3) and the lower (0) curves. Each curve has 0.5dB
+ * power steps on x axis and PCDAC steps on y axis and looks like a
+ * linear function. To recreate the curve and pass the power values
+ * on hw, we read 4 points for xpd 0 (lower gain -> max power)
+ * 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.
  */
+
+/* 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
 ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
 {
        struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
        struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info;
        struct ath5k_chan_pcal_info *gen_chan_info;
+       u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
        u32 offset;
-       unsigned int i, c;
+       u8 i, c;
        u16 val;
        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) {
        case AR5K_EEPROM_MODE_11A:
@@ -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++) {
                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
                 * (0 dBm -> higher output power) */
                for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) {
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pwr_x0[c] = (val & 0xff);
-                       chan_pcal_info->pwr_x0[++c] = ((val >> 8) & 0xff);
+                       chan_pcal_info->pwr_x0[c] = (s8) (val & 0xff);
+                       chan_pcal_info->pwr_x0[++c] = (s8) ((val >> 8) & 0xff);
                }
 
                /* PCDAC steps
@@ -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[3] = ((val >> 10) & 0x1f);
 
-               /* Power values in dBm * 4
+               /* Power values in quarter dB
                 * for the higher xpd gain curve
                 * (18 dBm -> lower output power) */
                AR5K_EEPROM_READ(offset++, val);
-               chan_pcal_info->pwr_x3[0] = (val & 0xff);
-               chan_pcal_info->pwr_x3[1] = ((val >> 8) & 0xff);
+               chan_pcal_info->pwr_x3[0] = (s8) (val & 0xff);
+               chan_pcal_info->pwr_x3[1] = (s8) ((val >> 8) & 0xff);
 
                AR5K_EEPROM_READ(offset++, val);
                chan_pcal_info->pwr_x3[2] = (val & 0xff);
@@ -843,24 +1052,36 @@ ath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode)
                chan_pcal_info->pcdac_x3[2] = 63;
 
                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 */
                        gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3];
                } else {
                        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
  * 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
@@ -890,13 +1111,15 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
        switch(mode) {
        case AR5K_EEPROM_MODE_11G:
                if (AR5K_EEPROM_HDR_11B(ee->ee_header))
-                       offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11B) +
-                                                       AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
+                       offset += ath5k_pdgains_size_2413(ee,
+                                       AR5K_EEPROM_MODE_11B) +
+                                       AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
                /* fall through */
        case AR5K_EEPROM_MODE_11B:
                if (AR5K_EEPROM_HDR_11A(ee->ee_header))
-                       offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11A) +
-                                                       AR5K_EEPROM_N_5GHZ_CHAN / 2;
+                       offset += ath5k_pdgains_size_2413(ee,
+                                       AR5K_EEPROM_MODE_11A) +
+                                       AR5K_EEPROM_N_5GHZ_CHAN / 2;
                /* fall through */
        case AR5K_EEPROM_MODE_11A:
                break;
@@ -907,37 +1130,118 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode)
        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.
- */
+/* Convert RF2413 specific data to generic raw data
+ * used by interpolation code */
+static int
+ath5k_eeprom_convert_pcal_info_2413(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_rf2413 *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].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
 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_rf2413 *chan_pcal_info;
-       struct ath5k_chan_pcal_info *gen_chan_info;
-       unsigned int i, c;
+       struct ath5k_chan_pcal_info_rf2413 *pcinfo;
+       struct ath5k_chan_pcal_info *chinfo;
+       u8 *pdgain_idx = ee->ee_pdc_to_idx[mode];
        u32 offset;
-       int ret;
+       int idx, i, 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++;
+       /* 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 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;
 
+       if (pd_gains == 0)
+               return -EINVAL;
+
        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))
@@ -945,7 +1249,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
 
                ath5k_eeprom_init_11a_pcal_freq(ah, offset);
                offset += AR5K_EEPROM_N_5GHZ_CHAN / 2;
-               gen_chan_info = ee->ee_pwr_cal_a;
+               chinfo = ee->ee_pwr_cal_a;
                break;
        case AR5K_EEPROM_MODE_11B:
                if (!AR5K_EEPROM_HDR_11B(ee->ee_header))
@@ -953,7 +1257,7 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
 
                ath5k_eeprom_init_11bg_2413(ah, mode, offset);
                offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
-               gen_chan_info = ee->ee_pwr_cal_b;
+               chinfo = ee->ee_pwr_cal_b;
                break;
        case AR5K_EEPROM_MODE_11G:
                if (!AR5K_EEPROM_HDR_11G(ee->ee_header))
@@ -961,41 +1265,35 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
 
                ath5k_eeprom_init_11bg_2413(ah, mode, offset);
                offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2;
-               gen_chan_info = ee->ee_pwr_cal_g;
+               chinfo = ee->ee_pwr_cal_g;
                break;
        default:
                return -EINVAL;
        }
 
-       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 +1301,36 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
                         * 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;
-
-                       chan_pcal_info->pddac[1][2] =
-                                               (val >> 14) & 0x3;
+                       pcinfo->pwr[1][1] = val & 0xf;
+                       pcinfo->pddac[1][1] = (val >> 4) & 0x3f;
+                       pcinfo->pwr[1][2] = (val >> 10) & 0xf;
+
+                       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 +1338,65 @@ ath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode)
                 * 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;
+                       pcinfo->pwr[2][0] = (val >> 0) & 0xf;
+                       pcinfo->pddac[2][0] = (val >> 4) & 0x3f;
+                       pcinfo->pwr[2][1] = (val >> 10) & 0xf;
+
+                       pcinfo->pddac[2][1] = (val >> 14) & 0x3;
                        AR5K_EEPROM_READ(offset++, val);
-                       chan_pcal_info->pddac[2][1] |=
-                                               (val & 0xF) << 2;
+                       pcinfo->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][2] = (val >> 4) & 0xf;
+                       pcinfo->pddac[2][2] = (val >> 8) & 0x3f;
 
-                       chan_pcal_info->pwr[2][3] = 0;
-                       chan_pcal_info->pddac[2][3] = 0;
+                       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;
-
-                       chan_pcal_info->pwr[3][2] =
-                                               (val >> 14) & 0x3;
+                       pcinfo->pddac[3][0] |= (val & 0xF) << 2;
+                       pcinfo->pwr[3][1] = (val >> 4) & 0xf;
+                       pcinfo->pddac[3][1] = (val >> 8) & 0x3f;
+
+                       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;
-
-                       chan_pcal_info->pddac[2][3] =
-                                               (val >> 2) & 0x3f;
-               }
+                       pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2;
 
-               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;
+                       pcinfo->pddac[2][3] = (val >> 2) & 0x3f;
                }
        }
 
-       return 0;
+       return ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo);
 }
 
+
 /*
  * Read per rate target power (this is the maximum tx power
  * 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)
  *
  * 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_rate_pcal_info *rate_pcal_info;
-       u16 *rate_target_pwr_num;
+       u8 *rate_target_pwr_num;
        u32 offset;
        u16 val;
        int ret, i;
@@ -1264,7 +1515,9 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
        else
                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);
                if (err)
                        return err;
@@ -1277,6 +1530,62 @@ ath5k_eeprom_read_pcal_info(struct ath5k_hw *ah)
        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 */
 static int
 ath5k_eeprom_read_ctl_info(struct ath5k_hw *ah)
@@ -1457,3 +1766,4 @@ bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah)
        else
                return false;
 }
+
index 1deebc0257d40d63d44346d5dbb19b31cbb518af..b0c0606dea0be78b785376390f6f98b504a54110 100644 (file)
 #define AR5K_EEPROM_N_5GHZ_CHAN                10
 #define AR5K_EEPROM_N_2GHZ_CHAN                3
 #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_N_PWR_POINTS_5111  11
 #define AR5K_EEPROM_N_PCDAC            11
 #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_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_XPD3_POINTS      3
 #define AR5K_EEPROM_N_PD_GAINS         4
@@ -232,7 +233,7 @@ enum ath5k_ctl_mode {
        AR5K_CTL_11B = 1,
        AR5K_CTL_11G = 2,
        AR5K_CTL_TURBO = 3,
-       AR5K_CTL_108G = 4,
+       AR5K_CTL_TURBOG = 4,
        AR5K_CTL_2GHT20 = 5,
        AR5K_CTL_5GHT20 = 6,
        AR5K_CTL_2GHT40 = 7,
@@ -240,65 +241,114 @@ enum ath5k_ctl_mode {
        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 */
 struct ath5k_chan_pcal_info_rf5111 {
        /* Power levels in half dbm units
         * for one power curve. */
-       u8              pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
+       u8 pwr[AR5K_EEPROM_N_PWR_POINTS_5111];
        /* PCDAC table steps
         * for the above values */
-       u8              pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
+       u8 pcdac[AR5K_EEPROM_N_PWR_POINTS_5111];
        /* Starting PCDAC step */
-       u8              pcdac_min;
+       u8 pcdac_min;
        /* Final PCDAC step */
-       u8              pcdac_max;
+       u8 pcdac_max;
 };
 
 struct ath5k_chan_pcal_info_rf5112 {
        /* Power levels in quarter dBm units
         * for lower (0) and higher (3)
-        * level curves */
-       s8              pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
-       s8              pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
+        * level curves in 0.25dB units */
+       s8 pwr_x0[AR5K_EEPROM_N_XPD0_POINTS];
+       s8 pwr_x3[AR5K_EEPROM_N_XPD3_POINTS];
        /* PCDAC table steps
         * for the above values */
-       u8      pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
-       u8      pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
+       u8 pcdac_x0[AR5K_EEPROM_N_XPD0_POINTS];
+       u8 pcdac_x3[AR5K_EEPROM_N_XPD3_POINTS];
 };
 
 struct ath5k_chan_pcal_info_rf2413 {
        /* Starting pwr/pddac values */
-       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_i[AR5K_EEPROM_N_PD_GAINS];
+       u8 pddac_i[AR5K_EEPROM_N_PD_GAINS];
+       /* (pwr,pddac) points
+        * power levels in 0.5dB units */
+       s8 pwr[AR5K_EEPROM_N_PD_GAINS]
+               [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 {
        /* Frequency */
        u16     freq;
-       /* Max available power */
-       s8              max_pwr;
+       /* Tx power boundaries */
+       s16     max_pwr;
+       s16     min_pwr;
        union {
                struct ath5k_chan_pcal_info_rf5111 rf5111_info;
                struct ath5k_chan_pcal_info_rf5112 rf5112_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 {
        u16     freq; /* Frequency */
-       /* Power level for 6-24Mbit/s rates */
+       /* Power level for 6-24Mbit/s rates or
+        * 1Mb rate */
        u16     target_power_6to24;
-       /* Power level for 36Mbit rate */
+       /* Power level for 36Mbit rate or
+        * 2Mb rate */
        u16     target_power_36;
-       /* Power level for 48Mbit rate */
+       /* Power level for 48Mbit rate or
+        * 5.5Mbit rate */
        u16     target_power_48;
-       /* Power level for 54Mbit rate */
+       /* Power level for 54Mbit rate or
+        * 11Mbit rate */
        u16     target_power_54;
 };
 
@@ -330,12 +380,6 @@ struct ath5k_eeprom_info {
        u16     ee_cck_ofdm_power_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) */
        u16     ee_i_cal[AR5K_EEPROM_N_MODES];
        u16     ee_q_cal[AR5K_EEPROM_N_MODES];
@@ -363,23 +407,25 @@ struct ath5k_eeprom_info {
        /* Power calibration data */
        u16     ee_false_detect[AR5K_EEPROM_N_MODES];
 
-       /* Number of pd gain curves per mode (RF2413) */
-       u8 ee_pd_gains[AR5K_EEPROM_N_MODES];
+       /* Number of pd gain curves per mode */
+       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_b[AR5K_EEPROM_N_2GHZ_CHAN];
-       struct ath5k_chan_pcal_info     ee_pwr_cal_g[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_MAX];
 
        /* Per rate target power levels */
-       u16     ee_rate_target_pwr_num[AR5K_EEPROM_N_MODES];
+       u     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_b[AR5K_EEPROM_N_2GHZ_CHAN];
-       struct ath5k_rate_pcal_info     ee_rate_tpwr_g[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_MAX];
 
        /* Conformance test limits (Unused) */
-       u16     ee_ctls;
-       u16     ee_ctl[AR5K_EEPROM_MAX_CTLS];
+       u     ee_ctls;
+       u     ee_ctl[AR5K_EEPROM_MAX_CTLS];
        struct ath5k_edge_power ee_ctl_pwr[AR5K_EEPROM_N_EDGES * AR5K_EEPROM_MAX_CTLS];
 
        /* Noise Floor Calibration settings */