1ab3e2cf4SAugustin Cavalier/*-
2ab3e2cf4SAugustin Cavalier * SPDX-License-Identifier: ISC
3ab3e2cf4SAugustin Cavalier *
4783c4cd1SColin Günther * Copyright (c) 2008 Sam Leffler, Errno Consulting
5783c4cd1SColin Günther * Copyright (c) 2008 Atheros Communications, Inc.
6783c4cd1SColin Günther *
7783c4cd1SColin Günther * Permission to use, copy, modify, and/or distribute this software for any
8783c4cd1SColin Günther * purpose with or without fee is hereby granted, provided that the above
9783c4cd1SColin Günther * copyright notice and this permission notice appear in all copies.
10783c4cd1SColin Günther *
11783c4cd1SColin Günther * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12783c4cd1SColin Günther * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13783c4cd1SColin Günther * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14783c4cd1SColin Günther * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15783c4cd1SColin Günther * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16783c4cd1SColin Günther * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17783c4cd1SColin Günther * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18783c4cd1SColin Günther *
19ab3e2cf4SAugustin Cavalier * $FreeBSD: releng/12.0/sys/dev/ath/ath_hal/ah_eeprom_v14.c 326695 2017-12-08 15:57:29Z pfg $
20783c4cd1SColin Günther */
21783c4cd1SColin Günther#include "opt_ah.h"
22783c4cd1SColin Günther
23783c4cd1SColin Günther#include "ah.h"
24783c4cd1SColin Günther#include "ah_internal.h"
25783c4cd1SColin Günther#include "ah_eeprom_v14.h"
26783c4cd1SColin Günther
27783c4cd1SColin Güntherstatic HAL_STATUS
28783c4cd1SColin Güntherv14EepromGet(struct ath_hal *ah, int param, void *val)
29783c4cd1SColin Günther{
30783c4cd1SColin Günther#define	CHAN_A_IDX	0
31783c4cd1SColin Günther#define	CHAN_B_IDX	1
32783c4cd1SColin Günther#define	IS_VERS(op, v)	((pBase->version & AR5416_EEP_VER_MINOR_MASK) op (v))
33783c4cd1SColin Günther	HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;
34783c4cd1SColin Günther	const MODAL_EEP_HEADER *pModal = ee->ee_base.modalHeader;
35783c4cd1SColin Günther	const BASE_EEP_HEADER  *pBase  = &ee->ee_base.baseEepHeader;
36783c4cd1SColin Günther	uint32_t sum;
37783c4cd1SColin Günther	uint8_t *macaddr;
38783c4cd1SColin Günther	int i;
39783c4cd1SColin Günther
40783c4cd1SColin Günther	switch (param) {
41783c4cd1SColin Günther        case AR_EEP_NFTHRESH_5:
42783c4cd1SColin Günther		*(int16_t *)val = pModal[0].noiseFloorThreshCh[0];
43783c4cd1SColin Günther		return HAL_OK;
44783c4cd1SColin Günther        case AR_EEP_NFTHRESH_2:
45783c4cd1SColin Günther		*(int16_t *)val = pModal[1].noiseFloorThreshCh[0];
46783c4cd1SColin Günther		return HAL_OK;
47783c4cd1SColin Günther        case AR_EEP_MACADDR:		/* Get MAC Address */
48783c4cd1SColin Günther		sum = 0;
49783c4cd1SColin Günther		macaddr = val;
50783c4cd1SColin Günther		for (i = 0; i < 6; i++) {
51783c4cd1SColin Günther			macaddr[i] = pBase->macAddr[i];
52783c4cd1SColin Günther			sum += pBase->macAddr[i];
53783c4cd1SColin Günther		}
54783c4cd1SColin Günther		if (sum == 0 || sum == 0xffff*3) {
55783c4cd1SColin Günther			HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad mac address %s\n",
56783c4cd1SColin Günther			    __func__, ath_hal_ether_sprintf(macaddr));
57783c4cd1SColin Günther			return HAL_EEBADMAC;
58783c4cd1SColin Günther		}
59783c4cd1SColin Günther		return HAL_OK;
60783c4cd1SColin Günther        case AR_EEP_REGDMN_0:
61783c4cd1SColin Günther		return pBase->regDmn[0];
62783c4cd1SColin Günther        case AR_EEP_REGDMN_1:
63783c4cd1SColin Günther		return pBase->regDmn[1];
64783c4cd1SColin Günther        case AR_EEP_OPCAP:
65783c4cd1SColin Günther		return pBase->deviceCap;
66783c4cd1SColin Günther        case AR_EEP_OPMODE:
67783c4cd1SColin Günther		return pBase->opCapFlags;
68783c4cd1SColin Günther        case AR_EEP_RFSILENT:
69783c4cd1SColin Günther		return pBase->rfSilent;
70783c4cd1SColin Günther	case AR_EEP_OB_5:
71783c4cd1SColin Günther		return pModal[CHAN_A_IDX].ob;
72783c4cd1SColin Günther    	case AR_EEP_DB_5:
73783c4cd1SColin Günther		return pModal[CHAN_A_IDX].db;
74783c4cd1SColin Günther    	case AR_EEP_OB_2:
75783c4cd1SColin Günther		return pModal[CHAN_B_IDX].ob;
76783c4cd1SColin Günther    	case AR_EEP_DB_2:
77783c4cd1SColin Günther		return pModal[CHAN_B_IDX].db;
78783c4cd1SColin Günther	case AR_EEP_TXMASK:
79783c4cd1SColin Günther		return pBase->txMask;
80783c4cd1SColin Günther	case AR_EEP_RXMASK:
81783c4cd1SColin Günther		return pBase->rxMask;
82783c4cd1SColin Günther	case AR_EEP_RXGAIN_TYPE:
83783c4cd1SColin Günther		return IS_VERS(>=, AR5416_EEP_MINOR_VER_17) ?
84783c4cd1SColin Günther		    pBase->rxGainType : AR5416_EEP_RXGAIN_ORIG;
85783c4cd1SColin Günther	case AR_EEP_TXGAIN_TYPE:
86783c4cd1SColin Günther		return IS_VERS(>=, AR5416_EEP_MINOR_VER_19) ?
87783c4cd1SColin Günther		    pBase->txGainType : AR5416_EEP_TXGAIN_ORIG;
88783c4cd1SColin Günther	case AR_EEP_FSTCLK_5G:
8974bf05c2SJérôme Duval		/* 5ghz fastclock is always enabled for Merlin minor <= 16 */
9074bf05c2SJérôme Duval		if (IS_VERS(<=, AR5416_EEP_MINOR_VER_16))
9174bf05c2SJérôme Duval			return HAL_OK;
9274bf05c2SJérôme Duval		return pBase->fastClk5g ? HAL_OK : HAL_EIO;
93783c4cd1SColin Günther	case AR_EEP_OL_PWRCTRL:
94783c4cd1SColin Günther		HALASSERT(val == AH_NULL);
95783c4cd1SColin Günther		return pBase->openLoopPwrCntl ?  HAL_OK : HAL_EIO;
9674bf05c2SJérôme Duval	case AR_EEP_DAC_HPWR_5G:
9774bf05c2SJérôme Duval		if (IS_VERS(>=, AR5416_EEP_MINOR_VER_20)) {
9874bf05c2SJérôme Duval			*(uint8_t *) val = pBase->dacHiPwrMode_5G;
9974bf05c2SJérôme Duval			return HAL_OK;
10074bf05c2SJérôme Duval		} else
10174bf05c2SJérôme Duval			return HAL_EIO;
10274bf05c2SJérôme Duval	case AR_EEP_FRAC_N_5G:
10374bf05c2SJérôme Duval		if (IS_VERS(>=, AR5416_EEP_MINOR_VER_22)) {
10474bf05c2SJérôme Duval			*(uint8_t *) val = pBase->frac_n_5g;
10574bf05c2SJérôme Duval		} else
10674bf05c2SJérôme Duval			*(uint8_t *) val = 0;
10774bf05c2SJérôme Duval		return HAL_OK;
108783c4cd1SColin Günther	case AR_EEP_AMODE:
109783c4cd1SColin Günther		HALASSERT(val == AH_NULL);
110783c4cd1SColin Günther		return pBase->opCapFlags & AR5416_OPFLAGS_11A ?
111783c4cd1SColin Günther		    HAL_OK : HAL_EIO;
112783c4cd1SColin Günther	case AR_EEP_BMODE:
113783c4cd1SColin Günther	case AR_EEP_GMODE:
114783c4cd1SColin Günther		HALASSERT(val == AH_NULL);
115783c4cd1SColin Günther		return pBase->opCapFlags & AR5416_OPFLAGS_11G ?
116783c4cd1SColin Günther		    HAL_OK : HAL_EIO;
117783c4cd1SColin Günther	case AR_EEP_32KHZCRYSTAL:
118783c4cd1SColin Günther	case AR_EEP_COMPRESS:
119783c4cd1SColin Günther	case AR_EEP_FASTFRAME:		/* XXX policy decision, h/w can do it */
120783c4cd1SColin Günther	case AR_EEP_WRITEPROTECT:	/* NB: no write protect bit */
121783c4cd1SColin Günther		HALASSERT(val == AH_NULL);
122783c4cd1SColin Günther		/* fall thru... */
123783c4cd1SColin Günther	case AR_EEP_MAXQCU:		/* NB: not in opCapFlags */
124783c4cd1SColin Günther	case AR_EEP_KCENTRIES:		/* NB: not in opCapFlags */
125783c4cd1SColin Günther		return HAL_EIO;
126783c4cd1SColin Günther	case AR_EEP_AES:
127783c4cd1SColin Günther	case AR_EEP_BURST:
128783c4cd1SColin Günther        case AR_EEP_RFKILL:
129783c4cd1SColin Günther	case AR_EEP_TURBO5DISABLE:
130783c4cd1SColin Günther	case AR_EEP_TURBO2DISABLE:
131783c4cd1SColin Günther		HALASSERT(val == AH_NULL);
132783c4cd1SColin Günther		return HAL_OK;
133783c4cd1SColin Günther	case AR_EEP_ANTGAINMAX_2:
134783c4cd1SColin Günther		*(int8_t *) val = ee->ee_antennaGainMax[1];
135783c4cd1SColin Günther		return HAL_OK;
136783c4cd1SColin Günther	case AR_EEP_ANTGAINMAX_5:
137783c4cd1SColin Günther		*(int8_t *) val = ee->ee_antennaGainMax[0];
138783c4cd1SColin Günther		return HAL_OK;
13974bf05c2SJérôme Duval	case AR_EEP_PWR_TABLE_OFFSET:
14074bf05c2SJérôme Duval		if (IS_VERS(>=, AR5416_EEP_MINOR_VER_21))
14174bf05c2SJérôme Duval			*(int8_t *) val = pBase->pwr_table_offset;
14274bf05c2SJérôme Duval		else
14374bf05c2SJérôme Duval			*(int8_t *) val = AR5416_PWR_TABLE_OFFSET_DB;
14474bf05c2SJérôme Duval		return HAL_OK;
14574bf05c2SJérôme Duval	case AR_EEP_PWDCLKIND:
14674bf05c2SJérôme Duval		if (IS_VERS(>=, AR5416_EEP_MINOR_VER_10)) {
14774bf05c2SJérôme Duval			*(uint8_t *) val = pBase->pwdclkind;
14874bf05c2SJérôme Duval			return HAL_OK;
14974bf05c2SJérôme Duval		}
15074bf05c2SJérôme Duval		return HAL_EIO;
15174bf05c2SJérôme Duval
152783c4cd1SColin Günther        default:
153783c4cd1SColin Günther		HALASSERT(0);
154783c4cd1SColin Günther		return HAL_EINVAL;
155783c4cd1SColin Günther	}
156783c4cd1SColin Günther#undef IS_VERS
157783c4cd1SColin Günther#undef CHAN_A_IDX
158783c4cd1SColin Günther#undef CHAN_B_IDX
159783c4cd1SColin Günther}
160783c4cd1SColin Günther
16174bf05c2SJérôme Duvalstatic HAL_STATUS
162783c4cd1SColin Güntherv14EepromSet(struct ath_hal *ah, int param, int v)
163783c4cd1SColin Günther{
164783c4cd1SColin Günther	HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;
165783c4cd1SColin Günther
166783c4cd1SColin Günther	switch (param) {
167783c4cd1SColin Günther	case AR_EEP_ANTGAINMAX_2:
168783c4cd1SColin Günther		ee->ee_antennaGainMax[1] = (int8_t) v;
169783c4cd1SColin Günther		return HAL_OK;
170783c4cd1SColin Günther	case AR_EEP_ANTGAINMAX_5:
171783c4cd1SColin Günther		ee->ee_antennaGainMax[0] = (int8_t) v;
172783c4cd1SColin Günther		return HAL_OK;
173783c4cd1SColin Günther	}
174783c4cd1SColin Günther	return HAL_EINVAL;
175783c4cd1SColin Günther}
176783c4cd1SColin Günther
177783c4cd1SColin Güntherstatic HAL_BOOL
178783c4cd1SColin Güntherv14EepromDiag(struct ath_hal *ah, int request,
179783c4cd1SColin Günther     const void *args, uint32_t argsize, void **result, uint32_t *resultsize)
180783c4cd1SColin Günther{
181783c4cd1SColin Günther	HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;
182783c4cd1SColin Günther
183783c4cd1SColin Günther	switch (request) {
184783c4cd1SColin Günther	case HAL_DIAG_EEPROM:
18574bf05c2SJérôme Duval		*result = ee;
18674bf05c2SJérôme Duval		*resultsize = sizeof(HAL_EEPROM_v14);
187783c4cd1SColin Günther		return AH_TRUE;
188783c4cd1SColin Günther	}
189783c4cd1SColin Günther	return AH_FALSE;
190783c4cd1SColin Günther}
191783c4cd1SColin Günther
192783c4cd1SColin Günther/* Do structure specific swaps if Eeprom format is non native to host */
193783c4cd1SColin Güntherstatic void
194783c4cd1SColin GünthereepromSwap(struct ar5416eeprom *ee)
195783c4cd1SColin Günther{
196783c4cd1SColin Günther	uint32_t integer, i, j;
197783c4cd1SColin Günther	uint16_t word;
198783c4cd1SColin Günther	MODAL_EEP_HEADER *pModal;
199783c4cd1SColin Günther
200783c4cd1SColin Günther	/* convert Base Eep header */
201783c4cd1SColin Günther	word = __bswap16(ee->baseEepHeader.length);
202783c4cd1SColin Günther	ee->baseEepHeader.length = word;
203783c4cd1SColin Günther
204783c4cd1SColin Günther	word = __bswap16(ee->baseEepHeader.checksum);
205783c4cd1SColin Günther	ee->baseEepHeader.checksum = word;
206783c4cd1SColin Günther
207783c4cd1SColin Günther	word = __bswap16(ee->baseEepHeader.version);
208783c4cd1SColin Günther	ee->baseEepHeader.version = word;
209783c4cd1SColin Günther
210783c4cd1SColin Günther	word = __bswap16(ee->baseEepHeader.regDmn[0]);
211783c4cd1SColin Günther	ee->baseEepHeader.regDmn[0] = word;
212783c4cd1SColin Günther
213783c4cd1SColin Günther	word = __bswap16(ee->baseEepHeader.regDmn[1]);
214783c4cd1SColin Günther	ee->baseEepHeader.regDmn[1] = word;
215783c4cd1SColin Günther
216783c4cd1SColin Günther	word = __bswap16(ee->baseEepHeader.rfSilent);
217783c4cd1SColin Günther	ee->baseEepHeader.rfSilent = word;
218783c4cd1SColin Günther
219783c4cd1SColin Günther	word = __bswap16(ee->baseEepHeader.blueToothOptions);
220783c4cd1SColin Günther	ee->baseEepHeader.blueToothOptions = word;
221783c4cd1SColin Günther
222783c4cd1SColin Günther	word = __bswap16(ee->baseEepHeader.deviceCap);
223783c4cd1SColin Günther	ee->baseEepHeader.deviceCap = word;
224783c4cd1SColin Günther
225783c4cd1SColin Günther	/* convert Modal Eep header */
226783c4cd1SColin Günther	for (j = 0; j < 2; j++) {
227783c4cd1SColin Günther		pModal = &ee->modalHeader[j];
228783c4cd1SColin Günther
229783c4cd1SColin Günther		/* XXX linux/ah_osdep.h only defines __bswap32 for BE */
230783c4cd1SColin Günther		integer = __bswap32(pModal->antCtrlCommon);
231783c4cd1SColin Günther		pModal->antCtrlCommon = integer;
232783c4cd1SColin Günther
233783c4cd1SColin Günther		for (i = 0; i < AR5416_MAX_CHAINS; i++) {
234783c4cd1SColin Günther			integer = __bswap32(pModal->antCtrlChain[i]);
235783c4cd1SColin Günther			pModal->antCtrlChain[i] = integer;
236783c4cd1SColin Günther		}
23774bf05c2SJérôme Duval		for (i = 0; i < 3; i++) {
23874bf05c2SJérôme Duval			word = __bswap16(pModal->xpaBiasLvlFreq[i]);
23974bf05c2SJérôme Duval			pModal->xpaBiasLvlFreq[i] = word;
24074bf05c2SJérôme Duval		}
241783c4cd1SColin Günther		for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) {
242783c4cd1SColin Günther			word = __bswap16(pModal->spurChans[i].spurChan);
243783c4cd1SColin Günther			pModal->spurChans[i].spurChan = word;
244783c4cd1SColin Günther		}
245783c4cd1SColin Günther	}
246783c4cd1SColin Günther}
247783c4cd1SColin Günther
248783c4cd1SColin Güntherstatic uint16_t
249783c4cd1SColin Güntherv14EepromGetSpurChan(struct ath_hal *ah, int ix, HAL_BOOL is2GHz)
250783c4cd1SColin Günther{
251783c4cd1SColin Günther	HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;
252783c4cd1SColin Günther
253783c4cd1SColin Günther	HALASSERT(0 <= ix && ix <  AR5416_EEPROM_MODAL_SPURS);
254783c4cd1SColin Günther	return ee->ee_base.modalHeader[is2GHz].spurChans[ix].spurChan;
255783c4cd1SColin Günther}
256783c4cd1SColin Günther
257783c4cd1SColin Günther/**************************************************************************
258783c4cd1SColin Günther * fbin2freq
259783c4cd1SColin Günther *
260783c4cd1SColin Günther * Get channel value from binary representation held in eeprom
261783c4cd1SColin Günther * RETURNS: the frequency in MHz
262783c4cd1SColin Günther */
263783c4cd1SColin Güntherstatic uint16_t
264783c4cd1SColin Güntherfbin2freq(uint8_t fbin, HAL_BOOL is2GHz)
265783c4cd1SColin Günther{
266783c4cd1SColin Günther	/*
267783c4cd1SColin Günther	 * Reserved value 0xFF provides an empty definition both as
268783c4cd1SColin Günther	 * an fbin and as a frequency - do not convert
269783c4cd1SColin Günther	 */
270783c4cd1SColin Günther	if (fbin == AR5416_BCHAN_UNUSED)
271783c4cd1SColin Günther		return fbin;
272783c4cd1SColin Günther	return (uint16_t)((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin));
273783c4cd1SColin Günther}
274783c4cd1SColin Günther
275783c4cd1SColin Günther/*
276783c4cd1SColin Günther * Copy EEPROM Conformance Testing Limits contents
277783c4cd1SColin Günther * into the allocated space
278783c4cd1SColin Günther */
279783c4cd1SColin Günther/* USE CTLS from chain zero */
280783c4cd1SColin Günther#define CTL_CHAIN	0
281783c4cd1SColin Günther
282783c4cd1SColin Güntherstatic void
283783c4cd1SColin Güntherv14EepromReadCTLInfo(struct ath_hal *ah, HAL_EEPROM_v14 *ee)
284783c4cd1SColin Günther{
285783c4cd1SColin Günther	RD_EDGES_POWER *rep = ee->ee_rdEdgesPower;
286783c4cd1SColin Günther	int i, j;
287783c4cd1SColin Günther
288783c4cd1SColin Günther	HALASSERT(AR5416_NUM_CTLS <= sizeof(ee->ee_rdEdgesPower)/NUM_EDGES);
289783c4cd1SColin Günther
290783c4cd1SColin Günther	for (i = 0; ee->ee_base.ctlIndex[i] != 0 && i < AR5416_NUM_CTLS; i++) {
291783c4cd1SColin Günther		for (j = 0; j < NUM_EDGES; j ++) {
292783c4cd1SColin Günther			/* XXX Confirm this is the right thing to do when an invalid channel is stored */
293783c4cd1SColin Günther			if (ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].bChannel == AR5416_BCHAN_UNUSED) {
294783c4cd1SColin Günther				rep[j].rdEdge = 0;
295783c4cd1SColin Günther				rep[j].twice_rdEdgePower = 0;
296783c4cd1SColin Günther				rep[j].flag = 0;
297783c4cd1SColin Günther			} else {
298783c4cd1SColin Günther				rep[j].rdEdge = fbin2freq(
299783c4cd1SColin Günther				    ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].bChannel,
300783c4cd1SColin Günther				    (ee->ee_base.ctlIndex[i] & CTL_MODE_M) != CTL_11A);
301783c4cd1SColin Günther				rep[j].twice_rdEdgePower = MS(ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].tPowerFlag, CAL_CTL_EDGES_POWER);
302783c4cd1SColin Günther				rep[j].flag = MS(ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].tPowerFlag, CAL_CTL_EDGES_FLAG) != 0;
303783c4cd1SColin Günther			}
304783c4cd1SColin Günther		}
305783c4cd1SColin Günther		rep += NUM_EDGES;
306783c4cd1SColin Günther	}
307783c4cd1SColin Günther	ee->ee_numCtls = i;
308783c4cd1SColin Günther	HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,
309783c4cd1SColin Günther	    "%s Numctls = %u\n",__func__,i);
310783c4cd1SColin Günther}
311783c4cd1SColin Günther
312783c4cd1SColin Günther/*
313783c4cd1SColin Günther * Reclaim any EEPROM-related storage.
314783c4cd1SColin Günther */
315783c4cd1SColin Güntherstatic void
316783c4cd1SColin Güntherv14EepromDetach(struct ath_hal *ah)
317783c4cd1SColin Günther{
318783c4cd1SColin Günther	HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;
319783c4cd1SColin Günther
320783c4cd1SColin Günther	ath_hal_free(ee);
321783c4cd1SColin Günther	AH_PRIVATE(ah)->ah_eeprom = AH_NULL;
322783c4cd1SColin Günther}
323783c4cd1SColin Günther
324783c4cd1SColin Günther#define owl_get_eep_ver(_ee)   \
325783c4cd1SColin Günther    (((_ee)->ee_base.baseEepHeader.version >> 12) & 0xF)
326783c4cd1SColin Günther#define owl_get_eep_rev(_ee)   \
327783c4cd1SColin Günther    (((_ee)->ee_base.baseEepHeader.version) & 0xFFF)
328783c4cd1SColin Günther
32974bf05c2SJérôme Duval/*
33074bf05c2SJérôme Duval * Howl is (hopefully) a special case where the endian-ness of the EEPROM
33174bf05c2SJérôme Duval * matches the native endian-ness; and that supplied EEPROMs don't have
33274bf05c2SJérôme Duval * a magic value to check.
33374bf05c2SJérôme Duval */
334783c4cd1SColin GüntherHAL_STATUS
335783c4cd1SColin Güntherath_hal_v14EepromAttach(struct ath_hal *ah)
336783c4cd1SColin Günther{
337783c4cd1SColin Günther#define	NW(a)	(sizeof(a) / sizeof(uint16_t))
338783c4cd1SColin Günther	HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom;
339783c4cd1SColin Günther	uint16_t *eep_data, magic;
340783c4cd1SColin Günther	HAL_BOOL need_swap;
341783c4cd1SColin Günther	u_int w, off, len;
342783c4cd1SColin Günther	uint32_t sum;
343783c4cd1SColin Günther
344783c4cd1SColin Günther	HALASSERT(ee == AH_NULL);
345783c4cd1SColin Günther
34674bf05c2SJérôme Duval	/*
34774bf05c2SJérôme Duval	 * Don't check magic if we're supplied with an EEPROM block,
34874bf05c2SJérôme Duval	 * typically this is from Howl but it may also be from later
34974bf05c2SJérôme Duval	 * boards w/ an embedded Merlin.
35074bf05c2SJérôme Duval	 */
35174bf05c2SJérôme Duval	if (ah->ah_eepromdata == NULL) {
35274bf05c2SJérôme Duval		if (!ath_hal_eepromRead(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
35374bf05c2SJérôme Duval			HALDEBUG(ah, HAL_DEBUG_ANY,
35474bf05c2SJérôme Duval			    "%s Error reading Eeprom MAGIC\n", __func__);
35574bf05c2SJérôme Duval			return HAL_EEREAD;
35674bf05c2SJérôme Duval		}
35774bf05c2SJérôme Duval		HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s Eeprom Magic = 0x%x\n",
35874bf05c2SJérôme Duval		    __func__, magic);
35974bf05c2SJérôme Duval		if (magic != AR5416_EEPROM_MAGIC) {
36074bf05c2SJérôme Duval			HALDEBUG(ah, HAL_DEBUG_ANY, "Bad magic number\n");
36174bf05c2SJérôme Duval			return HAL_EEMAGIC;
36274bf05c2SJérôme Duval		}
363783c4cd1SColin Günther	}
364783c4cd1SColin Günther
365783c4cd1SColin Günther	ee = ath_hal_malloc(sizeof(HAL_EEPROM_v14));
366783c4cd1SColin Günther	if (ee == AH_NULL) {
367783c4cd1SColin Günther		/* XXX message */
368783c4cd1SColin Günther		return HAL_ENOMEM;
369783c4cd1SColin Günther	}
370783c4cd1SColin Günther
371783c4cd1SColin Günther	eep_data = (uint16_t *)&ee->ee_base;
372783c4cd1SColin Günther	for (w = 0; w < NW(struct ar5416eeprom); w++) {
373783c4cd1SColin Günther		off = owl_eep_start_loc + w;	/* NB: AP71 starts at 0 */
374783c4cd1SColin Günther		if (!ath_hal_eepromRead(ah, off, &eep_data[w])) {
375783c4cd1SColin Günther			HALDEBUG(ah, HAL_DEBUG_ANY,
376783c4cd1SColin Günther			    "%s eeprom read error at offset 0x%x\n",
377783c4cd1SColin Günther			    __func__, off);
378783c4cd1SColin Günther			return HAL_EEREAD;
379783c4cd1SColin Günther		}
380783c4cd1SColin Günther	}
381783c4cd1SColin Günther	/* Convert to eeprom native eeprom endian format */
38274bf05c2SJérôme Duval	/* XXX this is likely incorrect but will do for now to get howl/ap83 working. */
38374bf05c2SJérôme Duval	if (ah->ah_eepromdata == NULL && isBigEndian()) {
384783c4cd1SColin Günther		for (w = 0; w < NW(struct ar5416eeprom); w++)
385783c4cd1SColin Günther			eep_data[w] = __bswap16(eep_data[w]);
386783c4cd1SColin Günther	}
387783c4cd1SColin Günther
388783c4cd1SColin Günther	/*
389783c4cd1SColin Günther	 * At this point, we're in the native eeprom endian format
390783c4cd1SColin Günther	 * Now, determine the eeprom endian by looking at byte 26??
391783c4cd1SColin Günther	 */
392783c4cd1SColin Günther	need_swap = ((ee->ee_base.baseEepHeader.eepMisc & AR5416_EEPMISC_BIG_ENDIAN) != 0) ^ isBigEndian();
393783c4cd1SColin Günther	if (need_swap) {
394783c4cd1SColin Günther		HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,
395783c4cd1SColin Günther		    "Byte swap EEPROM contents.\n");
396783c4cd1SColin Günther		len = __bswap16(ee->ee_base.baseEepHeader.length);
397783c4cd1SColin Günther	} else {
398783c4cd1SColin Günther		len = ee->ee_base.baseEepHeader.length;
399783c4cd1SColin Günther	}
400783c4cd1SColin Günther	len = AH_MIN(len, sizeof(struct ar5416eeprom)) / sizeof(uint16_t);
401783c4cd1SColin Günther
402783c4cd1SColin Günther	/* Apply the checksum, done in native eeprom format */
403783c4cd1SColin Günther	/* XXX - Need to check to make sure checksum calculation is done
404783c4cd1SColin Günther	 * in the correct endian format.  Right now, it seems it would
405783c4cd1SColin Günther	 * cast the raw data to host format and do the calculation, which may
406783c4cd1SColin Günther	 * not be correct as the calculation may need to be done in the native
407783c4cd1SColin Günther	 * eeprom format
408783c4cd1SColin Günther	 */
409783c4cd1SColin Günther	sum = 0;
410783c4cd1SColin Günther	for (w = 0; w < len; w++)
411783c4cd1SColin Günther		sum ^= eep_data[w];
412783c4cd1SColin Günther	/* Check CRC - Attach should fail on a bad checksum */
413783c4cd1SColin Günther	if (sum != 0xffff) {
414783c4cd1SColin Günther		HALDEBUG(ah, HAL_DEBUG_ANY,
415783c4cd1SColin Günther		    "Bad EEPROM checksum 0x%x (Len=%u)\n", sum, len);
416783c4cd1SColin Günther		return HAL_EEBADSUM;
417783c4cd1SColin Günther	}
418783c4cd1SColin Günther
419783c4cd1SColin Günther	if (need_swap)
420783c4cd1SColin Günther		eepromSwap(&ee->ee_base);	/* byte swap multi-byte data */
421783c4cd1SColin Günther
422783c4cd1SColin Günther	/* swap words 0+2 so version is at the front */
423783c4cd1SColin Günther	magic = eep_data[0];
424783c4cd1SColin Günther	eep_data[0] = eep_data[2];
425783c4cd1SColin Günther	eep_data[2] = magic;
426783c4cd1SColin Günther
427783c4cd1SColin Günther	HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,
428783c4cd1SColin Günther	    "%s Eeprom Version %u.%u\n", __func__,
429783c4cd1SColin Günther	    owl_get_eep_ver(ee), owl_get_eep_rev(ee));
430783c4cd1SColin Günther
431783c4cd1SColin Günther	/* NB: must be after all byte swapping */
432783c4cd1SColin Günther	if (owl_get_eep_ver(ee) != AR5416_EEP_VER) {
433783c4cd1SColin Günther		HALDEBUG(ah, HAL_DEBUG_ANY,
434783c4cd1SColin Günther		    "Bad EEPROM version 0x%x\n", owl_get_eep_ver(ee));
435783c4cd1SColin Günther		return HAL_EEBADSUM;
436783c4cd1SColin Günther	}
437783c4cd1SColin Günther
438783c4cd1SColin Günther	v14EepromReadCTLInfo(ah, ee);		/* Get CTLs */
439783c4cd1SColin Günther
440783c4cd1SColin Günther	AH_PRIVATE(ah)->ah_eeprom = ee;
441783c4cd1SColin Günther	AH_PRIVATE(ah)->ah_eeversion = ee->ee_base.baseEepHeader.version;
442783c4cd1SColin Günther	AH_PRIVATE(ah)->ah_eepromDetach = v14EepromDetach;
443783c4cd1SColin Günther	AH_PRIVATE(ah)->ah_eepromGet = v14EepromGet;
444783c4cd1SColin Günther	AH_PRIVATE(ah)->ah_eepromSet = v14EepromSet;
445783c4cd1SColin Günther	AH_PRIVATE(ah)->ah_getSpurChan = v14EepromGetSpurChan;
446783c4cd1SColin Günther	AH_PRIVATE(ah)->ah_eepromDiag = v14EepromDiag;
447783c4cd1SColin Günther	return HAL_OK;
448783c4cd1SColin Günther#undef NW
449783c4cd1SColin Günther}
450