164921Smarcel/*- 264921Smarcel * SPDX-License-Identifier: ISC 364921Smarcel * 464921Smarcel * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 564921Smarcel * Copyright (c) 2002-2008 Atheros Communications, Inc. 664921Smarcel * 764921Smarcel * Permission to use, copy, modify, and/or distribute this software for any 864921Smarcel * purpose with or without fee is hereby granted, provided that the above 964921Smarcel * copyright notice and this permission notice appear in all copies. 1064921Smarcel * 1164921Smarcel * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1264921Smarcel * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1364921Smarcel * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1464921Smarcel * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1565067Smarcel * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1664921Smarcel * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1764921Smarcel * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1864921Smarcel * 1964921Smarcel * $FreeBSD: releng/12.0/sys/dev/ath/ath_hal/ar5416/ar5416_misc.c 326695 2017-12-08 15:57:29Z pfg $ 2064921Smarcel */ 2164921Smarcel#include "opt_ah.h" 2264921Smarcel 2364921Smarcel#include "ah.h" 2464921Smarcel#include "ah_internal.h" 2564921Smarcel#include "ah_devid.h" 2664921Smarcel#include "ah_desc.h" /* NB: for HAL_PHYERR* */ 2764921Smarcel 2864921Smarcel#include "ar5416/ar5416.h" 2964921Smarcel#include "ar5416/ar5416reg.h" 3064921Smarcel#include "ar5416/ar5416phy.h" 3164921Smarcel 3276166Smarkm#include "ah_eeprom_v14.h" /* for owl_get_ntxchains() */ 3384811Sjhb 3464921Smarcel/* 3576166Smarkm * Return the wireless modes (a,b,g,n,t) supported by hardware. 3664921Smarcel * 3776166Smarkm * This value is what is actually supported by the hardware 3876166Smarkm * and is unaffected by regulatory/country code settings. 39104984Sbde * 40102814Siedowse */ 4164921Smarcelu_int 4264921Smarcelar5416GetWirelessModes(struct ath_hal *ah) 4364921Smarcel{ 4464921Smarcel u_int mode; 4564921Smarcel struct ath_hal_private *ahpriv = AH_PRIVATE(ah); 4664921Smarcel HAL_CAPABILITIES *pCap = &ahpriv->ah_caps; 4764921Smarcel 4864921Smarcel mode = ar5212GetWirelessModes(ah); 4967238Sgallatin 5067238Sgallatin /* Only enable HT modes if the NIC supports HT */ 5167238Sgallatin if (pCap->halHTSupport == AH_TRUE && (mode & HAL_MODE_11A)) 5267238Sgallatin mode |= HAL_MODE_11NA_HT20 5364921Smarcel | HAL_MODE_11NA_HT40PLUS 5468583Smarcel | HAL_MODE_11NA_HT40MINUS 5564921Smarcel ; 5664921Smarcel if (pCap->halHTSupport == AH_TRUE && (mode & HAL_MODE_11G)) 5764921Smarcel mode |= HAL_MODE_11NG_HT20 5864921Smarcel | HAL_MODE_11NG_HT40PLUS 5983221Smarcel | HAL_MODE_11NG_HT40MINUS 6083221Smarcel ; 6183221Smarcel return mode; 6283221Smarcel} 6383221Smarcel 6483221Smarcel/* 6583221Smarcel * Change the LED blinking pattern to correspond to the connectivity 6683221Smarcel */ 6783221Smarcelvoid 6883221Smarcelar5416SetLedState(struct ath_hal *ah, HAL_LED_STATE state) 6964921Smarcel{ 7064921Smarcel static const uint32_t ledbits[8] = { 7183221Smarcel AR_MAC_LED_ASSOC_NONE, /* HAL_LED_INIT */ 7283221Smarcel AR_MAC_LED_ASSOC_PEND, /* HAL_LED_SCAN */ 7383221Smarcel AR_MAC_LED_ASSOC_PEND, /* HAL_LED_AUTH */ 7483221Smarcel AR_MAC_LED_ASSOC_ACTIVE, /* HAL_LED_ASSOC*/ 7583221Smarcel AR_MAC_LED_ASSOC_ACTIVE, /* HAL_LED_RUN */ 7683221Smarcel AR_MAC_LED_ASSOC_NONE, 7764921Smarcel AR_MAC_LED_ASSOC_NONE, 7864921Smarcel AR_MAC_LED_ASSOC_NONE, 7964921Smarcel }; 8067051Sgallatin 8167051Sgallatin if (AR_SREV_HOWL(ah)) 8267051Sgallatin return; 8367051Sgallatin 8467051Sgallatin /* 8567051Sgallatin * Set the blink operating mode. 8667051Sgallatin */ 8767051Sgallatin OS_REG_RMW_FIELD(ah, AR_MAC_LED, 8867051Sgallatin AR_MAC_LED_ASSOC, ledbits[state & 0x7]); 8967051Sgallatin 9067051Sgallatin /* XXX Blink slow mode? */ 9167051Sgallatin /* XXX Blink threshold? */ 9267051Sgallatin /* XXX Blink sleep hystersis? */ 9367051Sgallatin 9467051Sgallatin /* 9567051Sgallatin * Set the LED blink configuration to be proportional 9667051Sgallatin * to the current TX and RX filter bytes. (Ie, RX'ed 9767051Sgallatin * frames that don't match the filter are ignored.) 9867051Sgallatin * This means that higher TX/RX throughput will result 9967051Sgallatin * in the blink rate increasing. 10067051Sgallatin */ 10167051Sgallatin OS_REG_RMW_FIELD(ah, AR_MAC_LED, AR_MAC_LED_MODE, 10267051Sgallatin AR_MAC_LED_MODE_PROP); 10367051Sgallatin} 10483366Sjulian 10564921Smarcel/* 10664921Smarcel * Get the current hardware tsf for stamlme 10764921Smarcel */ 10864921Smarceluint64_t 10964921Smarcelar5416GetTsf64(struct ath_hal *ah) 11083366Sjulian{ 11164921Smarcel uint32_t low1, low2, u32; 11264921Smarcel 11372543Sjlemon /* sync multi-word read */ 11472543Sjlemon low1 = OS_REG_READ(ah, AR_TSF_L32); 11564921Smarcel u32 = OS_REG_READ(ah, AR_TSF_U32); 11664921Smarcel low2 = OS_REG_READ(ah, AR_TSF_L32); 11764921Smarcel if (low2 < low1) { /* roll over */ 11864921Smarcel /* 11964921Smarcel * If we are not preempted this will work. If we are 12083366Sjulian * then we re-reading AR_TSF_U32 does no good as the 12164921Smarcel * low bits will be meaningless. Likewise reading 12264921Smarcel * L32, U32, U32, then comparing the last two reads 12383221Smarcel * to check for rollover doesn't help if preempted--so 12483221Smarcel * we take this approach as it costs one less PCI read 12583221Smarcel * which can be noticeable when doing things like 12683221Smarcel * timestamping packets in monitor mode. 12783221Smarcel */ 12864921Smarcel u32++; 12983366Sjulian } 13064921Smarcel return (((uint64_t) u32) << 32) | ((uint64_t) low2); 13183221Smarcel} 13283221Smarcel 13383221Smarcel/* 13483221Smarcel * Update the TSF. 13583221Smarcel * 13683221Smarcel * The full TSF is only updated once the upper 32 bits have 13783221Smarcel * been written. Writing only the lower 32 bits of the TSF 13883221Smarcel * will not actually correctly update the TSF. 13983366Sjulian * 14064921Smarcel * The #if 0'ed code is to check whether the previous TSF 14183221Smarcel * reset or write has completed before writing to the 14283221Smarcel * TSF. Strictly speaking, it should be also checked before 14364921Smarcel * reading the TSF as the write/reset may not have completed. 14483221Smarcel */ 14583221Smarcelvoid 14683221Smarcelar5416SetTsf64(struct ath_hal *ah, uint64_t tsf64) 14783366Sjulian{ 14883221Smarcel /* XXX check if this is correct! */ 14983221Smarcel#if 0 15083221Smarcel int i; 15183221Smarcel uint32_t v; 15283221Smarcel 15383221Smarcel for (i = 0; i < 10; i++) { 15483221Smarcel v = OS_REG_READ(ah, AR_SLP32_MODE); 15583221Smarcel if ((v & AR_SLP32_TSF_WRITE_STATUS) == 0) 156111797Sdes break; 15783221Smarcel OS_DELAY(10); 15883221Smarcel } 15983366Sjulian if (i == 10) 16083221Smarcel ath_hal_printf(ah, "%s: couldn't slew things right!\n", __func__); 16183221Smarcel#endif 16283221Smarcel 16383221Smarcel OS_REG_WRITE(ah, AR_TSF_L32, tsf64 & 0xffffffff); 16483221Smarcel OS_REG_WRITE(ah, AR_TSF_U32, (tsf64 >> 32) & 0xffffffff); 16583221Smarcel} 16683221Smarcel 16783221Smarcel/* 16883366Sjulian * Reset the current hardware tsf for stamlme. 16983221Smarcel */ 17083221Smarcelvoid 17183221Smarcelar5416ResetTsf(struct ath_hal *ah) 17283221Smarcel{ 17383221Smarcel uint32_t v; 17483221Smarcel int i; 17583221Smarcel 17683221Smarcel for (i = 0; i < 10; i++) { 17783221Smarcel v = OS_REG_READ(ah, AR_SLP32_MODE); 17883221Smarcel if ((v & AR_SLP32_TSF_WRITE_STATUS) == 0) 17983221Smarcel break; 18083221Smarcel OS_DELAY(10); 18183221Smarcel } 182111797Sdes OS_REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE); 18383221Smarcel} 18483221Smarcel 18583221Smarceluint32_t 18683221Smarcelar5416GetCurRssi(struct ath_hal *ah) 18783221Smarcel{ 18883221Smarcel if (AR_SREV_OWL(ah)) 18983221Smarcel return (OS_REG_READ(ah, AR_PHY_CURRENT_RSSI) & 0xff); 19083221Smarcel return (OS_REG_READ(ah, AR9130_PHY_CURRENT_RSSI) & 0xff); 19183366Sjulian} 19283221Smarcel 19383221SmarcelHAL_BOOL 19483221Smarcelar5416SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) 19583221Smarcel{ 19683221Smarcel return AH_TRUE; 19783221Smarcel} 19883366Sjulian 19983221Smarcel/* Setup decompression for given key index */ 20083221SmarcelHAL_BOOL 20183221Smarcelar5416SetDecompMask(struct ath_hal *ah, uint16_t keyidx, int en) 20283221Smarcel{ 20383221Smarcel return AH_TRUE; 20483221Smarcel} 20583221Smarcel 20683366Sjulian/* Setup coverage class */ 20783221Smarcelvoid 20883221Smarcelar5416SetCoverageClass(struct ath_hal *ah, uint8_t coverageclass, int now) 20983221Smarcel{ 21083221Smarcel 21183221Smarcel ar5212SetCoverageClass(ah, coverageclass, now); 21283221Smarcel} 21383221Smarcel 21483221Smarcel/* 21583366Sjulian * Return the busy for rx_frame, rx_clear, and tx_frame 21683221Smarcel */ 21783221SmarcelHAL_BOOL 21883221Smarcelar5416GetMibCycleCounts(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hsample) 21983221Smarcel{ 22083221Smarcel struct ath_hal_5416 *ahp = AH5416(ah); 22183366Sjulian u_int32_t good = AH_TRUE; 22283221Smarcel 22383221Smarcel /* XXX freeze/unfreeze mib counters */ 22483221Smarcel uint32_t rc = OS_REG_READ(ah, AR_RCCNT); 22583221Smarcel uint32_t ec = OS_REG_READ(ah, AR_EXTRCCNT); 22683221Smarcel uint32_t rf = OS_REG_READ(ah, AR_RFCNT); 22783221Smarcel uint32_t tf = OS_REG_READ(ah, AR_TFCNT); 22883221Smarcel uint32_t cc = OS_REG_READ(ah, AR_CCCNT); /* read cycles last */ 22983366Sjulian 23083221Smarcel if (ahp->ah_cycleCount == 0 || ahp->ah_cycleCount > cc) { 23183221Smarcel /* 23283221Smarcel * Cycle counter wrap (or initial call); it's not possible 23383221Smarcel * to accurately calculate a value because the registers 23483221Smarcel * right shift rather than wrap--so punt and return 0. 23583221Smarcel */ 23683221Smarcel HALDEBUG(ah, HAL_DEBUG_ANY, 23783366Sjulian "%s: cycle counter wrap. ExtBusy = 0\n", __func__); 23883221Smarcel good = AH_FALSE; 23983221Smarcel } else { 24083221Smarcel hsample->cycle_count = cc - ahp->ah_cycleCount; 24183221Smarcel hsample->chan_busy = rc - ahp->ah_ctlBusy; 24283221Smarcel hsample->ext_chan_busy = ec - ahp->ah_extBusy; 24383221Smarcel hsample->rx_busy = rf - ahp->ah_rxBusy; 24464921Smarcel hsample->tx_busy = tf - ahp->ah_txBusy; 24564921Smarcel } 24664921Smarcel 24783366Sjulian /* 24864921Smarcel * Keep a copy of the MIB results so the next sample has something 24983221Smarcel * to work from. 25083221Smarcel */ 25164921Smarcel ahp->ah_cycleCount = cc; 25264921Smarcel ahp->ah_rxBusy = rf; 25383221Smarcel ahp->ah_ctlBusy = rc; 25483221Smarcel ahp->ah_txBusy = tf; 25591437Speter ahp->ah_extBusy = ec; 25664921Smarcel 25764921Smarcel return (good); 258111797Sdes} 25964921Smarcel 26064921Smarcel/* 26164921Smarcel * Setup the TX/RX chainmasks - this needs to be done before a call 26264921Smarcel * to the reset method as it doesn't update the hardware. 26364921Smarcel */ 26464921Smarcelvoid 26564921Smarcelar5416SetChainMasks(struct ath_hal *ah, uint32_t tx_chainmask, 26664921Smarcel uint32_t rx_chainmask) 26783366Sjulian{ 26864921Smarcel HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; 26964921Smarcel 27064921Smarcel AH5416(ah)->ah_tx_chainmask = tx_chainmask & pCap->halTxChainMask; 27183366Sjulian AH5416(ah)->ah_rx_chainmask = rx_chainmask & pCap->halRxChainMask; 27264921Smarcel} 27364921Smarcel 27464921Smarcel/* 27564921Smarcel * Return approximation of extension channel busy over an time interval 27672543Sjlemon * 0% (clear) -> 100% (busy) 27772543Sjlemon * 27864921Smarcel * XXX TODO: update this to correctly sample all the counters, 27964921Smarcel * rather than a subset of it. 28083366Sjulian */ 28164921Smarceluint32_t 28264921Smarcelar5416Get11nExtBusy(struct ath_hal *ah) 28383366Sjulian{ 28483366Sjulian struct ath_hal_5416 *ahp = AH5416(ah); 28564921Smarcel uint32_t busy; /* percentage */ 28664921Smarcel uint32_t cycleCount, ctlBusy, extBusy; 28764921Smarcel 28864921Smarcel ctlBusy = OS_REG_READ(ah, AR_RCCNT); 28983366Sjulian extBusy = OS_REG_READ(ah, AR_EXTRCCNT); 29064921Smarcel cycleCount = OS_REG_READ(ah, AR_CCCNT); 29164921Smarcel 29264921Smarcel if (ahp->ah_cycleCount == 0 || ahp->ah_cycleCount > cycleCount) { 29364921Smarcel /* 29472543Sjlemon * Cycle counter wrap (or initial call); it's not possible 29572543Sjlemon * to accurately calculate a value because the registers 29664921Smarcel * right shift rather than wrap--so punt and return 0. 29764921Smarcel */ 29883366Sjulian busy = 0; 29964921Smarcel HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cycle counter wrap. ExtBusy = 0\n", 30064921Smarcel __func__); 30183366Sjulian 30283366Sjulian } else { 30364921Smarcel uint32_t cycleDelta = cycleCount - ahp->ah_cycleCount; 30464921Smarcel uint32_t ctlBusyDelta = ctlBusy - ahp->ah_ctlBusy; 30564921Smarcel uint32_t extBusyDelta = extBusy - ahp->ah_extBusy; 30664921Smarcel uint32_t ctlClearDelta = 0; 30764921Smarcel 30864921Smarcel /* Compute control channel rxclear. 30964921Smarcel * The cycle delta may be less than the control channel delta. 31064921Smarcel * This could be solved by freezing the timers (or an atomic read, 31164921Smarcel * if one was available). Checking for the condition should be 31264921Smarcel * sufficient. 31383366Sjulian */ 31464921Smarcel if (cycleDelta > ctlBusyDelta) { 31573856Sjhb ctlClearDelta = cycleDelta - ctlBusyDelta; 31664921Smarcel } 31764921Smarcel 31864921Smarcel /* Compute ratio of extension channel busy to control channel clear 31964921Smarcel * as an approximation to extension channel cleanliness. 32072543Sjlemon * 32172543Sjlemon * According to the hardware folks, ext rxclear is undefined 32272543Sjlemon * if the ctrl rxclear is de-asserted (i.e. busy) 32372543Sjlemon */ 32472543Sjlemon if (ctlClearDelta) { 32572543Sjlemon busy = (extBusyDelta * 100) / ctlClearDelta; 32664921Smarcel } else { 32764921Smarcel busy = 100; 32864921Smarcel } 32964921Smarcel if (busy > 100) { 33064921Smarcel busy = 100; 33164921Smarcel } 33264921Smarcel#if 0 33364921Smarcel HALDEBUG(ah, HAL_DEBUG_ANY, "%s: cycleDelta 0x%x, ctlBusyDelta 0x%x, " 33464921Smarcel "extBusyDelta 0x%x, ctlClearDelta 0x%x, " 33564921Smarcel "busy %d\n", 33664921Smarcel __func__, cycleDelta, ctlBusyDelta, extBusyDelta, ctlClearDelta, busy); 33764921Smarcel#endif 33864921Smarcel } 33964921Smarcel 34064921Smarcel ahp->ah_cycleCount = cycleCount; 34164921Smarcel ahp->ah_ctlBusy = ctlBusy; 34264921Smarcel ahp->ah_extBusy = extBusy; 34364921Smarcel 34464921Smarcel return busy; 34583221Smarcel} 346104354Sscottl 34783221Smarcel/* 34883366Sjulian * Configure 20/40 operation 34983366Sjulian * 35064921Smarcel * 20/40 = joint rx clear (control and extension) 35183221Smarcel * 20 = rx clear (control) 35283221Smarcel * 35390361Sjulian * - NOTE: must stop MAC (tx) and requeue 40 MHz packets as 20 MHz when changing 35490361Sjulian * from 20/40 => 20 only 35564921Smarcel */ 35664921Smarcelvoid 35783221Smarcelar5416Set11nMac2040(struct ath_hal *ah, HAL_HT_MACMODE mode) 35883221Smarcel{ 35983221Smarcel uint32_t macmode; 36064921Smarcel 36164921Smarcel /* Configure MAC for 20/40 operation */ 36283221Smarcel if (mode == HAL_HT_MACMODE_2040) { 36383221Smarcel macmode = AR_2040_JOINED_RX_CLEAR; 36483221Smarcel } else { 36583221Smarcel macmode = 0; 366103216Sjulian } 36790361Sjulian OS_REG_WRITE(ah, AR_2040_MODE, macmode); 36883221Smarcel} 36983221Smarcel 37083221Smarcel/* 37183221Smarcel * Get Rx clear (control/extension channel) 37273856Sjhb * 37383221Smarcel * Returns active low (busy) for ctrl/ext channel 37464921Smarcel * Owl 2.0 37564921Smarcel */ 37664921SmarcelHAL_HT_RXCLEAR 37783221Smarcelar5416Get11nRxClear(struct ath_hal *ah) 37883221Smarcel{ 37983221Smarcel HAL_HT_RXCLEAR rxclear = 0; 38083221Smarcel uint32_t val; 38183221Smarcel 38283221Smarcel val = OS_REG_READ(ah, AR_DIAG_SW); 38383221Smarcel 38464921Smarcel /* control channel */ 38564921Smarcel if (val & AR_DIAG_RXCLEAR_CTL_LOW) { 38664921Smarcel rxclear |= HAL_RX_CLEAR_CTL_LOW; 38764921Smarcel } 38864921Smarcel /* extension channel */ 389104893Ssobomax if (val & AR_DIAG_RXCLEAR_EXT_LOW) { 390104893Ssobomax rxclear |= HAL_RX_CLEAR_EXT_LOW; 39164921Smarcel } 392104893Ssobomax return rxclear; 393104893Ssobomax} 394104893Ssobomax 395104893Ssobomax/* 396104893Ssobomax * Set Rx clear (control/extension channel) 397104893Ssobomax * 398104893Ssobomax * Useful for forcing the channel to appear busy for 399104893Ssobomax * debugging/diagnostics 400104893Ssobomax * Owl 2.0 401104893Ssobomax */ 402104893Ssobomaxvoid 403104893Ssobomaxar5416Set11nRxClear(struct ath_hal *ah, HAL_HT_RXCLEAR rxclear) 404104893Ssobomax{ 405104893Ssobomax /* control channel */ 406104893Ssobomax if (rxclear & HAL_RX_CLEAR_CTL_LOW) { 407104893Ssobomax OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RXCLEAR_CTL_LOW); 408104893Ssobomax } else { 409104893Ssobomax OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RXCLEAR_CTL_LOW); 410104893Ssobomax } 411104893Ssobomax /* extension channel */ 412104893Ssobomax if (rxclear & HAL_RX_CLEAR_EXT_LOW) { 413104893Ssobomax OS_REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RXCLEAR_EXT_LOW); 41483366Sjulian } else { 41564921Smarcel OS_REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RXCLEAR_EXT_LOW); 41664921Smarcel } 41783221Smarcel} 41864921Smarcel 419111797Sdes/* XXX shouldn't be here! */ 42064921Smarcel#define TU_TO_USEC(_tu) ((_tu) << 10) 42164921Smarcel 42264921SmarcelHAL_STATUS 42364921Smarcelar5416SetQuiet(struct ath_hal *ah, uint32_t period, uint32_t duration, 42472543Sjlemon uint32_t nextStart, HAL_QUIET_FLAG flag) 42572543Sjlemon{ 426104984Sbde uint32_t period_us = TU_TO_USEC(period); /* convert to us unit */ 427104984Sbde uint32_t nextStart_us = TU_TO_USEC(nextStart); /* convert to us unit */ 42864921Smarcel if (flag & HAL_QUIET_ENABLE) { 42964921Smarcel if ((!nextStart) || (flag & HAL_QUIET_ADD_CURRENT_TSF)) { 430104893Ssobomax /* Add the nextStart offset to the current TSF */ 431104893Ssobomax nextStart_us += OS_REG_READ(ah, AR_TSF_L32); 432104893Ssobomax } 433104893Ssobomax if (flag & HAL_QUIET_ADD_SWBA_RESP_TIME) { 434104893Ssobomax nextStart_us += ah->ah_config.ah_sw_beacon_response_time; 435104893Ssobomax } 436104893Ssobomax OS_REG_RMW_FIELD(ah, AR_QUIET1, AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); 437104893Ssobomax OS_REG_WRITE(ah, AR_QUIET2, SM(duration, AR_QUIET2_QUIET_DUR)); 438104893Ssobomax OS_REG_WRITE(ah, AR_QUIET_PERIOD, period_us); 439104893Ssobomax OS_REG_WRITE(ah, AR_NEXT_QUIET, nextStart_us); 440104893Ssobomax OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_QUIET); 441104893Ssobomax } else { 442104893Ssobomax OS_REG_CLR_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_QUIET); 443104893Ssobomax } 444104893Ssobomax return HAL_OK; 445104893Ssobomax} 446104893Ssobomax#undef TU_TO_USEC 44764921Smarcel 448104893SsobomaxHAL_STATUS 44964921Smarcelar5416GetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, 450104893Ssobomax uint32_t capability, uint32_t *result) 45164921Smarcel{ 452104893Ssobomax switch (type) { 45364921Smarcel case HAL_CAP_BB_HANG: 454104893Ssobomax switch (capability) { 45564921Smarcel case HAL_BB_HANG_RIFS: 45673213Sdillon return (AR_SREV_HOWL(ah) || AR_SREV_SOWL(ah)) ? HAL_OK : HAL_ENOTSUPP; 45773213Sdillon case HAL_BB_HANG_DFS: 458104893Ssobomax return (AR_SREV_HOWL(ah) || AR_SREV_SOWL(ah)) ? HAL_OK : HAL_ENOTSUPP; 45964921Smarcel case HAL_BB_HANG_RX_CLEAR: 46064921Smarcel return AR_SREV_MERLIN(ah) ? HAL_OK : HAL_ENOTSUPP; 46164921Smarcel } 46264921Smarcel break; 46364921Smarcel case HAL_CAP_MAC_HANG: 46464921Smarcel return ((ah->ah_macVersion == AR_XSREV_VERSION_OWL_PCI) || 46564921Smarcel (ah->ah_macVersion == AR_XSREV_VERSION_OWL_PCIE) || 46664921Smarcel AR_SREV_HOWL(ah) || AR_SREV_SOWL(ah)) ? 46764921Smarcel HAL_OK : HAL_ENOTSUPP; 46864921Smarcel case HAL_CAP_DIVERSITY: /* disable classic fast diversity */ 46964921Smarcel return HAL_ENXIO; 47064921Smarcel case HAL_CAP_ENFORCE_TXOP: 47164921Smarcel if (capability == 0) 47264921Smarcel return (HAL_OK); 47364921Smarcel if (capability != 1) 47464921Smarcel return (HAL_ENOTSUPP); 47564921Smarcel (*result) = 47664921Smarcel !! (AH5212(ah)->ah_miscMode & AR_PCU_TXOP_TBTT_LIMIT_ENA); 47764921Smarcel return (HAL_OK); 47864921Smarcel default: 47964921Smarcel break; 48064921Smarcel } 48164921Smarcel return ar5212GetCapability(ah, type, capability, result); 48264921Smarcel} 483104893Ssobomax 48464921SmarcelHAL_BOOL 48567238Sgallatinar5416SetCapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, 48667238Sgallatin u_int32_t capability, u_int32_t setting, HAL_STATUS *status) 48767238Sgallatin{ 48867238Sgallatin HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; 48967238Sgallatin 49067238Sgallatin switch (type) { 49167238Sgallatin case HAL_CAP_RX_CHAINMASK: 49267238Sgallatin setting &= ath_hal_eepromGet(ah, AR_EEP_RXMASK, NULL); 49367238Sgallatin pCap->halRxChainMask = setting; 49467238Sgallatin if (owl_get_ntxchains(setting) > 2) 49567238Sgallatin pCap->halRxStreams = 2; 49667238Sgallatin else 49767238Sgallatin pCap->halRxStreams = 1; 49867238Sgallatin return AH_TRUE; 49967238Sgallatin case HAL_CAP_TX_CHAINMASK: 50067238Sgallatin setting &= ath_hal_eepromGet(ah, AR_EEP_TXMASK, NULL); 50171494Sjhb pCap->halTxChainMask = setting; 50267238Sgallatin if (owl_get_ntxchains(setting) > 2) 50367238Sgallatin pCap->halTxStreams = 2; 50467238Sgallatin else 50567238Sgallatin pCap->halTxStreams = 1; 50664921Smarcel return AH_TRUE; 507104893Ssobomax case HAL_CAP_ENFORCE_TXOP: 508104893Ssobomax if (capability != 1) 50964921Smarcel return AH_FALSE; 51064921Smarcel if (setting) { 51164921Smarcel AH5212(ah)->ah_miscMode 51264921Smarcel |= AR_PCU_TXOP_TBTT_LIMIT_ENA; 51364921Smarcel OS_REG_SET_BIT(ah, AR_MISC_MODE, 51464921Smarcel AR_PCU_TXOP_TBTT_LIMIT_ENA); 51564921Smarcel } else { 51664921Smarcel AH5212(ah)->ah_miscMode 51764921Smarcel &= ~AR_PCU_TXOP_TBTT_LIMIT_ENA; 51864921Smarcel OS_REG_CLR_BIT(ah, AR_MISC_MODE, 51964921Smarcel AR_PCU_TXOP_TBTT_LIMIT_ENA); 520104893Ssobomax } 521104893Ssobomax return AH_TRUE; 52264921Smarcel default: 52364921Smarcel break; 524104893Ssobomax } 525104893Ssobomax return ar5212SetCapability(ah, type, capability, setting, status); 52664921Smarcel} 52764921Smarcel 528104893Ssobomaxstatic int ar5416DetectMacHang(struct ath_hal *ah); 529104893Ssobomaxstatic int ar5416DetectBBHang(struct ath_hal *ah); 53064921Smarcel 53164921SmarcelHAL_BOOL 53264921Smarcelar5416GetDiagState(struct ath_hal *ah, int request, 53372543Sjlemon const void *args, uint32_t argsize, 53472543Sjlemon void **result, uint32_t *resultsize) 53572543Sjlemon{ 53672543Sjlemon struct ath_hal_5416 *ahp = AH5416(ah); 53764921Smarcel int hangs; 53864921Smarcel 53983366Sjulian if (ath_hal_getdiagstate(ah, request, args, argsize, result, resultsize)) 54064921Smarcel return AH_TRUE; 54164921Smarcel switch (request) { 54264921Smarcel case HAL_DIAG_EEPROM: 54383366Sjulian return ath_hal_eepromDiag(ah, request, 54464921Smarcel args, argsize, result, resultsize); 54564921Smarcel case HAL_DIAG_CHECK_HANGS: 54664921Smarcel if (argsize != sizeof(int)) 54764921Smarcel return AH_FALSE; 54864921Smarcel hangs = *(const int *) args; 54972543Sjlemon ahp->ah_hangs = 0; 55072543Sjlemon if (hangs & HAL_BB_HANGS) 55164921Smarcel ahp->ah_hangs |= ar5416DetectBBHang(ah); 55264921Smarcel /* NB: if BB is hung MAC will be hung too so skip check */ 55383366Sjulian if (ahp->ah_hangs == 0 && (hangs & HAL_MAC_HANGS)) 55483366Sjulian ahp->ah_hangs |= ar5416DetectMacHang(ah); 55564921Smarcel *result = &ahp->ah_hangs; 55683366Sjulian *resultsize = sizeof(ahp->ah_hangs); 55764921Smarcel return AH_TRUE; 55864921Smarcel } 55964921Smarcel return ar5212GetDiagState(ah, request, 56083366Sjulian args, argsize, result, resultsize); 56164921Smarcel} 56283366Sjulian 56364921SmarcelHAL_BOOL 56464921Smarcelar5416SetRifsDelay(struct ath_hal *ah, const struct ieee80211_channel *chan, 56564921Smarcel HAL_BOOL enable) 56683366Sjulian{ 56783366Sjulian uint32_t val; 56864921Smarcel HAL_BOOL is_chan_2g = AH_FALSE; 56964921Smarcel HAL_BOOL is_ht40 = AH_FALSE; 57064921Smarcel 57164921Smarcel if (chan) 57283366Sjulian is_chan_2g = IEEE80211_IS_CHAN_2GHZ(chan); 57364921Smarcel 57464921Smarcel if (chan) 57564921Smarcel is_ht40 = IEEE80211_IS_CHAN_HT40(chan); 57664921Smarcel 57764921Smarcel /* Only support disabling RIFS delay for now */ 57864921Smarcel HALASSERT(enable == AH_FALSE); 57964921Smarcel 58064921Smarcel if (enable == AH_TRUE) 58164921Smarcel return AH_FALSE; 58264921Smarcel 58364921Smarcel /* Change RIFS init delay to 0 */ 58464921Smarcel val = OS_REG_READ(ah, AR_PHY_HEAVY_CLIP_FACTOR_RIFS); 58583366Sjulian val &= ~AR_PHY_RIFS_INIT_DELAY; 58664921Smarcel OS_REG_WRITE(ah, AR_PHY_HEAVY_CLIP_FACTOR_RIFS, val); 58764921Smarcel 58864921Smarcel /* 58983366Sjulian * For Owl, RIFS RX parameters are controlled differently; 59064921Smarcel * it isn't enabled in the inivals by default. 59164921Smarcel * 59264921Smarcel * For Sowl/Howl, RIFS RX is enabled in the inivals by default; 59364921Smarcel * the following code sets them back to non-RIFS values. 59464921Smarcel * 59593593Sjhb * For > Sowl/Howl, RIFS RX can be left on by default and so 59664921Smarcel * this function shouldn't be called. 59791406Sjhb */ 59883981Srwatson if ((! AR_SREV_SOWL(ah)) && (! AR_SREV_HOWL(ah))) 59983366Sjulian return AH_TRUE; 60064921Smarcel 60164921Smarcel /* Reset search delay to default values */ 60264921Smarcel if (is_chan_2g) 60364921Smarcel if (is_ht40) 60464921Smarcel OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, 0x268); 605105441Smarkm else 60664921Smarcel OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, 0x134); 60764921Smarcel else 60864921Smarcel if (is_ht40) 60964921Smarcel OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, 0x370); 61064921Smarcel else 61183221Smarcel OS_REG_WRITE(ah, AR_PHY_SEARCH_START_DELAY, 0x1b8); 61264921Smarcel 61364921Smarcel return AH_TRUE; 61464921Smarcel} 61564921Smarcel 61664921Smarcelstatic HAL_BOOL 61764921Smarcelar5416CompareDbgHang(struct ath_hal *ah, const mac_dbg_regs_t *regs, 61864921Smarcel const hal_mac_hang_check_t *check) 61964921Smarcel{ 62064921Smarcel int found_states; 62164921Smarcel 62264921Smarcel found_states = 0; 62364921Smarcel if (check->states & dcu_chain_state) { 62464921Smarcel int i; 62564921Smarcel 62664921Smarcel for (i = 0; i < 6; i++) { 62783366Sjulian if (((regs->dma_dbg_4 >> (5*i)) & 0x1f) == 62883366Sjulian check->dcu_chain_state) 62964921Smarcel found_states |= dcu_chain_state; 63064921Smarcel } 63164921Smarcel for (i = 0; i < 4; i++) { 63264921Smarcel if (((regs->dma_dbg_5 >> (5*i)) & 0x1f) == 63364921Smarcel check->dcu_chain_state) 63464921Smarcel found_states |= dcu_chain_state; 63564921Smarcel } 63664921Smarcel } 63764921Smarcel if (check->states & dcu_complete_state) { 63864921Smarcel if ((regs->dma_dbg_6 & 0x3) == check->dcu_complete_state) 63964921Smarcel found_states |= dcu_complete_state; 64064921Smarcel } 64164921Smarcel if (check->states & qcu_stitch_state) { 64264921Smarcel if (((regs->dma_dbg_3 >> 18) & 0xf) == check->qcu_stitch_state) 64364921Smarcel found_states |= qcu_stitch_state; 64464921Smarcel } 64564921Smarcel if (check->states & qcu_fetch_state) { 64664921Smarcel if (((regs->dma_dbg_3 >> 22) & 0xf) == check->qcu_fetch_state) 64764921Smarcel found_states |= qcu_fetch_state; 64864921Smarcel } 64964921Smarcel if (check->states & qcu_complete_state) { 65064921Smarcel if (((regs->dma_dbg_3 >> 26) & 0x7) == check->qcu_complete_state) 65164921Smarcel found_states |= qcu_complete_state; 65264921Smarcel } 65364921Smarcel return (found_states == check->states); 65464921Smarcel} 65564921Smarcel 65664921Smarcel#define NUM_STATUS_READS 50 65783366Sjulian 65864921Smarcelstatic int 65964921Smarcelar5416DetectMacHang(struct ath_hal *ah) 66064921Smarcel{ 66164921Smarcel static const hal_mac_hang_check_t hang_sig1 = { 66264921Smarcel .dcu_chain_state = 0x6, 66364921Smarcel .dcu_complete_state = 0x1, 66464921Smarcel .states = dcu_chain_state 66564921Smarcel | dcu_complete_state, 66664921Smarcel }; 66764921Smarcel static const hal_mac_hang_check_t hang_sig2 = { 66864921Smarcel .qcu_stitch_state = 0x9, 66964921Smarcel .qcu_fetch_state = 0x8, 67064921Smarcel .qcu_complete_state = 0x4, 67164921Smarcel .states = qcu_stitch_state 67264921Smarcel | qcu_fetch_state 67383366Sjulian | qcu_complete_state, 67464921Smarcel }; 67583221Smarcel mac_dbg_regs_t mac_dbg; 67683221Smarcel int i; 67764921Smarcel 67864921Smarcel mac_dbg.dma_dbg_3 = OS_REG_READ(ah, AR_DMADBG_3); 67964921Smarcel mac_dbg.dma_dbg_4 = OS_REG_READ(ah, AR_DMADBG_4); 68072543Sjlemon mac_dbg.dma_dbg_5 = OS_REG_READ(ah, AR_DMADBG_5); 68172543Sjlemon mac_dbg.dma_dbg_6 = OS_REG_READ(ah, AR_DMADBG_6); 68272543Sjlemon for (i = 1; i <= NUM_STATUS_READS; i++) { 68364921Smarcel if (mac_dbg.dma_dbg_3 != OS_REG_READ(ah, AR_DMADBG_3) || 68464921Smarcel mac_dbg.dma_dbg_4 != OS_REG_READ(ah, AR_DMADBG_4) || 68564921Smarcel mac_dbg.dma_dbg_5 != OS_REG_READ(ah, AR_DMADBG_5) || 686111797Sdes mac_dbg.dma_dbg_6 != OS_REG_READ(ah, AR_DMADBG_6)) 68764921Smarcel return 0; 68864921Smarcel } 68964921Smarcel 69064921Smarcel if (ar5416CompareDbgHang(ah, &mac_dbg, &hang_sig1)) 69164921Smarcel return HAL_MAC_HANG_SIG1; 69264921Smarcel if (ar5416CompareDbgHang(ah, &mac_dbg, &hang_sig2)) 69364921Smarcel return HAL_MAC_HANG_SIG2; 69464921Smarcel 69564921Smarcel HALDEBUG(ah, HAL_DEBUG_HANG, "%s Found an unknown MAC hang signature " 69683366Sjulian "DMADBG_3=0x%x DMADBG_4=0x%x DMADBG_5=0x%x DMADBG_6=0x%x\n", 69764921Smarcel __func__, mac_dbg.dma_dbg_3, mac_dbg.dma_dbg_4, mac_dbg.dma_dbg_5, 69864921Smarcel mac_dbg.dma_dbg_6); 69964921Smarcel 70064921Smarcel return 0; 70164921Smarcel} 70264921Smarcel 70364921Smarcel/* 704111797Sdes * Determine if the baseband using the Observation Bus Register 70564921Smarcel */ 70664921Smarcelstatic int 70764921Smarcelar5416DetectBBHang(struct ath_hal *ah) 70864921Smarcel{ 70964921Smarcel#define N(a) (sizeof(a)/sizeof(a[0])) 71064921Smarcel /* 71164921Smarcel * Check the PCU Observation Bus 1 register (0x806c) 71264921Smarcel * NUM_STATUS_READS times 71364921Smarcel * 71464921Smarcel * 4 known BB hang signatures - 71564921Smarcel * [1] bits 8,9,11 are 0. State machine state (bits 25-31) is 0x1E 71683366Sjulian * [2] bits 8,9 are 1, bit 11 is 0. State machine state 71764921Smarcel * (bits 25-31) is 0x52 718102814Siedowse * [3] bits 8,9 are 1, bit 11 is 0. State machine state 71983221Smarcel * (bits 25-31) is 0x18 72064921Smarcel * [4] bit 10 is 1, bit 11 is 0. WEP state (bits 12-17) is 0x2, 72164921Smarcel * Rx State (bits 20-24) is 0x7. 72272543Sjlemon */ 72372543Sjlemon static const struct { 72464921Smarcel uint32_t val; 72564921Smarcel uint32_t mask; 72664921Smarcel int code; 72764921Smarcel } hang_list[] = { 728102814Siedowse /* Reg Value Reg Mask Hang Code XXX */ 729102814Siedowse { 0x1E000000, 0x7E000B00, HAL_BB_HANG_DFS }, 73064921Smarcel { 0x52000B00, 0x7E000B00, HAL_BB_HANG_RIFS }, 73164921Smarcel { 0x18000B00, 0x7E000B00, HAL_BB_HANG_RX_CLEAR }, 73264921Smarcel { 0x00702400, 0x7E7FFFEF, HAL_BB_HANG_RX_CLEAR } 733105441Smarkm }; 73464921Smarcel uint32_t hang_sig; 73583221Smarcel int i; 736102814Siedowse 73764921Smarcel hang_sig = OS_REG_READ(ah, AR_OBSERV_1); 73864921Smarcel for (i = 1; i <= NUM_STATUS_READS; i++) { 73964921Smarcel if (hang_sig != OS_REG_READ(ah, AR_OBSERV_1)) 74072543Sjlemon return 0; 74172543Sjlemon } 74272543Sjlemon for (i = 0; i < N(hang_list); i++) 74364921Smarcel if ((hang_sig & hang_list[i].mask) == hang_list[i].val) { 74464921Smarcel HALDEBUG(ah, HAL_DEBUG_HANG, 74583221Smarcel "%s BB hang, signature 0x%x, code 0x%x\n", 74664921Smarcel __func__, hang_sig, hang_list[i].code); 74764921Smarcel return hang_list[i].code; 74883221Smarcel } 74964921Smarcel 75064921Smarcel HALDEBUG(ah, HAL_DEBUG_HANG, "%s Found an unknown BB hang signature! " 75164921Smarcel "<0x806c>=0x%x\n", __func__, hang_sig); 752102814Siedowse 753102814Siedowse return 0; 75464921Smarcel#undef N 75564921Smarcel} 75664921Smarcel#undef NUM_STATUS_READS 75783366Sjulian