ar5416_radar.c revision 231927
1231927Sadrian/* 2231927Sadrian * Copyright (c) 2010-2011 Atheros Communications, Inc. 3231927Sadrian * 4231927Sadrian * Permission to use, copy, modify, and/or distribute this software for any 5231927Sadrian * purpose with or without fee is hereby granted, provided that the above 6231927Sadrian * copyright notice and this permission notice appear in all copies. 7231927Sadrian * 8231927Sadrian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9231927Sadrian * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10231927Sadrian * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11231927Sadrian * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12231927Sadrian * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13231927Sadrian * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14231927Sadrian * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15231927Sadrian * 16231927Sadrian * $FreeBSD: head/sys/dev/ath/ath_hal/ar5416/ar5416_radar.c 231927 2012-02-20 03:07:07Z adrian $ 17231927Sadrian */ 18231927Sadrian#include "opt_ah.h" 19231927Sadrian 20231927Sadrian#include "ah.h" 21231927Sadrian#include "ah_internal.h" 22231927Sadrian#include "ah_devid.h" 23231927Sadrian#include "ah_desc.h" /* NB: for HAL_PHYERR* */ 24231927Sadrian 25231927Sadrian#include "ar5416/ar5416.h" 26231927Sadrian#include "ar5416/ar5416reg.h" 27231927Sadrian#include "ar5416/ar5416phy.h" 28231927Sadrian 29231927Sadrian#include "ah_eeprom_v14.h" /* for owl_get_ntxchains() */ 30231927Sadrian 31231927Sadrian/* 32231927Sadrian * Get the radar parameter values and return them in the pe 33231927Sadrian * structure 34231927Sadrian */ 35231927Sadrianvoid 36231927Sadrianar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) 37231927Sadrian{ 38231927Sadrian uint32_t val, temp; 39231927Sadrian 40231927Sadrian val = OS_REG_READ(ah, AR_PHY_RADAR_0); 41231927Sadrian 42231927Sadrian temp = MS(val,AR_PHY_RADAR_0_FIRPWR); 43231927Sadrian temp |= 0xFFFFFF80; 44231927Sadrian pe->pe_firpwr = temp; 45231927Sadrian pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI); 46231927Sadrian pe->pe_height = MS(val, AR_PHY_RADAR_0_HEIGHT); 47231927Sadrian pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI); 48231927Sadrian pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND); 49231927Sadrian 50231927Sadrian /* RADAR_1 values */ 51231927Sadrian val = OS_REG_READ(ah, AR_PHY_RADAR_1); 52231927Sadrian pe->pe_relpwr = MS(val, AR_PHY_RADAR_1_RELPWR_THRESH); 53231927Sadrian pe->pe_relstep = MS(val, AR_PHY_RADAR_1_RELSTEP_THRESH); 54231927Sadrian pe->pe_maxlen = MS(val, AR_PHY_RADAR_1_MAXLEN); 55231927Sadrian 56231927Sadrian pe->pe_extchannel = !! (OS_REG_READ(ah, AR_PHY_RADAR_EXT) & 57231927Sadrian AR_PHY_RADAR_EXT_ENA); 58231927Sadrian 59231927Sadrian pe->pe_usefir128 = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & 60231927Sadrian AR_PHY_RADAR_1_USE_FIR128); 61231927Sadrian pe->pe_blockradar = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & 62231927Sadrian AR_PHY_RADAR_1_BLOCK_CHECK); 63231927Sadrian pe->pe_enmaxrssi = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & 64231927Sadrian AR_PHY_RADAR_1_MAX_RRSSI); 65231927Sadrian pe->pe_enabled = !! 66231927Sadrian (OS_REG_READ(ah, AR_PHY_RADAR_0) & AR_PHY_RADAR_0_ENA); 67231927Sadrian pe->pe_enrelpwr = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & 68231927Sadrian AR_PHY_RADAR_1_RELPWR_ENA); 69231927Sadrian pe->pe_en_relstep_check = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & 70231927Sadrian AR_PHY_RADAR_1_RELSTEP_CHECK); 71231927Sadrian} 72231927Sadrian 73231927Sadrian/* 74231927Sadrian * Enable radar detection and set the radar parameters per the 75231927Sadrian * values in pe 76231927Sadrian */ 77231927Sadrianvoid 78231927Sadrianar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) 79231927Sadrian{ 80231927Sadrian uint32_t val; 81231927Sadrian 82231927Sadrian val = OS_REG_READ(ah, AR_PHY_RADAR_0); 83231927Sadrian 84231927Sadrian if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) { 85231927Sadrian val &= ~AR_PHY_RADAR_0_FIRPWR; 86231927Sadrian val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR); 87231927Sadrian } 88231927Sadrian if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) { 89231927Sadrian val &= ~AR_PHY_RADAR_0_RRSSI; 90231927Sadrian val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI); 91231927Sadrian } 92231927Sadrian if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) { 93231927Sadrian val &= ~AR_PHY_RADAR_0_HEIGHT; 94231927Sadrian val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT); 95231927Sadrian } 96231927Sadrian if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) { 97231927Sadrian val &= ~AR_PHY_RADAR_0_PRSSI; 98231927Sadrian val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI); 99231927Sadrian } 100231927Sadrian if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) { 101231927Sadrian val &= ~AR_PHY_RADAR_0_INBAND; 102231927Sadrian val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND); 103231927Sadrian } 104231927Sadrian 105231927Sadrian /*Enable FFT data*/ 106231927Sadrian val |= AR_PHY_RADAR_0_FFT_ENA; 107231927Sadrian OS_REG_WRITE(ah, AR_PHY_RADAR_0, val); 108231927Sadrian 109231927Sadrian /* Implicitly enable */ 110231927Sadrian if (pe->pe_enabled == 1) 111231927Sadrian OS_REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA); 112231927Sadrian else if (pe->pe_enabled == 0) 113231927Sadrian OS_REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA); 114231927Sadrian 115231927Sadrian if (pe->pe_usefir128 == 1) 116231927Sadrian OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128); 117231927Sadrian else if (pe->pe_usefir128 == 0) 118231927Sadrian OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128); 119231927Sadrian 120231927Sadrian if (pe->pe_enmaxrssi == 1) 121231927Sadrian OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI); 122231927Sadrian else if (pe->pe_enmaxrssi == 0) 123231927Sadrian OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI); 124231927Sadrian 125231927Sadrian if (pe->pe_blockradar == 1) 126231927Sadrian OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK); 127231927Sadrian else if (pe->pe_blockradar == 0) 128231927Sadrian OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK); 129231927Sadrian 130231927Sadrian if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) { 131231927Sadrian val = OS_REG_READ(ah, AR_PHY_RADAR_1); 132231927Sadrian val &= ~AR_PHY_RADAR_1_RELSTEP_THRESH; 133231927Sadrian val |= SM(pe->pe_relstep, AR_PHY_RADAR_1_RELSTEP_THRESH); 134231927Sadrian OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); 135231927Sadrian } 136231927Sadrian if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) { 137231927Sadrian val = OS_REG_READ(ah, AR_PHY_RADAR_1); 138231927Sadrian val &= ~AR_PHY_RADAR_1_RELPWR_THRESH; 139231927Sadrian val |= SM(pe->pe_relpwr, AR_PHY_RADAR_1_RELPWR_THRESH); 140231927Sadrian OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); 141231927Sadrian } 142231927Sadrian 143231927Sadrian if (pe->pe_en_relstep_check == 1) 144231927Sadrian OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, 145231927Sadrian AR_PHY_RADAR_1_RELSTEP_CHECK); 146231927Sadrian else if (pe->pe_en_relstep_check == 0) 147231927Sadrian OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, 148231927Sadrian AR_PHY_RADAR_1_RELSTEP_CHECK); 149231927Sadrian 150231927Sadrian if (pe->pe_enrelpwr == 1) 151231927Sadrian OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, 152231927Sadrian AR_PHY_RADAR_1_RELPWR_ENA); 153231927Sadrian else if (pe->pe_enrelpwr == 0) 154231927Sadrian OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, 155231927Sadrian AR_PHY_RADAR_1_RELPWR_ENA); 156231927Sadrian 157231927Sadrian if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) { 158231927Sadrian val = OS_REG_READ(ah, AR_PHY_RADAR_1); 159231927Sadrian val &= ~AR_PHY_RADAR_1_MAXLEN; 160231927Sadrian val |= SM(pe->pe_maxlen, AR_PHY_RADAR_1_MAXLEN); 161231927Sadrian OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); 162231927Sadrian } 163231927Sadrian 164231927Sadrian /* 165231927Sadrian * Enable HT/40 if the upper layer asks; 166231927Sadrian * it should check the channel is HT/40 and HAL_CAP_EXT_CHAN_DFS 167231927Sadrian * is available. 168231927Sadrian */ 169231927Sadrian if (pe->pe_extchannel == 1) 170231927Sadrian OS_REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); 171231927Sadrian else if (pe->pe_extchannel == 0) 172231927Sadrian OS_REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); 173231927Sadrian} 174231927Sadrian 175231927Sadrian/* 176231927Sadrian * Extract the radar event information from the given phy error. 177231927Sadrian * 178231927Sadrian * Returns AH_TRUE if the phy error was actually a phy error, 179231927Sadrian * AH_FALSE if the phy error wasn't a phy error. 180231927Sadrian */ 181231927Sadrian 182231927Sadrian/* Flags for pulse_bw_info */ 183231927Sadrian#define PRI_CH_RADAR_FOUND 0x01 184231927Sadrian#define EXT_CH_RADAR_FOUND 0x02 185231927Sadrian#define EXT_CH_RADAR_EARLY_FOUND 0x04 186231927Sadrian 187231927SadrianHAL_BOOL 188231927Sadrianar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs, 189231927Sadrian uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event) 190231927Sadrian{ 191231927Sadrian HAL_BOOL doDfsExtCh; 192231927Sadrian HAL_BOOL doDfsEnhanced; 193231927Sadrian HAL_BOOL doDfsCombinedRssi; 194231927Sadrian 195231927Sadrian uint8_t rssi = 0, ext_rssi = 0; 196231927Sadrian uint8_t pulse_bw_info = 0, pulse_length_ext = 0, pulse_length_pri = 0; 197231927Sadrian uint32_t dur = 0; 198231927Sadrian int pri_found = 1, ext_found = 0; 199231927Sadrian int early_ext = 0; 200231927Sadrian int is_dc = 0; 201231927Sadrian uint16_t datalen; /* length from the RX status field */ 202231927Sadrian 203231927Sadrian /* Check whether the given phy error is a radar event */ 204231927Sadrian if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) && 205231927Sadrian (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) { 206231927Sadrian return AH_FALSE; 207231927Sadrian } 208231927Sadrian 209231927Sadrian /* Grab copies of the capabilities; just to make the code clearer */ 210231927Sadrian doDfsExtCh = AH_PRIVATE(ah)->ah_caps.halExtChanDfsSupport; 211231927Sadrian doDfsEnhanced = AH_PRIVATE(ah)->ah_caps.halEnhancedDfsSupport; 212231927Sadrian doDfsCombinedRssi = AH_PRIVATE(ah)->ah_caps.halUseCombinedRadarRssi; 213231927Sadrian 214231927Sadrian datalen = rxs->rs_datalen; 215231927Sadrian 216231927Sadrian /* If hardware supports it, use combined RSSI, else use chain 0 RSSI */ 217231927Sadrian if (doDfsCombinedRssi) 218231927Sadrian rssi = (uint8_t) rxs->rs_rssi; 219231927Sadrian else 220231927Sadrian rssi = (uint8_t) rxs->rs_rssi_ctl[0]; 221231927Sadrian 222231927Sadrian /* Set this; but only use it if doDfsExtCh is set */ 223231927Sadrian ext_rssi = (uint8_t) rxs->rs_rssi_ext[0]; 224231927Sadrian 225231927Sadrian /* Cap it at 0 if the RSSI is a negative number */ 226231927Sadrian if (rssi & 0x80) 227231927Sadrian rssi = 0; 228231927Sadrian 229231927Sadrian if (ext_rssi & 0x80) 230231927Sadrian ext_rssi = 0; 231231927Sadrian 232231927Sadrian /* 233231927Sadrian * Fetch the relevant data from the frame 234231927Sadrian */ 235231927Sadrian if (doDfsExtCh) { 236231927Sadrian if (datalen < 3) 237231927Sadrian return AH_FALSE; 238231927Sadrian 239231927Sadrian /* Last three bytes of the frame are of interest */ 240231927Sadrian pulse_length_pri = *(buf + datalen - 3); 241231927Sadrian pulse_length_ext = *(buf + datalen - 2); 242231927Sadrian pulse_bw_info = *(buf + datalen - 1); 243231927Sadrian HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, ext_rssi=%d, pulse_length_pri=%d," 244231927Sadrian " pulse_length_ext=%d, pulse_bw_info=%x\n", 245231927Sadrian __func__, rssi, ext_rssi, pulse_length_pri, pulse_length_ext, 246231927Sadrian pulse_bw_info); 247231927Sadrian } else { 248231927Sadrian /* The pulse width is byte 0 of the data */ 249231927Sadrian if (datalen >= 1) 250231927Sadrian dur = ((uint8_t) buf[0]) & 0xff; 251231927Sadrian else 252231927Sadrian dur = 0; 253231927Sadrian 254231927Sadrian if (dur == 0 && rssi == 0) { 255231927Sadrian HALDEBUG(ah, HAL_DEBUG_DFS, "%s: dur and rssi are 0\n", __func__); 256231927Sadrian return AH_FALSE; 257231927Sadrian } 258231927Sadrian 259231927Sadrian HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n", __func__, rssi, dur); 260231927Sadrian 261231927Sadrian /* Single-channel only */ 262231927Sadrian pri_found = 1; 263231927Sadrian ext_found = 0; 264231927Sadrian } 265231927Sadrian 266231927Sadrian /* 267231927Sadrian * If doing extended channel data, pulse_bw_info must 268231927Sadrian * have one of the flags set. 269231927Sadrian */ 270231927Sadrian if (doDfsExtCh && pulse_bw_info == 0x0) 271231927Sadrian return AH_FALSE; 272231927Sadrian 273231927Sadrian /* 274231927Sadrian * If the extended channel data is available, calculate 275231927Sadrian * which to pay attention to. 276231927Sadrian */ 277231927Sadrian if (doDfsExtCh) { 278231927Sadrian /* If pulse is on DC, take the larger duration of the two */ 279231927Sadrian if ((pulse_bw_info & EXT_CH_RADAR_FOUND) && 280231927Sadrian (pulse_bw_info & PRI_CH_RADAR_FOUND)) { 281231927Sadrian is_dc = 1; 282231927Sadrian if (pulse_length_ext > pulse_length_pri) { 283231927Sadrian dur = pulse_length_ext; 284231927Sadrian pri_found = 0; 285231927Sadrian ext_found = 1; 286231927Sadrian } else { 287231927Sadrian dur = pulse_length_pri; 288231927Sadrian pri_found = 1; 289231927Sadrian ext_found = 0; 290231927Sadrian } 291231927Sadrian } else if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) { 292231927Sadrian dur = pulse_length_ext; 293231927Sadrian pri_found = 0; 294231927Sadrian ext_found = 1; 295231927Sadrian early_ext = 1; 296231927Sadrian } else if (pulse_bw_info & PRI_CH_RADAR_FOUND) { 297231927Sadrian dur = pulse_length_pri; 298231927Sadrian pri_found = 1; 299231927Sadrian ext_found = 0; 300231927Sadrian } else if (pulse_bw_info & EXT_CH_RADAR_FOUND) { 301231927Sadrian dur = pulse_length_ext; 302231927Sadrian pri_found = 0; 303231927Sadrian ext_found = 1; 304231927Sadrian } 305231927Sadrian 306231927Sadrian } 307231927Sadrian 308231927Sadrian /* 309231927Sadrian * For enhanced DFS (Merlin and later), pulse_bw_info has 310231927Sadrian * implications for selecting the correct RSSI value. 311231927Sadrian */ 312231927Sadrian if (doDfsEnhanced) { 313231927Sadrian switch (pulse_bw_info & 0x03) { 314231927Sadrian case 0: 315231927Sadrian /* No radar? */ 316231927Sadrian rssi = 0; 317231927Sadrian break; 318231927Sadrian case PRI_CH_RADAR_FOUND: 319231927Sadrian /* Radar in primary channel */ 320231927Sadrian /* Cannot use ctrl channel RSSI if ext channel is stronger */ 321231927Sadrian if (ext_rssi >= (rssi + 3)) { 322231927Sadrian rssi = 0; 323231927Sadrian }; 324231927Sadrian break; 325231927Sadrian case EXT_CH_RADAR_FOUND: 326231927Sadrian /* Radar in extended channel */ 327231927Sadrian /* Cannot use ext channel RSSI if ctrl channel is stronger */ 328231927Sadrian if (rssi >= (ext_rssi + 12)) { 329231927Sadrian rssi = 0; 330231927Sadrian } else { 331231927Sadrian rssi = ext_rssi; 332231927Sadrian } 333231927Sadrian break; 334231927Sadrian case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND): 335231927Sadrian /* When both are present, use stronger one */ 336231927Sadrian if (rssi < ext_rssi) 337231927Sadrian rssi = ext_rssi; 338231927Sadrian break; 339231927Sadrian } 340231927Sadrian } 341231927Sadrian 342231927Sadrian /* 343231927Sadrian * If not doing enhanced DFS, choose the ext channel if 344231927Sadrian * it is stronger than the main channel 345231927Sadrian */ 346231927Sadrian if (doDfsExtCh && !doDfsEnhanced) { 347231927Sadrian if ((ext_rssi > rssi) && (ext_rssi < 128)) 348231927Sadrian rssi = ext_rssi; 349231927Sadrian } 350231927Sadrian 351231927Sadrian /* 352231927Sadrian * XXX what happens if the above code decides the RSSI 353231927Sadrian * XXX wasn't valid, an sets it to 0? 354231927Sadrian */ 355231927Sadrian 356231927Sadrian /* 357231927Sadrian * Fill out dfs_event structure. 358231927Sadrian */ 359231927Sadrian event->re_full_ts = fulltsf; 360231927Sadrian event->re_ts = rxs->rs_tstamp; 361231927Sadrian event->re_rssi = rssi; 362231927Sadrian event->re_dur = dur; 363231927Sadrian 364231927Sadrian event->re_flags = 0; 365231927Sadrian if (pri_found) 366231927Sadrian event->re_flags |= HAL_DFS_EVENT_PRICH; 367231927Sadrian if (ext_found) 368231927Sadrian event->re_flags |= HAL_DFS_EVENT_EXTCH; 369231927Sadrian if (early_ext) 370231927Sadrian event->re_flags |= HAL_DFS_EVENT_EXTEARLY; 371231927Sadrian if (is_dc) 372231927Sadrian event->re_flags |= HAL_DFS_EVENT_ISDC; 373231927Sadrian 374231927Sadrian return AH_TRUE; 375231927Sadrian} 376231927Sadrian 377231927Sadrian/* 378231927Sadrian * Return whether fast-clock is currently enabled for this 379231927Sadrian * channel. 380231927Sadrian */ 381231927SadrianHAL_BOOL 382231927Sadrianar5416IsFastClockEnabled(struct ath_hal *ah) 383231927Sadrian{ 384231927Sadrian struct ath_hal_private *ahp = AH_PRIVATE(ah); 385231927Sadrian 386231927Sadrian return IS_5GHZ_FAST_CLOCK_EN(ah, ahp->ah_curchan); 387231927Sadrian} 388