1 /* 2 * Copyright (c) 2010-2011 Atheros Communications, Inc. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 * 16 * $FreeBSD$ 17 */ 18 #include "opt_ah.h" 19 20 #include "ah.h" 21 #include "ah_internal.h" 22 #include "ah_devid.h" 23 #include "ah_desc.h" /* NB: for HAL_PHYERR* */ 24 25 #include "ar5416/ar5416.h" 26 #include "ar5416/ar5416reg.h" 27 #include "ar5416/ar5416phy.h" 28 29 #include "ah_eeprom_v14.h" /* for owl_get_ntxchains() */ 30 31 /* 32 * Get the radar parameter values and return them in the pe 33 * structure 34 */ 35 void 36 ar5416GetDfsThresh(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) 37 { 38 uint32_t val, temp; 39 40 val = OS_REG_READ(ah, AR_PHY_RADAR_0); 41 42 temp = MS(val,AR_PHY_RADAR_0_FIRPWR); 43 temp |= 0xFFFFFF80; 44 pe->pe_firpwr = temp; 45 pe->pe_rrssi = MS(val, AR_PHY_RADAR_0_RRSSI); 46 pe->pe_height = MS(val, AR_PHY_RADAR_0_HEIGHT); 47 pe->pe_prssi = MS(val, AR_PHY_RADAR_0_PRSSI); 48 pe->pe_inband = MS(val, AR_PHY_RADAR_0_INBAND); 49 50 /* RADAR_1 values */ 51 val = OS_REG_READ(ah, AR_PHY_RADAR_1); 52 pe->pe_relpwr = MS(val, AR_PHY_RADAR_1_RELPWR_THRESH); 53 pe->pe_relstep = MS(val, AR_PHY_RADAR_1_RELSTEP_THRESH); 54 pe->pe_maxlen = MS(val, AR_PHY_RADAR_1_MAXLEN); 55 56 pe->pe_extchannel = !! (OS_REG_READ(ah, AR_PHY_RADAR_EXT) & 57 AR_PHY_RADAR_EXT_ENA); 58 59 pe->pe_usefir128 = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & 60 AR_PHY_RADAR_1_USE_FIR128); 61 pe->pe_blockradar = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & 62 AR_PHY_RADAR_1_BLOCK_CHECK); 63 pe->pe_enmaxrssi = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & 64 AR_PHY_RADAR_1_MAX_RRSSI); 65 pe->pe_enabled = !! 66 (OS_REG_READ(ah, AR_PHY_RADAR_0) & AR_PHY_RADAR_0_ENA); 67 pe->pe_enrelpwr = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & 68 AR_PHY_RADAR_1_RELPWR_ENA); 69 pe->pe_en_relstep_check = !! (OS_REG_READ(ah, AR_PHY_RADAR_1) & 70 AR_PHY_RADAR_1_RELSTEP_CHECK); 71 } 72 73 /* 74 * Enable radar detection and set the radar parameters per the 75 * values in pe 76 */ 77 void 78 ar5416EnableDfs(struct ath_hal *ah, HAL_PHYERR_PARAM *pe) 79 { 80 uint32_t val; 81 82 val = OS_REG_READ(ah, AR_PHY_RADAR_0); 83 84 if (pe->pe_firpwr != HAL_PHYERR_PARAM_NOVAL) { 85 val &= ~AR_PHY_RADAR_0_FIRPWR; 86 val |= SM(pe->pe_firpwr, AR_PHY_RADAR_0_FIRPWR); 87 } 88 if (pe->pe_rrssi != HAL_PHYERR_PARAM_NOVAL) { 89 val &= ~AR_PHY_RADAR_0_RRSSI; 90 val |= SM(pe->pe_rrssi, AR_PHY_RADAR_0_RRSSI); 91 } 92 if (pe->pe_height != HAL_PHYERR_PARAM_NOVAL) { 93 val &= ~AR_PHY_RADAR_0_HEIGHT; 94 val |= SM(pe->pe_height, AR_PHY_RADAR_0_HEIGHT); 95 } 96 if (pe->pe_prssi != HAL_PHYERR_PARAM_NOVAL) { 97 val &= ~AR_PHY_RADAR_0_PRSSI; 98 val |= SM(pe->pe_prssi, AR_PHY_RADAR_0_PRSSI); 99 } 100 if (pe->pe_inband != HAL_PHYERR_PARAM_NOVAL) { 101 val &= ~AR_PHY_RADAR_0_INBAND; 102 val |= SM(pe->pe_inband, AR_PHY_RADAR_0_INBAND); 103 } 104 105 /*Enable FFT data*/ 106 val |= AR_PHY_RADAR_0_FFT_ENA; 107 OS_REG_WRITE(ah, AR_PHY_RADAR_0, val); 108 109 /* Implicitly enable */ 110 if (pe->pe_enabled == 1) 111 OS_REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA); 112 else if (pe->pe_enabled == 0) 113 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_ENA); 114 115 if (pe->pe_usefir128 == 1) 116 OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128); 117 else if (pe->pe_usefir128 == 0) 118 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_USE_FIR128); 119 120 if (pe->pe_enmaxrssi == 1) 121 OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI); 122 else if (pe->pe_enmaxrssi == 0) 123 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_MAX_RRSSI); 124 125 if (pe->pe_blockradar == 1) 126 OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK); 127 else if (pe->pe_blockradar == 0) 128 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, AR_PHY_RADAR_1_BLOCK_CHECK); 129 130 if (pe->pe_relstep != HAL_PHYERR_PARAM_NOVAL) { 131 val = OS_REG_READ(ah, AR_PHY_RADAR_1); 132 val &= ~AR_PHY_RADAR_1_RELSTEP_THRESH; 133 val |= SM(pe->pe_relstep, AR_PHY_RADAR_1_RELSTEP_THRESH); 134 OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); 135 } 136 if (pe->pe_relpwr != HAL_PHYERR_PARAM_NOVAL) { 137 val = OS_REG_READ(ah, AR_PHY_RADAR_1); 138 val &= ~AR_PHY_RADAR_1_RELPWR_THRESH; 139 val |= SM(pe->pe_relpwr, AR_PHY_RADAR_1_RELPWR_THRESH); 140 OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); 141 } 142 143 if (pe->pe_en_relstep_check == 1) 144 OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, 145 AR_PHY_RADAR_1_RELSTEP_CHECK); 146 else if (pe->pe_en_relstep_check == 0) 147 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, 148 AR_PHY_RADAR_1_RELSTEP_CHECK); 149 150 if (pe->pe_enrelpwr == 1) 151 OS_REG_SET_BIT(ah, AR_PHY_RADAR_1, 152 AR_PHY_RADAR_1_RELPWR_ENA); 153 else if (pe->pe_enrelpwr == 0) 154 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_1, 155 AR_PHY_RADAR_1_RELPWR_ENA); 156 157 if (pe->pe_maxlen != HAL_PHYERR_PARAM_NOVAL) { 158 val = OS_REG_READ(ah, AR_PHY_RADAR_1); 159 val &= ~AR_PHY_RADAR_1_MAXLEN; 160 val |= SM(pe->pe_maxlen, AR_PHY_RADAR_1_MAXLEN); 161 OS_REG_WRITE(ah, AR_PHY_RADAR_1, val); 162 } 163 164 /* 165 * Enable HT/40 if the upper layer asks; 166 * it should check the channel is HT/40 and HAL_CAP_EXT_CHAN_DFS 167 * is available. 168 */ 169 if (pe->pe_extchannel == 1) 170 OS_REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); 171 else if (pe->pe_extchannel == 0) 172 OS_REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); 173 } 174 175 /* 176 * Extract the radar event information from the given phy error. 177 * 178 * Returns AH_TRUE if the phy error was actually a phy error, 179 * AH_FALSE if the phy error wasn't a phy error. 180 */ 181 182 /* Flags for pulse_bw_info */ 183 #define PRI_CH_RADAR_FOUND 0x01 184 #define EXT_CH_RADAR_FOUND 0x02 185 #define EXT_CH_RADAR_EARLY_FOUND 0x04 186 187 HAL_BOOL 188 ar5416ProcessRadarEvent(struct ath_hal *ah, struct ath_rx_status *rxs, 189 uint64_t fulltsf, const char *buf, HAL_DFS_EVENT *event) 190 { 191 HAL_BOOL doDfsExtCh; 192 HAL_BOOL doDfsEnhanced; 193 HAL_BOOL doDfsCombinedRssi; 194 195 uint8_t rssi = 0, ext_rssi = 0; 196 uint8_t pulse_bw_info = 0, pulse_length_ext = 0, pulse_length_pri = 0; 197 uint32_t dur = 0; 198 int pri_found = 1, ext_found = 0; 199 int early_ext = 0; 200 int is_dc = 0; 201 uint16_t datalen; /* length from the RX status field */ 202 203 /* Check whether the given phy error is a radar event */ 204 if ((rxs->rs_phyerr != HAL_PHYERR_RADAR) && 205 (rxs->rs_phyerr != HAL_PHYERR_FALSE_RADAR_EXT)) { 206 return AH_FALSE; 207 } 208 209 /* Grab copies of the capabilities; just to make the code clearer */ 210 doDfsExtCh = AH_PRIVATE(ah)->ah_caps.halExtChanDfsSupport; 211 doDfsEnhanced = AH_PRIVATE(ah)->ah_caps.halEnhancedDfsSupport; 212 doDfsCombinedRssi = AH_PRIVATE(ah)->ah_caps.halUseCombinedRadarRssi; 213 214 datalen = rxs->rs_datalen; 215 216 /* If hardware supports it, use combined RSSI, else use chain 0 RSSI */ 217 if (doDfsCombinedRssi) 218 rssi = (uint8_t) rxs->rs_rssi; 219 else 220 rssi = (uint8_t) rxs->rs_rssi_ctl[0]; 221 222 /* Set this; but only use it if doDfsExtCh is set */ 223 ext_rssi = (uint8_t) rxs->rs_rssi_ext[0]; 224 225 /* Cap it at 0 if the RSSI is a negative number */ 226 if (rssi & 0x80) 227 rssi = 0; 228 229 if (ext_rssi & 0x80) 230 ext_rssi = 0; 231 232 /* 233 * Fetch the relevant data from the frame 234 */ 235 if (doDfsExtCh) { 236 if (datalen < 3) 237 return AH_FALSE; 238 239 /* Last three bytes of the frame are of interest */ 240 pulse_length_pri = *(buf + datalen - 3); 241 pulse_length_ext = *(buf + datalen - 2); 242 pulse_bw_info = *(buf + datalen - 1); 243 HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, ext_rssi=%d, pulse_length_pri=%d," 244 " pulse_length_ext=%d, pulse_bw_info=%x\n", 245 __func__, rssi, ext_rssi, pulse_length_pri, pulse_length_ext, 246 pulse_bw_info); 247 } else { 248 /* The pulse width is byte 0 of the data */ 249 if (datalen >= 1) 250 dur = ((uint8_t) buf[0]) & 0xff; 251 else 252 dur = 0; 253 254 if (dur == 0 && rssi == 0) { 255 HALDEBUG(ah, HAL_DEBUG_DFS, "%s: dur and rssi are 0\n", __func__); 256 return AH_FALSE; 257 } 258 259 HALDEBUG(ah, HAL_DEBUG_DFS, "%s: rssi=%d, dur=%d\n", __func__, rssi, dur); 260 261 /* Single-channel only */ 262 pri_found = 1; 263 ext_found = 0; 264 } 265 266 /* 267 * If doing extended channel data, pulse_bw_info must 268 * have one of the flags set. 269 */ 270 if (doDfsExtCh && pulse_bw_info == 0x0) 271 return AH_FALSE; 272 273 /* 274 * If the extended channel data is available, calculate 275 * which to pay attention to. 276 */ 277 if (doDfsExtCh) { 278 /* If pulse is on DC, take the larger duration of the two */ 279 if ((pulse_bw_info & EXT_CH_RADAR_FOUND) && 280 (pulse_bw_info & PRI_CH_RADAR_FOUND)) { 281 is_dc = 1; 282 if (pulse_length_ext > pulse_length_pri) { 283 dur = pulse_length_ext; 284 pri_found = 0; 285 ext_found = 1; 286 } else { 287 dur = pulse_length_pri; 288 pri_found = 1; 289 ext_found = 0; 290 } 291 } else if (pulse_bw_info & EXT_CH_RADAR_EARLY_FOUND) { 292 dur = pulse_length_ext; 293 pri_found = 0; 294 ext_found = 1; 295 early_ext = 1; 296 } else if (pulse_bw_info & PRI_CH_RADAR_FOUND) { 297 dur = pulse_length_pri; 298 pri_found = 1; 299 ext_found = 0; 300 } else if (pulse_bw_info & EXT_CH_RADAR_FOUND) { 301 dur = pulse_length_ext; 302 pri_found = 0; 303 ext_found = 1; 304 } 305 306 } 307 308 /* 309 * For enhanced DFS (Merlin and later), pulse_bw_info has 310 * implications for selecting the correct RSSI value. 311 */ 312 if (doDfsEnhanced) { 313 switch (pulse_bw_info & 0x03) { 314 case 0: 315 /* No radar? */ 316 rssi = 0; 317 break; 318 case PRI_CH_RADAR_FOUND: 319 /* Radar in primary channel */ 320 /* Cannot use ctrl channel RSSI if ext channel is stronger */ 321 if (ext_rssi >= (rssi + 3)) { 322 rssi = 0; 323 }; 324 break; 325 case EXT_CH_RADAR_FOUND: 326 /* Radar in extended channel */ 327 /* Cannot use ext channel RSSI if ctrl channel is stronger */ 328 if (rssi >= (ext_rssi + 12)) { 329 rssi = 0; 330 } else { 331 rssi = ext_rssi; 332 } 333 break; 334 case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND): 335 /* When both are present, use stronger one */ 336 if (rssi < ext_rssi) 337 rssi = ext_rssi; 338 break; 339 } 340 } 341 342 /* 343 * If not doing enhanced DFS, choose the ext channel if 344 * it is stronger than the main channel 345 */ 346 if (doDfsExtCh && !doDfsEnhanced) { 347 if ((ext_rssi > rssi) && (ext_rssi < 128)) 348 rssi = ext_rssi; 349 } 350 351 /* 352 * XXX what happens if the above code decides the RSSI 353 * XXX wasn't valid, an sets it to 0? 354 */ 355 356 /* 357 * Fill out dfs_event structure. 358 */ 359 event->re_full_ts = fulltsf; 360 event->re_ts = rxs->rs_tstamp; 361 event->re_rssi = rssi; 362 event->re_dur = dur; 363 364 event->re_flags = 0; 365 if (pri_found) 366 event->re_flags |= HAL_DFS_EVENT_PRICH; 367 if (ext_found) 368 event->re_flags |= HAL_DFS_EVENT_EXTCH; 369 if (early_ext) 370 event->re_flags |= HAL_DFS_EVENT_EXTEARLY; 371 if (is_dc) 372 event->re_flags |= HAL_DFS_EVENT_ISDC; 373 374 return AH_TRUE; 375 } 376 377 /* 378 * Return whether fast-clock is currently enabled for this 379 * channel. 380 */ 381 HAL_BOOL 382 ar5416IsFastClockEnabled(struct ath_hal *ah) 383 { 384 struct ath_hal_private *ahp = AH_PRIVATE(ah); 385 386 return IS_5GHZ_FAST_CLOCK_EN(ah, ahp->ah_curchan); 387 } 388