| 1 | /* |
| 2 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
| 3 | * Copyright (c) 2002-2008 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: ar5416_beacon.c,v 1.2 2013/09/12 12:08:49 martin Exp $ |
| 18 | */ |
| 19 | #include "opt_ah.h" |
| 20 | |
| 21 | #include "ah.h" |
| 22 | #include "ah_internal.h" |
| 23 | |
| 24 | #include "ar5416/ar5416.h" |
| 25 | #include "ar5416/ar5416reg.h" |
| 26 | #include "ar5416/ar5416phy.h" |
| 27 | |
| 28 | #define TU_TO_USEC(_tu) ((_tu) << 10) |
| 29 | |
| 30 | /* |
| 31 | * Initialize all of the hardware registers used to |
| 32 | * send beacons. Note that for station operation the |
| 33 | * driver calls ar5416SetStaBeaconTimers instead. |
| 34 | */ |
| 35 | void |
| 36 | ar5416SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt) |
| 37 | { |
| 38 | uint32_t bperiod; |
| 39 | |
| 40 | OS_REG_WRITE(ah, AR_NEXT_TBTT, TU_TO_USEC(bt->bt_nexttbtt)); |
| 41 | OS_REG_WRITE(ah, AR_NEXT_DBA, TU_TO_USEC(bt->bt_nextdba) >> 3); |
| 42 | OS_REG_WRITE(ah, AR_NEXT_SWBA, TU_TO_USEC(bt->bt_nextswba) >> 3); |
| 43 | OS_REG_WRITE(ah, AR_NEXT_NDP, TU_TO_USEC(bt->bt_nextatim)); |
| 44 | |
| 45 | bperiod = TU_TO_USEC(bt->bt_intval & HAL_BEACON_PERIOD); |
| 46 | OS_REG_WRITE(ah, AR5416_BEACON_PERIOD, bperiod); |
| 47 | OS_REG_WRITE(ah, AR_DBA_PERIOD, bperiod); |
| 48 | OS_REG_WRITE(ah, AR_SWBA_PERIOD, bperiod); |
| 49 | OS_REG_WRITE(ah, AR_NDP_PERIOD, bperiod); |
| 50 | |
| 51 | /* |
| 52 | * Reset TSF if required. |
| 53 | */ |
| 54 | if (bt->bt_intval & AR_BEACON_RESET_TSF) |
| 55 | ar5416ResetTsf(ah); |
| 56 | |
| 57 | /* enable timers */ |
| 58 | /* NB: flags == 0 handled specially for backwards compatibility */ |
| 59 | OS_REG_SET_BIT(ah, AR_TIMER_MODE, |
| 60 | bt->bt_flags != 0 ? bt->bt_flags : |
| 61 | AR_TIMER_MODE_TBTT | AR_TIMER_MODE_DBA | AR_TIMER_MODE_SWBA); |
| 62 | } |
| 63 | |
| 64 | /* |
| 65 | * Initializes all of the hardware registers used to |
| 66 | * send beacons. Note that for station operation the |
| 67 | * driver calls ar5212SetStaBeaconTimers instead. |
| 68 | */ |
| 69 | void |
| 70 | ar5416BeaconInit(struct ath_hal *ah, |
| 71 | uint32_t next_beacon, uint32_t beacon_period) |
| 72 | { |
| 73 | HAL_BEACON_TIMERS bt; |
| 74 | |
| 75 | bt.bt_nexttbtt = next_beacon; |
| 76 | /* |
| 77 | * TIMER1: in AP/adhoc mode this controls the DMA beacon |
| 78 | * alert timer; otherwise it controls the next wakeup time. |
| 79 | * TIMER2: in AP mode, it controls the SBA beacon alert |
| 80 | * interrupt; otherwise it sets the start of the next CFP. |
| 81 | */ |
| 82 | bt.bt_flags = 0; |
| 83 | switch (AH_PRIVATE(ah)->ah_opmode) { |
| 84 | case HAL_M_STA: |
| 85 | case HAL_M_MONITOR: |
| 86 | bt.bt_nextdba = 0xffff; |
| 87 | bt.bt_nextswba = 0x7ffff; |
| 88 | bt.bt_flags |= AR_TIMER_MODE_TBTT; |
| 89 | break; |
| 90 | case HAL_M_IBSS: |
| 91 | OS_REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ATIM_TXPOLICY); |
| 92 | bt.bt_flags |= AR_TIMER_MODE_NDP; |
| 93 | /* fall thru... */ |
| 94 | case HAL_M_HOSTAP: |
| 95 | bt.bt_nextdba = (next_beacon - |
| 96 | ath_hal_dma_beacon_response_time) << 3; /* 1/8 TU */ |
| 97 | bt.bt_nextswba = (next_beacon - |
| 98 | ath_hal_sw_beacon_response_time) << 3; /* 1/8 TU */ |
| 99 | bt.bt_flags |= AR_TIMER_MODE_TBTT |
| 100 | | AR_TIMER_MODE_DBA |
| 101 | | AR_TIMER_MODE_SWBA; |
| 102 | break; |
| 103 | } |
| 104 | /* |
| 105 | * Set the ATIM window |
| 106 | * Our hardware does not support an ATIM window of 0 |
| 107 | * (beacons will not work). If the ATIM windows is 0, |
| 108 | * force it to 1. |
| 109 | */ |
| 110 | bt.bt_nextatim = next_beacon + 1; |
| 111 | bt.bt_intval = beacon_period & |
| 112 | (AR_BEACON_PERIOD | AR_BEACON_RESET_TSF | AR_BEACON_EN); |
| 113 | ar5416SetBeaconTimers(ah, &bt); |
| 114 | } |
| 115 | |
| 116 | #define AR_BEACON_PERIOD_MAX 0xffff |
| 117 | |
| 118 | void |
| 119 | ar5416ResetStaBeaconTimers(struct ath_hal *ah) |
| 120 | { |
| 121 | uint32_t val; |
| 122 | |
| 123 | OS_REG_WRITE(ah, AR_NEXT_TBTT, 0); /* no beacons */ |
| 124 | val = OS_REG_READ(ah, AR_STA_ID1); |
| 125 | val |= AR_STA_ID1_PWR_SAV; /* XXX */ |
| 126 | /* tell the h/w that the associated AP is not PCF capable */ |
| 127 | OS_REG_WRITE(ah, AR_STA_ID1, |
| 128 | val & ~(AR_STA_ID1_USE_DEFANT | AR_STA_ID1_PCF)); |
| 129 | OS_REG_WRITE(ah, AR5416_BEACON_PERIOD, AR_BEACON_PERIOD_MAX); |
| 130 | OS_REG_WRITE(ah, AR_DBA_PERIOD, AR_BEACON_PERIOD_MAX); |
| 131 | } |
| 132 | |
| 133 | /* |
| 134 | * Set all the beacon related bits on the h/w for stations |
| 135 | * i.e. initializes the corresponding h/w timers; |
| 136 | * also tells the h/w whether to anticipate PCF beacons |
| 137 | */ |
| 138 | void |
| 139 | ar5416SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) |
| 140 | { |
| 141 | uint32_t nextTbtt,beaconintval, dtimperiod; |
| 142 | |
| 143 | HALASSERT(bs->bs_intval != 0); |
| 144 | |
| 145 | /* NB: no cfp setting since h/w automatically takes care */ |
| 146 | |
| 147 | OS_REG_WRITE(ah, AR_NEXT_TBTT, bs->bs_nexttbtt); |
| 148 | |
| 149 | /* |
| 150 | * Start the beacon timers by setting the BEACON register |
| 151 | * to the beacon interval; no need to write tim offset since |
| 152 | * h/w parses IEs. |
| 153 | */ |
| 154 | OS_REG_WRITE(ah, AR5416_BEACON_PERIOD, |
| 155 | TU_TO_USEC(bs->bs_intval & HAL_BEACON_PERIOD)); |
| 156 | OS_REG_WRITE(ah, AR_DBA_PERIOD, |
| 157 | TU_TO_USEC(bs->bs_intval & HAL_BEACON_PERIOD)); |
| 158 | |
| 159 | /* |
| 160 | * Configure the BMISS interrupt. Note that we |
| 161 | * assume the caller blocks interrupts while enabling |
| 162 | * the threshold. |
| 163 | */ |
| 164 | HALASSERT(bs->bs_bmissthreshold <= |
| 165 | (AR_RSSI_THR_BM_THR >> AR_RSSI_THR_BM_THR_S)); |
| 166 | OS_REG_RMW_FIELD(ah, AR_RSSI_THR, |
| 167 | AR_RSSI_THR_BM_THR, bs->bs_bmissthreshold); |
| 168 | |
| 169 | /* |
| 170 | * Program the sleep registers to correlate with the beacon setup. |
| 171 | */ |
| 172 | |
| 173 | /* |
| 174 | * Oahu beacons timers on the station were used for power |
| 175 | * save operation (waking up in anticipation of a beacon) |
| 176 | * and any CFP function; Venice does sleep/power-save timers |
| 177 | * differently - so this is the right place to set them up; |
| 178 | * don't think the beacon timers are used by venice sta hw |
| 179 | * for any useful purpose anymore |
| 180 | * Setup venice's sleep related timers |
| 181 | * Current implementation assumes sw processing of beacons - |
| 182 | * assuming an interrupt is generated every beacon which |
| 183 | * causes the hardware to become awake until the sw tells |
| 184 | * it to go to sleep again; beacon timeout is to allow for |
| 185 | * beacon jitter; cab timeout is max time to wait for cab |
| 186 | * after seeing the last DTIM or MORE CAB bit |
| 187 | */ |
| 188 | #define CAB_TIMEOUT_VAL 10 /* in TU */ |
| 189 | #define BEACON_TIMEOUT_VAL 10 /* in TU */ |
| 190 | #define SLEEP_SLOP 3 /* in TU */ |
| 191 | |
| 192 | /* |
| 193 | * For max powersave mode we may want to sleep for longer than a |
| 194 | * beacon period and not want to receive all beacons; modify the |
| 195 | * timers accordingly; make sure to align the next TIM to the |
| 196 | * next DTIM if we decide to wake for DTIMs only |
| 197 | */ |
| 198 | beaconintval = bs->bs_intval & HAL_BEACON_PERIOD; |
| 199 | HALASSERT(beaconintval != 0); |
| 200 | if (bs->bs_sleepduration > beaconintval) { |
| 201 | HALASSERT(roundup(bs->bs_sleepduration, beaconintval) == |
| 202 | bs->bs_sleepduration); |
| 203 | beaconintval = bs->bs_sleepduration; |
| 204 | } |
| 205 | dtimperiod = bs->bs_dtimperiod; |
| 206 | if (bs->bs_sleepduration > dtimperiod) { |
| 207 | HALASSERT(dtimperiod == 0 || |
| 208 | roundup(bs->bs_sleepduration, dtimperiod) == |
| 209 | bs->bs_sleepduration); |
| 210 | dtimperiod = bs->bs_sleepduration; |
| 211 | } |
| 212 | HALASSERT(beaconintval <= dtimperiod); |
| 213 | if (beaconintval == dtimperiod) |
| 214 | nextTbtt = bs->bs_nextdtim; |
| 215 | else |
| 216 | nextTbtt = bs->bs_nexttbtt; |
| 217 | |
| 218 | OS_REG_WRITE(ah, AR_NEXT_DTIM, |
| 219 | TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP)); |
| 220 | OS_REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(nextTbtt - SLEEP_SLOP)); |
| 221 | |
| 222 | /* cab timeout is now in 1/8 TU */ |
| 223 | OS_REG_WRITE(ah, AR_SLEEP1, |
| 224 | SM((CAB_TIMEOUT_VAL << 3), AR5416_SLEEP1_CAB_TIMEOUT) |
| 225 | | AR_SLEEP1_ASSUME_DTIM); |
| 226 | /* beacon timeout is now in 1/8 TU */ |
| 227 | OS_REG_WRITE(ah, AR_SLEEP2, |
| 228 | SM((BEACON_TIMEOUT_VAL << 3), AR5416_SLEEP2_BEACON_TIMEOUT)); |
| 229 | |
| 230 | OS_REG_WRITE(ah, AR_TIM_PERIOD, beaconintval); |
| 231 | OS_REG_WRITE(ah, AR_DTIM_PERIOD, dtimperiod); |
| 232 | OS_REG_SET_BIT(ah, AR_TIMER_MODE, |
| 233 | AR_TIMER_MODE_TBTT | AR_TIMER_MODE_TIM | AR_TIMER_MODE_DTIM); |
| 234 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next DTIM %d\n" , |
| 235 | __func__, bs->bs_nextdtim); |
| 236 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next beacon %d\n" , |
| 237 | __func__, nextTbtt); |
| 238 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: beacon period %d\n" , |
| 239 | __func__, beaconintval); |
| 240 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: DTIM period %d\n" , |
| 241 | __func__, dtimperiod); |
| 242 | #undef CAB_TIMEOUT_VAL |
| 243 | #undef BEACON_TIMEOUT_VAL |
| 244 | #undef SLEEP_SLOP |
| 245 | } |
| 246 | |