18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> 38c2ecf20Sopenharmony_ci * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and distribute this software for any 68c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 78c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 108c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 118c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 128c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 138c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 148c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 158c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/*************************************\ 208c2ecf20Sopenharmony_ci* DMA and interrupt masking functions * 218c2ecf20Sopenharmony_ci\*************************************/ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/** 248c2ecf20Sopenharmony_ci * DOC: DMA and interrupt masking functions 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Here we setup descriptor pointers (rxdp/txdp) start/stop dma engine and 278c2ecf20Sopenharmony_ci * handle queue setup for 5210 chipset (rest are handled on qcu.c). 288c2ecf20Sopenharmony_ci * Also we setup interrupt mask register (IMR) and read the various interrupt 298c2ecf20Sopenharmony_ci * status registers (ISR). 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include "ath5k.h" 358c2ecf20Sopenharmony_ci#include "reg.h" 368c2ecf20Sopenharmony_ci#include "debug.h" 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/*********\ 408c2ecf20Sopenharmony_ci* Receive * 418c2ecf20Sopenharmony_ci\*********/ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/** 448c2ecf20Sopenharmony_ci * ath5k_hw_start_rx_dma() - Start DMA receive 458c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_civoid 488c2ecf20Sopenharmony_ciath5k_hw_start_rx_dma(struct ath5k_hw *ah) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR); 518c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_CR); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/** 558c2ecf20Sopenharmony_ci * ath5k_hw_stop_rx_dma() - Stop DMA receive 568c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_cistatic int 598c2ecf20Sopenharmony_ciath5k_hw_stop_rx_dma(struct ath5k_hw *ah) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci unsigned int i; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_CR_RXD, AR5K_CR); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* 668c2ecf20Sopenharmony_ci * It may take some time to disable the DMA receive unit 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci for (i = 1000; i > 0 && 698c2ecf20Sopenharmony_ci (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) != 0; 708c2ecf20Sopenharmony_ci i--) 718c2ecf20Sopenharmony_ci udelay(100); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!i) 748c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 758c2ecf20Sopenharmony_ci "failed to stop RX DMA !\n"); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return i ? 0 : -EBUSY; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/** 818c2ecf20Sopenharmony_ci * ath5k_hw_get_rxdp() - Get RX Descriptor's address 828c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ciu32 858c2ecf20Sopenharmony_ciath5k_hw_get_rxdp(struct ath5k_hw *ah) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci return ath5k_hw_reg_read(ah, AR5K_RXDP); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/** 918c2ecf20Sopenharmony_ci * ath5k_hw_set_rxdp() - Set RX Descriptor's address 928c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 938c2ecf20Sopenharmony_ci * @phys_addr: RX descriptor address 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * Returns -EIO if rx is active 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ciint 988c2ecf20Sopenharmony_ciath5k_hw_set_rxdp(struct ath5k_hw *ah, u32 phys_addr) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci if (ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_CR_RXE) { 1018c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 1028c2ecf20Sopenharmony_ci "tried to set RXDP while rx was active !\n"); 1038c2ecf20Sopenharmony_ci return -EIO; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, phys_addr, AR5K_RXDP); 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/**********\ 1128c2ecf20Sopenharmony_ci* Transmit * 1138c2ecf20Sopenharmony_ci\**********/ 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/** 1168c2ecf20Sopenharmony_ci * ath5k_hw_start_tx_dma() - Start DMA transmit for a specific queue 1178c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 1188c2ecf20Sopenharmony_ci * @queue: The hw queue number 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * Start DMA transmit for a specific queue and since 5210 doesn't have 1218c2ecf20Sopenharmony_ci * QCU/DCU, set up queue parameters for 5210 here based on queue type (one 1228c2ecf20Sopenharmony_ci * queue for normal data and one queue for beacons). For queue setup 1238c2ecf20Sopenharmony_ci * on newer chips check out qcu.c. Returns -EINVAL if queue number is out 1248c2ecf20Sopenharmony_ci * of range or if queue is already disabled. 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * NOTE: Must be called after setting up tx control descriptor for that 1278c2ecf20Sopenharmony_ci * queue (see below). 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ciint 1308c2ecf20Sopenharmony_ciath5k_hw_start_tx_dma(struct ath5k_hw *ah, unsigned int queue) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci u32 tx_queue; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Return if queue is declared inactive */ 1378c2ecf20Sopenharmony_ci if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) 1388c2ecf20Sopenharmony_ci return -EINVAL; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 1418c2ecf20Sopenharmony_ci tx_queue = ath5k_hw_reg_read(ah, AR5K_CR); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* 1448c2ecf20Sopenharmony_ci * Set the queue by type on 5210 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_ci switch (ah->ah_txq[queue].tqi_type) { 1478c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_DATA: 1488c2ecf20Sopenharmony_ci tx_queue |= AR5K_CR_TXE0 & ~AR5K_CR_TXD0; 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_BEACON: 1518c2ecf20Sopenharmony_ci tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; 1528c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, 1538c2ecf20Sopenharmony_ci AR5K_BSR); 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_CAB: 1568c2ecf20Sopenharmony_ci tx_queue |= AR5K_CR_TXE1 & ~AR5K_CR_TXD1; 1578c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_BCR_TQ1FV | AR5K_BCR_TQ1V | 1588c2ecf20Sopenharmony_ci AR5K_BCR_BDMAE, AR5K_BSR); 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci default: 1618c2ecf20Sopenharmony_ci return -EINVAL; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci /* Start queue */ 1648c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); 1658c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_CR); 1668c2ecf20Sopenharmony_ci } else { 1678c2ecf20Sopenharmony_ci /* Return if queue is disabled */ 1688c2ecf20Sopenharmony_ci if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue)) 1698c2ecf20Sopenharmony_ci return -EIO; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Start queue */ 1728c2ecf20Sopenharmony_ci AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXE, queue); 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/** 1798c2ecf20Sopenharmony_ci * ath5k_hw_stop_tx_dma() - Stop DMA transmit on a specific queue 1808c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 1818c2ecf20Sopenharmony_ci * @queue: The hw queue number 1828c2ecf20Sopenharmony_ci * 1838c2ecf20Sopenharmony_ci * Stop DMA transmit on a specific hw queue and drain queue so we don't 1848c2ecf20Sopenharmony_ci * have any pending frames. Returns -EBUSY if we still have pending frames, 1858c2ecf20Sopenharmony_ci * -EINVAL if queue number is out of range or inactive. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_cistatic int 1888c2ecf20Sopenharmony_ciath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci unsigned int i = 40; 1918c2ecf20Sopenharmony_ci u32 tx_queue, pending; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* Return if queue is declared inactive */ 1968c2ecf20Sopenharmony_ci if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) 1978c2ecf20Sopenharmony_ci return -EINVAL; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 2008c2ecf20Sopenharmony_ci tx_queue = ath5k_hw_reg_read(ah, AR5K_CR); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * Set by queue type 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci switch (ah->ah_txq[queue].tqi_type) { 2068c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_DATA: 2078c2ecf20Sopenharmony_ci tx_queue |= AR5K_CR_TXD0 & ~AR5K_CR_TXE0; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_BEACON: 2108c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_CAB: 2118c2ecf20Sopenharmony_ci /* XXX Fix me... */ 2128c2ecf20Sopenharmony_ci tx_queue |= AR5K_CR_TXD1 & ~AR5K_CR_TXD1; 2138c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_BSR); 2148c2ecf20Sopenharmony_ci break; 2158c2ecf20Sopenharmony_ci default: 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* Stop queue */ 2208c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); 2218c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_CR); 2228c2ecf20Sopenharmony_ci } else { 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * Enable DCU early termination to quickly 2268c2ecf20Sopenharmony_ci * flush any pending frames from QCU 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), 2298c2ecf20Sopenharmony_ci AR5K_QCU_MISC_DCU_EARLY); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* 2328c2ecf20Sopenharmony_ci * Schedule TX disable and wait until queue is empty 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci AR5K_REG_WRITE_Q(ah, AR5K_QCU_TXD, queue); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* Wait for queue to stop */ 2378c2ecf20Sopenharmony_ci for (i = 1000; i > 0 && 2388c2ecf20Sopenharmony_ci (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue) != 0); 2398c2ecf20Sopenharmony_ci i--) 2408c2ecf20Sopenharmony_ci udelay(100); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) 2438c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 2448c2ecf20Sopenharmony_ci "queue %i didn't stop !\n", queue); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* Check for pending frames */ 2478c2ecf20Sopenharmony_ci i = 1000; 2488c2ecf20Sopenharmony_ci do { 2498c2ecf20Sopenharmony_ci pending = ath5k_hw_reg_read(ah, 2508c2ecf20Sopenharmony_ci AR5K_QUEUE_STATUS(queue)) & 2518c2ecf20Sopenharmony_ci AR5K_QCU_STS_FRMPENDCNT; 2528c2ecf20Sopenharmony_ci udelay(100); 2538c2ecf20Sopenharmony_ci } while (--i && pending); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* For 2413+ order PCU to drop packets using 2568c2ecf20Sopenharmony_ci * QUIET mechanism */ 2578c2ecf20Sopenharmony_ci if (ah->ah_mac_version >= (AR5K_SREV_AR2414 >> 4) && 2588c2ecf20Sopenharmony_ci pending) { 2598c2ecf20Sopenharmony_ci /* Set periodicity and duration */ 2608c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 2618c2ecf20Sopenharmony_ci AR5K_REG_SM(100, AR5K_QUIET_CTL2_QT_PER)| 2628c2ecf20Sopenharmony_ci AR5K_REG_SM(10, AR5K_QUIET_CTL2_QT_DUR), 2638c2ecf20Sopenharmony_ci AR5K_QUIET_CTL2); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* Enable quiet period for current TSF */ 2668c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 2678c2ecf20Sopenharmony_ci AR5K_QUIET_CTL1_QT_EN | 2688c2ecf20Sopenharmony_ci AR5K_REG_SM(ath5k_hw_reg_read(ah, 2698c2ecf20Sopenharmony_ci AR5K_TSF_L32_5211) >> 10, 2708c2ecf20Sopenharmony_ci AR5K_QUIET_CTL1_NEXT_QT_TSF), 2718c2ecf20Sopenharmony_ci AR5K_QUIET_CTL1); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Force channel idle high */ 2748c2ecf20Sopenharmony_ci AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW_5211, 2758c2ecf20Sopenharmony_ci AR5K_DIAG_SW_CHANNEL_IDLE_HIGH); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* Wait a while and disable mechanism */ 2788c2ecf20Sopenharmony_ci udelay(400); 2798c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_QUIET_CTL1, 2808c2ecf20Sopenharmony_ci AR5K_QUIET_CTL1_QT_EN); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* Re-check for pending frames */ 2838c2ecf20Sopenharmony_ci i = 100; 2848c2ecf20Sopenharmony_ci do { 2858c2ecf20Sopenharmony_ci pending = ath5k_hw_reg_read(ah, 2868c2ecf20Sopenharmony_ci AR5K_QUEUE_STATUS(queue)) & 2878c2ecf20Sopenharmony_ci AR5K_QCU_STS_FRMPENDCNT; 2888c2ecf20Sopenharmony_ci udelay(100); 2898c2ecf20Sopenharmony_ci } while (--i && pending); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW_5211, 2928c2ecf20Sopenharmony_ci AR5K_DIAG_SW_CHANNEL_IDLE_HIGH); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (pending) 2958c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 2968c2ecf20Sopenharmony_ci "quiet mechanism didn't work q:%i !\n", 2978c2ecf20Sopenharmony_ci queue); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * Disable DCU early termination 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci AR5K_REG_DISABLE_BITS(ah, AR5K_QUEUE_MISC(queue), 3048c2ecf20Sopenharmony_ci AR5K_QCU_MISC_DCU_EARLY); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Clear register */ 3078c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD); 3088c2ecf20Sopenharmony_ci if (pending) { 3098c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 3108c2ecf20Sopenharmony_ci "tx dma didn't stop (q:%i, frm:%i) !\n", 3118c2ecf20Sopenharmony_ci queue, pending); 3128c2ecf20Sopenharmony_ci return -EBUSY; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* TODO: Check for success on 5210 else return error */ 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/** 3218c2ecf20Sopenharmony_ci * ath5k_hw_stop_beacon_queue() - Stop beacon queue 3228c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 3238c2ecf20Sopenharmony_ci * @queue: The queue number 3248c2ecf20Sopenharmony_ci * 3258c2ecf20Sopenharmony_ci * Returns -EIO if queue didn't stop 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ciint 3288c2ecf20Sopenharmony_ciath5k_hw_stop_beacon_queue(struct ath5k_hw *ah, unsigned int queue) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci int ret; 3318c2ecf20Sopenharmony_ci ret = ath5k_hw_stop_tx_dma(ah, queue); 3328c2ecf20Sopenharmony_ci if (ret) { 3338c2ecf20Sopenharmony_ci ATH5K_DBG(ah, ATH5K_DEBUG_DMA, 3348c2ecf20Sopenharmony_ci "beacon queue didn't stop !\n"); 3358c2ecf20Sopenharmony_ci return -EIO; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/** 3418c2ecf20Sopenharmony_ci * ath5k_hw_get_txdp() - Get TX Descriptor's address for a specific queue 3428c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 3438c2ecf20Sopenharmony_ci * @queue: The hw queue number 3448c2ecf20Sopenharmony_ci * 3458c2ecf20Sopenharmony_ci * Get TX descriptor's address for a specific queue. For 5210 we ignore 3468c2ecf20Sopenharmony_ci * the queue number and use tx queue type since we only have 2 queues. 3478c2ecf20Sopenharmony_ci * We use TXDP0 for normal data queue and TXDP1 for beacon queue. 3488c2ecf20Sopenharmony_ci * For newer chips with QCU/DCU we just read the corresponding TXDP register. 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci * XXX: Is TXDP read and clear ? 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_ciu32 3538c2ecf20Sopenharmony_ciath5k_hw_get_txdp(struct ath5k_hw *ah, unsigned int queue) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci u16 tx_reg; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* 3608c2ecf20Sopenharmony_ci * Get the transmit queue descriptor pointer from the selected queue 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_ci /*5210 doesn't have QCU*/ 3638c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 3648c2ecf20Sopenharmony_ci switch (ah->ah_txq[queue].tqi_type) { 3658c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_DATA: 3668c2ecf20Sopenharmony_ci tx_reg = AR5K_NOQCU_TXDP0; 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_BEACON: 3698c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_CAB: 3708c2ecf20Sopenharmony_ci tx_reg = AR5K_NOQCU_TXDP1; 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci default: 3738c2ecf20Sopenharmony_ci return 0xffffffff; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci } else { 3768c2ecf20Sopenharmony_ci tx_reg = AR5K_QUEUE_TXDP(queue); 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci return ath5k_hw_reg_read(ah, tx_reg); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci/** 3838c2ecf20Sopenharmony_ci * ath5k_hw_set_txdp() - Set TX Descriptor's address for a specific queue 3848c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 3858c2ecf20Sopenharmony_ci * @queue: The hw queue number 3868c2ecf20Sopenharmony_ci * @phys_addr: The physical address 3878c2ecf20Sopenharmony_ci * 3888c2ecf20Sopenharmony_ci * Set TX descriptor's address for a specific queue. For 5210 we ignore 3898c2ecf20Sopenharmony_ci * the queue number and we use tx queue type since we only have 2 queues 3908c2ecf20Sopenharmony_ci * so as above we use TXDP0 for normal data queue and TXDP1 for beacon queue. 3918c2ecf20Sopenharmony_ci * For newer chips with QCU/DCU we just set the corresponding TXDP register. 3928c2ecf20Sopenharmony_ci * Returns -EINVAL if queue type is invalid for 5210 and -EIO if queue is still 3938c2ecf20Sopenharmony_ci * active. 3948c2ecf20Sopenharmony_ci */ 3958c2ecf20Sopenharmony_ciint 3968c2ecf20Sopenharmony_ciath5k_hw_set_txdp(struct ath5k_hw *ah, unsigned int queue, u32 phys_addr) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci u16 tx_reg; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* 4038c2ecf20Sopenharmony_ci * Set the transmit queue descriptor pointer register by type 4048c2ecf20Sopenharmony_ci * on 5210 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 4078c2ecf20Sopenharmony_ci switch (ah->ah_txq[queue].tqi_type) { 4088c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_DATA: 4098c2ecf20Sopenharmony_ci tx_reg = AR5K_NOQCU_TXDP0; 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_BEACON: 4128c2ecf20Sopenharmony_ci case AR5K_TX_QUEUE_CAB: 4138c2ecf20Sopenharmony_ci tx_reg = AR5K_NOQCU_TXDP1; 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci default: 4168c2ecf20Sopenharmony_ci return -EINVAL; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci /* 4208c2ecf20Sopenharmony_ci * Set the transmit queue descriptor pointer for 4218c2ecf20Sopenharmony_ci * the selected queue on QCU for 5211+ 4228c2ecf20Sopenharmony_ci * (this won't work if the queue is still active) 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) 4258c2ecf20Sopenharmony_ci return -EIO; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci tx_reg = AR5K_QUEUE_TXDP(queue); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* Set descriptor pointer */ 4318c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, phys_addr, tx_reg); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/** 4378c2ecf20Sopenharmony_ci * ath5k_hw_update_tx_triglevel() - Update tx trigger level 4388c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 4398c2ecf20Sopenharmony_ci * @increase: Flag to force increase of trigger level 4408c2ecf20Sopenharmony_ci * 4418c2ecf20Sopenharmony_ci * This function increases/decreases the tx trigger level for the tx fifo 4428c2ecf20Sopenharmony_ci * buffer (aka FIFO threshold) that is used to indicate when PCU flushes 4438c2ecf20Sopenharmony_ci * the buffer and transmits its data. Lowering this results sending small 4448c2ecf20Sopenharmony_ci * frames more quickly but can lead to tx underruns, raising it a lot can 4458c2ecf20Sopenharmony_ci * result other problems. Right now we start with the lowest possible 4468c2ecf20Sopenharmony_ci * (64Bytes) and if we get tx underrun we increase it using the increase 4478c2ecf20Sopenharmony_ci * flag. Returns -EIO if we have reached maximum/minimum. 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * XXX: Link this with tx DMA size ? 4508c2ecf20Sopenharmony_ci * XXX2: Use it to save interrupts ? 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ciint 4538c2ecf20Sopenharmony_ciath5k_hw_update_tx_triglevel(struct ath5k_hw *ah, bool increase) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci u32 trigger_level, imr; 4568c2ecf20Sopenharmony_ci int ret = -EIO; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /* 4598c2ecf20Sopenharmony_ci * Disable interrupts by setting the mask 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci imr = ath5k_hw_set_imr(ah, ah->ah_imr & ~AR5K_INT_GLOBAL); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci trigger_level = AR5K_REG_MS(ath5k_hw_reg_read(ah, AR5K_TXCFG), 4648c2ecf20Sopenharmony_ci AR5K_TXCFG_TXFULL); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci if (!increase) { 4678c2ecf20Sopenharmony_ci if (--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES) 4688c2ecf20Sopenharmony_ci goto done; 4698c2ecf20Sopenharmony_ci } else 4708c2ecf20Sopenharmony_ci trigger_level += 4718c2ecf20Sopenharmony_ci ((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* 4748c2ecf20Sopenharmony_ci * Update trigger level on success 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) 4778c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, trigger_level, AR5K_TRIG_LVL); 4788c2ecf20Sopenharmony_ci else 4798c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, 4808c2ecf20Sopenharmony_ci AR5K_TXCFG_TXFULL, trigger_level); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci ret = 0; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cidone: 4858c2ecf20Sopenharmony_ci /* 4868c2ecf20Sopenharmony_ci * Restore interrupt mask 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_ci ath5k_hw_set_imr(ah, imr); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci return ret; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci/*******************\ 4958c2ecf20Sopenharmony_ci* Interrupt masking * 4968c2ecf20Sopenharmony_ci\*******************/ 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/** 4998c2ecf20Sopenharmony_ci * ath5k_hw_is_intr_pending() - Check if we have pending interrupts 5008c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 5018c2ecf20Sopenharmony_ci * 5028c2ecf20Sopenharmony_ci * Check if we have pending interrupts to process. Returns 1 if we 5038c2ecf20Sopenharmony_ci * have pending interrupts and 0 if we haven't. 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_cibool 5068c2ecf20Sopenharmony_ciath5k_hw_is_intr_pending(struct ath5k_hw *ah) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci return ath5k_hw_reg_read(ah, AR5K_INTPEND) == 1 ? 1 : 0; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci/** 5128c2ecf20Sopenharmony_ci * ath5k_hw_get_isr() - Get interrupt status 5138c2ecf20Sopenharmony_ci * @ah: The @struct ath5k_hw 5148c2ecf20Sopenharmony_ci * @interrupt_mask: Driver's interrupt mask used to filter out 5158c2ecf20Sopenharmony_ci * interrupts in sw. 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * This function is used inside our interrupt handler to determine the reason 5188c2ecf20Sopenharmony_ci * for the interrupt by reading Primary Interrupt Status Register. Returns an 5198c2ecf20Sopenharmony_ci * abstract interrupt status mask which is mostly ISR with some uncommon bits 5208c2ecf20Sopenharmony_ci * being mapped on some standard non hw-specific positions 5218c2ecf20Sopenharmony_ci * (check out &ath5k_int). 5228c2ecf20Sopenharmony_ci * 5238c2ecf20Sopenharmony_ci * NOTE: We do write-to-clear, so the active PISR/SISR bits at the time this 5248c2ecf20Sopenharmony_ci * function gets called are cleared on return. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ciint 5278c2ecf20Sopenharmony_ciath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci u32 data = 0; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* 5328c2ecf20Sopenharmony_ci * Read interrupt status from Primary Interrupt 5338c2ecf20Sopenharmony_ci * Register. 5348c2ecf20Sopenharmony_ci * 5358c2ecf20Sopenharmony_ci * Note: PISR/SISR Not available on 5210 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ci if (ah->ah_version == AR5K_AR5210) { 5388c2ecf20Sopenharmony_ci u32 isr = 0; 5398c2ecf20Sopenharmony_ci isr = ath5k_hw_reg_read(ah, AR5K_ISR); 5408c2ecf20Sopenharmony_ci if (unlikely(isr == AR5K_INT_NOCARD)) { 5418c2ecf20Sopenharmony_ci *interrupt_mask = isr; 5428c2ecf20Sopenharmony_ci return -ENODEV; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* 5468c2ecf20Sopenharmony_ci * Filter out the non-common bits from the interrupt 5478c2ecf20Sopenharmony_ci * status. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ci *interrupt_mask = (isr & AR5K_INT_COMMON) & ah->ah_imr; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Hanlde INT_FATAL */ 5528c2ecf20Sopenharmony_ci if (unlikely(isr & (AR5K_ISR_SSERR | AR5K_ISR_MCABT 5538c2ecf20Sopenharmony_ci | AR5K_ISR_DPERR))) 5548c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_FATAL; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* 5578c2ecf20Sopenharmony_ci * XXX: BMISS interrupts may occur after association. 5588c2ecf20Sopenharmony_ci * I found this on 5210 code but it needs testing. If this is 5598c2ecf20Sopenharmony_ci * true we should disable them before assoc and re-enable them 5608c2ecf20Sopenharmony_ci * after a successful assoc + some jiffies. 5618c2ecf20Sopenharmony_ci interrupt_mask &= ~AR5K_INT_BMISS; 5628c2ecf20Sopenharmony_ci */ 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci data = isr; 5658c2ecf20Sopenharmony_ci } else { 5668c2ecf20Sopenharmony_ci u32 pisr = 0; 5678c2ecf20Sopenharmony_ci u32 pisr_clear = 0; 5688c2ecf20Sopenharmony_ci u32 sisr0 = 0; 5698c2ecf20Sopenharmony_ci u32 sisr1 = 0; 5708c2ecf20Sopenharmony_ci u32 sisr2 = 0; 5718c2ecf20Sopenharmony_ci u32 sisr3 = 0; 5728c2ecf20Sopenharmony_ci u32 sisr4 = 0; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* Read PISR and SISRs... */ 5758c2ecf20Sopenharmony_ci pisr = ath5k_hw_reg_read(ah, AR5K_PISR); 5768c2ecf20Sopenharmony_ci if (unlikely(pisr == AR5K_INT_NOCARD)) { 5778c2ecf20Sopenharmony_ci *interrupt_mask = pisr; 5788c2ecf20Sopenharmony_ci return -ENODEV; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci sisr0 = ath5k_hw_reg_read(ah, AR5K_SISR0); 5828c2ecf20Sopenharmony_ci sisr1 = ath5k_hw_reg_read(ah, AR5K_SISR1); 5838c2ecf20Sopenharmony_ci sisr2 = ath5k_hw_reg_read(ah, AR5K_SISR2); 5848c2ecf20Sopenharmony_ci sisr3 = ath5k_hw_reg_read(ah, AR5K_SISR3); 5858c2ecf20Sopenharmony_ci sisr4 = ath5k_hw_reg_read(ah, AR5K_SISR4); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* 5888c2ecf20Sopenharmony_ci * PISR holds the logical OR of interrupt bits 5898c2ecf20Sopenharmony_ci * from SISR registers: 5908c2ecf20Sopenharmony_ci * 5918c2ecf20Sopenharmony_ci * TXOK and TXDESC -> Logical OR of TXOK and TXDESC 5928c2ecf20Sopenharmony_ci * per-queue bits on SISR0 5938c2ecf20Sopenharmony_ci * 5948c2ecf20Sopenharmony_ci * TXERR and TXEOL -> Logical OR of TXERR and TXEOL 5958c2ecf20Sopenharmony_ci * per-queue bits on SISR1 5968c2ecf20Sopenharmony_ci * 5978c2ecf20Sopenharmony_ci * TXURN -> Logical OR of TXURN per-queue bits on SISR2 5988c2ecf20Sopenharmony_ci * 5998c2ecf20Sopenharmony_ci * HIUERR -> Logical OR of MCABT, SSERR and DPER bits on SISR2 6008c2ecf20Sopenharmony_ci * 6018c2ecf20Sopenharmony_ci * BCNMISC -> Logical OR of TIM, CAB_END, DTIM_SYNC 6028c2ecf20Sopenharmony_ci * BCN_TIMEOUT, CAB_TIMEOUT and DTIM 6038c2ecf20Sopenharmony_ci * (and TSFOOR ?) bits on SISR2 6048c2ecf20Sopenharmony_ci * 6058c2ecf20Sopenharmony_ci * QCBRORN and QCBRURN -> Logical OR of QCBRORN and 6068c2ecf20Sopenharmony_ci * QCBRURN per-queue bits on SISR3 6078c2ecf20Sopenharmony_ci * QTRIG -> Logical OR of QTRIG per-queue bits on SISR4 6088c2ecf20Sopenharmony_ci * 6098c2ecf20Sopenharmony_ci * If we clean these bits on PISR we 'll also clear all 6108c2ecf20Sopenharmony_ci * related bits from SISRs, e.g. if we write the TXOK bit on 6118c2ecf20Sopenharmony_ci * PISR we 'll clean all TXOK bits from SISR0 so if a new TXOK 6128c2ecf20Sopenharmony_ci * interrupt got fired for another queue while we were reading 6138c2ecf20Sopenharmony_ci * the interrupt registers and we write back the TXOK bit on 6148c2ecf20Sopenharmony_ci * PISR we 'll lose it. So make sure that we don't write back 6158c2ecf20Sopenharmony_ci * on PISR any bits that come from SISRs. Clearing them from 6168c2ecf20Sopenharmony_ci * SISRs will also clear PISR so no need to worry here. 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* XXX: There seems to be an issue on some cards 6208c2ecf20Sopenharmony_ci * with tx interrupt flags not being updated 6218c2ecf20Sopenharmony_ci * on PISR despite that all Tx interrupt bits 6228c2ecf20Sopenharmony_ci * are cleared on SISRs. Since we handle all 6238c2ecf20Sopenharmony_ci * Tx queues all together it shouldn't be an 6248c2ecf20Sopenharmony_ci * issue if we clear Tx interrupt flags also 6258c2ecf20Sopenharmony_ci * on PISR to avoid that. 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_ci pisr_clear = (pisr & ~AR5K_ISR_BITS_FROM_SISRS) | 6288c2ecf20Sopenharmony_ci (pisr & AR5K_INT_TX_ALL); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci /* 6318c2ecf20Sopenharmony_ci * Write to clear them... 6328c2ecf20Sopenharmony_ci * Note: This means that each bit we write back 6338c2ecf20Sopenharmony_ci * to the registers will get cleared, leaving the 6348c2ecf20Sopenharmony_ci * rest unaffected. So this won't affect new interrupts 6358c2ecf20Sopenharmony_ci * we didn't catch while reading/processing, we 'll get 6368c2ecf20Sopenharmony_ci * them next time get_isr gets called. 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, sisr0, AR5K_SISR0); 6398c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, sisr1, AR5K_SISR1); 6408c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, sisr2, AR5K_SISR2); 6418c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, sisr3, AR5K_SISR3); 6428c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, sisr4, AR5K_SISR4); 6438c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, pisr_clear, AR5K_PISR); 6448c2ecf20Sopenharmony_ci /* Flush previous write */ 6458c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_PISR); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* 6488c2ecf20Sopenharmony_ci * Filter out the non-common bits from the interrupt 6498c2ecf20Sopenharmony_ci * status. 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci *interrupt_mask = (pisr & AR5K_INT_COMMON) & ah->ah_imr; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* We treat TXOK,TXDESC, TXERR and TXEOL 6558c2ecf20Sopenharmony_ci * the same way (schedule the tx tasklet) 6568c2ecf20Sopenharmony_ci * so we track them all together per queue */ 6578c2ecf20Sopenharmony_ci if (pisr & AR5K_ISR_TXOK) 6588c2ecf20Sopenharmony_ci ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0, 6598c2ecf20Sopenharmony_ci AR5K_SISR0_QCU_TXOK); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (pisr & AR5K_ISR_TXDESC) 6628c2ecf20Sopenharmony_ci ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr0, 6638c2ecf20Sopenharmony_ci AR5K_SISR0_QCU_TXDESC); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (pisr & AR5K_ISR_TXERR) 6668c2ecf20Sopenharmony_ci ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, 6678c2ecf20Sopenharmony_ci AR5K_SISR1_QCU_TXERR); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (pisr & AR5K_ISR_TXEOL) 6708c2ecf20Sopenharmony_ci ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, 6718c2ecf20Sopenharmony_ci AR5K_SISR1_QCU_TXEOL); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci /* Currently this is not much useful since we treat 6748c2ecf20Sopenharmony_ci * all queues the same way if we get a TXURN (update 6758c2ecf20Sopenharmony_ci * tx trigger level) but we might need it later on*/ 6768c2ecf20Sopenharmony_ci if (pisr & AR5K_ISR_TXURN) 6778c2ecf20Sopenharmony_ci ah->ah_txq_isr_txurn |= AR5K_REG_MS(sisr2, 6788c2ecf20Sopenharmony_ci AR5K_SISR2_QCU_TXURN); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* Misc Beacon related interrupts */ 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* For AR5211 */ 6838c2ecf20Sopenharmony_ci if (pisr & AR5K_ISR_TIM) 6848c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_TIM; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci /* For AR5212+ */ 6878c2ecf20Sopenharmony_ci if (pisr & AR5K_ISR_BCNMISC) { 6888c2ecf20Sopenharmony_ci if (sisr2 & AR5K_SISR2_TIM) 6898c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_TIM; 6908c2ecf20Sopenharmony_ci if (sisr2 & AR5K_SISR2_DTIM) 6918c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_DTIM; 6928c2ecf20Sopenharmony_ci if (sisr2 & AR5K_SISR2_DTIM_SYNC) 6938c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_DTIM_SYNC; 6948c2ecf20Sopenharmony_ci if (sisr2 & AR5K_SISR2_BCN_TIMEOUT) 6958c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_BCN_TIMEOUT; 6968c2ecf20Sopenharmony_ci if (sisr2 & AR5K_SISR2_CAB_TIMEOUT) 6978c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_CAB_TIMEOUT; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* Below interrupts are unlikely to happen */ 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* HIU = Host Interface Unit (PCI etc) 7038c2ecf20Sopenharmony_ci * Can be one of MCABT, SSERR, DPERR from SISR2 */ 7048c2ecf20Sopenharmony_ci if (unlikely(pisr & (AR5K_ISR_HIUERR))) 7058c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_FATAL; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci /*Beacon Not Ready*/ 7088c2ecf20Sopenharmony_ci if (unlikely(pisr & (AR5K_ISR_BNR))) 7098c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_BNR; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* A queue got CBR overrun */ 7128c2ecf20Sopenharmony_ci if (unlikely(pisr & (AR5K_ISR_QCBRORN))) { 7138c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_QCBRORN; 7148c2ecf20Sopenharmony_ci ah->ah_txq_isr_qcborn |= AR5K_REG_MS(sisr3, 7158c2ecf20Sopenharmony_ci AR5K_SISR3_QCBRORN); 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* A queue got CBR underrun */ 7198c2ecf20Sopenharmony_ci if (unlikely(pisr & (AR5K_ISR_QCBRURN))) { 7208c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_QCBRURN; 7218c2ecf20Sopenharmony_ci ah->ah_txq_isr_qcburn |= AR5K_REG_MS(sisr3, 7228c2ecf20Sopenharmony_ci AR5K_SISR3_QCBRURN); 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* A queue got triggered */ 7268c2ecf20Sopenharmony_ci if (unlikely(pisr & (AR5K_ISR_QTRIG))) { 7278c2ecf20Sopenharmony_ci *interrupt_mask |= AR5K_INT_QTRIG; 7288c2ecf20Sopenharmony_ci ah->ah_txq_isr_qtrig |= AR5K_REG_MS(sisr4, 7298c2ecf20Sopenharmony_ci AR5K_SISR4_QTRIG); 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci data = pisr; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* 7368c2ecf20Sopenharmony_ci * In case we didn't handle anything, 7378c2ecf20Sopenharmony_ci * print the register value. 7388c2ecf20Sopenharmony_ci */ 7398c2ecf20Sopenharmony_ci if (unlikely(*interrupt_mask == 0 && net_ratelimit())) 7408c2ecf20Sopenharmony_ci ATH5K_PRINTF("ISR: 0x%08x IMR: 0x%08x\n", data, ah->ah_imr); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci return 0; 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci/** 7468c2ecf20Sopenharmony_ci * ath5k_hw_set_imr() - Set interrupt mask 7478c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 7488c2ecf20Sopenharmony_ci * @new_mask: The new interrupt mask to be set 7498c2ecf20Sopenharmony_ci * 7508c2ecf20Sopenharmony_ci * Set the interrupt mask in hw to save interrupts. We do that by mapping 7518c2ecf20Sopenharmony_ci * ath5k_int bits to hw-specific bits to remove abstraction and writing 7528c2ecf20Sopenharmony_ci * Interrupt Mask Register. 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_cienum ath5k_int 7558c2ecf20Sopenharmony_ciath5k_hw_set_imr(struct ath5k_hw *ah, enum ath5k_int new_mask) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci enum ath5k_int old_mask, int_mask; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci old_mask = ah->ah_imr; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* 7628c2ecf20Sopenharmony_ci * Disable card interrupts to prevent any race conditions 7638c2ecf20Sopenharmony_ci * (they will be re-enabled afterwards if AR5K_INT GLOBAL 7648c2ecf20Sopenharmony_ci * is set again on the new mask). 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_ci if (old_mask & AR5K_INT_GLOBAL) { 7678c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); 7688c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_IER); 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* 7728c2ecf20Sopenharmony_ci * Add additional, chipset-dependent interrupt mask flags 7738c2ecf20Sopenharmony_ci * and write them to the IMR (interrupt mask register). 7748c2ecf20Sopenharmony_ci */ 7758c2ecf20Sopenharmony_ci int_mask = new_mask & AR5K_INT_COMMON; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) { 7788c2ecf20Sopenharmony_ci /* Preserve per queue TXURN interrupt mask */ 7798c2ecf20Sopenharmony_ci u32 simr2 = ath5k_hw_reg_read(ah, AR5K_SIMR2) 7808c2ecf20Sopenharmony_ci & AR5K_SIMR2_QCU_TXURN; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* Fatal interrupt abstraction for 5211+ */ 7838c2ecf20Sopenharmony_ci if (new_mask & AR5K_INT_FATAL) { 7848c2ecf20Sopenharmony_ci int_mask |= AR5K_IMR_HIUERR; 7858c2ecf20Sopenharmony_ci simr2 |= (AR5K_SIMR2_MCABT | AR5K_SIMR2_SSERR 7868c2ecf20Sopenharmony_ci | AR5K_SIMR2_DPERR); 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* Misc beacon related interrupts */ 7908c2ecf20Sopenharmony_ci if (new_mask & AR5K_INT_TIM) 7918c2ecf20Sopenharmony_ci int_mask |= AR5K_IMR_TIM; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if (new_mask & AR5K_INT_TIM) 7948c2ecf20Sopenharmony_ci simr2 |= AR5K_SISR2_TIM; 7958c2ecf20Sopenharmony_ci if (new_mask & AR5K_INT_DTIM) 7968c2ecf20Sopenharmony_ci simr2 |= AR5K_SISR2_DTIM; 7978c2ecf20Sopenharmony_ci if (new_mask & AR5K_INT_DTIM_SYNC) 7988c2ecf20Sopenharmony_ci simr2 |= AR5K_SISR2_DTIM_SYNC; 7998c2ecf20Sopenharmony_ci if (new_mask & AR5K_INT_BCN_TIMEOUT) 8008c2ecf20Sopenharmony_ci simr2 |= AR5K_SISR2_BCN_TIMEOUT; 8018c2ecf20Sopenharmony_ci if (new_mask & AR5K_INT_CAB_TIMEOUT) 8028c2ecf20Sopenharmony_ci simr2 |= AR5K_SISR2_CAB_TIMEOUT; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /*Beacon Not Ready*/ 8058c2ecf20Sopenharmony_ci if (new_mask & AR5K_INT_BNR) 8068c2ecf20Sopenharmony_ci int_mask |= AR5K_INT_BNR; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* Note: Per queue interrupt masks 8098c2ecf20Sopenharmony_ci * are set via ath5k_hw_reset_tx_queue() (qcu.c) */ 8108c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, int_mask, AR5K_PIMR); 8118c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, simr2, AR5K_SIMR2); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci } else { 8148c2ecf20Sopenharmony_ci /* Fatal interrupt abstraction for 5210 */ 8158c2ecf20Sopenharmony_ci if (new_mask & AR5K_INT_FATAL) 8168c2ecf20Sopenharmony_ci int_mask |= (AR5K_IMR_SSERR | AR5K_IMR_MCABT 8178c2ecf20Sopenharmony_ci | AR5K_IMR_HIUERR | AR5K_IMR_DPERR); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci /* Only common interrupts left for 5210 (no SIMRs) */ 8208c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, int_mask, AR5K_IMR); 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* If RXNOFRM interrupt is masked disable it 8248c2ecf20Sopenharmony_ci * by setting AR5K_RXNOFRM to zero */ 8258c2ecf20Sopenharmony_ci if (!(new_mask & AR5K_INT_RXNOFRM)) 8268c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0, AR5K_RXNOFRM); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* Store new interrupt mask */ 8298c2ecf20Sopenharmony_ci ah->ah_imr = new_mask; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* ..re-enable interrupts if AR5K_INT_GLOBAL is set */ 8328c2ecf20Sopenharmony_ci if (new_mask & AR5K_INT_GLOBAL) { 8338c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER); 8348c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_IER); 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci return old_mask; 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci/********************\ 8428c2ecf20Sopenharmony_ci Init/Stop functions 8438c2ecf20Sopenharmony_ci\********************/ 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci/** 8468c2ecf20Sopenharmony_ci * ath5k_hw_dma_init() - Initialize DMA unit 8478c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 8488c2ecf20Sopenharmony_ci * 8498c2ecf20Sopenharmony_ci * Set DMA size and pre-enable interrupts 8508c2ecf20Sopenharmony_ci * (driver handles tx/rx buffer setup and 8518c2ecf20Sopenharmony_ci * dma start/stop) 8528c2ecf20Sopenharmony_ci * 8538c2ecf20Sopenharmony_ci * XXX: Save/restore RXDP/TXDP registers ? 8548c2ecf20Sopenharmony_ci */ 8558c2ecf20Sopenharmony_civoid 8568c2ecf20Sopenharmony_ciath5k_hw_dma_init(struct ath5k_hw *ah) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci /* 8598c2ecf20Sopenharmony_ci * Set Rx/Tx DMA Configuration 8608c2ecf20Sopenharmony_ci * 8618c2ecf20Sopenharmony_ci * Set standard DMA size (128). Note that 8628c2ecf20Sopenharmony_ci * a DMA size of 512 causes rx overruns and tx errors 8638c2ecf20Sopenharmony_ci * on pci-e cards (tested on 5424 but since rx overruns 8648c2ecf20Sopenharmony_ci * also occur on 5416/5418 with madwifi we set 128 8658c2ecf20Sopenharmony_ci * for all PCI-E cards to be safe). 8668c2ecf20Sopenharmony_ci * 8678c2ecf20Sopenharmony_ci * XXX: need to check 5210 for this 8688c2ecf20Sopenharmony_ci * TODO: Check out tx trigger level, it's always 64 on dumps but I 8698c2ecf20Sopenharmony_ci * guess we can tweak it and see how it goes ;-) 8708c2ecf20Sopenharmony_ci */ 8718c2ecf20Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) { 8728c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_TXCFG, 8738c2ecf20Sopenharmony_ci AR5K_TXCFG_SDMAMR, AR5K_DMASIZE_128B); 8748c2ecf20Sopenharmony_ci AR5K_REG_WRITE_BITS(ah, AR5K_RXCFG, 8758c2ecf20Sopenharmony_ci AR5K_RXCFG_SDMAMW, AR5K_DMASIZE_128B); 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* Pre-enable interrupts on 5211/5212*/ 8798c2ecf20Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) 8808c2ecf20Sopenharmony_ci ath5k_hw_set_imr(ah, ah->ah_imr); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci/** 8858c2ecf20Sopenharmony_ci * ath5k_hw_dma_stop() - stop DMA unit 8868c2ecf20Sopenharmony_ci * @ah: The &struct ath5k_hw 8878c2ecf20Sopenharmony_ci * 8888c2ecf20Sopenharmony_ci * Stop tx/rx DMA and interrupts. Returns 8898c2ecf20Sopenharmony_ci * -EBUSY if tx or rx dma failed to stop. 8908c2ecf20Sopenharmony_ci * 8918c2ecf20Sopenharmony_ci * XXX: Sometimes DMA unit hangs and we have 8928c2ecf20Sopenharmony_ci * stuck frames on tx queues, only a reset 8938c2ecf20Sopenharmony_ci * can fix that. 8948c2ecf20Sopenharmony_ci */ 8958c2ecf20Sopenharmony_ciint 8968c2ecf20Sopenharmony_ciath5k_hw_dma_stop(struct ath5k_hw *ah) 8978c2ecf20Sopenharmony_ci{ 8988c2ecf20Sopenharmony_ci int i, qmax, err; 8998c2ecf20Sopenharmony_ci err = 0; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* Disable interrupts */ 9028c2ecf20Sopenharmony_ci ath5k_hw_set_imr(ah, 0); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci /* Stop rx dma */ 9058c2ecf20Sopenharmony_ci err = ath5k_hw_stop_rx_dma(ah); 9068c2ecf20Sopenharmony_ci if (err) 9078c2ecf20Sopenharmony_ci return err; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /* Clear any pending interrupts 9108c2ecf20Sopenharmony_ci * and disable tx dma */ 9118c2ecf20Sopenharmony_ci if (ah->ah_version != AR5K_AR5210) { 9128c2ecf20Sopenharmony_ci ath5k_hw_reg_write(ah, 0xffffffff, AR5K_PISR); 9138c2ecf20Sopenharmony_ci qmax = AR5K_NUM_TX_QUEUES; 9148c2ecf20Sopenharmony_ci } else { 9158c2ecf20Sopenharmony_ci /* PISR/SISR Not available on 5210 */ 9168c2ecf20Sopenharmony_ci ath5k_hw_reg_read(ah, AR5K_ISR); 9178c2ecf20Sopenharmony_ci qmax = AR5K_NUM_TX_QUEUES_NOQCU; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci for (i = 0; i < qmax; i++) { 9218c2ecf20Sopenharmony_ci err = ath5k_hw_stop_tx_dma(ah, i); 9228c2ecf20Sopenharmony_ci /* -EINVAL -> queue inactive */ 9238c2ecf20Sopenharmony_ci if (err && err != -EINVAL) 9248c2ecf20Sopenharmony_ci return err; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci return 0; 9288c2ecf20Sopenharmony_ci} 929