1/*-
2 * SPDX-License-Identifier: ISC
3 *
4 * Copyright (c) 2009 Rui Paulo <rpaulo@FreeBSD.org>
5 * Copyright (c) 2008 Sam Leffler, Errno Consulting
6 * Copyright (c) 2008 Atheros Communications, Inc.
7 *
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *
20 * $FreeBSD: releng/12.0/sys/dev/ath/ath_hal/ah_eeprom_v4k.c 326695 2017-12-08 15:57:29Z pfg $
21 */
22#include "opt_ah.h"
23
24#include "ah.h"
25#include "ah_internal.h"
26#include "ah_eeprom_v14.h"
27#include "ah_eeprom_v4k.h"
28
29static HAL_STATUS
30v4kEepromGet(struct ath_hal *ah, int param, void *val)
31{
32#define	CHAN_A_IDX	0
33#define	CHAN_B_IDX	1
34#define	IS_VERS(op, v)	((pBase->version & AR5416_EEP_VER_MINOR_MASK) op (v))
35	HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
36	const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader;
37	const BASE_EEP4K_HEADER  *pBase  = &ee->ee_base.baseEepHeader;
38	uint32_t sum;
39	uint8_t *macaddr;
40	int i;
41
42	switch (param) {
43        case AR_EEP_NFTHRESH_2:
44		*(int16_t *)val = pModal->noiseFloorThreshCh[0];
45		return HAL_OK;
46        case AR_EEP_MACADDR:		/* Get MAC Address */
47		sum = 0;
48		macaddr = val;
49		for (i = 0; i < 6; i++) {
50			macaddr[i] = pBase->macAddr[i];
51			sum += pBase->macAddr[i];
52		}
53		if (sum == 0 || sum == 0xffff*3) {
54			HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad mac address %s\n",
55			    __func__, ath_hal_ether_sprintf(macaddr));
56			return HAL_EEBADMAC;
57		}
58		return HAL_OK;
59        case AR_EEP_REGDMN_0:
60		return pBase->regDmn[0];
61        case AR_EEP_REGDMN_1:
62		return pBase->regDmn[1];
63        case AR_EEP_OPCAP:
64		return pBase->deviceCap;
65        case AR_EEP_OPMODE:
66		return pBase->opCapFlags;
67        case AR_EEP_RFSILENT:
68		return pBase->rfSilent;
69    	case AR_EEP_OB_2:
70		return pModal->ob_0;
71    	case AR_EEP_DB_2:
72		return pModal->db1_1;
73	case AR_EEP_TXMASK:
74		return pBase->txMask;
75	case AR_EEP_RXMASK:
76		return pBase->rxMask;
77	case AR_EEP_RXGAIN_TYPE:
78		return AR5416_EEP_RXGAIN_ORIG;
79	case AR_EEP_TXGAIN_TYPE:
80		return pBase->txGainType;
81	case AR_EEP_OL_PWRCTRL:
82		HALASSERT(val == AH_NULL);
83		return HAL_EIO;
84	case AR_EEP_AMODE:
85		HALASSERT(val == AH_NULL);
86		return pBase->opCapFlags & AR5416_OPFLAGS_11A ?
87		    HAL_OK : HAL_EIO;
88	case AR_EEP_BMODE:
89	case AR_EEP_GMODE:
90		HALASSERT(val == AH_NULL);
91		return pBase->opCapFlags & AR5416_OPFLAGS_11G ?
92		    HAL_OK : HAL_EIO;
93	case AR_EEP_32KHZCRYSTAL:
94	case AR_EEP_COMPRESS:
95	case AR_EEP_FASTFRAME:		/* XXX policy decision, h/w can do it */
96	case AR_EEP_WRITEPROTECT:	/* NB: no write protect bit */
97		HALASSERT(val == AH_NULL);
98		/* fall thru... */
99	case AR_EEP_MAXQCU:		/* NB: not in opCapFlags */
100	case AR_EEP_KCENTRIES:		/* NB: not in opCapFlags */
101		return HAL_EIO;
102	case AR_EEP_AES:
103	case AR_EEP_BURST:
104        case AR_EEP_RFKILL:
105	case AR_EEP_TURBO2DISABLE:
106		HALASSERT(val == AH_NULL);
107		return HAL_OK;
108	case AR_EEP_ANTGAINMAX_2:
109		*(int8_t *) val = ee->ee_antennaGainMax;
110		return HAL_OK;
111        default:
112		HALASSERT(0);
113		return HAL_EINVAL;
114	}
115#undef IS_VERS
116#undef CHAN_A_IDX
117#undef CHAN_B_IDX
118}
119
120static HAL_STATUS
121v4kEepromSet(struct ath_hal *ah, int param, int v)
122{
123	HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
124
125	switch (param) {
126	case AR_EEP_ANTGAINMAX_2:
127		ee->ee_antennaGainMax = (int8_t) v;
128		return HAL_OK;
129	}
130	return HAL_EINVAL;
131}
132
133static HAL_BOOL
134v4kEepromDiag(struct ath_hal *ah, int request,
135     const void *args, uint32_t argsize, void **result, uint32_t *resultsize)
136{
137	HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
138
139	switch (request) {
140	case HAL_DIAG_EEPROM:
141		*result = ee;
142		*resultsize = sizeof(HAL_EEPROM_v4k);
143		return AH_TRUE;
144	}
145	return AH_FALSE;
146}
147
148/* Do structure specific swaps if Eeprom format is non native to host */
149static void
150eepromSwap(struct ar5416eeprom_4k *ee)
151{
152	uint32_t integer, i;
153	uint16_t word;
154	MODAL_EEP4K_HEADER *pModal;
155
156	/* convert Base Eep header */
157	word = __bswap16(ee->baseEepHeader.length);
158	ee->baseEepHeader.length = word;
159
160	word = __bswap16(ee->baseEepHeader.checksum);
161	ee->baseEepHeader.checksum = word;
162
163	word = __bswap16(ee->baseEepHeader.version);
164	ee->baseEepHeader.version = word;
165
166	word = __bswap16(ee->baseEepHeader.regDmn[0]);
167	ee->baseEepHeader.regDmn[0] = word;
168
169	word = __bswap16(ee->baseEepHeader.regDmn[1]);
170	ee->baseEepHeader.regDmn[1] = word;
171
172	word = __bswap16(ee->baseEepHeader.rfSilent);
173	ee->baseEepHeader.rfSilent = word;
174
175	word = __bswap16(ee->baseEepHeader.blueToothOptions);
176	ee->baseEepHeader.blueToothOptions = word;
177
178	word = __bswap16(ee->baseEepHeader.deviceCap);
179	ee->baseEepHeader.deviceCap = word;
180
181	/* convert Modal Eep header */
182	pModal = &ee->modalHeader;
183
184	/* XXX linux/ah_osdep.h only defines __bswap32 for BE */
185	integer = __bswap32(pModal->antCtrlCommon);
186	pModal->antCtrlCommon = integer;
187
188	for (i = 0; i < AR5416_4K_MAX_CHAINS; i++) {
189		integer = __bswap32(pModal->antCtrlChain[i]);
190		pModal->antCtrlChain[i] = integer;
191	}
192
193	for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) {
194		word = __bswap16(pModal->spurChans[i].spurChan);
195		pModal->spurChans[i].spurChan = word;
196	}
197}
198
199static uint16_t
200v4kEepromGetSpurChan(struct ath_hal *ah, int ix, HAL_BOOL is2GHz)
201{
202	HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
203
204	HALASSERT(0 <= ix && ix <  AR5416_EEPROM_MODAL_SPURS);
205	HALASSERT(is2GHz);
206	return ee->ee_base.modalHeader.spurChans[ix].spurChan;
207}
208
209/**************************************************************************
210 * fbin2freq
211 *
212 * Get channel value from binary representation held in eeprom
213 * RETURNS: the frequency in MHz
214 */
215static uint16_t
216fbin2freq(uint8_t fbin, HAL_BOOL is2GHz)
217{
218	/*
219	 * Reserved value 0xFF provides an empty definition both as
220	 * an fbin and as a frequency - do not convert
221	 */
222	if (fbin == AR5416_BCHAN_UNUSED)
223		return fbin;
224	return (uint16_t)((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin));
225}
226
227/*
228 * Copy EEPROM Conformance Testing Limits contents
229 * into the allocated space
230 */
231/* USE CTLS from chain zero */
232#define CTL_CHAIN	0
233
234static void
235v4kEepromReadCTLInfo(struct ath_hal *ah, HAL_EEPROM_v4k *ee)
236{
237	RD_EDGES_POWER *rep = ee->ee_rdEdgesPower;
238	int i, j;
239
240	HALASSERT(AR5416_4K_NUM_CTLS <= sizeof(ee->ee_rdEdgesPower)/NUM_EDGES);
241
242	for (i = 0; ee->ee_base.ctlIndex[i] != 0 && i < AR5416_4K_NUM_CTLS; i++) {
243		for (j = 0; j < NUM_EDGES; j ++) {
244			/* XXX Confirm this is the right thing to do when an invalid channel is stored */
245			if (ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].bChannel == AR5416_BCHAN_UNUSED) {
246				rep[j].rdEdge = 0;
247				rep[j].twice_rdEdgePower = 0;
248				rep[j].flag = 0;
249			} else {
250				rep[j].rdEdge = fbin2freq(
251				    ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].bChannel,
252				    (ee->ee_base.ctlIndex[i] & CTL_MODE_M) != CTL_11A);
253				rep[j].twice_rdEdgePower = MS(ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].tPowerFlag, CAL_CTL_EDGES_POWER);
254				rep[j].flag = MS(ee->ee_base.ctlData[i].ctlEdges[CTL_CHAIN][j].tPowerFlag, CAL_CTL_EDGES_FLAG) != 0;
255			}
256		}
257		rep += NUM_EDGES;
258	}
259	ee->ee_numCtls = i;
260	HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,
261	    "%s Numctls = %u\n",__func__,i);
262}
263
264/*
265 * Reclaim any EEPROM-related storage.
266 */
267static void
268v4kEepromDetach(struct ath_hal *ah)
269{
270	HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
271
272	ath_hal_free(ee);
273	AH_PRIVATE(ah)->ah_eeprom = AH_NULL;
274}
275
276#define owl_get_eep_ver(_ee)   \
277    (((_ee)->ee_base.baseEepHeader.version >> 12) & 0xF)
278#define owl_get_eep_rev(_ee)   \
279    (((_ee)->ee_base.baseEepHeader.version) & 0xFFF)
280
281HAL_STATUS
282ath_hal_v4kEepromAttach(struct ath_hal *ah)
283{
284#define	NW(a)	(sizeof(a) / sizeof(uint16_t))
285	HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
286	uint16_t *eep_data, magic;
287	HAL_BOOL need_swap;
288	u_int w, off, len;
289	uint32_t sum;
290
291	HALASSERT(ee == AH_NULL);
292	/*
293	 * Don't check magic if we're supplied with an EEPROM block,
294	 * typically this is from Howl but it may also be from later
295	 * boards w/ an embedded WMAC.
296	 */
297	if (ah->ah_eepromdata == NULL) {
298		if (!ath_hal_eepromRead(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) {
299			HALDEBUG(ah, HAL_DEBUG_ANY,
300			    "%s Error reading Eeprom MAGIC\n", __func__);
301			return HAL_EEREAD;
302		}
303		HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s Eeprom Magic = 0x%x\n",
304		    __func__, magic);
305		if (magic != AR5416_EEPROM_MAGIC) {
306			HALDEBUG(ah, HAL_DEBUG_ANY, "Bad magic number\n");
307			return HAL_EEMAGIC;
308		}
309	}
310
311	ee = ath_hal_malloc(sizeof(HAL_EEPROM_v4k));
312	if (ee == AH_NULL) {
313		/* XXX message */
314		return HAL_ENOMEM;
315	}
316
317	eep_data = (uint16_t *)&ee->ee_base;
318	for (w = 0; w < NW(struct ar5416eeprom_4k); w++) {
319		off = owl_eep_start_loc + w;	/* NB: AP71 starts at 0 */
320		if (!ath_hal_eepromRead(ah, off, &eep_data[w])) {
321			HALDEBUG(ah, HAL_DEBUG_ANY,
322			    "%s eeprom read error at offset 0x%x\n",
323			    __func__, off);
324			return HAL_EEREAD;
325		}
326	}
327	/* Convert to eeprom native eeprom endian format */
328	/*
329	 * XXX this is likely incorrect but will do for now
330	 * XXX to get embedded boards working.
331	 */
332	if (ah->ah_eepromdata == NULL && isBigEndian()) {
333		for (w = 0; w < NW(struct ar5416eeprom_4k); w++)
334			eep_data[w] = __bswap16(eep_data[w]);
335	}
336
337	/*
338	 * At this point, we're in the native eeprom endian format
339	 * Now, determine the eeprom endian by looking at byte 26??
340	 */
341	need_swap = ((ee->ee_base.baseEepHeader.eepMisc & AR5416_EEPMISC_BIG_ENDIAN) != 0) ^ isBigEndian();
342	if (need_swap) {
343		HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,
344		    "Byte swap EEPROM contents.\n");
345		len = __bswap16(ee->ee_base.baseEepHeader.length);
346	} else {
347		len = ee->ee_base.baseEepHeader.length;
348	}
349	len = AH_MIN(len, sizeof(struct ar5416eeprom_4k)) / sizeof(uint16_t);
350
351	/* Apply the checksum, done in native eeprom format */
352	/* XXX - Need to check to make sure checksum calculation is done
353	 * in the correct endian format.  Right now, it seems it would
354	 * cast the raw data to host format and do the calculation, which may
355	 * not be correct as the calculation may need to be done in the native
356	 * eeprom format
357	 */
358	sum = 0;
359	for (w = 0; w < len; w++) {
360		sum ^= eep_data[w];
361	}
362	/* Check CRC - Attach should fail on a bad checksum */
363	if (sum != 0xffff) {
364		HALDEBUG(ah, HAL_DEBUG_ANY,
365		    "Bad EEPROM checksum 0x%x (Len=%u)\n", sum, len);
366		return HAL_EEBADSUM;
367	}
368
369	if (need_swap)
370		eepromSwap(&ee->ee_base);	/* byte swap multi-byte data */
371
372	/* swap words 0+2 so version is at the front */
373	magic = eep_data[0];
374	eep_data[0] = eep_data[2];
375	eep_data[2] = magic;
376
377	HALDEBUG(ah, HAL_DEBUG_ATTACH | HAL_DEBUG_EEPROM,
378	    "%s Eeprom Version %u.%u\n", __func__,
379	    owl_get_eep_ver(ee), owl_get_eep_rev(ee));
380
381	/* NB: must be after all byte swapping */
382	if (owl_get_eep_ver(ee) != AR5416_EEP_VER) {
383		HALDEBUG(ah, HAL_DEBUG_ANY,
384		    "Bad EEPROM version 0x%x\n", owl_get_eep_ver(ee));
385		return HAL_EEBADSUM;
386	}
387
388	v4kEepromReadCTLInfo(ah, ee);		/* Get CTLs */
389
390	AH_PRIVATE(ah)->ah_eeprom = ee;
391	AH_PRIVATE(ah)->ah_eeversion = ee->ee_base.baseEepHeader.version;
392	AH_PRIVATE(ah)->ah_eepromDetach = v4kEepromDetach;
393	AH_PRIVATE(ah)->ah_eepromGet = v4kEepromGet;
394	AH_PRIVATE(ah)->ah_eepromSet = v4kEepromSet;
395	AH_PRIVATE(ah)->ah_getSpurChan = v4kEepromGetSpurChan;
396	AH_PRIVATE(ah)->ah_eepromDiag = v4kEepromDiag;
397	return HAL_OK;
398#undef NW
399}
400