ar5211_misc.c revision 185377
1/*
2 * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3 * Copyright (c) 2002-2006 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: ar5211_misc.c,v 1.6 2008/11/10 04:08:02 sam Exp $
18 */
19#include "opt_ah.h"
20
21#ifdef AH_SUPPORT_AR5211
22
23#include "ah.h"
24#include "ah_internal.h"
25
26#include "ar5211/ar5211.h"
27#include "ar5211/ar5211reg.h"
28#include "ar5211/ar5211phy.h"
29
30#include "ah_eeprom_v3.h"
31
32#define	AR_NUM_GPIO	6		/* 6 GPIO bits */
33#define	AR_GPIOD_MASK	0x2f		/* 6-bit mask */
34
35void
36ar5211GetMacAddress(struct ath_hal *ah, uint8_t *mac)
37{
38	struct ath_hal_5211 *ahp = AH5211(ah);
39
40	OS_MEMCPY(mac, ahp->ah_macaddr, IEEE80211_ADDR_LEN);
41}
42
43HAL_BOOL
44ar5211SetMacAddress(struct ath_hal *ah, const uint8_t *mac)
45{
46	struct ath_hal_5211 *ahp = AH5211(ah);
47
48	OS_MEMCPY(ahp->ah_macaddr, mac, IEEE80211_ADDR_LEN);
49	return AH_TRUE;
50}
51
52void
53ar5211GetBssIdMask(struct ath_hal *ah, uint8_t *mask)
54{
55	static const uint8_t ones[IEEE80211_ADDR_LEN] =
56		{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
57	OS_MEMCPY(mask, ones, IEEE80211_ADDR_LEN);
58}
59
60HAL_BOOL
61ar5211SetBssIdMask(struct ath_hal *ah, const uint8_t *mask)
62{
63	return AH_FALSE;
64}
65
66/*
67 * Read 16 bits of data from the specified EEPROM offset.
68 */
69HAL_BOOL
70ar5211EepromRead(struct ath_hal *ah, u_int off, uint16_t *data)
71{
72	OS_REG_WRITE(ah, AR_EEPROM_ADDR, off);
73	OS_REG_WRITE(ah, AR_EEPROM_CMD, AR_EEPROM_CMD_READ);
74
75	if (!ath_hal_wait(ah, AR_EEPROM_STS,
76	    AR_EEPROM_STS_READ_COMPLETE | AR_EEPROM_STS_READ_ERROR,
77	    AR_EEPROM_STS_READ_COMPLETE)) {
78		HALDEBUG(ah, HAL_DEBUG_ANY,
79		    "%s: read failed for entry 0x%x\n", __func__, off);
80		return AH_FALSE;
81	}
82	*data = OS_REG_READ(ah, AR_EEPROM_DATA) & 0xffff;
83	return AH_TRUE;
84}
85
86/*
87 * Return the wireless modes (a,b,g,t) supported by hardware.
88 *
89 * This value is what is actually supported by the hardware
90 * and is unaffected by regulatory/country code settings.
91 *
92 */
93u_int
94ar5211GetWirelessModes(struct ath_hal *ah)
95{
96	u_int mode = 0;
97
98	if (ath_hal_eepromGetFlag(ah, AR_EEP_AMODE)) {
99		mode = HAL_MODE_11A;
100		if (!ath_hal_eepromGetFlag(ah, AR_EEP_TURBO5DISABLE))
101			mode |= HAL_MODE_TURBO | HAL_MODE_108A;
102	}
103	if (ath_hal_eepromGetFlag(ah, AR_EEP_BMODE))
104		mode |= HAL_MODE_11B;
105	return mode;
106}
107
108#if 0
109HAL_BOOL
110ar5211GetTurboDisable(struct ath_hal *ah)
111{
112	return (AH5211(ah)->ah_turboDisable != 0);
113}
114#endif
115
116/*
117 * Called if RfKill is supported (according to EEPROM).  Set the interrupt and
118 * GPIO values so the ISR and can disable RF on a switch signal
119 */
120void
121ar5211EnableRfKill(struct ath_hal *ah)
122{
123	uint16_t rfsilent = AH_PRIVATE(ah)->ah_rfsilent;
124	int select = MS(rfsilent, AR_EEPROM_RFSILENT_GPIO_SEL);
125	int polarity = MS(rfsilent, AR_EEPROM_RFSILENT_POLARITY);
126
127	/*
128	 * Configure the desired GPIO port for input
129	 * and enable baseband rf silence.
130	 */
131	ar5211GpioCfgInput(ah, select);
132	OS_REG_SET_BIT(ah, AR_PHY_BASE, 0x00002000);
133	/*
134	 * If radio disable switch connection to GPIO bit x is enabled
135	 * program GPIO interrupt.
136	 * If rfkill bit on eeprom is 1, setupeeprommap routine has already
137	 * verified that it is a later version of eeprom, it has a place for
138	 * rfkill bit and it is set to 1, indicating that GPIO bit x hardware
139	 * connection is present.
140	 */
141	ar5211GpioSetIntr(ah, select, (ar5211GpioGet(ah, select) != polarity));
142}
143
144/*
145 * Configure GPIO Output lines
146 */
147HAL_BOOL
148ar5211GpioCfgOutput(struct ath_hal *ah, uint32_t gpio)
149{
150	uint32_t reg;
151
152	HALASSERT(gpio < AR_NUM_GPIO);
153
154	reg =  OS_REG_READ(ah, AR_GPIOCR);
155	reg &= ~(AR_GPIOCR_0_CR_A << (gpio * AR_GPIOCR_CR_SHIFT));
156	reg |= AR_GPIOCR_0_CR_A << (gpio * AR_GPIOCR_CR_SHIFT);
157
158	OS_REG_WRITE(ah, AR_GPIOCR, reg);
159	return AH_TRUE;
160}
161
162/*
163 * Configure GPIO Input lines
164 */
165HAL_BOOL
166ar5211GpioCfgInput(struct ath_hal *ah, uint32_t gpio)
167{
168	uint32_t reg;
169
170	HALASSERT(gpio < AR_NUM_GPIO);
171
172	reg =  OS_REG_READ(ah, AR_GPIOCR);
173	reg &= ~(AR_GPIOCR_0_CR_A << (gpio * AR_GPIOCR_CR_SHIFT));
174	reg |= AR_GPIOCR_0_CR_N << (gpio * AR_GPIOCR_CR_SHIFT);
175
176	OS_REG_WRITE(ah, AR_GPIOCR, reg);
177	return AH_TRUE;
178}
179
180/*
181 * Once configured for I/O - set output lines
182 */
183HAL_BOOL
184ar5211GpioSet(struct ath_hal *ah, uint32_t gpio, uint32_t val)
185{
186	uint32_t reg;
187
188	HALASSERT(gpio < AR_NUM_GPIO);
189
190	reg =  OS_REG_READ(ah, AR_GPIODO);
191	reg &= ~(1 << gpio);
192	reg |= (val&1) << gpio;
193
194	OS_REG_WRITE(ah, AR_GPIODO, reg);
195	return AH_TRUE;
196}
197
198/*
199 * Once configured for I/O - get input lines
200 */
201uint32_t
202ar5211GpioGet(struct ath_hal *ah, uint32_t gpio)
203{
204	if (gpio < AR_NUM_GPIO) {
205		uint32_t val = OS_REG_READ(ah, AR_GPIODI);
206		val = ((val & AR_GPIOD_MASK) >> gpio) & 0x1;
207		return val;
208	} else  {
209		return 0xffffffff;
210	}
211}
212
213/*
214 * Set the GPIO 0 Interrupt (gpio is ignored)
215 */
216void
217ar5211GpioSetIntr(struct ath_hal *ah, u_int gpio, uint32_t ilevel)
218{
219	uint32_t val = OS_REG_READ(ah, AR_GPIOCR);
220
221	/* Clear the bits that we will modify. */
222	val &= ~(AR_GPIOCR_INT_SEL0 | AR_GPIOCR_INT_SELH | AR_GPIOCR_INT_ENA |
223			AR_GPIOCR_0_CR_A);
224
225	val |= AR_GPIOCR_INT_SEL0 | AR_GPIOCR_INT_ENA;
226	if (ilevel)
227		val |= AR_GPIOCR_INT_SELH;
228
229	/* Don't need to change anything for low level interrupt. */
230	OS_REG_WRITE(ah, AR_GPIOCR, val);
231
232	/* Change the interrupt mask. */
233	ar5211SetInterrupts(ah, AH5211(ah)->ah_maskReg | HAL_INT_GPIO);
234}
235
236/*
237 * Change the LED blinking pattern to correspond to the connectivity
238 */
239void
240ar5211SetLedState(struct ath_hal *ah, HAL_LED_STATE state)
241{
242	static const uint32_t ledbits[8] = {
243		AR_PCICFG_LEDCTL_NONE|AR_PCICFG_LEDMODE_PROP, /* HAL_LED_INIT */
244		AR_PCICFG_LEDCTL_PEND|AR_PCICFG_LEDMODE_PROP, /* HAL_LED_SCAN */
245		AR_PCICFG_LEDCTL_PEND|AR_PCICFG_LEDMODE_PROP, /* HAL_LED_AUTH */
246		AR_PCICFG_LEDCTL_ASSOC|AR_PCICFG_LEDMODE_PROP,/* HAL_LED_ASSOC*/
247		AR_PCICFG_LEDCTL_ASSOC|AR_PCICFG_LEDMODE_PROP,/* HAL_LED_RUN */
248		AR_PCICFG_LEDCTL_NONE|AR_PCICFG_LEDMODE_RAND,
249		AR_PCICFG_LEDCTL_NONE|AR_PCICFG_LEDMODE_RAND,
250		AR_PCICFG_LEDCTL_NONE|AR_PCICFG_LEDMODE_RAND,
251	};
252	OS_REG_WRITE(ah, AR_PCICFG,
253		(OS_REG_READ(ah, AR_PCICFG) &~
254			(AR_PCICFG_LEDCTL | AR_PCICFG_LEDMODE))
255		| ledbits[state & 0x7]
256	);
257}
258
259/*
260 * Change association related fields programmed into the hardware.
261 * Writing a valid BSSID to the hardware effectively enables the hardware
262 * to synchronize its TSF to the correct beacons and receive frames coming
263 * from that BSSID. It is called by the SME JOIN operation.
264 */
265void
266ar5211WriteAssocid(struct ath_hal *ah, const uint8_t *bssid, uint16_t assocId)
267{
268	struct ath_hal_5211 *ahp = AH5211(ah);
269
270	/* XXX save bssid for possible re-use on reset */
271	OS_MEMCPY(ahp->ah_bssid, bssid, IEEE80211_ADDR_LEN);
272	OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid));
273	OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid+4) |
274				     ((assocId & 0x3fff)<<AR_BSS_ID1_AID_S));
275}
276
277/*
278 * Get the current hardware tsf for stamlme.
279 */
280uint64_t
281ar5211GetTsf64(struct ath_hal *ah)
282{
283	uint32_t low1, low2, u32;
284
285	/* sync multi-word read */
286	low1 = OS_REG_READ(ah, AR_TSF_L32);
287	u32 = OS_REG_READ(ah, AR_TSF_U32);
288	low2 = OS_REG_READ(ah, AR_TSF_L32);
289	if (low2 < low1) {	/* roll over */
290		/*
291		 * If we are not preempted this will work.  If we are
292		 * then we re-reading AR_TSF_U32 does no good as the
293		 * low bits will be meaningless.  Likewise reading
294		 * L32, U32, U32, then comparing the last two reads
295		 * to check for rollover
296		 * doesn't help if preempted--so we take this approach
297		 * as it costs one less PCI read which can be noticeable
298		 * when doing things like timestamping packets in
299		 * monitor mode.
300		 */
301		u32++;
302	}
303	return (((uint64_t) u32) << 32) | ((uint64_t) low2);
304}
305
306/*
307 * Get the current hardware tsf for stamlme.
308 */
309uint32_t
310ar5211GetTsf32(struct ath_hal *ah)
311{
312	return OS_REG_READ(ah, AR_TSF_L32);
313}
314
315/*
316 * Reset the current hardware tsf for stamlme
317 */
318void
319ar5211ResetTsf(struct ath_hal *ah)
320{
321	uint32_t val = OS_REG_READ(ah, AR_BEACON);
322
323	OS_REG_WRITE(ah, AR_BEACON, val | AR_BEACON_RESET_TSF);
324}
325
326/*
327 * Grab a semi-random value from hardware registers - may not
328 * change often
329 */
330uint32_t
331ar5211GetRandomSeed(struct ath_hal *ah)
332{
333	uint32_t nf;
334
335	nf = (OS_REG_READ(ah, AR_PHY(25)) >> 19) & 0x1ff;
336	if (nf & 0x100)
337		nf = 0 - ((nf ^ 0x1ff) + 1);
338	return (OS_REG_READ(ah, AR_TSF_U32) ^
339		OS_REG_READ(ah, AR_TSF_L32) ^ nf);
340}
341
342/*
343 * Detect if our card is present
344 */
345HAL_BOOL
346ar5211DetectCardPresent(struct ath_hal *ah)
347{
348	uint16_t macVersion, macRev;
349	uint32_t v;
350
351	/*
352	 * Read the Silicon Revision register and compare that
353	 * to what we read at attach time.  If the same, we say
354	 * a card/device is present.
355	 */
356	v = OS_REG_READ(ah, AR_SREV) & AR_SREV_ID_M;
357	macVersion = v >> AR_SREV_ID_S;
358	macRev = v & AR_SREV_REVISION_M;
359	return (AH_PRIVATE(ah)->ah_macVersion == macVersion &&
360		AH_PRIVATE(ah)->ah_macRev == macRev);
361}
362
363/*
364 * Update MIB Counters
365 */
366void
367ar5211UpdateMibCounters(struct ath_hal *ah, HAL_MIB_STATS *stats)
368{
369	stats->ackrcv_bad += OS_REG_READ(ah, AR_ACK_FAIL);
370	stats->rts_bad	  += OS_REG_READ(ah, AR_RTS_FAIL);
371	stats->fcs_bad	  += OS_REG_READ(ah, AR_FCS_FAIL);
372	stats->rts_good	  += OS_REG_READ(ah, AR_RTS_OK);
373	stats->beacons	  += OS_REG_READ(ah, AR_BEACON_CNT);
374}
375
376HAL_BOOL
377ar5211SetSifsTime(struct ath_hal *ah, u_int us)
378{
379	struct ath_hal_5211 *ahp = AH5211(ah);
380
381	if (us > ath_hal_mac_usec(ah, 0xffff)) {
382		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad SIFS time %u\n",
383		    __func__, us);
384		ahp->ah_sifstime = (u_int) -1;	/* restore default handling */
385		return AH_FALSE;
386	} else {
387		/* convert to system clocks */
388		OS_REG_WRITE(ah, AR_D_GBL_IFS_SIFS, ath_hal_mac_clks(ah, us));
389		ahp->ah_slottime = us;
390		return AH_TRUE;
391	}
392}
393
394u_int
395ar5211GetSifsTime(struct ath_hal *ah)
396{
397	u_int clks = OS_REG_READ(ah, AR_D_GBL_IFS_SIFS) & 0xffff;
398	return ath_hal_mac_usec(ah, clks);	/* convert from system clocks */
399}
400
401HAL_BOOL
402ar5211SetSlotTime(struct ath_hal *ah, u_int us)
403{
404	struct ath_hal_5211 *ahp = AH5211(ah);
405
406	if (us < HAL_SLOT_TIME_9 || us > ath_hal_mac_usec(ah, 0xffff)) {
407		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad slot time %u\n",
408		    __func__, us);
409		ahp->ah_slottime = us;	/* restore default handling */
410		return AH_FALSE;
411	} else {
412		/* convert to system clocks */
413		OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath_hal_mac_clks(ah, us));
414		ahp->ah_slottime = us;
415		return AH_TRUE;
416	}
417}
418
419u_int
420ar5211GetSlotTime(struct ath_hal *ah)
421{
422	u_int clks = OS_REG_READ(ah, AR_D_GBL_IFS_SLOT) & 0xffff;
423	return ath_hal_mac_usec(ah, clks);	/* convert from system clocks */
424}
425
426HAL_BOOL
427ar5211SetAckTimeout(struct ath_hal *ah, u_int us)
428{
429	struct ath_hal_5211 *ahp = AH5211(ah);
430
431	if (us > ath_hal_mac_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) {
432		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad ack timeout %u\n",
433		    __func__, us);
434		ahp->ah_acktimeout = (u_int) -1; /* restore default handling */
435		return AH_FALSE;
436	} else {
437		/* convert to system clocks */
438		OS_REG_RMW_FIELD(ah, AR_TIME_OUT,
439			AR_TIME_OUT_ACK, ath_hal_mac_clks(ah, us));
440		ahp->ah_acktimeout = us;
441		return AH_TRUE;
442	}
443}
444
445u_int
446ar5211GetAckTimeout(struct ath_hal *ah)
447{
448	u_int clks = MS(OS_REG_READ(ah, AR_TIME_OUT), AR_TIME_OUT_ACK);
449	return ath_hal_mac_usec(ah, clks);	/* convert from system clocks */
450}
451
452u_int
453ar5211GetAckCTSRate(struct ath_hal *ah)
454{
455	return ((AH5211(ah)->ah_staId1Defaults & AR_STA_ID1_ACKCTS_6MB) == 0);
456}
457
458HAL_BOOL
459ar5211SetAckCTSRate(struct ath_hal *ah, u_int high)
460{
461	struct ath_hal_5211 *ahp = AH5211(ah);
462
463	if (high) {
464		OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_ACKCTS_6MB);
465		ahp->ah_staId1Defaults &= ~AR_STA_ID1_ACKCTS_6MB;
466	} else {
467		OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_ACKCTS_6MB);
468		ahp->ah_staId1Defaults |= AR_STA_ID1_ACKCTS_6MB;
469	}
470	return AH_TRUE;
471}
472
473HAL_BOOL
474ar5211SetCTSTimeout(struct ath_hal *ah, u_int us)
475{
476	struct ath_hal_5211 *ahp = AH5211(ah);
477
478	if (us > ath_hal_mac_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) {
479		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad cts timeout %u\n",
480		    __func__, us);
481		ahp->ah_ctstimeout = (u_int) -1; /* restore default handling */
482		return AH_FALSE;
483	} else {
484		/* convert to system clocks */
485		OS_REG_RMW_FIELD(ah, AR_TIME_OUT,
486			AR_TIME_OUT_CTS, ath_hal_mac_clks(ah, us));
487		ahp->ah_ctstimeout = us;
488		return AH_TRUE;
489	}
490}
491
492u_int
493ar5211GetCTSTimeout(struct ath_hal *ah)
494{
495	u_int clks = MS(OS_REG_READ(ah, AR_TIME_OUT), AR_TIME_OUT_CTS);
496	return ath_hal_mac_usec(ah, clks);	/* convert from system clocks */
497}
498
499HAL_BOOL
500ar5211SetDecompMask(struct ath_hal *ah, uint16_t keyidx, int en)
501{
502	/* nothing to do */
503        return AH_TRUE;
504}
505
506void
507ar5211SetCoverageClass(struct ath_hal *ah, uint8_t coverageclass, int now)
508{
509}
510
511/*
512 * Control Adaptive Noise Immunity Parameters
513 */
514HAL_BOOL
515ar5211AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param)
516{
517	return AH_FALSE;
518}
519
520void
521ar5211AniPoll(struct ath_hal *ah, const HAL_NODE_STATS *stats, HAL_CHANNEL *chan)
522{
523}
524
525void
526ar5211MibEvent(struct ath_hal *ah, const HAL_NODE_STATS *stats)
527{
528}
529
530/*
531 * Get the rssi of frame curently being received.
532 */
533uint32_t
534ar5211GetCurRssi(struct ath_hal *ah)
535{
536	return (OS_REG_READ(ah, AR_PHY_CURRENT_RSSI) & 0xff);
537}
538
539u_int
540ar5211GetDefAntenna(struct ath_hal *ah)
541{
542	return (OS_REG_READ(ah, AR_DEF_ANTENNA) & 0x7);
543}
544
545void
546ar5211SetDefAntenna(struct ath_hal *ah, u_int antenna)
547{
548	OS_REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7));
549}
550
551HAL_ANT_SETTING
552ar5211GetAntennaSwitch(struct ath_hal *ah)
553{
554	return AH5211(ah)->ah_diversityControl;
555}
556
557HAL_BOOL
558ar5211SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings)
559{
560	const HAL_CHANNEL *chan =
561		(const HAL_CHANNEL *) AH_PRIVATE(ah)->ah_curchan;
562
563	if (chan == AH_NULL) {
564		AH5211(ah)->ah_diversityControl = settings;
565		return AH_TRUE;
566	}
567	return ar5211SetAntennaSwitchInternal(ah, settings, chan);
568}
569
570HAL_STATUS
571ar5211GetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
572	uint32_t capability, uint32_t *result)
573{
574
575	switch (type) {
576	case HAL_CAP_CIPHER:		/* cipher handled in hardware */
577		switch (capability) {
578		case HAL_CIPHER_AES_OCB:
579		case HAL_CIPHER_WEP:
580		case HAL_CIPHER_CLR:
581			return HAL_OK;
582		default:
583			return HAL_ENOTSUPP;
584		}
585	default:
586		return ath_hal_getcapability(ah, type, capability, result);
587	}
588}
589
590HAL_BOOL
591ar5211SetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
592	uint32_t capability, uint32_t setting, HAL_STATUS *status)
593{
594	switch (type) {
595	case HAL_CAP_DIAG:		/* hardware diagnostic support */
596		/*
597		 * NB: could split this up into virtual capabilities,
598		 *     (e.g. 1 => ACK, 2 => CTS, etc.) but it hardly
599		 *     seems worth the additional complexity.
600		 */
601#ifdef AH_DEBUG
602		AH_PRIVATE(ah)->ah_diagreg = setting;
603#else
604		AH_PRIVATE(ah)->ah_diagreg = setting & 0x6;	/* ACK+CTS */
605#endif
606		OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg);
607		return AH_TRUE;
608	default:
609		return ath_hal_setcapability(ah, type, capability,
610			setting, status);
611	}
612}
613
614HAL_BOOL
615ar5211GetDiagState(struct ath_hal *ah, int request,
616	const void *args, uint32_t argsize,
617	void **result, uint32_t *resultsize)
618{
619	struct ath_hal_5211 *ahp = AH5211(ah);
620
621	(void) ahp;
622	if (ath_hal_getdiagstate(ah, request, args, argsize, result, resultsize))
623		return AH_TRUE;
624	switch (request) {
625	case HAL_DIAG_EEPROM:
626		return ath_hal_eepromDiag(ah, request,
627		    args, argsize, result, resultsize);
628	case HAL_DIAG_RFGAIN:
629		*result = &ahp->ah_gainValues;
630		*resultsize = sizeof(GAIN_VALUES);
631		return AH_TRUE;
632	case HAL_DIAG_RFGAIN_CURSTEP:
633		*result = __DECONST(void *, ahp->ah_gainValues.currStep);
634		*resultsize = (*result == AH_NULL) ?
635			0 : sizeof(GAIN_OPTIMIZATION_STEP);
636		return AH_TRUE;
637	}
638	return AH_FALSE;
639}
640#endif /* AH_SUPPORT_AR5211 */
641