diff options
Diffstat (limited to 'sys/dev/ath/ath_hal/ar5416/ar5416_radar.c')
-rw-r--r-- | sys/dev/ath/ath_hal/ar5416/ar5416_radar.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c b/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c new file mode 100644 index 000000000000..d6a7d51cc704 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c @@ -0,0 +1,432 @@ +/*- + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2010-2011 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" +#include "ah_desc.h" /* NB: for HAL_PHYERR* */ + +#include "ar5416/ar5416.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" + +#include "ah_eeprom_v14.h" /* for owl_get_ntxchains() */ + +/* + * These are default parameters for the AR5416 and + * later 802.11n NICs. They simply enable some + * radar pulse event generation. + * + * These are very likely not valid for the AR5212 era + * NICs. + * + * Since these define signal sizing and threshold + * parameters, they may need changing based on the + * specific antenna and receive amplifier + * configuration. + */ +#define AR5416_DFS_FIRPWR -33 +#define AR5416_DFS_RRSSI 20 +#define AR5416_DFS_HEIGHT 10 +#define AR5416_DFS_PRSSI 15 +#define AR5416_DFS_INBAND 15 +#define AR5416_DFS_RELPWR 8 +#define AR5416_DFS_RELSTEP 12 +#define AR5416_DFS_MAXLEN 255 + +HAL_BOOL +ar5416GetDfsDefaultThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ + + /* + * These are general examples of the parameter values + * to use when configuring radar pulse detection for + * the AR5416, AR91xx, AR92xx NICs. They are only + * for testing and do require tuning depending upon the + * hardware and deployment specifics. + */ + pe->pe_firpwr = AR5416_DFS_FIRPWR; + pe->pe_rrssi = AR5416_DFS_RRSSI; + pe->pe_height = AR5416_DFS_HEIGHT; + pe->pe_prssi = AR5416_DFS_PRSSI; + pe->pe_inband = AR5416_DFS_INBAND; + pe->pe_relpwr = AR5416_DFS_RELPWR; + pe->pe_relstep = AR5416_DFS_RELSTEP; + pe->pe_maxlen = AR5416_DFS_MAXLEN; + + return (AH_TRUE); +} + +/* + * Get the radar parameter values and return them in the pe + * structure + */ +void +ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ + uint32_t val, temp; + + val = OS_REG_READ(ah, AR_PHY_RADAR_0); + + temp = MS(val,AR_PHY_RADAR_0_FIRPWR); + temp |= 0xFFFFFF80; + pe->pe_firpwr = temp; + pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI); + pe->pe_height = MS(val, AR_PHY_RADAR_0_HEIGHT); + pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI); + pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND); + + /* RADAR_1 values */ + val = OS_REG_READ(ah, AR_PHY_RADAR_1); + pe->pe_relpwr = MS(val, AR_PHY_RADAR_1_RELPWR_THRESH); + pe->pe_relstep = MS(val, AR_PHY_RADAR_1_RELSTEP_THRESH); + pe->pe_maxlen = MS(val, AR_PHY_RADAR_1_MAXLEN); + + pe->pe_extchannel = !! (OS_REG_READ(ah, AR_PHY_RADAR_EXT) & + AR_PHY_RADAR_EXT_ENA); + + pe->pe_usefir128 = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & + AR_PHY_RADAR_1_USE_FIR128); + pe->pe_blockradar = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & + AR_PHY_RADAR_1_BLOCK_CHECK); + pe->pe_enmaxrssi = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & + AR_PHY_RADAR_1_MAX_RRSSI); + pe->pe_enabled = !! + (OS_REG_READ(ah, AR_PHY_RADAR_0) & AR_PHY_RADAR_0_ENA); + pe->pe_enrelpwr = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & + AR_PHY_RADAR_1_RELPWR_ENA); + pe->pe_en_relstep_check = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & + AR_PHY_RADAR_1_RELSTEP_CHECK); +} + +/* + * Enable radar detection and set the radar parameters per the + * values in pe + */ +void +ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) +{ + uint32_t val; + + val = OS_REG_READ(ah, AR_PHY_RADAR_0); + + if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) { + val &= ~AR_PHY_RADAR_0_FIRPWR; + val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR); + } + if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) { + val &= ~AR_PHY_RADAR_0_RRSSI; + val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI); + } + if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) { + val &= ~AR_PHY_RADAR_0_HEIGHT; + val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT); + } + if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) { + val &= ~AR_PHY_RADAR_0_PRSSI; + val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI); + } + if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) { + val &= ~AR_PHY_RADAR_0_INBAND; + val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND); + } + + /*Enable FFT data*/ + val |= AR_PHY_RADAR_0_FFT_ENA; + OS_REG_WRITE(ah, AR_PHY_RADAR_0, val); + + /* Implicitly enable */ + if (pe->pe_enabled == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA); + else if (pe->pe_enabled == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA); + + if (pe->pe_usefir128 == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128); + else if (pe->pe_usefir128 == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128); + + if (pe->pe_enmaxrssi == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI); + else if (pe->pe_enmaxrssi == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI); + + if (pe->pe_blockradar == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK); + else if (pe->pe_blockradar == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK); + + if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) { + val = OS_REG_READ(ah, AR_PHY_RADAR_1); + val &= ~AR_PHY_RADAR_1_RELSTEP_THRESH; + val |= SM(pe->pe_relstep, AR_PHY_RADAR_1_RELSTEP_THRESH); + OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); + } + if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) { + val = OS_REG_READ(ah, AR_PHY_RADAR_1); + val &= ~AR_PHY_RADAR_1_RELPWR_THRESH; + val |= SM(pe->pe_relpwr, AR_PHY_RADAR_1_RELPWR_THRESH); + OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); + } + + if (pe->pe_en_relstep_check == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, + AR_PHY_RADAR_1_RELSTEP_CHECK); + else if (pe->pe_en_relstep_check == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, + AR_PHY_RADAR_1_RELSTEP_CHECK); + + if (pe->pe_enrelpwr == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, + AR_PHY_RADAR_1_RELPWR_ENA); + else if (pe->pe_enrelpwr == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, + AR_PHY_RADAR_1_RELPWR_ENA); + + if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) { + val = OS_REG_READ(ah, AR_PHY_RADAR_1); + val &= ~AR_PHY_RADAR_1_MAXLEN; + val |= SM(pe->pe_maxlen, AR_PHY_RADAR_1_MAXLEN); + OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); + } + + /* + * Enable HT/40 if the upper layer asks; + * it should check the channel is HT/40 and HAL_CAP_EXT_CHAN_DFS + * is available. + */ + if (pe->pe_extchannel == 1) + OS_REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); + else if (pe->pe_extchannel == 0) + OS_REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); +} + +/* + * Extract the radar event information from the given phy error. + * + * Returns AH_TRUE if the phy error was actually a phy error, + * AH_FALSE if the phy error wasn't a phy error. + */ + +/* Flags for pulse_bw_info */ +#define PRI_CH_RADAR_FOUND 0x01 +#define EXT_CH_RADAR_FOUND 0x02 +#define EXT_CH_RADAR_EARLY_FOUND 0x04 + +HAL_BOOL +ar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs, + uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event) +{ + HAL_BOOL doDfsExtCh; + HAL_BOOL doDfsEnhanced; + HAL_BOOL doDfsCombinedRssi; + + uint8_t rssi = 0, ext_rssi = 0; + uint8_t pulse_bw_info = 0, pulse_length_ext = 0, pulse_length_pri = 0; + uint32_t dur = 0; + int pri_found = 1, ext_found = 0; + int early_ext = 0; + int is_dc = 0; + uint16_t datalen; /* length from the RX status field */ + + /* Check whether the given phy error is a radar event */ + if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) && + (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) { + return AH_FALSE; + } + + /* Grab copies of the capabilities; just to make the code clearer */ + doDfsExtCh = AH_PRIVATE(ah)->ah_caps.halExtChanDfsSupport; + doDfsEnhanced = AH_PRIVATE(ah)->ah_caps.halEnhancedDfsSupport; + doDfsCombinedRssi = AH_PRIVATE(ah)->ah_caps.halUseCombinedRadarRssi; + + datalen = rxs->rs_datalen; + + /* If hardware supports it, use combined RSSI, else use chain 0 RSSI */ + if (doDfsCombinedRssi) + rssi = (uint8_t) rxs->rs_rssi; + else + rssi = (uint8_t) rxs->rs_rssi_ctl[0]; + + /* Set this; but only use it if doDfsExtCh is set */ + ext_rssi = (uint8_t) rxs->rs_rssi_ext[0]; + + /* Cap it at 0 if the RSSI is a negative number */ + if (rssi & 0x80) + rssi = 0; + + if (ext_rssi & 0x80) + ext_rssi = 0; + + /* + * Fetch the relevant data from the frame + */ + if (doDfsExtCh) { + if (datalen < 3) + return AH_FALSE; + + /* Last three bytes of the frame are of interest */ + pulse_length_pri = *(buf + datalen - 3); + pulse_length_ext = *(buf + datalen - 2); + pulse_bw_info = *(buf + datalen - 1); + HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, ext_rssi=%d, pulse_length_pri=%d," + " pulse_length_ext=%d, pulse_bw_info=%x\n", + __func__, rssi, ext_rssi, pulse_length_pri, pulse_length_ext, + pulse_bw_info); + } else { + /* The pulse width is byte 0 of the data */ + if (datalen >= 1) + dur = ((uint8_t) buf[0]) & 0xff; + else + dur = 0; + + if (dur == 0 && rssi == 0) { + HALDEBUG(ah, HAL_DEBUG_DFS, "%s: dur and rssi are 0\n", __func__); + return AH_FALSE; + } + + HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n", __func__, rssi, dur); + + /* Single-channel only */ + pri_found = 1; + ext_found = 0; + } + + /* + * If doing extended channel data, pulse_bw_info must + * have one of the flags set. + */ + if (doDfsExtCh && pulse_bw_info == 0x0) + return AH_FALSE; + + /* + * If the extended channel data is available, calculate + * which to pay attention to. + */ + if (doDfsExtCh) { + /* If pulse is on DC, take the larger duration of the two */ + if ((pulse_bw_info & EXT_CH_RADAR_FOUND) && + (pulse_bw_info & PRI_CH_RADAR_FOUND)) { + is_dc = 1; + if (pulse_length_ext > pulse_length_pri) { + dur = pulse_length_ext; + pri_found = 0; + ext_found = 1; + } else { + dur = pulse_length_pri; + pri_found = 1; + ext_found = 0; + } + } else if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) { + dur = pulse_length_ext; + pri_found = 0; + ext_found = 1; + early_ext = 1; + } else if (pulse_bw_info & PRI_CH_RADAR_FOUND) { + dur = pulse_length_pri; + pri_found = 1; + ext_found = 0; + } else if (pulse_bw_info & EXT_CH_RADAR_FOUND) { + dur = pulse_length_ext; + pri_found = 0; + ext_found = 1; + } + + } + + /* + * For enhanced DFS (Merlin and later), pulse_bw_info has + * implications for selecting the correct RSSI value. + */ + if (doDfsEnhanced) { + switch (pulse_bw_info & 0x03) { + case 0: + /* No radar? */ + rssi = 0; + break; + case PRI_CH_RADAR_FOUND: + /* Radar in primary channel */ + /* Cannot use ctrl channel RSSI if ext channel is stronger */ + if (ext_rssi >= (rssi + 3)) { + rssi = 0; + } + break; + case EXT_CH_RADAR_FOUND: + /* Radar in extended channel */ + /* Cannot use ext channel RSSI if ctrl channel is stronger */ + if (rssi >= (ext_rssi + 12)) { + rssi = 0; + } else { + rssi = ext_rssi; + } + break; + case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND): + /* When both are present, use stronger one */ + if (rssi < ext_rssi) + rssi = ext_rssi; + break; + } + } + + /* + * If not doing enhanced DFS, choose the ext channel if + * it is stronger than the main channel + */ + if (doDfsExtCh && !doDfsEnhanced) { + if ((ext_rssi > rssi) && (ext_rssi < 128)) + rssi = ext_rssi; + } + + /* + * XXX what happens if the above code decides the RSSI + * XXX wasn't valid, an sets it to 0? + */ + + /* + * Fill out dfs_event structure. + */ + event->re_full_ts = fulltsf; + event->re_ts = rxs->rs_tstamp; + event->re_rssi = rssi; + event->re_dur = dur; + + event->re_flags = 0; + if (pri_found) + event->re_flags |= HAL_DFS_EVENT_PRICH; + if (ext_found) + event->re_flags |= HAL_DFS_EVENT_EXTCH; + if (early_ext) + event->re_flags |= HAL_DFS_EVENT_EXTEARLY; + if (is_dc) + event->re_flags |= HAL_DFS_EVENT_ISDC; + + return AH_TRUE; +} + +/* + * Return whether fast-clock is currently enabled for this + * channel. + */ +HAL_BOOL +ar5416IsFastClockEnabled(struct ath_hal *ah) +{ + struct ath_hal_private *ahp = AH_PRIVATE(ah); + + return IS_5GHZ_FAST_CLOCK_EN(ah, ahp->ah_curchan); +} |