18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Broadcom B43legacy wireless driver 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci DMA ringbuffer and descriptor allocation/management 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci Copyright (c) 2005, 2006 Michael Buesch <m@bues.ch> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci Some code in this file is derived from the b44.c driver 118c2ecf20Sopenharmony_ci Copyright (C) 2002 David S. Miller 128c2ecf20Sopenharmony_ci Copyright (C) Pekka Pietikainen 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci*/ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "b43legacy.h" 188c2ecf20Sopenharmony_ci#include "dma.h" 198c2ecf20Sopenharmony_ci#include "main.h" 208c2ecf20Sopenharmony_ci#include "debugfs.h" 218c2ecf20Sopenharmony_ci#include "xmit.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 248c2ecf20Sopenharmony_ci#include <linux/pci.h> 258c2ecf20Sopenharmony_ci#include <linux/delay.h> 268c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <net/dst.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 32bit DMA ops. */ 318c2ecf20Sopenharmony_cistatic 328c2ecf20Sopenharmony_cistruct b43legacy_dmadesc32 *op32_idx2desc(struct b43legacy_dmaring *ring, 338c2ecf20Sopenharmony_ci int slot, 348c2ecf20Sopenharmony_ci struct b43legacy_dmadesc_meta **meta) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct b43legacy_dmadesc32 *desc; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci *meta = &(ring->meta[slot]); 398c2ecf20Sopenharmony_ci desc = ring->descbase; 408c2ecf20Sopenharmony_ci desc = &(desc[slot]); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return desc; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void op32_fill_descriptor(struct b43legacy_dmaring *ring, 468c2ecf20Sopenharmony_ci struct b43legacy_dmadesc32 *desc, 478c2ecf20Sopenharmony_ci dma_addr_t dmaaddr, u16 bufsize, 488c2ecf20Sopenharmony_ci int start, int end, int irq) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct b43legacy_dmadesc32 *descbase = ring->descbase; 518c2ecf20Sopenharmony_ci int slot; 528c2ecf20Sopenharmony_ci u32 ctl; 538c2ecf20Sopenharmony_ci u32 addr; 548c2ecf20Sopenharmony_ci u32 addrext; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci slot = (int)(desc - descbase); 578c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci addr = (u32)(dmaaddr & ~SSB_DMA_TRANSLATION_MASK); 608c2ecf20Sopenharmony_ci addrext = (u32)(dmaaddr & SSB_DMA_TRANSLATION_MASK) 618c2ecf20Sopenharmony_ci >> SSB_DMA_TRANSLATION_SHIFT; 628c2ecf20Sopenharmony_ci addr |= ring->dev->dma.translation; 638c2ecf20Sopenharmony_ci ctl = (bufsize - ring->frameoffset) 648c2ecf20Sopenharmony_ci & B43legacy_DMA32_DCTL_BYTECNT; 658c2ecf20Sopenharmony_ci if (slot == ring->nr_slots - 1) 668c2ecf20Sopenharmony_ci ctl |= B43legacy_DMA32_DCTL_DTABLEEND; 678c2ecf20Sopenharmony_ci if (start) 688c2ecf20Sopenharmony_ci ctl |= B43legacy_DMA32_DCTL_FRAMESTART; 698c2ecf20Sopenharmony_ci if (end) 708c2ecf20Sopenharmony_ci ctl |= B43legacy_DMA32_DCTL_FRAMEEND; 718c2ecf20Sopenharmony_ci if (irq) 728c2ecf20Sopenharmony_ci ctl |= B43legacy_DMA32_DCTL_IRQ; 738c2ecf20Sopenharmony_ci ctl |= (addrext << B43legacy_DMA32_DCTL_ADDREXT_SHIFT) 748c2ecf20Sopenharmony_ci & B43legacy_DMA32_DCTL_ADDREXT_MASK; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci desc->control = cpu_to_le32(ctl); 778c2ecf20Sopenharmony_ci desc->address = cpu_to_le32(addr); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void op32_poke_tx(struct b43legacy_dmaring *ring, int slot) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXINDEX, 838c2ecf20Sopenharmony_ci (u32)(slot * sizeof(struct b43legacy_dmadesc32))); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void op32_tx_suspend(struct b43legacy_dmaring *ring) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, 898c2ecf20Sopenharmony_ci b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL) 908c2ecf20Sopenharmony_ci | B43legacy_DMA32_TXSUSPEND); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void op32_tx_resume(struct b43legacy_dmaring *ring) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, 968c2ecf20Sopenharmony_ci b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL) 978c2ecf20Sopenharmony_ci & ~B43legacy_DMA32_TXSUSPEND); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int op32_get_current_rxslot(struct b43legacy_dmaring *ring) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci u32 val; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci val = b43legacy_dma_read(ring, B43legacy_DMA32_RXSTATUS); 1058c2ecf20Sopenharmony_ci val &= B43legacy_DMA32_RXDPTR; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return (val / sizeof(struct b43legacy_dmadesc32)); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic void op32_set_current_rxslot(struct b43legacy_dmaring *ring, 1118c2ecf20Sopenharmony_ci int slot) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, 1148c2ecf20Sopenharmony_ci (u32)(slot * sizeof(struct b43legacy_dmadesc32))); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic inline int free_slots(struct b43legacy_dmaring *ring) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci return (ring->nr_slots - ring->used_slots); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic inline int next_slot(struct b43legacy_dmaring *ring, int slot) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!(slot >= -1 && slot <= ring->nr_slots - 1)); 1258c2ecf20Sopenharmony_ci if (slot == ring->nr_slots - 1) 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci return slot + 1; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic inline int prev_slot(struct b43legacy_dmaring *ring, int slot) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!(slot >= 0 && slot <= ring->nr_slots - 1)); 1338c2ecf20Sopenharmony_ci if (slot == 0) 1348c2ecf20Sopenharmony_ci return ring->nr_slots - 1; 1358c2ecf20Sopenharmony_ci return slot - 1; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#ifdef CONFIG_B43LEGACY_DEBUG 1398c2ecf20Sopenharmony_cistatic void update_max_used_slots(struct b43legacy_dmaring *ring, 1408c2ecf20Sopenharmony_ci int current_used_slots) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci if (current_used_slots <= ring->max_used_slots) 1438c2ecf20Sopenharmony_ci return; 1448c2ecf20Sopenharmony_ci ring->max_used_slots = current_used_slots; 1458c2ecf20Sopenharmony_ci if (b43legacy_debug(ring->dev, B43legacy_DBG_DMAVERBOSE)) 1468c2ecf20Sopenharmony_ci b43legacydbg(ring->dev->wl, 1478c2ecf20Sopenharmony_ci "max_used_slots increased to %d on %s ring %d\n", 1488c2ecf20Sopenharmony_ci ring->max_used_slots, 1498c2ecf20Sopenharmony_ci ring->tx ? "TX" : "RX", 1508c2ecf20Sopenharmony_ci ring->index); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci#else 1538c2ecf20Sopenharmony_cistatic inline 1548c2ecf20Sopenharmony_civoid update_max_used_slots(struct b43legacy_dmaring *ring, 1558c2ecf20Sopenharmony_ci int current_used_slots) 1568c2ecf20Sopenharmony_ci{ } 1578c2ecf20Sopenharmony_ci#endif /* DEBUG */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* Request a slot for usage. */ 1608c2ecf20Sopenharmony_cistatic inline 1618c2ecf20Sopenharmony_ciint request_slot(struct b43legacy_dmaring *ring) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int slot; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 1668c2ecf20Sopenharmony_ci B43legacy_WARN_ON(ring->stopped); 1678c2ecf20Sopenharmony_ci B43legacy_WARN_ON(free_slots(ring) == 0); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci slot = next_slot(ring, ring->current_slot); 1708c2ecf20Sopenharmony_ci ring->current_slot = slot; 1718c2ecf20Sopenharmony_ci ring->used_slots++; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci update_max_used_slots(ring, ring->used_slots); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return slot; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* Mac80211-queue to b43legacy-ring mapping */ 1798c2ecf20Sopenharmony_cistatic struct b43legacy_dmaring *priority_to_txring( 1808c2ecf20Sopenharmony_ci struct b43legacy_wldev *dev, 1818c2ecf20Sopenharmony_ci int queue_priority) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct b43legacy_dmaring *ring; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/*FIXME: For now we always run on TX-ring-1 */ 1868c2ecf20Sopenharmony_cireturn dev->dma.tx_ring1; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* 0 = highest priority */ 1898c2ecf20Sopenharmony_ci switch (queue_priority) { 1908c2ecf20Sopenharmony_ci default: 1918c2ecf20Sopenharmony_ci B43legacy_WARN_ON(1); 1928c2ecf20Sopenharmony_ci fallthrough; 1938c2ecf20Sopenharmony_ci case 0: 1948c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring3; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case 1: 1978c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring2; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case 2: 2008c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring1; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case 3: 2038c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring0; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci case 4: 2068c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring4; 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci case 5: 2098c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring5; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return ring; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* Bcm4301-ring to mac80211-queue mapping */ 2178c2ecf20Sopenharmony_cistatic inline int txring_to_priority(struct b43legacy_dmaring *ring) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci static const u8 idx_to_prio[] = 2208c2ecf20Sopenharmony_ci { 3, 2, 1, 0, 4, 5, }; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci/*FIXME: have only one queue, for now */ 2238c2ecf20Sopenharmony_cireturn 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return idx_to_prio[ring->index]; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic u16 b43legacy_dmacontroller_base(enum b43legacy_dmatype type, 2308c2ecf20Sopenharmony_ci int controller_idx) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci static const u16 map32[] = { 2338c2ecf20Sopenharmony_ci B43legacy_MMIO_DMA32_BASE0, 2348c2ecf20Sopenharmony_ci B43legacy_MMIO_DMA32_BASE1, 2358c2ecf20Sopenharmony_ci B43legacy_MMIO_DMA32_BASE2, 2368c2ecf20Sopenharmony_ci B43legacy_MMIO_DMA32_BASE3, 2378c2ecf20Sopenharmony_ci B43legacy_MMIO_DMA32_BASE4, 2388c2ecf20Sopenharmony_ci B43legacy_MMIO_DMA32_BASE5, 2398c2ecf20Sopenharmony_ci }; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!(controller_idx >= 0 && 2428c2ecf20Sopenharmony_ci controller_idx < ARRAY_SIZE(map32))); 2438c2ecf20Sopenharmony_ci return map32[controller_idx]; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic inline 2478c2ecf20Sopenharmony_cidma_addr_t map_descbuffer(struct b43legacy_dmaring *ring, 2488c2ecf20Sopenharmony_ci unsigned char *buf, 2498c2ecf20Sopenharmony_ci size_t len, 2508c2ecf20Sopenharmony_ci int tx) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci dma_addr_t dmaaddr; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (tx) 2558c2ecf20Sopenharmony_ci dmaaddr = dma_map_single(ring->dev->dev->dma_dev, 2568c2ecf20Sopenharmony_ci buf, len, 2578c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 2588c2ecf20Sopenharmony_ci else 2598c2ecf20Sopenharmony_ci dmaaddr = dma_map_single(ring->dev->dev->dma_dev, 2608c2ecf20Sopenharmony_ci buf, len, 2618c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return dmaaddr; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic inline 2678c2ecf20Sopenharmony_civoid unmap_descbuffer(struct b43legacy_dmaring *ring, 2688c2ecf20Sopenharmony_ci dma_addr_t addr, 2698c2ecf20Sopenharmony_ci size_t len, 2708c2ecf20Sopenharmony_ci int tx) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci if (tx) 2738c2ecf20Sopenharmony_ci dma_unmap_single(ring->dev->dev->dma_dev, 2748c2ecf20Sopenharmony_ci addr, len, 2758c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 2768c2ecf20Sopenharmony_ci else 2778c2ecf20Sopenharmony_ci dma_unmap_single(ring->dev->dev->dma_dev, 2788c2ecf20Sopenharmony_ci addr, len, 2798c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic inline 2838c2ecf20Sopenharmony_civoid sync_descbuffer_for_cpu(struct b43legacy_dmaring *ring, 2848c2ecf20Sopenharmony_ci dma_addr_t addr, 2858c2ecf20Sopenharmony_ci size_t len) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci B43legacy_WARN_ON(ring->tx); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(ring->dev->dev->dma_dev, 2908c2ecf20Sopenharmony_ci addr, len, DMA_FROM_DEVICE); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic inline 2948c2ecf20Sopenharmony_civoid sync_descbuffer_for_device(struct b43legacy_dmaring *ring, 2958c2ecf20Sopenharmony_ci dma_addr_t addr, 2968c2ecf20Sopenharmony_ci size_t len) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci B43legacy_WARN_ON(ring->tx); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci dma_sync_single_for_device(ring->dev->dev->dma_dev, 3018c2ecf20Sopenharmony_ci addr, len, DMA_FROM_DEVICE); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic inline 3058c2ecf20Sopenharmony_civoid free_descriptor_buffer(struct b43legacy_dmaring *ring, 3068c2ecf20Sopenharmony_ci struct b43legacy_dmadesc_meta *meta, 3078c2ecf20Sopenharmony_ci int irq_context) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci if (meta->skb) { 3108c2ecf20Sopenharmony_ci if (irq_context) 3118c2ecf20Sopenharmony_ci dev_kfree_skb_irq(meta->skb); 3128c2ecf20Sopenharmony_ci else 3138c2ecf20Sopenharmony_ci dev_kfree_skb(meta->skb); 3148c2ecf20Sopenharmony_ci meta->skb = NULL; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int alloc_ringmemory(struct b43legacy_dmaring *ring) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci /* GFP flags must match the flags in free_ringmemory()! */ 3218c2ecf20Sopenharmony_ci ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev, 3228c2ecf20Sopenharmony_ci B43legacy_DMA_RINGMEMSIZE, 3238c2ecf20Sopenharmony_ci &(ring->dmabase), GFP_KERNEL); 3248c2ecf20Sopenharmony_ci if (!ring->descbase) 3258c2ecf20Sopenharmony_ci return -ENOMEM; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic void free_ringmemory(struct b43legacy_dmaring *ring) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci dma_free_coherent(ring->dev->dev->dma_dev, B43legacy_DMA_RINGMEMSIZE, 3338c2ecf20Sopenharmony_ci ring->descbase, ring->dmabase); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* Reset the RX DMA channel */ 3378c2ecf20Sopenharmony_cistatic int b43legacy_dmacontroller_rx_reset(struct b43legacy_wldev *dev, 3388c2ecf20Sopenharmony_ci u16 mmio_base, 3398c2ecf20Sopenharmony_ci enum b43legacy_dmatype type) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci int i; 3428c2ecf20Sopenharmony_ci u32 value; 3438c2ecf20Sopenharmony_ci u16 offset; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci might_sleep(); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci offset = B43legacy_DMA32_RXCTL; 3488c2ecf20Sopenharmony_ci b43legacy_write32(dev, mmio_base + offset, 0); 3498c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 3508c2ecf20Sopenharmony_ci offset = B43legacy_DMA32_RXSTATUS; 3518c2ecf20Sopenharmony_ci value = b43legacy_read32(dev, mmio_base + offset); 3528c2ecf20Sopenharmony_ci value &= B43legacy_DMA32_RXSTATE; 3538c2ecf20Sopenharmony_ci if (value == B43legacy_DMA32_RXSTAT_DISABLED) { 3548c2ecf20Sopenharmony_ci i = -1; 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci msleep(1); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci if (i != -1) { 3608c2ecf20Sopenharmony_ci b43legacyerr(dev->wl, "DMA RX reset timed out\n"); 3618c2ecf20Sopenharmony_ci return -ENODEV; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/* Reset the RX DMA channel */ 3688c2ecf20Sopenharmony_cistatic int b43legacy_dmacontroller_tx_reset(struct b43legacy_wldev *dev, 3698c2ecf20Sopenharmony_ci u16 mmio_base, 3708c2ecf20Sopenharmony_ci enum b43legacy_dmatype type) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci int i; 3738c2ecf20Sopenharmony_ci u32 value; 3748c2ecf20Sopenharmony_ci u16 offset; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci might_sleep(); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 3798c2ecf20Sopenharmony_ci offset = B43legacy_DMA32_TXSTATUS; 3808c2ecf20Sopenharmony_ci value = b43legacy_read32(dev, mmio_base + offset); 3818c2ecf20Sopenharmony_ci value &= B43legacy_DMA32_TXSTATE; 3828c2ecf20Sopenharmony_ci if (value == B43legacy_DMA32_TXSTAT_DISABLED || 3838c2ecf20Sopenharmony_ci value == B43legacy_DMA32_TXSTAT_IDLEWAIT || 3848c2ecf20Sopenharmony_ci value == B43legacy_DMA32_TXSTAT_STOPPED) 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci msleep(1); 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci offset = B43legacy_DMA32_TXCTL; 3898c2ecf20Sopenharmony_ci b43legacy_write32(dev, mmio_base + offset, 0); 3908c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 3918c2ecf20Sopenharmony_ci offset = B43legacy_DMA32_TXSTATUS; 3928c2ecf20Sopenharmony_ci value = b43legacy_read32(dev, mmio_base + offset); 3938c2ecf20Sopenharmony_ci value &= B43legacy_DMA32_TXSTATE; 3948c2ecf20Sopenharmony_ci if (value == B43legacy_DMA32_TXSTAT_DISABLED) { 3958c2ecf20Sopenharmony_ci i = -1; 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci msleep(1); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci if (i != -1) { 4018c2ecf20Sopenharmony_ci b43legacyerr(dev->wl, "DMA TX reset timed out\n"); 4028c2ecf20Sopenharmony_ci return -ENODEV; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci /* ensure the reset is completed. */ 4058c2ecf20Sopenharmony_ci msleep(1); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci/* Check if a DMA mapping address is invalid. */ 4118c2ecf20Sopenharmony_cistatic bool b43legacy_dma_mapping_error(struct b43legacy_dmaring *ring, 4128c2ecf20Sopenharmony_ci dma_addr_t addr, 4138c2ecf20Sopenharmony_ci size_t buffersize, 4148c2ecf20Sopenharmony_ci bool dma_to_device) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr))) 4178c2ecf20Sopenharmony_ci return true; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci switch (ring->type) { 4208c2ecf20Sopenharmony_ci case B43legacy_DMA_30BIT: 4218c2ecf20Sopenharmony_ci if ((u64)addr + buffersize > (1ULL << 30)) 4228c2ecf20Sopenharmony_ci goto address_error; 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci case B43legacy_DMA_32BIT: 4258c2ecf20Sopenharmony_ci if ((u64)addr + buffersize > (1ULL << 32)) 4268c2ecf20Sopenharmony_ci goto address_error; 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* The address is OK. */ 4318c2ecf20Sopenharmony_ci return false; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ciaddress_error: 4348c2ecf20Sopenharmony_ci /* We can't support this address. Unmap it again. */ 4358c2ecf20Sopenharmony_ci unmap_descbuffer(ring, addr, buffersize, dma_to_device); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return true; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic int setup_rx_descbuffer(struct b43legacy_dmaring *ring, 4418c2ecf20Sopenharmony_ci struct b43legacy_dmadesc32 *desc, 4428c2ecf20Sopenharmony_ci struct b43legacy_dmadesc_meta *meta, 4438c2ecf20Sopenharmony_ci gfp_t gfp_flags) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct b43legacy_rxhdr_fw3 *rxhdr; 4468c2ecf20Sopenharmony_ci struct b43legacy_hwtxstatus *txstat; 4478c2ecf20Sopenharmony_ci dma_addr_t dmaaddr; 4488c2ecf20Sopenharmony_ci struct sk_buff *skb; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci B43legacy_WARN_ON(ring->tx); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); 4538c2ecf20Sopenharmony_ci if (unlikely(!skb)) 4548c2ecf20Sopenharmony_ci return -ENOMEM; 4558c2ecf20Sopenharmony_ci dmaaddr = map_descbuffer(ring, skb->data, 4568c2ecf20Sopenharmony_ci ring->rx_buffersize, 0); 4578c2ecf20Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { 4588c2ecf20Sopenharmony_ci /* ugh. try to realloc in zone_dma */ 4598c2ecf20Sopenharmony_ci gfp_flags |= GFP_DMA; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); 4648c2ecf20Sopenharmony_ci if (unlikely(!skb)) 4658c2ecf20Sopenharmony_ci return -ENOMEM; 4668c2ecf20Sopenharmony_ci dmaaddr = map_descbuffer(ring, skb->data, 4678c2ecf20Sopenharmony_ci ring->rx_buffersize, 0); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { 4718c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 4728c2ecf20Sopenharmony_ci return -EIO; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci meta->skb = skb; 4768c2ecf20Sopenharmony_ci meta->dmaaddr = dmaaddr; 4778c2ecf20Sopenharmony_ci op32_fill_descriptor(ring, desc, dmaaddr, ring->rx_buffersize, 0, 0, 0); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci rxhdr = (struct b43legacy_rxhdr_fw3 *)(skb->data); 4808c2ecf20Sopenharmony_ci rxhdr->frame_len = 0; 4818c2ecf20Sopenharmony_ci txstat = (struct b43legacy_hwtxstatus *)(skb->data); 4828c2ecf20Sopenharmony_ci txstat->cookie = 0; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci/* Allocate the initial descbuffers. 4888c2ecf20Sopenharmony_ci * This is used for an RX ring only. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_cistatic int alloc_initial_descbuffers(struct b43legacy_dmaring *ring) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci int i; 4938c2ecf20Sopenharmony_ci int err = -ENOMEM; 4948c2ecf20Sopenharmony_ci struct b43legacy_dmadesc32 *desc; 4958c2ecf20Sopenharmony_ci struct b43legacy_dmadesc_meta *meta; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci for (i = 0; i < ring->nr_slots; i++) { 4988c2ecf20Sopenharmony_ci desc = op32_idx2desc(ring, i, &meta); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); 5018c2ecf20Sopenharmony_ci if (err) { 5028c2ecf20Sopenharmony_ci b43legacyerr(ring->dev->wl, 5038c2ecf20Sopenharmony_ci "Failed to allocate initial descbuffers\n"); 5048c2ecf20Sopenharmony_ci goto err_unwind; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci mb(); /* all descbuffer setup before next line */ 5088c2ecf20Sopenharmony_ci ring->used_slots = ring->nr_slots; 5098c2ecf20Sopenharmony_ci err = 0; 5108c2ecf20Sopenharmony_ciout: 5118c2ecf20Sopenharmony_ci return err; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cierr_unwind: 5148c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) { 5158c2ecf20Sopenharmony_ci desc = op32_idx2desc(ring, i, &meta); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); 5188c2ecf20Sopenharmony_ci dev_kfree_skb(meta->skb); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci goto out; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci/* Do initial setup of the DMA controller. 5248c2ecf20Sopenharmony_ci * Reset the controller, write the ring busaddress 5258c2ecf20Sopenharmony_ci * and switch the "enable" bit on. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_cistatic int dmacontroller_setup(struct b43legacy_dmaring *ring) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci int err = 0; 5308c2ecf20Sopenharmony_ci u32 value; 5318c2ecf20Sopenharmony_ci u32 addrext; 5328c2ecf20Sopenharmony_ci u32 trans = ring->dev->dma.translation; 5338c2ecf20Sopenharmony_ci u32 ringbase = (u32)(ring->dmabase); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (ring->tx) { 5368c2ecf20Sopenharmony_ci addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) 5378c2ecf20Sopenharmony_ci >> SSB_DMA_TRANSLATION_SHIFT; 5388c2ecf20Sopenharmony_ci value = B43legacy_DMA32_TXENABLE; 5398c2ecf20Sopenharmony_ci value |= (addrext << B43legacy_DMA32_TXADDREXT_SHIFT) 5408c2ecf20Sopenharmony_ci & B43legacy_DMA32_TXADDREXT_MASK; 5418c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, value); 5428c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, 5438c2ecf20Sopenharmony_ci (ringbase & ~SSB_DMA_TRANSLATION_MASK) 5448c2ecf20Sopenharmony_ci | trans); 5458c2ecf20Sopenharmony_ci } else { 5468c2ecf20Sopenharmony_ci err = alloc_initial_descbuffers(ring); 5478c2ecf20Sopenharmony_ci if (err) 5488c2ecf20Sopenharmony_ci goto out; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) 5518c2ecf20Sopenharmony_ci >> SSB_DMA_TRANSLATION_SHIFT; 5528c2ecf20Sopenharmony_ci value = (ring->frameoffset << 5538c2ecf20Sopenharmony_ci B43legacy_DMA32_RXFROFF_SHIFT); 5548c2ecf20Sopenharmony_ci value |= B43legacy_DMA32_RXENABLE; 5558c2ecf20Sopenharmony_ci value |= (addrext << B43legacy_DMA32_RXADDREXT_SHIFT) 5568c2ecf20Sopenharmony_ci & B43legacy_DMA32_RXADDREXT_MASK; 5578c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_RXCTL, value); 5588c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, 5598c2ecf20Sopenharmony_ci (ringbase & ~SSB_DMA_TRANSLATION_MASK) 5608c2ecf20Sopenharmony_ci | trans); 5618c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, 200); 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ciout: 5658c2ecf20Sopenharmony_ci return err; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci/* Shutdown the DMA controller. */ 5698c2ecf20Sopenharmony_cistatic void dmacontroller_cleanup(struct b43legacy_dmaring *ring) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci if (ring->tx) { 5728c2ecf20Sopenharmony_ci b43legacy_dmacontroller_tx_reset(ring->dev, ring->mmio_base, 5738c2ecf20Sopenharmony_ci ring->type); 5748c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, 0); 5758c2ecf20Sopenharmony_ci } else { 5768c2ecf20Sopenharmony_ci b43legacy_dmacontroller_rx_reset(ring->dev, ring->mmio_base, 5778c2ecf20Sopenharmony_ci ring->type); 5788c2ecf20Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, 0); 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic void free_all_descbuffers(struct b43legacy_dmaring *ring) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct b43legacy_dmadesc_meta *meta; 5858c2ecf20Sopenharmony_ci int i; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (!ring->used_slots) 5888c2ecf20Sopenharmony_ci return; 5898c2ecf20Sopenharmony_ci for (i = 0; i < ring->nr_slots; i++) { 5908c2ecf20Sopenharmony_ci op32_idx2desc(ring, i, &meta); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci if (!meta->skb) { 5938c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 5948c2ecf20Sopenharmony_ci continue; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci if (ring->tx) 5978c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 5988c2ecf20Sopenharmony_ci meta->skb->len, 1); 5998c2ecf20Sopenharmony_ci else 6008c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 6018c2ecf20Sopenharmony_ci ring->rx_buffersize, 0); 6028c2ecf20Sopenharmony_ci free_descriptor_buffer(ring, meta, 0); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic enum b43legacy_dmatype b43legacy_engine_type(struct b43legacy_wldev *dev) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci u32 tmp; 6098c2ecf20Sopenharmony_ci u16 mmio_base; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci mmio_base = b43legacy_dmacontroller_base(0, 0); 6128c2ecf20Sopenharmony_ci b43legacy_write32(dev, 6138c2ecf20Sopenharmony_ci mmio_base + B43legacy_DMA32_TXCTL, 6148c2ecf20Sopenharmony_ci B43legacy_DMA32_TXADDREXT_MASK); 6158c2ecf20Sopenharmony_ci tmp = b43legacy_read32(dev, mmio_base + 6168c2ecf20Sopenharmony_ci B43legacy_DMA32_TXCTL); 6178c2ecf20Sopenharmony_ci if (tmp & B43legacy_DMA32_TXADDREXT_MASK) 6188c2ecf20Sopenharmony_ci return B43legacy_DMA_32BIT; 6198c2ecf20Sopenharmony_ci return B43legacy_DMA_30BIT; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci/* Main initialization function. */ 6238c2ecf20Sopenharmony_cistatic 6248c2ecf20Sopenharmony_cistruct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, 6258c2ecf20Sopenharmony_ci int controller_index, 6268c2ecf20Sopenharmony_ci int for_tx, 6278c2ecf20Sopenharmony_ci enum b43legacy_dmatype type) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct b43legacy_dmaring *ring; 6308c2ecf20Sopenharmony_ci int err; 6318c2ecf20Sopenharmony_ci int nr_slots; 6328c2ecf20Sopenharmony_ci dma_addr_t dma_test; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci ring = kzalloc(sizeof(*ring), GFP_KERNEL); 6358c2ecf20Sopenharmony_ci if (!ring) 6368c2ecf20Sopenharmony_ci goto out; 6378c2ecf20Sopenharmony_ci ring->type = type; 6388c2ecf20Sopenharmony_ci ring->dev = dev; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci nr_slots = B43legacy_RXRING_SLOTS; 6418c2ecf20Sopenharmony_ci if (for_tx) 6428c2ecf20Sopenharmony_ci nr_slots = B43legacy_TXRING_SLOTS; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci ring->meta = kcalloc(nr_slots, sizeof(struct b43legacy_dmadesc_meta), 6458c2ecf20Sopenharmony_ci GFP_KERNEL); 6468c2ecf20Sopenharmony_ci if (!ring->meta) 6478c2ecf20Sopenharmony_ci goto err_kfree_ring; 6488c2ecf20Sopenharmony_ci if (for_tx) { 6498c2ecf20Sopenharmony_ci ring->txhdr_cache = kcalloc(nr_slots, 6508c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 6518c2ecf20Sopenharmony_ci GFP_KERNEL); 6528c2ecf20Sopenharmony_ci if (!ring->txhdr_cache) 6538c2ecf20Sopenharmony_ci goto err_kfree_meta; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* test for ability to dma to txhdr_cache */ 6568c2ecf20Sopenharmony_ci dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, 6578c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 6588c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, dma_test, 6618c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1)) { 6628c2ecf20Sopenharmony_ci /* ugh realloc */ 6638c2ecf20Sopenharmony_ci kfree(ring->txhdr_cache); 6648c2ecf20Sopenharmony_ci ring->txhdr_cache = kcalloc(nr_slots, 6658c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 6668c2ecf20Sopenharmony_ci GFP_KERNEL | GFP_DMA); 6678c2ecf20Sopenharmony_ci if (!ring->txhdr_cache) 6688c2ecf20Sopenharmony_ci goto err_kfree_meta; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci dma_test = dma_map_single(dev->dev->dma_dev, 6718c2ecf20Sopenharmony_ci ring->txhdr_cache, 6728c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 6738c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, dma_test, 6768c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1)) 6778c2ecf20Sopenharmony_ci goto err_kfree_txhdr_cache; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci dma_unmap_single(dev->dev->dma_dev, dma_test, 6818c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 6828c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci ring->nr_slots = nr_slots; 6868c2ecf20Sopenharmony_ci ring->mmio_base = b43legacy_dmacontroller_base(type, controller_index); 6878c2ecf20Sopenharmony_ci ring->index = controller_index; 6888c2ecf20Sopenharmony_ci if (for_tx) { 6898c2ecf20Sopenharmony_ci ring->tx = true; 6908c2ecf20Sopenharmony_ci ring->current_slot = -1; 6918c2ecf20Sopenharmony_ci } else { 6928c2ecf20Sopenharmony_ci if (ring->index == 0) { 6938c2ecf20Sopenharmony_ci ring->rx_buffersize = B43legacy_DMA0_RX_BUFFERSIZE; 6948c2ecf20Sopenharmony_ci ring->frameoffset = B43legacy_DMA0_RX_FRAMEOFFSET; 6958c2ecf20Sopenharmony_ci } else if (ring->index == 3) { 6968c2ecf20Sopenharmony_ci ring->rx_buffersize = B43legacy_DMA3_RX_BUFFERSIZE; 6978c2ecf20Sopenharmony_ci ring->frameoffset = B43legacy_DMA3_RX_FRAMEOFFSET; 6988c2ecf20Sopenharmony_ci } else 6998c2ecf20Sopenharmony_ci B43legacy_WARN_ON(1); 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci#ifdef CONFIG_B43LEGACY_DEBUG 7028c2ecf20Sopenharmony_ci ring->last_injected_overflow = jiffies; 7038c2ecf20Sopenharmony_ci#endif 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci err = alloc_ringmemory(ring); 7068c2ecf20Sopenharmony_ci if (err) 7078c2ecf20Sopenharmony_ci goto err_kfree_txhdr_cache; 7088c2ecf20Sopenharmony_ci err = dmacontroller_setup(ring); 7098c2ecf20Sopenharmony_ci if (err) 7108c2ecf20Sopenharmony_ci goto err_free_ringmemory; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ciout: 7138c2ecf20Sopenharmony_ci return ring; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cierr_free_ringmemory: 7168c2ecf20Sopenharmony_ci free_ringmemory(ring); 7178c2ecf20Sopenharmony_cierr_kfree_txhdr_cache: 7188c2ecf20Sopenharmony_ci kfree(ring->txhdr_cache); 7198c2ecf20Sopenharmony_cierr_kfree_meta: 7208c2ecf20Sopenharmony_ci kfree(ring->meta); 7218c2ecf20Sopenharmony_cierr_kfree_ring: 7228c2ecf20Sopenharmony_ci kfree(ring); 7238c2ecf20Sopenharmony_ci ring = NULL; 7248c2ecf20Sopenharmony_ci goto out; 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci/* Main cleanup function. */ 7288c2ecf20Sopenharmony_cistatic void b43legacy_destroy_dmaring(struct b43legacy_dmaring *ring) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci if (!ring) 7318c2ecf20Sopenharmony_ci return; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci b43legacydbg(ring->dev->wl, "DMA-%u 0x%04X (%s) max used slots:" 7348c2ecf20Sopenharmony_ci " %d/%d\n", (unsigned int)(ring->type), ring->mmio_base, 7358c2ecf20Sopenharmony_ci (ring->tx) ? "TX" : "RX", ring->max_used_slots, 7368c2ecf20Sopenharmony_ci ring->nr_slots); 7378c2ecf20Sopenharmony_ci /* Device IRQs are disabled prior entering this function, 7388c2ecf20Sopenharmony_ci * so no need to take care of concurrency with rx handler stuff. 7398c2ecf20Sopenharmony_ci */ 7408c2ecf20Sopenharmony_ci dmacontroller_cleanup(ring); 7418c2ecf20Sopenharmony_ci free_all_descbuffers(ring); 7428c2ecf20Sopenharmony_ci free_ringmemory(ring); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci kfree(ring->txhdr_cache); 7458c2ecf20Sopenharmony_ci kfree(ring->meta); 7468c2ecf20Sopenharmony_ci kfree(ring); 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_civoid b43legacy_dma_free(struct b43legacy_wldev *dev) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct b43legacy_dma *dma; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (b43legacy_using_pio(dev)) 7548c2ecf20Sopenharmony_ci return; 7558c2ecf20Sopenharmony_ci dma = &dev->dma; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->rx_ring3); 7588c2ecf20Sopenharmony_ci dma->rx_ring3 = NULL; 7598c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->rx_ring0); 7608c2ecf20Sopenharmony_ci dma->rx_ring0 = NULL; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring5); 7638c2ecf20Sopenharmony_ci dma->tx_ring5 = NULL; 7648c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring4); 7658c2ecf20Sopenharmony_ci dma->tx_ring4 = NULL; 7668c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring3); 7678c2ecf20Sopenharmony_ci dma->tx_ring3 = NULL; 7688c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring2); 7698c2ecf20Sopenharmony_ci dma->tx_ring2 = NULL; 7708c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring1); 7718c2ecf20Sopenharmony_ci dma->tx_ring1 = NULL; 7728c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring0); 7738c2ecf20Sopenharmony_ci dma->tx_ring0 = NULL; 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ciint b43legacy_dma_init(struct b43legacy_wldev *dev) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct b43legacy_dma *dma = &dev->dma; 7798c2ecf20Sopenharmony_ci struct b43legacy_dmaring *ring; 7808c2ecf20Sopenharmony_ci enum b43legacy_dmatype type = b43legacy_engine_type(dev); 7818c2ecf20Sopenharmony_ci int err; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci err = dma_set_mask_and_coherent(dev->dev->dma_dev, DMA_BIT_MASK(type)); 7848c2ecf20Sopenharmony_ci if (err) { 7858c2ecf20Sopenharmony_ci#ifdef CONFIG_B43LEGACY_PIO 7868c2ecf20Sopenharmony_ci b43legacywarn(dev->wl, "DMA for this device not supported. " 7878c2ecf20Sopenharmony_ci "Falling back to PIO\n"); 7888c2ecf20Sopenharmony_ci dev->__using_pio = true; 7898c2ecf20Sopenharmony_ci return -EAGAIN; 7908c2ecf20Sopenharmony_ci#else 7918c2ecf20Sopenharmony_ci b43legacyerr(dev->wl, "DMA for this device not supported and " 7928c2ecf20Sopenharmony_ci "no PIO support compiled in\n"); 7938c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 7948c2ecf20Sopenharmony_ci#endif 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci dma->translation = ssb_dma_translation(dev->dev); 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci err = -ENOMEM; 7998c2ecf20Sopenharmony_ci /* setup TX DMA channels. */ 8008c2ecf20Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 0, 1, type); 8018c2ecf20Sopenharmony_ci if (!ring) 8028c2ecf20Sopenharmony_ci goto out; 8038c2ecf20Sopenharmony_ci dma->tx_ring0 = ring; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 1, 1, type); 8068c2ecf20Sopenharmony_ci if (!ring) 8078c2ecf20Sopenharmony_ci goto err_destroy_tx0; 8088c2ecf20Sopenharmony_ci dma->tx_ring1 = ring; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 2, 1, type); 8118c2ecf20Sopenharmony_ci if (!ring) 8128c2ecf20Sopenharmony_ci goto err_destroy_tx1; 8138c2ecf20Sopenharmony_ci dma->tx_ring2 = ring; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 3, 1, type); 8168c2ecf20Sopenharmony_ci if (!ring) 8178c2ecf20Sopenharmony_ci goto err_destroy_tx2; 8188c2ecf20Sopenharmony_ci dma->tx_ring3 = ring; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 4, 1, type); 8218c2ecf20Sopenharmony_ci if (!ring) 8228c2ecf20Sopenharmony_ci goto err_destroy_tx3; 8238c2ecf20Sopenharmony_ci dma->tx_ring4 = ring; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 5, 1, type); 8268c2ecf20Sopenharmony_ci if (!ring) 8278c2ecf20Sopenharmony_ci goto err_destroy_tx4; 8288c2ecf20Sopenharmony_ci dma->tx_ring5 = ring; 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci /* setup RX DMA channels. */ 8318c2ecf20Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 0, 0, type); 8328c2ecf20Sopenharmony_ci if (!ring) 8338c2ecf20Sopenharmony_ci goto err_destroy_tx5; 8348c2ecf20Sopenharmony_ci dma->rx_ring0 = ring; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (dev->dev->id.revision < 5) { 8378c2ecf20Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 3, 0, type); 8388c2ecf20Sopenharmony_ci if (!ring) 8398c2ecf20Sopenharmony_ci goto err_destroy_rx0; 8408c2ecf20Sopenharmony_ci dma->rx_ring3 = ring; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci b43legacydbg(dev->wl, "%u-bit DMA initialized\n", (unsigned int)type); 8448c2ecf20Sopenharmony_ci err = 0; 8458c2ecf20Sopenharmony_ciout: 8468c2ecf20Sopenharmony_ci return err; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cierr_destroy_rx0: 8498c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->rx_ring0); 8508c2ecf20Sopenharmony_ci dma->rx_ring0 = NULL; 8518c2ecf20Sopenharmony_cierr_destroy_tx5: 8528c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring5); 8538c2ecf20Sopenharmony_ci dma->tx_ring5 = NULL; 8548c2ecf20Sopenharmony_cierr_destroy_tx4: 8558c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring4); 8568c2ecf20Sopenharmony_ci dma->tx_ring4 = NULL; 8578c2ecf20Sopenharmony_cierr_destroy_tx3: 8588c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring3); 8598c2ecf20Sopenharmony_ci dma->tx_ring3 = NULL; 8608c2ecf20Sopenharmony_cierr_destroy_tx2: 8618c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring2); 8628c2ecf20Sopenharmony_ci dma->tx_ring2 = NULL; 8638c2ecf20Sopenharmony_cierr_destroy_tx1: 8648c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring1); 8658c2ecf20Sopenharmony_ci dma->tx_ring1 = NULL; 8668c2ecf20Sopenharmony_cierr_destroy_tx0: 8678c2ecf20Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring0); 8688c2ecf20Sopenharmony_ci dma->tx_ring0 = NULL; 8698c2ecf20Sopenharmony_ci goto out; 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci/* Generate a cookie for the TX header. */ 8738c2ecf20Sopenharmony_cistatic u16 generate_cookie(struct b43legacy_dmaring *ring, 8748c2ecf20Sopenharmony_ci int slot) 8758c2ecf20Sopenharmony_ci{ 8768c2ecf20Sopenharmony_ci u16 cookie = 0x1000; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* Use the upper 4 bits of the cookie as 8798c2ecf20Sopenharmony_ci * DMA controller ID and store the slot number 8808c2ecf20Sopenharmony_ci * in the lower 12 bits. 8818c2ecf20Sopenharmony_ci * Note that the cookie must never be 0, as this 8828c2ecf20Sopenharmony_ci * is a special value used in RX path. 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci switch (ring->index) { 8858c2ecf20Sopenharmony_ci case 0: 8868c2ecf20Sopenharmony_ci cookie = 0xA000; 8878c2ecf20Sopenharmony_ci break; 8888c2ecf20Sopenharmony_ci case 1: 8898c2ecf20Sopenharmony_ci cookie = 0xB000; 8908c2ecf20Sopenharmony_ci break; 8918c2ecf20Sopenharmony_ci case 2: 8928c2ecf20Sopenharmony_ci cookie = 0xC000; 8938c2ecf20Sopenharmony_ci break; 8948c2ecf20Sopenharmony_ci case 3: 8958c2ecf20Sopenharmony_ci cookie = 0xD000; 8968c2ecf20Sopenharmony_ci break; 8978c2ecf20Sopenharmony_ci case 4: 8988c2ecf20Sopenharmony_ci cookie = 0xE000; 8998c2ecf20Sopenharmony_ci break; 9008c2ecf20Sopenharmony_ci case 5: 9018c2ecf20Sopenharmony_ci cookie = 0xF000; 9028c2ecf20Sopenharmony_ci break; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!(((u16)slot & 0xF000) == 0x0000)); 9058c2ecf20Sopenharmony_ci cookie |= (u16)slot; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci return cookie; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci/* Inspect a cookie and find out to which controller/slot it belongs. */ 9118c2ecf20Sopenharmony_cistatic 9128c2ecf20Sopenharmony_cistruct b43legacy_dmaring *parse_cookie(struct b43legacy_wldev *dev, 9138c2ecf20Sopenharmony_ci u16 cookie, int *slot) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci struct b43legacy_dma *dma = &dev->dma; 9168c2ecf20Sopenharmony_ci struct b43legacy_dmaring *ring = NULL; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci switch (cookie & 0xF000) { 9198c2ecf20Sopenharmony_ci case 0xA000: 9208c2ecf20Sopenharmony_ci ring = dma->tx_ring0; 9218c2ecf20Sopenharmony_ci break; 9228c2ecf20Sopenharmony_ci case 0xB000: 9238c2ecf20Sopenharmony_ci ring = dma->tx_ring1; 9248c2ecf20Sopenharmony_ci break; 9258c2ecf20Sopenharmony_ci case 0xC000: 9268c2ecf20Sopenharmony_ci ring = dma->tx_ring2; 9278c2ecf20Sopenharmony_ci break; 9288c2ecf20Sopenharmony_ci case 0xD000: 9298c2ecf20Sopenharmony_ci ring = dma->tx_ring3; 9308c2ecf20Sopenharmony_ci break; 9318c2ecf20Sopenharmony_ci case 0xE000: 9328c2ecf20Sopenharmony_ci ring = dma->tx_ring4; 9338c2ecf20Sopenharmony_ci break; 9348c2ecf20Sopenharmony_ci case 0xF000: 9358c2ecf20Sopenharmony_ci ring = dma->tx_ring5; 9368c2ecf20Sopenharmony_ci break; 9378c2ecf20Sopenharmony_ci default: 9388c2ecf20Sopenharmony_ci B43legacy_WARN_ON(1); 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci *slot = (cookie & 0x0FFF); 9418c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots)); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci return ring; 9448c2ecf20Sopenharmony_ci} 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_cistatic int dma_tx_fragment(struct b43legacy_dmaring *ring, 9478c2ecf20Sopenharmony_ci struct sk_buff **in_skb) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci struct sk_buff *skb = *in_skb; 9508c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 9518c2ecf20Sopenharmony_ci u8 *header; 9528c2ecf20Sopenharmony_ci int slot, old_top_slot, old_used_slots; 9538c2ecf20Sopenharmony_ci int err; 9548c2ecf20Sopenharmony_ci struct b43legacy_dmadesc32 *desc; 9558c2ecf20Sopenharmony_ci struct b43legacy_dmadesc_meta *meta; 9568c2ecf20Sopenharmony_ci struct b43legacy_dmadesc_meta *meta_hdr; 9578c2ecf20Sopenharmony_ci struct sk_buff *bounce_skb; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci#define SLOTS_PER_PACKET 2 9608c2ecf20Sopenharmony_ci B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci old_top_slot = ring->current_slot; 9638c2ecf20Sopenharmony_ci old_used_slots = ring->used_slots; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* Get a slot for the header. */ 9668c2ecf20Sopenharmony_ci slot = request_slot(ring); 9678c2ecf20Sopenharmony_ci desc = op32_idx2desc(ring, slot, &meta_hdr); 9688c2ecf20Sopenharmony_ci memset(meta_hdr, 0, sizeof(*meta_hdr)); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci header = &(ring->txhdr_cache[slot * sizeof( 9718c2ecf20Sopenharmony_ci struct b43legacy_txhdr_fw3)]); 9728c2ecf20Sopenharmony_ci err = b43legacy_generate_txhdr(ring->dev, header, 9738c2ecf20Sopenharmony_ci skb->data, skb->len, info, 9748c2ecf20Sopenharmony_ci generate_cookie(ring, slot)); 9758c2ecf20Sopenharmony_ci if (unlikely(err)) { 9768c2ecf20Sopenharmony_ci ring->current_slot = old_top_slot; 9778c2ecf20Sopenharmony_ci ring->used_slots = old_used_slots; 9788c2ecf20Sopenharmony_ci return err; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, 9828c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1); 9838c2ecf20Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, meta_hdr->dmaaddr, 9848c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1)) { 9858c2ecf20Sopenharmony_ci ring->current_slot = old_top_slot; 9868c2ecf20Sopenharmony_ci ring->used_slots = old_used_slots; 9878c2ecf20Sopenharmony_ci return -EIO; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci op32_fill_descriptor(ring, desc, meta_hdr->dmaaddr, 9908c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1, 0, 0); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* Get a slot for the payload. */ 9938c2ecf20Sopenharmony_ci slot = request_slot(ring); 9948c2ecf20Sopenharmony_ci desc = op32_idx2desc(ring, slot, &meta); 9958c2ecf20Sopenharmony_ci memset(meta, 0, sizeof(*meta)); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci meta->skb = skb; 9988c2ecf20Sopenharmony_ci meta->is_last_fragment = true; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); 10018c2ecf20Sopenharmony_ci /* create a bounce buffer in zone_dma on mapping failure. */ 10028c2ecf20Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { 10038c2ecf20Sopenharmony_ci bounce_skb = alloc_skb(skb->len, GFP_KERNEL | GFP_DMA); 10048c2ecf20Sopenharmony_ci if (!bounce_skb) { 10058c2ecf20Sopenharmony_ci ring->current_slot = old_top_slot; 10068c2ecf20Sopenharmony_ci ring->used_slots = old_used_slots; 10078c2ecf20Sopenharmony_ci err = -ENOMEM; 10088c2ecf20Sopenharmony_ci goto out_unmap_hdr; 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci skb_put_data(bounce_skb, skb->data, skb->len); 10128c2ecf20Sopenharmony_ci memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb)); 10138c2ecf20Sopenharmony_ci bounce_skb->dev = skb->dev; 10148c2ecf20Sopenharmony_ci skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb)); 10158c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(bounce_skb); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 10188c2ecf20Sopenharmony_ci skb = bounce_skb; 10198c2ecf20Sopenharmony_ci *in_skb = bounce_skb; 10208c2ecf20Sopenharmony_ci meta->skb = skb; 10218c2ecf20Sopenharmony_ci meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); 10228c2ecf20Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { 10238c2ecf20Sopenharmony_ci ring->current_slot = old_top_slot; 10248c2ecf20Sopenharmony_ci ring->used_slots = old_used_slots; 10258c2ecf20Sopenharmony_ci err = -EIO; 10268c2ecf20Sopenharmony_ci goto out_free_bounce; 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci op32_fill_descriptor(ring, desc, meta->dmaaddr, 10318c2ecf20Sopenharmony_ci skb->len, 0, 1, 1); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci wmb(); /* previous stuff MUST be done */ 10348c2ecf20Sopenharmony_ci /* Now transfer the whole frame. */ 10358c2ecf20Sopenharmony_ci op32_poke_tx(ring, next_slot(ring, slot)); 10368c2ecf20Sopenharmony_ci return 0; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ciout_free_bounce: 10398c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 10408c2ecf20Sopenharmony_ciout_unmap_hdr: 10418c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta_hdr->dmaaddr, 10428c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1); 10438c2ecf20Sopenharmony_ci return err; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic inline 10478c2ecf20Sopenharmony_ciint should_inject_overflow(struct b43legacy_dmaring *ring) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci#ifdef CONFIG_B43LEGACY_DEBUG 10508c2ecf20Sopenharmony_ci if (unlikely(b43legacy_debug(ring->dev, 10518c2ecf20Sopenharmony_ci B43legacy_DBG_DMAOVERFLOW))) { 10528c2ecf20Sopenharmony_ci /* Check if we should inject another ringbuffer overflow 10538c2ecf20Sopenharmony_ci * to test handling of this situation in the stack. */ 10548c2ecf20Sopenharmony_ci unsigned long next_overflow; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci next_overflow = ring->last_injected_overflow + HZ; 10578c2ecf20Sopenharmony_ci if (time_after(jiffies, next_overflow)) { 10588c2ecf20Sopenharmony_ci ring->last_injected_overflow = jiffies; 10598c2ecf20Sopenharmony_ci b43legacydbg(ring->dev->wl, 10608c2ecf20Sopenharmony_ci "Injecting TX ring overflow on " 10618c2ecf20Sopenharmony_ci "DMA controller %d\n", ring->index); 10628c2ecf20Sopenharmony_ci return 1; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci#endif /* CONFIG_B43LEGACY_DEBUG */ 10668c2ecf20Sopenharmony_ci return 0; 10678c2ecf20Sopenharmony_ci} 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ciint b43legacy_dma_tx(struct b43legacy_wldev *dev, 10708c2ecf20Sopenharmony_ci struct sk_buff *skb) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci struct b43legacy_dmaring *ring; 10738c2ecf20Sopenharmony_ci int err = 0; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci ring = priority_to_txring(dev, skb_get_queue_mapping(skb)); 10768c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (unlikely(ring->stopped)) { 10798c2ecf20Sopenharmony_ci /* We get here only because of a bug in mac80211. 10808c2ecf20Sopenharmony_ci * Because of a race, one packet may be queued after 10818c2ecf20Sopenharmony_ci * the queue is stopped, thus we got called when we shouldn't. 10828c2ecf20Sopenharmony_ci * For now, just refuse the transmit. */ 10838c2ecf20Sopenharmony_ci if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) 10848c2ecf20Sopenharmony_ci b43legacyerr(dev->wl, "Packet after queue stopped\n"); 10858c2ecf20Sopenharmony_ci return -ENOSPC; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (WARN_ON(free_slots(ring) < SLOTS_PER_PACKET)) { 10898c2ecf20Sopenharmony_ci /* If we get here, we have a real error with the queue 10908c2ecf20Sopenharmony_ci * full, but queues not stopped. */ 10918c2ecf20Sopenharmony_ci b43legacyerr(dev->wl, "DMA queue overflow\n"); 10928c2ecf20Sopenharmony_ci return -ENOSPC; 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing 10968c2ecf20Sopenharmony_ci * into the skb data or cb now. */ 10978c2ecf20Sopenharmony_ci err = dma_tx_fragment(ring, &skb); 10988c2ecf20Sopenharmony_ci if (unlikely(err == -ENOKEY)) { 10998c2ecf20Sopenharmony_ci /* Drop this packet, as we don't have the encryption key 11008c2ecf20Sopenharmony_ci * anymore and must not transmit it unencrypted. */ 11018c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 11028c2ecf20Sopenharmony_ci return 0; 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci if (unlikely(err)) { 11058c2ecf20Sopenharmony_ci b43legacyerr(dev->wl, "DMA tx mapping failure\n"); 11068c2ecf20Sopenharmony_ci return err; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci if ((free_slots(ring) < SLOTS_PER_PACKET) || 11098c2ecf20Sopenharmony_ci should_inject_overflow(ring)) { 11108c2ecf20Sopenharmony_ci /* This TX ring is full. */ 11118c2ecf20Sopenharmony_ci unsigned int skb_mapping = skb_get_queue_mapping(skb); 11128c2ecf20Sopenharmony_ci ieee80211_stop_queue(dev->wl->hw, skb_mapping); 11138c2ecf20Sopenharmony_ci dev->wl->tx_queue_stopped[skb_mapping] = 1; 11148c2ecf20Sopenharmony_ci ring->stopped = true; 11158c2ecf20Sopenharmony_ci if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) 11168c2ecf20Sopenharmony_ci b43legacydbg(dev->wl, "Stopped TX ring %d\n", 11178c2ecf20Sopenharmony_ci ring->index); 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci return err; 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_civoid b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, 11238c2ecf20Sopenharmony_ci const struct b43legacy_txstatus *status) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci struct b43legacy_dmaring *ring; 11268c2ecf20Sopenharmony_ci struct b43legacy_dmadesc_meta *meta; 11278c2ecf20Sopenharmony_ci int retry_limit; 11288c2ecf20Sopenharmony_ci int slot; 11298c2ecf20Sopenharmony_ci int firstused; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci ring = parse_cookie(dev, status->cookie, &slot); 11328c2ecf20Sopenharmony_ci if (unlikely(!ring)) 11338c2ecf20Sopenharmony_ci return; 11348c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci /* Sanity check: TX packets are processed in-order on one ring. 11378c2ecf20Sopenharmony_ci * Check if the slot deduced from the cookie really is the first 11388c2ecf20Sopenharmony_ci * used slot. */ 11398c2ecf20Sopenharmony_ci firstused = ring->current_slot - ring->used_slots + 1; 11408c2ecf20Sopenharmony_ci if (firstused < 0) 11418c2ecf20Sopenharmony_ci firstused = ring->nr_slots + firstused; 11428c2ecf20Sopenharmony_ci if (unlikely(slot != firstused)) { 11438c2ecf20Sopenharmony_ci /* This possibly is a firmware bug and will result in 11448c2ecf20Sopenharmony_ci * malfunction, memory leaks and/or stall of DMA functionality. 11458c2ecf20Sopenharmony_ci */ 11468c2ecf20Sopenharmony_ci b43legacydbg(dev->wl, "Out of order TX status report on DMA " 11478c2ecf20Sopenharmony_ci "ring %d. Expected %d, but got %d\n", 11488c2ecf20Sopenharmony_ci ring->index, firstused, slot); 11498c2ecf20Sopenharmony_ci return; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci while (1) { 11538c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); 11548c2ecf20Sopenharmony_ci op32_idx2desc(ring, slot, &meta); 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (meta->skb) 11578c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 11588c2ecf20Sopenharmony_ci meta->skb->len, 1); 11598c2ecf20Sopenharmony_ci else 11608c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 11618c2ecf20Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 11628c2ecf20Sopenharmony_ci 1); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci if (meta->is_last_fragment) { 11658c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 11668c2ecf20Sopenharmony_ci BUG_ON(!meta->skb); 11678c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(meta->skb); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci /* preserve the confiured retry limit before clearing the status 11708c2ecf20Sopenharmony_ci * The xmit function has overwritten the rc's value with the actual 11718c2ecf20Sopenharmony_ci * retry limit done by the hardware */ 11728c2ecf20Sopenharmony_ci retry_limit = info->status.rates[0].count; 11738c2ecf20Sopenharmony_ci ieee80211_tx_info_clear_status(info); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci if (status->acked) 11768c2ecf20Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { 11798c2ecf20Sopenharmony_ci /* 11808c2ecf20Sopenharmony_ci * If the short retries (RTS, not data frame) have exceeded 11818c2ecf20Sopenharmony_ci * the limit, the hw will not have tried the selected rate, 11828c2ecf20Sopenharmony_ci * but will have used the fallback rate instead. 11838c2ecf20Sopenharmony_ci * Don't let the rate control count attempts for the selected 11848c2ecf20Sopenharmony_ci * rate in this case, otherwise the statistics will be off. 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_ci info->status.rates[0].count = 0; 11878c2ecf20Sopenharmony_ci info->status.rates[1].count = status->frame_count; 11888c2ecf20Sopenharmony_ci } else { 11898c2ecf20Sopenharmony_ci if (status->frame_count > retry_limit) { 11908c2ecf20Sopenharmony_ci info->status.rates[0].count = retry_limit; 11918c2ecf20Sopenharmony_ci info->status.rates[1].count = status->frame_count - 11928c2ecf20Sopenharmony_ci retry_limit; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci } else { 11958c2ecf20Sopenharmony_ci info->status.rates[0].count = status->frame_count; 11968c2ecf20Sopenharmony_ci info->status.rates[1].idx = -1; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* Call back to inform the ieee80211 subsystem about the 12018c2ecf20Sopenharmony_ci * status of the transmission. 12028c2ecf20Sopenharmony_ci * Some fields of txstat are already filled in dma_tx(). 12038c2ecf20Sopenharmony_ci */ 12048c2ecf20Sopenharmony_ci ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb); 12058c2ecf20Sopenharmony_ci /* skb is freed by ieee80211_tx_status_irqsafe() */ 12068c2ecf20Sopenharmony_ci meta->skb = NULL; 12078c2ecf20Sopenharmony_ci } else { 12088c2ecf20Sopenharmony_ci /* No need to call free_descriptor_buffer here, as 12098c2ecf20Sopenharmony_ci * this is only the txhdr, which is not allocated. 12108c2ecf20Sopenharmony_ci */ 12118c2ecf20Sopenharmony_ci B43legacy_WARN_ON(meta->skb != NULL); 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* Everything unmapped and free'd. So it's not used anymore. */ 12158c2ecf20Sopenharmony_ci ring->used_slots--; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (meta->is_last_fragment) 12188c2ecf20Sopenharmony_ci break; 12198c2ecf20Sopenharmony_ci slot = next_slot(ring, slot); 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci dev->stats.last_tx = jiffies; 12228c2ecf20Sopenharmony_ci if (ring->stopped) { 12238c2ecf20Sopenharmony_ci B43legacy_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET); 12248c2ecf20Sopenharmony_ci ring->stopped = false; 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (dev->wl->tx_queue_stopped[ring->queue_prio]) { 12288c2ecf20Sopenharmony_ci dev->wl->tx_queue_stopped[ring->queue_prio] = 0; 12298c2ecf20Sopenharmony_ci } else { 12308c2ecf20Sopenharmony_ci /* If the driver queue is running wake the corresponding 12318c2ecf20Sopenharmony_ci * mac80211 queue. */ 12328c2ecf20Sopenharmony_ci ieee80211_wake_queue(dev->wl->hw, ring->queue_prio); 12338c2ecf20Sopenharmony_ci if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) 12348c2ecf20Sopenharmony_ci b43legacydbg(dev->wl, "Woke up TX ring %d\n", 12358c2ecf20Sopenharmony_ci ring->index); 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci /* Add work to the queue. */ 12388c2ecf20Sopenharmony_ci ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work); 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cistatic void dma_rx(struct b43legacy_dmaring *ring, 12428c2ecf20Sopenharmony_ci int *slot) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci struct b43legacy_dmadesc32 *desc; 12458c2ecf20Sopenharmony_ci struct b43legacy_dmadesc_meta *meta; 12468c2ecf20Sopenharmony_ci struct b43legacy_rxhdr_fw3 *rxhdr; 12478c2ecf20Sopenharmony_ci struct sk_buff *skb; 12488c2ecf20Sopenharmony_ci u16 len; 12498c2ecf20Sopenharmony_ci int err; 12508c2ecf20Sopenharmony_ci dma_addr_t dmaaddr; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci desc = op32_idx2desc(ring, *slot, &meta); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); 12558c2ecf20Sopenharmony_ci skb = meta->skb; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci if (ring->index == 3) { 12588c2ecf20Sopenharmony_ci /* We received an xmit status. */ 12598c2ecf20Sopenharmony_ci struct b43legacy_hwtxstatus *hw = 12608c2ecf20Sopenharmony_ci (struct b43legacy_hwtxstatus *)skb->data; 12618c2ecf20Sopenharmony_ci int i = 0; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci while (hw->cookie == 0) { 12648c2ecf20Sopenharmony_ci if (i > 100) 12658c2ecf20Sopenharmony_ci break; 12668c2ecf20Sopenharmony_ci i++; 12678c2ecf20Sopenharmony_ci udelay(2); 12688c2ecf20Sopenharmony_ci barrier(); 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci b43legacy_handle_hwtxstatus(ring->dev, hw); 12718c2ecf20Sopenharmony_ci /* recycle the descriptor buffer. */ 12728c2ecf20Sopenharmony_ci sync_descbuffer_for_device(ring, meta->dmaaddr, 12738c2ecf20Sopenharmony_ci ring->rx_buffersize); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci return; 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci rxhdr = (struct b43legacy_rxhdr_fw3 *)skb->data; 12788c2ecf20Sopenharmony_ci len = le16_to_cpu(rxhdr->frame_len); 12798c2ecf20Sopenharmony_ci if (len == 0) { 12808c2ecf20Sopenharmony_ci int i = 0; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci do { 12838c2ecf20Sopenharmony_ci udelay(2); 12848c2ecf20Sopenharmony_ci barrier(); 12858c2ecf20Sopenharmony_ci len = le16_to_cpu(rxhdr->frame_len); 12868c2ecf20Sopenharmony_ci } while (len == 0 && i++ < 5); 12878c2ecf20Sopenharmony_ci if (unlikely(len == 0)) { 12888c2ecf20Sopenharmony_ci /* recycle the descriptor buffer. */ 12898c2ecf20Sopenharmony_ci sync_descbuffer_for_device(ring, meta->dmaaddr, 12908c2ecf20Sopenharmony_ci ring->rx_buffersize); 12918c2ecf20Sopenharmony_ci goto drop; 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci if (unlikely(len > ring->rx_buffersize)) { 12958c2ecf20Sopenharmony_ci /* The data did not fit into one descriptor buffer 12968c2ecf20Sopenharmony_ci * and is split over multiple buffers. 12978c2ecf20Sopenharmony_ci * This should never happen, as we try to allocate buffers 12988c2ecf20Sopenharmony_ci * big enough. So simply ignore this packet. 12998c2ecf20Sopenharmony_ci */ 13008c2ecf20Sopenharmony_ci int cnt = 0; 13018c2ecf20Sopenharmony_ci s32 tmp = len; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci while (1) { 13048c2ecf20Sopenharmony_ci desc = op32_idx2desc(ring, *slot, &meta); 13058c2ecf20Sopenharmony_ci /* recycle the descriptor buffer. */ 13068c2ecf20Sopenharmony_ci sync_descbuffer_for_device(ring, meta->dmaaddr, 13078c2ecf20Sopenharmony_ci ring->rx_buffersize); 13088c2ecf20Sopenharmony_ci *slot = next_slot(ring, *slot); 13098c2ecf20Sopenharmony_ci cnt++; 13108c2ecf20Sopenharmony_ci tmp -= ring->rx_buffersize; 13118c2ecf20Sopenharmony_ci if (tmp <= 0) 13128c2ecf20Sopenharmony_ci break; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci b43legacyerr(ring->dev->wl, "DMA RX buffer too small " 13158c2ecf20Sopenharmony_ci "(len: %u, buffer: %u, nr-dropped: %d)\n", 13168c2ecf20Sopenharmony_ci len, ring->rx_buffersize, cnt); 13178c2ecf20Sopenharmony_ci goto drop; 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci dmaaddr = meta->dmaaddr; 13218c2ecf20Sopenharmony_ci err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); 13228c2ecf20Sopenharmony_ci if (unlikely(err)) { 13238c2ecf20Sopenharmony_ci b43legacydbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer()" 13248c2ecf20Sopenharmony_ci " failed\n"); 13258c2ecf20Sopenharmony_ci sync_descbuffer_for_device(ring, dmaaddr, 13268c2ecf20Sopenharmony_ci ring->rx_buffersize); 13278c2ecf20Sopenharmony_ci goto drop; 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); 13318c2ecf20Sopenharmony_ci skb_put(skb, len + ring->frameoffset); 13328c2ecf20Sopenharmony_ci skb_pull(skb, ring->frameoffset); 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci b43legacy_rx(ring->dev, skb, rxhdr); 13358c2ecf20Sopenharmony_cidrop: 13368c2ecf20Sopenharmony_ci return; 13378c2ecf20Sopenharmony_ci} 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_civoid b43legacy_dma_rx(struct b43legacy_dmaring *ring) 13408c2ecf20Sopenharmony_ci{ 13418c2ecf20Sopenharmony_ci int slot; 13428c2ecf20Sopenharmony_ci int current_slot; 13438c2ecf20Sopenharmony_ci int used_slots = 0; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci B43legacy_WARN_ON(ring->tx); 13468c2ecf20Sopenharmony_ci current_slot = op32_get_current_rxslot(ring); 13478c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!(current_slot >= 0 && current_slot < 13488c2ecf20Sopenharmony_ci ring->nr_slots)); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci slot = ring->current_slot; 13518c2ecf20Sopenharmony_ci for (; slot != current_slot; slot = next_slot(ring, slot)) { 13528c2ecf20Sopenharmony_ci dma_rx(ring, &slot); 13538c2ecf20Sopenharmony_ci update_max_used_slots(ring, ++used_slots); 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci op32_set_current_rxslot(ring, slot); 13568c2ecf20Sopenharmony_ci ring->current_slot = slot; 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 13628c2ecf20Sopenharmony_ci op32_tx_suspend(ring); 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_cistatic void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 13688c2ecf20Sopenharmony_ci op32_tx_resume(ring); 13698c2ecf20Sopenharmony_ci} 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_civoid b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci b43legacy_power_saving_ctl_bits(dev, -1, 1); 13748c2ecf20Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring0); 13758c2ecf20Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring1); 13768c2ecf20Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring2); 13778c2ecf20Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring3); 13788c2ecf20Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring4); 13798c2ecf20Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring5); 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_civoid b43legacy_dma_tx_resume(struct b43legacy_wldev *dev) 13838c2ecf20Sopenharmony_ci{ 13848c2ecf20Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring5); 13858c2ecf20Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring4); 13868c2ecf20Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring3); 13878c2ecf20Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring2); 13888c2ecf20Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring1); 13898c2ecf20Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring0); 13908c2ecf20Sopenharmony_ci b43legacy_power_saving_ctl_bits(dev, -1, -1); 13918c2ecf20Sopenharmony_ci} 1392