18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> 48c2ecf20Sopenharmony_ci <http://rt2x00.serialmonkey.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci Module: rt2x00mmio 108c2ecf20Sopenharmony_ci Abstract: rt2x00 generic mmio device routines. 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "rt2x00.h" 198c2ecf20Sopenharmony_ci#include "rt2x00mmio.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * Register access. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ciint rt2x00mmio_regbusy_read(struct rt2x00_dev *rt2x00dev, 258c2ecf20Sopenharmony_ci const unsigned int offset, 268c2ecf20Sopenharmony_ci const struct rt2x00_field32 field, 278c2ecf20Sopenharmony_ci u32 *reg) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci unsigned int i; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) 328c2ecf20Sopenharmony_ci return 0; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci for (i = 0; i < REGISTER_BUSY_COUNT; i++) { 358c2ecf20Sopenharmony_ci *reg = rt2x00mmio_register_read(rt2x00dev, offset); 368c2ecf20Sopenharmony_ci if (!rt2x00_get_field32(*reg, field)) 378c2ecf20Sopenharmony_ci return 1; 388c2ecf20Sopenharmony_ci udelay(REGISTER_BUSY_DELAY); 398c2ecf20Sopenharmony_ci } 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci printk_once(KERN_ERR "%s() Indirect register access failed: " 428c2ecf20Sopenharmony_ci "offset=0x%.08x, value=0x%.08x\n", __func__, offset, *reg); 438c2ecf20Sopenharmony_ci *reg = ~0; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci return 0; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00mmio_regbusy_read); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cibool rt2x00mmio_rxdone(struct rt2x00_dev *rt2x00dev) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct data_queue *queue = rt2x00dev->rx; 528c2ecf20Sopenharmony_ci struct queue_entry *entry; 538c2ecf20Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv; 548c2ecf20Sopenharmony_ci struct skb_frame_desc *skbdesc; 558c2ecf20Sopenharmony_ci int max_rx = 16; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci while (--max_rx) { 588c2ecf20Sopenharmony_ci entry = rt2x00queue_get_entry(queue, Q_INDEX); 598c2ecf20Sopenharmony_ci entry_priv = entry->priv_data; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (rt2x00dev->ops->lib->get_entry_state(entry)) 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* 658c2ecf20Sopenharmony_ci * Fill in desc fields of the skb descriptor 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci skbdesc = get_skb_frame_desc(entry->skb); 688c2ecf20Sopenharmony_ci skbdesc->desc = entry_priv->desc; 698c2ecf20Sopenharmony_ci skbdesc->desc_len = entry->queue->desc_size; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * DMA is already done, notify rt2x00lib that 738c2ecf20Sopenharmony_ci * it finished successfully. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci rt2x00lib_dmastart(entry); 768c2ecf20Sopenharmony_ci rt2x00lib_dmadone(entry); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * Send the frame to rt2x00lib for further processing. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci rt2x00lib_rxdone(entry, GFP_ATOMIC); 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return !max_rx; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00mmio_rxdone); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_civoid rt2x00mmio_flush_queue(struct data_queue *queue, bool drop) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci unsigned int i; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci for (i = 0; !rt2x00queue_empty(queue) && i < 10; i++) 938c2ecf20Sopenharmony_ci msleep(50); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00mmio_flush_queue); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* 988c2ecf20Sopenharmony_ci * Device initialization handlers. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_cistatic int rt2x00mmio_alloc_queue_dma(struct rt2x00_dev *rt2x00dev, 1018c2ecf20Sopenharmony_ci struct data_queue *queue) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv; 1048c2ecf20Sopenharmony_ci void *addr; 1058c2ecf20Sopenharmony_ci dma_addr_t dma; 1068c2ecf20Sopenharmony_ci unsigned int i; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * Allocate DMA memory for descriptor and buffer. 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci addr = dma_alloc_coherent(rt2x00dev->dev, 1128c2ecf20Sopenharmony_ci queue->limit * queue->desc_size, &dma, 1138c2ecf20Sopenharmony_ci GFP_KERNEL); 1148c2ecf20Sopenharmony_ci if (!addr) 1158c2ecf20Sopenharmony_ci return -ENOMEM; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* 1188c2ecf20Sopenharmony_ci * Initialize all queue entries to contain valid addresses. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci for (i = 0; i < queue->limit; i++) { 1218c2ecf20Sopenharmony_ci entry_priv = queue->entries[i].priv_data; 1228c2ecf20Sopenharmony_ci entry_priv->desc = addr + i * queue->desc_size; 1238c2ecf20Sopenharmony_ci entry_priv->desc_dma = dma + i * queue->desc_size; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void rt2x00mmio_free_queue_dma(struct rt2x00_dev *rt2x00dev, 1308c2ecf20Sopenharmony_ci struct data_queue *queue) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct queue_entry_priv_mmio *entry_priv = 1338c2ecf20Sopenharmony_ci queue->entries[0].priv_data; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (entry_priv->desc) 1368c2ecf20Sopenharmony_ci dma_free_coherent(rt2x00dev->dev, 1378c2ecf20Sopenharmony_ci queue->limit * queue->desc_size, 1388c2ecf20Sopenharmony_ci entry_priv->desc, entry_priv->desc_dma); 1398c2ecf20Sopenharmony_ci entry_priv->desc = NULL; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciint rt2x00mmio_initialize(struct rt2x00_dev *rt2x00dev) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct data_queue *queue; 1458c2ecf20Sopenharmony_ci int status; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * Allocate DMA 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci queue_for_each(rt2x00dev, queue) { 1518c2ecf20Sopenharmony_ci status = rt2x00mmio_alloc_queue_dma(rt2x00dev, queue); 1528c2ecf20Sopenharmony_ci if (status) 1538c2ecf20Sopenharmony_ci goto exit; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * Register interrupt handler. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci status = request_irq(rt2x00dev->irq, 1608c2ecf20Sopenharmony_ci rt2x00dev->ops->lib->irq_handler, 1618c2ecf20Sopenharmony_ci IRQF_SHARED, rt2x00dev->name, rt2x00dev); 1628c2ecf20Sopenharmony_ci if (status) { 1638c2ecf20Sopenharmony_ci rt2x00_err(rt2x00dev, "IRQ %d allocation failed (error %d)\n", 1648c2ecf20Sopenharmony_ci rt2x00dev->irq, status); 1658c2ecf20Sopenharmony_ci goto exit; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ciexit: 1718c2ecf20Sopenharmony_ci queue_for_each(rt2x00dev, queue) 1728c2ecf20Sopenharmony_ci rt2x00mmio_free_queue_dma(rt2x00dev, queue); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return status; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00mmio_initialize); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_civoid rt2x00mmio_uninitialize(struct rt2x00_dev *rt2x00dev) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct data_queue *queue; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * Free irq line. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci free_irq(rt2x00dev->irq, rt2x00dev); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci /* 1888c2ecf20Sopenharmony_ci * Free DMA 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci queue_for_each(rt2x00dev, queue) 1918c2ecf20Sopenharmony_ci rt2x00mmio_free_queue_dma(rt2x00dev, queue); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rt2x00mmio_uninitialize); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * rt2x00mmio module information. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRV_PROJECT); 1998c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 2008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("rt2x00 mmio library"); 2018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 202