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