162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> 362306a36Sopenharmony_ci * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Permission to use, copy, modify, and distribute this software for any 662306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 762306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1062306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1162306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1262306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1362306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1462306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1562306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/*************************************\ 2062306a36Sopenharmony_ci* DMA and interrupt masking functions * 2162306a36Sopenharmony_ci\*************************************/ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/** 2462306a36Sopenharmony_ci * DOC: DMA and interrupt masking functions 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and 2762306a36Sopenharmony_ci * handle queue setup for 5210 chipset (rest are handled on qcu.c). 2862306a36Sopenharmony_ci * Also we setup interrupt mask register (IMR) and read the various interrupt 2962306a36Sopenharmony_ci * status registers (ISR). 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "ath5k.h" 3562306a36Sopenharmony_ci#include "reg.h" 3662306a36Sopenharmony_ci#include "debug.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/*********\ 4062306a36Sopenharmony_ci* Receive * 4162306a36Sopenharmony_ci\*********/ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/** 4462306a36Sopenharmony_ci * ath5k_hw_start_rx_dma() - Start DMA receive 4562306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_civoid 4862306a36Sopenharmony_ciath5k_hw_start_rx_dma(struct ath5k_hw *ah) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR); 5162306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_CR); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/** 5562306a36Sopenharmony_ci * ath5k_hw_stop_rx_dma() - Stop DMA receive 5662306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic int 5962306a36Sopenharmony_ciath5k_hw_stop_rx_dma(struct ath5k_hw *ah) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci unsigned int i; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * It may take some time to disable the DMA receive unit 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci for (i = 1000; i > 0 && 6962306a36Sopenharmony_ci (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0; 7062306a36Sopenharmony_ci i--) 7162306a36Sopenharmony_ci udelay(100); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (!i) 7462306a36Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 7562306a36Sopenharmony_ci "failed to stop RX DMA !\n"); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return i ? 0 : -EBUSY; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/** 8162306a36Sopenharmony_ci * ath5k_hw_get_rxdp() - Get RX Descriptor's address 8262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_ciu32 8562306a36Sopenharmony_ciath5k_hw_get_rxdp(struct ath5k_hw *ah) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci return ath5k_hw_reg_read(ah, AR5K_RXDP); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/** 9162306a36Sopenharmony_ci * ath5k_hw_set_rxdp() - Set RX Descriptor's address 9262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 9362306a36Sopenharmony_ci * @phys_addr: RX descriptor address 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * Returns -EIO if rx is active 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ciint 9862306a36Sopenharmony_ciath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci if (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) { 10162306a36Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 10262306a36Sopenharmony_ci "tried to set RXDP while rx was active !\n"); 10362306a36Sopenharmony_ci return -EIO; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP); 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/**********\ 11262306a36Sopenharmony_ci* Transmit * 11362306a36Sopenharmony_ci\**********/ 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/** 11662306a36Sopenharmony_ci * ath5k_hw_start_tx_dma() - Start DMA transmit for a specific queue 11762306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 11862306a36Sopenharmony_ci * @queue: The hw queue number 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * Start DMA transmit for a specific queue and since 5210 doesn't have 12162306a36Sopenharmony_ci * QCU/DCU, set up queue parameters for 5210 here based on queue type (one 12262306a36Sopenharmony_ci * queue for normal data and one queue for beacons). For queue setup 12362306a36Sopenharmony_ci * on newer chips check out qcu.c. Returns -EINVAL if queue number is out 12462306a36Sopenharmony_ci * of range or if queue is already disabled. 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci * NOTE: Must be called after setting up tx control descriptor for that 12762306a36Sopenharmony_ci * queue (see below). 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ciint 13062306a36Sopenharmony_ciath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci u32 tx_queue; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Return if queue is declared inactive */ 13762306a36Sopenharmony_ci if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) 13862306a36Sopenharmony_ci return -EINVAL; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 14162306a36Sopenharmony_ci tx_queue = ath5k_hw_reg_read(ah, AR5K_CR); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * Set the queue by type on 5210 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci switch (ah->ah_txq[queue].tqi_type) { 14762306a36Sopenharmony_ci case AR5K_TX_QUEUE_DATA: 14862306a36Sopenharmony_ci tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci case AR5K_TX_QUEUE_BEACON: 15162306a36Sopenharmony_ci tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; 15262306a36Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, 15362306a36Sopenharmony_ci AR5K_BSR); 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci case AR5K_TX_QUEUE_CAB: 15662306a36Sopenharmony_ci tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; 15762306a36Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V | 15862306a36Sopenharmony_ci AR5K_BCR_BDMAE, AR5K_BSR); 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci default: 16162306a36Sopenharmony_ci return -EINVAL; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci /* Start queue */ 16462306a36Sopenharmony_ci ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); 16562306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_CR); 16662306a36Sopenharmony_ci } else { 16762306a36Sopenharmony_ci /* Return if queue is disabled */ 16862306a36Sopenharmony_ci if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue)) 16962306a36Sopenharmony_ci return -EIO; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Start queue */ 17262306a36Sopenharmony_ci AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXE, queue); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return 0; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/** 17962306a36Sopenharmony_ci * ath5k_hw_stop_tx_dma() - Stop DMA transmit on a specific queue 18062306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 18162306a36Sopenharmony_ci * @queue: The hw queue number 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * Stop DMA transmit on a specific hw queue and drain queue so we don't 18462306a36Sopenharmony_ci * have any pending frames. Returns -EBUSY if we still have pending frames, 18562306a36Sopenharmony_ci * -EINVAL if queue number is out of range or inactive. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_cistatic int 18862306a36Sopenharmony_ciath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci unsigned int i = 40; 19162306a36Sopenharmony_ci u32 tx_queue, pending; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Return if queue is declared inactive */ 19662306a36Sopenharmony_ci if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 20062306a36Sopenharmony_ci tx_queue = ath5k_hw_reg_read(ah, AR5K_CR); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci * Set by queue type 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci switch (ah->ah_txq[queue].tqi_type) { 20662306a36Sopenharmony_ci case AR5K_TX_QUEUE_DATA: 20762306a36Sopenharmony_ci tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0; 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci case AR5K_TX_QUEUE_BEACON: 21062306a36Sopenharmony_ci case AR5K_TX_QUEUE_CAB: 21162306a36Sopenharmony_ci /* XXX Fix me... */ 21262306a36Sopenharmony_ci tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1; 21362306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_BSR); 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci default: 21662306a36Sopenharmony_ci return -EINVAL; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* Stop queue */ 22062306a36Sopenharmony_ci ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); 22162306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_CR); 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * Enable DCU early termination to quickly 22662306a36Sopenharmony_ci * flush any pending frames from QCU 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), 22962306a36Sopenharmony_ci AR5K_QCU_MISC_DCU_EARLY); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* 23262306a36Sopenharmony_ci * Schedule TX disable and wait until queue is empty 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* Wait for queue to stop */ 23762306a36Sopenharmony_ci for (i = 1000; i > 0 && 23862306a36Sopenharmony_ci (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue) != 0); 23962306a36Sopenharmony_ci i--) 24062306a36Sopenharmony_ci udelay(100); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) 24362306a36Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 24462306a36Sopenharmony_ci "queue %i didn't stop !\n", queue); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Check for pending frames */ 24762306a36Sopenharmony_ci i = 1000; 24862306a36Sopenharmony_ci do { 24962306a36Sopenharmony_ci pending = ath5k_hw_reg_read(ah, 25062306a36Sopenharmony_ci AR5K_QUEUE_STATUS(queue)) & 25162306a36Sopenharmony_ci AR5K_QCU_STS_FRMPENDCNT; 25262306a36Sopenharmony_ci udelay(100); 25362306a36Sopenharmony_ci } while (--i && pending); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* For 2413+ order PCU to drop packets using 25662306a36Sopenharmony_ci * QUIET mechanism */ 25762306a36Sopenharmony_ci if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) && 25862306a36Sopenharmony_ci pending) { 25962306a36Sopenharmony_ci /* Set periodicity and duration */ 26062306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 26162306a36Sopenharmony_ci AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)| 26262306a36Sopenharmony_ci AR5K_REG_SM(10, AR5K_QUIET_CTL2_QT_DUR), 26362306a36Sopenharmony_ci AR5K_QUIET_CTL2); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* Enable quiet period for current TSF */ 26662306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 26762306a36Sopenharmony_ci AR5K_QUIET_CTL1_QT_EN | 26862306a36Sopenharmony_ci AR5K_REG_SM(ath5k_hw_reg_read(ah, 26962306a36Sopenharmony_ci AR5K_TSF_L32_5211) >> 10, 27062306a36Sopenharmony_ci AR5K_QUIET_CTL1_NEXT_QT_TSF), 27162306a36Sopenharmony_ci AR5K_QUIET_CTL1); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Force channel idle high */ 27462306a36Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211, 27562306a36Sopenharmony_ci AR5K_DIAG_SW_CHANNEL_IDLE_HIGH); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Wait a while and disable mechanism */ 27862306a36Sopenharmony_ci udelay(400); 27962306a36Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1, 28062306a36Sopenharmony_ci AR5K_QUIET_CTL1_QT_EN); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Re-check for pending frames */ 28362306a36Sopenharmony_ci i = 100; 28462306a36Sopenharmony_ci do { 28562306a36Sopenharmony_ci pending = ath5k_hw_reg_read(ah, 28662306a36Sopenharmony_ci AR5K_QUEUE_STATUS(queue)) & 28762306a36Sopenharmony_ci AR5K_QCU_STS_FRMPENDCNT; 28862306a36Sopenharmony_ci udelay(100); 28962306a36Sopenharmony_ci } while (--i && pending); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211, 29262306a36Sopenharmony_ci AR5K_DIAG_SW_CHANNEL_IDLE_HIGH); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (pending) 29562306a36Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 29662306a36Sopenharmony_ci "quiet mechanism didn't work q:%i !\n", 29762306a36Sopenharmony_ci queue); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * Disable DCU early termination 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_QUEUE_MISC(queue), 30462306a36Sopenharmony_ci AR5K_QCU_MISC_DCU_EARLY); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Clear register */ 30762306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD); 30862306a36Sopenharmony_ci if (pending) { 30962306a36Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 31062306a36Sopenharmony_ci "tx dma didn't stop (q:%i, frm:%i) !\n", 31162306a36Sopenharmony_ci queue, pending); 31262306a36Sopenharmony_ci return -EBUSY; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* TODO: Check for success on 5210 else return error */ 31762306a36Sopenharmony_ci return 0; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/** 32162306a36Sopenharmony_ci * ath5k_hw_stop_beacon_queue() - Stop beacon queue 32262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 32362306a36Sopenharmony_ci * @queue: The queue number 32462306a36Sopenharmony_ci * 32562306a36Sopenharmony_ci * Returns -EIO if queue didn't stop 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ciint 32862306a36Sopenharmony_ciath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci int ret; 33162306a36Sopenharmony_ci ret = ath5k_hw_stop_tx_dma(ah, queue); 33262306a36Sopenharmony_ci if (ret) { 33362306a36Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 33462306a36Sopenharmony_ci "beacon queue didn't stop !\n"); 33562306a36Sopenharmony_ci return -EIO; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/** 34162306a36Sopenharmony_ci * ath5k_hw_get_txdp() - Get TX Descriptor's address for a specific queue 34262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 34362306a36Sopenharmony_ci * @queue: The hw queue number 34462306a36Sopenharmony_ci * 34562306a36Sopenharmony_ci * Get TX descriptor's address for a specific queue. For 5210 we ignore 34662306a36Sopenharmony_ci * the queue number and use tx queue type since we only have 2 queues. 34762306a36Sopenharmony_ci * We use TXDP0 for normal data queue and TXDP1 for beacon queue. 34862306a36Sopenharmony_ci * For newer chips with QCU/DCU we just read the corresponding TXDP register. 34962306a36Sopenharmony_ci * 35062306a36Sopenharmony_ci * XXX: Is TXDP read and clear ? 35162306a36Sopenharmony_ci */ 35262306a36Sopenharmony_ciu32 35362306a36Sopenharmony_ciath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci u16 tx_reg; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * Get the transmit queue descriptor pointer from the selected queue 36162306a36Sopenharmony_ci */ 36262306a36Sopenharmony_ci /*5210 doesn't have QCU*/ 36362306a36Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 36462306a36Sopenharmony_ci switch (ah->ah_txq[queue].tqi_type) { 36562306a36Sopenharmony_ci case AR5K_TX_QUEUE_DATA: 36662306a36Sopenharmony_ci tx_reg = AR5K_NOQCU_TXDP0; 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci case AR5K_TX_QUEUE_BEACON: 36962306a36Sopenharmony_ci case AR5K_TX_QUEUE_CAB: 37062306a36Sopenharmony_ci tx_reg = AR5K_NOQCU_TXDP1; 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci default: 37362306a36Sopenharmony_ci return 0xffffffff; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci } else { 37662306a36Sopenharmony_ci tx_reg = AR5K_QUEUE_TXDP(queue); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return ath5k_hw_reg_read(ah, tx_reg); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci/** 38362306a36Sopenharmony_ci * ath5k_hw_set_txdp() - Set TX Descriptor's address for a specific queue 38462306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 38562306a36Sopenharmony_ci * @queue: The hw queue number 38662306a36Sopenharmony_ci * @phys_addr: The physical address 38762306a36Sopenharmony_ci * 38862306a36Sopenharmony_ci * Set TX descriptor's address for a specific queue. For 5210 we ignore 38962306a36Sopenharmony_ci * the queue number and we use tx queue type since we only have 2 queues 39062306a36Sopenharmony_ci * so as above we use TXDP0 for normal data queue and TXDP1 for beacon queue. 39162306a36Sopenharmony_ci * For newer chips with QCU/DCU we just set the corresponding TXDP register. 39262306a36Sopenharmony_ci * Returns -EINVAL if queue type is invalid for 5210 and -EIO if queue is still 39362306a36Sopenharmony_ci * active. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ciint 39662306a36Sopenharmony_ciath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci u16 tx_reg; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* 40362306a36Sopenharmony_ci * Set the transmit queue descriptor pointer register by type 40462306a36Sopenharmony_ci * on 5210 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 40762306a36Sopenharmony_ci switch (ah->ah_txq[queue].tqi_type) { 40862306a36Sopenharmony_ci case AR5K_TX_QUEUE_DATA: 40962306a36Sopenharmony_ci tx_reg = AR5K_NOQCU_TXDP0; 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci case AR5K_TX_QUEUE_BEACON: 41262306a36Sopenharmony_ci case AR5K_TX_QUEUE_CAB: 41362306a36Sopenharmony_ci tx_reg = AR5K_NOQCU_TXDP1; 41462306a36Sopenharmony_ci break; 41562306a36Sopenharmony_ci default: 41662306a36Sopenharmony_ci return -EINVAL; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci } else { 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * Set the transmit queue descriptor pointer for 42162306a36Sopenharmony_ci * the selected queue on QCU for 5211+ 42262306a36Sopenharmony_ci * (this won't work if the queue is still active) 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) 42562306a36Sopenharmony_ci return -EIO; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci tx_reg = AR5K_QUEUE_TXDP(queue); 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Set descriptor pointer */ 43162306a36Sopenharmony_ci ath5k_hw_reg_write(ah, phys_addr, tx_reg); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return 0; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * ath5k_hw_update_tx_triglevel() - Update tx trigger level 43862306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 43962306a36Sopenharmony_ci * @increase: Flag to force increase of trigger level 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * This function increases/decreases the tx trigger level for the tx fifo 44262306a36Sopenharmony_ci * buffer (aka FIFO threshold) that is used to indicate when PCU flushes 44362306a36Sopenharmony_ci * the buffer and transmits its data. Lowering this results sending small 44462306a36Sopenharmony_ci * frames more quickly but can lead to tx underruns, raising it a lot can 44562306a36Sopenharmony_ci * result other problems. Right now we start with the lowest possible 44662306a36Sopenharmony_ci * (64Bytes) and if we get tx underrun we increase it using the increase 44762306a36Sopenharmony_ci * flag. Returns -EIO if we have reached maximum/minimum. 44862306a36Sopenharmony_ci * 44962306a36Sopenharmony_ci * XXX: Link this with tx DMA size ? 45062306a36Sopenharmony_ci * XXX2: Use it to save interrupts ? 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ciint 45362306a36Sopenharmony_ciath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci u32 trigger_level, imr; 45662306a36Sopenharmony_ci int ret = -EIO; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* 45962306a36Sopenharmony_ci * Disable interrupts by setting the mask 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci imr = ath5k_hw_set_imr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG), 46462306a36Sopenharmony_ci AR5K_TXCFG_TXFULL); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (!increase) { 46762306a36Sopenharmony_ci if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES) 46862306a36Sopenharmony_ci goto done; 46962306a36Sopenharmony_ci } else 47062306a36Sopenharmony_ci trigger_level += 47162306a36Sopenharmony_ci ((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * Update trigger level on success 47562306a36Sopenharmony_ci */ 47662306a36Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) 47762306a36Sopenharmony_ci ath5k_hw_reg_write(ah, trigger_level, AR5K_TRIG_LVL); 47862306a36Sopenharmony_ci else 47962306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, 48062306a36Sopenharmony_ci AR5K_TXCFG_TXFULL, trigger_level); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci ret = 0; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_cidone: 48562306a36Sopenharmony_ci /* 48662306a36Sopenharmony_ci * Restore interrupt mask 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci ath5k_hw_set_imr(ah, imr); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return ret; 49162306a36Sopenharmony_ci} 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/*******************\ 49562306a36Sopenharmony_ci* Interrupt masking * 49662306a36Sopenharmony_ci\*******************/ 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/** 49962306a36Sopenharmony_ci * ath5k_hw_is_intr_pending() - Check if we have pending interrupts 50062306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 50162306a36Sopenharmony_ci * 50262306a36Sopenharmony_ci * Check if we have pending interrupts to process. Returns 1 if we 50362306a36Sopenharmony_ci * have pending interrupts and 0 if we haven't. 50462306a36Sopenharmony_ci */ 50562306a36Sopenharmony_cibool 50662306a36Sopenharmony_ciath5k_hw_is_intr_pending(struct ath5k_hw *ah) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0; 50962306a36Sopenharmony_ci} 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci/** 51262306a36Sopenharmony_ci * ath5k_hw_get_isr() - Get interrupt status 51362306a36Sopenharmony_ci * @ah: The @struct ath5k_hw 51462306a36Sopenharmony_ci * @interrupt_mask: Driver's interrupt mask used to filter out 51562306a36Sopenharmony_ci * interrupts in sw. 51662306a36Sopenharmony_ci * 51762306a36Sopenharmony_ci * This function is used inside our interrupt handler to determine the reason 51862306a36Sopenharmony_ci * for the interrupt by reading Primary Interrupt Status Register. Returns an 51962306a36Sopenharmony_ci * abstract interrupt status mask which is mostly ISR with some uncommon bits 52062306a36Sopenharmony_ci * being mapped on some standard non hw-specific positions 52162306a36Sopenharmony_ci * (check out &ath5k_int). 52262306a36Sopenharmony_ci * 52362306a36Sopenharmony_ci * NOTE: We do write-to-clear, so the active PISR/SISR bits at the time this 52462306a36Sopenharmony_ci * function gets called are cleared on return. 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ciint 52762306a36Sopenharmony_ciath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci u32 data = 0; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* 53262306a36Sopenharmony_ci * Read interrupt status from Primary Interrupt 53362306a36Sopenharmony_ci * Register. 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * Note: PISR/SISR Not available on 5210 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 53862306a36Sopenharmony_ci u32 isr = 0; 53962306a36Sopenharmony_ci isr = ath5k_hw_reg_read(ah, AR5K_ISR); 54062306a36Sopenharmony_ci if (unlikely(isr == AR5K_INT_NOCARD)) { 54162306a36Sopenharmony_ci *interrupt_mask = isr; 54262306a36Sopenharmony_ci return -ENODEV; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* 54662306a36Sopenharmony_ci * Filter out the non-common bits from the interrupt 54762306a36Sopenharmony_ci * status. 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ci *interrupt_mask = (isr & AR5K_INT_COMMON) & ah->ah_imr; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Hanlde INT_FATAL */ 55262306a36Sopenharmony_ci if (unlikely(isr & (AR5K_ISR_SSERR | AR5K_ISR_MCABT 55362306a36Sopenharmony_ci | AR5K_ISR_DPERR))) 55462306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_FATAL; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* 55762306a36Sopenharmony_ci * XXX: BMISS interrupts may occur after association. 55862306a36Sopenharmony_ci * I found this on 5210 code but it needs testing. If this is 55962306a36Sopenharmony_ci * true we should disable them before assoc and re-enable them 56062306a36Sopenharmony_ci * after a successful assoc + some jiffies. 56162306a36Sopenharmony_ci interrupt_mask &= ~AR5K_INT_BMISS; 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci data = isr; 56562306a36Sopenharmony_ci } else { 56662306a36Sopenharmony_ci u32 pisr = 0; 56762306a36Sopenharmony_ci u32 pisr_clear = 0; 56862306a36Sopenharmony_ci u32 sisr0 = 0; 56962306a36Sopenharmony_ci u32 sisr1 = 0; 57062306a36Sopenharmony_ci u32 sisr2 = 0; 57162306a36Sopenharmony_ci u32 sisr3 = 0; 57262306a36Sopenharmony_ci u32 sisr4 = 0; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Read PISR and SISRs... */ 57562306a36Sopenharmony_ci pisr = ath5k_hw_reg_read(ah, AR5K_PISR); 57662306a36Sopenharmony_ci if (unlikely(pisr == AR5K_INT_NOCARD)) { 57762306a36Sopenharmony_ci *interrupt_mask = pisr; 57862306a36Sopenharmony_ci return -ENODEV; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci sisr0 = ath5k_hw_reg_read(ah, AR5K_SISR0); 58262306a36Sopenharmony_ci sisr1 = ath5k_hw_reg_read(ah, AR5K_SISR1); 58362306a36Sopenharmony_ci sisr2 = ath5k_hw_reg_read(ah, AR5K_SISR2); 58462306a36Sopenharmony_ci sisr3 = ath5k_hw_reg_read(ah, AR5K_SISR3); 58562306a36Sopenharmony_ci sisr4 = ath5k_hw_reg_read(ah, AR5K_SISR4); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* 58862306a36Sopenharmony_ci * PISR holds the logical OR of interrupt bits 58962306a36Sopenharmony_ci * from SISR registers: 59062306a36Sopenharmony_ci * 59162306a36Sopenharmony_ci * TXOK and TXDESC -> Logical OR of TXOK and TXDESC 59262306a36Sopenharmony_ci * per-queue bits on SISR0 59362306a36Sopenharmony_ci * 59462306a36Sopenharmony_ci * TXERR and TXEOL -> Logical OR of TXERR and TXEOL 59562306a36Sopenharmony_ci * per-queue bits on SISR1 59662306a36Sopenharmony_ci * 59762306a36Sopenharmony_ci * TXURN -> Logical OR of TXURN per-queue bits on SISR2 59862306a36Sopenharmony_ci * 59962306a36Sopenharmony_ci * HIUERR -> Logical OR of MCABT, SSERR and DPER bits on SISR2 60062306a36Sopenharmony_ci * 60162306a36Sopenharmony_ci * BCNMISC -> Logical OR of TIM, CAB_END, DTIM_SYNC 60262306a36Sopenharmony_ci * BCN_TIMEOUT, CAB_TIMEOUT and DTIM 60362306a36Sopenharmony_ci * (and TSFOOR ?) bits on SISR2 60462306a36Sopenharmony_ci * 60562306a36Sopenharmony_ci * QCBRORN and QCBRURN -> Logical OR of QCBRORN and 60662306a36Sopenharmony_ci * QCBRURN per-queue bits on SISR3 60762306a36Sopenharmony_ci * QTRIG -> Logical OR of QTRIG per-queue bits on SISR4 60862306a36Sopenharmony_ci * 60962306a36Sopenharmony_ci * If we clean these bits on PISR we 'll also clear all 61062306a36Sopenharmony_ci * related bits from SISRs, e.g. if we write the TXOK bit on 61162306a36Sopenharmony_ci * PISR we 'll clean all TXOK bits from SISR0 so if a new TXOK 61262306a36Sopenharmony_ci * interrupt got fired for another queue while we were reading 61362306a36Sopenharmony_ci * the interrupt registers and we write back the TXOK bit on 61462306a36Sopenharmony_ci * PISR we 'll lose it. So make sure that we don't write back 61562306a36Sopenharmony_ci * on PISR any bits that come from SISRs. Clearing them from 61662306a36Sopenharmony_ci * SISRs will also clear PISR so no need to worry here. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* XXX: There seems to be an issue on some cards 62062306a36Sopenharmony_ci * with tx interrupt flags not being updated 62162306a36Sopenharmony_ci * on PISR despite that all Tx interrupt bits 62262306a36Sopenharmony_ci * are cleared on SISRs. Since we handle all 62362306a36Sopenharmony_ci * Tx queues all together it shouldn't be an 62462306a36Sopenharmony_ci * issue if we clear Tx interrupt flags also 62562306a36Sopenharmony_ci * on PISR to avoid that. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci pisr_clear = (pisr & ~AR5K_ISR_BITS_FROM_SISRS) | 62862306a36Sopenharmony_ci (pisr & AR5K_INT_TX_ALL); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci /* 63162306a36Sopenharmony_ci * Write to clear them... 63262306a36Sopenharmony_ci * Note: This means that each bit we write back 63362306a36Sopenharmony_ci * to the registers will get cleared, leaving the 63462306a36Sopenharmony_ci * rest unaffected. So this won't affect new interrupts 63562306a36Sopenharmony_ci * we didn't catch while reading/processing, we 'll get 63662306a36Sopenharmony_ci * them next time get_isr gets called. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci ath5k_hw_reg_write(ah, sisr0, AR5K_SISR0); 63962306a36Sopenharmony_ci ath5k_hw_reg_write(ah, sisr1, AR5K_SISR1); 64062306a36Sopenharmony_ci ath5k_hw_reg_write(ah, sisr2, AR5K_SISR2); 64162306a36Sopenharmony_ci ath5k_hw_reg_write(ah, sisr3, AR5K_SISR3); 64262306a36Sopenharmony_ci ath5k_hw_reg_write(ah, sisr4, AR5K_SISR4); 64362306a36Sopenharmony_ci ath5k_hw_reg_write(ah, pisr_clear, AR5K_PISR); 64462306a36Sopenharmony_ci /* Flush previous write */ 64562306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_PISR); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* 64862306a36Sopenharmony_ci * Filter out the non-common bits from the interrupt 64962306a36Sopenharmony_ci * status. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci *interrupt_mask = (pisr & AR5K_INT_COMMON) & ah->ah_imr; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ah->ah_txq_isr_txok_all = 0; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* We treat TXOK,TXDESC, TXERR and TXEOL 65662306a36Sopenharmony_ci * the same way (schedule the tx tasklet) 65762306a36Sopenharmony_ci * so we track them all together per queue */ 65862306a36Sopenharmony_ci if (pisr & AR5K_ISR_TXOK) 65962306a36Sopenharmony_ci ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0, 66062306a36Sopenharmony_ci AR5K_SISR0_QCU_TXOK); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (pisr & AR5K_ISR_TXDESC) 66362306a36Sopenharmony_ci ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0, 66462306a36Sopenharmony_ci AR5K_SISR0_QCU_TXDESC); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (pisr & AR5K_ISR_TXERR) 66762306a36Sopenharmony_ci ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, 66862306a36Sopenharmony_ci AR5K_SISR1_QCU_TXERR); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (pisr & AR5K_ISR_TXEOL) 67162306a36Sopenharmony_ci ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, 67262306a36Sopenharmony_ci AR5K_SISR1_QCU_TXEOL); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* Misc Beacon related interrupts */ 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* For AR5211 */ 67762306a36Sopenharmony_ci if (pisr & AR5K_ISR_TIM) 67862306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_TIM; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* For AR5212+ */ 68162306a36Sopenharmony_ci if (pisr & AR5K_ISR_BCNMISC) { 68262306a36Sopenharmony_ci if (sisr2 & AR5K_SISR2_TIM) 68362306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_TIM; 68462306a36Sopenharmony_ci if (sisr2 & AR5K_SISR2_DTIM) 68562306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_DTIM; 68662306a36Sopenharmony_ci if (sisr2 & AR5K_SISR2_DTIM_SYNC) 68762306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_DTIM_SYNC; 68862306a36Sopenharmony_ci if (sisr2 & AR5K_SISR2_BCN_TIMEOUT) 68962306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_BCN_TIMEOUT; 69062306a36Sopenharmony_ci if (sisr2 & AR5K_SISR2_CAB_TIMEOUT) 69162306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_CAB_TIMEOUT; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* Below interrupts are unlikely to happen */ 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* HIU = Host Interface Unit (PCI etc) 69762306a36Sopenharmony_ci * Can be one of MCABT, SSERR, DPERR from SISR2 */ 69862306a36Sopenharmony_ci if (unlikely(pisr & (AR5K_ISR_HIUERR))) 69962306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_FATAL; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci /*Beacon Not Ready*/ 70262306a36Sopenharmony_ci if (unlikely(pisr & (AR5K_ISR_BNR))) 70362306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_BNR; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* A queue got CBR overrun */ 70662306a36Sopenharmony_ci if (unlikely(pisr & (AR5K_ISR_QCBRORN))) 70762306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_QCBRORN; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* A queue got CBR underrun */ 71062306a36Sopenharmony_ci if (unlikely(pisr & (AR5K_ISR_QCBRURN))) 71162306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_QCBRURN; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* A queue got triggered */ 71462306a36Sopenharmony_ci if (unlikely(pisr & (AR5K_ISR_QTRIG))) 71562306a36Sopenharmony_ci *interrupt_mask |= AR5K_INT_QTRIG; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci data = pisr; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* 72162306a36Sopenharmony_ci * In case we didn't handle anything, 72262306a36Sopenharmony_ci * print the register value. 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_ci if (unlikely(*interrupt_mask == 0 && net_ratelimit())) 72562306a36Sopenharmony_ci ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci/** 73162306a36Sopenharmony_ci * ath5k_hw_set_imr() - Set interrupt mask 73262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 73362306a36Sopenharmony_ci * @new_mask: The new interrupt mask to be set 73462306a36Sopenharmony_ci * 73562306a36Sopenharmony_ci * Set the interrupt mask in hw to save interrupts. We do that by mapping 73662306a36Sopenharmony_ci * ath5k_int bits to hw-specific bits to remove abstraction and writing 73762306a36Sopenharmony_ci * Interrupt Mask Register. 73862306a36Sopenharmony_ci */ 73962306a36Sopenharmony_cienum ath5k_int 74062306a36Sopenharmony_ciath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci enum ath5k_int old_mask, int_mask; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci old_mask = ah->ah_imr; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci /* 74762306a36Sopenharmony_ci * Disable card interrupts to prevent any race conditions 74862306a36Sopenharmony_ci * (they will be re-enabled afterwards if AR5K_INT GLOBAL 74962306a36Sopenharmony_ci * is set again on the new mask). 75062306a36Sopenharmony_ci */ 75162306a36Sopenharmony_ci if (old_mask & AR5K_INT_GLOBAL) { 75262306a36Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); 75362306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_IER); 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* 75762306a36Sopenharmony_ci * Add additional, chipset-dependent interrupt mask flags 75862306a36Sopenharmony_ci * and write them to the IMR (interrupt mask register). 75962306a36Sopenharmony_ci */ 76062306a36Sopenharmony_ci int_mask = new_mask & AR5K_INT_COMMON; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) { 76362306a36Sopenharmony_ci /* Preserve per queue TXURN interrupt mask */ 76462306a36Sopenharmony_ci u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2) 76562306a36Sopenharmony_ci & AR5K_SIMR2_QCU_TXURN; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci /* Fatal interrupt abstraction for 5211+ */ 76862306a36Sopenharmony_ci if (new_mask & AR5K_INT_FATAL) { 76962306a36Sopenharmony_ci int_mask |= AR5K_IMR_HIUERR; 77062306a36Sopenharmony_ci simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR 77162306a36Sopenharmony_ci | AR5K_SIMR2_DPERR); 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* Misc beacon related interrupts */ 77562306a36Sopenharmony_ci if (new_mask & AR5K_INT_TIM) 77662306a36Sopenharmony_ci int_mask |= AR5K_IMR_TIM; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (new_mask & AR5K_INT_TIM) 77962306a36Sopenharmony_ci simr2 |= AR5K_SISR2_TIM; 78062306a36Sopenharmony_ci if (new_mask & AR5K_INT_DTIM) 78162306a36Sopenharmony_ci simr2 |= AR5K_SISR2_DTIM; 78262306a36Sopenharmony_ci if (new_mask & AR5K_INT_DTIM_SYNC) 78362306a36Sopenharmony_ci simr2 |= AR5K_SISR2_DTIM_SYNC; 78462306a36Sopenharmony_ci if (new_mask & AR5K_INT_BCN_TIMEOUT) 78562306a36Sopenharmony_ci simr2 |= AR5K_SISR2_BCN_TIMEOUT; 78662306a36Sopenharmony_ci if (new_mask & AR5K_INT_CAB_TIMEOUT) 78762306a36Sopenharmony_ci simr2 |= AR5K_SISR2_CAB_TIMEOUT; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /*Beacon Not Ready*/ 79062306a36Sopenharmony_ci if (new_mask & AR5K_INT_BNR) 79162306a36Sopenharmony_ci int_mask |= AR5K_INT_BNR; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* Note: Per queue interrupt masks 79462306a36Sopenharmony_ci * are set via ath5k_hw_reset_tx_queue() (qcu.c) */ 79562306a36Sopenharmony_ci ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR); 79662306a36Sopenharmony_ci ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci } else { 79962306a36Sopenharmony_ci /* Fatal interrupt abstraction for 5210 */ 80062306a36Sopenharmony_ci if (new_mask & AR5K_INT_FATAL) 80162306a36Sopenharmony_ci int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT 80262306a36Sopenharmony_ci | AR5K_IMR_HIUERR | AR5K_IMR_DPERR); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* Only common interrupts left for 5210 (no SIMRs) */ 80562306a36Sopenharmony_ci ath5k_hw_reg_write(ah, int_mask, AR5K_IMR); 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* If RXNOFRM interrupt is masked disable it 80962306a36Sopenharmony_ci * by setting AR5K_RXNOFRM to zero */ 81062306a36Sopenharmony_ci if (!(new_mask & AR5K_INT_RXNOFRM)) 81162306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* Store new interrupt mask */ 81462306a36Sopenharmony_ci ah->ah_imr = new_mask; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* ..re-enable interrupts if AR5K_INT_GLOBAL is set */ 81762306a36Sopenharmony_ci if (new_mask & AR5K_INT_GLOBAL) { 81862306a36Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER); 81962306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_IER); 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return old_mask; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci/********************\ 82762306a36Sopenharmony_ci Init/Stop functions 82862306a36Sopenharmony_ci\********************/ 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci/** 83162306a36Sopenharmony_ci * ath5k_hw_dma_init() - Initialize DMA unit 83262306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 83362306a36Sopenharmony_ci * 83462306a36Sopenharmony_ci * Set DMA size and pre-enable interrupts 83562306a36Sopenharmony_ci * (driver handles tx/rx buffer setup and 83662306a36Sopenharmony_ci * dma start/stop) 83762306a36Sopenharmony_ci * 83862306a36Sopenharmony_ci * XXX: Save/restore RXDP/TXDP registers ? 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_civoid 84162306a36Sopenharmony_ciath5k_hw_dma_init(struct ath5k_hw *ah) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci /* 84462306a36Sopenharmony_ci * Set Rx/Tx DMA Configuration 84562306a36Sopenharmony_ci * 84662306a36Sopenharmony_ci * Set standard DMA size (128). Note that 84762306a36Sopenharmony_ci * a DMA size of 512 causes rx overruns and tx errors 84862306a36Sopenharmony_ci * on pci-e cards (tested on 5424 but since rx overruns 84962306a36Sopenharmony_ci * also occur on 5416/5418 with madwifi we set 128 85062306a36Sopenharmony_ci * for all PCI-E cards to be safe). 85162306a36Sopenharmony_ci * 85262306a36Sopenharmony_ci * XXX: need to check 5210 for this 85362306a36Sopenharmony_ci * TODO: Check out tx trigger level, it's always 64 on dumps but I 85462306a36Sopenharmony_ci * guess we can tweak it and see how it goes ;-) 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) { 85762306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, 85862306a36Sopenharmony_ci AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B); 85962306a36Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG, 86062306a36Sopenharmony_ci AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B); 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* Pre-enable interrupts on 5211/5212*/ 86462306a36Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) 86562306a36Sopenharmony_ci ath5k_hw_set_imr(ah, ah->ah_imr); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci/** 87062306a36Sopenharmony_ci * ath5k_hw_dma_stop() - stop DMA unit 87162306a36Sopenharmony_ci * @ah: The &struct ath5k_hw 87262306a36Sopenharmony_ci * 87362306a36Sopenharmony_ci * Stop tx/rx DMA and interrupts. Returns 87462306a36Sopenharmony_ci * -EBUSY if tx or rx dma failed to stop. 87562306a36Sopenharmony_ci * 87662306a36Sopenharmony_ci * XXX: Sometimes DMA unit hangs and we have 87762306a36Sopenharmony_ci * stuck frames on tx queues, only a reset 87862306a36Sopenharmony_ci * can fix that. 87962306a36Sopenharmony_ci */ 88062306a36Sopenharmony_ciint 88162306a36Sopenharmony_ciath5k_hw_dma_stop(struct ath5k_hw *ah) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci int i, qmax, err; 88462306a36Sopenharmony_ci err = 0; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* Disable interrupts */ 88762306a36Sopenharmony_ci ath5k_hw_set_imr(ah, 0); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* Stop rx dma */ 89062306a36Sopenharmony_ci err = ath5k_hw_stop_rx_dma(ah); 89162306a36Sopenharmony_ci if (err) 89262306a36Sopenharmony_ci return err; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* Clear any pending interrupts 89562306a36Sopenharmony_ci * and disable tx dma */ 89662306a36Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) { 89762306a36Sopenharmony_ci ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR); 89862306a36Sopenharmony_ci qmax = AR5K_NUM_TX_QUEUES; 89962306a36Sopenharmony_ci } else { 90062306a36Sopenharmony_ci /* PISR/SISR Not available on 5210 */ 90162306a36Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_ISR); 90262306a36Sopenharmony_ci qmax = AR5K_NUM_TX_QUEUES_NOQCU; 90362306a36Sopenharmony_ci } 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci for (i = 0; i < qmax; i++) { 90662306a36Sopenharmony_ci err = ath5k_hw_stop_tx_dma(ah, i); 90762306a36Sopenharmony_ci /* -EINVAL -> queue inactive */ 90862306a36Sopenharmony_ci if (err && err != -EINVAL) 90962306a36Sopenharmony_ci return err; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return 0; 91362306a36Sopenharmony_ci} 914