| 1 | /* |
| 2 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
| 3 | * Copyright (c) 2002-2004 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: ar5210_beacon.c,v 1.1.1.1 2008/12/11 04:46:27 alc Exp $ |
| 18 | */ |
| 19 | #include "opt_ah.h" |
| 20 | |
| 21 | #include "ah.h" |
| 22 | #include "ah_internal.h" |
| 23 | #include "ah_desc.h" |
| 24 | |
| 25 | #include "ar5210/ar5210.h" |
| 26 | #include "ar5210/ar5210reg.h" |
| 27 | #include "ar5210/ar5210desc.h" |
| 28 | |
| 29 | /* |
| 30 | * Initialize all of the hardware registers used to send beacons. |
| 31 | */ |
| 32 | void |
| 33 | ar5210SetBeaconTimers(struct ath_hal *ah, const HAL_BEACON_TIMERS *bt) |
| 34 | { |
| 35 | |
| 36 | OS_REG_WRITE(ah, AR_TIMER0, bt->bt_nexttbtt); |
| 37 | OS_REG_WRITE(ah, AR_TIMER1, bt->bt_nextdba); |
| 38 | OS_REG_WRITE(ah, AR_TIMER2, bt->bt_nextswba); |
| 39 | OS_REG_WRITE(ah, AR_TIMER3, bt->bt_nextatim); |
| 40 | /* |
| 41 | * Set the Beacon register after setting all timers. |
| 42 | */ |
| 43 | OS_REG_WRITE(ah, AR_BEACON, bt->bt_intval); |
| 44 | } |
| 45 | |
| 46 | /* |
| 47 | * Legacy api to Initialize all of the beacon registers. |
| 48 | */ |
| 49 | void |
| 50 | ar5210BeaconInit(struct ath_hal *ah, |
| 51 | uint32_t next_beacon, uint32_t beacon_period) |
| 52 | { |
| 53 | HAL_BEACON_TIMERS bt; |
| 54 | |
| 55 | bt.bt_nexttbtt = next_beacon; |
| 56 | |
| 57 | if (AH_PRIVATE(ah)->ah_opmode != HAL_M_STA) { |
| 58 | bt.bt_nextdba = (next_beacon - |
| 59 | ath_hal_dma_beacon_response_time) << 3; /* 1/8 TU */ |
| 60 | bt.bt_nextswba = (next_beacon - |
| 61 | ath_hal_sw_beacon_response_time) << 3; /* 1/8 TU */ |
| 62 | /* |
| 63 | * The SWBA interrupt is not used for beacons in ad hoc mode |
| 64 | * as we don't yet support ATIMs. So since the beacon never |
| 65 | * changes, the beacon descriptor is set up once and read |
| 66 | * into a special HW buffer, from which it will be |
| 67 | * automagically retrieved at each DMA Beacon Alert (DBA). |
| 68 | */ |
| 69 | |
| 70 | /* Set the ATIM window */ |
| 71 | bt.bt_nextatim = next_beacon + 0; /* NB: no ATIMs */ |
| 72 | } else { |
| 73 | bt.bt_nextdba = ~0; |
| 74 | bt.bt_nextswba = ~0; |
| 75 | bt.bt_nextatim = 1; |
| 76 | } |
| 77 | bt.bt_intval = beacon_period & |
| 78 | (AR_BEACON_PERIOD | AR_BEACON_RESET_TSF | AR_BEACON_EN); |
| 79 | ar5210SetBeaconTimers(ah, &bt); |
| 80 | } |
| 81 | |
| 82 | void |
| 83 | ar5210ResetStaBeaconTimers(struct ath_hal *ah) |
| 84 | { |
| 85 | uint32_t val; |
| 86 | |
| 87 | OS_REG_WRITE(ah, AR_TIMER0, 0); /* no beacons */ |
| 88 | val = OS_REG_READ(ah, AR_STA_ID1); |
| 89 | val |= AR_STA_ID1_NO_PSPOLL; /* XXX */ |
| 90 | /* tell the h/w that the associated AP is not PCF capable */ |
| 91 | OS_REG_WRITE(ah, AR_STA_ID1, |
| 92 | val & ~(AR_STA_ID1_DEFAULT_ANTENNA | AR_STA_ID1_PCF)); |
| 93 | OS_REG_WRITE(ah, AR_BEACON, AR_BEACON_PERIOD); |
| 94 | } |
| 95 | |
| 96 | /* |
| 97 | * Set all the beacon related bits on the h/w for stations |
| 98 | * i.e. initializes the corresponding h/w timers; |
| 99 | * also tells the h/w whether to anticipate PCF beacons |
| 100 | * |
| 101 | * dtim_count and cfp_count from the current beacon - their current |
| 102 | * values aren't necessarily maintained in the device struct |
| 103 | */ |
| 104 | void |
| 105 | ar5210SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) |
| 106 | { |
| 107 | struct ath_hal_5210 *ahp = AH5210(ah); |
| 108 | |
| 109 | HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: setting beacon timers\n" , __func__); |
| 110 | |
| 111 | HALASSERT(bs->bs_intval != 0); |
| 112 | /* if the AP will do PCF */ |
| 113 | if (bs->bs_cfpmaxduration != 0) { |
| 114 | /* tell the h/w that the associated AP is PCF capable */ |
| 115 | OS_REG_WRITE(ah, AR_STA_ID1, |
| 116 | (OS_REG_READ(ah, AR_STA_ID1) &~ AR_STA_ID1_DEFAULT_ANTENNA) |
| 117 | | AR_STA_ID1_PCF); |
| 118 | |
| 119 | /* set CFP_PERIOD(1.024ms) register */ |
| 120 | OS_REG_WRITE(ah, AR_CFP_PERIOD, bs->bs_cfpperiod); |
| 121 | |
| 122 | /* set CFP_DUR(1.024ms) register to max cfp duration */ |
| 123 | OS_REG_WRITE(ah, AR_CFP_DUR, bs->bs_cfpmaxduration); |
| 124 | |
| 125 | /* set TIMER2(128us) to anticipated time of next CFP */ |
| 126 | OS_REG_WRITE(ah, AR_TIMER2, bs->bs_cfpnext << 3); |
| 127 | } else { |
| 128 | /* tell the h/w that the associated AP is not PCF capable */ |
| 129 | OS_REG_WRITE(ah, AR_STA_ID1, |
| 130 | OS_REG_READ(ah, AR_STA_ID1) &~ (AR_STA_ID1_DEFAULT_ANTENNA | AR_STA_ID1_PCF)); |
| 131 | } |
| 132 | |
| 133 | /* |
| 134 | * Set TIMER0(1.024ms) to the anticipated time of the next beacon. |
| 135 | */ |
| 136 | OS_REG_WRITE(ah, AR_TIMER0, bs->bs_nexttbtt); |
| 137 | |
| 138 | /* |
| 139 | * Start the beacon timers by setting the BEACON register |
| 140 | * to the beacon interval; also write the tim offset which |
| 141 | * we should know by now. The code, in ar5211WriteAssocid, |
| 142 | * also sets the tim offset once the AID is known which can |
| 143 | * be left as such for now. |
| 144 | */ |
| 145 | OS_REG_WRITE(ah, AR_BEACON, |
| 146 | (OS_REG_READ(ah, AR_BEACON) &~ (AR_BEACON_PERIOD|AR_BEACON_TIM)) |
| 147 | | SM(bs->bs_intval, AR_BEACON_PERIOD) |
| 148 | | SM(bs->bs_timoffset ? bs->bs_timoffset + 4 : 0, AR_BEACON_TIM) |
| 149 | ); |
| 150 | |
| 151 | /* |
| 152 | * Configure the BMISS interrupt. Note that we |
| 153 | * assume the caller blocks interrupts while enabling |
| 154 | * the threshold. |
| 155 | */ |
| 156 | |
| 157 | /* |
| 158 | * Interrupt works only on Crete. |
| 159 | */ |
| 160 | if (AH_PRIVATE(ah)->ah_macRev < AR_SREV_CRETE) |
| 161 | return; |
| 162 | /* |
| 163 | * Counter is only 3-bits. |
| 164 | * Count of 0 with BMISS interrupt enabled will hang the system |
| 165 | * with too many interrupts |
| 166 | */ |
| 167 | if (AH_PRIVATE(ah)->ah_macRev >= AR_SREV_CRETE && |
| 168 | (bs->bs_bmissthreshold&7) == 0) { |
| 169 | #ifdef AH_DEBUG |
| 170 | ath_hal_printf(ah, "%s: invalid beacon miss threshold %u\n" , |
| 171 | __func__, bs->bs_bmissthreshold); |
| 172 | #endif |
| 173 | return; |
| 174 | } |
| 175 | #define BMISS_MAX (AR_RSSI_THR_BM_THR >> AR_RSSI_THR_BM_THR_S) |
| 176 | /* |
| 177 | * Configure the BMISS interrupt. Note that we |
| 178 | * assume the caller blocks interrupts while enabling |
| 179 | * the threshold. |
| 180 | * |
| 181 | * NB: the beacon miss count field is only 3 bits which |
| 182 | * is much smaller than what's found on later parts; |
| 183 | * clamp overflow values as a safeguard. |
| 184 | */ |
| 185 | ahp->ah_rssiThr = (ahp->ah_rssiThr &~ AR_RSSI_THR_BM_THR) |
| 186 | | SM(bs->bs_bmissthreshold > BMISS_MAX ? |
| 187 | BMISS_MAX : bs->bs_bmissthreshold, |
| 188 | AR_RSSI_THR_BM_THR); |
| 189 | OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr); |
| 190 | #undef BMISS_MAX |
| 191 | } |
| 192 | |