diff options
Diffstat (limited to 'sys/dev/ic/ar5211.c')
-rw-r--r-- | sys/dev/ic/ar5211.c | 2488 |
1 files changed, 2488 insertions, 0 deletions
diff --git a/sys/dev/ic/ar5211.c b/sys/dev/ic/ar5211.c new file mode 100644 index 00000000000..0adaf5b861b --- /dev/null +++ b/sys/dev/ic/ar5211.c @@ -0,0 +1,2488 @@ +/* $OpenBSD: ar5211.c,v 1.1 2005/02/25 22:25:30 reyk Exp $ */ + +/* + * Copyright (c) 2004, 2005 Reyk Floeter <reyk@vantronix.net> + * + * Permission to use, copy, modify, and 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. + */ + +/* + * HAL interface for the Atheros AR5001 Wireless LAN chipset + * (AR5211 + AR5111). + */ + +#include <dev/ic/ar5xxx.h> +#include <dev/ic/ar5211reg.h> +#include <dev/ic/ar5211var.h> + +HAL_BOOL ar5k_ar5211_nic_reset(struct ath_hal *, u_int32_t); +HAL_BOOL ar5k_ar5211_nic_wakeup(struct ath_hal *, u_int16_t); +u_int16_t ar5k_ar5211_radio_revision(struct ath_hal *, HAL_CHIP); +const void ar5k_ar5211_fill(struct ath_hal *); + +/* + * Initial register setting for the AR5211 + */ +static const struct ar5k_ini ar5211_ini[] = + AR5K_AR5211_INI; +static const struct ar5k_ar5211_ini_mode ar5211_mode[] = + AR5K_AR5211_INI_MODE; + +AR5K_HAL_FUNCTIONS(extern, ar5k_ar5211,); + +const void +ar5k_ar5211_fill(hal) + struct ath_hal *hal; +{ + hal->ah_magic = AR5K_AR5211_MAGIC; + + /* + * Init/Exit functions + */ + AR5K_HAL_FUNCTION(hal, ar5211, getRateTable); + AR5K_HAL_FUNCTION(hal, ar5211, detach); + + /* + * Reset functions + */ + AR5K_HAL_FUNCTION(hal, ar5211, reset); + AR5K_HAL_FUNCTION(hal, ar5211, setPCUConfig); + AR5K_HAL_FUNCTION(hal, ar5211, perCalibration); + + /* + * TX functions + */ + AR5K_HAL_FUNCTION(hal, ar5211, updateTxTrigLevel); + AR5K_HAL_FUNCTION(hal, ar5211, setupTxQueue); + AR5K_HAL_FUNCTION(hal, ar5211, setTxQueueProps); + AR5K_HAL_FUNCTION(hal, ar5211, releaseTxQueue); + AR5K_HAL_FUNCTION(hal, ar5211, resetTxQueue); + AR5K_HAL_FUNCTION(hal, ar5211, getTxDP); + AR5K_HAL_FUNCTION(hal, ar5211, setTxDP); + AR5K_HAL_FUNCTION(hal, ar5211, startTxDma); + AR5K_HAL_FUNCTION(hal, ar5211, stopTxDma); + AR5K_HAL_FUNCTION(hal, ar5211, setupTxDesc); + AR5K_HAL_FUNCTION(hal, ar5211, setupXTxDesc); + AR5K_HAL_FUNCTION(hal, ar5211, fillTxDesc); + AR5K_HAL_FUNCTION(hal, ar5211, procTxDesc); + AR5K_HAL_FUNCTION(hal, ar5211, hasVEOL); + + /* + * RX functions + */ + AR5K_HAL_FUNCTION(hal, ar5211, getRxDP); + AR5K_HAL_FUNCTION(hal, ar5211, setRxDP); + AR5K_HAL_FUNCTION(hal, ar5211, enableReceive); + AR5K_HAL_FUNCTION(hal, ar5211, stopDmaReceive); + AR5K_HAL_FUNCTION(hal, ar5211, startPcuReceive); + AR5K_HAL_FUNCTION(hal, ar5211, stopPcuReceive); + AR5K_HAL_FUNCTION(hal, ar5211, setMulticastFilter); + AR5K_HAL_FUNCTION(hal, ar5211, setMulticastFilterIndex); + AR5K_HAL_FUNCTION(hal, ar5211, clrMulticastFilterIndex); + AR5K_HAL_FUNCTION(hal, ar5211, getRxFilter); + AR5K_HAL_FUNCTION(hal, ar5211, setRxFilter); + AR5K_HAL_FUNCTION(hal, ar5211, setupRxDesc); + AR5K_HAL_FUNCTION(hal, ar5211, procRxDesc); + AR5K_HAL_FUNCTION(hal, ar5211, rxMonitor); + + /* + * Misc functions + */ + AR5K_HAL_FUNCTION(hal, ar5211, dumpState); + AR5K_HAL_FUNCTION(hal, ar5211, getDiagState); + AR5K_HAL_FUNCTION(hal, ar5211, getMacAddress); + AR5K_HAL_FUNCTION(hal, ar5211, setMacAddress); + AR5K_HAL_FUNCTION(hal, ar5211, setRegulatoryDomain); + AR5K_HAL_FUNCTION(hal, ar5211, setLedState); + AR5K_HAL_FUNCTION(hal, ar5211, writeAssocid); + AR5K_HAL_FUNCTION(hal, ar5211, gpioCfgInput); + AR5K_HAL_FUNCTION(hal, ar5211, gpioCfgOutput); + AR5K_HAL_FUNCTION(hal, ar5211, gpioGet); + AR5K_HAL_FUNCTION(hal, ar5211, gpioSet); + AR5K_HAL_FUNCTION(hal, ar5211, gpioSetIntr); + AR5K_HAL_FUNCTION(hal, ar5211, getTsf32); + AR5K_HAL_FUNCTION(hal, ar5211, getTsf64); + AR5K_HAL_FUNCTION(hal, ar5211, resetTsf); + AR5K_HAL_FUNCTION(hal, ar5211, getRegDomain); + AR5K_HAL_FUNCTION(hal, ar5211, detectCardPresent); + AR5K_HAL_FUNCTION(hal, ar5211, updateMibCounters); + AR5K_HAL_FUNCTION(hal, ar5211, getRfGain); + AR5K_HAL_FUNCTION(hal, ar5211, setSlotTime); + AR5K_HAL_FUNCTION(hal, ar5211, getSlotTime); + AR5K_HAL_FUNCTION(hal, ar5211, setAckTimeout); + AR5K_HAL_FUNCTION(hal, ar5211, getAckTimeout); + AR5K_HAL_FUNCTION(hal, ar5211, setCTSTimeout); + AR5K_HAL_FUNCTION(hal, ar5211, getCTSTimeout); + + /* + * Key table (WEP) functions + */ + AR5K_HAL_FUNCTION(hal, ar5211, isHwCipherSupported); + AR5K_HAL_FUNCTION(hal, ar5211, getKeyCacheSize); + AR5K_HAL_FUNCTION(hal, ar5211, resetKeyCacheEntry); + AR5K_HAL_FUNCTION(hal, ar5211, isKeyCacheEntryValid); + AR5K_HAL_FUNCTION(hal, ar5211, setKeyCacheEntry); + AR5K_HAL_FUNCTION(hal, ar5211, setKeyCacheEntryMac); + + /* + * Power management functions + */ + AR5K_HAL_FUNCTION(hal, ar5211, setPowerMode); + AR5K_HAL_FUNCTION(hal, ar5211, getPowerMode); + AR5K_HAL_FUNCTION(hal, ar5211, queryPSPollSupport); + AR5K_HAL_FUNCTION(hal, ar5211, initPSPoll); + AR5K_HAL_FUNCTION(hal, ar5211, enablePSPoll); + AR5K_HAL_FUNCTION(hal, ar5211, disablePSPoll); + + /* + * Beacon functions + */ + AR5K_HAL_FUNCTION(hal, ar5211, beaconInit); + AR5K_HAL_FUNCTION(hal, ar5211, setStationBeaconTimers); + AR5K_HAL_FUNCTION(hal, ar5211, resetStationBeaconTimers); + AR5K_HAL_FUNCTION(hal, ar5211, waitForBeaconDone); + + /* + * Interrupt functions + */ + AR5K_HAL_FUNCTION(hal, ar5211, isInterruptPending); + AR5K_HAL_FUNCTION(hal, ar5211, getPendingInterrupts); + AR5K_HAL_FUNCTION(hal, ar5211, getInterrupts); + AR5K_HAL_FUNCTION(hal, ar5211, setInterrupts); + + /* + * Chipset functions (ar5k-specific, non-HAL) + */ + AR5K_HAL_FUNCTION(hal, ar5211, get_capabilities); + AR5K_HAL_FUNCTION(hal, ar5211, radar_alert); + + /* + * EEPROM access + */ + AR5K_HAL_FUNCTION(hal, ar5211, eeprom_is_busy); + AR5K_HAL_FUNCTION(hal, ar5211, eeprom_read); + AR5K_HAL_FUNCTION(hal, ar5211, eeprom_write); +} + +struct ath_hal * +ar5k_ar5211_attach(device, sc, st, sh, status) + u_int16_t device; + void *sc; + bus_space_tag_t st; + bus_space_handle_t sh; + int *status; +{ + struct ath_hal *hal = (struct ath_hal*) sc; + u_int8_t mac[IEEE80211_ADDR_LEN]; + u_int32_t srev; + + ar5k_ar5211_fill(hal); + + /* Bring device out of sleep and reset it's units */ + if (ar5k_ar5211_nic_wakeup(hal, AR5K_INIT_MODE) != AH_TRUE) + return (NULL); + + /* Get MAC, PHY and RADIO revisions */ + srev = AR5K_REG_READ(AR5K_AR5211_SREV) & AR5K_AR5211_SREV_M; + hal->ah_mac_version = srev & AR5K_AR5211_SREV_VERSION; + hal->ah_mac_revision = srev & AR5K_AR5211_SREV_REVISION; + hal->ah_phy_revision = AR5K_REG_READ(AR5K_AR5211_PHY_CHIP_ID) & + 0x00ffffffff; + + hal->ah_radio_5ghz_revision = + ar5k_ar5211_radio_revision(hal, HAL_CHIP_5GHZ); + + /* Get the 2GHz radio revision if it's supported */ + if (hal->ah_mac_version >= AR5K_SREV_VER_AR5211) + hal->ah_radio_2ghz_revision = + ar5k_ar5211_radio_revision(hal, HAL_CHIP_2GHZ); + + /* Identify the chipset (this has to be done in an early step) */ + hal->ah_version = AR5K_AR5211; + hal->ah_radio = AR5K_AR5111; + hal->ah_phy = AR5K_AR5211_PHY(0); + + memset(&mac, 0xff, sizeof(mac)); + ar5k_ar5211_writeAssocid(hal, mac, 0, 0); + ar5k_ar5211_getMacAddress(hal, mac); + ar5k_ar5211_setPCUConfig(hal); + + return (hal); +} + +HAL_BOOL +ar5k_ar5211_nic_reset(hal, val) + struct ath_hal *hal; + u_int32_t val; +{ + HAL_BOOL ret = AH_FALSE; + u_int32_t mask = val ? val : ~0; + + /* Read-and-clear */ + AR5K_REG_READ(AR5K_AR5211_RXDP); + + /* + * Reset the device and wait until success + */ + AR5K_REG_WRITE(AR5K_AR5211_RC, val); + + /* Wait at least 128 PCI clocks */ + AR5K_DELAY(15); + + val &= + AR5K_AR5211_RC_PCU | AR5K_AR5211_RC_BB; + + mask &= + AR5K_AR5211_RC_PCU | AR5K_AR5211_RC_BB; + + ret = ar5k_register_timeout(hal, AR5K_AR5211_RC, mask, val, AH_FALSE); + + /* + * Reset configuration register + */ + if ((val & AR5K_AR5211_RC_PCU) == 0) + AR5K_REG_WRITE(AR5K_AR5211_CFG, AR5K_AR5211_INIT_CFG); + + return (ret); +} + +HAL_BOOL +ar5k_ar5211_nic_wakeup(hal, flags) + struct ath_hal *hal; + u_int16_t flags; +{ + u_int32_t turbo, mode, clock; + + turbo = 0; + mode = 0; + clock = 0; + + /* + * Get channel mode flags + */ + + if (flags & IEEE80211_CHAN_2GHZ) { + mode |= AR5K_AR5211_PHY_MODE_FREQ_2GHZ; + clock |= AR5K_AR5211_PHY_PLL_44MHZ; + } else if (flags & IEEE80211_CHAN_5GHZ) { + mode |= AR5K_AR5211_PHY_MODE_FREQ_5GHZ; + clock |= AR5K_AR5211_PHY_PLL_40MHZ; + } else { + AR5K_PRINT("invalid radio frequency mode\n"); + return (AH_FALSE); + } + + if ((flags & IEEE80211_CHAN_CCK) || + (flags & IEEE80211_CHAN_DYN)) { + /* Dynamic OFDM/CCK is not supported by the AR5211 */ + mode |= AR5K_AR5211_PHY_MODE_MOD_CCK; + } else if (flags & IEEE80211_CHAN_OFDM) { + mode |= AR5K_AR5211_PHY_MODE_MOD_OFDM; + } else { + AR5K_PRINT("invalid radio frequency mode\n"); + return (AH_FALSE); + } + + if (flags & IEEE80211_CHAN_TURBO) { + turbo = AR5K_AR5211_PHY_TURBO_MODE | + AR5K_AR5211_PHY_TURBO_SHORT; + } + + /* + * Reset and wakeup the device + */ + + /* ...reset chipset and PCI device */ + if (ar5k_ar5211_nic_reset(hal, + AR5K_AR5211_RC_CHIP | AR5K_AR5211_RC_PCI) == AH_FALSE) { + AR5K_PRINT("failed to reset the AR5211 + PCI chipset\n"); + return (AH_FALSE); + } + + /* ...wakeup */ + if (ar5k_ar5211_setPowerMode(hal, + HAL_PM_AWAKE, AH_TRUE, 0) == AH_FALSE) { + AR5K_PRINT("failed to resume the AR5211 (again)\n"); + return (AH_FALSE); + } + + /* ...final warm reset */ + if (ar5k_ar5211_nic_reset(hal, 0) == AH_FALSE) { + AR5K_PRINT("failed to warm reset the AR5211\n"); + return (AH_FALSE); + } + + /* ...set the PHY operating mode */ + AR5K_REG_WRITE(AR5K_AR5211_PHY_PLL, clock); + AR5K_DELAY(300); + + AR5K_REG_WRITE(AR5K_AR5211_PHY_MODE, mode); + AR5K_REG_WRITE(AR5K_AR5211_PHY_TURBO, turbo); + + return (AH_TRUE); +} + +u_int16_t +ar5k_ar5211_radio_revision(hal, chip) + struct ath_hal *hal; + HAL_CHIP chip; +{ + int i; + u_int32_t srev; + u_int16_t ret; + + /* + * Set the radio chip access register + */ + switch (chip) { + case HAL_CHIP_2GHZ: + AR5K_REG_WRITE(AR5K_AR5211_PHY(0), AR5K_AR5211_PHY_SHIFT_2GHZ); + break; + case HAL_CHIP_5GHZ: + AR5K_REG_WRITE(AR5K_AR5211_PHY(0), AR5K_AR5211_PHY_SHIFT_5GHZ); + break; + default: + return (0); + } + + AR5K_DELAY(2000); + + /* ...wait until PHY is ready and read the selected radio revision */ + AR5K_REG_WRITE(AR5K_AR5211_PHY(0x34), 0x00001c16); + + for (i = 0; i < 8; i++) + AR5K_REG_WRITE(AR5K_AR5211_PHY(0x20), 0x00010000); + srev = (AR5K_REG_READ(AR5K_AR5211_PHY(0x100)) >> 24) & 0xff; + + ret = ar5k_bitswap(((srev & 0xf0) >> 4) | ((srev & 0x0f) << 4), 8); + + /* Reset to the 5GHz mode */ + AR5K_REG_WRITE(AR5K_AR5211_PHY(0), AR5K_AR5211_PHY_SHIFT_5GHZ); + + return (ret); +} + +const HAL_RATE_TABLE * +ar5k_ar5211_getRateTable(hal, mode) + struct ath_hal *hal; + u_int mode; +{ + switch (mode) { + case HAL_MODE_11A: + return (&hal->ah_rt_11a); + case HAL_MODE_TURBO: + return (&hal->ah_rt_turbo); + case HAL_MODE_11B: + return (&hal->ah_rt_11b); + case HAL_MODE_11G: + case HAL_MODE_PUREG: + return (&hal->ah_rt_11g); + default: + return (NULL); + } + + return (NULL); +} + +void +ar5k_ar5211_detach(hal) + struct ath_hal *hal; +{ + /* + * Free HAL structure, assume interrupts are down + */ + free(hal, M_DEVBUF); +} + +HAL_BOOL +ar5k_ar5211_reset(hal, op_mode, channel, change_channel, status) + struct ath_hal *hal; + HAL_OPMODE op_mode; + HAL_CHANNEL *channel; + HAL_BOOL change_channel; + HAL_STATUS *status; +{ + struct ar5k_eeprom_info *ee = &hal->ah_capabilities.cap_eeprom; + u_int8_t mac[IEEE80211_ADDR_LEN]; + u_int32_t data; + u_int i, mode, freq, ee_mode, ant[2]; + + if (ar5k_ar5211_nic_wakeup(hal, channel->c_channel_flags) == AH_FALSE) + return (AH_FALSE); + + /* + * Initialize operating mode + */ + hal->ah_op_mode = op_mode; + + if (channel->c_channel_flags & IEEE80211_CHAN_A) { + mode = AR5K_INI_VAL_11A; + freq = AR5K_INI_RFGAIN_5GHZ; + ee_mode = AR5K_EEPROM_MODE_11A; + } else if (channel->c_channel_flags & IEEE80211_CHAN_T) { + mode = AR5K_INI_VAL_11A_TURBO; + freq = AR5K_INI_RFGAIN_5GHZ; + ee_mode = AR5K_EEPROM_MODE_11A; + } else if (channel->c_channel_flags & IEEE80211_CHAN_B) { + mode = AR5K_INI_VAL_11B; + freq = AR5K_INI_RFGAIN_2GHZ; + ee_mode = AR5K_EEPROM_MODE_11B; + } else if (channel->c_channel_flags & IEEE80211_CHAN_G) { + mode = AR5K_INI_VAL_11G; + freq = AR5K_INI_RFGAIN_2GHZ; + ee_mode = AR5K_EEPROM_MODE_11G; + } else { + AR5K_PRINTF("invalid channel: %d\n", channel->c_channel); + return (AH_FALSE); + } + + /* PHY access enable */ + AR5K_REG_WRITE(AR5K_AR5211_PHY(0), AR5K_AR5211_PHY_SHIFT_5GHZ); + + /* + * Write initial register settings + */ + for (i = 0; i < AR5K_ELEMENTS(ar5211_ini); i++) { + if (change_channel == AH_TRUE && + ar5211_ini[i].ini_register >= AR5K_AR5211_PCU_MIN && + ar5211_ini[i].ini_register <= AR5K_AR5211_PCU_MAX) + continue; + + AR5K_REG_WRITE((u_int32_t)ar5211_ini[i].ini_register, + ar5211_ini[i].ini_value); + } + + /* + * Write initial mode settings + */ + for (i = 0; i < AR5K_ELEMENTS(ar5211_mode); i++) { + AR5K_REG_WRITE((u_int32_t)ar5211_mode[i].mode_register, + ar5211_mode[i].mode_value[mode]); + } + + /* + * Write initial RF gain settings + */ + if (ar5k_rfgain(hal, AR5K_INI_PHY_5111, freq) == AH_FALSE) + return (AH_FALSE); + + AR5K_DELAY(1000); + + /* + * Configure additional registers + */ + + /* Set antenna mode */ + AR5K_REG_MASKED_BITS(AR5K_AR5211_PHY(0x44), + hal->ah_antenna[ee_mode][0], 0xfffffc06); + + ant[0] = HAL_ANT_FIXED_A; + ant[1] = HAL_ANT_FIXED_B; + + if (hal->ah_ant_diversity == AH_FALSE) { + if (freq == AR5K_INI_RFGAIN_2GHZ) + ant[0] = HAL_ANT_FIXED_B; + else if (freq == AR5K_INI_RFGAIN_5GHZ) + ant[1] = HAL_ANT_FIXED_A; + } + + AR5K_REG_WRITE(AR5K_AR5211_PHY_ANT_SWITCH_TABLE_0, + hal->ah_antenna[ee_mode][ant[0]]); + AR5K_REG_WRITE(AR5K_AR5211_PHY_ANT_SWITCH_TABLE_1, + hal->ah_antenna[ee_mode][ant[1]]); + + /* Commit values from EEPROM */ + AR5K_REG_WRITE_BITS(AR5K_AR5211_PHY_FC, + AR5K_AR5211_PHY_FC_TX_CLIP, ee->ee_tx_clip); + + AR5K_REG_WRITE(AR5K_AR5211_PHY(0x5a), + AR5K_AR5211_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode])); + + AR5K_REG_MASKED_BITS(AR5K_AR5211_PHY(0x11), + (ee->ee_switch_settling[ee_mode] << 7) & 0x3f80, 0xffffc07f); + AR5K_REG_MASKED_BITS(AR5K_AR5211_PHY(0x12), + (ee->ee_ant_tx_rx[ee_mode] << 12) & 0x3f000, 0xfffc0fff); + AR5K_REG_MASKED_BITS(AR5K_AR5211_PHY(0x14), + (ee->ee_adc_desired_size[ee_mode] & 0x00ff) | + ((ee->ee_pga_desired_size[ee_mode] << 8) & 0xff00), 0xffff0000); + + AR5K_REG_WRITE(AR5K_AR5211_PHY(0x0d), + (ee->ee_tx_end2xpa_disable[ee_mode] << 24) | + (ee->ee_tx_end2xpa_disable[ee_mode] << 16) | + (ee->ee_tx_frm2xpa_enable[ee_mode] << 8) | + (ee->ee_tx_frm2xpa_enable[ee_mode])); + + AR5K_REG_MASKED_BITS(AR5K_AR5211_PHY(0x0a), + ee->ee_tx_end2xlna_enable[ee_mode] << 8, 0xffff00ff); + AR5K_REG_MASKED_BITS(AR5K_AR5211_PHY(0x19), + (ee->ee_thr_62[ee_mode] << 12) & 0x7f000, 0xfff80fff); + AR5K_REG_MASKED_BITS(AR5K_AR5211_PHY(0x49), 4, 0xffffff01); + + AR5K_REG_ENABLE_BITS(AR5K_AR5211_PHY_IQ, + AR5K_AR5211_PHY_IQ_CORR_ENABLE | + (ee->ee_i_cal[ee_mode] << AR5K_AR5211_PHY_IQ_CORR_Q_I_COFF_S) | + ee->ee_q_cal[ee_mode]); + + /* Misc */ + memset(&mac, 0xff, sizeof(mac)); + ar5k_ar5211_writeAssocid(hal, mac, 0, 0); + ar5k_ar5211_setPCUConfig(hal); + AR5K_REG_WRITE(AR5K_AR5211_PISR, 0xffffffff); + AR5K_REG_WRITE(AR5K_AR5211_RSSI_THR, AR5K_TUNE_RSSI_THRES); + + /* + * Set channel and calibrate the PHY + */ + if (ar5k_channel(hal, channel) == AH_FALSE) + return (AH_FALSE); + + /* + * Enable the PHY and wait until completion + */ + AR5K_REG_WRITE(AR5K_AR5211_PHY_ACTIVE, AR5K_AR5211_PHY_ENABLE); + + data = AR5K_REG_READ(AR5K_AR5211_PHY_RX_DELAY) & + AR5K_AR5211_PHY_RX_DELAY_M; + data = (channel->c_channel_flags & IEEE80211_CHAN_CCK) ? + ((data << 2) / 22) : (data / 10); + + AR5K_DELAY(100 + data); + + /* + * Start calibration + */ + AR5K_REG_ENABLE_BITS(AR5K_AR5211_PHY_AGCCTL, + AR5K_AR5211_PHY_AGCCTL_NF | + AR5K_AR5211_PHY_AGCCTL_CAL); + + if (channel->c_channel_flags & IEEE80211_CHAN_B) { + hal->ah_calibration = AH_FALSE; + } else { + hal->ah_calibration = AH_TRUE; + AR5K_REG_WRITE_BITS(AR5K_AR5211_PHY_IQ, + AR5K_AR5211_PHY_IQ_CAL_NUM_LOG_MAX, 15); + AR5K_REG_ENABLE_BITS(AR5K_AR5211_PHY_IQ, + AR5K_AR5211_PHY_IQ_RUN); + } + + /* + * Reset queues and start beacon timers at the end of the reset routine + */ + for (i = 0; i < hal->ah_capabilities.cap_queues.q_tx_num; i++) { + AR5K_REG_WRITE_Q(AR5K_AR5211_DCU_QCUMASK(i), i); + if (ar5k_ar5211_resetTxQueue(hal, i) == AH_FALSE) { + AR5K_PRINTF("failed to reset TX queue #%d\n", i); + return (AH_FALSE); + } + } + + /* Pre-enable interrupts */ + ar5k_ar5211_setInterrupts(hal, HAL_INT_RX | HAL_INT_TX | HAL_INT_FATAL); + + /* + * Set RF kill flags if supported by the device (read from the EEPROM) + */ + if (AR5K_EEPROM_HDR_RFKILL(hal->ah_capabilities.cap_eeprom.ee_header)) { + ar5k_ar5211_gpioCfgInput(hal, 0); + if ((hal->ah_gpio[0] = ar5k_ar5211_gpioGet(hal, 0)) == 0) + ar5k_ar5211_gpioSetIntr(hal, 0, 1); + else + ar5k_ar5211_gpioSetIntr(hal, 0, 0); + } + + /* + * Disable beacons and reset the register + */ + AR5K_REG_DISABLE_BITS(AR5K_AR5211_BEACON, + AR5K_AR5211_BEACON_ENABLE | AR5K_AR5211_BEACON_RESET_TSF); + + return (AH_TRUE); +} + +void +ar5k_ar5211_setPCUConfig(hal) + struct ath_hal *hal; +{ + u_int32_t pcu_reg, low_id, high_id; + + pcu_reg = 0; + + switch (hal->ah_op_mode) { + case IEEE80211_M_IBSS: + pcu_reg |= AR5K_AR5211_STA_ID1_ADHOC | + AR5K_AR5211_STA_ID1_DESC_ANTENNA; + break; + + case IEEE80211_M_HOSTAP: + pcu_reg |= AR5K_AR5211_STA_ID1_AP | + AR5K_AR5211_STA_ID1_RTS_DEFAULT_ANTENNA; + break; + + case IEEE80211_M_STA: + case IEEE80211_M_MONITOR: + pcu_reg |= AR5K_AR5211_STA_ID1_DEFAULT_ANTENNA; + break; + + default: + return; + } + + /* + * Set PCU registers + */ + memcpy(&low_id, &(hal->ah_sta_id[0]), 4); + memcpy(&high_id, &(hal->ah_sta_id[4]), 2); + AR5K_REG_WRITE(AR5K_AR5211_STA_ID0, low_id); + AR5K_REG_WRITE(AR5K_AR5211_STA_ID1, pcu_reg | high_id); + + return; +} + +HAL_BOOL +ar5k_ar5211_perCalibration(hal, channel) + struct ath_hal *hal; + HAL_CHANNEL *channel; +{ + u_int32_t i_pwr, q_pwr; + int32_t iq_corr, i_coff, i_coffd, q_coff, q_coffd; + + if (hal->ah_calibration == AH_FALSE || + AR5K_REG_READ(AR5K_AR5211_PHY_IQ) & AR5K_AR5211_PHY_IQ_RUN) + goto done; + + hal->ah_calibration = AH_FALSE; + + iq_corr = AR5K_REG_READ(AR5K_AR5211_PHY_IQRES_CAL_CORR); + i_pwr = AR5K_REG_READ(AR5K_AR5211_PHY_IQRES_CAL_PWR_I); + q_pwr = AR5K_REG_READ(AR5K_AR5211_PHY_IQRES_CAL_PWR_Q); + i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7; + q_coffd = q_pwr >> 6; + + if (i_coffd == 0 || q_coffd == 0) + goto done; + + i_coff = ((-iq_corr) / i_coffd) & 0x3f; + q_coff = (((int32_t)i_pwr / q_coffd) - 64) & 0x1f; + + /* Commit new IQ value */ + AR5K_REG_ENABLE_BITS(AR5K_AR5211_PHY_IQ, + AR5K_AR5211_PHY_IQ_CORR_ENABLE | + ((u_int32_t)q_coff) | + ((u_int32_t)i_coff << AR5K_AR5211_PHY_IQ_CORR_Q_I_COFF_S)); + + done: + /* Start noise floor calibration */ + AR5K_REG_ENABLE_BITS(AR5K_AR5211_PHY_AGCCTL, + AR5K_AR5211_PHY_AGCCTL_NF); + + return (AH_TRUE); +} + +/* + * Transmit functions + */ + +HAL_BOOL +ar5k_ar5211_updateTxTrigLevel(hal, increase) + struct ath_hal *hal; + HAL_BOOL increase; +{ + u_int32_t trigger_level, imr; + HAL_BOOL status = AH_FALSE; + + /* + * Disable interrupts by setting the mask + */ + imr = ar5k_ar5211_setInterrupts(hal, hal->ah_imr & ~HAL_INT_GLOBAL); + + trigger_level = AR5K_REG_MS(AR5K_REG_READ(AR5K_AR5211_TXCFG), + AR5K_AR5211_TXCFG_TXFULL); + + if (increase == AH_FALSE) { + if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES) + goto done; + } else + trigger_level += + ((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2); + + /* + * Update trigger level on success + */ + AR5K_REG_WRITE_BITS(AR5K_AR5211_TXCFG, + AR5K_AR5211_TXCFG_TXFULL, trigger_level); + status = AH_TRUE; + + done: + /* + * Restore interrupt mask + */ + ar5k_ar5211_setInterrupts(hal, imr); + + return (status); +} + +int +ar5k_ar5211_setupTxQueue(hal, queue_type, queue_info) + struct ath_hal *hal; + HAL_TX_QUEUE queue_type; + const HAL_TXQ_INFO *queue_info; +{ + u_int queue; + + /* + * Get queue by type + */ + if (queue_type == HAL_TX_QUEUE_DATA) { + for (queue = HAL_TX_QUEUE_ID_DATA_MIN; + hal->ah_txq[queue].tqi_type != HAL_TX_QUEUE_INACTIVE; + queue++) + if (queue > HAL_TX_QUEUE_ID_DATA_MAX) + return (-1); + } else if (queue_type == HAL_TX_QUEUE_PSPOLL) { + queue = HAL_TX_QUEUE_ID_PSPOLL; + } else if (queue_type == HAL_TX_QUEUE_BEACON) { + queue = HAL_TX_QUEUE_ID_BEACON; + } else if (queue_type == HAL_TX_QUEUE_CAB) { + queue = HAL_TX_QUEUE_ID_CAB; + } else + return (-1); + + /* + * Setup internal queue structure + */ + bzero(&hal->ah_txq[queue], sizeof(HAL_TXQ_INFO)); + hal->ah_txq[queue].tqi_type = queue_type; + + if (queue_info != NULL) { + if (ar5k_ar5211_setTxQueueProps(hal, queue, queue_info) + != AH_TRUE) + return (-1); + } + + AR5K_Q_ENABLE_BITS(hal->ah_txq_interrupts, queue); + + return (queue); +} + +HAL_BOOL +ar5k_ar5211_setTxQueueProps(hal, queue, queue_info) + struct ath_hal *hal; + int queue; + const HAL_TXQ_INFO *queue_info; +{ + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + if (hal->ah_txq[queue].tqi_type == HAL_TX_QUEUE_INACTIVE) + return (AH_FALSE); + + bcopy(queue_info, &hal->ah_txq[queue], sizeof(HAL_TXQ_INFO)); + + if (queue_info->tqi_type == HAL_TX_QUEUE_DATA && + (queue_info->tqi_subtype >= HAL_WME_AC_VI) && + (queue_info->tqi_subtype <= HAL_WME_UPSD)) + hal->ah_txq[queue].tqi_flags |= + AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS; + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5211_releaseTxQueue(hal, queue) + struct ath_hal *hal; + u_int queue; +{ + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + /* This queue will be skipped in further operations */ + hal->ah_txq[queue].tqi_type = HAL_TX_QUEUE_INACTIVE; + AR5K_Q_DISABLE_BITS(hal->ah_txq_interrupts, queue); + + return (AH_FALSE); +} + +HAL_BOOL +ar5k_ar5211_resetTxQueue(hal, queue) + struct ath_hal *hal; + u_int queue; +{ + u_int32_t cw_min, cw_max, retry_lg, retry_sh; + struct ieee80211_channel *channel = (struct ieee80211_channel*) + &hal->ah_current_channel; + HAL_TXQ_INFO *tq; + + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + tq = &hal->ah_txq[queue]; + + if (tq->tqi_type == HAL_TX_QUEUE_INACTIVE) + return (AH_TRUE); + + /* + * Set registers by channel mode + */ + if (IEEE80211_IS_CHAN_B(channel)) { + hal->ah_cw_min = AR5K_TUNE_CWMIN_11B; + hal->ah_cw_max = AR5K_TUNE_CWMAX_11B; + hal->ah_aifs = AR5K_TUNE_AIFS_11B; + } else { + hal->ah_cw_min = AR5K_TUNE_CWMIN; + hal->ah_cw_max = AR5K_TUNE_CWMAX; + hal->ah_aifs = AR5K_TUNE_AIFS; + } + + /* + * Set retry limits + */ + if (hal->ah_software_retry == AH_TRUE) { + /* XXX Need to test this */ + retry_lg = hal->ah_limit_tx_retries; + retry_sh = retry_lg = + retry_lg > AR5K_AR5211_DCU_RETRY_LMT_SH_RETRY ? + AR5K_AR5211_DCU_RETRY_LMT_SH_RETRY : retry_lg; + } else { + retry_lg = AR5K_INIT_LG_RETRY; + retry_sh = AR5K_INIT_SH_RETRY; + } + + AR5K_REG_WRITE(AR5K_AR5211_DCU_RETRY_LMT(queue), + AR5K_REG_SM(AR5K_INIT_SLG_RETRY, + AR5K_AR5211_DCU_RETRY_LMT_SLG_RETRY) + | AR5K_REG_SM(AR5K_INIT_SSH_RETRY, + AR5K_AR5211_DCU_RETRY_LMT_SSH_RETRY) + | AR5K_REG_SM(retry_lg, AR5K_AR5211_DCU_RETRY_LMT_LG_RETRY) + | AR5K_REG_SM(retry_sh, AR5K_AR5211_DCU_RETRY_LMT_SH_RETRY)); + + /* + * Set initial content window (cw_min/cw_max) + */ + cw_min = 1; + while (cw_min < hal->ah_cw_min) + cw_min = (cw_min << 1) | 1; + + cw_min = tq->tqi_cw_min < 0 ? + (cw_min >> (-tq->tqi_cw_min)) : + ((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1); + cw_max = tq->tqi_cw_max < 0 ? + (cw_max >> (-tq->tqi_cw_max)) : + ((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1); + + AR5K_REG_WRITE(AR5K_AR5211_DCU_LCL_IFS(queue), + AR5K_REG_SM(cw_min, AR5K_AR5211_DCU_LCL_IFS_CW_MIN) | + AR5K_REG_SM(cw_max, AR5K_AR5211_DCU_LCL_IFS_CW_MAX) | + AR5K_REG_SM(hal->ah_aifs + tq->tqi_aifs, + AR5K_AR5211_DCU_LCL_IFS_AIFS)); + + /* + * Set misc registers + */ + AR5K_REG_WRITE(AR5K_AR5211_QCU_MISC(queue), + AR5K_AR5211_QCU_MISC_DCU_EARLY); + + if (tq->tqi_cbr_period) { + AR5K_REG_WRITE(AR5K_AR5211_QCU_CBRCFG(queue), + AR5K_REG_SM(tq->tqi_cbr_period, + AR5K_AR5211_QCU_CBRCFG_INTVAL) | + AR5K_REG_SM(tq->tqi_cbr_overflow_limit, + AR5K_AR5211_QCU_CBRCFG_ORN_THRES)); + AR5K_REG_ENABLE_BITS(AR5K_AR5211_QCU_MISC(queue), + AR5K_AR5211_QCU_MISC_FRSHED_CBR); + if (tq->tqi_cbr_overflow_limit) + AR5K_REG_ENABLE_BITS(AR5K_AR5211_QCU_MISC(queue), + AR5K_AR5211_QCU_MISC_CBR_THRES_ENABLE); + } + + if (tq->tqi_ready_time) { + AR5K_REG_WRITE(AR5K_AR5211_QCU_RDYTIMECFG(queue), + AR5K_REG_SM(tq->tqi_ready_time, + AR5K_AR5211_QCU_RDYTIMECFG_INTVAL) | + AR5K_AR5211_QCU_RDYTIMECFG_ENABLE); + } + + if (tq->tqi_burst_time) { + AR5K_REG_WRITE(AR5K_AR5211_DCU_CHAN_TIME(queue), + AR5K_REG_SM(tq->tqi_burst_time, + AR5K_AR5211_DCU_CHAN_TIME_DUR) | + AR5K_AR5211_DCU_CHAN_TIME_ENABLE); + + if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) { + AR5K_REG_ENABLE_BITS(AR5K_AR5211_QCU_MISC(queue), + AR5K_AR5211_QCU_MISC_TXE); + } + } + + if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE) { + AR5K_REG_WRITE(AR5K_AR5211_DCU_MISC(queue), + AR5K_AR5211_DCU_MISC_POST_FR_BKOFF_DIS); + } + + if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) { + AR5K_REG_WRITE(AR5K_AR5211_DCU_MISC(queue), + AR5K_AR5211_DCU_MISC_BACKOFF_FRAG); + } + + /* + * Set registers by queue type + */ + switch (tq->tqi_type) { + case HAL_TX_QUEUE_BEACON: + AR5K_REG_ENABLE_BITS(AR5K_AR5211_QCU_MISC(queue), + AR5K_AR5211_QCU_MISC_FRSHED_DBA_GT | + AR5K_AR5211_QCU_MISC_CBREXP_BCN | + AR5K_AR5211_QCU_MISC_BCN_ENABLE); + + AR5K_REG_ENABLE_BITS(AR5K_AR5211_DCU_MISC(queue), + (AR5K_AR5211_DCU_MISC_ARBLOCK_CTL_GLOBAL << + AR5K_AR5211_DCU_MISC_ARBLOCK_CTL_GLOBAL) | + AR5K_AR5211_DCU_MISC_POST_FR_BKOFF_DIS | + AR5K_AR5211_DCU_MISC_BCN_ENABLE); + + AR5K_REG_WRITE(AR5K_AR5211_QCU_RDYTIMECFG(queue), + ((AR5K_TUNE_BEACON_INTERVAL - + (AR5K_TUNE_SW_BEACON_RESP - AR5K_TUNE_DMA_BEACON_RESP) - + AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) | + AR5K_AR5211_QCU_RDYTIMECFG_ENABLE); + break; + + case HAL_TX_QUEUE_CAB: + AR5K_REG_ENABLE_BITS(AR5K_AR5211_QCU_MISC(queue), + AR5K_AR5211_QCU_MISC_FRSHED_DBA_GT | + AR5K_AR5211_QCU_MISC_CBREXP | + AR5K_AR5211_QCU_MISC_CBREXP_BCN); + + AR5K_REG_ENABLE_BITS(AR5K_AR5211_DCU_MISC(queue), + (AR5K_AR5211_DCU_MISC_ARBLOCK_CTL_GLOBAL << + AR5K_AR5211_DCU_MISC_ARBLOCK_CTL_GLOBAL)); + break; + + case HAL_TX_QUEUE_PSPOLL: + AR5K_REG_ENABLE_BITS(AR5K_AR5211_QCU_MISC(queue), + AR5K_AR5211_QCU_MISC_CBREXP); + break; + + case HAL_TX_QUEUE_DATA: + default: + break; + } + + /* + * Enable tx queue in the secondary interrupt mask registers + */ + AR5K_REG_WRITE(AR5K_AR5211_SIMR0, + AR5K_REG_SM(hal->ah_txq_interrupts, AR5K_AR5211_SIMR0_QCU_TXOK) | + AR5K_REG_SM(hal->ah_txq_interrupts, AR5K_AR5211_SIMR0_QCU_TXDESC)); + AR5K_REG_WRITE(AR5K_AR5211_SIMR1, + AR5K_REG_SM(hal->ah_txq_interrupts, AR5K_AR5211_SIMR1_QCU_TXERR)); + AR5K_REG_WRITE(AR5K_AR5211_SIMR2, + AR5K_REG_SM(hal->ah_txq_interrupts, AR5K_AR5211_SIMR2_QCU_TXURN)); + + return (AH_TRUE); +} + +u_int32_t +ar5k_ar5211_getTxDP(hal, queue) + struct ath_hal *hal; + u_int queue; +{ + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + /* + * Get the transmit queue descriptor pointer from the selected queue + */ + return (AR5K_REG_READ(AR5K_AR5211_QCU_TXDP(queue))); +} + +HAL_BOOL +ar5k_ar5211_setTxDP(hal, queue, phys_addr) + struct ath_hal *hal; + u_int queue; + u_int32_t phys_addr; +{ + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + /* + * Set the transmit queue descriptor pointer for the selected queue + * (this won't work if the queue is still active) + */ + if (AR5K_REG_READ_Q(AR5K_AR5211_QCU_TXE, queue)) + return (AH_FALSE); + + AR5K_REG_WRITE(AR5K_AR5211_QCU_TXDP(queue), phys_addr); + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5211_startTxDma(hal, queue) + struct ath_hal *hal; + u_int queue; +{ + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + /* Return if queue is disabled */ + if (AR5K_REG_READ_Q(AR5K_AR5211_QCU_TXD, queue)) + return (AH_FALSE); + + /* Start queue */ + AR5K_REG_WRITE_Q(AR5K_AR5211_QCU_TXE, queue); + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5211_stopTxDma(hal, queue) + struct ath_hal *hal; + u_int queue; +{ + HAL_BOOL ret; + + AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num); + + /* + * Schedule TX disable and wait until queue is empty + */ + AR5K_REG_WRITE_Q(AR5K_AR5211_QCU_TXD, queue); + + ret = ar5k_register_timeout(hal, AR5K_AR5211_QCU_STS(queue), + AR5K_AR5211_QCU_STS_FRMPENDCNT, 0, AH_FALSE); + + /* Clear register */ + AR5K_REG_WRITE(AR5K_AR5211_QCU_TXD, 0); + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5211_setupTxDesc(hal, desc, packet_length, header_length, type, tx_power, + tx_rate0, tx_tries0, key_index, antenna_mode, flags, rtscts_rate, + rtscts_duration) + struct ath_hal *hal; + struct ath_desc *desc; + u_int packet_length; + u_int header_length; + HAL_PKT_TYPE type; + u_int tx_power; + u_int tx_rate0; + u_int tx_tries0; + u_int key_index; + u_int antenna_mode; + u_int flags; + u_int rtscts_rate; + u_int rtscts_duration; +{ + struct ar5k_ar5211_tx_desc *tx_desc; + + tx_desc = (struct ar5k_ar5211_tx_desc*)&desc->ds_ctl0; + + /* Clear descriptor */ + bzero(tx_desc, sizeof(struct ar5k_ar5211_tx_desc)); + + /* + * Validate input + */ + if (tx_tries0 == 0) + return (AH_FALSE); + + if ((tx_desc->frame_len = packet_length) != packet_length) + return (AH_FALSE); + + tx_desc->frame_type = type; + tx_desc->xmit_rate = tx_rate0; + tx_desc->ant_mode_xmit = antenna_mode; + +#define _TX_FLAGS(_flag, _field) \ + tx_desc->_field = flags & HAL_TXDESC_##_flag ? 1 : 0 + + _TX_FLAGS(CLRDMASK, clear_dest_mask); + _TX_FLAGS(VEOL, veol); + _TX_FLAGS(INTREQ, inter_req); + _TX_FLAGS(NOACK, no_ack); + _TX_FLAGS(RTSENA, rts_cts_enable); + +#undef _TX_FLAGS + + /* + * WEP crap + */ + if (key_index != HAL_TXKEYIX_INVALID) { + tx_desc->encrypt_key_valid = 1; + tx_desc->encrypt_key_index = key_index; + } + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5211_fillTxDesc(hal, desc, segment_length, first_segment, last_segment) + struct ath_hal *hal; + struct ath_desc *desc; + u_int segment_length; + HAL_BOOL first_segment; + HAL_BOOL last_segment; +{ + struct ar5k_ar5211_tx_desc *tx_desc; + + tx_desc = (struct ar5k_ar5211_tx_desc*)&desc->ds_ctl0; + + /* Clear status descriptor */ + desc->ds_hw[0] = desc->ds_hw[1] = 0; + + /* Validate segment length and initialize the descriptor */ + if ((tx_desc->buf_len = segment_length) != segment_length) + return (AH_FALSE); + + if (first_segment != AH_TRUE) + tx_desc->frame_len = 0; + + tx_desc->more = last_segment == AH_TRUE ? 0 : 1; + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5211_setupXTxDesc(hal, desc, tx_rate1, tx_tries1, tx_rate2, tx_tries2, + tx_rate3, tx_tries3) + struct ath_hal *hal; + struct ath_desc *desc; + u_int tx_rate1; + u_int tx_tries1; + u_int tx_rate2; + u_int tx_tries2; + u_int tx_rate3; + u_int tx_tries3; +{ + return (AH_FALSE); +} + +HAL_STATUS +ar5k_ar5211_procTxDesc(hal, desc) + struct ath_hal *hal; + struct ath_desc *desc; +{ + struct ar5k_ar5211_tx_status *tx_status; + struct ar5k_ar5211_tx_desc *tx_desc; + + tx_desc = (struct ar5k_ar5211_tx_desc*)&desc->ds_ctl0; + tx_status = (struct ar5k_ar5211_tx_status*)&desc->ds_hw[0]; + + /* No frame has been send or error */ + if (tx_status->done == 0) + return (HAL_EINPROGRESS); + + /* + * Get descriptor status + */ + desc->ds_us.tx.ts_seqnum = tx_status->seq_num; + desc->ds_us.tx.ts_tstamp = tx_status->send_timestamp; + desc->ds_us.tx.ts_shortretry = tx_status->rts_fail_count ? + (tx_status->rts_fail_count + 1) : 0; + desc->ds_us.tx.ts_longretry = tx_status->data_fail_count ? + (tx_status->data_fail_count + 1) : 0; + desc->ds_us.tx.ts_rssi = tx_status->ack_sig_strength; + desc->ds_us.tx.ts_antenna = 0; + desc->ds_us.tx.ts_status = 0; + desc->ds_us.tx.ts_rate = tx_desc->xmit_rate; + + if (tx_status->frame_xmit_ok == 0) { + if (tx_status->excessive_retries) + desc->ds_us.tx.ts_status |= HAL_TXERR_XRETRY; + + if (tx_status->fifo_underrun) + desc->ds_us.tx.ts_status |= HAL_TXERR_FIFO; + + if (tx_status->filtered) + desc->ds_us.tx.ts_status |= HAL_TXERR_FILT; + } + + return (HAL_OK); +} + +HAL_BOOL +ar5k_ar5211_hasVEOL(hal) + struct ath_hal *hal; +{ + return (AH_TRUE); +} + +/* + * Receive functions + */ + +u_int32_t +ar5k_ar5211_getRxDP(hal) + struct ath_hal *hal; +{ + return (AR5K_REG_READ(AR5K_AR5211_RXDP)); +} + +void +ar5k_ar5211_setRxDP(hal, phys_addr) + struct ath_hal *hal; + u_int32_t phys_addr; +{ + AR5K_REG_WRITE(AR5K_AR5211_RXDP, phys_addr); +} + +void +ar5k_ar5211_enableReceive(hal) + struct ath_hal *hal; +{ + AR5K_REG_WRITE(AR5K_AR5211_CR, AR5K_AR5211_CR_RXE); +} + +HAL_BOOL +ar5k_ar5211_stopDmaReceive(hal) + struct ath_hal *hal; +{ + int i; + + AR5K_REG_WRITE(AR5K_AR5211_CR, AR5K_AR5211_CR_RXD); + + /* + * It may take some time to disable the DMA receive unit + */ + for (i = 2000; + i > 0 && (AR5K_REG_READ(AR5K_AR5211_CR) & AR5K_AR5211_CR_RXE) != 0; + i--) + AR5K_DELAY(10); + + return (i > 0 ? AH_TRUE : AH_FALSE); +} + +void +ar5k_ar5211_startPcuReceive(hal) + struct ath_hal *hal; +{ + AR5K_REG_DISABLE_BITS(AR5K_AR5211_DIAG_SW, AR5K_AR5211_DIAG_SW_DIS_RX); +} + +void +ar5k_ar5211_stopPcuReceive(hal) + struct ath_hal *hal; +{ + AR5K_REG_ENABLE_BITS(AR5K_AR5211_DIAG_SW, AR5K_AR5211_DIAG_SW_DIS_RX); +} + +void +ar5k_ar5211_setMulticastFilter(hal, filter0, filter1) + struct ath_hal *hal; + u_int32_t filter0; + u_int32_t filter1; +{ + /* Set the multicat filter */ + AR5K_REG_WRITE(AR5K_AR5211_MCAST_FIL0, filter0); + AR5K_REG_WRITE(AR5K_AR5211_MCAST_FIL1, filter1); +} + +HAL_BOOL +ar5k_ar5211_setMulticastFilterIndex(hal, index) + struct ath_hal *hal; + u_int32_t index; +{ + if (index >= 64) { + return (AH_FALSE); + } else if (index >= 32) { + AR5K_REG_ENABLE_BITS(AR5K_AR5211_MCAST_FIL1, + (1 << (index - 32))); + } else { + AR5K_REG_ENABLE_BITS(AR5K_AR5211_MCAST_FIL0, + (1 << index)); + } + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5211_clrMulticastFilterIndex(hal, index) + struct ath_hal *hal; + u_int32_t index; +{ + + if (index >= 64) { + return (AH_FALSE); + } else if (index >= 32) { + AR5K_REG_DISABLE_BITS(AR5K_AR5211_MCAST_FIL1, + (1 << (index - 32))); + } else { + AR5K_REG_DISABLE_BITS(AR5K_AR5211_MCAST_FIL0, + (1 << index)); + } + + return (AH_TRUE); +} + +u_int32_t +ar5k_ar5211_getRxFilter(hal) + struct ath_hal *hal; +{ + return (AR5K_REG_READ(AR5K_AR5211_RX_FILTER)); +} + +void +ar5k_ar5211_setRxFilter(hal, filter) + struct ath_hal *hal; + u_int32_t filter; +{ + AR5K_REG_WRITE(AR5K_AR5211_RX_FILTER, filter); +} + +HAL_BOOL +ar5k_ar5211_setupRxDesc(hal, desc, size, flags) + struct ath_hal *hal; + struct ath_desc *desc; + u_int32_t size; + u_int flags; +{ + struct ar5k_ar5211_rx_desc *rx_desc; + + /* Reset descriptor */ + desc->ds_ctl0 = 0; + desc->ds_ctl1 = 0; + bzero(&desc->ds_hw[0], sizeof(struct ar5k_ar5211_rx_status)); + + rx_desc = (struct ar5k_ar5211_rx_desc*)&desc->ds_ctl0; + + if ((rx_desc->buf_len = size) != size) + return (AH_FALSE); + + if (flags & HAL_RXDESC_INTREQ) + rx_desc->inter_req = 1; + + return (AH_TRUE); +} + +HAL_STATUS +ar5k_ar5211_procRxDesc(hal, desc, phys_addr, next) + struct ath_hal *hal; + struct ath_desc *desc; + u_int32_t phys_addr; + struct ath_desc *next; +{ + u_int32_t now, tstamp; + struct ar5k_ar5211_rx_status *rx_status; + + rx_status = (struct ar5k_ar5211_rx_status*)&desc->ds_hw[0]; + + /* No frame received / not ready */ + if (!rx_status->done) + return (HAL_EINPROGRESS); + + /* + * Frame receive status + */ + now = (AR5K_REG_READ(AR5K_AR5211_TSF_L32) >> 10) & 0xffff; + tstamp = ((now & 0x1fff) < rx_status->receive_timestamp) ? + (((now - 0x2000) & 0xffff) | + (u_int32_t)rx_status->receive_timestamp) : + (now | (u_int32_t)rx_status->receive_timestamp); + desc->ds_us.rx.rs_tstamp = rx_status->receive_timestamp & 0x7fff; + desc->ds_us.rx.rs_datalen = rx_status->data_len; + desc->ds_us.rx.rs_rssi = rx_status->receive_sig_strength; + desc->ds_us.rx.rs_rate = rx_status->receive_rate; + desc->ds_us.rx.rs_antenna = rx_status->receive_antenna ? 1 : 0; + desc->ds_us.rx.rs_more = rx_status->more ? 1 : 0; + desc->ds_us.rx.rs_status = 0; + + /* + * Key table status + */ + if (!rx_status->key_index_valid) { + desc->ds_us.rx.rs_keyix = HAL_RXKEYIX_INVALID; + } else { + desc->ds_us.rx.rs_keyix = rx_status->key_index; + } + + /* + * Receive/descriptor errors + */ + if (!rx_status->frame_receive_ok) { + if (rx_status->crc_error) + desc->ds_us.rx.rs_status |= HAL_RXERR_CRC; + + if (rx_status->phy_error) { + desc->ds_us.rx.rs_status |= HAL_RXERR_PHY; + desc->ds_us.rx.rs_phyerr = rx_status->phy_error; + } + + if (rx_status->decrypt_crc_error) + desc->ds_us.rx.rs_status |= HAL_RXERR_DECRYPT; + } + + return (HAL_OK); +} + +void +ar5k_ar5211_rxMonitor(hal) + struct ath_hal *hal; +{ + AR5K_REG_ENABLE_BITS(AR5K_AR5211_RX_FILTER, + AR5K_AR5211_RX_FILTER_PROMISC); +} + +/* + * Misc functions + */ + +void +ar5k_ar5211_dumpState(hal) + struct ath_hal *hal; +{ + /* Not used */ + return; +} + +HAL_BOOL +ar5k_ar5211_getDiagState(hal, id, device, size) + struct ath_hal *hal; + int id; + void **device; + u_int *size; + +{ + /* + * We'll ignore this right now. This seems to be some kind of an obscure + * debugging interface for the binary-only HAL. + */ + return (AH_FALSE); +} + +void +ar5k_ar5211_getMacAddress(hal, mac) + struct ath_hal *hal; + u_int8_t *mac; +{ + memcpy(mac, hal->ah_sta_id, IEEE80211_ADDR_LEN); +} + +HAL_BOOL +ar5k_ar5211_setMacAddress(hal, mac) + struct ath_hal *hal; + const u_int8_t *mac; +{ + u_int32_t low_id, high_id; + + /* Set new station ID */ + memcpy(hal->ah_sta_id, mac, IEEE80211_ADDR_LEN); + + memcpy(&low_id, mac, 4); + memcpy(&high_id, mac + 4, 2); + high_id = 0x0000ffff & htole32(high_id); + + AR5K_REG_WRITE(AR5K_AR5211_STA_ID0, htole32(low_id)); + AR5K_REG_WRITE(AR5K_AR5211_STA_ID1, high_id); + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5211_setRegulatoryDomain(hal, regdomain, status) + struct ath_hal *hal; + u_int16_t regdomain; + HAL_STATUS *status; + +{ + ieee80211_regdomain_t ieee_regdomain; + + ieee_regdomain = ar5k_regdomain_to_ieee(regdomain); + + if (ar5k_eeprom_regulation_domain(hal, AH_TRUE, + &ieee_regdomain) == AH_TRUE) { + *status = HAL_OK; + return (AH_TRUE); + } + + *status = EIO; + + return (AH_FALSE); +} + +void +ar5k_ar5211_setLedState(hal, state) + struct ath_hal *hal; + HAL_LED_STATE state; +{ + u_int32_t led; + + AR5K_REG_DISABLE_BITS(AR5K_AR5211_PCICFG, + AR5K_AR5211_PCICFG_LEDMODE | AR5K_AR5211_PCICFG_LED); + + /* + * Some blinking values, define at your wish + */ + switch (state) { + case IEEE80211_S_SCAN: + case IEEE80211_S_AUTH: + led = + AR5K_AR5211_PCICFG_LEDMODE_PROP | + AR5K_AR5211_PCICFG_LED_PEND; + break; + + case IEEE80211_S_INIT: + led = + AR5K_AR5211_PCICFG_LEDMODE_PROP | + AR5K_AR5211_PCICFG_LED_NONE; + break; + + case IEEE80211_S_ASSOC: + case IEEE80211_S_RUN: + led |= + AR5K_AR5211_PCICFG_LEDMODE_PROP | + AR5K_AR5211_PCICFG_LED_ASSOC; + break; + + default: + led |= + AR5K_AR5211_PCICFG_LEDMODE_PROM | + AR5K_AR5211_PCICFG_LED_NONE; + break; + } + + AR5K_REG_ENABLE_BITS(AR5K_AR5211_PCICFG, led); +} + +void +ar5k_ar5211_writeAssocid(hal, bssid, assoc_id, tim_offset) + struct ath_hal *hal; + const u_int8_t *bssid; + u_int16_t assoc_id; + u_int16_t tim_offset; +{ + u_int32_t low_id, high_id; + + /* + * Set BSSID which triggers the "SME Join" operation + */ + memcpy(&low_id, bssid, 4); + memcpy(&high_id, bssid + 4, 2); + AR5K_REG_WRITE(AR5K_AR5211_BSS_ID0, htole32(low_id)); + AR5K_REG_WRITE(AR5K_AR5211_BSS_ID1, htole32(high_id) | + ((assoc_id & 0x3fff) << AR5K_AR5211_BSS_ID1_AID_S)); + memcpy(&hal->ah_bssid, bssid, IEEE80211_ADDR_LEN); + + if (assoc_id == 0) { + ar5k_ar5211_disablePSPoll(hal); + return; + } + + AR5K_REG_WRITE(AR5K_AR5211_BEACON, + (AR5K_REG_READ(AR5K_AR5211_BEACON) & + ~AR5K_AR5211_BEACON_TIM) | + (((tim_offset ? tim_offset + 4 : 0) << + AR5K_AR5211_BEACON_TIM_S) & + AR5K_AR5211_BEACON_TIM)); + + ar5k_ar5211_enablePSPoll(hal, NULL, 0); +} + +HAL_BOOL +ar5k_ar5211_gpioCfgOutput(hal, gpio) + struct ath_hal *hal; + u_int32_t gpio; +{ + if (gpio > AR5K_AR5211_NUM_GPIO) + return (AH_FALSE); + + AR5K_REG_WRITE(AR5K_AR5211_GPIOCR, + (AR5K_REG_READ(AR5K_AR5211_GPIOCR) &~ AR5K_AR5211_GPIOCR_ALL(gpio)) + | AR5K_AR5211_GPIOCR_ALL(gpio)); + + return (AH_TRUE); +} + +HAL_BOOL +ar5k_ar5211_gpioCfgInput(hal, gpio) + struct ath_hal *hal; + u_int32_t gpio; +{ + if (gpio > AR5K_AR5211_NUM_GPIO) + return (AH_FALSE); + + AR5K_REG_WRITE(AR5K_AR5211_GPIOCR, + (AR5K_REG_READ(AR5K_AR5211_GPIOCR) &~ AR5K_AR5211_GPIOCR_ALL(gpio)) + | AR5K_AR5211_GPIOCR_NONE(gpio)); + + return (AH_TRUE); +} + +u_int32_t +ar5k_ar5211_gpioGet(hal, gpio) + struct ath_hal *hal; + u_int32_t gpio; +{ + if (gpio > AR5K_AR5211_NUM_GPIO) + return (0xffffffff); + + /* GPIO input magic */ + return (((AR5K_REG_READ(AR5K_AR5211_GPIODI) & + AR5K_AR5211_GPIODI_M) >> gpio) & 0x1); +} + +HAL_BOOL +ar5k_ar5211_gpioSet(hal, gpio, val) + struct ath_hal *hal; + u_int32_t gpio; + u_int32_t val; +{ + u_int32_t data; + + if (gpio > AR5K_AR5211_NUM_GPIO) + return (0xffffffff); + + /* GPIO output magic */ + data = AR5K_REG_READ(AR5K_AR5211_GPIODO); + + data &= ~(1 << gpio); + data |= (val&1) << gpio; + + AR5K_REG_WRITE(AR5K_AR5211_GPIODO, data); + + return (AH_TRUE); +} + +void +ar5k_ar5211_gpioSetIntr(hal, gpio, interrupt_level) + struct ath_hal *hal; + u_int gpio; + u_int32_t interrupt_level; +{ + u_int32_t data; + + if (gpio > AR5K_AR5211_NUM_GPIO) + return; + + /* + * Set the GPIO interrupt + */ + data = (AR5K_REG_READ(AR5K_AR5211_GPIOCR) & + ~(AR5K_AR5211_GPIOCR_INT_SEL(gpio) | AR5K_AR5211_GPIOCR_INT_SELH | + AR5K_AR5211_GPIOCR_INT_ENA | AR5K_AR5211_GPIOCR_ALL(gpio))) | + (AR5K_AR5211_GPIOCR_INT_SEL(gpio) | AR5K_AR5211_GPIOCR_INT_ENA); + + AR5K_REG_WRITE(AR5K_AR5211_GPIOCR, + interrupt_level ? data : (data | AR5K_AR5211_GPIOCR_INT_SELH)); + + hal->ah_imr |= AR5K_AR5211_PIMR_GPIO; + + /* Enable GPIO interrupts */ + AR5K_REG_ENABLE_BITS(AR5K_AR5211_PIMR, AR5K_AR5211_PIMR_GPIO); +} + +u_int32_t +ar5k_ar5211_getTsf32(hal) + struct ath_hal *hal; +{ + return (AR5K_REG_READ(AR5K_AR5211_TSF_L32)); +} + +u_int64_t +ar5k_ar5211_getTsf64(hal) + struct ath_hal *hal; +{ + u_int64_t tsf = AR5K_REG_READ(AR5K_AR5211_TSF_U32); + + return (AR5K_REG_READ(AR5K_AR5211_TSF_L32) | (tsf << 32)); +} + +void +ar5k_ar5211_resetTsf(hal) + struct ath_hal *hal; +{ + AR5K_REG_ENABLE_BITS(AR5K_AR5211_BEACON, + AR5K_AR5211_BEACON_RESET_TSF); +} + +u_int16_t +ar5k_ar5211_getRegDomain(hal) + struct ath_hal *hal; +{ + return (ar5k_get_regdomain(hal)); +} + +HAL_BOOL +ar5k_ar5211_detectCardPresent(hal) + struct ath_hal *hal; +{ + u_int16_t magic; + + /* + * Checking the EEPROM's magic value could be an indication + * if the card is still present. I didn't find another suitable + * way to do this. + */ + if (ar5k_ar5211_eeprom_read(hal, AR5K_EEPROM_MAGIC, &magic) != 0) + return (AH_FALSE); + + return (magic == AR5K_EEPROM_MAGIC_VALUE ? AH_TRUE : AH_FALSE); +} + +void +ar5k_ar5211_updateMibCounters(hal, statistics) + struct ath_hal *hal; + HAL_MIB_STATS *statistics; +{ + statistics->ackrcv_bad += AR5K_REG_READ(AR5K_AR5211_ACK_FAIL); + statistics->rts_bad += AR5K_REG_READ(AR5K_AR5211_RTS_FAIL); + statistics->rts_good += AR5K_REG_READ(AR5K_AR5211_RTS_OK); + statistics->fcs_bad += AR5K_REG_READ(AR5K_AR5211_FCS_FAIL); + statistics->beacons += AR5K_REG_READ(AR5K_AR5211_BEACON_CNT); +} + +HAL_RFGAIN +ar5k_ar5211_getRfGain(hal) + struct ath_hal *hal; +{ + return (HAL_RFGAIN_INACTIVE); +} + +HAL_BOOL +ar5k_ar5211_setSlotTime(hal, slot_time) + struct ath_hal *hal; + u_int slot_time; +{ + if (slot_time < HAL_SLOT_TIME_9 || slot_time > HAL_SLOT_TIME_MAX) + return (AH_FALSE); + + AR5K_REG_WRITE(AR5K_AR5211_DCU_GBL_IFS_SLOT, + ar5k_htoclock(slot_time, hal->ah_turbo)); + + return (AH_TRUE); +} + +u_int +ar5k_ar5211_getSlotTime(hal) + struct ath_hal *hal; +{ + return (ar5k_clocktoh(AR5K_REG_READ(AR5K_AR5211_DCU_GBL_IFS_SLOT) & + 0xffff, hal->ah_turbo)); +} + +HAL_BOOL +ar5k_ar5211_setAckTimeout(hal, timeout) + struct ath_hal *hal; + u_int timeout; +{ + if (ar5k_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_AR5211_TIME_OUT_ACK), + hal->ah_turbo) <= timeout) + return (AH_FALSE); + + AR5K_REG_WRITE_BITS(AR5K_AR5211_TIME_OUT, AR5K_AR5211_TIME_OUT_ACK, + ar5k_htoclock(timeout, hal->ah_turbo)); + + return (AH_TRUE); +} + +u_int +ar5k_ar5211_getAckTimeout(hal) + struct ath_hal *hal; +{ + return (ar5k_clocktoh(AR5K_REG_MS(AR5K_REG_READ(AR5K_AR5211_TIME_OUT), + AR5K_AR5211_TIME_OUT_ACK), hal->ah_turbo)); +} + +HAL_BOOL +ar5k_ar5211_setCTSTimeout(hal, timeout) + struct ath_hal *hal; + u_int timeout; +{ + if (ar5k_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_AR5211_TIME_OUT_CTS), + hal->ah_turbo) <= timeout) + return (AH_FALSE); + + AR5K_REG_WRITE_BITS(AR5K_AR5211_TIME_OUT, AR5K_AR5211_TIME_OUT_CTS, + ar5k_htoclock(timeout, hal->ah_turbo)); + + return (AH_TRUE); +} + +u_int +ar5k_ar5211_getCTSTimeout(hal) + struct ath_hal *hal; +{ + return (ar5k_clocktoh(AR5K_REG_MS(AR5K_REG_READ(AR5K_AR5211_TIME_OUT), + AR5K_AR5211_TIME_OUT_CTS), hal->ah_turbo)); +} + +/* + * Key table (WEP) functions + */ + +HAL_BOOL +ar5k_ar5211_isHwCipherSupported(hal, cipher) + struct ath_hal *hal; + HAL_CIPHER cipher; +{ + /* + * The AR5211 only supports WEP + */ + if (cipher == HAL_CIPHER_WEP) + return (AH_TRUE); + + return (AH_FALSE); +} + +u_int32_t +ar5k_ar5211_getKeyCacheSize(hal) + struct ath_hal *hal; +{ + return (AR5K_AR5211_KEYTABLE_SIZE); +} + +HAL_BOOL +ar5k_ar5211_resetKeyCacheEntry(hal, entry) + struct ath_hal *hal; + u_int16_t entry; +{ + int i; + + AR5K_ASSERT_ENTRY(entry, AR5K_AR5211_KEYTABLE_SIZE); + + for (i = 0; i < AR5K_AR5211_KEYCACHE_SIZE; i++) + AR5K_REG_WRITE(AR5K_AR5211_KEYTABLE(entry) + (i * 4), 0); + + return (AH_FALSE); +} + +HAL_BOOL +ar5k_ar5211_isKeyCacheEntryValid(hal, entry) + struct ath_hal *hal; + u_int16_t entry; +{ + int offset; + + AR5K_ASSERT_ENTRY(entry, AR5K_AR5211_KEYTABLE_SIZE); + + /* + * Check the validation flag at the end of the entry + */ + offset = (AR5K_AR5211_KEYCACHE_SIZE - 1) * 4; + if (AR5K_REG_READ(AR5K_AR5211_KEYTABLE(entry) + offset) & + AR5K_AR5211_KEYTABLE_VALID) + return AH_TRUE; + + return (AH_FALSE); +} + +HAL_BOOL +ar5k_ar5211_setKeyCacheEntry(hal, entry, keyval, mac, xor_notused) + struct ath_hal *hal; + u_int16_t entry; + const HAL_KEYVAL *keyval; + const u_int8_t *mac; + int xor_notused; +{ + int elements = AR5K_AR5211_KEYCACHE_SIZE - 2; + u_int32_t key_v[elements]; + int i, offset = 0; + + AR5K_ASSERT_ENTRY(entry, AR5K_AR5211_KEYTABLE_SIZE); + + /* + * Store the key type in the last field + */ + switch (keyval->wk_len) { + case 5: + key_v[elements - 1] = AR5K_AR5211_KEYTABLE_TYPE_40; + break; + + case 13: + key_v[elements - 1] = AR5K_AR5211_KEYTABLE_TYPE_104; + break; + + case 16: + key_v[elements - 1] = AR5K_AR5211_KEYTABLE_TYPE_128; + break; + + default: + /* Unsupported key length (not WEP40/104/128) */ + return (AH_FALSE); + } + + /* + * Write key cache entry + */ + for (i = 0; i < elements; i++) { + if (elements < 5) { + if (i % 2) { + key_v[i] = AR5K_LE_READ_2(keyval->wk_key + + offset) & 0xffff; + offset += 2; + } else { + key_v[i] = AR5K_LE_READ_4(keyval->wk_key + + offset); + offset += 4; + } + + if (i == 4 && keyval->wk_len <= 13) + key_v[i] &= 0xff; + } + + /* Write value */ + AR5K_REG_WRITE(AR5K_AR5211_KEYTABLE(entry) + (i * 4), key_v[i]); + } + + return (ar5k_ar5211_setKeyCacheEntryMac(hal, entry, mac)); +} + +HAL_BOOL +ar5k_ar5211_setKeyCacheEntryMac(hal, entry, mac) + struct ath_hal *hal; + u_int16_t entry; + const u_int8_t *mac; +{ + u_int32_t low_id, high_id; + int offset; + + /* + * Invalid entry (key table overflow) + */ + AR5K_ASSERT_ENTRY(entry, AR5K_AR5211_KEYTABLE_SIZE); + + offset = AR5K_AR5211_KEYCACHE_SIZE - 2; + + /* XXX big endian problems? */ + bcopy(mac, &low_id, 4); + bcopy(mac + 4, &high_id, 2); + + high_id = 0x0000ffff & htole32(high_id); + + AR5K_REG_WRITE(AR5K_AR5211_KEYTABLE(entry) + (offset++ * 4), + htole32(low_id)); + AR5K_REG_WRITE(AR5K_AR5211_KEYTABLE(entry) + (offset * 4), high_id); + + return (AH_TRUE); +} + +/* + * Power management functions + */ + +HAL_BOOL +ar5k_ar5211_setPowerMode(hal, mode, set_chip, sleep_duration) + struct ath_hal *hal; + HAL_POWER_MODE mode; + HAL_BOOL set_chip; + u_int16_t sleep_duration; +{ + int i; + + switch (mode) { + case HAL_PM_AUTO: + if (set_chip == AH_TRUE) { + AR5K_REG_WRITE(AR5K_AR5211_SCR, + AR5K_AR5211_SCR_SLE | sleep_duration); + } + break; + + case HAL_PM_FULL_SLEEP: + if (set_chip == AH_TRUE) { + AR5K_REG_WRITE(AR5K_AR5211_SCR, + AR5K_AR5211_SCR_SLE_SLP); + } + break; + + case HAL_PM_AWAKE: + if (set_chip == AH_FALSE) + goto commit; + + AR5K_REG_WRITE(AR5K_AR5211_SCR, AR5K_AR5211_SCR_SLE_WAKE); + + for (i = 5000; i > 0; i--) { + /* Check if the AR5211 did wake up */ + if ((AR5K_REG_READ(AR5K_AR5211_PCICFG) & + AR5K_AR5211_PCICFG_SPWR_DN) == 0) + break; + + /* Wait a bit and retry */ + AR5K_DELAY(200); + AR5K_REG_WRITE(AR5K_AR5211_SCR, + AR5K_AR5211_SCR_SLE_WAKE); + } + + /* Fail if the AR5211 didn't wake up */ + if (i <= 0) + return (AH_FALSE); + break; + + case HAL_PM_NETWORK_SLEEP: + case HAL_PM_UNDEFINED: + default: + return (AH_FALSE); + } + + commit: + hal->ah_power_mode = mode; + + AR5K_REG_DISABLE_BITS(AR5K_AR5211_STA_ID1, + AR5K_AR5211_STA_ID1_DEFAULT_ANTENNA); + AR5K_REG_ENABLE_BITS(AR5K_AR5211_STA_ID1, + AR5K_AR5211_STA_ID1_PWR_SV); + + return (AH_TRUE); +} + +HAL_POWER_MODE +ar5k_ar5211_getPowerMode(hal) + struct ath_hal *hal; +{ + return (hal->ah_power_mode); +} + +HAL_BOOL +ar5k_ar5211_queryPSPollSupport(hal) + struct ath_hal *hal; +{ + /* nope */ + return (AH_FALSE); +} + +HAL_BOOL +ar5k_ar5211_initPSPoll(hal) + struct ath_hal *hal; +{ + /* + * Not used on the AR5211 + */ + return (AH_FALSE); +} + +HAL_BOOL +ar5k_ar5211_enablePSPoll(hal, bssid, assoc_id) + struct ath_hal *hal; + u_int8_t *bssid; + u_int16_t assoc_id; +{ + return (AH_FALSE); +} + +HAL_BOOL +ar5k_ar5211_disablePSPoll(hal) + struct ath_hal *hal; +{ + return (AH_FALSE); +} + +/* + * Beacon functions + */ + +void +ar5k_ar5211_beaconInit(hal, next_beacon, interval) + struct ath_hal *hal; + u_int32_t next_beacon; + u_int32_t interval; +{ + u_int32_t timer1, timer2, timer3; + + /* + * Set the additional timers by mode + */ + switch (hal->ah_op_mode) { + case HAL_M_STA: + timer1 = 0x0000ffff; + timer2 = 0x0007ffff; + break; + + default: + timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << + 0x00000003; + timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << + 0x00000003; + } + + timer3 = next_beacon + + (hal->ah_atim_window ? hal->ah_atim_window : 1); + + /* + * Enable all timers and set the beacon register + * (next beacon, DMA beacon, software beacon, ATIM window time) + */ + AR5K_REG_WRITE(AR5K_AR5211_TIMER0, next_beacon); + AR5K_REG_WRITE(AR5K_AR5211_TIMER1, timer1); + AR5K_REG_WRITE(AR5K_AR5211_TIMER2, timer2); + AR5K_REG_WRITE(AR5K_AR5211_TIMER3, timer3); + + AR5K_REG_WRITE(AR5K_AR5211_BEACON, interval & + (AR5K_AR5211_BEACON_PERIOD | AR5K_AR5211_BEACON_RESET_TSF | + AR5K_AR5211_BEACON_ENABLE)); +} + +void +ar5k_ar5211_setStationBeaconTimers(hal, state, tsf, dtim_count, cfp_count) + struct ath_hal *hal; + const HAL_BEACON_STATE *state; + u_int32_t tsf; + u_int32_t dtim_count; + u_int32_t cfp_count; + +{ + u_int32_t cfp_period, next_cfp; + + /* Return on an invalid beacon state */ + if (state->bs_interval < 1) + return; + + /* + * PCF support? + */ + if (state->bs_cfp_period > 0) { + /* Enable CFP mode and set the CFP and timer registers */ + cfp_period = state->bs_cfp_period * state->bs_dtim_period * + state->bs_interval; + next_cfp = (cfp_count * state->bs_dtim_period + dtim_count) * + state->bs_interval; + + AR5K_REG_DISABLE_BITS(AR5K_AR5211_STA_ID1, + AR5K_AR5211_STA_ID1_DEFAULT_ANTENNA | + AR5K_AR5211_STA_ID1_PCF); + AR5K_REG_WRITE(AR5K_AR5211_CFP_PERIOD, cfp_period); + AR5K_REG_WRITE(AR5K_AR5211_CFP_DUR, state->bs_cfp_max_duration); + AR5K_REG_WRITE(AR5K_AR5211_TIMER2, + (tsf + (next_cfp == 0 ? cfp_period : next_cfp)) << 3); + } else { + /* Disable PCF mode */ + AR5K_REG_DISABLE_BITS(AR5K_AR5211_STA_ID1, + AR5K_AR5211_STA_ID1_DEFAULT_ANTENNA | + AR5K_AR5211_STA_ID1_PCF); + } + + /* + * Enable the beacon timer register + */ + AR5K_REG_WRITE(AR5K_AR5211_TIMER0, state->bs_next_beacon); + + /* + * Start the beacon timers + */ + AR5K_REG_WRITE(AR5K_AR5211_BEACON, + (AR5K_REG_READ(AR5K_AR5211_BEACON) &~ + (AR5K_AR5211_BEACON_PERIOD | AR5K_AR5211_BEACON_TIM)) | + AR5K_REG_SM(state->bs_tim_offset ? state->bs_tim_offset + 4 : 0, + AR5K_AR5211_BEACON_TIM) | AR5K_REG_SM(state->bs_interval, + AR5K_AR5211_BEACON_PERIOD)); + + /* + * Write new beacon miss threshold, if it appears to be valid + */ + if ((state->bs_bmiss_threshold > (AR5K_AR5211_RSSI_THR_BMISS >> + AR5K_AR5211_RSSI_THR_BMISS_S)) && + (state->bs_bmiss_threshold & 0x00007) != 0) { + AR5K_REG_WRITE_BITS(AR5K_AR5211_RSSI_THR_M, + AR5K_AR5211_RSSI_THR_BMISS, state->bs_bmiss_threshold); + } +} + +void +ar5k_ar5211_resetStationBeaconTimers(hal) + struct ath_hal *hal; +{ + /* + * Disable beacon timer + */ + AR5K_REG_WRITE(AR5K_AR5211_TIMER0, 0); + + /* + * Disable some beacon register values + */ + AR5K_REG_DISABLE_BITS(AR5K_AR5211_STA_ID1, + AR5K_AR5211_STA_ID1_DEFAULT_ANTENNA | AR5K_AR5211_STA_ID1_PCF); + AR5K_REG_WRITE(AR5K_AR5211_BEACON, AR5K_AR5211_BEACON_PERIOD); +} + +HAL_BOOL +ar5k_ar5211_waitForBeaconDone(hal, phys_addr) + struct ath_hal *hal; + bus_addr_t phys_addr; +{ + HAL_BOOL ret; + + /* + * Wait for beaconn queue to be done + */ + ret = ar5k_register_timeout(hal, + AR5K_AR5211_QCU_STS(HAL_TX_QUEUE_BEACON), + AR5K_AR5211_QCU_STS_FRMPENDCNT, 0, AH_FALSE); + + return (ret); +} + +/* + * Interrupt handling + */ + +HAL_BOOL +ar5k_ar5211_isInterruptPending(hal) + struct ath_hal *hal; +{ + return (AR5K_REG_READ(AR5K_AR5211_INTPEND) == 0 ? AH_FALSE : AH_TRUE); +} + +HAL_BOOL +ar5k_ar5211_getPendingInterrupts(hal, interrupt_mask) + struct ath_hal *hal; + u_int32_t *interrupt_mask; +{ + u_int32_t data; + + /* + * Read interrupt status from the Read-And-Clear shadow register + */ + data = AR5K_REG_READ(AR5K_AR5211_RAC_PISR); + + /* + * Get abstract interrupt mask (HAL-compatible) + */ + *interrupt_mask = (data & HAL_INT_COMMON) & hal->ah_imr; + + if (data == HAL_INT_NOCARD) + return (AH_FALSE); + + if (data & (AR5K_AR5211_PISR_RXOK | AR5K_AR5211_PISR_RXERR)) + *interrupt_mask |= HAL_INT_RX; + + if (data & (AR5K_AR5211_PISR_TXOK | AR5K_AR5211_PISR_TXERR)) + *interrupt_mask |= HAL_INT_TX; + + if (data & (AR5K_AR5211_PISR_HIUERR)) + *interrupt_mask |= HAL_INT_FATAL; + + /* + * Special interrupt handling (not catched by the driver) + */ + if (((*interrupt_mask) & AR5K_AR5211_PISR_RXPHY) && + hal->ah_radar.r_enabled == AH_TRUE) + ar5k_radar_alert(hal); + + return (AH_TRUE); +} + +u_int32_t +ar5k_ar5211_getInterrupts(hal) + struct ath_hal *hal; +{ + /* Return the interrupt mask stored previously */ + return (hal->ah_imr); +} + +HAL_INT +ar5k_ar5211_setInterrupts(hal, new_mask) + struct ath_hal *hal; + HAL_INT new_mask; +{ + HAL_INT old_mask, int_mask; + + /* + * Disable card interrupts to prevent any race conditions + * (they will be re-enabled afterwards). + */ + AR5K_REG_WRITE(AR5K_AR5211_IER, AR5K_AR5211_IER_DISABLE); + + old_mask = hal->ah_imr; + + /* + * Add additional, chipset-dependent interrupt mask flags + * and write them to the IMR (interrupt mask register). + */ + int_mask = new_mask & HAL_INT_COMMON; + + if (new_mask & HAL_INT_RX) + int_mask |= + AR5K_AR5211_PIMR_RXOK | + AR5K_AR5211_PIMR_RXERR | + AR5K_AR5211_PIMR_RXORN | + AR5K_AR5211_PIMR_RXDESC; + + if (new_mask & HAL_INT_TX) + int_mask |= + AR5K_AR5211_PIMR_TXOK | + AR5K_AR5211_PIMR_TXERR | + AR5K_AR5211_PIMR_TXDESC | + AR5K_AR5211_PIMR_TXURN; + + if (new_mask & HAL_INT_FATAL) { + int_mask |= AR5K_AR5211_PIMR_HIUERR; + AR5K_REG_ENABLE_BITS(AR5K_AR5211_SIMR2, + AR5K_AR5211_SIMR2_MCABT | + AR5K_AR5211_SIMR2_SSERR | + AR5K_AR5211_SIMR2_DPERR); + } + + if (hal->ah_op_mode & HAL_M_HOSTAP) { + int_mask |= AR5K_AR5211_PIMR_MIB; + } else { + int_mask &= ~AR5K_AR5211_PIMR_MIB; + } + + AR5K_REG_WRITE(AR5K_AR5211_PIMR, int_mask); + + /* Store new interrupt mask */ + hal->ah_imr = new_mask; + + /* ..re-enable interrupts */ + AR5K_REG_WRITE(AR5K_AR5211_IER, AR5K_AR5211_IER_ENABLE); + + return (old_mask); +} + +/* + * Misc internal functions + */ + +HAL_BOOL +ar5k_ar5211_get_capabilities(hal) + struct ath_hal *hal; +{ + u_int16_t ee_header; + + /* Capabilities stored in the EEPROM */ + ee_header = hal->ah_capabilities.cap_eeprom.ee_header; + + /* + * XXX The AR5211 tranceiver supports frequencies from 4920 to 6100GHz + * XXX and from 2312 to 2732GHz. There are problems with the current + * XXX ieee80211 implementation because the IEEE channel mapping + * XXX does not support negative channel numbers (2312MHz is channel + * XXX -19). Of course, this doesn't matter because these channels + * XXX are out of range but some regulation domains like MKK (Japan) + * XXX will support frequencies somewhere around 4.8GHz. + */ + + /* + * Set radio capabilities + */ + + if (AR5K_EEPROM_HDR_11A(ee_header)) { + hal->ah_capabilities.cap_range.range_5ghz_min = 5005; /* 4920 */ + hal->ah_capabilities.cap_range.range_5ghz_max = 6100; + + /* Set supported modes */ + hal->ah_capabilities.cap_mode = HAL_MODE_11A | HAL_MODE_TURBO; + } + + /* This chip will support 802.11b if the 2GHz radio is connected */ + if (AR5K_EEPROM_HDR_11B(ee_header) || AR5K_EEPROM_HDR_11G(ee_header)) { + hal->ah_capabilities.cap_range.range_2ghz_min = 2412; /* 2312 */ + hal->ah_capabilities.cap_range.range_2ghz_max = 2732; + hal->ah_capabilities.cap_mode |= HAL_MODE_11B; + + if (AR5K_EEPROM_HDR_11B(ee_header)) + hal->ah_capabilities.cap_mode |= HAL_MODE_11B; + if (AR5K_EEPROM_HDR_11G(ee_header)) + hal->ah_capabilities.cap_mode |= HAL_MODE_11G; + } + + /* GPIO */ + hal->ah_gpio_npins = AR5K_AR5211_NUM_GPIO; + + /* Set number of supported TX queues */ + hal->ah_capabilities.cap_queues.q_tx_num = AR5K_AR5211_TX_NUM_QUEUES; + + return (AH_TRUE); +} + +void +ar5k_ar5211_radar_alert(hal, enable) + struct ath_hal *hal; + HAL_BOOL enable; +{ + /* + * Enable radar detection + */ + AR5K_REG_WRITE(AR5K_AR5211_IER, AR5K_AR5211_IER_DISABLE); + + if (enable == AH_TRUE) { + AR5K_REG_WRITE(AR5K_AR5211_PHY_RADAR, + AR5K_AR5211_PHY_RADAR_ENABLE); + AR5K_REG_ENABLE_BITS(AR5K_AR5211_PIMR, + AR5K_AR5211_PIMR_RXPHY); + } else { + AR5K_REG_WRITE(AR5K_AR5211_PHY_RADAR, + AR5K_AR5211_PHY_RADAR_DISABLE); + AR5K_REG_DISABLE_BITS(AR5K_AR5211_PIMR, + AR5K_AR5211_PIMR_RXPHY); + } + + AR5K_REG_WRITE(AR5K_AR5211_IER, AR5K_AR5211_IER_ENABLE); +} + +/* + * EEPROM access functions + */ + +HAL_BOOL +ar5k_ar5211_eeprom_is_busy(hal) + struct ath_hal *hal; +{ + return (AR5K_REG_READ(AR5K_AR5211_CFG) & AR5K_AR5211_CFG_EEBS ? + AH_TRUE : AH_FALSE); +} + +int +ar5k_ar5211_eeprom_read(hal, offset, data) + struct ath_hal *hal; + u_int32_t offset; + u_int16_t *data; +{ + u_int32_t status, i; + + /* + * Initialize EEPROM access + */ + AR5K_REG_WRITE(AR5K_AR5211_EEPROM_BASE, (u_int8_t)offset); + AR5K_REG_ENABLE_BITS(AR5K_AR5211_EEPROM_CMD, + AR5K_AR5211_EEPROM_CMD_READ); + + for (i = AR5K_TUNE_REGISTER_TIMEOUT; i > 0; i--) { + status = AR5K_REG_READ(AR5K_AR5211_EEPROM_STATUS); + if (status & AR5K_AR5211_EEPROM_STAT_RDDONE) { + if (status & AR5K_AR5211_EEPROM_STAT_RDERR) + return (EIO); + *data = (u_int16_t) + (AR5K_REG_READ(AR5K_AR5211_EEPROM_DATA) & 0xffff); + return (0); + } + AR5K_DELAY(15); + } + + return (ETIMEDOUT); +} + +int +ar5k_ar5211_eeprom_write(hal, offset, data) + struct ath_hal *hal; + u_int32_t offset; + u_int16_t data; +{ + u_int32_t status, timeout; + + /* Enable eeprom access */ + AR5K_REG_ENABLE_BITS(AR5K_AR5211_EEPROM_CMD, + AR5K_AR5211_EEPROM_CMD_RESET); + AR5K_REG_ENABLE_BITS(AR5K_AR5211_EEPROM_CMD, + AR5K_AR5211_EEPROM_CMD_WRITE); + + /* + * Prime write pump + */ + AR5K_REG_WRITE(AR5K_AR5211_EEPROM_BASE, (u_int8_t)offset - 1); + + for (timeout = 10000; timeout > 0; timeout--) { + AR5K_DELAY(1); + status = AR5K_REG_READ(AR5K_AR5211_EEPROM_STATUS); + if (status & AR5K_AR5211_EEPROM_STAT_WRDONE) { + if (status & AR5K_AR5211_EEPROM_STAT_WRERR) + return (EIO); + return (0); + } + } + + return (ETIMEDOUT); +} |