18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Broadcom B43 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 "b43.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/etherdevice.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <asm/div64.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* Required number of TX DMA slots per TX frame. 338c2ecf20Sopenharmony_ci * This currently is 2, because we put the header and the ieee80211 frame 348c2ecf20Sopenharmony_ci * into separate slots. */ 358c2ecf20Sopenharmony_ci#define TX_SLOTS_PER_FRAME 2 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic u32 b43_dma_address(struct b43_dma *dma, dma_addr_t dmaaddr, 388c2ecf20Sopenharmony_ci enum b43_addrtype addrtype) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci u32 addr; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci switch (addrtype) { 438c2ecf20Sopenharmony_ci case B43_DMA_ADDR_LOW: 448c2ecf20Sopenharmony_ci addr = lower_32_bits(dmaaddr); 458c2ecf20Sopenharmony_ci if (dma->translation_in_low) { 468c2ecf20Sopenharmony_ci addr &= ~SSB_DMA_TRANSLATION_MASK; 478c2ecf20Sopenharmony_ci addr |= dma->translation; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci break; 508c2ecf20Sopenharmony_ci case B43_DMA_ADDR_HIGH: 518c2ecf20Sopenharmony_ci addr = upper_32_bits(dmaaddr); 528c2ecf20Sopenharmony_ci if (!dma->translation_in_low) { 538c2ecf20Sopenharmony_ci addr &= ~SSB_DMA_TRANSLATION_MASK; 548c2ecf20Sopenharmony_ci addr |= dma->translation; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci break; 578c2ecf20Sopenharmony_ci case B43_DMA_ADDR_EXT: 588c2ecf20Sopenharmony_ci if (dma->translation_in_low) 598c2ecf20Sopenharmony_ci addr = lower_32_bits(dmaaddr); 608c2ecf20Sopenharmony_ci else 618c2ecf20Sopenharmony_ci addr = upper_32_bits(dmaaddr); 628c2ecf20Sopenharmony_ci addr &= SSB_DMA_TRANSLATION_MASK; 638c2ecf20Sopenharmony_ci addr >>= SSB_DMA_TRANSLATION_SHIFT; 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return addr; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* 32bit DMA ops. */ 718c2ecf20Sopenharmony_cistatic 728c2ecf20Sopenharmony_cistruct b43_dmadesc_generic *op32_idx2desc(struct b43_dmaring *ring, 738c2ecf20Sopenharmony_ci int slot, 748c2ecf20Sopenharmony_ci struct b43_dmadesc_meta **meta) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct b43_dmadesc32 *desc; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci *meta = &(ring->meta[slot]); 798c2ecf20Sopenharmony_ci desc = ring->descbase; 808c2ecf20Sopenharmony_ci desc = &(desc[slot]); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return (struct b43_dmadesc_generic *)desc; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void op32_fill_descriptor(struct b43_dmaring *ring, 868c2ecf20Sopenharmony_ci struct b43_dmadesc_generic *desc, 878c2ecf20Sopenharmony_ci dma_addr_t dmaaddr, u16 bufsize, 888c2ecf20Sopenharmony_ci int start, int end, int irq) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct b43_dmadesc32 *descbase = ring->descbase; 918c2ecf20Sopenharmony_ci int slot; 928c2ecf20Sopenharmony_ci u32 ctl; 938c2ecf20Sopenharmony_ci u32 addr; 948c2ecf20Sopenharmony_ci u32 addrext; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci slot = (int)(&(desc->dma32) - descbase); 978c2ecf20Sopenharmony_ci B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci addr = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW); 1008c2ecf20Sopenharmony_ci addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci ctl = bufsize & B43_DMA32_DCTL_BYTECNT; 1038c2ecf20Sopenharmony_ci if (slot == ring->nr_slots - 1) 1048c2ecf20Sopenharmony_ci ctl |= B43_DMA32_DCTL_DTABLEEND; 1058c2ecf20Sopenharmony_ci if (start) 1068c2ecf20Sopenharmony_ci ctl |= B43_DMA32_DCTL_FRAMESTART; 1078c2ecf20Sopenharmony_ci if (end) 1088c2ecf20Sopenharmony_ci ctl |= B43_DMA32_DCTL_FRAMEEND; 1098c2ecf20Sopenharmony_ci if (irq) 1108c2ecf20Sopenharmony_ci ctl |= B43_DMA32_DCTL_IRQ; 1118c2ecf20Sopenharmony_ci ctl |= (addrext << B43_DMA32_DCTL_ADDREXT_SHIFT) 1128c2ecf20Sopenharmony_ci & B43_DMA32_DCTL_ADDREXT_MASK; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci desc->dma32.control = cpu_to_le32(ctl); 1158c2ecf20Sopenharmony_ci desc->dma32.address = cpu_to_le32(addr); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void op32_poke_tx(struct b43_dmaring *ring, int slot) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_TXINDEX, 1218c2ecf20Sopenharmony_ci (u32) (slot * sizeof(struct b43_dmadesc32))); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void op32_tx_suspend(struct b43_dmaring *ring) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_TXCTL, b43_dma_read(ring, B43_DMA32_TXCTL) 1278c2ecf20Sopenharmony_ci | B43_DMA32_TXSUSPEND); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void op32_tx_resume(struct b43_dmaring *ring) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_TXCTL, b43_dma_read(ring, B43_DMA32_TXCTL) 1338c2ecf20Sopenharmony_ci & ~B43_DMA32_TXSUSPEND); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int op32_get_current_rxslot(struct b43_dmaring *ring) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci u32 val; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci val = b43_dma_read(ring, B43_DMA32_RXSTATUS); 1418c2ecf20Sopenharmony_ci val &= B43_DMA32_RXDPTR; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return (val / sizeof(struct b43_dmadesc32)); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void op32_set_current_rxslot(struct b43_dmaring *ring, int slot) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_RXINDEX, 1498c2ecf20Sopenharmony_ci (u32) (slot * sizeof(struct b43_dmadesc32))); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic const struct b43_dma_ops dma32_ops = { 1538c2ecf20Sopenharmony_ci .idx2desc = op32_idx2desc, 1548c2ecf20Sopenharmony_ci .fill_descriptor = op32_fill_descriptor, 1558c2ecf20Sopenharmony_ci .poke_tx = op32_poke_tx, 1568c2ecf20Sopenharmony_ci .tx_suspend = op32_tx_suspend, 1578c2ecf20Sopenharmony_ci .tx_resume = op32_tx_resume, 1588c2ecf20Sopenharmony_ci .get_current_rxslot = op32_get_current_rxslot, 1598c2ecf20Sopenharmony_ci .set_current_rxslot = op32_set_current_rxslot, 1608c2ecf20Sopenharmony_ci}; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* 64bit DMA ops. */ 1638c2ecf20Sopenharmony_cistatic 1648c2ecf20Sopenharmony_cistruct b43_dmadesc_generic *op64_idx2desc(struct b43_dmaring *ring, 1658c2ecf20Sopenharmony_ci int slot, 1668c2ecf20Sopenharmony_ci struct b43_dmadesc_meta **meta) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct b43_dmadesc64 *desc; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci *meta = &(ring->meta[slot]); 1718c2ecf20Sopenharmony_ci desc = ring->descbase; 1728c2ecf20Sopenharmony_ci desc = &(desc[slot]); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return (struct b43_dmadesc_generic *)desc; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void op64_fill_descriptor(struct b43_dmaring *ring, 1788c2ecf20Sopenharmony_ci struct b43_dmadesc_generic *desc, 1798c2ecf20Sopenharmony_ci dma_addr_t dmaaddr, u16 bufsize, 1808c2ecf20Sopenharmony_ci int start, int end, int irq) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct b43_dmadesc64 *descbase = ring->descbase; 1838c2ecf20Sopenharmony_ci int slot; 1848c2ecf20Sopenharmony_ci u32 ctl0 = 0, ctl1 = 0; 1858c2ecf20Sopenharmony_ci u32 addrlo, addrhi; 1868c2ecf20Sopenharmony_ci u32 addrext; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci slot = (int)(&(desc->dma64) - descbase); 1898c2ecf20Sopenharmony_ci B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci addrlo = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_LOW); 1928c2ecf20Sopenharmony_ci addrhi = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_HIGH); 1938c2ecf20Sopenharmony_ci addrext = b43_dma_address(&ring->dev->dma, dmaaddr, B43_DMA_ADDR_EXT); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (slot == ring->nr_slots - 1) 1968c2ecf20Sopenharmony_ci ctl0 |= B43_DMA64_DCTL0_DTABLEEND; 1978c2ecf20Sopenharmony_ci if (start) 1988c2ecf20Sopenharmony_ci ctl0 |= B43_DMA64_DCTL0_FRAMESTART; 1998c2ecf20Sopenharmony_ci if (end) 2008c2ecf20Sopenharmony_ci ctl0 |= B43_DMA64_DCTL0_FRAMEEND; 2018c2ecf20Sopenharmony_ci if (irq) 2028c2ecf20Sopenharmony_ci ctl0 |= B43_DMA64_DCTL0_IRQ; 2038c2ecf20Sopenharmony_ci ctl1 |= bufsize & B43_DMA64_DCTL1_BYTECNT; 2048c2ecf20Sopenharmony_ci ctl1 |= (addrext << B43_DMA64_DCTL1_ADDREXT_SHIFT) 2058c2ecf20Sopenharmony_ci & B43_DMA64_DCTL1_ADDREXT_MASK; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci desc->dma64.control0 = cpu_to_le32(ctl0); 2088c2ecf20Sopenharmony_ci desc->dma64.control1 = cpu_to_le32(ctl1); 2098c2ecf20Sopenharmony_ci desc->dma64.address_low = cpu_to_le32(addrlo); 2108c2ecf20Sopenharmony_ci desc->dma64.address_high = cpu_to_le32(addrhi); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void op64_poke_tx(struct b43_dmaring *ring, int slot) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_TXINDEX, 2168c2ecf20Sopenharmony_ci (u32) (slot * sizeof(struct b43_dmadesc64))); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void op64_tx_suspend(struct b43_dmaring *ring) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_TXCTL, b43_dma_read(ring, B43_DMA64_TXCTL) 2228c2ecf20Sopenharmony_ci | B43_DMA64_TXSUSPEND); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void op64_tx_resume(struct b43_dmaring *ring) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_TXCTL, b43_dma_read(ring, B43_DMA64_TXCTL) 2288c2ecf20Sopenharmony_ci & ~B43_DMA64_TXSUSPEND); 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int op64_get_current_rxslot(struct b43_dmaring *ring) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci u32 val; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci val = b43_dma_read(ring, B43_DMA64_RXSTATUS); 2368c2ecf20Sopenharmony_ci val &= B43_DMA64_RXSTATDPTR; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return (val / sizeof(struct b43_dmadesc64)); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void op64_set_current_rxslot(struct b43_dmaring *ring, int slot) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_RXINDEX, 2448c2ecf20Sopenharmony_ci (u32) (slot * sizeof(struct b43_dmadesc64))); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic const struct b43_dma_ops dma64_ops = { 2488c2ecf20Sopenharmony_ci .idx2desc = op64_idx2desc, 2498c2ecf20Sopenharmony_ci .fill_descriptor = op64_fill_descriptor, 2508c2ecf20Sopenharmony_ci .poke_tx = op64_poke_tx, 2518c2ecf20Sopenharmony_ci .tx_suspend = op64_tx_suspend, 2528c2ecf20Sopenharmony_ci .tx_resume = op64_tx_resume, 2538c2ecf20Sopenharmony_ci .get_current_rxslot = op64_get_current_rxslot, 2548c2ecf20Sopenharmony_ci .set_current_rxslot = op64_set_current_rxslot, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic inline int free_slots(struct b43_dmaring *ring) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci return (ring->nr_slots - ring->used_slots); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic inline int next_slot(struct b43_dmaring *ring, int slot) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci B43_WARN_ON(!(slot >= -1 && slot <= ring->nr_slots - 1)); 2658c2ecf20Sopenharmony_ci if (slot == ring->nr_slots - 1) 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci return slot + 1; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic inline int prev_slot(struct b43_dmaring *ring, int slot) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci B43_WARN_ON(!(slot >= 0 && slot <= ring->nr_slots - 1)); 2738c2ecf20Sopenharmony_ci if (slot == 0) 2748c2ecf20Sopenharmony_ci return ring->nr_slots - 1; 2758c2ecf20Sopenharmony_ci return slot - 1; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_DEBUG 2798c2ecf20Sopenharmony_cistatic void update_max_used_slots(struct b43_dmaring *ring, 2808c2ecf20Sopenharmony_ci int current_used_slots) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci if (current_used_slots <= ring->max_used_slots) 2838c2ecf20Sopenharmony_ci return; 2848c2ecf20Sopenharmony_ci ring->max_used_slots = current_used_slots; 2858c2ecf20Sopenharmony_ci if (b43_debug(ring->dev, B43_DBG_DMAVERBOSE)) { 2868c2ecf20Sopenharmony_ci b43dbg(ring->dev->wl, 2878c2ecf20Sopenharmony_ci "max_used_slots increased to %d on %s ring %d\n", 2888c2ecf20Sopenharmony_ci ring->max_used_slots, 2898c2ecf20Sopenharmony_ci ring->tx ? "TX" : "RX", ring->index); 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci#else 2938c2ecf20Sopenharmony_cistatic inline 2948c2ecf20Sopenharmony_ci void update_max_used_slots(struct b43_dmaring *ring, int current_used_slots) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci#endif /* DEBUG */ 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* Request a slot for usage. */ 3008c2ecf20Sopenharmony_cistatic inline int request_slot(struct b43_dmaring *ring) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci int slot; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci B43_WARN_ON(!ring->tx); 3058c2ecf20Sopenharmony_ci B43_WARN_ON(ring->stopped); 3068c2ecf20Sopenharmony_ci B43_WARN_ON(free_slots(ring) == 0); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci slot = next_slot(ring, ring->current_slot); 3098c2ecf20Sopenharmony_ci ring->current_slot = slot; 3108c2ecf20Sopenharmony_ci ring->used_slots++; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci update_max_used_slots(ring, ring->used_slots); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return slot; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic u16 b43_dmacontroller_base(enum b43_dmatype type, int controller_idx) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci static const u16 map64[] = { 3208c2ecf20Sopenharmony_ci B43_MMIO_DMA64_BASE0, 3218c2ecf20Sopenharmony_ci B43_MMIO_DMA64_BASE1, 3228c2ecf20Sopenharmony_ci B43_MMIO_DMA64_BASE2, 3238c2ecf20Sopenharmony_ci B43_MMIO_DMA64_BASE3, 3248c2ecf20Sopenharmony_ci B43_MMIO_DMA64_BASE4, 3258c2ecf20Sopenharmony_ci B43_MMIO_DMA64_BASE5, 3268c2ecf20Sopenharmony_ci }; 3278c2ecf20Sopenharmony_ci static const u16 map32[] = { 3288c2ecf20Sopenharmony_ci B43_MMIO_DMA32_BASE0, 3298c2ecf20Sopenharmony_ci B43_MMIO_DMA32_BASE1, 3308c2ecf20Sopenharmony_ci B43_MMIO_DMA32_BASE2, 3318c2ecf20Sopenharmony_ci B43_MMIO_DMA32_BASE3, 3328c2ecf20Sopenharmony_ci B43_MMIO_DMA32_BASE4, 3338c2ecf20Sopenharmony_ci B43_MMIO_DMA32_BASE5, 3348c2ecf20Sopenharmony_ci }; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (type == B43_DMA_64BIT) { 3378c2ecf20Sopenharmony_ci B43_WARN_ON(!(controller_idx >= 0 && 3388c2ecf20Sopenharmony_ci controller_idx < ARRAY_SIZE(map64))); 3398c2ecf20Sopenharmony_ci return map64[controller_idx]; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci B43_WARN_ON(!(controller_idx >= 0 && 3428c2ecf20Sopenharmony_ci controller_idx < ARRAY_SIZE(map32))); 3438c2ecf20Sopenharmony_ci return map32[controller_idx]; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic inline 3478c2ecf20Sopenharmony_ci dma_addr_t map_descbuffer(struct b43_dmaring *ring, 3488c2ecf20Sopenharmony_ci unsigned char *buf, size_t len, int tx) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci dma_addr_t dmaaddr; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (tx) { 3538c2ecf20Sopenharmony_ci dmaaddr = dma_map_single(ring->dev->dev->dma_dev, 3548c2ecf20Sopenharmony_ci buf, len, DMA_TO_DEVICE); 3558c2ecf20Sopenharmony_ci } else { 3568c2ecf20Sopenharmony_ci dmaaddr = dma_map_single(ring->dev->dev->dma_dev, 3578c2ecf20Sopenharmony_ci buf, len, DMA_FROM_DEVICE); 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return dmaaddr; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic inline 3648c2ecf20Sopenharmony_ci void unmap_descbuffer(struct b43_dmaring *ring, 3658c2ecf20Sopenharmony_ci dma_addr_t addr, size_t len, int tx) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci if (tx) { 3688c2ecf20Sopenharmony_ci dma_unmap_single(ring->dev->dev->dma_dev, 3698c2ecf20Sopenharmony_ci addr, len, DMA_TO_DEVICE); 3708c2ecf20Sopenharmony_ci } else { 3718c2ecf20Sopenharmony_ci dma_unmap_single(ring->dev->dev->dma_dev, 3728c2ecf20Sopenharmony_ci addr, len, DMA_FROM_DEVICE); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic inline 3778c2ecf20Sopenharmony_ci void sync_descbuffer_for_cpu(struct b43_dmaring *ring, 3788c2ecf20Sopenharmony_ci dma_addr_t addr, size_t len) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci B43_WARN_ON(ring->tx); 3818c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(ring->dev->dev->dma_dev, 3828c2ecf20Sopenharmony_ci addr, len, DMA_FROM_DEVICE); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic inline 3868c2ecf20Sopenharmony_ci void sync_descbuffer_for_device(struct b43_dmaring *ring, 3878c2ecf20Sopenharmony_ci dma_addr_t addr, size_t len) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci B43_WARN_ON(ring->tx); 3908c2ecf20Sopenharmony_ci dma_sync_single_for_device(ring->dev->dev->dma_dev, 3918c2ecf20Sopenharmony_ci addr, len, DMA_FROM_DEVICE); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic inline 3958c2ecf20Sopenharmony_ci void free_descriptor_buffer(struct b43_dmaring *ring, 3968c2ecf20Sopenharmony_ci struct b43_dmadesc_meta *meta) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci if (meta->skb) { 3998c2ecf20Sopenharmony_ci if (ring->tx) 4008c2ecf20Sopenharmony_ci ieee80211_free_txskb(ring->dev->wl->hw, meta->skb); 4018c2ecf20Sopenharmony_ci else 4028c2ecf20Sopenharmony_ci dev_kfree_skb_any(meta->skb); 4038c2ecf20Sopenharmony_ci meta->skb = NULL; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int alloc_ringmemory(struct b43_dmaring *ring) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci /* The specs call for 4K buffers for 30- and 32-bit DMA with 4K 4108c2ecf20Sopenharmony_ci * alignment and 8K buffers for 64-bit DMA with 8K alignment. 4118c2ecf20Sopenharmony_ci * In practice we could use smaller buffers for the latter, but the 4128c2ecf20Sopenharmony_ci * alignment is really important because of the hardware bug. If bit 4138c2ecf20Sopenharmony_ci * 0x00001000 is used in DMA address, some hardware (like BCM4331) 4148c2ecf20Sopenharmony_ci * copies that bit into B43_DMA64_RXSTATUS and we get false values from 4158c2ecf20Sopenharmony_ci * B43_DMA64_RXSTATDPTR. Let's just use 8K buffers even if we don't use 4168c2ecf20Sopenharmony_ci * more than 256 slots for ring. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_ci u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ? 4198c2ecf20Sopenharmony_ci B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev, 4228c2ecf20Sopenharmony_ci ring_mem_size, &(ring->dmabase), 4238c2ecf20Sopenharmony_ci GFP_KERNEL); 4248c2ecf20Sopenharmony_ci if (!ring->descbase) 4258c2ecf20Sopenharmony_ci return -ENOMEM; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic void free_ringmemory(struct b43_dmaring *ring) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci u16 ring_mem_size = (ring->type == B43_DMA_64BIT) ? 4338c2ecf20Sopenharmony_ci B43_DMA64_RINGMEMSIZE : B43_DMA32_RINGMEMSIZE; 4348c2ecf20Sopenharmony_ci dma_free_coherent(ring->dev->dev->dma_dev, ring_mem_size, 4358c2ecf20Sopenharmony_ci ring->descbase, ring->dmabase); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/* Reset the RX DMA channel */ 4398c2ecf20Sopenharmony_cistatic int b43_dmacontroller_rx_reset(struct b43_wldev *dev, u16 mmio_base, 4408c2ecf20Sopenharmony_ci enum b43_dmatype type) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci int i; 4438c2ecf20Sopenharmony_ci u32 value; 4448c2ecf20Sopenharmony_ci u16 offset; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci might_sleep(); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci offset = (type == B43_DMA_64BIT) ? B43_DMA64_RXCTL : B43_DMA32_RXCTL; 4498c2ecf20Sopenharmony_ci b43_write32(dev, mmio_base + offset, 0); 4508c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 4518c2ecf20Sopenharmony_ci offset = (type == B43_DMA_64BIT) ? B43_DMA64_RXSTATUS : 4528c2ecf20Sopenharmony_ci B43_DMA32_RXSTATUS; 4538c2ecf20Sopenharmony_ci value = b43_read32(dev, mmio_base + offset); 4548c2ecf20Sopenharmony_ci if (type == B43_DMA_64BIT) { 4558c2ecf20Sopenharmony_ci value &= B43_DMA64_RXSTAT; 4568c2ecf20Sopenharmony_ci if (value == B43_DMA64_RXSTAT_DISABLED) { 4578c2ecf20Sopenharmony_ci i = -1; 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci } else { 4618c2ecf20Sopenharmony_ci value &= B43_DMA32_RXSTATE; 4628c2ecf20Sopenharmony_ci if (value == B43_DMA32_RXSTAT_DISABLED) { 4638c2ecf20Sopenharmony_ci i = -1; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci msleep(1); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci if (i != -1) { 4708c2ecf20Sopenharmony_ci b43err(dev->wl, "DMA RX reset timed out\n"); 4718c2ecf20Sopenharmony_ci return -ENODEV; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci/* Reset the TX DMA channel */ 4788c2ecf20Sopenharmony_cistatic int b43_dmacontroller_tx_reset(struct b43_wldev *dev, u16 mmio_base, 4798c2ecf20Sopenharmony_ci enum b43_dmatype type) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci int i; 4828c2ecf20Sopenharmony_ci u32 value; 4838c2ecf20Sopenharmony_ci u16 offset; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci might_sleep(); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 4888c2ecf20Sopenharmony_ci offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXSTATUS : 4898c2ecf20Sopenharmony_ci B43_DMA32_TXSTATUS; 4908c2ecf20Sopenharmony_ci value = b43_read32(dev, mmio_base + offset); 4918c2ecf20Sopenharmony_ci if (type == B43_DMA_64BIT) { 4928c2ecf20Sopenharmony_ci value &= B43_DMA64_TXSTAT; 4938c2ecf20Sopenharmony_ci if (value == B43_DMA64_TXSTAT_DISABLED || 4948c2ecf20Sopenharmony_ci value == B43_DMA64_TXSTAT_IDLEWAIT || 4958c2ecf20Sopenharmony_ci value == B43_DMA64_TXSTAT_STOPPED) 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci } else { 4988c2ecf20Sopenharmony_ci value &= B43_DMA32_TXSTATE; 4998c2ecf20Sopenharmony_ci if (value == B43_DMA32_TXSTAT_DISABLED || 5008c2ecf20Sopenharmony_ci value == B43_DMA32_TXSTAT_IDLEWAIT || 5018c2ecf20Sopenharmony_ci value == B43_DMA32_TXSTAT_STOPPED) 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci msleep(1); 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXCTL : B43_DMA32_TXCTL; 5078c2ecf20Sopenharmony_ci b43_write32(dev, mmio_base + offset, 0); 5088c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) { 5098c2ecf20Sopenharmony_ci offset = (type == B43_DMA_64BIT) ? B43_DMA64_TXSTATUS : 5108c2ecf20Sopenharmony_ci B43_DMA32_TXSTATUS; 5118c2ecf20Sopenharmony_ci value = b43_read32(dev, mmio_base + offset); 5128c2ecf20Sopenharmony_ci if (type == B43_DMA_64BIT) { 5138c2ecf20Sopenharmony_ci value &= B43_DMA64_TXSTAT; 5148c2ecf20Sopenharmony_ci if (value == B43_DMA64_TXSTAT_DISABLED) { 5158c2ecf20Sopenharmony_ci i = -1; 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci } else { 5198c2ecf20Sopenharmony_ci value &= B43_DMA32_TXSTATE; 5208c2ecf20Sopenharmony_ci if (value == B43_DMA32_TXSTAT_DISABLED) { 5218c2ecf20Sopenharmony_ci i = -1; 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci msleep(1); 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci if (i != -1) { 5288c2ecf20Sopenharmony_ci b43err(dev->wl, "DMA TX reset timed out\n"); 5298c2ecf20Sopenharmony_ci return -ENODEV; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci /* ensure the reset is completed. */ 5328c2ecf20Sopenharmony_ci msleep(1); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci return 0; 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci/* Check if a DMA mapping address is invalid. */ 5388c2ecf20Sopenharmony_cistatic bool b43_dma_mapping_error(struct b43_dmaring *ring, 5398c2ecf20Sopenharmony_ci dma_addr_t addr, 5408c2ecf20Sopenharmony_ci size_t buffersize, bool dma_to_device) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr))) 5438c2ecf20Sopenharmony_ci return true; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci switch (ring->type) { 5468c2ecf20Sopenharmony_ci case B43_DMA_30BIT: 5478c2ecf20Sopenharmony_ci if ((u64)addr + buffersize > (1ULL << 30)) 5488c2ecf20Sopenharmony_ci goto address_error; 5498c2ecf20Sopenharmony_ci break; 5508c2ecf20Sopenharmony_ci case B43_DMA_32BIT: 5518c2ecf20Sopenharmony_ci if ((u64)addr + buffersize > (1ULL << 32)) 5528c2ecf20Sopenharmony_ci goto address_error; 5538c2ecf20Sopenharmony_ci break; 5548c2ecf20Sopenharmony_ci case B43_DMA_64BIT: 5558c2ecf20Sopenharmony_ci /* Currently we can't have addresses beyond 5568c2ecf20Sopenharmony_ci * 64bit in the kernel. */ 5578c2ecf20Sopenharmony_ci break; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* The address is OK. */ 5618c2ecf20Sopenharmony_ci return false; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ciaddress_error: 5648c2ecf20Sopenharmony_ci /* We can't support this address. Unmap it again. */ 5658c2ecf20Sopenharmony_ci unmap_descbuffer(ring, addr, buffersize, dma_to_device); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci return true; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic bool b43_rx_buffer_is_poisoned(struct b43_dmaring *ring, struct sk_buff *skb) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci unsigned char *f = skb->data + ring->frameoffset; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return ((f[0] & f[1] & f[2] & f[3] & f[4] & f[5] & f[6] & f[7]) == 0xFF); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic void b43_poison_rx_buffer(struct b43_dmaring *ring, struct sk_buff *skb) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci struct b43_rxhdr_fw4 *rxhdr; 5808c2ecf20Sopenharmony_ci unsigned char *frame; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* This poisons the RX buffer to detect DMA failures. */ 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci rxhdr = (struct b43_rxhdr_fw4 *)(skb->data); 5858c2ecf20Sopenharmony_ci rxhdr->frame_len = 0; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci B43_WARN_ON(ring->rx_buffersize < ring->frameoffset + sizeof(struct b43_plcp_hdr6) + 2); 5888c2ecf20Sopenharmony_ci frame = skb->data + ring->frameoffset; 5898c2ecf20Sopenharmony_ci memset(frame, 0xFF, sizeof(struct b43_plcp_hdr6) + 2 /* padding */); 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic int setup_rx_descbuffer(struct b43_dmaring *ring, 5938c2ecf20Sopenharmony_ci struct b43_dmadesc_generic *desc, 5948c2ecf20Sopenharmony_ci struct b43_dmadesc_meta *meta, gfp_t gfp_flags) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci dma_addr_t dmaaddr; 5978c2ecf20Sopenharmony_ci struct sk_buff *skb; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci B43_WARN_ON(ring->tx); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); 6028c2ecf20Sopenharmony_ci if (unlikely(!skb)) 6038c2ecf20Sopenharmony_ci return -ENOMEM; 6048c2ecf20Sopenharmony_ci b43_poison_rx_buffer(ring, skb); 6058c2ecf20Sopenharmony_ci dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); 6068c2ecf20Sopenharmony_ci if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { 6078c2ecf20Sopenharmony_ci /* ugh. try to realloc in zone_dma */ 6088c2ecf20Sopenharmony_ci gfp_flags |= GFP_DMA; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); 6138c2ecf20Sopenharmony_ci if (unlikely(!skb)) 6148c2ecf20Sopenharmony_ci return -ENOMEM; 6158c2ecf20Sopenharmony_ci b43_poison_rx_buffer(ring, skb); 6168c2ecf20Sopenharmony_ci dmaaddr = map_descbuffer(ring, skb->data, 6178c2ecf20Sopenharmony_ci ring->rx_buffersize, 0); 6188c2ecf20Sopenharmony_ci if (b43_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { 6198c2ecf20Sopenharmony_ci b43err(ring->dev->wl, "RX DMA buffer allocation failed\n"); 6208c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 6218c2ecf20Sopenharmony_ci return -EIO; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci meta->skb = skb; 6268c2ecf20Sopenharmony_ci meta->dmaaddr = dmaaddr; 6278c2ecf20Sopenharmony_ci ring->ops->fill_descriptor(ring, desc, dmaaddr, 6288c2ecf20Sopenharmony_ci ring->rx_buffersize, 0, 0, 0); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci/* Allocate the initial descbuffers. 6348c2ecf20Sopenharmony_ci * This is used for an RX ring only. 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_cistatic int alloc_initial_descbuffers(struct b43_dmaring *ring) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci int i, err = -ENOMEM; 6398c2ecf20Sopenharmony_ci struct b43_dmadesc_generic *desc; 6408c2ecf20Sopenharmony_ci struct b43_dmadesc_meta *meta; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci for (i = 0; i < ring->nr_slots; i++) { 6438c2ecf20Sopenharmony_ci desc = ring->ops->idx2desc(ring, i, &meta); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); 6468c2ecf20Sopenharmony_ci if (err) { 6478c2ecf20Sopenharmony_ci b43err(ring->dev->wl, 6488c2ecf20Sopenharmony_ci "Failed to allocate initial descbuffers\n"); 6498c2ecf20Sopenharmony_ci goto err_unwind; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci mb(); 6538c2ecf20Sopenharmony_ci ring->used_slots = ring->nr_slots; 6548c2ecf20Sopenharmony_ci err = 0; 6558c2ecf20Sopenharmony_ci out: 6568c2ecf20Sopenharmony_ci return err; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci err_unwind: 6598c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) { 6608c2ecf20Sopenharmony_ci desc = ring->ops->idx2desc(ring, i, &meta); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); 6638c2ecf20Sopenharmony_ci dev_kfree_skb(meta->skb); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci goto out; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci/* Do initial setup of the DMA controller. 6698c2ecf20Sopenharmony_ci * Reset the controller, write the ring busaddress 6708c2ecf20Sopenharmony_ci * and switch the "enable" bit on. 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_cistatic int dmacontroller_setup(struct b43_dmaring *ring) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci int err = 0; 6758c2ecf20Sopenharmony_ci u32 value; 6768c2ecf20Sopenharmony_ci u32 addrext; 6778c2ecf20Sopenharmony_ci bool parity = ring->dev->dma.parity; 6788c2ecf20Sopenharmony_ci u32 addrlo; 6798c2ecf20Sopenharmony_ci u32 addrhi; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (ring->tx) { 6828c2ecf20Sopenharmony_ci if (ring->type == B43_DMA_64BIT) { 6838c2ecf20Sopenharmony_ci u64 ringbase = (u64) (ring->dmabase); 6848c2ecf20Sopenharmony_ci addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); 6858c2ecf20Sopenharmony_ci addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); 6868c2ecf20Sopenharmony_ci addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci value = B43_DMA64_TXENABLE; 6898c2ecf20Sopenharmony_ci value |= (addrext << B43_DMA64_TXADDREXT_SHIFT) 6908c2ecf20Sopenharmony_ci & B43_DMA64_TXADDREXT_MASK; 6918c2ecf20Sopenharmony_ci if (!parity) 6928c2ecf20Sopenharmony_ci value |= B43_DMA64_TXPARITYDISABLE; 6938c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_TXCTL, value); 6948c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_TXRINGLO, addrlo); 6958c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_TXRINGHI, addrhi); 6968c2ecf20Sopenharmony_ci } else { 6978c2ecf20Sopenharmony_ci u32 ringbase = (u32) (ring->dmabase); 6988c2ecf20Sopenharmony_ci addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); 6998c2ecf20Sopenharmony_ci addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci value = B43_DMA32_TXENABLE; 7028c2ecf20Sopenharmony_ci value |= (addrext << B43_DMA32_TXADDREXT_SHIFT) 7038c2ecf20Sopenharmony_ci & B43_DMA32_TXADDREXT_MASK; 7048c2ecf20Sopenharmony_ci if (!parity) 7058c2ecf20Sopenharmony_ci value |= B43_DMA32_TXPARITYDISABLE; 7068c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_TXCTL, value); 7078c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_TXRING, addrlo); 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci } else { 7108c2ecf20Sopenharmony_ci err = alloc_initial_descbuffers(ring); 7118c2ecf20Sopenharmony_ci if (err) 7128c2ecf20Sopenharmony_ci goto out; 7138c2ecf20Sopenharmony_ci if (ring->type == B43_DMA_64BIT) { 7148c2ecf20Sopenharmony_ci u64 ringbase = (u64) (ring->dmabase); 7158c2ecf20Sopenharmony_ci addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); 7168c2ecf20Sopenharmony_ci addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); 7178c2ecf20Sopenharmony_ci addrhi = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_HIGH); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci value = (ring->frameoffset << B43_DMA64_RXFROFF_SHIFT); 7208c2ecf20Sopenharmony_ci value |= B43_DMA64_RXENABLE; 7218c2ecf20Sopenharmony_ci value |= (addrext << B43_DMA64_RXADDREXT_SHIFT) 7228c2ecf20Sopenharmony_ci & B43_DMA64_RXADDREXT_MASK; 7238c2ecf20Sopenharmony_ci if (!parity) 7248c2ecf20Sopenharmony_ci value |= B43_DMA64_RXPARITYDISABLE; 7258c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_RXCTL, value); 7268c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_RXRINGLO, addrlo); 7278c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_RXRINGHI, addrhi); 7288c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_RXINDEX, ring->nr_slots * 7298c2ecf20Sopenharmony_ci sizeof(struct b43_dmadesc64)); 7308c2ecf20Sopenharmony_ci } else { 7318c2ecf20Sopenharmony_ci u32 ringbase = (u32) (ring->dmabase); 7328c2ecf20Sopenharmony_ci addrext = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_EXT); 7338c2ecf20Sopenharmony_ci addrlo = b43_dma_address(&ring->dev->dma, ringbase, B43_DMA_ADDR_LOW); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci value = (ring->frameoffset << B43_DMA32_RXFROFF_SHIFT); 7368c2ecf20Sopenharmony_ci value |= B43_DMA32_RXENABLE; 7378c2ecf20Sopenharmony_ci value |= (addrext << B43_DMA32_RXADDREXT_SHIFT) 7388c2ecf20Sopenharmony_ci & B43_DMA32_RXADDREXT_MASK; 7398c2ecf20Sopenharmony_ci if (!parity) 7408c2ecf20Sopenharmony_ci value |= B43_DMA32_RXPARITYDISABLE; 7418c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_RXCTL, value); 7428c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_RXRING, addrlo); 7438c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_RXINDEX, ring->nr_slots * 7448c2ecf20Sopenharmony_ci sizeof(struct b43_dmadesc32)); 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ciout: 7498c2ecf20Sopenharmony_ci return err; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci/* Shutdown the DMA controller. */ 7538c2ecf20Sopenharmony_cistatic void dmacontroller_cleanup(struct b43_dmaring *ring) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci if (ring->tx) { 7568c2ecf20Sopenharmony_ci b43_dmacontroller_tx_reset(ring->dev, ring->mmio_base, 7578c2ecf20Sopenharmony_ci ring->type); 7588c2ecf20Sopenharmony_ci if (ring->type == B43_DMA_64BIT) { 7598c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_TXRINGLO, 0); 7608c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_TXRINGHI, 0); 7618c2ecf20Sopenharmony_ci } else 7628c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_TXRING, 0); 7638c2ecf20Sopenharmony_ci } else { 7648c2ecf20Sopenharmony_ci b43_dmacontroller_rx_reset(ring->dev, ring->mmio_base, 7658c2ecf20Sopenharmony_ci ring->type); 7668c2ecf20Sopenharmony_ci if (ring->type == B43_DMA_64BIT) { 7678c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_RXRINGLO, 0); 7688c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA64_RXRINGHI, 0); 7698c2ecf20Sopenharmony_ci } else 7708c2ecf20Sopenharmony_ci b43_dma_write(ring, B43_DMA32_RXRING, 0); 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic void free_all_descbuffers(struct b43_dmaring *ring) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct b43_dmadesc_meta *meta; 7778c2ecf20Sopenharmony_ci int i; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci if (!ring->used_slots) 7808c2ecf20Sopenharmony_ci return; 7818c2ecf20Sopenharmony_ci for (i = 0; i < ring->nr_slots; i++) { 7828c2ecf20Sopenharmony_ci /* get meta - ignore returned value */ 7838c2ecf20Sopenharmony_ci ring->ops->idx2desc(ring, i, &meta); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) { 7868c2ecf20Sopenharmony_ci B43_WARN_ON(!ring->tx); 7878c2ecf20Sopenharmony_ci continue; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci if (ring->tx) { 7908c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 7918c2ecf20Sopenharmony_ci meta->skb->len, 1); 7928c2ecf20Sopenharmony_ci } else { 7938c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 7948c2ecf20Sopenharmony_ci ring->rx_buffersize, 0); 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci free_descriptor_buffer(ring, meta); 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic enum b43_dmatype b43_engine_type(struct b43_wldev *dev) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci u32 tmp; 8038c2ecf20Sopenharmony_ci u16 mmio_base; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci switch (dev->dev->bus_type) { 8068c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_BCMA 8078c2ecf20Sopenharmony_ci case B43_BUS_BCMA: 8088c2ecf20Sopenharmony_ci tmp = bcma_aread32(dev->dev->bdev, BCMA_IOST); 8098c2ecf20Sopenharmony_ci if (tmp & BCMA_IOST_DMA64) 8108c2ecf20Sopenharmony_ci return B43_DMA_64BIT; 8118c2ecf20Sopenharmony_ci break; 8128c2ecf20Sopenharmony_ci#endif 8138c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_SSB 8148c2ecf20Sopenharmony_ci case B43_BUS_SSB: 8158c2ecf20Sopenharmony_ci tmp = ssb_read32(dev->dev->sdev, SSB_TMSHIGH); 8168c2ecf20Sopenharmony_ci if (tmp & SSB_TMSHIGH_DMA64) 8178c2ecf20Sopenharmony_ci return B43_DMA_64BIT; 8188c2ecf20Sopenharmony_ci break; 8198c2ecf20Sopenharmony_ci#endif 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci mmio_base = b43_dmacontroller_base(0, 0); 8238c2ecf20Sopenharmony_ci b43_write32(dev, mmio_base + B43_DMA32_TXCTL, B43_DMA32_TXADDREXT_MASK); 8248c2ecf20Sopenharmony_ci tmp = b43_read32(dev, mmio_base + B43_DMA32_TXCTL); 8258c2ecf20Sopenharmony_ci if (tmp & B43_DMA32_TXADDREXT_MASK) 8268c2ecf20Sopenharmony_ci return B43_DMA_32BIT; 8278c2ecf20Sopenharmony_ci return B43_DMA_30BIT; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci/* Main initialization function. */ 8318c2ecf20Sopenharmony_cistatic 8328c2ecf20Sopenharmony_cistruct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev, 8338c2ecf20Sopenharmony_ci int controller_index, 8348c2ecf20Sopenharmony_ci int for_tx, 8358c2ecf20Sopenharmony_ci enum b43_dmatype type) 8368c2ecf20Sopenharmony_ci{ 8378c2ecf20Sopenharmony_ci struct b43_dmaring *ring; 8388c2ecf20Sopenharmony_ci int i, err; 8398c2ecf20Sopenharmony_ci dma_addr_t dma_test; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci ring = kzalloc(sizeof(*ring), GFP_KERNEL); 8428c2ecf20Sopenharmony_ci if (!ring) 8438c2ecf20Sopenharmony_ci goto out; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci ring->nr_slots = B43_RXRING_SLOTS; 8468c2ecf20Sopenharmony_ci if (for_tx) 8478c2ecf20Sopenharmony_ci ring->nr_slots = B43_TXRING_SLOTS; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci ring->meta = kcalloc(ring->nr_slots, sizeof(struct b43_dmadesc_meta), 8508c2ecf20Sopenharmony_ci GFP_KERNEL); 8518c2ecf20Sopenharmony_ci if (!ring->meta) 8528c2ecf20Sopenharmony_ci goto err_kfree_ring; 8538c2ecf20Sopenharmony_ci for (i = 0; i < ring->nr_slots; i++) 8548c2ecf20Sopenharmony_ci ring->meta->skb = B43_DMA_PTR_POISON; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci ring->type = type; 8578c2ecf20Sopenharmony_ci ring->dev = dev; 8588c2ecf20Sopenharmony_ci ring->mmio_base = b43_dmacontroller_base(type, controller_index); 8598c2ecf20Sopenharmony_ci ring->index = controller_index; 8608c2ecf20Sopenharmony_ci if (type == B43_DMA_64BIT) 8618c2ecf20Sopenharmony_ci ring->ops = &dma64_ops; 8628c2ecf20Sopenharmony_ci else 8638c2ecf20Sopenharmony_ci ring->ops = &dma32_ops; 8648c2ecf20Sopenharmony_ci if (for_tx) { 8658c2ecf20Sopenharmony_ci ring->tx = true; 8668c2ecf20Sopenharmony_ci ring->current_slot = -1; 8678c2ecf20Sopenharmony_ci } else { 8688c2ecf20Sopenharmony_ci if (ring->index == 0) { 8698c2ecf20Sopenharmony_ci switch (dev->fw.hdr_format) { 8708c2ecf20Sopenharmony_ci case B43_FW_HDR_598: 8718c2ecf20Sopenharmony_ci ring->rx_buffersize = B43_DMA0_RX_FW598_BUFSIZE; 8728c2ecf20Sopenharmony_ci ring->frameoffset = B43_DMA0_RX_FW598_FO; 8738c2ecf20Sopenharmony_ci break; 8748c2ecf20Sopenharmony_ci case B43_FW_HDR_410: 8758c2ecf20Sopenharmony_ci case B43_FW_HDR_351: 8768c2ecf20Sopenharmony_ci ring->rx_buffersize = B43_DMA0_RX_FW351_BUFSIZE; 8778c2ecf20Sopenharmony_ci ring->frameoffset = B43_DMA0_RX_FW351_FO; 8788c2ecf20Sopenharmony_ci break; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci } else 8818c2ecf20Sopenharmony_ci B43_WARN_ON(1); 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_DEBUG 8848c2ecf20Sopenharmony_ci ring->last_injected_overflow = jiffies; 8858c2ecf20Sopenharmony_ci#endif 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (for_tx) { 8888c2ecf20Sopenharmony_ci /* Assumption: B43_TXRING_SLOTS can be divided by TX_SLOTS_PER_FRAME */ 8898c2ecf20Sopenharmony_ci BUILD_BUG_ON(B43_TXRING_SLOTS % TX_SLOTS_PER_FRAME != 0); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci ring->txhdr_cache = kcalloc(ring->nr_slots / TX_SLOTS_PER_FRAME, 8928c2ecf20Sopenharmony_ci b43_txhdr_size(dev), 8938c2ecf20Sopenharmony_ci GFP_KERNEL); 8948c2ecf20Sopenharmony_ci if (!ring->txhdr_cache) 8958c2ecf20Sopenharmony_ci goto err_kfree_meta; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* test for ability to dma to txhdr_cache */ 8988c2ecf20Sopenharmony_ci dma_test = dma_map_single(dev->dev->dma_dev, 8998c2ecf20Sopenharmony_ci ring->txhdr_cache, 9008c2ecf20Sopenharmony_ci b43_txhdr_size(dev), 9018c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (b43_dma_mapping_error(ring, dma_test, 9048c2ecf20Sopenharmony_ci b43_txhdr_size(dev), 1)) { 9058c2ecf20Sopenharmony_ci /* ugh realloc */ 9068c2ecf20Sopenharmony_ci kfree(ring->txhdr_cache); 9078c2ecf20Sopenharmony_ci ring->txhdr_cache = kcalloc(ring->nr_slots / TX_SLOTS_PER_FRAME, 9088c2ecf20Sopenharmony_ci b43_txhdr_size(dev), 9098c2ecf20Sopenharmony_ci GFP_KERNEL | GFP_DMA); 9108c2ecf20Sopenharmony_ci if (!ring->txhdr_cache) 9118c2ecf20Sopenharmony_ci goto err_kfree_meta; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci dma_test = dma_map_single(dev->dev->dma_dev, 9148c2ecf20Sopenharmony_ci ring->txhdr_cache, 9158c2ecf20Sopenharmony_ci b43_txhdr_size(dev), 9168c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (b43_dma_mapping_error(ring, dma_test, 9198c2ecf20Sopenharmony_ci b43_txhdr_size(dev), 1)) { 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci b43err(dev->wl, 9228c2ecf20Sopenharmony_ci "TXHDR DMA allocation failed\n"); 9238c2ecf20Sopenharmony_ci goto err_kfree_txhdr_cache; 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci dma_unmap_single(dev->dev->dma_dev, 9288c2ecf20Sopenharmony_ci dma_test, b43_txhdr_size(dev), 9298c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci err = alloc_ringmemory(ring); 9338c2ecf20Sopenharmony_ci if (err) 9348c2ecf20Sopenharmony_ci goto err_kfree_txhdr_cache; 9358c2ecf20Sopenharmony_ci err = dmacontroller_setup(ring); 9368c2ecf20Sopenharmony_ci if (err) 9378c2ecf20Sopenharmony_ci goto err_free_ringmemory; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci out: 9408c2ecf20Sopenharmony_ci return ring; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci err_free_ringmemory: 9438c2ecf20Sopenharmony_ci free_ringmemory(ring); 9448c2ecf20Sopenharmony_ci err_kfree_txhdr_cache: 9458c2ecf20Sopenharmony_ci kfree(ring->txhdr_cache); 9468c2ecf20Sopenharmony_ci err_kfree_meta: 9478c2ecf20Sopenharmony_ci kfree(ring->meta); 9488c2ecf20Sopenharmony_ci err_kfree_ring: 9498c2ecf20Sopenharmony_ci kfree(ring); 9508c2ecf20Sopenharmony_ci ring = NULL; 9518c2ecf20Sopenharmony_ci goto out; 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci#define divide(a, b) ({ \ 9558c2ecf20Sopenharmony_ci typeof(a) __a = a; \ 9568c2ecf20Sopenharmony_ci do_div(__a, b); \ 9578c2ecf20Sopenharmony_ci __a; \ 9588c2ecf20Sopenharmony_ci }) 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci#define modulo(a, b) ({ \ 9618c2ecf20Sopenharmony_ci typeof(a) __a = a; \ 9628c2ecf20Sopenharmony_ci do_div(__a, b); \ 9638c2ecf20Sopenharmony_ci }) 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci/* Main cleanup function. */ 9668c2ecf20Sopenharmony_cistatic void b43_destroy_dmaring(struct b43_dmaring *ring, 9678c2ecf20Sopenharmony_ci const char *ringname) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci if (!ring) 9708c2ecf20Sopenharmony_ci return; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_DEBUG 9738c2ecf20Sopenharmony_ci { 9748c2ecf20Sopenharmony_ci /* Print some statistics. */ 9758c2ecf20Sopenharmony_ci u64 failed_packets = ring->nr_failed_tx_packets; 9768c2ecf20Sopenharmony_ci u64 succeed_packets = ring->nr_succeed_tx_packets; 9778c2ecf20Sopenharmony_ci u64 nr_packets = failed_packets + succeed_packets; 9788c2ecf20Sopenharmony_ci u64 permille_failed = 0, average_tries = 0; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (nr_packets) 9818c2ecf20Sopenharmony_ci permille_failed = divide(failed_packets * 1000, nr_packets); 9828c2ecf20Sopenharmony_ci if (nr_packets) 9838c2ecf20Sopenharmony_ci average_tries = divide(ring->nr_total_packet_tries * 100, nr_packets); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci b43dbg(ring->dev->wl, "DMA-%u %s: " 9868c2ecf20Sopenharmony_ci "Used slots %d/%d, Failed frames %llu/%llu = %llu.%01llu%%, " 9878c2ecf20Sopenharmony_ci "Average tries %llu.%02llu\n", 9888c2ecf20Sopenharmony_ci (unsigned int)(ring->type), ringname, 9898c2ecf20Sopenharmony_ci ring->max_used_slots, 9908c2ecf20Sopenharmony_ci ring->nr_slots, 9918c2ecf20Sopenharmony_ci (unsigned long long)failed_packets, 9928c2ecf20Sopenharmony_ci (unsigned long long)nr_packets, 9938c2ecf20Sopenharmony_ci (unsigned long long)divide(permille_failed, 10), 9948c2ecf20Sopenharmony_ci (unsigned long long)modulo(permille_failed, 10), 9958c2ecf20Sopenharmony_ci (unsigned long long)divide(average_tries, 100), 9968c2ecf20Sopenharmony_ci (unsigned long long)modulo(average_tries, 100)); 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci#endif /* DEBUG */ 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci /* Device IRQs are disabled prior entering this function, 10018c2ecf20Sopenharmony_ci * so no need to take care of concurrency with rx handler stuff. 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_ci dmacontroller_cleanup(ring); 10048c2ecf20Sopenharmony_ci free_all_descbuffers(ring); 10058c2ecf20Sopenharmony_ci free_ringmemory(ring); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci kfree(ring->txhdr_cache); 10088c2ecf20Sopenharmony_ci kfree(ring->meta); 10098c2ecf20Sopenharmony_ci kfree(ring); 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci#define destroy_ring(dma, ring) do { \ 10138c2ecf20Sopenharmony_ci b43_destroy_dmaring((dma)->ring, __stringify(ring)); \ 10148c2ecf20Sopenharmony_ci (dma)->ring = NULL; \ 10158c2ecf20Sopenharmony_ci } while (0) 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_civoid b43_dma_free(struct b43_wldev *dev) 10188c2ecf20Sopenharmony_ci{ 10198c2ecf20Sopenharmony_ci struct b43_dma *dma; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (b43_using_pio_transfers(dev)) 10228c2ecf20Sopenharmony_ci return; 10238c2ecf20Sopenharmony_ci dma = &dev->dma; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci destroy_ring(dma, rx_ring); 10268c2ecf20Sopenharmony_ci destroy_ring(dma, tx_ring_AC_BK); 10278c2ecf20Sopenharmony_ci destroy_ring(dma, tx_ring_AC_BE); 10288c2ecf20Sopenharmony_ci destroy_ring(dma, tx_ring_AC_VI); 10298c2ecf20Sopenharmony_ci destroy_ring(dma, tx_ring_AC_VO); 10308c2ecf20Sopenharmony_ci destroy_ring(dma, tx_ring_mcast); 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci/* Some hardware with 64-bit DMA seems to be bugged and looks for translation 10348c2ecf20Sopenharmony_ci * bit in low address word instead of high one. 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_cistatic bool b43_dma_translation_in_low_word(struct b43_wldev *dev, 10378c2ecf20Sopenharmony_ci enum b43_dmatype type) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci if (type != B43_DMA_64BIT) 10408c2ecf20Sopenharmony_ci return true; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_SSB 10438c2ecf20Sopenharmony_ci if (dev->dev->bus_type == B43_BUS_SSB && 10448c2ecf20Sopenharmony_ci dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI && 10458c2ecf20Sopenharmony_ci !(pci_is_pcie(dev->dev->sdev->bus->host_pci) && 10468c2ecf20Sopenharmony_ci ssb_read32(dev->dev->sdev, SSB_TMSHIGH) & SSB_TMSHIGH_DMA64)) 10478c2ecf20Sopenharmony_ci return true; 10488c2ecf20Sopenharmony_ci#endif 10498c2ecf20Sopenharmony_ci return false; 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ciint b43_dma_init(struct b43_wldev *dev) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci struct b43_dma *dma = &dev->dma; 10558c2ecf20Sopenharmony_ci enum b43_dmatype type = b43_engine_type(dev); 10568c2ecf20Sopenharmony_ci int err; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci err = dma_set_mask_and_coherent(dev->dev->dma_dev, DMA_BIT_MASK(type)); 10598c2ecf20Sopenharmony_ci if (err) { 10608c2ecf20Sopenharmony_ci b43err(dev->wl, "The machine/kernel does not support " 10618c2ecf20Sopenharmony_ci "the required %u-bit DMA mask\n", type); 10628c2ecf20Sopenharmony_ci return err; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci switch (dev->dev->bus_type) { 10668c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_BCMA 10678c2ecf20Sopenharmony_ci case B43_BUS_BCMA: 10688c2ecf20Sopenharmony_ci dma->translation = bcma_core_dma_translation(dev->dev->bdev); 10698c2ecf20Sopenharmony_ci break; 10708c2ecf20Sopenharmony_ci#endif 10718c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_SSB 10728c2ecf20Sopenharmony_ci case B43_BUS_SSB: 10738c2ecf20Sopenharmony_ci dma->translation = ssb_dma_translation(dev->dev->sdev); 10748c2ecf20Sopenharmony_ci break; 10758c2ecf20Sopenharmony_ci#endif 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci dma->translation_in_low = b43_dma_translation_in_low_word(dev, type); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci dma->parity = true; 10808c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_BCMA 10818c2ecf20Sopenharmony_ci /* TODO: find out which SSB devices need disabling parity */ 10828c2ecf20Sopenharmony_ci if (dev->dev->bus_type == B43_BUS_BCMA) 10838c2ecf20Sopenharmony_ci dma->parity = false; 10848c2ecf20Sopenharmony_ci#endif 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci err = -ENOMEM; 10878c2ecf20Sopenharmony_ci /* setup TX DMA channels. */ 10888c2ecf20Sopenharmony_ci dma->tx_ring_AC_BK = b43_setup_dmaring(dev, 0, 1, type); 10898c2ecf20Sopenharmony_ci if (!dma->tx_ring_AC_BK) 10908c2ecf20Sopenharmony_ci goto out; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci dma->tx_ring_AC_BE = b43_setup_dmaring(dev, 1, 1, type); 10938c2ecf20Sopenharmony_ci if (!dma->tx_ring_AC_BE) 10948c2ecf20Sopenharmony_ci goto err_destroy_bk; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci dma->tx_ring_AC_VI = b43_setup_dmaring(dev, 2, 1, type); 10978c2ecf20Sopenharmony_ci if (!dma->tx_ring_AC_VI) 10988c2ecf20Sopenharmony_ci goto err_destroy_be; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci dma->tx_ring_AC_VO = b43_setup_dmaring(dev, 3, 1, type); 11018c2ecf20Sopenharmony_ci if (!dma->tx_ring_AC_VO) 11028c2ecf20Sopenharmony_ci goto err_destroy_vi; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci dma->tx_ring_mcast = b43_setup_dmaring(dev, 4, 1, type); 11058c2ecf20Sopenharmony_ci if (!dma->tx_ring_mcast) 11068c2ecf20Sopenharmony_ci goto err_destroy_vo; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci /* setup RX DMA channel. */ 11098c2ecf20Sopenharmony_ci dma->rx_ring = b43_setup_dmaring(dev, 0, 0, type); 11108c2ecf20Sopenharmony_ci if (!dma->rx_ring) 11118c2ecf20Sopenharmony_ci goto err_destroy_mcast; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* No support for the TX status DMA ring. */ 11148c2ecf20Sopenharmony_ci B43_WARN_ON(dev->dev->core_rev < 5); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci b43dbg(dev->wl, "%u-bit DMA initialized\n", 11178c2ecf20Sopenharmony_ci (unsigned int)type); 11188c2ecf20Sopenharmony_ci err = 0; 11198c2ecf20Sopenharmony_ciout: 11208c2ecf20Sopenharmony_ci return err; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_cierr_destroy_mcast: 11238c2ecf20Sopenharmony_ci destroy_ring(dma, tx_ring_mcast); 11248c2ecf20Sopenharmony_cierr_destroy_vo: 11258c2ecf20Sopenharmony_ci destroy_ring(dma, tx_ring_AC_VO); 11268c2ecf20Sopenharmony_cierr_destroy_vi: 11278c2ecf20Sopenharmony_ci destroy_ring(dma, tx_ring_AC_VI); 11288c2ecf20Sopenharmony_cierr_destroy_be: 11298c2ecf20Sopenharmony_ci destroy_ring(dma, tx_ring_AC_BE); 11308c2ecf20Sopenharmony_cierr_destroy_bk: 11318c2ecf20Sopenharmony_ci destroy_ring(dma, tx_ring_AC_BK); 11328c2ecf20Sopenharmony_ci return err; 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci/* Generate a cookie for the TX header. */ 11368c2ecf20Sopenharmony_cistatic u16 generate_cookie(struct b43_dmaring *ring, int slot) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci u16 cookie; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci /* Use the upper 4 bits of the cookie as 11418c2ecf20Sopenharmony_ci * DMA controller ID and store the slot number 11428c2ecf20Sopenharmony_ci * in the lower 12 bits. 11438c2ecf20Sopenharmony_ci * Note that the cookie must never be 0, as this 11448c2ecf20Sopenharmony_ci * is a special value used in RX path. 11458c2ecf20Sopenharmony_ci * It can also not be 0xFFFF because that is special 11468c2ecf20Sopenharmony_ci * for multicast frames. 11478c2ecf20Sopenharmony_ci */ 11488c2ecf20Sopenharmony_ci cookie = (((u16)ring->index + 1) << 12); 11498c2ecf20Sopenharmony_ci B43_WARN_ON(slot & ~0x0FFF); 11508c2ecf20Sopenharmony_ci cookie |= (u16)slot; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci return cookie; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci/* Inspect a cookie and find out to which controller/slot it belongs. */ 11568c2ecf20Sopenharmony_cistatic 11578c2ecf20Sopenharmony_cistruct b43_dmaring *parse_cookie(struct b43_wldev *dev, u16 cookie, int *slot) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct b43_dma *dma = &dev->dma; 11608c2ecf20Sopenharmony_ci struct b43_dmaring *ring = NULL; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci switch (cookie & 0xF000) { 11638c2ecf20Sopenharmony_ci case 0x1000: 11648c2ecf20Sopenharmony_ci ring = dma->tx_ring_AC_BK; 11658c2ecf20Sopenharmony_ci break; 11668c2ecf20Sopenharmony_ci case 0x2000: 11678c2ecf20Sopenharmony_ci ring = dma->tx_ring_AC_BE; 11688c2ecf20Sopenharmony_ci break; 11698c2ecf20Sopenharmony_ci case 0x3000: 11708c2ecf20Sopenharmony_ci ring = dma->tx_ring_AC_VI; 11718c2ecf20Sopenharmony_ci break; 11728c2ecf20Sopenharmony_ci case 0x4000: 11738c2ecf20Sopenharmony_ci ring = dma->tx_ring_AC_VO; 11748c2ecf20Sopenharmony_ci break; 11758c2ecf20Sopenharmony_ci case 0x5000: 11768c2ecf20Sopenharmony_ci ring = dma->tx_ring_mcast; 11778c2ecf20Sopenharmony_ci break; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci *slot = (cookie & 0x0FFF); 11808c2ecf20Sopenharmony_ci if (unlikely(!ring || *slot < 0 || *slot >= ring->nr_slots)) { 11818c2ecf20Sopenharmony_ci b43dbg(dev->wl, "TX-status contains " 11828c2ecf20Sopenharmony_ci "invalid cookie: 0x%04X\n", cookie); 11838c2ecf20Sopenharmony_ci return NULL; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci return ring; 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic int dma_tx_fragment(struct b43_dmaring *ring, 11908c2ecf20Sopenharmony_ci struct sk_buff *skb) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci const struct b43_dma_ops *ops = ring->ops; 11938c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 11948c2ecf20Sopenharmony_ci struct b43_private_tx_info *priv_info = b43_get_priv_tx_info(info); 11958c2ecf20Sopenharmony_ci u8 *header; 11968c2ecf20Sopenharmony_ci int slot, old_top_slot, old_used_slots; 11978c2ecf20Sopenharmony_ci int err; 11988c2ecf20Sopenharmony_ci struct b43_dmadesc_generic *desc; 11998c2ecf20Sopenharmony_ci struct b43_dmadesc_meta *meta; 12008c2ecf20Sopenharmony_ci struct b43_dmadesc_meta *meta_hdr; 12018c2ecf20Sopenharmony_ci u16 cookie; 12028c2ecf20Sopenharmony_ci size_t hdrsize = b43_txhdr_size(ring->dev); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci /* Important note: If the number of used DMA slots per TX frame 12058c2ecf20Sopenharmony_ci * is changed here, the TX_SLOTS_PER_FRAME definition at the top of 12068c2ecf20Sopenharmony_ci * the file has to be updated, too! 12078c2ecf20Sopenharmony_ci */ 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci old_top_slot = ring->current_slot; 12108c2ecf20Sopenharmony_ci old_used_slots = ring->used_slots; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci /* Get a slot for the header. */ 12138c2ecf20Sopenharmony_ci slot = request_slot(ring); 12148c2ecf20Sopenharmony_ci desc = ops->idx2desc(ring, slot, &meta_hdr); 12158c2ecf20Sopenharmony_ci memset(meta_hdr, 0, sizeof(*meta_hdr)); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci header = &(ring->txhdr_cache[(slot / TX_SLOTS_PER_FRAME) * hdrsize]); 12188c2ecf20Sopenharmony_ci cookie = generate_cookie(ring, slot); 12198c2ecf20Sopenharmony_ci err = b43_generate_txhdr(ring->dev, header, 12208c2ecf20Sopenharmony_ci skb, info, cookie); 12218c2ecf20Sopenharmony_ci if (unlikely(err)) { 12228c2ecf20Sopenharmony_ci ring->current_slot = old_top_slot; 12238c2ecf20Sopenharmony_ci ring->used_slots = old_used_slots; 12248c2ecf20Sopenharmony_ci return err; 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, 12288c2ecf20Sopenharmony_ci hdrsize, 1); 12298c2ecf20Sopenharmony_ci if (b43_dma_mapping_error(ring, meta_hdr->dmaaddr, hdrsize, 1)) { 12308c2ecf20Sopenharmony_ci ring->current_slot = old_top_slot; 12318c2ecf20Sopenharmony_ci ring->used_slots = old_used_slots; 12328c2ecf20Sopenharmony_ci return -EIO; 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci ops->fill_descriptor(ring, desc, meta_hdr->dmaaddr, 12358c2ecf20Sopenharmony_ci hdrsize, 1, 0, 0); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci /* Get a slot for the payload. */ 12388c2ecf20Sopenharmony_ci slot = request_slot(ring); 12398c2ecf20Sopenharmony_ci desc = ops->idx2desc(ring, slot, &meta); 12408c2ecf20Sopenharmony_ci memset(meta, 0, sizeof(*meta)); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci meta->skb = skb; 12438c2ecf20Sopenharmony_ci meta->is_last_fragment = true; 12448c2ecf20Sopenharmony_ci priv_info->bouncebuffer = NULL; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); 12478c2ecf20Sopenharmony_ci /* create a bounce buffer in zone_dma on mapping failure. */ 12488c2ecf20Sopenharmony_ci if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { 12498c2ecf20Sopenharmony_ci priv_info->bouncebuffer = kmemdup(skb->data, skb->len, 12508c2ecf20Sopenharmony_ci GFP_ATOMIC | GFP_DMA); 12518c2ecf20Sopenharmony_ci if (!priv_info->bouncebuffer) { 12528c2ecf20Sopenharmony_ci ring->current_slot = old_top_slot; 12538c2ecf20Sopenharmony_ci ring->used_slots = old_used_slots; 12548c2ecf20Sopenharmony_ci err = -ENOMEM; 12558c2ecf20Sopenharmony_ci goto out_unmap_hdr; 12568c2ecf20Sopenharmony_ci } 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci meta->dmaaddr = map_descbuffer(ring, priv_info->bouncebuffer, skb->len, 1); 12598c2ecf20Sopenharmony_ci if (b43_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { 12608c2ecf20Sopenharmony_ci kfree(priv_info->bouncebuffer); 12618c2ecf20Sopenharmony_ci priv_info->bouncebuffer = NULL; 12628c2ecf20Sopenharmony_ci ring->current_slot = old_top_slot; 12638c2ecf20Sopenharmony_ci ring->used_slots = old_used_slots; 12648c2ecf20Sopenharmony_ci err = -EIO; 12658c2ecf20Sopenharmony_ci goto out_unmap_hdr; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci } 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci ops->fill_descriptor(ring, desc, meta->dmaaddr, skb->len, 0, 1, 1); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { 12728c2ecf20Sopenharmony_ci /* Tell the firmware about the cookie of the last 12738c2ecf20Sopenharmony_ci * mcast frame, so it can clear the more-data bit in it. */ 12748c2ecf20Sopenharmony_ci b43_shm_write16(ring->dev, B43_SHM_SHARED, 12758c2ecf20Sopenharmony_ci B43_SHM_SH_MCASTCOOKIE, cookie); 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci /* Now transfer the whole frame. */ 12788c2ecf20Sopenharmony_ci wmb(); 12798c2ecf20Sopenharmony_ci ops->poke_tx(ring, next_slot(ring, slot)); 12808c2ecf20Sopenharmony_ci return 0; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ciout_unmap_hdr: 12838c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta_hdr->dmaaddr, 12848c2ecf20Sopenharmony_ci hdrsize, 1); 12858c2ecf20Sopenharmony_ci return err; 12868c2ecf20Sopenharmony_ci} 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_cistatic inline int should_inject_overflow(struct b43_dmaring *ring) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_DEBUG 12918c2ecf20Sopenharmony_ci if (unlikely(b43_debug(ring->dev, B43_DBG_DMAOVERFLOW))) { 12928c2ecf20Sopenharmony_ci /* Check if we should inject another ringbuffer overflow 12938c2ecf20Sopenharmony_ci * to test handling of this situation in the stack. */ 12948c2ecf20Sopenharmony_ci unsigned long next_overflow; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci next_overflow = ring->last_injected_overflow + HZ; 12978c2ecf20Sopenharmony_ci if (time_after(jiffies, next_overflow)) { 12988c2ecf20Sopenharmony_ci ring->last_injected_overflow = jiffies; 12998c2ecf20Sopenharmony_ci b43dbg(ring->dev->wl, 13008c2ecf20Sopenharmony_ci "Injecting TX ring overflow on " 13018c2ecf20Sopenharmony_ci "DMA controller %d\n", ring->index); 13028c2ecf20Sopenharmony_ci return 1; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci } 13058c2ecf20Sopenharmony_ci#endif /* CONFIG_B43_DEBUG */ 13068c2ecf20Sopenharmony_ci return 0; 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci/* Static mapping of mac80211's queues (priorities) to b43 DMA rings. */ 13108c2ecf20Sopenharmony_cistatic struct b43_dmaring *select_ring_by_priority(struct b43_wldev *dev, 13118c2ecf20Sopenharmony_ci u8 queue_prio) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci struct b43_dmaring *ring; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (dev->qos_enabled) { 13168c2ecf20Sopenharmony_ci /* 0 = highest priority */ 13178c2ecf20Sopenharmony_ci switch (queue_prio) { 13188c2ecf20Sopenharmony_ci default: 13198c2ecf20Sopenharmony_ci B43_WARN_ON(1); 13208c2ecf20Sopenharmony_ci fallthrough; 13218c2ecf20Sopenharmony_ci case 0: 13228c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring_AC_VO; 13238c2ecf20Sopenharmony_ci break; 13248c2ecf20Sopenharmony_ci case 1: 13258c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring_AC_VI; 13268c2ecf20Sopenharmony_ci break; 13278c2ecf20Sopenharmony_ci case 2: 13288c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring_AC_BE; 13298c2ecf20Sopenharmony_ci break; 13308c2ecf20Sopenharmony_ci case 3: 13318c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring_AC_BK; 13328c2ecf20Sopenharmony_ci break; 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci } else 13358c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring_AC_BE; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci return ring; 13388c2ecf20Sopenharmony_ci} 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ciint b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci struct b43_dmaring *ring; 13438c2ecf20Sopenharmony_ci struct ieee80211_hdr *hdr; 13448c2ecf20Sopenharmony_ci int err = 0; 13458c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci hdr = (struct ieee80211_hdr *)skb->data; 13488c2ecf20Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { 13498c2ecf20Sopenharmony_ci /* The multicast ring will be sent after the DTIM */ 13508c2ecf20Sopenharmony_ci ring = dev->dma.tx_ring_mcast; 13518c2ecf20Sopenharmony_ci /* Set the more-data bit. Ucode will clear it on 13528c2ecf20Sopenharmony_ci * the last frame for us. */ 13538c2ecf20Sopenharmony_ci hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); 13548c2ecf20Sopenharmony_ci } else { 13558c2ecf20Sopenharmony_ci /* Decide by priority where to put this frame. */ 13568c2ecf20Sopenharmony_ci ring = select_ring_by_priority( 13578c2ecf20Sopenharmony_ci dev, skb_get_queue_mapping(skb)); 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci B43_WARN_ON(!ring->tx); 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci if (unlikely(ring->stopped)) { 13638c2ecf20Sopenharmony_ci /* We get here only because of a bug in mac80211. 13648c2ecf20Sopenharmony_ci * Because of a race, one packet may be queued after 13658c2ecf20Sopenharmony_ci * the queue is stopped, thus we got called when we shouldn't. 13668c2ecf20Sopenharmony_ci * For now, just refuse the transmit. */ 13678c2ecf20Sopenharmony_ci if (b43_debug(dev, B43_DBG_DMAVERBOSE)) 13688c2ecf20Sopenharmony_ci b43err(dev->wl, "Packet after queue stopped\n"); 13698c2ecf20Sopenharmony_ci err = -ENOSPC; 13708c2ecf20Sopenharmony_ci goto out; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME)) { 13748c2ecf20Sopenharmony_ci /* If we get here, we have a real error with the queue 13758c2ecf20Sopenharmony_ci * full, but queues not stopped. */ 13768c2ecf20Sopenharmony_ci b43err(dev->wl, "DMA queue overflow\n"); 13778c2ecf20Sopenharmony_ci err = -ENOSPC; 13788c2ecf20Sopenharmony_ci goto out; 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci /* Assign the queue number to the ring (if not already done before) 13828c2ecf20Sopenharmony_ci * so TX status handling can use it. The queue to ring mapping is 13838c2ecf20Sopenharmony_ci * static, so we don't need to store it per frame. */ 13848c2ecf20Sopenharmony_ci ring->queue_prio = skb_get_queue_mapping(skb); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci err = dma_tx_fragment(ring, skb); 13878c2ecf20Sopenharmony_ci if (unlikely(err == -ENOKEY)) { 13888c2ecf20Sopenharmony_ci /* Drop this packet, as we don't have the encryption key 13898c2ecf20Sopenharmony_ci * anymore and must not transmit it unencrypted. */ 13908c2ecf20Sopenharmony_ci ieee80211_free_txskb(dev->wl->hw, skb); 13918c2ecf20Sopenharmony_ci err = 0; 13928c2ecf20Sopenharmony_ci goto out; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci if (unlikely(err)) { 13958c2ecf20Sopenharmony_ci b43err(dev->wl, "DMA tx mapping failure\n"); 13968c2ecf20Sopenharmony_ci goto out; 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci if ((free_slots(ring) < TX_SLOTS_PER_FRAME) || 13998c2ecf20Sopenharmony_ci should_inject_overflow(ring)) { 14008c2ecf20Sopenharmony_ci /* This TX ring is full. */ 14018c2ecf20Sopenharmony_ci unsigned int skb_mapping = skb_get_queue_mapping(skb); 14028c2ecf20Sopenharmony_ci ieee80211_stop_queue(dev->wl->hw, skb_mapping); 14038c2ecf20Sopenharmony_ci dev->wl->tx_queue_stopped[skb_mapping] = true; 14048c2ecf20Sopenharmony_ci ring->stopped = true; 14058c2ecf20Sopenharmony_ci if (b43_debug(dev, B43_DBG_DMAVERBOSE)) { 14068c2ecf20Sopenharmony_ci b43dbg(dev->wl, "Stopped TX ring %d\n", ring->index); 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ciout: 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci return err; 14128c2ecf20Sopenharmony_ci} 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_civoid b43_dma_handle_txstatus(struct b43_wldev *dev, 14158c2ecf20Sopenharmony_ci const struct b43_txstatus *status) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci const struct b43_dma_ops *ops; 14188c2ecf20Sopenharmony_ci struct b43_dmaring *ring; 14198c2ecf20Sopenharmony_ci struct b43_dmadesc_meta *meta; 14208c2ecf20Sopenharmony_ci static const struct b43_txstatus fake; /* filled with 0 */ 14218c2ecf20Sopenharmony_ci const struct b43_txstatus *txstat; 14228c2ecf20Sopenharmony_ci int slot, firstused; 14238c2ecf20Sopenharmony_ci bool frame_succeed; 14248c2ecf20Sopenharmony_ci int skip; 14258c2ecf20Sopenharmony_ci static u8 err_out1; 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci ring = parse_cookie(dev, status->cookie, &slot); 14288c2ecf20Sopenharmony_ci if (unlikely(!ring)) 14298c2ecf20Sopenharmony_ci return; 14308c2ecf20Sopenharmony_ci B43_WARN_ON(!ring->tx); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci /* Sanity check: TX packets are processed in-order on one ring. 14338c2ecf20Sopenharmony_ci * Check if the slot deduced from the cookie really is the first 14348c2ecf20Sopenharmony_ci * used slot. */ 14358c2ecf20Sopenharmony_ci firstused = ring->current_slot - ring->used_slots + 1; 14368c2ecf20Sopenharmony_ci if (firstused < 0) 14378c2ecf20Sopenharmony_ci firstused = ring->nr_slots + firstused; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci skip = 0; 14408c2ecf20Sopenharmony_ci if (unlikely(slot != firstused)) { 14418c2ecf20Sopenharmony_ci /* This possibly is a firmware bug and will result in 14428c2ecf20Sopenharmony_ci * malfunction, memory leaks and/or stall of DMA functionality. 14438c2ecf20Sopenharmony_ci */ 14448c2ecf20Sopenharmony_ci if (slot == next_slot(ring, next_slot(ring, firstused))) { 14458c2ecf20Sopenharmony_ci /* If a single header/data pair was missed, skip over 14468c2ecf20Sopenharmony_ci * the first two slots in an attempt to recover. 14478c2ecf20Sopenharmony_ci */ 14488c2ecf20Sopenharmony_ci slot = firstused; 14498c2ecf20Sopenharmony_ci skip = 2; 14508c2ecf20Sopenharmony_ci if (!err_out1) { 14518c2ecf20Sopenharmony_ci /* Report the error once. */ 14528c2ecf20Sopenharmony_ci b43dbg(dev->wl, 14538c2ecf20Sopenharmony_ci "Skip on DMA ring %d slot %d.\n", 14548c2ecf20Sopenharmony_ci ring->index, slot); 14558c2ecf20Sopenharmony_ci err_out1 = 1; 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci } else { 14588c2ecf20Sopenharmony_ci /* More than a single header/data pair were missed. 14598c2ecf20Sopenharmony_ci * Report this error. If running with open-source 14608c2ecf20Sopenharmony_ci * firmware, then reset the controller to 14618c2ecf20Sopenharmony_ci * revive operation. 14628c2ecf20Sopenharmony_ci */ 14638c2ecf20Sopenharmony_ci b43dbg(dev->wl, 14648c2ecf20Sopenharmony_ci "Out of order TX status report on DMA ring %d. Expected %d, but got %d\n", 14658c2ecf20Sopenharmony_ci ring->index, firstused, slot); 14668c2ecf20Sopenharmony_ci if (dev->fw.opensource) 14678c2ecf20Sopenharmony_ci b43_controller_restart(dev, "Out of order TX"); 14688c2ecf20Sopenharmony_ci return; 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci ops = ring->ops; 14738c2ecf20Sopenharmony_ci while (1) { 14748c2ecf20Sopenharmony_ci B43_WARN_ON(slot < 0 || slot >= ring->nr_slots); 14758c2ecf20Sopenharmony_ci /* get meta - ignore returned value */ 14768c2ecf20Sopenharmony_ci ops->idx2desc(ring, slot, &meta); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci if (b43_dma_ptr_is_poisoned(meta->skb)) { 14798c2ecf20Sopenharmony_ci b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) " 14808c2ecf20Sopenharmony_ci "on ring %d\n", 14818c2ecf20Sopenharmony_ci slot, firstused, ring->index); 14828c2ecf20Sopenharmony_ci break; 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci if (meta->skb) { 14868c2ecf20Sopenharmony_ci struct b43_private_tx_info *priv_info = 14878c2ecf20Sopenharmony_ci b43_get_priv_tx_info(IEEE80211_SKB_CB(meta->skb)); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 14908c2ecf20Sopenharmony_ci meta->skb->len, 1); 14918c2ecf20Sopenharmony_ci kfree(priv_info->bouncebuffer); 14928c2ecf20Sopenharmony_ci priv_info->bouncebuffer = NULL; 14938c2ecf20Sopenharmony_ci } else { 14948c2ecf20Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 14958c2ecf20Sopenharmony_ci b43_txhdr_size(dev), 1); 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (meta->is_last_fragment) { 14998c2ecf20Sopenharmony_ci struct ieee80211_tx_info *info; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci if (unlikely(!meta->skb)) { 15028c2ecf20Sopenharmony_ci /* This is a scatter-gather fragment of a frame, 15038c2ecf20Sopenharmony_ci * so the skb pointer must not be NULL. 15048c2ecf20Sopenharmony_ci */ 15058c2ecf20Sopenharmony_ci b43dbg(dev->wl, "TX status unexpected NULL skb " 15068c2ecf20Sopenharmony_ci "at slot %d (first=%d) on ring %d\n", 15078c2ecf20Sopenharmony_ci slot, firstused, ring->index); 15088c2ecf20Sopenharmony_ci break; 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci info = IEEE80211_SKB_CB(meta->skb); 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci /* 15148c2ecf20Sopenharmony_ci * Call back to inform the ieee80211 subsystem about 15158c2ecf20Sopenharmony_ci * the status of the transmission. When skipping over 15168c2ecf20Sopenharmony_ci * a missed TX status report, use a status structure 15178c2ecf20Sopenharmony_ci * filled with zeros to indicate that the frame was not 15188c2ecf20Sopenharmony_ci * sent (frame_count 0) and not acknowledged 15198c2ecf20Sopenharmony_ci */ 15208c2ecf20Sopenharmony_ci if (unlikely(skip)) 15218c2ecf20Sopenharmony_ci txstat = &fake; 15228c2ecf20Sopenharmony_ci else 15238c2ecf20Sopenharmony_ci txstat = status; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci frame_succeed = b43_fill_txstatus_report(dev, info, 15268c2ecf20Sopenharmony_ci txstat); 15278c2ecf20Sopenharmony_ci#ifdef CONFIG_B43_DEBUG 15288c2ecf20Sopenharmony_ci if (frame_succeed) 15298c2ecf20Sopenharmony_ci ring->nr_succeed_tx_packets++; 15308c2ecf20Sopenharmony_ci else 15318c2ecf20Sopenharmony_ci ring->nr_failed_tx_packets++; 15328c2ecf20Sopenharmony_ci ring->nr_total_packet_tries += status->frame_count; 15338c2ecf20Sopenharmony_ci#endif /* DEBUG */ 15348c2ecf20Sopenharmony_ci ieee80211_tx_status(dev->wl->hw, meta->skb); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* skb will be freed by ieee80211_tx_status(). 15378c2ecf20Sopenharmony_ci * Poison our pointer. */ 15388c2ecf20Sopenharmony_ci meta->skb = B43_DMA_PTR_POISON; 15398c2ecf20Sopenharmony_ci } else { 15408c2ecf20Sopenharmony_ci /* No need to call free_descriptor_buffer here, as 15418c2ecf20Sopenharmony_ci * this is only the txhdr, which is not allocated. 15428c2ecf20Sopenharmony_ci */ 15438c2ecf20Sopenharmony_ci if (unlikely(meta->skb)) { 15448c2ecf20Sopenharmony_ci b43dbg(dev->wl, "TX status unexpected non-NULL skb " 15458c2ecf20Sopenharmony_ci "at slot %d (first=%d) on ring %d\n", 15468c2ecf20Sopenharmony_ci slot, firstused, ring->index); 15478c2ecf20Sopenharmony_ci break; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci /* Everything unmapped and free'd. So it's not used anymore. */ 15528c2ecf20Sopenharmony_ci ring->used_slots--; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci if (meta->is_last_fragment && !skip) { 15558c2ecf20Sopenharmony_ci /* This is the last scatter-gather 15568c2ecf20Sopenharmony_ci * fragment of the frame. We are done. */ 15578c2ecf20Sopenharmony_ci break; 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci slot = next_slot(ring, slot); 15608c2ecf20Sopenharmony_ci if (skip > 0) 15618c2ecf20Sopenharmony_ci --skip; 15628c2ecf20Sopenharmony_ci } 15638c2ecf20Sopenharmony_ci if (ring->stopped) { 15648c2ecf20Sopenharmony_ci B43_WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME); 15658c2ecf20Sopenharmony_ci ring->stopped = false; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci if (dev->wl->tx_queue_stopped[ring->queue_prio]) { 15698c2ecf20Sopenharmony_ci dev->wl->tx_queue_stopped[ring->queue_prio] = false; 15708c2ecf20Sopenharmony_ci } else { 15718c2ecf20Sopenharmony_ci /* If the driver queue is running wake the corresponding 15728c2ecf20Sopenharmony_ci * mac80211 queue. */ 15738c2ecf20Sopenharmony_ci ieee80211_wake_queue(dev->wl->hw, ring->queue_prio); 15748c2ecf20Sopenharmony_ci if (b43_debug(dev, B43_DBG_DMAVERBOSE)) { 15758c2ecf20Sopenharmony_ci b43dbg(dev->wl, "Woke up TX ring %d\n", ring->index); 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ci } 15788c2ecf20Sopenharmony_ci /* Add work to the queue. */ 15798c2ecf20Sopenharmony_ci ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work); 15808c2ecf20Sopenharmony_ci} 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_cistatic void dma_rx(struct b43_dmaring *ring, int *slot) 15838c2ecf20Sopenharmony_ci{ 15848c2ecf20Sopenharmony_ci const struct b43_dma_ops *ops = ring->ops; 15858c2ecf20Sopenharmony_ci struct b43_dmadesc_generic *desc; 15868c2ecf20Sopenharmony_ci struct b43_dmadesc_meta *meta; 15878c2ecf20Sopenharmony_ci struct b43_rxhdr_fw4 *rxhdr; 15888c2ecf20Sopenharmony_ci struct sk_buff *skb; 15898c2ecf20Sopenharmony_ci u16 len; 15908c2ecf20Sopenharmony_ci int err; 15918c2ecf20Sopenharmony_ci dma_addr_t dmaaddr; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci desc = ops->idx2desc(ring, *slot, &meta); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); 15968c2ecf20Sopenharmony_ci skb = meta->skb; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci rxhdr = (struct b43_rxhdr_fw4 *)skb->data; 15998c2ecf20Sopenharmony_ci len = le16_to_cpu(rxhdr->frame_len); 16008c2ecf20Sopenharmony_ci if (len == 0) { 16018c2ecf20Sopenharmony_ci int i = 0; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci do { 16048c2ecf20Sopenharmony_ci udelay(2); 16058c2ecf20Sopenharmony_ci barrier(); 16068c2ecf20Sopenharmony_ci len = le16_to_cpu(rxhdr->frame_len); 16078c2ecf20Sopenharmony_ci } while (len == 0 && i++ < 5); 16088c2ecf20Sopenharmony_ci if (unlikely(len == 0)) { 16098c2ecf20Sopenharmony_ci dmaaddr = meta->dmaaddr; 16108c2ecf20Sopenharmony_ci goto drop_recycle_buffer; 16118c2ecf20Sopenharmony_ci } 16128c2ecf20Sopenharmony_ci } 16138c2ecf20Sopenharmony_ci if (unlikely(b43_rx_buffer_is_poisoned(ring, skb))) { 16148c2ecf20Sopenharmony_ci /* Something went wrong with the DMA. 16158c2ecf20Sopenharmony_ci * The device did not touch the buffer and did not overwrite the poison. */ 16168c2ecf20Sopenharmony_ci b43dbg(ring->dev->wl, "DMA RX: Dropping poisoned buffer.\n"); 16178c2ecf20Sopenharmony_ci dmaaddr = meta->dmaaddr; 16188c2ecf20Sopenharmony_ci goto drop_recycle_buffer; 16198c2ecf20Sopenharmony_ci } 16208c2ecf20Sopenharmony_ci if (unlikely(len + ring->frameoffset > ring->rx_buffersize)) { 16218c2ecf20Sopenharmony_ci /* The data did not fit into one descriptor buffer 16228c2ecf20Sopenharmony_ci * and is split over multiple buffers. 16238c2ecf20Sopenharmony_ci * This should never happen, as we try to allocate buffers 16248c2ecf20Sopenharmony_ci * big enough. So simply ignore this packet. 16258c2ecf20Sopenharmony_ci */ 16268c2ecf20Sopenharmony_ci int cnt = 0; 16278c2ecf20Sopenharmony_ci s32 tmp = len; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci while (1) { 16308c2ecf20Sopenharmony_ci desc = ops->idx2desc(ring, *slot, &meta); 16318c2ecf20Sopenharmony_ci /* recycle the descriptor buffer. */ 16328c2ecf20Sopenharmony_ci b43_poison_rx_buffer(ring, meta->skb); 16338c2ecf20Sopenharmony_ci sync_descbuffer_for_device(ring, meta->dmaaddr, 16348c2ecf20Sopenharmony_ci ring->rx_buffersize); 16358c2ecf20Sopenharmony_ci *slot = next_slot(ring, *slot); 16368c2ecf20Sopenharmony_ci cnt++; 16378c2ecf20Sopenharmony_ci tmp -= ring->rx_buffersize; 16388c2ecf20Sopenharmony_ci if (tmp <= 0) 16398c2ecf20Sopenharmony_ci break; 16408c2ecf20Sopenharmony_ci } 16418c2ecf20Sopenharmony_ci b43err(ring->dev->wl, "DMA RX buffer too small " 16428c2ecf20Sopenharmony_ci "(len: %u, buffer: %u, nr-dropped: %d)\n", 16438c2ecf20Sopenharmony_ci len, ring->rx_buffersize, cnt); 16448c2ecf20Sopenharmony_ci goto drop; 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci dmaaddr = meta->dmaaddr; 16488c2ecf20Sopenharmony_ci err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); 16498c2ecf20Sopenharmony_ci if (unlikely(err)) { 16508c2ecf20Sopenharmony_ci b43dbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer() failed\n"); 16518c2ecf20Sopenharmony_ci goto drop_recycle_buffer; 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); 16558c2ecf20Sopenharmony_ci skb_put(skb, len + ring->frameoffset); 16568c2ecf20Sopenharmony_ci skb_pull(skb, ring->frameoffset); 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci b43_rx(ring->dev, skb, rxhdr); 16598c2ecf20Sopenharmony_cidrop: 16608c2ecf20Sopenharmony_ci return; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_cidrop_recycle_buffer: 16638c2ecf20Sopenharmony_ci /* Poison and recycle the RX buffer. */ 16648c2ecf20Sopenharmony_ci b43_poison_rx_buffer(ring, skb); 16658c2ecf20Sopenharmony_ci sync_descbuffer_for_device(ring, dmaaddr, ring->rx_buffersize); 16668c2ecf20Sopenharmony_ci} 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_civoid b43_dma_handle_rx_overflow(struct b43_dmaring *ring) 16698c2ecf20Sopenharmony_ci{ 16708c2ecf20Sopenharmony_ci int current_slot, previous_slot; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci B43_WARN_ON(ring->tx); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci /* Device has filled all buffers, drop all packets and let TCP 16758c2ecf20Sopenharmony_ci * decrease speed. 16768c2ecf20Sopenharmony_ci * Decrement RX index by one will let the device to see all slots 16778c2ecf20Sopenharmony_ci * as free again 16788c2ecf20Sopenharmony_ci */ 16798c2ecf20Sopenharmony_ci /* 16808c2ecf20Sopenharmony_ci *TODO: How to increase rx_drop in mac80211? 16818c2ecf20Sopenharmony_ci */ 16828c2ecf20Sopenharmony_ci current_slot = ring->ops->get_current_rxslot(ring); 16838c2ecf20Sopenharmony_ci previous_slot = prev_slot(ring, current_slot); 16848c2ecf20Sopenharmony_ci ring->ops->set_current_rxslot(ring, previous_slot); 16858c2ecf20Sopenharmony_ci} 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_civoid b43_dma_rx(struct b43_dmaring *ring) 16888c2ecf20Sopenharmony_ci{ 16898c2ecf20Sopenharmony_ci const struct b43_dma_ops *ops = ring->ops; 16908c2ecf20Sopenharmony_ci int slot, current_slot; 16918c2ecf20Sopenharmony_ci int used_slots = 0; 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci B43_WARN_ON(ring->tx); 16948c2ecf20Sopenharmony_ci current_slot = ops->get_current_rxslot(ring); 16958c2ecf20Sopenharmony_ci B43_WARN_ON(!(current_slot >= 0 && current_slot < ring->nr_slots)); 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_ci slot = ring->current_slot; 16988c2ecf20Sopenharmony_ci for (; slot != current_slot; slot = next_slot(ring, slot)) { 16998c2ecf20Sopenharmony_ci dma_rx(ring, &slot); 17008c2ecf20Sopenharmony_ci update_max_used_slots(ring, ++used_slots); 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci wmb(); 17038c2ecf20Sopenharmony_ci ops->set_current_rxslot(ring, slot); 17048c2ecf20Sopenharmony_ci ring->current_slot = slot; 17058c2ecf20Sopenharmony_ci} 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_cistatic void b43_dma_tx_suspend_ring(struct b43_dmaring *ring) 17088c2ecf20Sopenharmony_ci{ 17098c2ecf20Sopenharmony_ci B43_WARN_ON(!ring->tx); 17108c2ecf20Sopenharmony_ci ring->ops->tx_suspend(ring); 17118c2ecf20Sopenharmony_ci} 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_cistatic void b43_dma_tx_resume_ring(struct b43_dmaring *ring) 17148c2ecf20Sopenharmony_ci{ 17158c2ecf20Sopenharmony_ci B43_WARN_ON(!ring->tx); 17168c2ecf20Sopenharmony_ci ring->ops->tx_resume(ring); 17178c2ecf20Sopenharmony_ci} 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_civoid b43_dma_tx_suspend(struct b43_wldev *dev) 17208c2ecf20Sopenharmony_ci{ 17218c2ecf20Sopenharmony_ci b43_power_saving_ctl_bits(dev, B43_PS_AWAKE); 17228c2ecf20Sopenharmony_ci b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BK); 17238c2ecf20Sopenharmony_ci b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_BE); 17248c2ecf20Sopenharmony_ci b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VI); 17258c2ecf20Sopenharmony_ci b43_dma_tx_suspend_ring(dev->dma.tx_ring_AC_VO); 17268c2ecf20Sopenharmony_ci b43_dma_tx_suspend_ring(dev->dma.tx_ring_mcast); 17278c2ecf20Sopenharmony_ci} 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_civoid b43_dma_tx_resume(struct b43_wldev *dev) 17308c2ecf20Sopenharmony_ci{ 17318c2ecf20Sopenharmony_ci b43_dma_tx_resume_ring(dev->dma.tx_ring_mcast); 17328c2ecf20Sopenharmony_ci b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VO); 17338c2ecf20Sopenharmony_ci b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_VI); 17348c2ecf20Sopenharmony_ci b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BE); 17358c2ecf20Sopenharmony_ci b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BK); 17368c2ecf20Sopenharmony_ci b43_power_saving_ctl_bits(dev, 0); 17378c2ecf20Sopenharmony_ci} 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_cistatic void direct_fifo_rx(struct b43_wldev *dev, enum b43_dmatype type, 17408c2ecf20Sopenharmony_ci u16 mmio_base, bool enable) 17418c2ecf20Sopenharmony_ci{ 17428c2ecf20Sopenharmony_ci u32 ctl; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci if (type == B43_DMA_64BIT) { 17458c2ecf20Sopenharmony_ci ctl = b43_read32(dev, mmio_base + B43_DMA64_RXCTL); 17468c2ecf20Sopenharmony_ci ctl &= ~B43_DMA64_RXDIRECTFIFO; 17478c2ecf20Sopenharmony_ci if (enable) 17488c2ecf20Sopenharmony_ci ctl |= B43_DMA64_RXDIRECTFIFO; 17498c2ecf20Sopenharmony_ci b43_write32(dev, mmio_base + B43_DMA64_RXCTL, ctl); 17508c2ecf20Sopenharmony_ci } else { 17518c2ecf20Sopenharmony_ci ctl = b43_read32(dev, mmio_base + B43_DMA32_RXCTL); 17528c2ecf20Sopenharmony_ci ctl &= ~B43_DMA32_RXDIRECTFIFO; 17538c2ecf20Sopenharmony_ci if (enable) 17548c2ecf20Sopenharmony_ci ctl |= B43_DMA32_RXDIRECTFIFO; 17558c2ecf20Sopenharmony_ci b43_write32(dev, mmio_base + B43_DMA32_RXCTL, ctl); 17568c2ecf20Sopenharmony_ci } 17578c2ecf20Sopenharmony_ci} 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci/* Enable/Disable Direct FIFO Receive Mode (PIO) on a RX engine. 17608c2ecf20Sopenharmony_ci * This is called from PIO code, so DMA structures are not available. */ 17618c2ecf20Sopenharmony_civoid b43_dma_direct_fifo_rx(struct b43_wldev *dev, 17628c2ecf20Sopenharmony_ci unsigned int engine_index, bool enable) 17638c2ecf20Sopenharmony_ci{ 17648c2ecf20Sopenharmony_ci enum b43_dmatype type; 17658c2ecf20Sopenharmony_ci u16 mmio_base; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci type = b43_engine_type(dev); 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci mmio_base = b43_dmacontroller_base(type, engine_index); 17708c2ecf20Sopenharmony_ci direct_fifo_rx(dev, type, mmio_base, enable); 17718c2ecf20Sopenharmony_ci} 1772