Path: blob/main/sys/dev/ath/ath_hal/ah_eeprom_v14.c
39537 views
/*-1* SPDX-License-Identifier: ISC2*3* Copyright (c) 2008 Sam Leffler, Errno Consulting4* Copyright (c) 2008 Atheros Communications, Inc.5*6* Permission to use, copy, modify, and/or distribute this software for any7* purpose with or without fee is hereby granted, provided that the above8* copyright notice and this permission notice appear in all copies.9*10* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES11* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR13* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF16* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17*/18#include "opt_ah.h"1920#include "ah.h"21#include "ah_internal.h"22#include "ah_eeprom_v14.h"2324static HAL_STATUS25v14EepromGet(struct ath_hal *ah, int param, void *val)26{27#define CHAN_A_IDX 028#define CHAN_B_IDX 129#define IS_VERS(op, v) ((pBase->version & AR5416_EEP_VER_MINOR_MASK) op (v))30HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;31const MODAL_EEP_HEADER *pModal = ee->ee_base.modalHeader;32const BASE_EEP_HEADER *pBase = &ee->ee_base.baseEepHeader;33uint32_t sum;34uint8_t *macaddr;35int i;3637switch (param) {38case AR_EEP_NFTHRESH_5:39*(int16_t *)val = pModal[0].noiseFloorThreshCh[0];40return HAL_OK;41case AR_EEP_NFTHRESH_2:42*(int16_t *)val = pModal[1].noiseFloorThreshCh[0];43return HAL_OK;44case AR_EEP_MACADDR: /* Get MAC Address */45sum = 0;46macaddr = val;47for (i = 0; i < 6; i++) {48macaddr[i] = pBase->macAddr[i];49sum += pBase->macAddr[i];50}51if (sum == 0 || sum == 0xffff*3) {52HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad mac address %s\n",53__func__, ath_hal_ether_sprintf(macaddr));54return HAL_EEBADMAC;55}56return HAL_OK;57case AR_EEP_REGDMN_0:58return pBase->regDmn[0];59case AR_EEP_REGDMN_1:60return pBase->regDmn[1];61case AR_EEP_OPCAP:62return pBase->deviceCap;63case AR_EEP_OPMODE:64return pBase->opCapFlags;65case AR_EEP_RFSILENT:66return pBase->rfSilent;67case AR_EEP_OB_5:68return pModal[CHAN_A_IDX].ob;69case AR_EEP_DB_5:70return pModal[CHAN_A_IDX].db;71case AR_EEP_OB_2:72return pModal[CHAN_B_IDX].ob;73case AR_EEP_DB_2:74return pModal[CHAN_B_IDX].db;75case AR_EEP_TXMASK:76return pBase->txMask;77case AR_EEP_RXMASK:78return pBase->rxMask;79case AR_EEP_RXGAIN_TYPE:80return IS_VERS(>=, AR5416_EEP_MINOR_VER_17) ?81pBase->rxGainType : AR5416_EEP_RXGAIN_ORIG;82case AR_EEP_TXGAIN_TYPE:83return IS_VERS(>=, AR5416_EEP_MINOR_VER_19) ?84pBase->txGainType : AR5416_EEP_TXGAIN_ORIG;85case AR_EEP_FSTCLK_5G:86/* 5ghz fastclock is always enabled for Merlin minor <= 16 */87if (IS_VERS(<=, AR5416_EEP_MINOR_VER_16))88return HAL_OK;89return pBase->fastClk5g ? HAL_OK : HAL_EIO;90case AR_EEP_OL_PWRCTRL:91HALASSERT(val == AH_NULL);92return pBase->openLoopPwrCntl ? HAL_OK : HAL_EIO;93case AR_EEP_DAC_HPWR_5G:94if (IS_VERS(>=, AR5416_EEP_MINOR_VER_20)) {95*(uint8_t *) val = pBase->dacHiPwrMode_5G;96return HAL_OK;97} else98return HAL_EIO;99case AR_EEP_FRAC_N_5G:100if (IS_VERS(>=, AR5416_EEP_MINOR_VER_22)) {101*(uint8_t *) val = pBase->frac_n_5g;102} else103*(uint8_t *) val = 0;104return HAL_OK;105case AR_EEP_AMODE:106HALASSERT(val == AH_NULL);107return pBase->opCapFlags & AR5416_OPFLAGS_11A ?108HAL_OK : HAL_EIO;109case AR_EEP_BMODE:110case AR_EEP_GMODE:111HALASSERT(val == AH_NULL);112return pBase->opCapFlags & AR5416_OPFLAGS_11G ?113HAL_OK : HAL_EIO;114case AR_EEP_32KHZCRYSTAL:115case AR_EEP_COMPRESS:116case AR_EEP_FASTFRAME: /* XXX policy decision, h/w can do it */117case AR_EEP_WRITEPROTECT: /* NB: no write protect bit */118HALASSERT(val == AH_NULL);119/* fall thru... */120case AR_EEP_MAXQCU: /* NB: not in opCapFlags */121case AR_EEP_KCENTRIES: /* NB: not in opCapFlags */122return HAL_EIO;123case AR_EEP_AES:124case AR_EEP_BURST:125case AR_EEP_RFKILL:126case AR_EEP_TURBO5DISABLE:127case AR_EEP_TURBO2DISABLE:128HALASSERT(val == AH_NULL);129return HAL_OK;130case AR_EEP_ANTGAINMAX_2:131*(int8_t *) val = ee->ee_antennaGainMax[1];132return HAL_OK;133case AR_EEP_ANTGAINMAX_5:134*(int8_t *) val = ee->ee_antennaGainMax[0];135return HAL_OK;136case AR_EEP_PWR_TABLE_OFFSET:137if (IS_VERS(>=, AR5416_EEP_MINOR_VER_21))138*(int8_t *) val = pBase->pwr_table_offset;139else140*(int8_t *) val = AR5416_PWR_TABLE_OFFSET_DB;141return HAL_OK;142case AR_EEP_PWDCLKIND:143if (IS_VERS(>=, AR5416_EEP_MINOR_VER_10)) {144*(uint8_t *) val = pBase->pwdclkind;145return HAL_OK;146}147return HAL_EIO;148149default:150HALASSERT(0);151return HAL_EINVAL;152}153#undef IS_VERS154#undef CHAN_A_IDX155#undef CHAN_B_IDX156}157158static HAL_STATUS159v14EepromSet(struct ath_hal *ah, int param, int v)160{161HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;162163switch (param) {164case AR_EEP_ANTGAINMAX_2:165ee->ee_antennaGainMax[1] = (int8_t) v;166return HAL_OK;167case AR_EEP_ANTGAINMAX_5:168ee->ee_antennaGainMax[0] = (int8_t) v;169return HAL_OK;170}171return HAL_EINVAL;172}173174static HAL_BOOL175v14EepromDiag(struct ath_hal *ah, int request,176const void *args, uint32_t argsize, void **result, uint32_t *resultsize)177{178HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;179180switch (request) {181case HAL_DIAG_EEPROM:182*result = ee;183*resultsize = sizeof(HAL_EEPROM_v14);184return AH_TRUE;185}186return AH_FALSE;187}188189/* Do structure specific swaps if Eeprom format is non native to host */190static void191eepromSwap(struct ar5416eeprom *ee)192{193uint32_t integer, i, j;194uint16_t word;195MODAL_EEP_HEADER *pModal;196197/* convert Base Eep header */198word = __bswap16(ee->baseEepHeader.length);199ee->baseEepHeader.length = word;200201word = __bswap16(ee->baseEepHeader.checksum);202ee->baseEepHeader.checksum = word;203204word = __bswap16(ee->baseEepHeader.version);205ee->baseEepHeader.version = word;206207word = __bswap16(ee->baseEepHeader.regDmn[0]);208ee->baseEepHeader.regDmn[0] = word;209210word = __bswap16(ee->baseEepHeader.regDmn[1]);211ee->baseEepHeader.regDmn[1] = word;212213word = __bswap16(ee->baseEepHeader.rfSilent);214ee->baseEepHeader.rfSilent = word;215216word = __bswap16(ee->baseEepHeader.blueToothOptions);217ee->baseEepHeader.blueToothOptions = word;218219word = __bswap16(ee->baseEepHeader.deviceCap);220ee->baseEepHeader.deviceCap = word;221222/* convert Modal Eep header */223for (j = 0; j < 2; j++) {224pModal = &ee->modalHeader[j];225226/* XXX linux/ah_osdep.h only defines __bswap32 for BE */227integer = __bswap32(pModal->antCtrlCommon);228pModal->antCtrlCommon = integer;229230for (i = 0; i < AR5416_MAX_CHAINS; i++) {231integer = __bswap32(pModal->antCtrlChain[i]);232pModal->antCtrlChain[i] = integer;233}234for (i = 0; i < 3; i++) {235word = __bswap16(pModal->xpaBiasLvlFreq[i]);236pModal->xpaBiasLvlFreq[i] = word;237}238for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) {239word = __bswap16(pModal->spurChans[i].spurChan);240pModal->spurChans[i].spurChan = word;241}242}243}244245static uint16_t246v14EepromGetSpurChan(struct ath_hal *ah, int ix, HAL_BOOL is2GHz)247{248HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;249250HALASSERT(0 <= ix && ix < AR5416_EEPROM_MODAL_SPURS);251return ee->ee_base.modalHeader[is2GHz].spurChans[ix].spurChan;252}253254/**************************************************************************255* fbin2freq256*257* Get channel value from binary representation held in eeprom258* RETURNS: the frequency in MHz259*/260static uint16_t261fbin2freq(uint8_t fbin, HAL_BOOL is2GHz)262{263/*264* Reserved value 0xFF provides an empty definition both as265* an fbin and as a frequency - do not convert266*/267if (fbin == AR5416_BCHAN_UNUSED)268return fbin;269return (uint16_t)((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin));270}271272/*273* Copy EEPROM Conformance Testing Limits contents274* into the allocated space275*/276/* USE CTLS from chain zero */277#define CTL_CHAIN 0278279static void280v14EepromReadCTLInfo(struct ath_hal *ah, HAL_EEPROM_v14 *ee)281{282RD_EDGES_POWER *rep = ee->ee_rdEdgesPower;283int i, j;284285HALASSERT(AR5416_NUM_CTLS <= sizeof(ee->ee_rdEdgesPower)/NUM_EDGES);286287for (i = 0; ee->ee_base.ctlIndex[i] != 0 && i < AR5416_NUM_CTLS; i++) {288for (j = 0; j < NUM_EDGES; j ++) {289/* XXX Confirm this is the right thing to do when an invalid channel is stored */290if (ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].bChannel == AR5416_BCHAN_UNUSED) {291rep[j].rdEdge = 0;292rep[j].twice_rdEdgePower = 0;293rep[j].flag = 0;294} else {295rep[j].rdEdge = fbin2freq(296ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].bChannel,297(ee->ee_base.ctlIndex[i] & CTL_MODE_M) != CTL_11A);298rep[j].twice_rdEdgePower = MS(ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].tPowerFlag, CAL_CTL_EDGES_POWER);299rep[j].flag = MS(ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].tPowerFlag, CAL_CTL_EDGES_FLAG) != 0;300}301}302rep += NUM_EDGES;303}304ee->ee_numCtls = i;305HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,306"%s Numctls = %u\n",__func__,i);307}308309/*310* Reclaim any EEPROM-related storage.311*/312static void313v14EepromDetach(struct ath_hal *ah)314{315HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;316317ath_hal_free(ee);318AH_PRIVATE(ah)->ah_eeprom = AH_NULL;319}320321#define owl_get_eep_ver(_ee) \322(((_ee)->ee_base.baseEepHeader.version >> 12) & 0xF)323#define owl_get_eep_rev(_ee) \324(((_ee)->ee_base.baseEepHeader.version) & 0xFFF)325326/*327* Howl is (hopefully) a special case where the endian-ness of the EEPROM328* matches the native endian-ness; and that supplied EEPROMs don't have329* a magic value to check.330*/331HAL_STATUS332ath_hal_v14EepromAttach(struct ath_hal *ah)333{334#define NW(a) (sizeof(a) / sizeof(uint16_t))335HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;336uint16_t *eep_data, magic;337HAL_BOOL need_swap;338u_int w, off, len;339uint32_t sum;340341HALASSERT(ee == AH_NULL);342343/*344* Don't check magic if we're supplied with an EEPROM block,345* typically this is from Howl but it may also be from later346* boards w/ an embedded Merlin.347*/348if (ah->ah_eepromdata == NULL) {349if (!ath_hal_eepromRead(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {350HALDEBUG(ah, HAL_DEBUG_ANY,351"%s Error reading Eeprom MAGIC\n", __func__);352return HAL_EEREAD;353}354HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s Eeprom Magic = 0x%x\n",355__func__, magic);356if (magic != AR5416_EEPROM_MAGIC) {357HALDEBUG(ah, HAL_DEBUG_ANY, "Bad magic number\n");358return HAL_EEMAGIC;359}360}361362ee = ath_hal_malloc(sizeof(HAL_EEPROM_v14));363if (ee == AH_NULL) {364/* XXX message */365return HAL_ENOMEM;366}367368eep_data = (uint16_t *)&ee->ee_base;369for (w = 0; w < NW(struct ar5416eeprom); w++) {370off = owl_eep_start_loc + w; /* NB: AP71 starts at 0 */371if (!ath_hal_eepromRead(ah, off, &eep_data[w])) {372HALDEBUG(ah, HAL_DEBUG_ANY,373"%s eeprom read error at offset 0x%x\n",374__func__, off);375return HAL_EEREAD;376}377}378/* Convert to eeprom native eeprom endian format */379/* XXX this is likely incorrect but will do for now to get howl/ap83 working. */380if (ah->ah_eepromdata == NULL && isBigEndian()) {381for (w = 0; w < NW(struct ar5416eeprom); w++)382eep_data[w] = __bswap16(eep_data[w]);383}384385/*386* At this point, we're in the native eeprom endian format387* Now, determine the eeprom endian by looking at byte 26??388*/389need_swap = ((ee->ee_base.baseEepHeader.eepMisc & AR5416_EEPMISC_BIG_ENDIAN) != 0) ^ isBigEndian();390if (need_swap) {391HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,392"Byte swap EEPROM contents.\n");393len = __bswap16(ee->ee_base.baseEepHeader.length);394} else {395len = ee->ee_base.baseEepHeader.length;396}397len = AH_MIN(len, sizeof(struct ar5416eeprom)) / sizeof(uint16_t);398399/* Apply the checksum, done in native eeprom format */400/* XXX - Need to check to make sure checksum calculation is done401* in the correct endian format. Right now, it seems it would402* cast the raw data to host format and do the calculation, which may403* not be correct as the calculation may need to be done in the native404* eeprom format405*/406sum = 0;407for (w = 0; w < len; w++)408sum ^= eep_data[w];409/* Check CRC - Attach should fail on a bad checksum */410if (sum != 0xffff) {411HALDEBUG(ah, HAL_DEBUG_ANY,412"Bad EEPROM checksum 0x%x (Len=%u)\n", sum, len);413return HAL_EEBADSUM;414}415416if (need_swap)417eepromSwap(&ee->ee_base); /* byte swap multi-byte data */418419/* swap words 0+2 so version is at the front */420magic = eep_data[0];421eep_data[0] = eep_data[2];422eep_data[2] = magic;423424HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,425"%s Eeprom Version %u.%u\n", __func__,426owl_get_eep_ver(ee), owl_get_eep_rev(ee));427428/* NB: must be after all byte swapping */429if (owl_get_eep_ver(ee) != AR5416_EEP_VER) {430HALDEBUG(ah, HAL_DEBUG_ANY,431"Bad EEPROM version 0x%x\n", owl_get_eep_ver(ee));432return HAL_EEBADSUM;433}434435v14EepromReadCTLInfo(ah, ee); /* Get CTLs */436437AH_PRIVATE(ah)->ah_eeprom = ee;438AH_PRIVATE(ah)->ah_eeversion = ee->ee_base.baseEepHeader.version;439AH_PRIVATE(ah)->ah_eepromDetach = v14EepromDetach;440AH_PRIVATE(ah)->ah_eepromGet = v14EepromGet;441AH_PRIVATE(ah)->ah_eepromSet = v14EepromSet;442AH_PRIVATE(ah)->ah_getSpurChan = v14EepromGetSpurChan;443AH_PRIVATE(ah)->ah_eepromDiag = v14EepromDiag;444return HAL_OK;445#undef NW446}447448449