162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com> 362306a36Sopenharmony_ci * Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> 462306a36Sopenharmony_ci * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> 562306a36Sopenharmony_ci * Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> 662306a36Sopenharmony_ci * Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> 762306a36Sopenharmony_ci * Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> 862306a36Sopenharmony_ci * Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> 962306a36Sopenharmony_ci * Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> 1062306a36Sopenharmony_ci * <http://rt2x00.serialmonkey.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* Module: rt2800mmio 1462306a36Sopenharmony_ci * Abstract: rt2800 MMIO device routines. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/export.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "rt2x00.h" 2262306a36Sopenharmony_ci#include "rt2x00mmio.h" 2362306a36Sopenharmony_ci#include "rt2800.h" 2462306a36Sopenharmony_ci#include "rt2800lib.h" 2562306a36Sopenharmony_ci#include "rt2800mmio.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciunsigned int rt2800mmio_get_dma_done(struct data_queue *queue) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 3062306a36Sopenharmony_ci struct queue_entry *entry; 3162306a36Sopenharmony_ci int idx, qid; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci switch (queue->qid) { 3462306a36Sopenharmony_ci case QID_AC_VO: 3562306a36Sopenharmony_ci case QID_AC_VI: 3662306a36Sopenharmony_ci case QID_AC_BE: 3762306a36Sopenharmony_ci case QID_AC_BK: 3862306a36Sopenharmony_ci qid = queue->qid; 3962306a36Sopenharmony_ci idx = rt2x00mmio_register_read(rt2x00dev, TX_DTX_IDX(qid)); 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci case QID_MGMT: 4262306a36Sopenharmony_ci idx = rt2x00mmio_register_read(rt2x00dev, TX_DTX_IDX(5)); 4362306a36Sopenharmony_ci break; 4462306a36Sopenharmony_ci case QID_RX: 4562306a36Sopenharmony_ci entry = rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE); 4662306a36Sopenharmony_ci idx = entry->entry_idx; 4762306a36Sopenharmony_ci break; 4862306a36Sopenharmony_ci default: 4962306a36Sopenharmony_ci WARN_ON_ONCE(1); 5062306a36Sopenharmony_ci idx = 0; 5162306a36Sopenharmony_ci break; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return idx; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_get_dma_done); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * TX descriptor initialization 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_ci__le32 *rt2800mmio_get_txwi(struct queue_entry *entry) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci return (__le32 *) entry->skb->data; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_get_txwi); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_civoid rt2800mmio_write_tx_desc(struct queue_entry *entry, 6862306a36Sopenharmony_ci struct txentry_desc *txdesc) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); 7162306a36Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv = entry->priv_data; 7262306a36Sopenharmony_ci __le32 *txd = entry_priv->desc; 7362306a36Sopenharmony_ci u32 word; 7462306a36Sopenharmony_ci const unsigned int txwi_size = entry->queue->winfo_size; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 7762306a36Sopenharmony_ci * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 7862306a36Sopenharmony_ci * must contains a TXWI structure + 802.11 header + padding + 802.11 7962306a36Sopenharmony_ci * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and 8062306a36Sopenharmony_ci * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 8162306a36Sopenharmony_ci * data. It means that LAST_SEC0 is always 0. 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * Initialize TX descriptor 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci word = 0; 8862306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); 8962306a36Sopenharmony_ci rt2x00_desc_write(txd, 0, word); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci word = 0; 9262306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len); 9362306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, 9462306a36Sopenharmony_ci !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); 9562306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_BURST, 9662306a36Sopenharmony_ci test_bit(ENTRY_TXD_BURST, &txdesc->flags)); 9762306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size); 9862306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); 9962306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); 10062306a36Sopenharmony_ci rt2x00_desc_write(txd, 1, word); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci word = 0; 10362306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W2_SD_PTR1, 10462306a36Sopenharmony_ci skbdesc->skb_dma + txwi_size); 10562306a36Sopenharmony_ci rt2x00_desc_write(txd, 2, word); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci word = 0; 10862306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W3_WIV, 10962306a36Sopenharmony_ci !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); 11062306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W3_QSEL, 2); 11162306a36Sopenharmony_ci rt2x00_desc_write(txd, 3, word); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* 11462306a36Sopenharmony_ci * Register descriptor details in skb frame descriptor. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci skbdesc->desc = txd; 11762306a36Sopenharmony_ci skbdesc->desc_len = TXD_DESC_SIZE; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_write_tx_desc); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * RX control handlers 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_civoid rt2800mmio_fill_rxdone(struct queue_entry *entry, 12562306a36Sopenharmony_ci struct rxdone_entry_desc *rxdesc) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv = entry->priv_data; 12862306a36Sopenharmony_ci __le32 *rxd = entry_priv->desc; 12962306a36Sopenharmony_ci u32 word; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci word = rt2x00_desc_read(rxd, 3); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR)) 13462306a36Sopenharmony_ci rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * Unfortunately we don't know the cipher type used during 13862306a36Sopenharmony_ci * decryption. This prevents us from correct providing 13962306a36Sopenharmony_ci * correct statistics through debugfs. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) { 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * Hardware has stripped IV/EIV data from 802.11 frame during 14662306a36Sopenharmony_ci * decryption. Unfortunately the descriptor doesn't contain 14762306a36Sopenharmony_ci * any fields with the EIV/IV data either, so they can't 14862306a36Sopenharmony_ci * be restored by rt2x00lib. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci rxdesc->flags |= RX_FLAG_IV_STRIPPED; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * The hardware has already checked the Michael Mic and has 15462306a36Sopenharmony_ci * stripped it from the frame. Signal this to mac80211. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) { 15962306a36Sopenharmony_ci rxdesc->flags |= RX_FLAG_DECRYPTED; 16062306a36Sopenharmony_ci } else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) { 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * In order to check the Michael Mic, the packet must have 16362306a36Sopenharmony_ci * been decrypted. Mac80211 doesnt check the MMIC failure 16462306a36Sopenharmony_ci * flag to initiate MMIC countermeasures if the decoded flag 16562306a36Sopenharmony_ci * has not been set. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci rxdesc->flags |= RX_FLAG_DECRYPTED; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci rxdesc->flags |= RX_FLAG_MMIC_ERROR; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) 17462306a36Sopenharmony_ci rxdesc->dev_flags |= RXDONE_MY_BSS; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (rt2x00_get_field32(word, RXD_W3_L2PAD)) 17762306a36Sopenharmony_ci rxdesc->dev_flags |= RXDONE_L2PAD; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* 18062306a36Sopenharmony_ci * Process the RXWI structure that is at the start of the buffer. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci rt2800_process_rxwi(entry, rxdesc); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* 18762306a36Sopenharmony_ci * Interrupt functions. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistatic void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct ieee80211_conf conf = { .flags = 0 }; 19262306a36Sopenharmony_ci struct rt2x00lib_conf libconf = { .conf = &conf }; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, 19862306a36Sopenharmony_ci struct rt2x00_field32 irq_field) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci u32 reg; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci * Enable a single interrupt. The interrupt mask register 20462306a36Sopenharmony_ci * access needs locking. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci spin_lock_irq(&rt2x00dev->irqmask_lock); 20762306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR); 20862306a36Sopenharmony_ci rt2x00_set_field32(®, irq_field, 1); 20962306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); 21062306a36Sopenharmony_ci spin_unlock_irq(&rt2x00dev->irqmask_lock); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_civoid rt2800mmio_pretbtt_tasklet(struct tasklet_struct *t) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = from_tasklet(rt2x00dev, t, 21662306a36Sopenharmony_ci pretbtt_tasklet); 21762306a36Sopenharmony_ci rt2x00lib_pretbtt(rt2x00dev); 21862306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 21962306a36Sopenharmony_ci rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_pretbtt_tasklet); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_civoid rt2800mmio_tbtt_tasklet(struct tasklet_struct *t) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = from_tasklet(rt2x00dev, t, tbtt_tasklet); 22662306a36Sopenharmony_ci struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; 22762306a36Sopenharmony_ci u32 reg; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci rt2x00lib_beacondone(rt2x00dev); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (rt2x00dev->intf_ap_count) { 23262306a36Sopenharmony_ci /* 23362306a36Sopenharmony_ci * The rt2800pci hardware tbtt timer is off by 1us per tbtt 23462306a36Sopenharmony_ci * causing beacon skew and as a result causing problems with 23562306a36Sopenharmony_ci * some powersaving clients over time. Shorten the beacon 23662306a36Sopenharmony_ci * interval every 64 beacons by 64us to mitigate this effect. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { 23962306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG); 24062306a36Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, 24162306a36Sopenharmony_ci (rt2x00dev->beacon_int * 16) - 1); 24262306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); 24362306a36Sopenharmony_ci } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { 24462306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG); 24562306a36Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, 24662306a36Sopenharmony_ci (rt2x00dev->beacon_int * 16)); 24762306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci drv_data->tbtt_tick++; 25062306a36Sopenharmony_ci drv_data->tbtt_tick %= BCN_TBTT_OFFSET; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 25462306a36Sopenharmony_ci rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_tbtt_tasklet); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_civoid rt2800mmio_rxdone_tasklet(struct tasklet_struct *t) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = from_tasklet(rt2x00dev, t, 26162306a36Sopenharmony_ci rxdone_tasklet); 26262306a36Sopenharmony_ci if (rt2x00mmio_rxdone(rt2x00dev)) 26362306a36Sopenharmony_ci tasklet_schedule(&rt2x00dev->rxdone_tasklet); 26462306a36Sopenharmony_ci else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 26562306a36Sopenharmony_ci rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_rxdone_tasklet); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_civoid rt2800mmio_autowake_tasklet(struct tasklet_struct *t) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = from_tasklet(rt2x00dev, t, 27262306a36Sopenharmony_ci autowake_tasklet); 27362306a36Sopenharmony_ci rt2800mmio_wakeup(rt2x00dev); 27462306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 27562306a36Sopenharmony_ci rt2800mmio_enable_interrupt(rt2x00dev, 27662306a36Sopenharmony_ci INT_MASK_CSR_AUTO_WAKEUP); 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic void rt2800mmio_fetch_txstatus(struct rt2x00_dev *rt2x00dev) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci u32 status; 28362306a36Sopenharmony_ci unsigned long flags; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* 28662306a36Sopenharmony_ci * The TX_FIFO_STATUS interrupt needs special care. We should 28762306a36Sopenharmony_ci * read TX_STA_FIFO but we should do it immediately as otherwise 28862306a36Sopenharmony_ci * the register can overflow and we would lose status reports. 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * Hence, read the TX_STA_FIFO register and copy all tx status 29162306a36Sopenharmony_ci * reports into a kernel FIFO which is handled in the txstatus 29262306a36Sopenharmony_ci * tasklet. We use a tasklet to process the tx status reports 29362306a36Sopenharmony_ci * because we can schedule the tasklet multiple times (when the 29462306a36Sopenharmony_ci * interrupt fires again during tx status processing). 29562306a36Sopenharmony_ci * 29662306a36Sopenharmony_ci * We also read statuses from tx status timeout timer, use 29762306a36Sopenharmony_ci * lock to prevent concurent writes to fifo. 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci while (!kfifo_is_full(&rt2x00dev->txstatus_fifo)) { 30362306a36Sopenharmony_ci status = rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO); 30462306a36Sopenharmony_ci if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci kfifo_put(&rt2x00dev->txstatus_fifo, status); 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_civoid rt2800mmio_txstatus_tasklet(struct tasklet_struct *t) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = from_tasklet(rt2x00dev, t, 31662306a36Sopenharmony_ci txstatus_tasklet); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci rt2800_txdone(rt2x00dev, 16); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) 32162306a36Sopenharmony_ci tasklet_schedule(&rt2x00dev->txstatus_tasklet); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ciirqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = dev_instance; 32962306a36Sopenharmony_ci u32 reg, mask; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Read status and ACK all interrupts */ 33262306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR); 33362306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (!reg) 33662306a36Sopenharmony_ci return IRQ_NONE; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 33962306a36Sopenharmony_ci return IRQ_HANDLED; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* 34262306a36Sopenharmony_ci * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits 34362306a36Sopenharmony_ci * for interrupts and interrupt masks we can just use the value of 34462306a36Sopenharmony_ci * INT_SOURCE_CSR to create the interrupt mask. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci mask = ~reg; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { 34962306a36Sopenharmony_ci rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); 35062306a36Sopenharmony_ci rt2800mmio_fetch_txstatus(rt2x00dev); 35162306a36Sopenharmony_ci if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) 35262306a36Sopenharmony_ci tasklet_schedule(&rt2x00dev->txstatus_tasklet); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) 35662306a36Sopenharmony_ci tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) 35962306a36Sopenharmony_ci tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) 36262306a36Sopenharmony_ci tasklet_schedule(&rt2x00dev->rxdone_tasklet); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) 36562306a36Sopenharmony_ci tasklet_schedule(&rt2x00dev->autowake_tasklet); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * Disable all interrupts for which a tasklet was scheduled right now, 36962306a36Sopenharmony_ci * the tasklet will reenable the appropriate interrupts. 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_ci spin_lock(&rt2x00dev->irqmask_lock); 37262306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR); 37362306a36Sopenharmony_ci reg &= mask; 37462306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); 37562306a36Sopenharmony_ci spin_unlock(&rt2x00dev->irqmask_lock); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return IRQ_HANDLED; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_interrupt); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_civoid rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, 38262306a36Sopenharmony_ci enum dev_state state) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci u32 reg; 38562306a36Sopenharmony_ci unsigned long flags; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* 38862306a36Sopenharmony_ci * When interrupts are being enabled, the interrupt registers 38962306a36Sopenharmony_ci * should clear the register to assure a clean state. 39062306a36Sopenharmony_ci */ 39162306a36Sopenharmony_ci if (state == STATE_RADIO_IRQ_ON) { 39262306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR); 39362306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); 39762306a36Sopenharmony_ci reg = 0; 39862306a36Sopenharmony_ci if (state == STATE_RADIO_IRQ_ON) { 39962306a36Sopenharmony_ci rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); 40062306a36Sopenharmony_ci rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); 40162306a36Sopenharmony_ci rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); 40262306a36Sopenharmony_ci rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); 40362306a36Sopenharmony_ci rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); 40662306a36Sopenharmony_ci spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (state == STATE_RADIO_IRQ_OFF) { 40962306a36Sopenharmony_ci /* 41062306a36Sopenharmony_ci * Wait for possibly running tasklets to finish. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci tasklet_kill(&rt2x00dev->txstatus_tasklet); 41362306a36Sopenharmony_ci tasklet_kill(&rt2x00dev->rxdone_tasklet); 41462306a36Sopenharmony_ci tasklet_kill(&rt2x00dev->autowake_tasklet); 41562306a36Sopenharmony_ci tasklet_kill(&rt2x00dev->tbtt_tasklet); 41662306a36Sopenharmony_ci tasklet_kill(&rt2x00dev->pretbtt_tasklet); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_toggle_irq); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci/* 42262306a36Sopenharmony_ci * Queue handlers. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_civoid rt2800mmio_start_queue(struct data_queue *queue) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 42762306a36Sopenharmony_ci u32 reg; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci switch (queue->qid) { 43062306a36Sopenharmony_ci case QID_RX: 43162306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL); 43262306a36Sopenharmony_ci rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); 43362306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); 43462306a36Sopenharmony_ci break; 43562306a36Sopenharmony_ci case QID_BEACON: 43662306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG); 43762306a36Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); 43862306a36Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); 43962306a36Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); 44062306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN); 44362306a36Sopenharmony_ci rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); 44462306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci default: 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_start_queue); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci/* 200 ms */ 45362306a36Sopenharmony_ci#define TXSTATUS_TIMEOUT 200000000 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_civoid rt2800mmio_kick_queue(struct data_queue *queue) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 45862306a36Sopenharmony_ci struct queue_entry *entry; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci switch (queue->qid) { 46162306a36Sopenharmony_ci case QID_AC_VO: 46262306a36Sopenharmony_ci case QID_AC_VI: 46362306a36Sopenharmony_ci case QID_AC_BE: 46462306a36Sopenharmony_ci case QID_AC_BK: 46562306a36Sopenharmony_ci WARN_ON_ONCE(rt2x00queue_empty(queue)); 46662306a36Sopenharmony_ci entry = rt2x00queue_get_entry(queue, Q_INDEX); 46762306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid), 46862306a36Sopenharmony_ci entry->entry_idx); 46962306a36Sopenharmony_ci hrtimer_start(&rt2x00dev->txstatus_timer, 47062306a36Sopenharmony_ci TXSTATUS_TIMEOUT, HRTIMER_MODE_REL); 47162306a36Sopenharmony_ci break; 47262306a36Sopenharmony_ci case QID_MGMT: 47362306a36Sopenharmony_ci entry = rt2x00queue_get_entry(queue, Q_INDEX); 47462306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5), 47562306a36Sopenharmony_ci entry->entry_idx); 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci default: 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_kick_queue); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_civoid rt2800mmio_flush_queue(struct data_queue *queue, bool drop) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 48662306a36Sopenharmony_ci bool tx_queue = false; 48762306a36Sopenharmony_ci unsigned int i; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci switch (queue->qid) { 49062306a36Sopenharmony_ci case QID_AC_VO: 49162306a36Sopenharmony_ci case QID_AC_VI: 49262306a36Sopenharmony_ci case QID_AC_BE: 49362306a36Sopenharmony_ci case QID_AC_BK: 49462306a36Sopenharmony_ci tx_queue = true; 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci case QID_RX: 49762306a36Sopenharmony_ci break; 49862306a36Sopenharmony_ci default: 49962306a36Sopenharmony_ci return; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci for (i = 0; i < 5; i++) { 50362306a36Sopenharmony_ci /* 50462306a36Sopenharmony_ci * Check if the driver is already done, otherwise we 50562306a36Sopenharmony_ci * have to sleep a little while to give the driver/hw 50662306a36Sopenharmony_ci * the oppurtunity to complete interrupt process itself. 50762306a36Sopenharmony_ci */ 50862306a36Sopenharmony_ci if (rt2x00queue_empty(queue)) 50962306a36Sopenharmony_ci break; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* 51262306a36Sopenharmony_ci * For TX queues schedule completion tasklet to catch 51362306a36Sopenharmony_ci * tx status timeouts, othewise just wait. 51462306a36Sopenharmony_ci */ 51562306a36Sopenharmony_ci if (tx_queue) 51662306a36Sopenharmony_ci queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* 51962306a36Sopenharmony_ci * Wait for a little while to give the driver 52062306a36Sopenharmony_ci * the oppurtunity to recover itself. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci msleep(50); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci} 52562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_flush_queue); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_civoid rt2800mmio_stop_queue(struct data_queue *queue) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 53062306a36Sopenharmony_ci u32 reg; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci switch (queue->qid) { 53362306a36Sopenharmony_ci case QID_RX: 53462306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL); 53562306a36Sopenharmony_ci rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); 53662306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); 53762306a36Sopenharmony_ci break; 53862306a36Sopenharmony_ci case QID_BEACON: 53962306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG); 54062306a36Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); 54162306a36Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); 54262306a36Sopenharmony_ci rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); 54362306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN); 54662306a36Sopenharmony_ci rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); 54762306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* 55062306a36Sopenharmony_ci * Wait for current invocation to finish. The tasklet 55162306a36Sopenharmony_ci * won't be scheduled anymore afterwards since we disabled 55262306a36Sopenharmony_ci * the TBTT and PRE TBTT timer. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci tasklet_kill(&rt2x00dev->tbtt_tasklet); 55562306a36Sopenharmony_ci tasklet_kill(&rt2x00dev->pretbtt_tasklet); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci default: 55962306a36Sopenharmony_ci break; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_stop_queue); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_civoid rt2800mmio_queue_init(struct data_queue *queue) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; 56762306a36Sopenharmony_ci unsigned short txwi_size, rxwi_size; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci switch (queue->qid) { 57262306a36Sopenharmony_ci case QID_RX: 57362306a36Sopenharmony_ci queue->limit = 128; 57462306a36Sopenharmony_ci queue->data_size = AGGREGATION_SIZE; 57562306a36Sopenharmony_ci queue->desc_size = RXD_DESC_SIZE; 57662306a36Sopenharmony_ci queue->winfo_size = rxwi_size; 57762306a36Sopenharmony_ci queue->priv_size = sizeof(struct queue_entry_priv_mmio); 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci case QID_AC_VO: 58162306a36Sopenharmony_ci case QID_AC_VI: 58262306a36Sopenharmony_ci case QID_AC_BE: 58362306a36Sopenharmony_ci case QID_AC_BK: 58462306a36Sopenharmony_ci queue->limit = 64; 58562306a36Sopenharmony_ci queue->data_size = AGGREGATION_SIZE; 58662306a36Sopenharmony_ci queue->desc_size = TXD_DESC_SIZE; 58762306a36Sopenharmony_ci queue->winfo_size = txwi_size; 58862306a36Sopenharmony_ci queue->priv_size = sizeof(struct queue_entry_priv_mmio); 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci case QID_BEACON: 59262306a36Sopenharmony_ci queue->limit = 8; 59362306a36Sopenharmony_ci queue->data_size = 0; /* No DMA required for beacons */ 59462306a36Sopenharmony_ci queue->desc_size = TXD_DESC_SIZE; 59562306a36Sopenharmony_ci queue->winfo_size = txwi_size; 59662306a36Sopenharmony_ci queue->priv_size = sizeof(struct queue_entry_priv_mmio); 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci case QID_ATIM: 60062306a36Sopenharmony_ci default: 60162306a36Sopenharmony_ci BUG(); 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_queue_init); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci/* 60862306a36Sopenharmony_ci * Initialization functions. 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_cibool rt2800mmio_get_entry_state(struct queue_entry *entry) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv = entry->priv_data; 61362306a36Sopenharmony_ci u32 word; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (entry->queue->qid == QID_RX) { 61662306a36Sopenharmony_ci word = rt2x00_desc_read(entry_priv->desc, 1); 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); 61962306a36Sopenharmony_ci } else { 62062306a36Sopenharmony_ci word = rt2x00_desc_read(entry_priv->desc, 1); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_get_entry_state); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_civoid rt2800mmio_clear_entry(struct queue_entry *entry) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv = entry->priv_data; 63062306a36Sopenharmony_ci struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); 63162306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; 63262306a36Sopenharmony_ci u32 word; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (entry->queue->qid == QID_RX) { 63562306a36Sopenharmony_ci word = rt2x00_desc_read(entry_priv->desc, 0); 63662306a36Sopenharmony_ci rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); 63762306a36Sopenharmony_ci rt2x00_desc_write(entry_priv->desc, 0, word); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci word = rt2x00_desc_read(entry_priv->desc, 1); 64062306a36Sopenharmony_ci rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); 64162306a36Sopenharmony_ci rt2x00_desc_write(entry_priv->desc, 1, word); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* 64462306a36Sopenharmony_ci * Set RX IDX in register to inform hardware that we have 64562306a36Sopenharmony_ci * handled this entry and it is available for reuse again. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, 64862306a36Sopenharmony_ci entry->entry_idx); 64962306a36Sopenharmony_ci } else { 65062306a36Sopenharmony_ci word = rt2x00_desc_read(entry_priv->desc, 1); 65162306a36Sopenharmony_ci rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); 65262306a36Sopenharmony_ci rt2x00_desc_write(entry_priv->desc, 1, word); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* If last entry stop txstatus timer */ 65562306a36Sopenharmony_ci if (entry->queue->length == 1) 65662306a36Sopenharmony_ci hrtimer_cancel(&rt2x00dev->txstatus_timer); 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_clear_entry); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ciint rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* 66662306a36Sopenharmony_ci * Initialize registers. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci entry_priv = rt2x00dev->tx[0].entries[0].priv_data; 66962306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0, 67062306a36Sopenharmony_ci entry_priv->desc_dma); 67162306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0, 67262306a36Sopenharmony_ci rt2x00dev->tx[0].limit); 67362306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0); 67462306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci entry_priv = rt2x00dev->tx[1].entries[0].priv_data; 67762306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1, 67862306a36Sopenharmony_ci entry_priv->desc_dma); 67962306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1, 68062306a36Sopenharmony_ci rt2x00dev->tx[1].limit); 68162306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0); 68262306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci entry_priv = rt2x00dev->tx[2].entries[0].priv_data; 68562306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2, 68662306a36Sopenharmony_ci entry_priv->desc_dma); 68762306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2, 68862306a36Sopenharmony_ci rt2x00dev->tx[2].limit); 68962306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0); 69062306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci entry_priv = rt2x00dev->tx[3].entries[0].priv_data; 69362306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3, 69462306a36Sopenharmony_ci entry_priv->desc_dma); 69562306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3, 69662306a36Sopenharmony_ci rt2x00dev->tx[3].limit); 69762306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0); 69862306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0); 70162306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0); 70262306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0); 70362306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0); 70662306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0); 70762306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0); 70862306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci entry_priv = rt2x00dev->rx->entries[0].priv_data; 71162306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR, 71262306a36Sopenharmony_ci entry_priv->desc_dma); 71362306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT, 71462306a36Sopenharmony_ci rt2x00dev->rx[0].limit); 71562306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, 71662306a36Sopenharmony_ci rt2x00dev->rx[0].limit - 1); 71762306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci rt2800_disable_wpdma(rt2x00dev); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_init_queues); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ciint rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci u32 reg; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* 73262306a36Sopenharmony_ci * Reset DMA indexes 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX); 73562306a36Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); 73662306a36Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); 73762306a36Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); 73862306a36Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); 73962306a36Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); 74062306a36Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); 74162306a36Sopenharmony_ci rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); 74262306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); 74562306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (rt2x00_is_pcie(rt2x00dev) && 74862306a36Sopenharmony_ci (rt2x00_rt(rt2x00dev, RT3090) || 74962306a36Sopenharmony_ci rt2x00_rt(rt2x00dev, RT3390) || 75062306a36Sopenharmony_ci rt2x00_rt(rt2x00dev, RT3572) || 75162306a36Sopenharmony_ci rt2x00_rt(rt2x00dev, RT3593) || 75262306a36Sopenharmony_ci rt2x00_rt(rt2x00dev, RT5390) || 75362306a36Sopenharmony_ci rt2x00_rt(rt2x00dev, RT5392) || 75462306a36Sopenharmony_ci rt2x00_rt(rt2x00dev, RT5592))) { 75562306a36Sopenharmony_ci reg = rt2x00mmio_register_read(rt2x00dev, AUX_CTRL); 75662306a36Sopenharmony_ci rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); 75762306a36Sopenharmony_ci rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); 75862306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg); 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci reg = 0; 76462306a36Sopenharmony_ci rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); 76562306a36Sopenharmony_ci rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); 76662306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return 0; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_init_registers); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/* 77562306a36Sopenharmony_ci * Device state switch handlers. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_ciint rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci /* Wait for DMA, ignore error until we initialize queues. */ 78062306a36Sopenharmony_ci rt2800_wait_wpdma_ready(rt2x00dev); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (unlikely(rt2800mmio_init_queues(rt2x00dev))) 78362306a36Sopenharmony_ci return -EIO; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return rt2800_enable_radio(rt2x00dev); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_enable_radio); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic void rt2800mmio_work_txdone(struct work_struct *work) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = 79262306a36Sopenharmony_ci container_of(work, struct rt2x00_dev, txdone_work); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 79562306a36Sopenharmony_ci return; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci while (!kfifo_is_empty(&rt2x00dev->txstatus_fifo) || 79862306a36Sopenharmony_ci rt2800_txstatus_timeout(rt2x00dev)) { 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci tasklet_disable(&rt2x00dev->txstatus_tasklet); 80162306a36Sopenharmony_ci rt2800_txdone(rt2x00dev, UINT_MAX); 80262306a36Sopenharmony_ci rt2800_txdone_nostatus(rt2x00dev); 80362306a36Sopenharmony_ci tasklet_enable(&rt2x00dev->txstatus_tasklet); 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (rt2800_txstatus_pending(rt2x00dev)) 80762306a36Sopenharmony_ci hrtimer_start(&rt2x00dev->txstatus_timer, 80862306a36Sopenharmony_ci TXSTATUS_TIMEOUT, HRTIMER_MODE_REL); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic enum hrtimer_restart rt2800mmio_tx_sta_fifo_timeout(struct hrtimer *timer) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci struct rt2x00_dev *rt2x00dev = 81462306a36Sopenharmony_ci container_of(timer, struct rt2x00_dev, txstatus_timer); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 81762306a36Sopenharmony_ci goto out; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (!rt2800_txstatus_pending(rt2x00dev)) 82062306a36Sopenharmony_ci goto out; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci rt2800mmio_fetch_txstatus(rt2x00dev); 82362306a36Sopenharmony_ci if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo)) 82462306a36Sopenharmony_ci tasklet_schedule(&rt2x00dev->txstatus_tasklet); 82562306a36Sopenharmony_ci else 82662306a36Sopenharmony_ci queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); 82762306a36Sopenharmony_ciout: 82862306a36Sopenharmony_ci return HRTIMER_NORESTART; 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ciint rt2800mmio_probe_hw(struct rt2x00_dev *rt2x00dev) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci int retval; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci retval = rt2800_probe_hw(rt2x00dev); 83662306a36Sopenharmony_ci if (retval) 83762306a36Sopenharmony_ci return retval; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci /* 84062306a36Sopenharmony_ci * Set txstatus timer function. 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_ci rt2x00dev->txstatus_timer.function = rt2800mmio_tx_sta_fifo_timeout; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* 84562306a36Sopenharmony_ci * Overwrite TX done handler 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_ci INIT_WORK(&rt2x00dev->txdone_work, rt2800mmio_work_txdone); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return 0; 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2800mmio_probe_hw); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ciMODULE_AUTHOR(DRV_PROJECT); 85462306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 85562306a36Sopenharmony_ciMODULE_DESCRIPTION("rt2800 MMIO library"); 85662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 857