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(&reg, 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(&reg, 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(&reg, 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(&reg, INT_MASK_CSR_RX_DONE, 1);
4008c2ecf20Sopenharmony_ci		rt2x00_set_field32(&reg, INT_MASK_CSR_TBTT, 1);
4018c2ecf20Sopenharmony_ci		rt2x00_set_field32(&reg, INT_MASK_CSR_PRE_TBTT, 1);
4028c2ecf20Sopenharmony_ci		rt2x00_set_field32(&reg, INT_MASK_CSR_TX_FIFO_STATUS, 1);
4038c2ecf20Sopenharmony_ci		rt2x00_set_field32(&reg, 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(&reg, 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(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
4388c2ecf20Sopenharmony_ci		rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 1);
4398c2ecf20Sopenharmony_ci		rt2x00_set_field32(&reg, 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(&reg, 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(&reg, 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(&reg, BCN_TIME_CFG_TSF_TICKING, 0);
5418c2ecf20Sopenharmony_ci		rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0);
5428c2ecf20Sopenharmony_ci		rt2x00_set_field32(&reg, 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(&reg, 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(&reg, WPDMA_RST_IDX_DTX_IDX0, 1);
7368c2ecf20Sopenharmony_ci	rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, 1);
7378c2ecf20Sopenharmony_ci	rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, 1);
7388c2ecf20Sopenharmony_ci	rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, 1);
7398c2ecf20Sopenharmony_ci	rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX4, 1);
7408c2ecf20Sopenharmony_ci	rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX5, 1);
7418c2ecf20Sopenharmony_ci	rt2x00_set_field32(&reg, 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(&reg, AUX_CTRL_FORCE_PCIE_CLK, 1);
7578c2ecf20Sopenharmony_ci		rt2x00_set_field32(&reg, 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(&reg, MAC_SYS_CTRL_RESET_CSR, 1);
7658c2ecf20Sopenharmony_ci	rt2x00_set_field32(&reg, 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