162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "hw.h" 1862306a36Sopenharmony_ci#include <linux/ath9k_platform.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_civoid ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci REG_WRITE(ah, reg, val); 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci if (ah->config.analog_shiftreg) 2562306a36Sopenharmony_ci udelay(100); 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_civoid ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask, 2962306a36Sopenharmony_ci u32 shift, u32 val) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci REG_RMW(ah, reg, ((val << shift) & mask), mask); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (ah->config.analog_shiftreg) 3462306a36Sopenharmony_ci udelay(100); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciint16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, 3862306a36Sopenharmony_ci int16_t targetLeft, int16_t targetRight) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci int16_t rv; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (srcRight == srcLeft) { 4362306a36Sopenharmony_ci rv = targetLeft; 4462306a36Sopenharmony_ci } else { 4562306a36Sopenharmony_ci rv = (int16_t) (((target - srcLeft) * targetRight + 4662306a36Sopenharmony_ci (srcRight - target) * targetLeft) / 4762306a36Sopenharmony_ci (srcRight - srcLeft)); 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci return rv; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cibool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, 5362306a36Sopenharmony_ci u16 *indexL, u16 *indexR) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci u16 i; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (target <= pList[0]) { 5862306a36Sopenharmony_ci *indexL = *indexR = 0; 5962306a36Sopenharmony_ci return true; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci if (target >= pList[listSize - 1]) { 6262306a36Sopenharmony_ci *indexL = *indexR = (u16) (listSize - 1); 6362306a36Sopenharmony_ci return true; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci for (i = 0; i < listSize - 1; i++) { 6762306a36Sopenharmony_ci if (pList[i] == target) { 6862306a36Sopenharmony_ci *indexL = *indexR = i; 6962306a36Sopenharmony_ci return true; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci if (target < pList[i + 1]) { 7262306a36Sopenharmony_ci *indexL = i; 7362306a36Sopenharmony_ci *indexR = (u16) (i + 1); 7462306a36Sopenharmony_ci return false; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci return false; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_civoid ath9k_hw_usb_gen_fill_eeprom(struct ath_hw *ah, u16 *eep_data, 8162306a36Sopenharmony_ci int eep_start_loc, int size) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int i = 0, j, addr; 8462306a36Sopenharmony_ci u32 addrdata[8]; 8562306a36Sopenharmony_ci u32 data[8]; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci for (addr = 0; addr < size; addr++) { 8862306a36Sopenharmony_ci addrdata[i] = AR5416_EEPROM_OFFSET + 8962306a36Sopenharmony_ci ((addr + eep_start_loc) << AR5416_EEPROM_S); 9062306a36Sopenharmony_ci i++; 9162306a36Sopenharmony_ci if (i == 8) { 9262306a36Sopenharmony_ci REG_READ_MULTI(ah, addrdata, data, i); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci for (j = 0; j < i; j++) { 9562306a36Sopenharmony_ci *eep_data = data[j]; 9662306a36Sopenharmony_ci eep_data++; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci i = 0; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (i != 0) { 10362306a36Sopenharmony_ci REG_READ_MULTI(ah, addrdata, data, i); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci for (j = 0; j < i; j++) { 10662306a36Sopenharmony_ci *eep_data = data[j]; 10762306a36Sopenharmony_ci eep_data++; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic bool ath9k_hw_nvram_read_array(u16 *blob, size_t blob_size, 11362306a36Sopenharmony_ci off_t offset, u16 *data) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci if (offset >= blob_size) 11662306a36Sopenharmony_ci return false; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci *data = blob[offset]; 11962306a36Sopenharmony_ci return true; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic bool ath9k_hw_nvram_read_pdata(struct ath9k_platform_data *pdata, 12362306a36Sopenharmony_ci off_t offset, u16 *data) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci return ath9k_hw_nvram_read_array(pdata->eeprom_data, 12662306a36Sopenharmony_ci ARRAY_SIZE(pdata->eeprom_data), 12762306a36Sopenharmony_ci offset, data); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic bool ath9k_hw_nvram_read_firmware(const struct firmware *eeprom_blob, 13162306a36Sopenharmony_ci off_t offset, u16 *data) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci return ath9k_hw_nvram_read_array((u16 *) eeprom_blob->data, 13462306a36Sopenharmony_ci eeprom_blob->size / sizeof(u16), 13562306a36Sopenharmony_ci offset, data); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic bool ath9k_hw_nvram_read_nvmem(struct ath_hw *ah, off_t offset, 13962306a36Sopenharmony_ci u16 *data) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci return ath9k_hw_nvram_read_array(ah->nvmem_blob, 14262306a36Sopenharmony_ci ah->nvmem_blob_len / sizeof(u16), 14362306a36Sopenharmony_ci offset, data); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cibool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 14962306a36Sopenharmony_ci struct ath9k_platform_data *pdata = ah->dev->platform_data; 15062306a36Sopenharmony_ci bool ret; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (ah->nvmem_blob) 15362306a36Sopenharmony_ci ret = ath9k_hw_nvram_read_nvmem(ah, off, data); 15462306a36Sopenharmony_ci else if (ah->eeprom_blob) 15562306a36Sopenharmony_ci ret = ath9k_hw_nvram_read_firmware(ah->eeprom_blob, off, data); 15662306a36Sopenharmony_ci else if (pdata && !pdata->use_eeprom) 15762306a36Sopenharmony_ci ret = ath9k_hw_nvram_read_pdata(pdata, off, data); 15862306a36Sopenharmony_ci else 15962306a36Sopenharmony_ci ret = common->bus_ops->eeprom_read(common, off, data); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (!ret) 16262306a36Sopenharmony_ci ath_dbg(common, EEPROM, 16362306a36Sopenharmony_ci "unable to read eeprom region at offset %u\n", off); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return ret; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciint ath9k_hw_nvram_swap_data(struct ath_hw *ah, bool *swap_needed, int size) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci u16 magic; 17162306a36Sopenharmony_ci u16 *eepdata; 17262306a36Sopenharmony_ci int i; 17362306a36Sopenharmony_ci bool needs_byteswap = false; 17462306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { 17762306a36Sopenharmony_ci ath_err(common, "Reading Magic # failed\n"); 17862306a36Sopenharmony_ci return -EIO; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (swab16(magic) == AR5416_EEPROM_MAGIC) { 18262306a36Sopenharmony_ci needs_byteswap = true; 18362306a36Sopenharmony_ci ath_dbg(common, EEPROM, 18462306a36Sopenharmony_ci "EEPROM needs byte-swapping to correct endianness.\n"); 18562306a36Sopenharmony_ci } else if (magic != AR5416_EEPROM_MAGIC) { 18662306a36Sopenharmony_ci if (ath9k_hw_use_flash(ah)) { 18762306a36Sopenharmony_ci ath_dbg(common, EEPROM, 18862306a36Sopenharmony_ci "Ignoring invalid EEPROM magic (0x%04x).\n", 18962306a36Sopenharmony_ci magic); 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci ath_err(common, 19262306a36Sopenharmony_ci "Invalid EEPROM magic (0x%04x).\n", magic); 19362306a36Sopenharmony_ci return -EINVAL; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (needs_byteswap) { 19862306a36Sopenharmony_ci if (ah->ah_flags & AH_NO_EEP_SWAP) { 19962306a36Sopenharmony_ci ath_info(common, 20062306a36Sopenharmony_ci "Ignoring endianness difference in EEPROM magic bytes.\n"); 20162306a36Sopenharmony_ci } else { 20262306a36Sopenharmony_ci eepdata = (u16 *)(&ah->eeprom); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci for (i = 0; i < size; i++) 20562306a36Sopenharmony_ci eepdata[i] = swab16(eepdata[i]); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (ah->eep_ops->get_eepmisc(ah) & AR5416_EEPMISC_BIG_ENDIAN) { 21062306a36Sopenharmony_ci *swap_needed = true; 21162306a36Sopenharmony_ci ath_dbg(common, EEPROM, 21262306a36Sopenharmony_ci "Big Endian EEPROM detected according to EEPMISC register.\n"); 21362306a36Sopenharmony_ci } else { 21462306a36Sopenharmony_ci *swap_needed = false; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cibool ath9k_hw_nvram_validate_checksum(struct ath_hw *ah, int size) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci u32 i, sum = 0; 22362306a36Sopenharmony_ci u16 *eepdata = (u16 *)(&ah->eeprom); 22462306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci for (i = 0; i < size; i++) 22762306a36Sopenharmony_ci sum ^= eepdata[i]; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (sum != 0xffff) { 23062306a36Sopenharmony_ci ath_err(common, "Bad EEPROM checksum 0x%x\n", sum); 23162306a36Sopenharmony_ci return false; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return true; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cibool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (ah->eep_ops->get_eeprom_ver(ah) != version || 24262306a36Sopenharmony_ci ah->eep_ops->get_eeprom_rev(ah) < minrev) { 24362306a36Sopenharmony_ci ath_err(common, "Bad EEPROM VER 0x%04x or REV 0x%04x\n", 24462306a36Sopenharmony_ci ah->eep_ops->get_eeprom_ver(ah), 24562306a36Sopenharmony_ci ah->eep_ops->get_eeprom_rev(ah)); 24662306a36Sopenharmony_ci return false; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return true; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_civoid ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, 25362306a36Sopenharmony_ci u8 *pVpdList, u16 numIntercepts, 25462306a36Sopenharmony_ci u8 *pRetVpdList) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci u16 i, k; 25762306a36Sopenharmony_ci u8 currPwr = pwrMin; 25862306a36Sopenharmony_ci u16 idxL = 0, idxR = 0; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { 26162306a36Sopenharmony_ci ath9k_hw_get_lower_upper_index(currPwr, pPwrList, 26262306a36Sopenharmony_ci numIntercepts, &(idxL), 26362306a36Sopenharmony_ci &(idxR)); 26462306a36Sopenharmony_ci if (idxR < 1) 26562306a36Sopenharmony_ci idxR = 1; 26662306a36Sopenharmony_ci if (idxL == numIntercepts - 1) 26762306a36Sopenharmony_ci idxL = (u16) (numIntercepts - 2); 26862306a36Sopenharmony_ci if (pPwrList[idxL] == pPwrList[idxR]) 26962306a36Sopenharmony_ci k = pVpdList[idxL]; 27062306a36Sopenharmony_ci else 27162306a36Sopenharmony_ci k = (u16)(((currPwr - pPwrList[idxL]) * pVpdList[idxR] + 27262306a36Sopenharmony_ci (pPwrList[idxR] - currPwr) * pVpdList[idxL]) / 27362306a36Sopenharmony_ci (pPwrList[idxR] - pPwrList[idxL])); 27462306a36Sopenharmony_ci pRetVpdList[i] = (u8) k; 27562306a36Sopenharmony_ci currPwr += 2; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_civoid ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, 28062306a36Sopenharmony_ci struct ath9k_channel *chan, 28162306a36Sopenharmony_ci struct cal_target_power_leg *powInfo, 28262306a36Sopenharmony_ci u16 numChannels, 28362306a36Sopenharmony_ci struct cal_target_power_leg *pNewPower, 28462306a36Sopenharmony_ci u16 numRates, bool isExtTarget) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct chan_centers centers; 28762306a36Sopenharmony_ci u16 clo, chi; 28862306a36Sopenharmony_ci int i; 28962306a36Sopenharmony_ci int matchIndex = -1, lowIndex = -1; 29062306a36Sopenharmony_ci u16 freq; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ath9k_hw_get_channel_centers(ah, chan, ¢ers); 29362306a36Sopenharmony_ci freq = (isExtTarget) ? centers.ext_center : centers.ctl_center; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, 29662306a36Sopenharmony_ci IS_CHAN_2GHZ(chan))) { 29762306a36Sopenharmony_ci matchIndex = 0; 29862306a36Sopenharmony_ci } else { 29962306a36Sopenharmony_ci for (i = 0; (i < numChannels) && 30062306a36Sopenharmony_ci (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { 30162306a36Sopenharmony_ci if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel, 30262306a36Sopenharmony_ci IS_CHAN_2GHZ(chan))) { 30362306a36Sopenharmony_ci matchIndex = i; 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci } else if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel, 30662306a36Sopenharmony_ci IS_CHAN_2GHZ(chan)) && i > 0 && 30762306a36Sopenharmony_ci freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel, 30862306a36Sopenharmony_ci IS_CHAN_2GHZ(chan))) { 30962306a36Sopenharmony_ci lowIndex = i - 1; 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci if ((matchIndex == -1) && (lowIndex == -1)) 31462306a36Sopenharmony_ci matchIndex = i - 1; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (matchIndex != -1) { 31862306a36Sopenharmony_ci *pNewPower = powInfo[matchIndex]; 31962306a36Sopenharmony_ci } else { 32062306a36Sopenharmony_ci clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, 32162306a36Sopenharmony_ci IS_CHAN_2GHZ(chan)); 32262306a36Sopenharmony_ci chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, 32362306a36Sopenharmony_ci IS_CHAN_2GHZ(chan)); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci for (i = 0; i < numRates; i++) { 32662306a36Sopenharmony_ci pNewPower->tPow2x[i] = 32762306a36Sopenharmony_ci (u8)ath9k_hw_interpolate(freq, clo, chi, 32862306a36Sopenharmony_ci powInfo[lowIndex].tPow2x[i], 32962306a36Sopenharmony_ci powInfo[lowIndex + 1].tPow2x[i]); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_civoid ath9k_hw_get_target_powers(struct ath_hw *ah, 33562306a36Sopenharmony_ci struct ath9k_channel *chan, 33662306a36Sopenharmony_ci struct cal_target_power_ht *powInfo, 33762306a36Sopenharmony_ci u16 numChannels, 33862306a36Sopenharmony_ci struct cal_target_power_ht *pNewPower, 33962306a36Sopenharmony_ci u16 numRates, bool isHt40Target) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci struct chan_centers centers; 34262306a36Sopenharmony_ci u16 clo, chi; 34362306a36Sopenharmony_ci int i; 34462306a36Sopenharmony_ci int matchIndex = -1, lowIndex = -1; 34562306a36Sopenharmony_ci u16 freq; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ath9k_hw_get_channel_centers(ah, chan, ¢ers); 34862306a36Sopenharmony_ci freq = isHt40Target ? centers.synth_center : centers.ctl_center; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) { 35162306a36Sopenharmony_ci matchIndex = 0; 35262306a36Sopenharmony_ci } else { 35362306a36Sopenharmony_ci for (i = 0; (i < numChannels) && 35462306a36Sopenharmony_ci (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { 35562306a36Sopenharmony_ci if (freq == ath9k_hw_fbin2freq(powInfo[i].bChannel, 35662306a36Sopenharmony_ci IS_CHAN_2GHZ(chan))) { 35762306a36Sopenharmony_ci matchIndex = i; 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci } else 36062306a36Sopenharmony_ci if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel, 36162306a36Sopenharmony_ci IS_CHAN_2GHZ(chan)) && i > 0 && 36262306a36Sopenharmony_ci freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel, 36362306a36Sopenharmony_ci IS_CHAN_2GHZ(chan))) { 36462306a36Sopenharmony_ci lowIndex = i - 1; 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci if ((matchIndex == -1) && (lowIndex == -1)) 36962306a36Sopenharmony_ci matchIndex = i - 1; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (matchIndex != -1) { 37362306a36Sopenharmony_ci *pNewPower = powInfo[matchIndex]; 37462306a36Sopenharmony_ci } else { 37562306a36Sopenharmony_ci clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, 37662306a36Sopenharmony_ci IS_CHAN_2GHZ(chan)); 37762306a36Sopenharmony_ci chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, 37862306a36Sopenharmony_ci IS_CHAN_2GHZ(chan)); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci for (i = 0; i < numRates; i++) { 38162306a36Sopenharmony_ci pNewPower->tPow2x[i] = (u8)ath9k_hw_interpolate(freq, 38262306a36Sopenharmony_ci clo, chi, 38362306a36Sopenharmony_ci powInfo[lowIndex].tPow2x[i], 38462306a36Sopenharmony_ci powInfo[lowIndex + 1].tPow2x[i]); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciu16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, 39062306a36Sopenharmony_ci bool is2GHz, int num_band_edges) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci u16 twiceMaxEdgePower = MAX_RATE_POWER; 39362306a36Sopenharmony_ci int i; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci for (i = 0; (i < num_band_edges) && 39662306a36Sopenharmony_ci (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) { 39762306a36Sopenharmony_ci if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, is2GHz)) { 39862306a36Sopenharmony_ci twiceMaxEdgePower = CTL_EDGE_TPOWER(pRdEdgesPower[i].ctl); 39962306a36Sopenharmony_ci break; 40062306a36Sopenharmony_ci } else if ((i > 0) && 40162306a36Sopenharmony_ci (freq < ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, 40262306a36Sopenharmony_ci is2GHz))) { 40362306a36Sopenharmony_ci if (ath9k_hw_fbin2freq(pRdEdgesPower[i - 1].bChannel, 40462306a36Sopenharmony_ci is2GHz) < freq && 40562306a36Sopenharmony_ci CTL_EDGE_FLAGS(pRdEdgesPower[i - 1].ctl)) { 40662306a36Sopenharmony_ci twiceMaxEdgePower = 40762306a36Sopenharmony_ci CTL_EDGE_TPOWER(pRdEdgesPower[i - 1].ctl); 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return twiceMaxEdgePower; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ciu16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit, 41762306a36Sopenharmony_ci u8 antenna_reduction) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci u16 reduction = antenna_reduction; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* 42262306a36Sopenharmony_ci * Reduce scaled Power by number of chains active 42362306a36Sopenharmony_ci * to get the per chain tx power level. 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci switch (ar5416_get_ntxchains(ah->txchainmask)) { 42662306a36Sopenharmony_ci case 1: 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci case 2: 42962306a36Sopenharmony_ci reduction += POWER_CORRECTION_FOR_TWO_CHAIN; 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci case 3: 43262306a36Sopenharmony_ci reduction += POWER_CORRECTION_FOR_THREE_CHAIN; 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (power_limit > reduction) 43762306a36Sopenharmony_ci power_limit -= reduction; 43862306a36Sopenharmony_ci else 43962306a36Sopenharmony_ci power_limit = 0; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return min_t(u16, power_limit, MAX_RATE_POWER); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_civoid ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 44762306a36Sopenharmony_ci struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci switch (ar5416_get_ntxchains(ah->txchainmask)) { 45062306a36Sopenharmony_ci case 1: 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci case 2: 45362306a36Sopenharmony_ci regulatory->max_power_level += POWER_CORRECTION_FOR_TWO_CHAIN; 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci case 3: 45662306a36Sopenharmony_ci regulatory->max_power_level += POWER_CORRECTION_FOR_THREE_CHAIN; 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci default: 45962306a36Sopenharmony_ci ath_dbg(common, EEPROM, "Invalid chainmask configuration\n"); 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_civoid ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, 46562306a36Sopenharmony_ci struct ath9k_channel *chan, 46662306a36Sopenharmony_ci void *pRawDataSet, 46762306a36Sopenharmony_ci u8 *bChans, u16 availPiers, 46862306a36Sopenharmony_ci u16 tPdGainOverlap, 46962306a36Sopenharmony_ci u16 *pPdGainBoundaries, u8 *pPDADCValues, 47062306a36Sopenharmony_ci u16 numXpdGains) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci int i, j, k; 47362306a36Sopenharmony_ci int16_t ss; 47462306a36Sopenharmony_ci u16 idxL = 0, idxR = 0, numPiers; 47562306a36Sopenharmony_ci static u8 vpdTableL[AR5416_NUM_PD_GAINS] 47662306a36Sopenharmony_ci [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; 47762306a36Sopenharmony_ci static u8 vpdTableR[AR5416_NUM_PD_GAINS] 47862306a36Sopenharmony_ci [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; 47962306a36Sopenharmony_ci static u8 vpdTableI[AR5416_NUM_PD_GAINS] 48062306a36Sopenharmony_ci [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR; 48362306a36Sopenharmony_ci u8 minPwrT4[AR5416_NUM_PD_GAINS]; 48462306a36Sopenharmony_ci u8 maxPwrT4[AR5416_NUM_PD_GAINS]; 48562306a36Sopenharmony_ci int16_t vpdStep; 48662306a36Sopenharmony_ci int16_t tmpVal; 48762306a36Sopenharmony_ci u16 sizeCurrVpdTable, maxIndex, tgtIndex; 48862306a36Sopenharmony_ci bool match; 48962306a36Sopenharmony_ci int16_t minDelta = 0; 49062306a36Sopenharmony_ci struct chan_centers centers; 49162306a36Sopenharmony_ci int pdgain_boundary_default; 49262306a36Sopenharmony_ci struct cal_data_per_freq *data_def = pRawDataSet; 49362306a36Sopenharmony_ci struct cal_data_per_freq_4k *data_4k = pRawDataSet; 49462306a36Sopenharmony_ci struct cal_data_per_freq_ar9287 *data_9287 = pRawDataSet; 49562306a36Sopenharmony_ci bool eeprom_4k = AR_SREV_9285(ah) || AR_SREV_9271(ah); 49662306a36Sopenharmony_ci int intercepts; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (AR_SREV_9287(ah)) 49962306a36Sopenharmony_ci intercepts = AR9287_PD_GAIN_ICEPTS; 50062306a36Sopenharmony_ci else 50162306a36Sopenharmony_ci intercepts = AR5416_PD_GAIN_ICEPTS; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci memset(&minPwrT4, 0, AR5416_NUM_PD_GAINS); 50462306a36Sopenharmony_ci ath9k_hw_get_channel_centers(ah, chan, ¢ers); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci for (numPiers = 0; numPiers < availPiers; numPiers++) { 50762306a36Sopenharmony_ci if (bChans[numPiers] == AR5416_BCHAN_UNUSED) 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci match = ath9k_hw_get_lower_upper_index((u8)FREQ2FBIN(centers.synth_center, 51262306a36Sopenharmony_ci IS_CHAN_2GHZ(chan)), 51362306a36Sopenharmony_ci bChans, numPiers, &idxL, &idxR); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (match) { 51662306a36Sopenharmony_ci if (AR_SREV_9287(ah)) { 51762306a36Sopenharmony_ci for (i = 0; i < numXpdGains; i++) { 51862306a36Sopenharmony_ci minPwrT4[i] = data_9287[idxL].pwrPdg[i][0]; 51962306a36Sopenharmony_ci maxPwrT4[i] = data_9287[idxL].pwrPdg[i][intercepts - 1]; 52062306a36Sopenharmony_ci ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], 52162306a36Sopenharmony_ci data_9287[idxL].pwrPdg[i], 52262306a36Sopenharmony_ci data_9287[idxL].vpdPdg[i], 52362306a36Sopenharmony_ci intercepts, 52462306a36Sopenharmony_ci vpdTableI[i]); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci } else if (eeprom_4k) { 52762306a36Sopenharmony_ci for (i = 0; i < numXpdGains; i++) { 52862306a36Sopenharmony_ci minPwrT4[i] = data_4k[idxL].pwrPdg[i][0]; 52962306a36Sopenharmony_ci maxPwrT4[i] = data_4k[idxL].pwrPdg[i][intercepts - 1]; 53062306a36Sopenharmony_ci ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], 53162306a36Sopenharmony_ci data_4k[idxL].pwrPdg[i], 53262306a36Sopenharmony_ci data_4k[idxL].vpdPdg[i], 53362306a36Sopenharmony_ci intercepts, 53462306a36Sopenharmony_ci vpdTableI[i]); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci } else { 53762306a36Sopenharmony_ci for (i = 0; i < numXpdGains; i++) { 53862306a36Sopenharmony_ci minPwrT4[i] = data_def[idxL].pwrPdg[i][0]; 53962306a36Sopenharmony_ci maxPwrT4[i] = data_def[idxL].pwrPdg[i][intercepts - 1]; 54062306a36Sopenharmony_ci ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], 54162306a36Sopenharmony_ci data_def[idxL].pwrPdg[i], 54262306a36Sopenharmony_ci data_def[idxL].vpdPdg[i], 54362306a36Sopenharmony_ci intercepts, 54462306a36Sopenharmony_ci vpdTableI[i]); 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci } else { 54862306a36Sopenharmony_ci for (i = 0; i < numXpdGains; i++) { 54962306a36Sopenharmony_ci if (AR_SREV_9287(ah)) { 55062306a36Sopenharmony_ci pVpdL = data_9287[idxL].vpdPdg[i]; 55162306a36Sopenharmony_ci pPwrL = data_9287[idxL].pwrPdg[i]; 55262306a36Sopenharmony_ci pVpdR = data_9287[idxR].vpdPdg[i]; 55362306a36Sopenharmony_ci pPwrR = data_9287[idxR].pwrPdg[i]; 55462306a36Sopenharmony_ci } else if (eeprom_4k) { 55562306a36Sopenharmony_ci pVpdL = data_4k[idxL].vpdPdg[i]; 55662306a36Sopenharmony_ci pPwrL = data_4k[idxL].pwrPdg[i]; 55762306a36Sopenharmony_ci pVpdR = data_4k[idxR].vpdPdg[i]; 55862306a36Sopenharmony_ci pPwrR = data_4k[idxR].pwrPdg[i]; 55962306a36Sopenharmony_ci } else { 56062306a36Sopenharmony_ci pVpdL = data_def[idxL].vpdPdg[i]; 56162306a36Sopenharmony_ci pPwrL = data_def[idxL].pwrPdg[i]; 56262306a36Sopenharmony_ci pVpdR = data_def[idxR].vpdPdg[i]; 56362306a36Sopenharmony_ci pPwrR = data_def[idxR].pwrPdg[i]; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci minPwrT4[i] = max(pPwrL[0], pPwrR[0]); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci maxPwrT4[i] = 56962306a36Sopenharmony_ci min(pPwrL[intercepts - 1], 57062306a36Sopenharmony_ci pPwrR[intercepts - 1]); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], 57462306a36Sopenharmony_ci pPwrL, pVpdL, 57562306a36Sopenharmony_ci intercepts, 57662306a36Sopenharmony_ci vpdTableL[i]); 57762306a36Sopenharmony_ci ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], 57862306a36Sopenharmony_ci pPwrR, pVpdR, 57962306a36Sopenharmony_ci intercepts, 58062306a36Sopenharmony_ci vpdTableR[i]); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) { 58362306a36Sopenharmony_ci vpdTableI[i][j] = 58462306a36Sopenharmony_ci (u8)(ath9k_hw_interpolate((u16) 58562306a36Sopenharmony_ci FREQ2FBIN(centers. 58662306a36Sopenharmony_ci synth_center, 58762306a36Sopenharmony_ci IS_CHAN_2GHZ 58862306a36Sopenharmony_ci (chan)), 58962306a36Sopenharmony_ci bChans[idxL], bChans[idxR], 59062306a36Sopenharmony_ci vpdTableL[i][j], vpdTableR[i][j])); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci k = 0; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci for (i = 0; i < numXpdGains; i++) { 59862306a36Sopenharmony_ci if (i == (numXpdGains - 1)) 59962306a36Sopenharmony_ci pPdGainBoundaries[i] = 60062306a36Sopenharmony_ci (u16)(maxPwrT4[i] / 2); 60162306a36Sopenharmony_ci else 60262306a36Sopenharmony_ci pPdGainBoundaries[i] = 60362306a36Sopenharmony_ci (u16)((maxPwrT4[i] + minPwrT4[i + 1]) / 4); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci pPdGainBoundaries[i] = 60662306a36Sopenharmony_ci min((u16)MAX_RATE_POWER, pPdGainBoundaries[i]); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci minDelta = 0; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (i == 0) { 61162306a36Sopenharmony_ci if (AR_SREV_9280_20_OR_LATER(ah)) 61262306a36Sopenharmony_ci ss = (int16_t)(0 - (minPwrT4[i] / 2)); 61362306a36Sopenharmony_ci else 61462306a36Sopenharmony_ci ss = 0; 61562306a36Sopenharmony_ci } else { 61662306a36Sopenharmony_ci ss = (int16_t)((pPdGainBoundaries[i - 1] - 61762306a36Sopenharmony_ci (minPwrT4[i] / 2)) - 61862306a36Sopenharmony_ci tPdGainOverlap + 1 + minDelta); 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]); 62162306a36Sopenharmony_ci vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { 62462306a36Sopenharmony_ci tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep); 62562306a36Sopenharmony_ci pPDADCValues[k++] = (u8)((tmpVal < 0) ? 0 : tmpVal); 62662306a36Sopenharmony_ci ss++; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci sizeCurrVpdTable = (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1); 63062306a36Sopenharmony_ci tgtIndex = (u8)(pPdGainBoundaries[i] + tPdGainOverlap - 63162306a36Sopenharmony_ci (minPwrT4[i] / 2)); 63262306a36Sopenharmony_ci maxIndex = (tgtIndex < sizeCurrVpdTable) ? 63362306a36Sopenharmony_ci tgtIndex : sizeCurrVpdTable; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { 63662306a36Sopenharmony_ci pPDADCValues[k++] = vpdTableI[i][ss++]; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] - 64062306a36Sopenharmony_ci vpdTableI[i][sizeCurrVpdTable - 2]); 64162306a36Sopenharmony_ci vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (tgtIndex >= maxIndex) { 64462306a36Sopenharmony_ci while ((ss <= tgtIndex) && 64562306a36Sopenharmony_ci (k < (AR5416_NUM_PDADC_VALUES - 1))) { 64662306a36Sopenharmony_ci tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] + 64762306a36Sopenharmony_ci (ss - maxIndex + 1) * vpdStep)); 64862306a36Sopenharmony_ci pPDADCValues[k++] = (u8)((tmpVal > 255) ? 64962306a36Sopenharmony_ci 255 : tmpVal); 65062306a36Sopenharmony_ci ss++; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (eeprom_4k) 65662306a36Sopenharmony_ci pdgain_boundary_default = 58; 65762306a36Sopenharmony_ci else 65862306a36Sopenharmony_ci pdgain_boundary_default = pPdGainBoundaries[i - 1]; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci while (i < AR5416_PD_GAINS_IN_MASK) { 66162306a36Sopenharmony_ci pPdGainBoundaries[i] = pdgain_boundary_default; 66262306a36Sopenharmony_ci i++; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci while (k < AR5416_NUM_PDADC_VALUES) { 66662306a36Sopenharmony_ci pPDADCValues[k] = pPDADCValues[k - 1]; 66762306a36Sopenharmony_ci k++; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ciint ath9k_hw_eeprom_init(struct ath_hw *ah) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci if (AR_SREV_9300_20_OR_LATER(ah)) 67462306a36Sopenharmony_ci ah->eep_ops = &eep_ar9300_ops; 67562306a36Sopenharmony_ci else if (AR_SREV_9287(ah)) { 67662306a36Sopenharmony_ci ah->eep_ops = &eep_ar9287_ops; 67762306a36Sopenharmony_ci } else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) { 67862306a36Sopenharmony_ci ah->eep_ops = &eep_4k_ops; 67962306a36Sopenharmony_ci } else { 68062306a36Sopenharmony_ci ah->eep_ops = &eep_def_ops; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!ah->eep_ops->fill_eeprom(ah)) 68462306a36Sopenharmony_ci return -EIO; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return ah->eep_ops->check_eeprom(ah); 68762306a36Sopenharmony_ci} 688