18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com> 38c2ecf20Sopenharmony_ci * Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> 48c2ecf20Sopenharmony_ci * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> 78c2ecf20Sopenharmony_ci * Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> 88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> 98c2ecf20Sopenharmony_ci * Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> 108c2ecf20Sopenharmony_ci * <http://rt2x00.serialmonkey.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* Module: rt2800mmio 148c2ecf20Sopenharmony_ci * Abstract: rt2800 MMIO device routines. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/export.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "rt2x00.h" 228c2ecf20Sopenharmony_ci#include "rt2x00mmio.h" 238c2ecf20Sopenharmony_ci#include "rt2800.h" 248c2ecf20Sopenharmony_ci#include "rt2800lib.h" 258c2ecf20Sopenharmony_ci#include "rt2800mmio.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciunsigned int rt2800mmio_get_dma_done(struct data_queue *queue) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 308c2ecf20Sopenharmony_ci struct queue_entry *entry; 318c2ecf20Sopenharmony_ci int idx, qid; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci switch (queue->qid) { 348c2ecf20Sopenharmony_ci case QID_AC_VO: 358c2ecf20Sopenharmony_ci case QID_AC_VI: 368c2ecf20Sopenharmony_ci case QID_AC_BE: 378c2ecf20Sopenharmony_ci case QID_AC_BK: 388c2ecf20Sopenharmony_ci qid = queue->qid; 398c2ecf20Sopenharmony_ci idx = rt2x00mmio_register_read(rt2x00dev, TX_DTX_IDX(qid)); 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci case QID_MGMT: 428c2ecf20Sopenharmony_ci idx = rt2x00mmio_register_read(rt2x00dev, TX_DTX_IDX(5)); 438c2ecf20Sopenharmony_ci break; 448c2ecf20Sopenharmony_ci case QID_RX: 458c2ecf20Sopenharmony_ci entry = rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE); 468c2ecf20Sopenharmony_ci idx = entry->entry_idx; 478c2ecf20Sopenharmony_ci break; 488c2ecf20Sopenharmony_ci default: 498c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 508c2ecf20Sopenharmony_ci idx = 0; 518c2ecf20Sopenharmony_ci break; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return idx; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_get_dma_done); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * TX descriptor initialization 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci__le32 *rt2800mmio_get_txwi(struct queue_entry *entry) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return (__le32 *) entry->skb->data; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_get_txwi); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_civoid rt2800mmio_write_tx_desc(struct queue_entry *entry, 688c2ecf20Sopenharmony_ci struct txentry_desc *txdesc) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); 718c2ecf20Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv = entry->priv_data; 728c2ecf20Sopenharmony_ci __le32 *txd = entry_priv->desc; 738c2ecf20Sopenharmony_ci u32 word; 748c2ecf20Sopenharmony_ci const unsigned int txwi_size = entry->queue->winfo_size; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 788c2ecf20Sopenharmony_ci * must contains a TXWI structure + 802.11 header + padding + 802.11 798c2ecf20Sopenharmony_ci * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and 808c2ecf20Sopenharmony_ci * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 818c2ecf20Sopenharmony_ci * data. It means that LAST_SEC0 is always 0. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* 858c2ecf20Sopenharmony_ci * Initialize TX descriptor 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci word = 0; 888c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); 898c2ecf20Sopenharmony_ci rt2x00_desc_write(txd, 0, word); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci word = 0; 928c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len); 938c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, 948c2ecf20Sopenharmony_ci !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); 958c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_BURST, 968c2ecf20Sopenharmony_ci test_bit(ENTRY_TXD_BURST, &txdesc->flags)); 978c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size); 988c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); 998c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); 1008c2ecf20Sopenharmony_ci rt2x00_desc_write(txd, 1, word); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci word = 0; 1038c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W2_SD_PTR1, 1048c2ecf20Sopenharmony_ci skbdesc->skb_dma + txwi_size); 1058c2ecf20Sopenharmony_ci rt2x00_desc_write(txd, 2, word); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci word = 0; 1088c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W3_WIV, 1098c2ecf20Sopenharmony_ci !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); 1108c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W3_QSEL, 2); 1118c2ecf20Sopenharmony_ci rt2x00_desc_write(txd, 3, word); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * Register descriptor details in skb frame descriptor. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci skbdesc->desc = txd; 1178c2ecf20Sopenharmony_ci skbdesc->desc_len = TXD_DESC_SIZE; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_write_tx_desc); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * RX control handlers 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_civoid rt2800mmio_fill_rxdone(struct queue_entry *entry, 1258c2ecf20Sopenharmony_ci struct rxdone_entry_desc *rxdesc) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv = entry->priv_data; 1288c2ecf20Sopenharmony_ci __le32 *rxd = entry_priv->desc; 1298c2ecf20Sopenharmony_ci u32 word; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci word = rt2x00_desc_read(rxd, 3); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR)) 1348c2ecf20Sopenharmony_ci rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * Unfortunately we don't know the cipher type used during 1388c2ecf20Sopenharmony_ci * decryption. This prevents us from correct providing 1398c2ecf20Sopenharmony_ci * correct statistics through debugfs. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) { 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * Hardware has stripped IV/EIV data from 802.11 frame during 1468c2ecf20Sopenharmony_ci * decryption. Unfortunately the descriptor doesn't contain 1478c2ecf20Sopenharmony_ci * any fields with the EIV/IV data either, so they can't 1488c2ecf20Sopenharmony_ci * be restored by rt2x00lib. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci rxdesc->flags |= RX_FLAG_IV_STRIPPED; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * The hardware has already checked the Michael Mic and has 1548c2ecf20Sopenharmony_ci * stripped it from the frame. Signal this to mac80211. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) { 1598c2ecf20Sopenharmony_ci rxdesc->flags |= RX_FLAG_DECRYPTED; 1608c2ecf20Sopenharmony_ci } else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) { 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * In order to check the Michael Mic, the packet must have 1638c2ecf20Sopenharmony_ci * been decrypted. Mac80211 doesnt check the MMIC failure 1648c2ecf20Sopenharmony_ci * flag to initiate MMIC countermeasures if the decoded flag 1658c2ecf20Sopenharmony_ci * has not been set. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci rxdesc->flags |= RX_FLAG_DECRYPTED; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci rxdesc->flags |= RX_FLAG_MMIC_ERROR; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) 1748c2ecf20Sopenharmony_ci rxdesc->dev_flags |= RXDONE_MY_BSS; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (rt2x00_get_field32(word, RXD_W3_L2PAD)) 1778c2ecf20Sopenharmony_ci rxdesc->dev_flags |= RXDONE_L2PAD; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* 1808c2ecf20Sopenharmony_ci * Process the RXWI structure that is at the start of the buffer. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci rt2800_process_rxwi(entry, rxdesc); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* 1878c2ecf20Sopenharmony_ci * Interrupt functions. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_cistatic void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct ieee80211_conf conf = { .flags = 0 }; 1928c2ecf20Sopenharmony_ci struct rt2x00lib_conf libconf = { .conf = &conf }; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, 1988c2ecf20Sopenharmony_ci struct rt2x00_field32 irq_field) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci u32 reg; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * Enable a single interrupt. The interrupt mask register 2048c2ecf20Sopenharmony_ci * access needs locking. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci spin_lock_irq(&rt2x00dev->irqmask_lock); 2078c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR); 2088c2ecf20Sopenharmony_ci rt2x00_set_field32(®, irq_field, 1); 2098c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); 2108c2ecf20Sopenharmony_ci spin_unlock_irq(&rt2x00dev->irqmask_lock); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_civoid rt2800mmio_pretbtt_tasklet(struct tasklet_struct *t) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = from_tasklet(rt2x00dev, t, 2168c2ecf20Sopenharmony_ci pretbtt_tasklet); 2178c2ecf20Sopenharmony_ci rt2x00lib_pretbtt(rt2x00dev); 2188c2ecf20Sopenharmony_ci if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 2198c2ecf20Sopenharmony_ci rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_pretbtt_tasklet); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_civoid rt2800mmio_tbtt_tasklet(struct tasklet_struct *t) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = from_tasklet(rt2x00dev, t, tbtt_tasklet); 2268c2ecf20Sopenharmony_ci struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; 2278c2ecf20Sopenharmony_ci u32 reg; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci rt2x00lib_beacondone(rt2x00dev); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (rt2x00dev->intf_ap_count) { 2328c2ecf20Sopenharmony_ci /* 2338c2ecf20Sopenharmony_ci * The rt2800pci hardware tbtt timer is off by 1us per tbtt 2348c2ecf20Sopenharmony_ci * causing beacon skew and as a result causing problems with 2358c2ecf20Sopenharmony_ci * some powersaving clients over time. Shorten the beacon 2368c2ecf20Sopenharmony_ci * interval every 64 beacons by 64us to mitigate this effect. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { 2398c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG); 2408c2ecf20Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, 2418c2ecf20Sopenharmony_ci (rt2x00dev->beacon_int * 16) - 1); 2428c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); 2438c2ecf20Sopenharmony_ci } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { 2448c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG); 2458c2ecf20Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, 2468c2ecf20Sopenharmony_ci (rt2x00dev->beacon_int * 16)); 2478c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci drv_data->tbtt_tick++; 2508c2ecf20Sopenharmony_ci drv_data->tbtt_tick %= BCN_TBTT_OFFSET; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 2548c2ecf20Sopenharmony_ci rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_tbtt_tasklet); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_civoid rt2800mmio_rxdone_tasklet(struct tasklet_struct *t) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = from_tasklet(rt2x00dev, t, 2618c2ecf20Sopenharmony_ci rxdone_tasklet); 2628c2ecf20Sopenharmony_ci if (rt2x00mmio_rxdone(rt2x00dev)) 2638c2ecf20Sopenharmony_ci tasklet_schedule(&rt2x00dev->rxdone_tasklet); 2648c2ecf20Sopenharmony_ci else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 2658c2ecf20Sopenharmony_ci rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_rxdone_tasklet); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_civoid rt2800mmio_autowake_tasklet(struct tasklet_struct *t) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = from_tasklet(rt2x00dev, t, 2728c2ecf20Sopenharmony_ci autowake_tasklet); 2738c2ecf20Sopenharmony_ci rt2800mmio_wakeup(rt2x00dev); 2748c2ecf20Sopenharmony_ci if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 2758c2ecf20Sopenharmony_ci rt2800mmio_enable_interrupt(rt2x00dev, 2768c2ecf20Sopenharmony_ci INT_MASK_CSR_AUTO_WAKEUP); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void rt2800mmio_fetch_txstatus(struct rt2x00_dev *rt2x00dev) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci u32 status; 2838c2ecf20Sopenharmony_ci unsigned long flags; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* 2868c2ecf20Sopenharmony_ci * The TX_FIFO_STATUS interrupt needs special care. We should 2878c2ecf20Sopenharmony_ci * read TX_STA_FIFO but we should do it immediately as otherwise 2888c2ecf20Sopenharmony_ci * the register can overflow and we would lose status reports. 2898c2ecf20Sopenharmony_ci * 2908c2ecf20Sopenharmony_ci * Hence, read the TX_STA_FIFO register and copy all tx status 2918c2ecf20Sopenharmony_ci * reports into a kernel FIFO which is handled in the txstatus 2928c2ecf20Sopenharmony_ci * tasklet. We use a tasklet to process the tx status reports 2938c2ecf20Sopenharmony_ci * because we can schedule the tasklet multiple times (when the 2948c2ecf20Sopenharmony_ci * interrupt fires again during tx status processing). 2958c2ecf20Sopenharmony_ci * 2968c2ecf20Sopenharmony_ci * We also read statuses from tx status timeout timer, use 2978c2ecf20Sopenharmony_ci * lock to prevent concurent writes to fifo. 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci while (!kfifo_is_full(&rt2x00dev->txstatus_fifo)) { 3038c2ecf20Sopenharmony_ci status = rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO); 3048c2ecf20Sopenharmony_ci if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci kfifo_put(&rt2x00dev->txstatus_fifo, status); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_civoid rt2800mmio_txstatus_tasklet(struct tasklet_struct *t) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = from_tasklet(rt2x00dev, t, 3168c2ecf20Sopenharmony_ci txstatus_tasklet); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci rt2800_txdone(rt2x00dev, 16); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) 3218c2ecf20Sopenharmony_ci tasklet_schedule(&rt2x00dev->txstatus_tasklet); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ciirqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = dev_instance; 3298c2ecf20Sopenharmony_ci u32 reg, mask; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* Read status and ACK all interrupts */ 3328c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR); 3338c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (!reg) 3368c2ecf20Sopenharmony_ci return IRQ_NONE; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 3398c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* 3428c2ecf20Sopenharmony_ci * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits 3438c2ecf20Sopenharmony_ci * for interrupts and interrupt masks we can just use the value of 3448c2ecf20Sopenharmony_ci * INT_SOURCE_CSR to create the interrupt mask. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci mask = ~reg; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { 3498c2ecf20Sopenharmony_ci rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); 3508c2ecf20Sopenharmony_ci rt2800mmio_fetch_txstatus(rt2x00dev); 3518c2ecf20Sopenharmony_ci if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) 3528c2ecf20Sopenharmony_ci tasklet_schedule(&rt2x00dev->txstatus_tasklet); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) 3568c2ecf20Sopenharmony_ci tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) 3598c2ecf20Sopenharmony_ci tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) 3628c2ecf20Sopenharmony_ci tasklet_schedule(&rt2x00dev->rxdone_tasklet); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) 3658c2ecf20Sopenharmony_ci tasklet_schedule(&rt2x00dev->autowake_tasklet); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* 3688c2ecf20Sopenharmony_ci * Disable all interrupts for which a tasklet was scheduled right now, 3698c2ecf20Sopenharmony_ci * the tasklet will reenable the appropriate interrupts. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci spin_lock(&rt2x00dev->irqmask_lock); 3728c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR); 3738c2ecf20Sopenharmony_ci reg &= mask; 3748c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); 3758c2ecf20Sopenharmony_ci spin_unlock(&rt2x00dev->irqmask_lock); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_interrupt); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_civoid rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, 3828c2ecf20Sopenharmony_ci enum dev_state state) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci u32 reg; 3858c2ecf20Sopenharmony_ci unsigned long flags; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* 3888c2ecf20Sopenharmony_ci * When interrupts are being enabled, the interrupt registers 3898c2ecf20Sopenharmony_ci * should clear the register to assure a clean state. 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_ci if (state == STATE_RADIO_IRQ_ON) { 3928c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR); 3938c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); 3978c2ecf20Sopenharmony_ci reg = 0; 3988c2ecf20Sopenharmony_ci if (state == STATE_RADIO_IRQ_ON) { 3998c2ecf20Sopenharmony_ci rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); 4008c2ecf20Sopenharmony_ci rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); 4018c2ecf20Sopenharmony_ci rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); 4028c2ecf20Sopenharmony_ci rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); 4038c2ecf20Sopenharmony_ci rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); 4068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (state == STATE_RADIO_IRQ_OFF) { 4098c2ecf20Sopenharmony_ci /* 4108c2ecf20Sopenharmony_ci * Wait for possibly running tasklets to finish. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->txstatus_tasklet); 4138c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->rxdone_tasklet); 4148c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->autowake_tasklet); 4158c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->tbtt_tasklet); 4168c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->pretbtt_tasklet); 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_toggle_irq); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* 4228c2ecf20Sopenharmony_ci * Queue handlers. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_civoid rt2800mmio_start_queue(struct data_queue *queue) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 4278c2ecf20Sopenharmony_ci u32 reg; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci switch (queue->qid) { 4308c2ecf20Sopenharmony_ci case QID_RX: 4318c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL); 4328c2ecf20Sopenharmony_ci rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); 4338c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); 4348c2ecf20Sopenharmony_ci break; 4358c2ecf20Sopenharmony_ci case QID_BEACON: 4368c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG); 4378c2ecf20Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); 4388c2ecf20Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); 4398c2ecf20Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); 4408c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN); 4438c2ecf20Sopenharmony_ci rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); 4448c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci default: 4478c2ecf20Sopenharmony_ci break; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_start_queue); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/* 200 ms */ 4538c2ecf20Sopenharmony_ci#define TXSTATUS_TIMEOUT 200000000 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_civoid rt2800mmio_kick_queue(struct data_queue *queue) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 4588c2ecf20Sopenharmony_ci struct queue_entry *entry; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci switch (queue->qid) { 4618c2ecf20Sopenharmony_ci case QID_AC_VO: 4628c2ecf20Sopenharmony_ci case QID_AC_VI: 4638c2ecf20Sopenharmony_ci case QID_AC_BE: 4648c2ecf20Sopenharmony_ci case QID_AC_BK: 4658c2ecf20Sopenharmony_ci WARN_ON_ONCE(rt2x00queue_empty(queue)); 4668c2ecf20Sopenharmony_ci entry = rt2x00queue_get_entry(queue, Q_INDEX); 4678c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid), 4688c2ecf20Sopenharmony_ci entry->entry_idx); 4698c2ecf20Sopenharmony_ci hrtimer_start(&rt2x00dev->txstatus_timer, 4708c2ecf20Sopenharmony_ci TXSTATUS_TIMEOUT, HRTIMER_MODE_REL); 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case QID_MGMT: 4738c2ecf20Sopenharmony_ci entry = rt2x00queue_get_entry(queue, Q_INDEX); 4748c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5), 4758c2ecf20Sopenharmony_ci entry->entry_idx); 4768c2ecf20Sopenharmony_ci break; 4778c2ecf20Sopenharmony_ci default: 4788c2ecf20Sopenharmony_ci break; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_kick_queue); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_civoid rt2800mmio_flush_queue(struct data_queue *queue, bool drop) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 4868c2ecf20Sopenharmony_ci bool tx_queue = false; 4878c2ecf20Sopenharmony_ci unsigned int i; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci switch (queue->qid) { 4908c2ecf20Sopenharmony_ci case QID_AC_VO: 4918c2ecf20Sopenharmony_ci case QID_AC_VI: 4928c2ecf20Sopenharmony_ci case QID_AC_BE: 4938c2ecf20Sopenharmony_ci case QID_AC_BK: 4948c2ecf20Sopenharmony_ci tx_queue = true; 4958c2ecf20Sopenharmony_ci break; 4968c2ecf20Sopenharmony_ci case QID_RX: 4978c2ecf20Sopenharmony_ci break; 4988c2ecf20Sopenharmony_ci default: 4998c2ecf20Sopenharmony_ci return; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 5038c2ecf20Sopenharmony_ci /* 5048c2ecf20Sopenharmony_ci * Check if the driver is already done, otherwise we 5058c2ecf20Sopenharmony_ci * have to sleep a little while to give the driver/hw 5068c2ecf20Sopenharmony_ci * the oppurtunity to complete interrupt process itself. 5078c2ecf20Sopenharmony_ci */ 5088c2ecf20Sopenharmony_ci if (rt2x00queue_empty(queue)) 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* 5128c2ecf20Sopenharmony_ci * For TX queues schedule completion tasklet to catch 5138c2ecf20Sopenharmony_ci * tx status timeouts, othewise just wait. 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_ci if (tx_queue) 5168c2ecf20Sopenharmony_ci queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* 5198c2ecf20Sopenharmony_ci * Wait for a little while to give the driver 5208c2ecf20Sopenharmony_ci * the oppurtunity to recover itself. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci msleep(50); 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_flush_queue); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_civoid rt2800mmio_stop_queue(struct data_queue *queue) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 5308c2ecf20Sopenharmony_ci u32 reg; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci switch (queue->qid) { 5338c2ecf20Sopenharmony_ci case QID_RX: 5348c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL); 5358c2ecf20Sopenharmony_ci rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); 5368c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); 5378c2ecf20Sopenharmony_ci break; 5388c2ecf20Sopenharmony_ci case QID_BEACON: 5398c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG); 5408c2ecf20Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); 5418c2ecf20Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); 5428c2ecf20Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); 5438c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN); 5468c2ecf20Sopenharmony_ci rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); 5478c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* 5508c2ecf20Sopenharmony_ci * Wait for current invocation to finish. The tasklet 5518c2ecf20Sopenharmony_ci * won't be scheduled anymore afterwards since we disabled 5528c2ecf20Sopenharmony_ci * the TBTT and PRE TBTT timer. 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->tbtt_tasklet); 5558c2ecf20Sopenharmony_ci tasklet_kill(&rt2x00dev->pretbtt_tasklet); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci break; 5588c2ecf20Sopenharmony_ci default: 5598c2ecf20Sopenharmony_ci break; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_stop_queue); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_civoid rt2800mmio_queue_init(struct data_queue *queue) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 5678c2ecf20Sopenharmony_ci unsigned short txwi_size, rxwi_size; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci switch (queue->qid) { 5728c2ecf20Sopenharmony_ci case QID_RX: 5738c2ecf20Sopenharmony_ci queue->limit = 128; 5748c2ecf20Sopenharmony_ci queue->data_size = AGGREGATION_SIZE; 5758c2ecf20Sopenharmony_ci queue->desc_size = RXD_DESC_SIZE; 5768c2ecf20Sopenharmony_ci queue->winfo_size = rxwi_size; 5778c2ecf20Sopenharmony_ci queue->priv_size = sizeof(struct queue_entry_priv_mmio); 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci case QID_AC_VO: 5818c2ecf20Sopenharmony_ci case QID_AC_VI: 5828c2ecf20Sopenharmony_ci case QID_AC_BE: 5838c2ecf20Sopenharmony_ci case QID_AC_BK: 5848c2ecf20Sopenharmony_ci queue->limit = 64; 5858c2ecf20Sopenharmony_ci queue->data_size = AGGREGATION_SIZE; 5868c2ecf20Sopenharmony_ci queue->desc_size = TXD_DESC_SIZE; 5878c2ecf20Sopenharmony_ci queue->winfo_size = txwi_size; 5888c2ecf20Sopenharmony_ci queue->priv_size = sizeof(struct queue_entry_priv_mmio); 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci case QID_BEACON: 5928c2ecf20Sopenharmony_ci queue->limit = 8; 5938c2ecf20Sopenharmony_ci queue->data_size = 0; /* No DMA required for beacons */ 5948c2ecf20Sopenharmony_ci queue->desc_size = TXD_DESC_SIZE; 5958c2ecf20Sopenharmony_ci queue->winfo_size = txwi_size; 5968c2ecf20Sopenharmony_ci queue->priv_size = sizeof(struct queue_entry_priv_mmio); 5978c2ecf20Sopenharmony_ci break; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci case QID_ATIM: 6008c2ecf20Sopenharmony_ci default: 6018c2ecf20Sopenharmony_ci BUG(); 6028c2ecf20Sopenharmony_ci break; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_queue_init); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci/* 6088c2ecf20Sopenharmony_ci * Initialization functions. 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_cibool rt2800mmio_get_entry_state(struct queue_entry *entry) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv = entry->priv_data; 6138c2ecf20Sopenharmony_ci u32 word; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (entry->queue->qid == QID_RX) { 6168c2ecf20Sopenharmony_ci word = rt2x00_desc_read(entry_priv->desc, 1); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); 6198c2ecf20Sopenharmony_ci } else { 6208c2ecf20Sopenharmony_ci word = rt2x00_desc_read(entry_priv->desc, 1); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_get_entry_state); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_civoid rt2800mmio_clear_entry(struct queue_entry *entry) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv = entry->priv_data; 6308c2ecf20Sopenharmony_ci struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); 6318c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; 6328c2ecf20Sopenharmony_ci u32 word; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (entry->queue->qid == QID_RX) { 6358c2ecf20Sopenharmony_ci word = rt2x00_desc_read(entry_priv->desc, 0); 6368c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); 6378c2ecf20Sopenharmony_ci rt2x00_desc_write(entry_priv->desc, 0, word); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci word = rt2x00_desc_read(entry_priv->desc, 1); 6408c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); 6418c2ecf20Sopenharmony_ci rt2x00_desc_write(entry_priv->desc, 1, word); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* 6448c2ecf20Sopenharmony_ci * Set RX IDX in register to inform hardware that we have 6458c2ecf20Sopenharmony_ci * handled this entry and it is available for reuse again. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, 6488c2ecf20Sopenharmony_ci entry->entry_idx); 6498c2ecf20Sopenharmony_ci } else { 6508c2ecf20Sopenharmony_ci word = rt2x00_desc_read(entry_priv->desc, 1); 6518c2ecf20Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); 6528c2ecf20Sopenharmony_ci rt2x00_desc_write(entry_priv->desc, 1, word); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* If last entry stop txstatus timer */ 6558c2ecf20Sopenharmony_ci if (entry->queue->length == 1) 6568c2ecf20Sopenharmony_ci hrtimer_cancel(&rt2x00dev->txstatus_timer); 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_clear_entry); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ciint rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* 6668c2ecf20Sopenharmony_ci * Initialize registers. 6678c2ecf20Sopenharmony_ci */ 6688c2ecf20Sopenharmony_ci entry_priv = rt2x00dev->tx[0].entries[0].priv_data; 6698c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0, 6708c2ecf20Sopenharmony_ci entry_priv->desc_dma); 6718c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0, 6728c2ecf20Sopenharmony_ci rt2x00dev->tx[0].limit); 6738c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0); 6748c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci entry_priv = rt2x00dev->tx[1].entries[0].priv_data; 6778c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1, 6788c2ecf20Sopenharmony_ci entry_priv->desc_dma); 6798c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1, 6808c2ecf20Sopenharmony_ci rt2x00dev->tx[1].limit); 6818c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0); 6828c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci entry_priv = rt2x00dev->tx[2].entries[0].priv_data; 6858c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2, 6868c2ecf20Sopenharmony_ci entry_priv->desc_dma); 6878c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2, 6888c2ecf20Sopenharmony_ci rt2x00dev->tx[2].limit); 6898c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0); 6908c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci entry_priv = rt2x00dev->tx[3].entries[0].priv_data; 6938c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3, 6948c2ecf20Sopenharmony_ci entry_priv->desc_dma); 6958c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3, 6968c2ecf20Sopenharmony_ci rt2x00dev->tx[3].limit); 6978c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0); 6988c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0); 7018c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0); 7028c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0); 7038c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0); 7068c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0); 7078c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0); 7088c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci entry_priv = rt2x00dev->rx->entries[0].priv_data; 7118c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR, 7128c2ecf20Sopenharmony_ci entry_priv->desc_dma); 7138c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT, 7148c2ecf20Sopenharmony_ci rt2x00dev->rx[0].limit); 7158c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, 7168c2ecf20Sopenharmony_ci rt2x00dev->rx[0].limit - 1); 7178c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci rt2800_disable_wpdma(rt2x00dev); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci return 0; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_init_queues); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ciint rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci u32 reg; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* 7328c2ecf20Sopenharmony_ci * Reset DMA indexes 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX); 7358c2ecf20Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); 7368c2ecf20Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); 7378c2ecf20Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); 7388c2ecf20Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); 7398c2ecf20Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); 7408c2ecf20Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); 7418c2ecf20Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); 7428c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); 7458c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (rt2x00_is_pcie(rt2x00dev) && 7488c2ecf20Sopenharmony_ci (rt2x00_rt(rt2x00dev, RT3090) || 7498c2ecf20Sopenharmony_ci rt2x00_rt(rt2x00dev, RT3390) || 7508c2ecf20Sopenharmony_ci rt2x00_rt(rt2x00dev, RT3572) || 7518c2ecf20Sopenharmony_ci rt2x00_rt(rt2x00dev, RT3593) || 7528c2ecf20Sopenharmony_ci rt2x00_rt(rt2x00dev, RT5390) || 7538c2ecf20Sopenharmony_ci rt2x00_rt(rt2x00dev, RT5392) || 7548c2ecf20Sopenharmony_ci rt2x00_rt(rt2x00dev, RT5592))) { 7558c2ecf20Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, AUX_CTRL); 7568c2ecf20Sopenharmony_ci rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); 7578c2ecf20Sopenharmony_ci rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); 7588c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg); 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci reg = 0; 7648c2ecf20Sopenharmony_ci rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); 7658c2ecf20Sopenharmony_ci rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); 7668c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci return 0; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_init_registers); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/* 7758c2ecf20Sopenharmony_ci * Device state switch handlers. 7768c2ecf20Sopenharmony_ci */ 7778c2ecf20Sopenharmony_ciint rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci /* Wait for DMA, ignore error until we initialize queues. */ 7808c2ecf20Sopenharmony_ci rt2800_wait_wpdma_ready(rt2x00dev); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (unlikely(rt2800mmio_init_queues(rt2x00dev))) 7838c2ecf20Sopenharmony_ci return -EIO; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci return rt2800_enable_radio(rt2x00dev); 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_enable_radio); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic void rt2800mmio_work_txdone(struct work_struct *work) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = 7928c2ecf20Sopenharmony_ci container_of(work, struct rt2x00_dev, txdone_work); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 7958c2ecf20Sopenharmony_ci return; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) || 7988c2ecf20Sopenharmony_ci rt2800_txstatus_timeout(rt2x00dev)) { 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci tasklet_disable(&rt2x00dev->txstatus_tasklet); 8018c2ecf20Sopenharmony_ci rt2800_txdone(rt2x00dev, UINT_MAX); 8028c2ecf20Sopenharmony_ci rt2800_txdone_nostatus(rt2x00dev); 8038c2ecf20Sopenharmony_ci tasklet_enable(&rt2x00dev->txstatus_tasklet); 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (rt2800_txstatus_pending(rt2x00dev)) 8078c2ecf20Sopenharmony_ci hrtimer_start(&rt2x00dev->txstatus_timer, 8088c2ecf20Sopenharmony_ci TXSTATUS_TIMEOUT, HRTIMER_MODE_REL); 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistatic enum hrtimer_restart rt2800mmio_tx_sta_fifo_timeout(struct hrtimer *timer) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci struct rt2x00_dev *rt2x00dev = 8148c2ecf20Sopenharmony_ci container_of(timer, struct rt2x00_dev, txstatus_timer); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 8178c2ecf20Sopenharmony_ci goto out; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (!rt2800_txstatus_pending(rt2x00dev)) 8208c2ecf20Sopenharmony_ci goto out; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci rt2800mmio_fetch_txstatus(rt2x00dev); 8238c2ecf20Sopenharmony_ci if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) 8248c2ecf20Sopenharmony_ci tasklet_schedule(&rt2x00dev->txstatus_tasklet); 8258c2ecf20Sopenharmony_ci else 8268c2ecf20Sopenharmony_ci queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); 8278c2ecf20Sopenharmony_ciout: 8288c2ecf20Sopenharmony_ci return HRTIMER_NORESTART; 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ciint rt2800mmio_probe_hw(struct rt2x00_dev *rt2x00dev) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci int retval; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci retval = rt2800_probe_hw(rt2x00dev); 8368c2ecf20Sopenharmony_ci if (retval) 8378c2ecf20Sopenharmony_ci return retval; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* 8408c2ecf20Sopenharmony_ci * Set txstatus timer function. 8418c2ecf20Sopenharmony_ci */ 8428c2ecf20Sopenharmony_ci rt2x00dev->txstatus_timer.function = rt2800mmio_tx_sta_fifo_timeout; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci /* 8458c2ecf20Sopenharmony_ci * Overwrite TX done handler 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_ci INIT_WORK(&rt2x00dev->txdone_work, rt2800mmio_work_txdone); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci return 0; 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_probe_hw); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRV_PROJECT); 8548c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 8558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("rt2800 MMIO library"); 8568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 857