Path: blob/main/sys/dev/ath/ath_hal/ah_eeprom_v4k.c
39537 views
/*-1* SPDX-License-Identifier: ISC2*3* Copyright (c) 2009 Rui Paulo <[email protected]>4* Copyright (c) 2008 Sam Leffler, Errno Consulting5* Copyright (c) 2008 Atheros Communications, Inc.6*7* Permission to use, copy, modify, and/or distribute this software for any8* purpose with or without fee is hereby granted, provided that the above9* copyright notice and this permission notice appear in all copies.10*11* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES12* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF13* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR14* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES15* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN16* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF17* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.18*/19#include "opt_ah.h"2021#include "ah.h"22#include "ah_internal.h"23#include "ah_eeprom_v14.h"24#include "ah_eeprom_v4k.h"2526static HAL_STATUS27v4kEepromGet(struct ath_hal *ah, int param, void *val)28{29#define CHAN_A_IDX 030#define CHAN_B_IDX 131#define IS_VERS(op, v) ((pBase->version & AR5416_EEP_VER_MINOR_MASK) op (v))32HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;33const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader;34const BASE_EEP4K_HEADER *pBase = &ee->ee_base.baseEepHeader;35uint32_t sum;36uint8_t *macaddr;37int i;3839switch (param) {40case AR_EEP_NFTHRESH_2:41*(int16_t *)val = pModal->noiseFloorThreshCh[0];42return HAL_OK;43case AR_EEP_MACADDR: /* Get MAC Address */44sum = 0;45macaddr = val;46for (i = 0; i < 6; i++) {47macaddr[i] = pBase->macAddr[i];48sum += pBase->macAddr[i];49}50if (sum == 0 || sum == 0xffff*3) {51HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad mac address %s\n",52__func__, ath_hal_ether_sprintf(macaddr));53return HAL_EEBADMAC;54}55return HAL_OK;56case AR_EEP_REGDMN_0:57return pBase->regDmn[0];58case AR_EEP_REGDMN_1:59return pBase->regDmn[1];60case AR_EEP_OPCAP:61return pBase->deviceCap;62case AR_EEP_OPMODE:63return pBase->opCapFlags;64case AR_EEP_RFSILENT:65return pBase->rfSilent;66case AR_EEP_OB_2:67return pModal->ob_0;68case AR_EEP_DB_2:69return pModal->db1_1;70case AR_EEP_TXMASK:71return pBase->txMask;72case AR_EEP_RXMASK:73return pBase->rxMask;74case AR_EEP_RXGAIN_TYPE:75return AR5416_EEP_RXGAIN_ORIG;76case AR_EEP_TXGAIN_TYPE:77return pBase->txGainType;78case AR_EEP_OL_PWRCTRL:79HALASSERT(val == AH_NULL);80return HAL_EIO;81case AR_EEP_AMODE:82HALASSERT(val == AH_NULL);83return pBase->opCapFlags & AR5416_OPFLAGS_11A ?84HAL_OK : HAL_EIO;85case AR_EEP_BMODE:86case AR_EEP_GMODE:87HALASSERT(val == AH_NULL);88return pBase->opCapFlags & AR5416_OPFLAGS_11G ?89HAL_OK : HAL_EIO;90case AR_EEP_32KHZCRYSTAL:91case AR_EEP_COMPRESS:92case AR_EEP_FASTFRAME: /* XXX policy decision, h/w can do it */93case AR_EEP_WRITEPROTECT: /* NB: no write protect bit */94HALASSERT(val == AH_NULL);95/* fall thru... */96case AR_EEP_MAXQCU: /* NB: not in opCapFlags */97case AR_EEP_KCENTRIES: /* NB: not in opCapFlags */98return HAL_EIO;99case AR_EEP_AES:100case AR_EEP_BURST:101case AR_EEP_RFKILL:102case AR_EEP_TURBO2DISABLE:103HALASSERT(val == AH_NULL);104return HAL_OK;105case AR_EEP_ANTGAINMAX_2:106*(int8_t *) val = ee->ee_antennaGainMax;107return HAL_OK;108default:109HALASSERT(0);110return HAL_EINVAL;111}112#undef IS_VERS113#undef CHAN_A_IDX114#undef CHAN_B_IDX115}116117static HAL_STATUS118v4kEepromSet(struct ath_hal *ah, int param, int v)119{120HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;121122switch (param) {123case AR_EEP_ANTGAINMAX_2:124ee->ee_antennaGainMax = (int8_t) v;125return HAL_OK;126}127return HAL_EINVAL;128}129130static HAL_BOOL131v4kEepromDiag(struct ath_hal *ah, int request,132const void *args, uint32_t argsize, void **result, uint32_t *resultsize)133{134HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;135136switch (request) {137case HAL_DIAG_EEPROM:138*result = ee;139*resultsize = sizeof(HAL_EEPROM_v4k);140return AH_TRUE;141}142return AH_FALSE;143}144145/* Do structure specific swaps if Eeprom format is non native to host */146static void147eepromSwap(struct ar5416eeprom_4k *ee)148{149uint32_t integer, i;150uint16_t word;151MODAL_EEP4K_HEADER *pModal;152153/* convert Base Eep header */154word = __bswap16(ee->baseEepHeader.length);155ee->baseEepHeader.length = word;156157word = __bswap16(ee->baseEepHeader.checksum);158ee->baseEepHeader.checksum = word;159160word = __bswap16(ee->baseEepHeader.version);161ee->baseEepHeader.version = word;162163word = __bswap16(ee->baseEepHeader.regDmn[0]);164ee->baseEepHeader.regDmn[0] = word;165166word = __bswap16(ee->baseEepHeader.regDmn[1]);167ee->baseEepHeader.regDmn[1] = word;168169word = __bswap16(ee->baseEepHeader.rfSilent);170ee->baseEepHeader.rfSilent = word;171172word = __bswap16(ee->baseEepHeader.blueToothOptions);173ee->baseEepHeader.blueToothOptions = word;174175word = __bswap16(ee->baseEepHeader.deviceCap);176ee->baseEepHeader.deviceCap = word;177178/* convert Modal Eep header */179pModal = &ee->modalHeader;180181/* XXX linux/ah_osdep.h only defines __bswap32 for BE */182integer = __bswap32(pModal->antCtrlCommon);183pModal->antCtrlCommon = integer;184185for (i = 0; i < AR5416_4K_MAX_CHAINS; i++) {186integer = __bswap32(pModal->antCtrlChain[i]);187pModal->antCtrlChain[i] = integer;188}189190for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) {191word = __bswap16(pModal->spurChans[i].spurChan);192pModal->spurChans[i].spurChan = word;193}194}195196static uint16_t197v4kEepromGetSpurChan(struct ath_hal *ah, int ix, HAL_BOOL is2GHz)198{199HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;200201HALASSERT(0 <= ix && ix < AR5416_EEPROM_MODAL_SPURS);202HALASSERT(is2GHz);203return ee->ee_base.modalHeader.spurChans[ix].spurChan;204}205206/**************************************************************************207* fbin2freq208*209* Get channel value from binary representation held in eeprom210* RETURNS: the frequency in MHz211*/212static uint16_t213fbin2freq(uint8_t fbin, HAL_BOOL is2GHz)214{215/*216* Reserved value 0xFF provides an empty definition both as217* an fbin and as a frequency - do not convert218*/219if (fbin == AR5416_BCHAN_UNUSED)220return fbin;221return (uint16_t)((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin));222}223224/*225* Copy EEPROM Conformance Testing Limits contents226* into the allocated space227*/228/* USE CTLS from chain zero */229#define CTL_CHAIN 0230231static void232v4kEepromReadCTLInfo(struct ath_hal *ah, HAL_EEPROM_v4k *ee)233{234RD_EDGES_POWER *rep = ee->ee_rdEdgesPower;235int i, j;236237HALASSERT(AR5416_4K_NUM_CTLS <= sizeof(ee->ee_rdEdgesPower)/NUM_EDGES);238239for (i = 0; ee->ee_base.ctlIndex[i] != 0 && i < AR5416_4K_NUM_CTLS; i++) {240for (j = 0; j < NUM_EDGES; j ++) {241/* XXX Confirm this is the right thing to do when an invalid channel is stored */242if (ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].bChannel == AR5416_BCHAN_UNUSED) {243rep[j].rdEdge = 0;244rep[j].twice_rdEdgePower = 0;245rep[j].flag = 0;246} else {247rep[j].rdEdge = fbin2freq(248ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].bChannel,249(ee->ee_base.ctlIndex[i] & CTL_MODE_M) != CTL_11A);250rep[j].twice_rdEdgePower = MS(ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].tPowerFlag, CAL_CTL_EDGES_POWER);251rep[j].flag = MS(ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].tPowerFlag, CAL_CTL_EDGES_FLAG) != 0;252}253}254rep += NUM_EDGES;255}256ee->ee_numCtls = i;257HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,258"%s Numctls = %u\n",__func__,i);259}260261/*262* Reclaim any EEPROM-related storage.263*/264static void265v4kEepromDetach(struct ath_hal *ah)266{267HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;268269ath_hal_free(ee);270AH_PRIVATE(ah)->ah_eeprom = AH_NULL;271}272273#define owl_get_eep_ver(_ee) \274(((_ee)->ee_base.baseEepHeader.version >> 12) & 0xF)275#define owl_get_eep_rev(_ee) \276(((_ee)->ee_base.baseEepHeader.version) & 0xFFF)277278HAL_STATUS279ath_hal_v4kEepromAttach(struct ath_hal *ah)280{281#define NW(a) (sizeof(a) / sizeof(uint16_t))282HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;283uint16_t *eep_data, magic;284HAL_BOOL need_swap;285u_int w, off, len;286uint32_t sum;287288HALASSERT(ee == AH_NULL);289/*290* Don't check magic if we're supplied with an EEPROM block,291* typically this is from Howl but it may also be from later292* boards w/ an embedded WMAC.293*/294if (ah->ah_eepromdata == NULL) {295if (!ath_hal_eepromRead(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {296HALDEBUG(ah, HAL_DEBUG_ANY,297"%s Error reading Eeprom MAGIC\n", __func__);298return HAL_EEREAD;299}300HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s Eeprom Magic = 0x%x\n",301__func__, magic);302if (magic != AR5416_EEPROM_MAGIC) {303HALDEBUG(ah, HAL_DEBUG_ANY, "Bad magic number\n");304return HAL_EEMAGIC;305}306}307308ee = ath_hal_malloc(sizeof(HAL_EEPROM_v4k));309if (ee == AH_NULL) {310/* XXX message */311return HAL_ENOMEM;312}313314eep_data = (uint16_t *)&ee->ee_base;315for (w = 0; w < NW(struct ar5416eeprom_4k); w++) {316off = owl_eep_start_loc + w; /* NB: AP71 starts at 0 */317if (!ath_hal_eepromRead(ah, off, &eep_data[w])) {318HALDEBUG(ah, HAL_DEBUG_ANY,319"%s eeprom read error at offset 0x%x\n",320__func__, off);321return HAL_EEREAD;322}323}324/* Convert to eeprom native eeprom endian format */325/*326* XXX this is likely incorrect but will do for now327* XXX to get embedded boards working.328*/329if (ah->ah_eepromdata == NULL && isBigEndian()) {330for (w = 0; w < NW(struct ar5416eeprom_4k); w++)331eep_data[w] = __bswap16(eep_data[w]);332}333334/*335* At this point, we're in the native eeprom endian format336* Now, determine the eeprom endian by looking at byte 26??337*/338need_swap = ((ee->ee_base.baseEepHeader.eepMisc & AR5416_EEPMISC_BIG_ENDIAN) != 0) ^ isBigEndian();339if (need_swap) {340HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,341"Byte swap EEPROM contents.\n");342len = __bswap16(ee->ee_base.baseEepHeader.length);343} else {344len = ee->ee_base.baseEepHeader.length;345}346len = AH_MIN(len, sizeof(struct ar5416eeprom_4k)) / sizeof(uint16_t);347348/* Apply the checksum, done in native eeprom format */349/* XXX - Need to check to make sure checksum calculation is done350* in the correct endian format. Right now, it seems it would351* cast the raw data to host format and do the calculation, which may352* not be correct as the calculation may need to be done in the native353* eeprom format354*/355sum = 0;356for (w = 0; w < len; w++) {357sum ^= eep_data[w];358}359/* Check CRC - Attach should fail on a bad checksum */360if (sum != 0xffff) {361HALDEBUG(ah, HAL_DEBUG_ANY,362"Bad EEPROM checksum 0x%x (Len=%u)\n", sum, len);363return HAL_EEBADSUM;364}365366if (need_swap)367eepromSwap(&ee->ee_base); /* byte swap multi-byte data */368369/* swap words 0+2 so version is at the front */370magic = eep_data[0];371eep_data[0] = eep_data[2];372eep_data[2] = magic;373374HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,375"%s Eeprom Version %u.%u\n", __func__,376owl_get_eep_ver(ee), owl_get_eep_rev(ee));377378/* NB: must be after all byte swapping */379if (owl_get_eep_ver(ee) != AR5416_EEP_VER) {380HALDEBUG(ah, HAL_DEBUG_ANY,381"Bad EEPROM version 0x%x\n", owl_get_eep_ver(ee));382return HAL_EEBADSUM;383}384385v4kEepromReadCTLInfo(ah, ee); /* Get CTLs */386387AH_PRIVATE(ah)->ah_eeprom = ee;388AH_PRIVATE(ah)->ah_eeversion = ee->ee_base.baseEepHeader.version;389AH_PRIVATE(ah)->ah_eepromDetach = v4kEepromDetach;390AH_PRIVATE(ah)->ah_eepromGet = v4kEepromGet;391AH_PRIVATE(ah)->ah_eepromSet = v4kEepromSet;392AH_PRIVATE(ah)->ah_getSpurChan = v4kEepromGetSpurChan;393AH_PRIVATE(ah)->ah_eepromDiag = v4kEepromDiag;394return HAL_OK;395#undef NW396}397398399