162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> 362306a36Sopenharmony_ci * Copyright (c) 2006-2009 Nick Kossifidis <mickflemm@gmail.com> 462306a36Sopenharmony_ci * Copyright (c) 2008-2009 Felix Fietkau <nbd@openwrt.org> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Permission to use, copy, modify, and distribute this software for any 762306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 862306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1162306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1262306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1362306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1462306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1562306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1662306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/*************************************\ 2162306a36Sopenharmony_ci* EEPROM access functions and helpers * 2262306a36Sopenharmony_ci\*************************************/ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "ath5k.h" 2962306a36Sopenharmony_ci#include "reg.h" 3062306a36Sopenharmony_ci#include "debug.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/******************\ 3462306a36Sopenharmony_ci* Helper functions * 3562306a36Sopenharmony_ci\******************/ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * Translate binary channel representation in EEPROM to frequency 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistatic u16 ath5k_eeprom_bin2freq(struct ath5k_eeprom_info *ee, u16 bin, 4162306a36Sopenharmony_ci unsigned int mode) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci u16 val; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (bin == AR5K_EEPROM_CHANNEL_DIS) 4662306a36Sopenharmony_ci return bin; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (mode == AR5K_EEPROM_MODE_11A) { 4962306a36Sopenharmony_ci if (ee->ee_version > AR5K_EEPROM_VERSION_3_2) 5062306a36Sopenharmony_ci val = (5 * bin) + 4800; 5162306a36Sopenharmony_ci else 5262306a36Sopenharmony_ci val = bin > 62 ? (10 * 62) + (5 * (bin - 62)) + 5100 : 5362306a36Sopenharmony_ci (bin * 10) + 5100; 5462306a36Sopenharmony_ci } else { 5562306a36Sopenharmony_ci if (ee->ee_version > AR5K_EEPROM_VERSION_3_2) 5662306a36Sopenharmony_ci val = bin + 2300; 5762306a36Sopenharmony_ci else 5862306a36Sopenharmony_ci val = bin + 2400; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return val; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/*********\ 6662306a36Sopenharmony_ci* Parsers * 6762306a36Sopenharmony_ci\*********/ 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* 7062306a36Sopenharmony_ci * Initialize eeprom & capabilities structs 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic int 7362306a36Sopenharmony_ciath5k_eeprom_init_header(struct ath5k_hw *ah) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 7662306a36Sopenharmony_ci u16 val; 7762306a36Sopenharmony_ci u32 cksum, offset, eep_max = AR5K_EEPROM_INFO_MAX; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* 8062306a36Sopenharmony_ci * Read values from EEPROM and store them in the capability structure 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MAGIC, ee_magic); 8362306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_PROTECT, ee_protect); 8462306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_REG_DOMAIN, ee_regdomain); 8562306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_VERSION, ee_version); 8662306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_HDR, ee_header); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* Return if we have an old EEPROM */ 8962306a36Sopenharmony_ci if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_0) 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* 9362306a36Sopenharmony_ci * Validate the checksum of the EEPROM date. There are some 9462306a36Sopenharmony_ci * devices with invalid EEPROMs. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci AR5K_EEPROM_READ(AR5K_EEPROM_SIZE_UPPER, val); 9762306a36Sopenharmony_ci if (val) { 9862306a36Sopenharmony_ci eep_max = (val & AR5K_EEPROM_SIZE_UPPER_MASK) << 9962306a36Sopenharmony_ci AR5K_EEPROM_SIZE_ENDLOC_SHIFT; 10062306a36Sopenharmony_ci AR5K_EEPROM_READ(AR5K_EEPROM_SIZE_LOWER, val); 10162306a36Sopenharmony_ci eep_max = (eep_max | val) - AR5K_EEPROM_INFO_BASE; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * Fail safe check to prevent stupid loops due 10562306a36Sopenharmony_ci * to busted EEPROMs. XXX: This value is likely too 10662306a36Sopenharmony_ci * big still, waiting on a better value. 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci if (eep_max > (3 * AR5K_EEPROM_INFO_MAX)) { 10962306a36Sopenharmony_ci ATH5K_ERR(ah, "Invalid max custom EEPROM size: " 11062306a36Sopenharmony_ci "%d (0x%04x) max expected: %d (0x%04x)\n", 11162306a36Sopenharmony_ci eep_max, eep_max, 11262306a36Sopenharmony_ci 3 * AR5K_EEPROM_INFO_MAX, 11362306a36Sopenharmony_ci 3 * AR5K_EEPROM_INFO_MAX); 11462306a36Sopenharmony_ci return -EIO; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci for (cksum = 0, offset = 0; offset < eep_max; offset++) { 11962306a36Sopenharmony_ci AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val); 12062306a36Sopenharmony_ci cksum ^= val; 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci if (cksum != AR5K_EEPROM_INFO_CKSUM) { 12362306a36Sopenharmony_ci ATH5K_ERR(ah, "Invalid EEPROM " 12462306a36Sopenharmony_ci "checksum: 0x%04x eep_max: 0x%04x (%s)\n", 12562306a36Sopenharmony_ci cksum, eep_max, 12662306a36Sopenharmony_ci eep_max == AR5K_EEPROM_INFO_MAX ? 12762306a36Sopenharmony_ci "default size" : "custom size"); 12862306a36Sopenharmony_ci return -EIO; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_ANT_GAIN(ah->ah_ee_version), 13262306a36Sopenharmony_ci ee_ant_gain); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) { 13562306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC0, ee_misc0); 13662306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC1, ee_misc1); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* XXX: Don't know which versions include these two */ 13962306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC2, ee_misc2); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) 14262306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC3, ee_misc3); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (ee->ee_version >= AR5K_EEPROM_VERSION_5_0) { 14562306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC4, ee_misc4); 14662306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC5, ee_misc5); 14762306a36Sopenharmony_ci AR5K_EEPROM_READ_HDR(AR5K_EEPROM_MISC6, ee_misc6); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_3) { 15262306a36Sopenharmony_ci AR5K_EEPROM_READ(AR5K_EEPROM_OBDB0_2GHZ, val); 15362306a36Sopenharmony_ci ee->ee_ob[AR5K_EEPROM_MODE_11B][0] = val & 0x7; 15462306a36Sopenharmony_ci ee->ee_db[AR5K_EEPROM_MODE_11B][0] = (val >> 3) & 0x7; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci AR5K_EEPROM_READ(AR5K_EEPROM_OBDB1_2GHZ, val); 15762306a36Sopenharmony_ci ee->ee_ob[AR5K_EEPROM_MODE_11G][0] = val & 0x7; 15862306a36Sopenharmony_ci ee->ee_db[AR5K_EEPROM_MODE_11G][0] = (val >> 3) & 0x7; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci AR5K_EEPROM_READ(AR5K_EEPROM_IS_HB63, val); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if ((ah->ah_mac_version == (AR5K_SREV_AR2425 >> 4)) && val) 16462306a36Sopenharmony_ci ee->ee_is_hb63 = true; 16562306a36Sopenharmony_ci else 16662306a36Sopenharmony_ci ee->ee_is_hb63 = false; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci AR5K_EEPROM_READ(AR5K_EEPROM_RFKILL, val); 16962306a36Sopenharmony_ci ee->ee_rfkill_pin = (u8) AR5K_REG_MS(val, AR5K_EEPROM_RFKILL_GPIO_SEL); 17062306a36Sopenharmony_ci ee->ee_rfkill_pol = val & AR5K_EEPROM_RFKILL_POLARITY ? true : false; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* Check if PCIE_OFFSET points to PCIE_SERDES_SECTION 17362306a36Sopenharmony_ci * and enable serdes programming if needed. 17462306a36Sopenharmony_ci * 17562306a36Sopenharmony_ci * XXX: Serdes values seem to be fixed so 17662306a36Sopenharmony_ci * no need to read them here, we write them 17762306a36Sopenharmony_ci * during ath5k_hw_init */ 17862306a36Sopenharmony_ci AR5K_EEPROM_READ(AR5K_EEPROM_PCIE_OFFSET, val); 17962306a36Sopenharmony_ci ee->ee_serdes = (val == AR5K_EEPROM_PCIE_SERDES_SECTION) ? 18062306a36Sopenharmony_ci true : false; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* 18762306a36Sopenharmony_ci * Read antenna infos from eeprom 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistatic int ath5k_eeprom_read_ants(struct ath5k_hw *ah, u32 *offset, 19062306a36Sopenharmony_ci unsigned int mode) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 19362306a36Sopenharmony_ci u32 o = *offset; 19462306a36Sopenharmony_ci u16 val; 19562306a36Sopenharmony_ci int i = 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 19862306a36Sopenharmony_ci ee->ee_switch_settling[mode] = (val >> 8) & 0x7f; 19962306a36Sopenharmony_ci ee->ee_atn_tx_rx[mode] = (val >> 2) & 0x3f; 20062306a36Sopenharmony_ci ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 20362306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; 20462306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; 20562306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] = val & 0x3f; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 20862306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] = (val >> 10) & 0x3f; 20962306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] = (val >> 4) & 0x3f; 21062306a36Sopenharmony_ci ee->ee_ant_control[mode][i] = (val << 2) & 0x3f; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 21362306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] |= (val >> 14) & 0x3; 21462306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] = (val >> 8) & 0x3f; 21562306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] = (val >> 2) & 0x3f; 21662306a36Sopenharmony_ci ee->ee_ant_control[mode][i] = (val << 4) & 0x3f; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 21962306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] |= (val >> 12) & 0xf; 22062306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] = (val >> 6) & 0x3f; 22162306a36Sopenharmony_ci ee->ee_ant_control[mode][i++] = val & 0x3f; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Get antenna switch tables */ 22462306a36Sopenharmony_ci ah->ah_ant_ctl[mode][AR5K_ANT_CTL] = 22562306a36Sopenharmony_ci (ee->ee_ant_control[mode][0] << 4); 22662306a36Sopenharmony_ci ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_A] = 22762306a36Sopenharmony_ci ee->ee_ant_control[mode][1] | 22862306a36Sopenharmony_ci (ee->ee_ant_control[mode][2] << 6) | 22962306a36Sopenharmony_ci (ee->ee_ant_control[mode][3] << 12) | 23062306a36Sopenharmony_ci (ee->ee_ant_control[mode][4] << 18) | 23162306a36Sopenharmony_ci (ee->ee_ant_control[mode][5] << 24); 23262306a36Sopenharmony_ci ah->ah_ant_ctl[mode][AR5K_ANT_SWTABLE_B] = 23362306a36Sopenharmony_ci ee->ee_ant_control[mode][6] | 23462306a36Sopenharmony_ci (ee->ee_ant_control[mode][7] << 6) | 23562306a36Sopenharmony_ci (ee->ee_ant_control[mode][8] << 12) | 23662306a36Sopenharmony_ci (ee->ee_ant_control[mode][9] << 18) | 23762306a36Sopenharmony_ci (ee->ee_ant_control[mode][10] << 24); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* return new offset */ 24062306a36Sopenharmony_ci *offset = o; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* 24662306a36Sopenharmony_ci * Read supported modes and some mode-specific calibration data 24762306a36Sopenharmony_ci * from eeprom 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_cistatic int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, 25062306a36Sopenharmony_ci unsigned int mode) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 25362306a36Sopenharmony_ci u32 o = *offset; 25462306a36Sopenharmony_ci u16 val; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci ee->ee_n_piers[mode] = 0; 25762306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 25862306a36Sopenharmony_ci ee->ee_adc_desired_size[mode] = (s8)((val >> 8) & 0xff); 25962306a36Sopenharmony_ci switch (mode) { 26062306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 26162306a36Sopenharmony_ci ee->ee_ob[mode][3] = (val >> 5) & 0x7; 26262306a36Sopenharmony_ci ee->ee_db[mode][3] = (val >> 2) & 0x7; 26362306a36Sopenharmony_ci ee->ee_ob[mode][2] = (val << 1) & 0x7; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 26662306a36Sopenharmony_ci ee->ee_ob[mode][2] |= (val >> 15) & 0x1; 26762306a36Sopenharmony_ci ee->ee_db[mode][2] = (val >> 12) & 0x7; 26862306a36Sopenharmony_ci ee->ee_ob[mode][1] = (val >> 9) & 0x7; 26962306a36Sopenharmony_ci ee->ee_db[mode][1] = (val >> 6) & 0x7; 27062306a36Sopenharmony_ci ee->ee_ob[mode][0] = (val >> 3) & 0x7; 27162306a36Sopenharmony_ci ee->ee_db[mode][0] = val & 0x7; 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 27462306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 27562306a36Sopenharmony_ci ee->ee_ob[mode][1] = (val >> 4) & 0x7; 27662306a36Sopenharmony_ci ee->ee_db[mode][1] = val & 0x7; 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 28162306a36Sopenharmony_ci ee->ee_tx_end2xlna_enable[mode] = (val >> 8) & 0xff; 28262306a36Sopenharmony_ci ee->ee_thr_62[mode] = val & 0xff; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) 28562306a36Sopenharmony_ci ee->ee_thr_62[mode] = mode == AR5K_EEPROM_MODE_11A ? 15 : 28; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 28862306a36Sopenharmony_ci ee->ee_tx_end2xpa_disable[mode] = (val >> 8) & 0xff; 28962306a36Sopenharmony_ci ee->ee_tx_frm2xpa_enable[mode] = val & 0xff; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 29262306a36Sopenharmony_ci ee->ee_pga_desired_size[mode] = (val >> 8) & 0xff; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if ((val & 0xff) & 0x80) 29562306a36Sopenharmony_ci ee->ee_noise_floor_thr[mode] = -((((val & 0xff) ^ 0xff)) + 1); 29662306a36Sopenharmony_ci else 29762306a36Sopenharmony_ci ee->ee_noise_floor_thr[mode] = val & 0xff; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) 30062306a36Sopenharmony_ci ee->ee_noise_floor_thr[mode] = 30162306a36Sopenharmony_ci mode == AR5K_EEPROM_MODE_11A ? -54 : -1; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 30462306a36Sopenharmony_ci ee->ee_xlna_gain[mode] = (val >> 5) & 0xff; 30562306a36Sopenharmony_ci ee->ee_x_gain[mode] = (val >> 1) & 0xf; 30662306a36Sopenharmony_ci ee->ee_xpd[mode] = val & 0x1; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 && 30962306a36Sopenharmony_ci mode != AR5K_EEPROM_MODE_11B) 31062306a36Sopenharmony_ci ee->ee_fixed_bias[mode] = (val >> 13) & 0x1; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_3) { 31362306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 31462306a36Sopenharmony_ci ee->ee_false_detect[mode] = (val >> 6) & 0x7f; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (mode == AR5K_EEPROM_MODE_11A) 31762306a36Sopenharmony_ci ee->ee_xr_power[mode] = val & 0x3f; 31862306a36Sopenharmony_ci else { 31962306a36Sopenharmony_ci /* b_DB_11[bg] and b_OB_11[bg] */ 32062306a36Sopenharmony_ci ee->ee_ob[mode][0] = val & 0x7; 32162306a36Sopenharmony_ci ee->ee_db[mode][0] = (val >> 3) & 0x7; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (ah->ah_ee_version < AR5K_EEPROM_VERSION_3_4) { 32662306a36Sopenharmony_ci ee->ee_i_gain[mode] = AR5K_EEPROM_I_GAIN; 32762306a36Sopenharmony_ci ee->ee_cck_ofdm_power_delta = AR5K_EEPROM_CCK_OFDM_DELTA; 32862306a36Sopenharmony_ci } else { 32962306a36Sopenharmony_ci ee->ee_i_gain[mode] = (val >> 13) & 0x7; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 33262306a36Sopenharmony_ci ee->ee_i_gain[mode] |= (val << 3) & 0x38; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (mode == AR5K_EEPROM_MODE_11G) { 33562306a36Sopenharmony_ci ee->ee_cck_ofdm_power_delta = (val >> 3) & 0xff; 33662306a36Sopenharmony_ci if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_6) 33762306a36Sopenharmony_ci ee->ee_scaled_cck_delta = (val >> 11) & 0x1f; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0 && 34262306a36Sopenharmony_ci mode == AR5K_EEPROM_MODE_11A) { 34362306a36Sopenharmony_ci ee->ee_i_cal[mode] = (val >> 8) & 0x3f; 34462306a36Sopenharmony_ci ee->ee_q_cal[mode] = (val >> 3) & 0x1f; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_0) 34862306a36Sopenharmony_ci goto done; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Note: >= v5 have bg freq piers on another location 35162306a36Sopenharmony_ci * so these freq piers are ignored for >= v5 (should be 0xff 35262306a36Sopenharmony_ci * anyway) */ 35362306a36Sopenharmony_ci switch (mode) { 35462306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 35562306a36Sopenharmony_ci if (ah->ah_ee_version < AR5K_EEPROM_VERSION_4_1) 35662306a36Sopenharmony_ci break; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 35962306a36Sopenharmony_ci ee->ee_margin_tx_rx[mode] = val & 0x3f; 36062306a36Sopenharmony_ci break; 36162306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 36262306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ee->ee_pwr_cal_b[0].freq = 36562306a36Sopenharmony_ci ath5k_eeprom_bin2freq(ee, val & 0xff, mode); 36662306a36Sopenharmony_ci if (ee->ee_pwr_cal_b[0].freq != AR5K_EEPROM_CHANNEL_DIS) 36762306a36Sopenharmony_ci ee->ee_n_piers[mode]++; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ee->ee_pwr_cal_b[1].freq = 37062306a36Sopenharmony_ci ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); 37162306a36Sopenharmony_ci if (ee->ee_pwr_cal_b[1].freq != AR5K_EEPROM_CHANNEL_DIS) 37262306a36Sopenharmony_ci ee->ee_n_piers[mode]++; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 37562306a36Sopenharmony_ci ee->ee_pwr_cal_b[2].freq = 37662306a36Sopenharmony_ci ath5k_eeprom_bin2freq(ee, val & 0xff, mode); 37762306a36Sopenharmony_ci if (ee->ee_pwr_cal_b[2].freq != AR5K_EEPROM_CHANNEL_DIS) 37862306a36Sopenharmony_ci ee->ee_n_piers[mode]++; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) 38162306a36Sopenharmony_ci ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 38462306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ee->ee_pwr_cal_g[0].freq = 38762306a36Sopenharmony_ci ath5k_eeprom_bin2freq(ee, val & 0xff, mode); 38862306a36Sopenharmony_ci if (ee->ee_pwr_cal_g[0].freq != AR5K_EEPROM_CHANNEL_DIS) 38962306a36Sopenharmony_ci ee->ee_n_piers[mode]++; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci ee->ee_pwr_cal_g[1].freq = 39262306a36Sopenharmony_ci ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); 39362306a36Sopenharmony_ci if (ee->ee_pwr_cal_g[1].freq != AR5K_EEPROM_CHANNEL_DIS) 39462306a36Sopenharmony_ci ee->ee_n_piers[mode]++; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 39762306a36Sopenharmony_ci ee->ee_turbo_max_power[mode] = val & 0x7f; 39862306a36Sopenharmony_ci ee->ee_xr_power[mode] = (val >> 7) & 0x3f; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 40162306a36Sopenharmony_ci ee->ee_pwr_cal_g[2].freq = 40262306a36Sopenharmony_ci ath5k_eeprom_bin2freq(ee, val & 0xff, mode); 40362306a36Sopenharmony_ci if (ee->ee_pwr_cal_g[2].freq != AR5K_EEPROM_CHANNEL_DIS) 40462306a36Sopenharmony_ci ee->ee_n_piers[mode]++; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_1) 40762306a36Sopenharmony_ci ee->ee_margin_tx_rx[mode] = (val >> 8) & 0x3f; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 41062306a36Sopenharmony_ci ee->ee_i_cal[mode] = (val >> 5) & 0x3f; 41162306a36Sopenharmony_ci ee->ee_q_cal[mode] = val & 0x1f; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_2) { 41462306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 41562306a36Sopenharmony_ci ee->ee_cck_ofdm_gain_delta = val & 0xff; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* 42162306a36Sopenharmony_ci * Read turbo mode information on newer EEPROM versions 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci if (ee->ee_version < AR5K_EEPROM_VERSION_5_0) 42462306a36Sopenharmony_ci goto done; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci switch (mode) { 42762306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 42862306a36Sopenharmony_ci ee->ee_switch_settling_turbo[mode] = (val >> 6) & 0x7f; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci ee->ee_atn_tx_rx_turbo[mode] = (val >> 13) & 0x7; 43162306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 43262306a36Sopenharmony_ci ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x7) << 3; 43362306a36Sopenharmony_ci ee->ee_margin_tx_rx_turbo[mode] = (val >> 3) & 0x3f; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci ee->ee_adc_desired_size_turbo[mode] = (val >> 9) & 0x7f; 43662306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 43762306a36Sopenharmony_ci ee->ee_adc_desired_size_turbo[mode] |= (val & 0x1) << 7; 43862306a36Sopenharmony_ci ee->ee_pga_desired_size_turbo[mode] = (val >> 1) & 0xff; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (AR5K_EEPROM_EEMAP(ee->ee_misc0) >= 2) 44162306a36Sopenharmony_ci ee->ee_pd_gain_overlap = (val >> 9) & 0xf; 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 44462306a36Sopenharmony_ci ee->ee_switch_settling_turbo[mode] = (val >> 8) & 0x7f; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci ee->ee_atn_tx_rx_turbo[mode] = (val >> 15) & 0x7; 44762306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 44862306a36Sopenharmony_ci ee->ee_atn_tx_rx_turbo[mode] |= (val & 0x1f) << 1; 44962306a36Sopenharmony_ci ee->ee_margin_tx_rx_turbo[mode] = (val >> 5) & 0x3f; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci ee->ee_adc_desired_size_turbo[mode] = (val >> 11) & 0x7f; 45262306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 45362306a36Sopenharmony_ci ee->ee_adc_desired_size_turbo[mode] |= (val & 0x7) << 5; 45462306a36Sopenharmony_ci ee->ee_pga_desired_size_turbo[mode] = (val >> 3) & 0xff; 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cidone: 45962306a36Sopenharmony_ci /* return new offset */ 46062306a36Sopenharmony_ci *offset = o; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci/* Read mode-specific data (except power calibration data) */ 46662306a36Sopenharmony_cistatic int 46762306a36Sopenharmony_ciath5k_eeprom_init_modes(struct ath5k_hw *ah) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 47062306a36Sopenharmony_ci u32 mode_offset[3]; 47162306a36Sopenharmony_ci unsigned int mode; 47262306a36Sopenharmony_ci u32 offset; 47362306a36Sopenharmony_ci int ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci /* 47662306a36Sopenharmony_ci * Get values for all modes 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci mode_offset[AR5K_EEPROM_MODE_11A] = AR5K_EEPROM_MODES_11A(ah->ah_ee_version); 47962306a36Sopenharmony_ci mode_offset[AR5K_EEPROM_MODE_11B] = AR5K_EEPROM_MODES_11B(ah->ah_ee_version); 48062306a36Sopenharmony_ci mode_offset[AR5K_EEPROM_MODE_11G] = AR5K_EEPROM_MODES_11G(ah->ah_ee_version); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci ee->ee_turbo_max_power[AR5K_EEPROM_MODE_11A] = 48362306a36Sopenharmony_ci AR5K_EEPROM_HDR_T_5GHZ_DBM(ee->ee_header); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) { 48662306a36Sopenharmony_ci offset = mode_offset[mode]; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci ret = ath5k_eeprom_read_ants(ah, &offset, mode); 48962306a36Sopenharmony_ci if (ret) 49062306a36Sopenharmony_ci return ret; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ret = ath5k_eeprom_read_modes(ah, &offset, mode); 49362306a36Sopenharmony_ci if (ret) 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* override for older eeprom versions for better performance */ 49862306a36Sopenharmony_ci if (ah->ah_ee_version <= AR5K_EEPROM_VERSION_3_2) { 49962306a36Sopenharmony_ci ee->ee_thr_62[AR5K_EEPROM_MODE_11A] = 15; 50062306a36Sopenharmony_ci ee->ee_thr_62[AR5K_EEPROM_MODE_11B] = 28; 50162306a36Sopenharmony_ci ee->ee_thr_62[AR5K_EEPROM_MODE_11G] = 28; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci/* Read the frequency piers for each mode (mostly used on newer eeproms with 0xff 50862306a36Sopenharmony_ci * frequency mask) */ 50962306a36Sopenharmony_cistatic inline int 51062306a36Sopenharmony_ciath5k_eeprom_read_freq_list(struct ath5k_hw *ah, int *offset, int max, 51162306a36Sopenharmony_ci struct ath5k_chan_pcal_info *pc, unsigned int mode) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 51462306a36Sopenharmony_ci int o = *offset; 51562306a36Sopenharmony_ci int i = 0; 51662306a36Sopenharmony_ci u8 freq1, freq2; 51762306a36Sopenharmony_ci u16 val; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci ee->ee_n_piers[mode] = 0; 52062306a36Sopenharmony_ci while (i < max) { 52162306a36Sopenharmony_ci AR5K_EEPROM_READ(o++, val); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci freq1 = val & 0xff; 52462306a36Sopenharmony_ci if (!freq1) 52562306a36Sopenharmony_ci break; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci pc[i++].freq = ath5k_eeprom_bin2freq(ee, 52862306a36Sopenharmony_ci freq1, mode); 52962306a36Sopenharmony_ci ee->ee_n_piers[mode]++; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci freq2 = (val >> 8) & 0xff; 53262306a36Sopenharmony_ci if (!freq2 || i >= max) 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci pc[i++].freq = ath5k_eeprom_bin2freq(ee, 53662306a36Sopenharmony_ci freq2, mode); 53762306a36Sopenharmony_ci ee->ee_n_piers[mode]++; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* return new offset */ 54162306a36Sopenharmony_ci *offset = o; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/* Read frequency piers for 802.11a */ 54762306a36Sopenharmony_cistatic int 54862306a36Sopenharmony_ciath5k_eeprom_init_11a_pcal_freq(struct ath5k_hw *ah, int offset) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 55162306a36Sopenharmony_ci struct ath5k_chan_pcal_info *pcal = ee->ee_pwr_cal_a; 55262306a36Sopenharmony_ci int i; 55362306a36Sopenharmony_ci u16 val; 55462306a36Sopenharmony_ci u8 mask; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) { 55762306a36Sopenharmony_ci ath5k_eeprom_read_freq_list(ah, &offset, 55862306a36Sopenharmony_ci AR5K_EEPROM_N_5GHZ_CHAN, pcal, 55962306a36Sopenharmony_ci AR5K_EEPROM_MODE_11A); 56062306a36Sopenharmony_ci } else { 56162306a36Sopenharmony_ci mask = AR5K_EEPROM_FREQ_M(ah->ah_ee_version); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 56462306a36Sopenharmony_ci pcal[0].freq = (val >> 9) & mask; 56562306a36Sopenharmony_ci pcal[1].freq = (val >> 2) & mask; 56662306a36Sopenharmony_ci pcal[2].freq = (val << 5) & mask; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 56962306a36Sopenharmony_ci pcal[2].freq |= (val >> 11) & 0x1f; 57062306a36Sopenharmony_ci pcal[3].freq = (val >> 4) & mask; 57162306a36Sopenharmony_ci pcal[4].freq = (val << 3) & mask; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 57462306a36Sopenharmony_ci pcal[4].freq |= (val >> 13) & 0x7; 57562306a36Sopenharmony_ci pcal[5].freq = (val >> 6) & mask; 57662306a36Sopenharmony_ci pcal[6].freq = (val << 1) & mask; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 57962306a36Sopenharmony_ci pcal[6].freq |= (val >> 15) & 0x1; 58062306a36Sopenharmony_ci pcal[7].freq = (val >> 8) & mask; 58162306a36Sopenharmony_ci pcal[8].freq = (val >> 1) & mask; 58262306a36Sopenharmony_ci pcal[9].freq = (val << 6) & mask; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 58562306a36Sopenharmony_ci pcal[9].freq |= (val >> 10) & 0x3f; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* Fixed number of piers */ 58862306a36Sopenharmony_ci ee->ee_n_piers[AR5K_EEPROM_MODE_11A] = 10; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci for (i = 0; i < AR5K_EEPROM_N_5GHZ_CHAN; i++) { 59162306a36Sopenharmony_ci pcal[i].freq = ath5k_eeprom_bin2freq(ee, 59262306a36Sopenharmony_ci pcal[i].freq, AR5K_EEPROM_MODE_11A); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/* Read frequency piers for 802.11bg on eeprom versions >= 5 and eemap >= 2 */ 60062306a36Sopenharmony_cistatic inline int 60162306a36Sopenharmony_ciath5k_eeprom_init_11bg_2413(struct ath5k_hw *ah, unsigned int mode, int offset) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 60462306a36Sopenharmony_ci struct ath5k_chan_pcal_info *pcal; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci switch (mode) { 60762306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 60862306a36Sopenharmony_ci pcal = ee->ee_pwr_cal_b; 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 61162306a36Sopenharmony_ci pcal = ee->ee_pwr_cal_g; 61262306a36Sopenharmony_ci break; 61362306a36Sopenharmony_ci default: 61462306a36Sopenharmony_ci return -EINVAL; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci ath5k_eeprom_read_freq_list(ah, &offset, 61862306a36Sopenharmony_ci AR5K_EEPROM_N_2GHZ_CHAN_2413, pcal, 61962306a36Sopenharmony_ci mode); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci/* 62662306a36Sopenharmony_ci * Read power calibration for RF5111 chips 62762306a36Sopenharmony_ci * 62862306a36Sopenharmony_ci * For RF5111 we have an XPD -eXternal Power Detector- curve 62962306a36Sopenharmony_ci * for each calibrated channel. Each curve has 0,5dB Power steps 63062306a36Sopenharmony_ci * on x axis and PCDAC steps (offsets) on y axis and looks like an 63162306a36Sopenharmony_ci * exponential function. To recreate the curve we read 11 points 63262306a36Sopenharmony_ci * here and interpolate later. 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci/* Used to match PCDAC steps with power values on RF5111 chips 63662306a36Sopenharmony_ci * (eeprom versions < 4). For RF5111 we have 11 pre-defined PCDAC 63762306a36Sopenharmony_ci * steps that match with the power values we read from eeprom. On 63862306a36Sopenharmony_ci * older eeprom versions (< 3.2) these steps are equally spaced at 63962306a36Sopenharmony_ci * 10% of the pcdac curve -until the curve reaches its maximum- 64062306a36Sopenharmony_ci * (11 steps from 0 to 100%) but on newer eeprom versions (>= 3.2) 64162306a36Sopenharmony_ci * these 11 steps are spaced in a different way. This function returns 64262306a36Sopenharmony_ci * the pcdac steps based on eeprom version and curve min/max so that we 64362306a36Sopenharmony_ci * can have pcdac/pwr points. 64462306a36Sopenharmony_ci */ 64562306a36Sopenharmony_cistatic inline void 64662306a36Sopenharmony_ciath5k_get_pcdac_intercepts(struct ath5k_hw *ah, u8 min, u8 max, u8 *vp) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci static const u16 intercepts3[] = { 64962306a36Sopenharmony_ci 0, 5, 10, 20, 30, 50, 70, 85, 90, 95, 100 65062306a36Sopenharmony_ci }; 65162306a36Sopenharmony_ci static const u16 intercepts3_2[] = { 65262306a36Sopenharmony_ci 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 65362306a36Sopenharmony_ci }; 65462306a36Sopenharmony_ci const u16 *ip; 65562306a36Sopenharmony_ci int i; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (ah->ah_ee_version >= AR5K_EEPROM_VERSION_3_2) 65862306a36Sopenharmony_ci ip = intercepts3_2; 65962306a36Sopenharmony_ci else 66062306a36Sopenharmony_ci ip = intercepts3; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(intercepts3); i++) 66362306a36Sopenharmony_ci vp[i] = (ip[i] * max + (100 - ip[i]) * min) / 100; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic int 66762306a36Sopenharmony_ciath5k_eeprom_free_pcal_info(struct ath5k_hw *ah, int mode) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 67062306a36Sopenharmony_ci struct ath5k_chan_pcal_info *chinfo; 67162306a36Sopenharmony_ci u8 pier, pdg; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci switch (mode) { 67462306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 67562306a36Sopenharmony_ci if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) 67662306a36Sopenharmony_ci return 0; 67762306a36Sopenharmony_ci chinfo = ee->ee_pwr_cal_a; 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 68062306a36Sopenharmony_ci if (!AR5K_EEPROM_HDR_11B(ee->ee_header)) 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci chinfo = ee->ee_pwr_cal_b; 68362306a36Sopenharmony_ci break; 68462306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 68562306a36Sopenharmony_ci if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) 68662306a36Sopenharmony_ci return 0; 68762306a36Sopenharmony_ci chinfo = ee->ee_pwr_cal_g; 68862306a36Sopenharmony_ci break; 68962306a36Sopenharmony_ci default: 69062306a36Sopenharmony_ci return -EINVAL; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { 69462306a36Sopenharmony_ci if (!chinfo[pier].pd_curves) 69562306a36Sopenharmony_ci continue; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci for (pdg = 0; pdg < AR5K_EEPROM_N_PD_CURVES; pdg++) { 69862306a36Sopenharmony_ci struct ath5k_pdgain_info *pd = 69962306a36Sopenharmony_ci &chinfo[pier].pd_curves[pdg]; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci kfree(pd->pd_step); 70262306a36Sopenharmony_ci kfree(pd->pd_pwr); 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci kfree(chinfo[pier].pd_curves); 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return 0; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci/* Convert RF5111 specific data to generic raw data 71262306a36Sopenharmony_ci * used by interpolation code */ 71362306a36Sopenharmony_cistatic int 71462306a36Sopenharmony_ciath5k_eeprom_convert_pcal_info_5111(struct ath5k_hw *ah, int mode, 71562306a36Sopenharmony_ci struct ath5k_chan_pcal_info *chinfo) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 71862306a36Sopenharmony_ci struct ath5k_chan_pcal_info_rf5111 *pcinfo; 71962306a36Sopenharmony_ci struct ath5k_pdgain_info *pd; 72062306a36Sopenharmony_ci u8 pier, point, idx; 72162306a36Sopenharmony_ci u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* Fill raw data for each calibration pier */ 72462306a36Sopenharmony_ci for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci pcinfo = &chinfo[pier].rf5111_info; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* Allocate pd_curves for this cal pier */ 72962306a36Sopenharmony_ci chinfo[pier].pd_curves = 73062306a36Sopenharmony_ci kcalloc(AR5K_EEPROM_N_PD_CURVES, 73162306a36Sopenharmony_ci sizeof(struct ath5k_pdgain_info), 73262306a36Sopenharmony_ci GFP_KERNEL); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (!chinfo[pier].pd_curves) 73562306a36Sopenharmony_ci goto err_out; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* Only one curve for RF5111 73862306a36Sopenharmony_ci * find out which one and place 73962306a36Sopenharmony_ci * in pd_curves. 74062306a36Sopenharmony_ci * Note: ee_x_gain is reversed here */ 74162306a36Sopenharmony_ci for (idx = 0; idx < AR5K_EEPROM_N_PD_CURVES; idx++) { 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!((ee->ee_x_gain[mode] >> idx) & 0x1)) { 74462306a36Sopenharmony_ci pdgain_idx[0] = idx; 74562306a36Sopenharmony_ci break; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (idx == AR5K_EEPROM_N_PD_CURVES) 75062306a36Sopenharmony_ci goto err_out; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci ee->ee_pd_gains[mode] = 1; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci pd = &chinfo[pier].pd_curves[idx]; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci pd->pd_points = AR5K_EEPROM_N_PWR_POINTS_5111; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* Allocate pd points for this curve */ 75962306a36Sopenharmony_ci pd->pd_step = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111, 76062306a36Sopenharmony_ci sizeof(u8), GFP_KERNEL); 76162306a36Sopenharmony_ci if (!pd->pd_step) 76262306a36Sopenharmony_ci goto err_out; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci pd->pd_pwr = kcalloc(AR5K_EEPROM_N_PWR_POINTS_5111, 76562306a36Sopenharmony_ci sizeof(s16), GFP_KERNEL); 76662306a36Sopenharmony_ci if (!pd->pd_pwr) 76762306a36Sopenharmony_ci goto err_out; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Fill raw dataset 77062306a36Sopenharmony_ci * (convert power to 0.25dB units 77162306a36Sopenharmony_ci * for RF5112 compatibility) */ 77262306a36Sopenharmony_ci for (point = 0; point < pd->pd_points; point++) { 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* Absolute values */ 77562306a36Sopenharmony_ci pd->pd_pwr[point] = 2 * pcinfo->pwr[point]; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* Already sorted */ 77862306a36Sopenharmony_ci pd->pd_step[point] = pcinfo->pcdac[point]; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* Set min/max pwr */ 78262306a36Sopenharmony_ci chinfo[pier].min_pwr = pd->pd_pwr[0]; 78362306a36Sopenharmony_ci chinfo[pier].max_pwr = pd->pd_pwr[10]; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cierr_out: 79062306a36Sopenharmony_ci ath5k_eeprom_free_pcal_info(ah, mode); 79162306a36Sopenharmony_ci return -ENOMEM; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci/* Parse EEPROM data */ 79562306a36Sopenharmony_cistatic int 79662306a36Sopenharmony_ciath5k_eeprom_read_pcal_info_5111(struct ath5k_hw *ah, int mode) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 79962306a36Sopenharmony_ci struct ath5k_chan_pcal_info *pcal; 80062306a36Sopenharmony_ci int offset, ret; 80162306a36Sopenharmony_ci int i; 80262306a36Sopenharmony_ci u16 val; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); 80562306a36Sopenharmony_ci switch (mode) { 80662306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 80762306a36Sopenharmony_ci if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) 80862306a36Sopenharmony_ci return 0; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci ret = ath5k_eeprom_init_11a_pcal_freq(ah, 81162306a36Sopenharmony_ci offset + AR5K_EEPROM_GROUP1_OFFSET); 81262306a36Sopenharmony_ci if (ret < 0) 81362306a36Sopenharmony_ci return ret; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci offset += AR5K_EEPROM_GROUP2_OFFSET; 81662306a36Sopenharmony_ci pcal = ee->ee_pwr_cal_a; 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 81962306a36Sopenharmony_ci if (!AR5K_EEPROM_HDR_11B(ee->ee_header) && 82062306a36Sopenharmony_ci !AR5K_EEPROM_HDR_11G(ee->ee_header)) 82162306a36Sopenharmony_ci return 0; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci pcal = ee->ee_pwr_cal_b; 82462306a36Sopenharmony_ci offset += AR5K_EEPROM_GROUP3_OFFSET; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci /* fixed piers */ 82762306a36Sopenharmony_ci pcal[0].freq = 2412; 82862306a36Sopenharmony_ci pcal[1].freq = 2447; 82962306a36Sopenharmony_ci pcal[2].freq = 2484; 83062306a36Sopenharmony_ci ee->ee_n_piers[mode] = 3; 83162306a36Sopenharmony_ci break; 83262306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 83362306a36Sopenharmony_ci if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci pcal = ee->ee_pwr_cal_g; 83762306a36Sopenharmony_ci offset += AR5K_EEPROM_GROUP4_OFFSET; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci /* fixed piers */ 84062306a36Sopenharmony_ci pcal[0].freq = 2312; 84162306a36Sopenharmony_ci pcal[1].freq = 2412; 84262306a36Sopenharmony_ci pcal[2].freq = 2484; 84362306a36Sopenharmony_ci ee->ee_n_piers[mode] = 3; 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci default: 84662306a36Sopenharmony_ci return -EINVAL; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci for (i = 0; i < ee->ee_n_piers[mode]; i++) { 85062306a36Sopenharmony_ci struct ath5k_chan_pcal_info_rf5111 *cdata = 85162306a36Sopenharmony_ci &pcal[i].rf5111_info; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 85462306a36Sopenharmony_ci cdata->pcdac_max = ((val >> 10) & AR5K_EEPROM_PCDAC_M); 85562306a36Sopenharmony_ci cdata->pcdac_min = ((val >> 4) & AR5K_EEPROM_PCDAC_M); 85662306a36Sopenharmony_ci cdata->pwr[0] = ((val << 2) & AR5K_EEPROM_POWER_M); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 85962306a36Sopenharmony_ci cdata->pwr[0] |= ((val >> 14) & 0x3); 86062306a36Sopenharmony_ci cdata->pwr[1] = ((val >> 8) & AR5K_EEPROM_POWER_M); 86162306a36Sopenharmony_ci cdata->pwr[2] = ((val >> 2) & AR5K_EEPROM_POWER_M); 86262306a36Sopenharmony_ci cdata->pwr[3] = ((val << 4) & AR5K_EEPROM_POWER_M); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 86562306a36Sopenharmony_ci cdata->pwr[3] |= ((val >> 12) & 0xf); 86662306a36Sopenharmony_ci cdata->pwr[4] = ((val >> 6) & AR5K_EEPROM_POWER_M); 86762306a36Sopenharmony_ci cdata->pwr[5] = (val & AR5K_EEPROM_POWER_M); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 87062306a36Sopenharmony_ci cdata->pwr[6] = ((val >> 10) & AR5K_EEPROM_POWER_M); 87162306a36Sopenharmony_ci cdata->pwr[7] = ((val >> 4) & AR5K_EEPROM_POWER_M); 87262306a36Sopenharmony_ci cdata->pwr[8] = ((val << 2) & AR5K_EEPROM_POWER_M); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 87562306a36Sopenharmony_ci cdata->pwr[8] |= ((val >> 14) & 0x3); 87662306a36Sopenharmony_ci cdata->pwr[9] = ((val >> 8) & AR5K_EEPROM_POWER_M); 87762306a36Sopenharmony_ci cdata->pwr[10] = ((val >> 2) & AR5K_EEPROM_POWER_M); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci ath5k_get_pcdac_intercepts(ah, cdata->pcdac_min, 88062306a36Sopenharmony_ci cdata->pcdac_max, cdata->pcdac); 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return ath5k_eeprom_convert_pcal_info_5111(ah, mode, pcal); 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci/* 88862306a36Sopenharmony_ci * Read power calibration for RF5112 chips 88962306a36Sopenharmony_ci * 89062306a36Sopenharmony_ci * For RF5112 we have 4 XPD -eXternal Power Detector- curves 89162306a36Sopenharmony_ci * for each calibrated channel on 0, -6, -12 and -18dBm but we only 89262306a36Sopenharmony_ci * use the higher (3) and the lower (0) curves. Each curve has 0.5dB 89362306a36Sopenharmony_ci * power steps on x axis and PCDAC steps on y axis and looks like a 89462306a36Sopenharmony_ci * linear function. To recreate the curve and pass the power values 89562306a36Sopenharmony_ci * on hw, we read 4 points for xpd 0 (lower gain -> max power) 89662306a36Sopenharmony_ci * and 3 points for xpd 3 (higher gain -> lower power) here and 89762306a36Sopenharmony_ci * interpolate later. 89862306a36Sopenharmony_ci * 89962306a36Sopenharmony_ci * Note: Many vendors just use xpd 0 so xpd 3 is zeroed. 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci/* Convert RF5112 specific data to generic raw data 90362306a36Sopenharmony_ci * used by interpolation code */ 90462306a36Sopenharmony_cistatic int 90562306a36Sopenharmony_ciath5k_eeprom_convert_pcal_info_5112(struct ath5k_hw *ah, int mode, 90662306a36Sopenharmony_ci struct ath5k_chan_pcal_info *chinfo) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 90962306a36Sopenharmony_ci struct ath5k_chan_pcal_info_rf5112 *pcinfo; 91062306a36Sopenharmony_ci u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; 91162306a36Sopenharmony_ci unsigned int pier, pdg, point; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci /* Fill raw data for each calibration pier */ 91462306a36Sopenharmony_ci for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci pcinfo = &chinfo[pier].rf5112_info; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci /* Allocate pd_curves for this cal pier */ 91962306a36Sopenharmony_ci chinfo[pier].pd_curves = 92062306a36Sopenharmony_ci kcalloc(AR5K_EEPROM_N_PD_CURVES, 92162306a36Sopenharmony_ci sizeof(struct ath5k_pdgain_info), 92262306a36Sopenharmony_ci GFP_KERNEL); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (!chinfo[pier].pd_curves) 92562306a36Sopenharmony_ci goto err_out; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* Fill pd_curves */ 92862306a36Sopenharmony_ci for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) { 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci u8 idx = pdgain_idx[pdg]; 93162306a36Sopenharmony_ci struct ath5k_pdgain_info *pd = 93262306a36Sopenharmony_ci &chinfo[pier].pd_curves[idx]; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* Lowest gain curve (max power) */ 93562306a36Sopenharmony_ci if (pdg == 0) { 93662306a36Sopenharmony_ci /* One more point for better accuracy */ 93762306a36Sopenharmony_ci pd->pd_points = AR5K_EEPROM_N_XPD0_POINTS; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* Allocate pd points for this curve */ 94062306a36Sopenharmony_ci pd->pd_step = kcalloc(pd->pd_points, 94162306a36Sopenharmony_ci sizeof(u8), GFP_KERNEL); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (!pd->pd_step) 94462306a36Sopenharmony_ci goto err_out; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci pd->pd_pwr = kcalloc(pd->pd_points, 94762306a36Sopenharmony_ci sizeof(s16), GFP_KERNEL); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (!pd->pd_pwr) 95062306a36Sopenharmony_ci goto err_out; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* Fill raw dataset 95362306a36Sopenharmony_ci * (all power levels are in 0.25dB units) */ 95462306a36Sopenharmony_ci pd->pd_step[0] = pcinfo->pcdac_x0[0]; 95562306a36Sopenharmony_ci pd->pd_pwr[0] = pcinfo->pwr_x0[0]; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci for (point = 1; point < pd->pd_points; 95862306a36Sopenharmony_ci point++) { 95962306a36Sopenharmony_ci /* Absolute values */ 96062306a36Sopenharmony_ci pd->pd_pwr[point] = 96162306a36Sopenharmony_ci pcinfo->pwr_x0[point]; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* Deltas */ 96462306a36Sopenharmony_ci pd->pd_step[point] = 96562306a36Sopenharmony_ci pd->pd_step[point - 1] + 96662306a36Sopenharmony_ci pcinfo->pcdac_x0[point]; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* Set min power for this frequency */ 97062306a36Sopenharmony_ci chinfo[pier].min_pwr = pd->pd_pwr[0]; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci /* Highest gain curve (min power) */ 97362306a36Sopenharmony_ci } else if (pdg == 1) { 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci pd->pd_points = AR5K_EEPROM_N_XPD3_POINTS; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* Allocate pd points for this curve */ 97862306a36Sopenharmony_ci pd->pd_step = kcalloc(pd->pd_points, 97962306a36Sopenharmony_ci sizeof(u8), GFP_KERNEL); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci if (!pd->pd_step) 98262306a36Sopenharmony_ci goto err_out; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci pd->pd_pwr = kcalloc(pd->pd_points, 98562306a36Sopenharmony_ci sizeof(s16), GFP_KERNEL); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (!pd->pd_pwr) 98862306a36Sopenharmony_ci goto err_out; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci /* Fill raw dataset 99162306a36Sopenharmony_ci * (all power levels are in 0.25dB units) */ 99262306a36Sopenharmony_ci for (point = 0; point < pd->pd_points; 99362306a36Sopenharmony_ci point++) { 99462306a36Sopenharmony_ci /* Absolute values */ 99562306a36Sopenharmony_ci pd->pd_pwr[point] = 99662306a36Sopenharmony_ci pcinfo->pwr_x3[point]; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* Fixed points */ 99962306a36Sopenharmony_ci pd->pd_step[point] = 100062306a36Sopenharmony_ci pcinfo->pcdac_x3[point]; 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* Since we have a higher gain curve 100462306a36Sopenharmony_ci * override min power */ 100562306a36Sopenharmony_ci chinfo[pier].min_pwr = pd->pd_pwr[0]; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci return 0; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_cierr_out: 101362306a36Sopenharmony_ci ath5k_eeprom_free_pcal_info(ah, mode); 101462306a36Sopenharmony_ci return -ENOMEM; 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci/* Parse EEPROM data */ 101862306a36Sopenharmony_cistatic int 101962306a36Sopenharmony_ciath5k_eeprom_read_pcal_info_5112(struct ath5k_hw *ah, int mode) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 102262306a36Sopenharmony_ci struct ath5k_chan_pcal_info_rf5112 *chan_pcal_info; 102362306a36Sopenharmony_ci struct ath5k_chan_pcal_info *gen_chan_info; 102462306a36Sopenharmony_ci u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; 102562306a36Sopenharmony_ci u32 offset; 102662306a36Sopenharmony_ci u8 i, c; 102762306a36Sopenharmony_ci u16 val; 102862306a36Sopenharmony_ci u8 pd_gains = 0; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci /* Count how many curves we have and 103162306a36Sopenharmony_ci * identify them (which one of the 4 103262306a36Sopenharmony_ci * available curves we have on each count). 103362306a36Sopenharmony_ci * Curves are stored from lower (x0) to 103462306a36Sopenharmony_ci * higher (x3) gain */ 103562306a36Sopenharmony_ci for (i = 0; i < AR5K_EEPROM_N_PD_CURVES; i++) { 103662306a36Sopenharmony_ci /* ee_x_gain[mode] is x gain mask */ 103762306a36Sopenharmony_ci if ((ee->ee_x_gain[mode] >> i) & 0x1) 103862306a36Sopenharmony_ci pdgain_idx[pd_gains++] = i; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci ee->ee_pd_gains[mode] = pd_gains; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci if (pd_gains == 0 || pd_gains > 2) 104362306a36Sopenharmony_ci return -EINVAL; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci switch (mode) { 104662306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 104762306a36Sopenharmony_ci /* 104862306a36Sopenharmony_ci * Read 5GHz EEPROM channels 104962306a36Sopenharmony_ci */ 105062306a36Sopenharmony_ci offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); 105162306a36Sopenharmony_ci ath5k_eeprom_init_11a_pcal_freq(ah, offset); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci offset += AR5K_EEPROM_GROUP2_OFFSET; 105462306a36Sopenharmony_ci gen_chan_info = ee->ee_pwr_cal_a; 105562306a36Sopenharmony_ci break; 105662306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 105762306a36Sopenharmony_ci offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); 105862306a36Sopenharmony_ci if (AR5K_EEPROM_HDR_11A(ee->ee_header)) 105962306a36Sopenharmony_ci offset += AR5K_EEPROM_GROUP3_OFFSET; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* NB: frequency piers parsed during mode init */ 106262306a36Sopenharmony_ci gen_chan_info = ee->ee_pwr_cal_b; 106362306a36Sopenharmony_ci break; 106462306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 106562306a36Sopenharmony_ci offset = AR5K_EEPROM_GROUPS_START(ee->ee_version); 106662306a36Sopenharmony_ci if (AR5K_EEPROM_HDR_11A(ee->ee_header)) 106762306a36Sopenharmony_ci offset += AR5K_EEPROM_GROUP4_OFFSET; 106862306a36Sopenharmony_ci else if (AR5K_EEPROM_HDR_11B(ee->ee_header)) 106962306a36Sopenharmony_ci offset += AR5K_EEPROM_GROUP2_OFFSET; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci /* NB: frequency piers parsed during mode init */ 107262306a36Sopenharmony_ci gen_chan_info = ee->ee_pwr_cal_g; 107362306a36Sopenharmony_ci break; 107462306a36Sopenharmony_ci default: 107562306a36Sopenharmony_ci return -EINVAL; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci for (i = 0; i < ee->ee_n_piers[mode]; i++) { 107962306a36Sopenharmony_ci chan_pcal_info = &gen_chan_info[i].rf5112_info; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* Power values in quarter dB 108262306a36Sopenharmony_ci * for the lower xpd gain curve 108362306a36Sopenharmony_ci * (0 dBm -> higher output power) */ 108462306a36Sopenharmony_ci for (c = 0; c < AR5K_EEPROM_N_XPD0_POINTS; c++) { 108562306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 108662306a36Sopenharmony_ci chan_pcal_info->pwr_x0[c] = (s8) (val & 0xff); 108762306a36Sopenharmony_ci chan_pcal_info->pwr_x0[++c] = (s8) ((val >> 8) & 0xff); 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* PCDAC steps 109162306a36Sopenharmony_ci * corresponding to the above power 109262306a36Sopenharmony_ci * measurements */ 109362306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 109462306a36Sopenharmony_ci chan_pcal_info->pcdac_x0[1] = (val & 0x1f); 109562306a36Sopenharmony_ci chan_pcal_info->pcdac_x0[2] = ((val >> 5) & 0x1f); 109662306a36Sopenharmony_ci chan_pcal_info->pcdac_x0[3] = ((val >> 10) & 0x1f); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci /* Power values in quarter dB 109962306a36Sopenharmony_ci * for the higher xpd gain curve 110062306a36Sopenharmony_ci * (18 dBm -> lower output power) */ 110162306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 110262306a36Sopenharmony_ci chan_pcal_info->pwr_x3[0] = (s8) (val & 0xff); 110362306a36Sopenharmony_ci chan_pcal_info->pwr_x3[1] = (s8) ((val >> 8) & 0xff); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 110662306a36Sopenharmony_ci chan_pcal_info->pwr_x3[2] = (val & 0xff); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci /* PCDAC steps 110962306a36Sopenharmony_ci * corresponding to the above power 111062306a36Sopenharmony_ci * measurements (fixed) */ 111162306a36Sopenharmony_ci chan_pcal_info->pcdac_x3[0] = 20; 111262306a36Sopenharmony_ci chan_pcal_info->pcdac_x3[1] = 35; 111362306a36Sopenharmony_ci chan_pcal_info->pcdac_x3[2] = 63; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (ee->ee_version >= AR5K_EEPROM_VERSION_4_3) { 111662306a36Sopenharmony_ci chan_pcal_info->pcdac_x0[0] = ((val >> 8) & 0x3f); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci /* Last xpd0 power level is also channel maximum */ 111962306a36Sopenharmony_ci gen_chan_info[i].max_pwr = chan_pcal_info->pwr_x0[3]; 112062306a36Sopenharmony_ci } else { 112162306a36Sopenharmony_ci chan_pcal_info->pcdac_x0[0] = 1; 112262306a36Sopenharmony_ci gen_chan_info[i].max_pwr = (s8) ((val >> 8) & 0xff); 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci return ath5k_eeprom_convert_pcal_info_5112(ah, mode, gen_chan_info); 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci/* 113262306a36Sopenharmony_ci * Read power calibration for RF2413 chips 113362306a36Sopenharmony_ci * 113462306a36Sopenharmony_ci * For RF2413 we have a Power to PDDAC table (Power Detector) 113562306a36Sopenharmony_ci * instead of a PCDAC and 4 pd gain curves for each calibrated channel. 113662306a36Sopenharmony_ci * Each curve has power on x axis in 0.5 db steps and PDDADC steps on y 113762306a36Sopenharmony_ci * axis and looks like an exponential function like the RF5111 curve. 113862306a36Sopenharmony_ci * 113962306a36Sopenharmony_ci * To recreate the curves we read here the points and interpolate 114062306a36Sopenharmony_ci * later. Note that in most cases only 2 (higher and lower) curves are 114162306a36Sopenharmony_ci * used (like RF5112) but vendors have the opportunity to include all 114262306a36Sopenharmony_ci * 4 curves on eeprom. The final curve (higher power) has an extra 114362306a36Sopenharmony_ci * point for better accuracy like RF5112. 114462306a36Sopenharmony_ci */ 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci/* For RF2413 power calibration data doesn't start on a fixed location and 114762306a36Sopenharmony_ci * if a mode is not supported, its section is missing -not zeroed-. 114862306a36Sopenharmony_ci * So we need to calculate the starting offset for each section by using 114962306a36Sopenharmony_ci * these two functions */ 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci/* Return the size of each section based on the mode and the number of pd 115262306a36Sopenharmony_ci * gains available (maximum 4). */ 115362306a36Sopenharmony_cistatic inline unsigned int 115462306a36Sopenharmony_ciath5k_pdgains_size_2413(struct ath5k_eeprom_info *ee, unsigned int mode) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci static const unsigned int pdgains_size[] = { 4, 6, 9, 12 }; 115762306a36Sopenharmony_ci unsigned int sz; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci sz = pdgains_size[ee->ee_pd_gains[mode] - 1]; 116062306a36Sopenharmony_ci sz *= ee->ee_n_piers[mode]; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci return sz; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci/* Return the starting offset for a section based on the modes supported 116662306a36Sopenharmony_ci * and each section's size. */ 116762306a36Sopenharmony_cistatic unsigned int 116862306a36Sopenharmony_ciath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci u32 offset = AR5K_EEPROM_CAL_DATA_START(ee->ee_misc4); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci switch (mode) { 117362306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 117462306a36Sopenharmony_ci if (AR5K_EEPROM_HDR_11B(ee->ee_header)) 117562306a36Sopenharmony_ci offset += ath5k_pdgains_size_2413(ee, 117662306a36Sopenharmony_ci AR5K_EEPROM_MODE_11B) + 117762306a36Sopenharmony_ci AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; 117862306a36Sopenharmony_ci fallthrough; 117962306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 118062306a36Sopenharmony_ci if (AR5K_EEPROM_HDR_11A(ee->ee_header)) 118162306a36Sopenharmony_ci offset += ath5k_pdgains_size_2413(ee, 118262306a36Sopenharmony_ci AR5K_EEPROM_MODE_11A) + 118362306a36Sopenharmony_ci AR5K_EEPROM_N_5GHZ_CHAN / 2; 118462306a36Sopenharmony_ci fallthrough; 118562306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 118662306a36Sopenharmony_ci break; 118762306a36Sopenharmony_ci default: 118862306a36Sopenharmony_ci break; 118962306a36Sopenharmony_ci } 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci return offset; 119262306a36Sopenharmony_ci} 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci/* Convert RF2413 specific data to generic raw data 119562306a36Sopenharmony_ci * used by interpolation code */ 119662306a36Sopenharmony_cistatic int 119762306a36Sopenharmony_ciath5k_eeprom_convert_pcal_info_2413(struct ath5k_hw *ah, int mode, 119862306a36Sopenharmony_ci struct ath5k_chan_pcal_info *chinfo) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 120162306a36Sopenharmony_ci struct ath5k_chan_pcal_info_rf2413 *pcinfo; 120262306a36Sopenharmony_ci u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; 120362306a36Sopenharmony_ci unsigned int pier, pdg, point; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* Fill raw data for each calibration pier */ 120662306a36Sopenharmony_ci for (pier = 0; pier < ee->ee_n_piers[mode]; pier++) { 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci pcinfo = &chinfo[pier].rf2413_info; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci /* Allocate pd_curves for this cal pier */ 121162306a36Sopenharmony_ci chinfo[pier].pd_curves = 121262306a36Sopenharmony_ci kcalloc(AR5K_EEPROM_N_PD_CURVES, 121362306a36Sopenharmony_ci sizeof(struct ath5k_pdgain_info), 121462306a36Sopenharmony_ci GFP_KERNEL); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci if (!chinfo[pier].pd_curves) 121762306a36Sopenharmony_ci goto err_out; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* Fill pd_curves */ 122062306a36Sopenharmony_ci for (pdg = 0; pdg < ee->ee_pd_gains[mode]; pdg++) { 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci u8 idx = pdgain_idx[pdg]; 122362306a36Sopenharmony_ci struct ath5k_pdgain_info *pd = 122462306a36Sopenharmony_ci &chinfo[pier].pd_curves[idx]; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci /* One more point for the highest power 122762306a36Sopenharmony_ci * curve (lowest gain) */ 122862306a36Sopenharmony_ci if (pdg == ee->ee_pd_gains[mode] - 1) 122962306a36Sopenharmony_ci pd->pd_points = AR5K_EEPROM_N_PD_POINTS; 123062306a36Sopenharmony_ci else 123162306a36Sopenharmony_ci pd->pd_points = AR5K_EEPROM_N_PD_POINTS - 1; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* Allocate pd points for this curve */ 123462306a36Sopenharmony_ci pd->pd_step = kcalloc(pd->pd_points, 123562306a36Sopenharmony_ci sizeof(u8), GFP_KERNEL); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci if (!pd->pd_step) 123862306a36Sopenharmony_ci goto err_out; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci pd->pd_pwr = kcalloc(pd->pd_points, 124162306a36Sopenharmony_ci sizeof(s16), GFP_KERNEL); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (!pd->pd_pwr) 124462306a36Sopenharmony_ci goto err_out; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci /* Fill raw dataset 124762306a36Sopenharmony_ci * convert all pwr levels to 124862306a36Sopenharmony_ci * quarter dB for RF5112 compatibility */ 124962306a36Sopenharmony_ci pd->pd_step[0] = pcinfo->pddac_i[pdg]; 125062306a36Sopenharmony_ci pd->pd_pwr[0] = 4 * pcinfo->pwr_i[pdg]; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci for (point = 1; point < pd->pd_points; point++) { 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci pd->pd_pwr[point] = pd->pd_pwr[point - 1] + 125562306a36Sopenharmony_ci 2 * pcinfo->pwr[pdg][point - 1]; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci pd->pd_step[point] = pd->pd_step[point - 1] + 125862306a36Sopenharmony_ci pcinfo->pddac[pdg][point - 1]; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci /* Highest gain curve -> min power */ 126362306a36Sopenharmony_ci if (pdg == 0) 126462306a36Sopenharmony_ci chinfo[pier].min_pwr = pd->pd_pwr[0]; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci /* Lowest gain curve -> max power */ 126762306a36Sopenharmony_ci if (pdg == ee->ee_pd_gains[mode] - 1) 126862306a36Sopenharmony_ci chinfo[pier].max_pwr = 126962306a36Sopenharmony_ci pd->pd_pwr[pd->pd_points - 1]; 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci return 0; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cierr_out: 127662306a36Sopenharmony_ci ath5k_eeprom_free_pcal_info(ah, mode); 127762306a36Sopenharmony_ci return -ENOMEM; 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci/* Parse EEPROM data */ 128162306a36Sopenharmony_cistatic int 128262306a36Sopenharmony_ciath5k_eeprom_read_pcal_info_2413(struct ath5k_hw *ah, int mode) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 128562306a36Sopenharmony_ci struct ath5k_chan_pcal_info_rf2413 *pcinfo; 128662306a36Sopenharmony_ci struct ath5k_chan_pcal_info *chinfo; 128762306a36Sopenharmony_ci u8 *pdgain_idx = ee->ee_pdc_to_idx[mode]; 128862306a36Sopenharmony_ci u32 offset; 128962306a36Sopenharmony_ci int idx, i; 129062306a36Sopenharmony_ci u16 val; 129162306a36Sopenharmony_ci u8 pd_gains = 0; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci /* Count how many curves we have and 129462306a36Sopenharmony_ci * identify them (which one of the 4 129562306a36Sopenharmony_ci * available curves we have on each count). 129662306a36Sopenharmony_ci * Curves are stored from higher to 129762306a36Sopenharmony_ci * lower gain so we go backwards */ 129862306a36Sopenharmony_ci for (idx = AR5K_EEPROM_N_PD_CURVES - 1; idx >= 0; idx--) { 129962306a36Sopenharmony_ci /* ee_x_gain[mode] is x gain mask */ 130062306a36Sopenharmony_ci if ((ee->ee_x_gain[mode] >> idx) & 0x1) 130162306a36Sopenharmony_ci pdgain_idx[pd_gains++] = idx; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci ee->ee_pd_gains[mode] = pd_gains; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (pd_gains == 0) 130762306a36Sopenharmony_ci return -EINVAL; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci offset = ath5k_cal_data_offset_2413(ee, mode); 131062306a36Sopenharmony_ci switch (mode) { 131162306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 131262306a36Sopenharmony_ci if (!AR5K_EEPROM_HDR_11A(ee->ee_header)) 131362306a36Sopenharmony_ci return 0; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci ath5k_eeprom_init_11a_pcal_freq(ah, offset); 131662306a36Sopenharmony_ci offset += AR5K_EEPROM_N_5GHZ_CHAN / 2; 131762306a36Sopenharmony_ci chinfo = ee->ee_pwr_cal_a; 131862306a36Sopenharmony_ci break; 131962306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 132062306a36Sopenharmony_ci if (!AR5K_EEPROM_HDR_11B(ee->ee_header)) 132162306a36Sopenharmony_ci return 0; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci ath5k_eeprom_init_11bg_2413(ah, mode, offset); 132462306a36Sopenharmony_ci offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; 132562306a36Sopenharmony_ci chinfo = ee->ee_pwr_cal_b; 132662306a36Sopenharmony_ci break; 132762306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 132862306a36Sopenharmony_ci if (!AR5K_EEPROM_HDR_11G(ee->ee_header)) 132962306a36Sopenharmony_ci return 0; 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci ath5k_eeprom_init_11bg_2413(ah, mode, offset); 133262306a36Sopenharmony_ci offset += AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; 133362306a36Sopenharmony_ci chinfo = ee->ee_pwr_cal_g; 133462306a36Sopenharmony_ci break; 133562306a36Sopenharmony_ci default: 133662306a36Sopenharmony_ci return -EINVAL; 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci for (i = 0; i < ee->ee_n_piers[mode]; i++) { 134062306a36Sopenharmony_ci pcinfo = &chinfo[i].rf2413_info; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci /* 134362306a36Sopenharmony_ci * Read pwr_i, pddac_i and the first 134462306a36Sopenharmony_ci * 2 pd points (pwr, pddac) 134562306a36Sopenharmony_ci */ 134662306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 134762306a36Sopenharmony_ci pcinfo->pwr_i[0] = val & 0x1f; 134862306a36Sopenharmony_ci pcinfo->pddac_i[0] = (val >> 5) & 0x7f; 134962306a36Sopenharmony_ci pcinfo->pwr[0][0] = (val >> 12) & 0xf; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 135262306a36Sopenharmony_ci pcinfo->pddac[0][0] = val & 0x3f; 135362306a36Sopenharmony_ci pcinfo->pwr[0][1] = (val >> 6) & 0xf; 135462306a36Sopenharmony_ci pcinfo->pddac[0][1] = (val >> 10) & 0x3f; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 135762306a36Sopenharmony_ci pcinfo->pwr[0][2] = val & 0xf; 135862306a36Sopenharmony_ci pcinfo->pddac[0][2] = (val >> 4) & 0x3f; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci pcinfo->pwr[0][3] = 0; 136162306a36Sopenharmony_ci pcinfo->pddac[0][3] = 0; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci if (pd_gains > 1) { 136462306a36Sopenharmony_ci /* 136562306a36Sopenharmony_ci * Pd gain 0 is not the last pd gain 136662306a36Sopenharmony_ci * so it only has 2 pd points. 136762306a36Sopenharmony_ci * Continue with pd gain 1. 136862306a36Sopenharmony_ci */ 136962306a36Sopenharmony_ci pcinfo->pwr_i[1] = (val >> 10) & 0x1f; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci pcinfo->pddac_i[1] = (val >> 15) & 0x1; 137262306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 137362306a36Sopenharmony_ci pcinfo->pddac_i[1] |= (val & 0x3F) << 1; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci pcinfo->pwr[1][0] = (val >> 6) & 0xf; 137662306a36Sopenharmony_ci pcinfo->pddac[1][0] = (val >> 10) & 0x3f; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 137962306a36Sopenharmony_ci pcinfo->pwr[1][1] = val & 0xf; 138062306a36Sopenharmony_ci pcinfo->pddac[1][1] = (val >> 4) & 0x3f; 138162306a36Sopenharmony_ci pcinfo->pwr[1][2] = (val >> 10) & 0xf; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci pcinfo->pddac[1][2] = (val >> 14) & 0x3; 138462306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 138562306a36Sopenharmony_ci pcinfo->pddac[1][2] |= (val & 0xF) << 2; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci pcinfo->pwr[1][3] = 0; 138862306a36Sopenharmony_ci pcinfo->pddac[1][3] = 0; 138962306a36Sopenharmony_ci } else if (pd_gains == 1) { 139062306a36Sopenharmony_ci /* 139162306a36Sopenharmony_ci * Pd gain 0 is the last one so 139262306a36Sopenharmony_ci * read the extra point. 139362306a36Sopenharmony_ci */ 139462306a36Sopenharmony_ci pcinfo->pwr[0][3] = (val >> 10) & 0xf; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci pcinfo->pddac[0][3] = (val >> 14) & 0x3; 139762306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 139862306a36Sopenharmony_ci pcinfo->pddac[0][3] |= (val & 0xF) << 2; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci /* 140262306a36Sopenharmony_ci * Proceed with the other pd_gains 140362306a36Sopenharmony_ci * as above. 140462306a36Sopenharmony_ci */ 140562306a36Sopenharmony_ci if (pd_gains > 2) { 140662306a36Sopenharmony_ci pcinfo->pwr_i[2] = (val >> 4) & 0x1f; 140762306a36Sopenharmony_ci pcinfo->pddac_i[2] = (val >> 9) & 0x7f; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 141062306a36Sopenharmony_ci pcinfo->pwr[2][0] = (val >> 0) & 0xf; 141162306a36Sopenharmony_ci pcinfo->pddac[2][0] = (val >> 4) & 0x3f; 141262306a36Sopenharmony_ci pcinfo->pwr[2][1] = (val >> 10) & 0xf; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci pcinfo->pddac[2][1] = (val >> 14) & 0x3; 141562306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 141662306a36Sopenharmony_ci pcinfo->pddac[2][1] |= (val & 0xF) << 2; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci pcinfo->pwr[2][2] = (val >> 4) & 0xf; 141962306a36Sopenharmony_ci pcinfo->pddac[2][2] = (val >> 8) & 0x3f; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci pcinfo->pwr[2][3] = 0; 142262306a36Sopenharmony_ci pcinfo->pddac[2][3] = 0; 142362306a36Sopenharmony_ci } else if (pd_gains == 2) { 142462306a36Sopenharmony_ci pcinfo->pwr[1][3] = (val >> 4) & 0xf; 142562306a36Sopenharmony_ci pcinfo->pddac[1][3] = (val >> 8) & 0x3f; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci if (pd_gains > 3) { 142962306a36Sopenharmony_ci pcinfo->pwr_i[3] = (val >> 14) & 0x3; 143062306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 143162306a36Sopenharmony_ci pcinfo->pwr_i[3] |= ((val >> 0) & 0x7) << 2; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci pcinfo->pddac_i[3] = (val >> 3) & 0x7f; 143462306a36Sopenharmony_ci pcinfo->pwr[3][0] = (val >> 10) & 0xf; 143562306a36Sopenharmony_ci pcinfo->pddac[3][0] = (val >> 14) & 0x3; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 143862306a36Sopenharmony_ci pcinfo->pddac[3][0] |= (val & 0xF) << 2; 143962306a36Sopenharmony_ci pcinfo->pwr[3][1] = (val >> 4) & 0xf; 144062306a36Sopenharmony_ci pcinfo->pddac[3][1] = (val >> 8) & 0x3f; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci pcinfo->pwr[3][2] = (val >> 14) & 0x3; 144362306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 144462306a36Sopenharmony_ci pcinfo->pwr[3][2] |= ((val >> 0) & 0x3) << 2; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci pcinfo->pddac[3][2] = (val >> 2) & 0x3f; 144762306a36Sopenharmony_ci pcinfo->pwr[3][3] = (val >> 8) & 0xf; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci pcinfo->pddac[3][3] = (val >> 12) & 0xF; 145062306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 145162306a36Sopenharmony_ci pcinfo->pddac[3][3] |= ((val >> 0) & 0x3) << 4; 145262306a36Sopenharmony_ci } else if (pd_gains == 3) { 145362306a36Sopenharmony_ci pcinfo->pwr[2][3] = (val >> 14) & 0x3; 145462306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 145562306a36Sopenharmony_ci pcinfo->pwr[2][3] |= ((val >> 0) & 0x3) << 2; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci pcinfo->pddac[2][3] = (val >> 2) & 0x3f; 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci return ath5k_eeprom_convert_pcal_info_2413(ah, mode, chinfo); 146262306a36Sopenharmony_ci} 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci/* 146662306a36Sopenharmony_ci * Read per rate target power (this is the maximum tx power 146762306a36Sopenharmony_ci * supported by the card). This info is used when setting 146862306a36Sopenharmony_ci * tx power, no matter the channel. 146962306a36Sopenharmony_ci * 147062306a36Sopenharmony_ci * This also works for v5 EEPROMs. 147162306a36Sopenharmony_ci */ 147262306a36Sopenharmony_cistatic int 147362306a36Sopenharmony_ciath5k_eeprom_read_target_rate_pwr_info(struct ath5k_hw *ah, unsigned int mode) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 147662306a36Sopenharmony_ci struct ath5k_rate_pcal_info *rate_pcal_info; 147762306a36Sopenharmony_ci u8 *rate_target_pwr_num; 147862306a36Sopenharmony_ci u32 offset; 147962306a36Sopenharmony_ci u16 val; 148062306a36Sopenharmony_ci int i; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci offset = AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1); 148362306a36Sopenharmony_ci rate_target_pwr_num = &ee->ee_rate_target_pwr_num[mode]; 148462306a36Sopenharmony_ci switch (mode) { 148562306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11A: 148662306a36Sopenharmony_ci offset += AR5K_EEPROM_TARGET_PWR_OFF_11A(ee->ee_version); 148762306a36Sopenharmony_ci rate_pcal_info = ee->ee_rate_tpwr_a; 148862306a36Sopenharmony_ci ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_5GHZ_RATE_CHAN; 148962306a36Sopenharmony_ci break; 149062306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11B: 149162306a36Sopenharmony_ci offset += AR5K_EEPROM_TARGET_PWR_OFF_11B(ee->ee_version); 149262306a36Sopenharmony_ci rate_pcal_info = ee->ee_rate_tpwr_b; 149362306a36Sopenharmony_ci ee->ee_rate_target_pwr_num[mode] = 2; /* 3rd is g mode's 1st */ 149462306a36Sopenharmony_ci break; 149562306a36Sopenharmony_ci case AR5K_EEPROM_MODE_11G: 149662306a36Sopenharmony_ci offset += AR5K_EEPROM_TARGET_PWR_OFF_11G(ee->ee_version); 149762306a36Sopenharmony_ci rate_pcal_info = ee->ee_rate_tpwr_g; 149862306a36Sopenharmony_ci ee->ee_rate_target_pwr_num[mode] = AR5K_EEPROM_N_2GHZ_CHAN; 149962306a36Sopenharmony_ci break; 150062306a36Sopenharmony_ci default: 150162306a36Sopenharmony_ci return -EINVAL; 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci /* Different freq mask for older eeproms (<= v3.2) */ 150562306a36Sopenharmony_ci if (ee->ee_version <= AR5K_EEPROM_VERSION_3_2) { 150662306a36Sopenharmony_ci for (i = 0; i < (*rate_target_pwr_num); i++) { 150762306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 150862306a36Sopenharmony_ci rate_pcal_info[i].freq = 150962306a36Sopenharmony_ci ath5k_eeprom_bin2freq(ee, (val >> 9) & 0x7f, mode); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci rate_pcal_info[i].target_power_6to24 = ((val >> 3) & 0x3f); 151262306a36Sopenharmony_ci rate_pcal_info[i].target_power_36 = (val << 3) & 0x3f; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS || 151762306a36Sopenharmony_ci val == 0) { 151862306a36Sopenharmony_ci (*rate_target_pwr_num) = i; 151962306a36Sopenharmony_ci break; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci rate_pcal_info[i].target_power_36 |= ((val >> 13) & 0x7); 152362306a36Sopenharmony_ci rate_pcal_info[i].target_power_48 = ((val >> 7) & 0x3f); 152462306a36Sopenharmony_ci rate_pcal_info[i].target_power_54 = ((val >> 1) & 0x3f); 152562306a36Sopenharmony_ci } 152662306a36Sopenharmony_ci } else { 152762306a36Sopenharmony_ci for (i = 0; i < (*rate_target_pwr_num); i++) { 152862306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 152962306a36Sopenharmony_ci rate_pcal_info[i].freq = 153062306a36Sopenharmony_ci ath5k_eeprom_bin2freq(ee, (val >> 8) & 0xff, mode); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci rate_pcal_info[i].target_power_6to24 = ((val >> 2) & 0x3f); 153362306a36Sopenharmony_ci rate_pcal_info[i].target_power_36 = (val << 4) & 0x3f; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (rate_pcal_info[i].freq == AR5K_EEPROM_CHANNEL_DIS || 153862306a36Sopenharmony_ci val == 0) { 153962306a36Sopenharmony_ci (*rate_target_pwr_num) = i; 154062306a36Sopenharmony_ci break; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci rate_pcal_info[i].target_power_36 |= (val >> 12) & 0xf; 154462306a36Sopenharmony_ci rate_pcal_info[i].target_power_48 = ((val >> 6) & 0x3f); 154562306a36Sopenharmony_ci rate_pcal_info[i].target_power_54 = (val & 0x3f); 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci } 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci return 0; 155062306a36Sopenharmony_ci} 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci/* 155462306a36Sopenharmony_ci * Read per channel calibration info from EEPROM 155562306a36Sopenharmony_ci * 155662306a36Sopenharmony_ci * This info is used to calibrate the baseband power table. Imagine 155762306a36Sopenharmony_ci * that for each channel there is a power curve that's hw specific 155862306a36Sopenharmony_ci * (depends on amplifier etc) and we try to "correct" this curve using 155962306a36Sopenharmony_ci * offsets we pass on to phy chip (baseband -> before amplifier) so that 156062306a36Sopenharmony_ci * it can use accurate power values when setting tx power (takes amplifier's 156162306a36Sopenharmony_ci * performance on each channel into account). 156262306a36Sopenharmony_ci * 156362306a36Sopenharmony_ci * EEPROM provides us with the offsets for some pre-calibrated channels 156462306a36Sopenharmony_ci * and we have to interpolate to create the full table for these channels and 156562306a36Sopenharmony_ci * also the table for any channel. 156662306a36Sopenharmony_ci */ 156762306a36Sopenharmony_cistatic int 156862306a36Sopenharmony_ciath5k_eeprom_read_pcal_info(struct ath5k_hw *ah) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 157162306a36Sopenharmony_ci int (*read_pcal)(struct ath5k_hw *hw, int mode); 157262306a36Sopenharmony_ci int mode; 157362306a36Sopenharmony_ci int err; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_4_0) && 157662306a36Sopenharmony_ci (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 1)) 157762306a36Sopenharmony_ci read_pcal = ath5k_eeprom_read_pcal_info_5112; 157862306a36Sopenharmony_ci else if ((ah->ah_ee_version >= AR5K_EEPROM_VERSION_5_0) && 157962306a36Sopenharmony_ci (AR5K_EEPROM_EEMAP(ee->ee_misc0) == 2)) 158062306a36Sopenharmony_ci read_pcal = ath5k_eeprom_read_pcal_info_2413; 158162306a36Sopenharmony_ci else 158262306a36Sopenharmony_ci read_pcal = ath5k_eeprom_read_pcal_info_5111; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; 158662306a36Sopenharmony_ci mode++) { 158762306a36Sopenharmony_ci err = read_pcal(ah, mode); 158862306a36Sopenharmony_ci if (err) 158962306a36Sopenharmony_ci return err; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci err = ath5k_eeprom_read_target_rate_pwr_info(ah, mode); 159262306a36Sopenharmony_ci if (err < 0) 159362306a36Sopenharmony_ci return err; 159462306a36Sopenharmony_ci } 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci return 0; 159762306a36Sopenharmony_ci} 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci/* Read conformance test limits used for regulatory control */ 160062306a36Sopenharmony_cistatic int 160162306a36Sopenharmony_ciath5k_eeprom_read_ctl_info(struct ath5k_hw *ah) 160262306a36Sopenharmony_ci{ 160362306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 160462306a36Sopenharmony_ci struct ath5k_edge_power *rep; 160562306a36Sopenharmony_ci unsigned int fmask, pmask; 160662306a36Sopenharmony_ci unsigned int ctl_mode; 160762306a36Sopenharmony_ci int i, j; 160862306a36Sopenharmony_ci u32 offset; 160962306a36Sopenharmony_ci u16 val; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci pmask = AR5K_EEPROM_POWER_M; 161262306a36Sopenharmony_ci fmask = AR5K_EEPROM_FREQ_M(ee->ee_version); 161362306a36Sopenharmony_ci offset = AR5K_EEPROM_CTL(ee->ee_version); 161462306a36Sopenharmony_ci ee->ee_ctls = AR5K_EEPROM_N_CTLS(ee->ee_version); 161562306a36Sopenharmony_ci for (i = 0; i < ee->ee_ctls; i += 2) { 161662306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 161762306a36Sopenharmony_ci ee->ee_ctl[i] = (val >> 8) & 0xff; 161862306a36Sopenharmony_ci ee->ee_ctl[i + 1] = val & 0xff; 161962306a36Sopenharmony_ci } 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci offset = AR5K_EEPROM_GROUP8_OFFSET; 162262306a36Sopenharmony_ci if (ee->ee_version >= AR5K_EEPROM_VERSION_4_0) 162362306a36Sopenharmony_ci offset += AR5K_EEPROM_TARGET_PWRSTART(ee->ee_misc1) - 162462306a36Sopenharmony_ci AR5K_EEPROM_GROUP5_OFFSET; 162562306a36Sopenharmony_ci else 162662306a36Sopenharmony_ci offset += AR5K_EEPROM_GROUPS_START(ee->ee_version); 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci rep = ee->ee_ctl_pwr; 162962306a36Sopenharmony_ci for (i = 0; i < ee->ee_ctls; i++) { 163062306a36Sopenharmony_ci switch (ee->ee_ctl[i] & AR5K_CTL_MODE_M) { 163162306a36Sopenharmony_ci case AR5K_CTL_11A: 163262306a36Sopenharmony_ci case AR5K_CTL_TURBO: 163362306a36Sopenharmony_ci ctl_mode = AR5K_EEPROM_MODE_11A; 163462306a36Sopenharmony_ci break; 163562306a36Sopenharmony_ci default: 163662306a36Sopenharmony_ci ctl_mode = AR5K_EEPROM_MODE_11G; 163762306a36Sopenharmony_ci break; 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci if (ee->ee_ctl[i] == 0) { 164062306a36Sopenharmony_ci if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) 164162306a36Sopenharmony_ci offset += 8; 164262306a36Sopenharmony_ci else 164362306a36Sopenharmony_ci offset += 7; 164462306a36Sopenharmony_ci rep += AR5K_EEPROM_N_EDGES; 164562306a36Sopenharmony_ci continue; 164662306a36Sopenharmony_ci } 164762306a36Sopenharmony_ci if (ee->ee_version >= AR5K_EEPROM_VERSION_3_3) { 164862306a36Sopenharmony_ci for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) { 164962306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 165062306a36Sopenharmony_ci rep[j].freq = (val >> 8) & fmask; 165162306a36Sopenharmony_ci rep[j + 1].freq = val & fmask; 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci for (j = 0; j < AR5K_EEPROM_N_EDGES; j += 2) { 165462306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 165562306a36Sopenharmony_ci rep[j].edge = (val >> 8) & pmask; 165662306a36Sopenharmony_ci rep[j].flag = (val >> 14) & 1; 165762306a36Sopenharmony_ci rep[j + 1].edge = val & pmask; 165862306a36Sopenharmony_ci rep[j + 1].flag = (val >> 6) & 1; 165962306a36Sopenharmony_ci } 166062306a36Sopenharmony_ci } else { 166162306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 166262306a36Sopenharmony_ci rep[0].freq = (val >> 9) & fmask; 166362306a36Sopenharmony_ci rep[1].freq = (val >> 2) & fmask; 166462306a36Sopenharmony_ci rep[2].freq = (val << 5) & fmask; 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 166762306a36Sopenharmony_ci rep[2].freq |= (val >> 11) & 0x1f; 166862306a36Sopenharmony_ci rep[3].freq = (val >> 4) & fmask; 166962306a36Sopenharmony_ci rep[4].freq = (val << 3) & fmask; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 167262306a36Sopenharmony_ci rep[4].freq |= (val >> 13) & 0x7; 167362306a36Sopenharmony_ci rep[5].freq = (val >> 6) & fmask; 167462306a36Sopenharmony_ci rep[6].freq = (val << 1) & fmask; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 167762306a36Sopenharmony_ci rep[6].freq |= (val >> 15) & 0x1; 167862306a36Sopenharmony_ci rep[7].freq = (val >> 8) & fmask; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci rep[0].edge = (val >> 2) & pmask; 168162306a36Sopenharmony_ci rep[1].edge = (val << 4) & pmask; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 168462306a36Sopenharmony_ci rep[1].edge |= (val >> 12) & 0xf; 168562306a36Sopenharmony_ci rep[2].edge = (val >> 6) & pmask; 168662306a36Sopenharmony_ci rep[3].edge = val & pmask; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 168962306a36Sopenharmony_ci rep[4].edge = (val >> 10) & pmask; 169062306a36Sopenharmony_ci rep[5].edge = (val >> 4) & pmask; 169162306a36Sopenharmony_ci rep[6].edge = (val << 2) & pmask; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci AR5K_EEPROM_READ(offset++, val); 169462306a36Sopenharmony_ci rep[6].edge |= (val >> 14) & 0x3; 169562306a36Sopenharmony_ci rep[7].edge = (val >> 8) & pmask; 169662306a36Sopenharmony_ci } 169762306a36Sopenharmony_ci for (j = 0; j < AR5K_EEPROM_N_EDGES; j++) { 169862306a36Sopenharmony_ci rep[j].freq = ath5k_eeprom_bin2freq(ee, 169962306a36Sopenharmony_ci rep[j].freq, ctl_mode); 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci rep += AR5K_EEPROM_N_EDGES; 170262306a36Sopenharmony_ci } 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci return 0; 170562306a36Sopenharmony_ci} 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_cistatic int 170862306a36Sopenharmony_ciath5k_eeprom_read_spur_chans(struct ath5k_hw *ah) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; 171162306a36Sopenharmony_ci u32 offset; 171262306a36Sopenharmony_ci u16 val; 171362306a36Sopenharmony_ci int i; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci offset = AR5K_EEPROM_CTL(ee->ee_version) + 171662306a36Sopenharmony_ci AR5K_EEPROM_N_CTLS(ee->ee_version); 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci if (ee->ee_version < AR5K_EEPROM_VERSION_5_3) { 171962306a36Sopenharmony_ci /* No spur info for 5GHz */ 172062306a36Sopenharmony_ci ee->ee_spur_chans[0][0] = AR5K_EEPROM_NO_SPUR; 172162306a36Sopenharmony_ci /* 2 channels for 2GHz (2464/2420) */ 172262306a36Sopenharmony_ci ee->ee_spur_chans[0][1] = AR5K_EEPROM_5413_SPUR_CHAN_1; 172362306a36Sopenharmony_ci ee->ee_spur_chans[1][1] = AR5K_EEPROM_5413_SPUR_CHAN_2; 172462306a36Sopenharmony_ci ee->ee_spur_chans[2][1] = AR5K_EEPROM_NO_SPUR; 172562306a36Sopenharmony_ci } else if (ee->ee_version >= AR5K_EEPROM_VERSION_5_3) { 172662306a36Sopenharmony_ci for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) { 172762306a36Sopenharmony_ci AR5K_EEPROM_READ(offset, val); 172862306a36Sopenharmony_ci ee->ee_spur_chans[i][0] = val; 172962306a36Sopenharmony_ci AR5K_EEPROM_READ(offset + AR5K_EEPROM_N_SPUR_CHANS, 173062306a36Sopenharmony_ci val); 173162306a36Sopenharmony_ci ee->ee_spur_chans[i][1] = val; 173262306a36Sopenharmony_ci offset++; 173362306a36Sopenharmony_ci } 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci return 0; 173762306a36Sopenharmony_ci} 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci/***********************\ 174162306a36Sopenharmony_ci* Init/Detach functions * 174262306a36Sopenharmony_ci\***********************/ 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci/* 174562306a36Sopenharmony_ci * Initialize eeprom data structure 174662306a36Sopenharmony_ci */ 174762306a36Sopenharmony_ciint 174862306a36Sopenharmony_ciath5k_eeprom_init(struct ath5k_hw *ah) 174962306a36Sopenharmony_ci{ 175062306a36Sopenharmony_ci int err; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci err = ath5k_eeprom_init_header(ah); 175362306a36Sopenharmony_ci if (err < 0) 175462306a36Sopenharmony_ci return err; 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci err = ath5k_eeprom_init_modes(ah); 175762306a36Sopenharmony_ci if (err < 0) 175862306a36Sopenharmony_ci return err; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci err = ath5k_eeprom_read_pcal_info(ah); 176162306a36Sopenharmony_ci if (err < 0) 176262306a36Sopenharmony_ci return err; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci err = ath5k_eeprom_read_ctl_info(ah); 176562306a36Sopenharmony_ci if (err < 0) 176662306a36Sopenharmony_ci return err; 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci err = ath5k_eeprom_read_spur_chans(ah); 176962306a36Sopenharmony_ci if (err < 0) 177062306a36Sopenharmony_ci return err; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci return 0; 177362306a36Sopenharmony_ci} 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_civoid 177662306a36Sopenharmony_ciath5k_eeprom_detach(struct ath5k_hw *ah) 177762306a36Sopenharmony_ci{ 177862306a36Sopenharmony_ci u8 mode; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci for (mode = AR5K_EEPROM_MODE_11A; mode <= AR5K_EEPROM_MODE_11G; mode++) 178162306a36Sopenharmony_ci ath5k_eeprom_free_pcal_info(ah, mode); 178262306a36Sopenharmony_ci} 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ciint 178562306a36Sopenharmony_ciath5k_eeprom_mode_from_channel(struct ath5k_hw *ah, 178662306a36Sopenharmony_ci struct ieee80211_channel *channel) 178762306a36Sopenharmony_ci{ 178862306a36Sopenharmony_ci switch (channel->hw_value) { 178962306a36Sopenharmony_ci case AR5K_MODE_11A: 179062306a36Sopenharmony_ci return AR5K_EEPROM_MODE_11A; 179162306a36Sopenharmony_ci case AR5K_MODE_11G: 179262306a36Sopenharmony_ci return AR5K_EEPROM_MODE_11G; 179362306a36Sopenharmony_ci case AR5K_MODE_11B: 179462306a36Sopenharmony_ci return AR5K_EEPROM_MODE_11B; 179562306a36Sopenharmony_ci default: 179662306a36Sopenharmony_ci ATH5K_WARN(ah, "channel is not A/B/G!"); 179762306a36Sopenharmony_ci return AR5K_EEPROM_MODE_11A; 179862306a36Sopenharmony_ci } 179962306a36Sopenharmony_ci} 1800