162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci Broadcom B43legacy wireless driver 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci DMA ringbuffer and descriptor allocation/management 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci Copyright (c) 2005, 2006 Michael Buesch <m@bues.ch> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci Some code in this file is derived from the b44.c driver 1162306a36Sopenharmony_ci Copyright (C) 2002 David S. Miller 1262306a36Sopenharmony_ci Copyright (C) Pekka Pietikainen 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci*/ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "b43legacy.h" 1862306a36Sopenharmony_ci#include "dma.h" 1962306a36Sopenharmony_ci#include "main.h" 2062306a36Sopenharmony_ci#include "debugfs.h" 2162306a36Sopenharmony_ci#include "xmit.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2462306a36Sopenharmony_ci#include <linux/pci.h> 2562306a36Sopenharmony_ci#include <linux/delay.h> 2662306a36Sopenharmony_ci#include <linux/skbuff.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <net/dst.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 32bit DMA ops. */ 3162306a36Sopenharmony_cistatic 3262306a36Sopenharmony_cistruct b43legacy_dmadesc32 *op32_idx2desc(struct b43legacy_dmaring *ring, 3362306a36Sopenharmony_ci int slot, 3462306a36Sopenharmony_ci struct b43legacy_dmadesc_meta **meta) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct b43legacy_dmadesc32 *desc; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci *meta = &(ring->meta[slot]); 3962306a36Sopenharmony_ci desc = ring->descbase; 4062306a36Sopenharmony_ci desc = &(desc[slot]); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return desc; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void op32_fill_descriptor(struct b43legacy_dmaring *ring, 4662306a36Sopenharmony_ci struct b43legacy_dmadesc32 *desc, 4762306a36Sopenharmony_ci dma_addr_t dmaaddr, u16 bufsize, 4862306a36Sopenharmony_ci int start, int end, int irq) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct b43legacy_dmadesc32 *descbase = ring->descbase; 5162306a36Sopenharmony_ci int slot; 5262306a36Sopenharmony_ci u32 ctl; 5362306a36Sopenharmony_ci u32 addr; 5462306a36Sopenharmony_ci u32 addrext; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci slot = (int)(desc - descbase); 5762306a36Sopenharmony_ci B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci addr = (u32)(dmaaddr & ~SSB_DMA_TRANSLATION_MASK); 6062306a36Sopenharmony_ci addrext = (u32)(dmaaddr & SSB_DMA_TRANSLATION_MASK) 6162306a36Sopenharmony_ci >> SSB_DMA_TRANSLATION_SHIFT; 6262306a36Sopenharmony_ci addr |= ring->dev->dma.translation; 6362306a36Sopenharmony_ci ctl = (bufsize - ring->frameoffset) 6462306a36Sopenharmony_ci & B43legacy_DMA32_DCTL_BYTECNT; 6562306a36Sopenharmony_ci if (slot == ring->nr_slots - 1) 6662306a36Sopenharmony_ci ctl |= B43legacy_DMA32_DCTL_DTABLEEND; 6762306a36Sopenharmony_ci if (start) 6862306a36Sopenharmony_ci ctl |= B43legacy_DMA32_DCTL_FRAMESTART; 6962306a36Sopenharmony_ci if (end) 7062306a36Sopenharmony_ci ctl |= B43legacy_DMA32_DCTL_FRAMEEND; 7162306a36Sopenharmony_ci if (irq) 7262306a36Sopenharmony_ci ctl |= B43legacy_DMA32_DCTL_IRQ; 7362306a36Sopenharmony_ci ctl |= (addrext << B43legacy_DMA32_DCTL_ADDREXT_SHIFT) 7462306a36Sopenharmony_ci & B43legacy_DMA32_DCTL_ADDREXT_MASK; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci desc->control = cpu_to_le32(ctl); 7762306a36Sopenharmony_ci desc->address = cpu_to_le32(addr); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void op32_poke_tx(struct b43legacy_dmaring *ring, int slot) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXINDEX, 8362306a36Sopenharmony_ci (u32)(slot * sizeof(struct b43legacy_dmadesc32))); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void op32_tx_suspend(struct b43legacy_dmaring *ring) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, 8962306a36Sopenharmony_ci b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL) 9062306a36Sopenharmony_ci | B43legacy_DMA32_TXSUSPEND); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void op32_tx_resume(struct b43legacy_dmaring *ring) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, 9662306a36Sopenharmony_ci b43legacy_dma_read(ring, B43legacy_DMA32_TXCTL) 9762306a36Sopenharmony_ci & ~B43legacy_DMA32_TXSUSPEND); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int op32_get_current_rxslot(struct b43legacy_dmaring *ring) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci u32 val; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci val = b43legacy_dma_read(ring, B43legacy_DMA32_RXSTATUS); 10562306a36Sopenharmony_ci val &= B43legacy_DMA32_RXDPTR; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return (val / sizeof(struct b43legacy_dmadesc32)); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void op32_set_current_rxslot(struct b43legacy_dmaring *ring, 11162306a36Sopenharmony_ci int slot) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, 11462306a36Sopenharmony_ci (u32)(slot * sizeof(struct b43legacy_dmadesc32))); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic inline int free_slots(struct b43legacy_dmaring *ring) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci return (ring->nr_slots - ring->used_slots); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic inline int next_slot(struct b43legacy_dmaring *ring, int slot) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci B43legacy_WARN_ON(!(slot >= -1 && slot <= ring->nr_slots - 1)); 12562306a36Sopenharmony_ci if (slot == ring->nr_slots - 1) 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci return slot + 1; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#ifdef CONFIG_B43LEGACY_DEBUG 13162306a36Sopenharmony_cistatic void update_max_used_slots(struct b43legacy_dmaring *ring, 13262306a36Sopenharmony_ci int current_used_slots) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci if (current_used_slots <= ring->max_used_slots) 13562306a36Sopenharmony_ci return; 13662306a36Sopenharmony_ci ring->max_used_slots = current_used_slots; 13762306a36Sopenharmony_ci if (b43legacy_debug(ring->dev, B43legacy_DBG_DMAVERBOSE)) 13862306a36Sopenharmony_ci b43legacydbg(ring->dev->wl, 13962306a36Sopenharmony_ci "max_used_slots increased to %d on %s ring %d\n", 14062306a36Sopenharmony_ci ring->max_used_slots, 14162306a36Sopenharmony_ci ring->tx ? "TX" : "RX", 14262306a36Sopenharmony_ci ring->index); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci#else 14562306a36Sopenharmony_cistatic inline 14662306a36Sopenharmony_civoid update_max_used_slots(struct b43legacy_dmaring *ring, 14762306a36Sopenharmony_ci int current_used_slots) 14862306a36Sopenharmony_ci{ } 14962306a36Sopenharmony_ci#endif /* DEBUG */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* Request a slot for usage. */ 15262306a36Sopenharmony_cistatic inline 15362306a36Sopenharmony_ciint request_slot(struct b43legacy_dmaring *ring) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int slot; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 15862306a36Sopenharmony_ci B43legacy_WARN_ON(ring->stopped); 15962306a36Sopenharmony_ci B43legacy_WARN_ON(free_slots(ring) == 0); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci slot = next_slot(ring, ring->current_slot); 16262306a36Sopenharmony_ci ring->current_slot = slot; 16362306a36Sopenharmony_ci ring->used_slots++; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci update_max_used_slots(ring, ring->used_slots); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return slot; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* Mac80211-queue to b43legacy-ring mapping */ 17162306a36Sopenharmony_cistatic struct b43legacy_dmaring *priority_to_txring( 17262306a36Sopenharmony_ci struct b43legacy_wldev *dev, 17362306a36Sopenharmony_ci int queue_priority) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct b43legacy_dmaring *ring; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/*FIXME: For now we always run on TX-ring-1 */ 17862306a36Sopenharmony_cireturn dev->dma.tx_ring1; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* 0 = highest priority */ 18162306a36Sopenharmony_ci switch (queue_priority) { 18262306a36Sopenharmony_ci default: 18362306a36Sopenharmony_ci B43legacy_WARN_ON(1); 18462306a36Sopenharmony_ci fallthrough; 18562306a36Sopenharmony_ci case 0: 18662306a36Sopenharmony_ci ring = dev->dma.tx_ring3; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci case 1: 18962306a36Sopenharmony_ci ring = dev->dma.tx_ring2; 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci case 2: 19262306a36Sopenharmony_ci ring = dev->dma.tx_ring1; 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci case 3: 19562306a36Sopenharmony_ci ring = dev->dma.tx_ring0; 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci case 4: 19862306a36Sopenharmony_ci ring = dev->dma.tx_ring4; 19962306a36Sopenharmony_ci break; 20062306a36Sopenharmony_ci case 5: 20162306a36Sopenharmony_ci ring = dev->dma.tx_ring5; 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return ring; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic u16 b43legacy_dmacontroller_base(enum b43legacy_dmatype type, 20962306a36Sopenharmony_ci int controller_idx) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci static const u16 map32[] = { 21262306a36Sopenharmony_ci B43legacy_MMIO_DMA32_BASE0, 21362306a36Sopenharmony_ci B43legacy_MMIO_DMA32_BASE1, 21462306a36Sopenharmony_ci B43legacy_MMIO_DMA32_BASE2, 21562306a36Sopenharmony_ci B43legacy_MMIO_DMA32_BASE3, 21662306a36Sopenharmony_ci B43legacy_MMIO_DMA32_BASE4, 21762306a36Sopenharmony_ci B43legacy_MMIO_DMA32_BASE5, 21862306a36Sopenharmony_ci }; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci B43legacy_WARN_ON(!(controller_idx >= 0 && 22162306a36Sopenharmony_ci controller_idx < ARRAY_SIZE(map32))); 22262306a36Sopenharmony_ci return map32[controller_idx]; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic inline 22662306a36Sopenharmony_cidma_addr_t map_descbuffer(struct b43legacy_dmaring *ring, 22762306a36Sopenharmony_ci unsigned char *buf, 22862306a36Sopenharmony_ci size_t len, 22962306a36Sopenharmony_ci int tx) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci dma_addr_t dmaaddr; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (tx) 23462306a36Sopenharmony_ci dmaaddr = dma_map_single(ring->dev->dev->dma_dev, 23562306a36Sopenharmony_ci buf, len, 23662306a36Sopenharmony_ci DMA_TO_DEVICE); 23762306a36Sopenharmony_ci else 23862306a36Sopenharmony_ci dmaaddr = dma_map_single(ring->dev->dev->dma_dev, 23962306a36Sopenharmony_ci buf, len, 24062306a36Sopenharmony_ci DMA_FROM_DEVICE); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return dmaaddr; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic inline 24662306a36Sopenharmony_civoid unmap_descbuffer(struct b43legacy_dmaring *ring, 24762306a36Sopenharmony_ci dma_addr_t addr, 24862306a36Sopenharmony_ci size_t len, 24962306a36Sopenharmony_ci int tx) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci if (tx) 25262306a36Sopenharmony_ci dma_unmap_single(ring->dev->dev->dma_dev, 25362306a36Sopenharmony_ci addr, len, 25462306a36Sopenharmony_ci DMA_TO_DEVICE); 25562306a36Sopenharmony_ci else 25662306a36Sopenharmony_ci dma_unmap_single(ring->dev->dev->dma_dev, 25762306a36Sopenharmony_ci addr, len, 25862306a36Sopenharmony_ci DMA_FROM_DEVICE); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic inline 26262306a36Sopenharmony_civoid sync_descbuffer_for_cpu(struct b43legacy_dmaring *ring, 26362306a36Sopenharmony_ci dma_addr_t addr, 26462306a36Sopenharmony_ci size_t len) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci B43legacy_WARN_ON(ring->tx); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci dma_sync_single_for_cpu(ring->dev->dev->dma_dev, 26962306a36Sopenharmony_ci addr, len, DMA_FROM_DEVICE); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic inline 27362306a36Sopenharmony_civoid sync_descbuffer_for_device(struct b43legacy_dmaring *ring, 27462306a36Sopenharmony_ci dma_addr_t addr, 27562306a36Sopenharmony_ci size_t len) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci B43legacy_WARN_ON(ring->tx); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci dma_sync_single_for_device(ring->dev->dev->dma_dev, 28062306a36Sopenharmony_ci addr, len, DMA_FROM_DEVICE); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic inline 28462306a36Sopenharmony_civoid free_descriptor_buffer(struct b43legacy_dmaring *ring, 28562306a36Sopenharmony_ci struct b43legacy_dmadesc_meta *meta, 28662306a36Sopenharmony_ci int irq_context) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci if (meta->skb) { 28962306a36Sopenharmony_ci if (irq_context) 29062306a36Sopenharmony_ci dev_kfree_skb_irq(meta->skb); 29162306a36Sopenharmony_ci else 29262306a36Sopenharmony_ci dev_kfree_skb(meta->skb); 29362306a36Sopenharmony_ci meta->skb = NULL; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic int alloc_ringmemory(struct b43legacy_dmaring *ring) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci /* GFP flags must match the flags in free_ringmemory()! */ 30062306a36Sopenharmony_ci ring->descbase = dma_alloc_coherent(ring->dev->dev->dma_dev, 30162306a36Sopenharmony_ci B43legacy_DMA_RINGMEMSIZE, 30262306a36Sopenharmony_ci &(ring->dmabase), GFP_KERNEL); 30362306a36Sopenharmony_ci if (!ring->descbase) 30462306a36Sopenharmony_ci return -ENOMEM; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void free_ringmemory(struct b43legacy_dmaring *ring) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci dma_free_coherent(ring->dev->dev->dma_dev, B43legacy_DMA_RINGMEMSIZE, 31262306a36Sopenharmony_ci ring->descbase, ring->dmabase); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* Reset the RX DMA channel */ 31662306a36Sopenharmony_cistatic int b43legacy_dmacontroller_rx_reset(struct b43legacy_wldev *dev, 31762306a36Sopenharmony_ci u16 mmio_base, 31862306a36Sopenharmony_ci enum b43legacy_dmatype type) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci int i; 32162306a36Sopenharmony_ci u32 value; 32262306a36Sopenharmony_ci u16 offset; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci might_sleep(); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci offset = B43legacy_DMA32_RXCTL; 32762306a36Sopenharmony_ci b43legacy_write32(dev, mmio_base + offset, 0); 32862306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 32962306a36Sopenharmony_ci offset = B43legacy_DMA32_RXSTATUS; 33062306a36Sopenharmony_ci value = b43legacy_read32(dev, mmio_base + offset); 33162306a36Sopenharmony_ci value &= B43legacy_DMA32_RXSTATE; 33262306a36Sopenharmony_ci if (value == B43legacy_DMA32_RXSTAT_DISABLED) { 33362306a36Sopenharmony_ci i = -1; 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci msleep(1); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci if (i != -1) { 33962306a36Sopenharmony_ci b43legacyerr(dev->wl, "DMA RX reset timed out\n"); 34062306a36Sopenharmony_ci return -ENODEV; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return 0; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/* Reset the RX DMA channel */ 34762306a36Sopenharmony_cistatic int b43legacy_dmacontroller_tx_reset(struct b43legacy_wldev *dev, 34862306a36Sopenharmony_ci u16 mmio_base, 34962306a36Sopenharmony_ci enum b43legacy_dmatype type) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci int i; 35262306a36Sopenharmony_ci u32 value; 35362306a36Sopenharmony_ci u16 offset; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci might_sleep(); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 35862306a36Sopenharmony_ci offset = B43legacy_DMA32_TXSTATUS; 35962306a36Sopenharmony_ci value = b43legacy_read32(dev, mmio_base + offset); 36062306a36Sopenharmony_ci value &= B43legacy_DMA32_TXSTATE; 36162306a36Sopenharmony_ci if (value == B43legacy_DMA32_TXSTAT_DISABLED || 36262306a36Sopenharmony_ci value == B43legacy_DMA32_TXSTAT_IDLEWAIT || 36362306a36Sopenharmony_ci value == B43legacy_DMA32_TXSTAT_STOPPED) 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci msleep(1); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci offset = B43legacy_DMA32_TXCTL; 36862306a36Sopenharmony_ci b43legacy_write32(dev, mmio_base + offset, 0); 36962306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 37062306a36Sopenharmony_ci offset = B43legacy_DMA32_TXSTATUS; 37162306a36Sopenharmony_ci value = b43legacy_read32(dev, mmio_base + offset); 37262306a36Sopenharmony_ci value &= B43legacy_DMA32_TXSTATE; 37362306a36Sopenharmony_ci if (value == B43legacy_DMA32_TXSTAT_DISABLED) { 37462306a36Sopenharmony_ci i = -1; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci msleep(1); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci if (i != -1) { 38062306a36Sopenharmony_ci b43legacyerr(dev->wl, "DMA TX reset timed out\n"); 38162306a36Sopenharmony_ci return -ENODEV; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci /* ensure the reset is completed. */ 38462306a36Sopenharmony_ci msleep(1); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/* Check if a DMA mapping address is invalid. */ 39062306a36Sopenharmony_cistatic bool b43legacy_dma_mapping_error(struct b43legacy_dmaring *ring, 39162306a36Sopenharmony_ci dma_addr_t addr, 39262306a36Sopenharmony_ci size_t buffersize, 39362306a36Sopenharmony_ci bool dma_to_device) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci if (unlikely(dma_mapping_error(ring->dev->dev->dma_dev, addr))) 39662306a36Sopenharmony_ci return true; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci switch (ring->type) { 39962306a36Sopenharmony_ci case B43legacy_DMA_30BIT: 40062306a36Sopenharmony_ci if ((u64)addr + buffersize > (1ULL << 30)) 40162306a36Sopenharmony_ci goto address_error; 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci case B43legacy_DMA_32BIT: 40462306a36Sopenharmony_ci if ((u64)addr + buffersize > (1ULL << 32)) 40562306a36Sopenharmony_ci goto address_error; 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* The address is OK. */ 41062306a36Sopenharmony_ci return false; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ciaddress_error: 41362306a36Sopenharmony_ci /* We can't support this address. Unmap it again. */ 41462306a36Sopenharmony_ci unmap_descbuffer(ring, addr, buffersize, dma_to_device); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci return true; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int setup_rx_descbuffer(struct b43legacy_dmaring *ring, 42062306a36Sopenharmony_ci struct b43legacy_dmadesc32 *desc, 42162306a36Sopenharmony_ci struct b43legacy_dmadesc_meta *meta, 42262306a36Sopenharmony_ci gfp_t gfp_flags) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct b43legacy_rxhdr_fw3 *rxhdr; 42562306a36Sopenharmony_ci struct b43legacy_hwtxstatus *txstat; 42662306a36Sopenharmony_ci dma_addr_t dmaaddr; 42762306a36Sopenharmony_ci struct sk_buff *skb; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci B43legacy_WARN_ON(ring->tx); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); 43262306a36Sopenharmony_ci if (unlikely(!skb)) 43362306a36Sopenharmony_ci return -ENOMEM; 43462306a36Sopenharmony_ci dmaaddr = map_descbuffer(ring, skb->data, 43562306a36Sopenharmony_ci ring->rx_buffersize, 0); 43662306a36Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { 43762306a36Sopenharmony_ci /* ugh. try to realloc in zone_dma */ 43862306a36Sopenharmony_ci gfp_flags |= GFP_DMA; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); 44362306a36Sopenharmony_ci if (unlikely(!skb)) 44462306a36Sopenharmony_ci return -ENOMEM; 44562306a36Sopenharmony_ci dmaaddr = map_descbuffer(ring, skb->data, 44662306a36Sopenharmony_ci ring->rx_buffersize, 0); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, dmaaddr, ring->rx_buffersize, 0)) { 45062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 45162306a36Sopenharmony_ci return -EIO; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci meta->skb = skb; 45562306a36Sopenharmony_ci meta->dmaaddr = dmaaddr; 45662306a36Sopenharmony_ci op32_fill_descriptor(ring, desc, dmaaddr, ring->rx_buffersize, 0, 0, 0); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci rxhdr = (struct b43legacy_rxhdr_fw3 *)(skb->data); 45962306a36Sopenharmony_ci rxhdr->frame_len = 0; 46062306a36Sopenharmony_ci txstat = (struct b43legacy_hwtxstatus *)(skb->data); 46162306a36Sopenharmony_ci txstat->cookie = 0; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/* Allocate the initial descbuffers. 46762306a36Sopenharmony_ci * This is used for an RX ring only. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_cistatic int alloc_initial_descbuffers(struct b43legacy_dmaring *ring) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci int i; 47262306a36Sopenharmony_ci int err = -ENOMEM; 47362306a36Sopenharmony_ci struct b43legacy_dmadesc32 *desc; 47462306a36Sopenharmony_ci struct b43legacy_dmadesc_meta *meta; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci for (i = 0; i < ring->nr_slots; i++) { 47762306a36Sopenharmony_ci desc = op32_idx2desc(ring, i, &meta); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); 48062306a36Sopenharmony_ci if (err) { 48162306a36Sopenharmony_ci b43legacyerr(ring->dev->wl, 48262306a36Sopenharmony_ci "Failed to allocate initial descbuffers\n"); 48362306a36Sopenharmony_ci goto err_unwind; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci mb(); /* all descbuffer setup before next line */ 48762306a36Sopenharmony_ci ring->used_slots = ring->nr_slots; 48862306a36Sopenharmony_ci err = 0; 48962306a36Sopenharmony_ciout: 49062306a36Sopenharmony_ci return err; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cierr_unwind: 49362306a36Sopenharmony_ci for (i--; i >= 0; i--) { 49462306a36Sopenharmony_ci desc = op32_idx2desc(ring, i, &meta); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); 49762306a36Sopenharmony_ci dev_kfree_skb(meta->skb); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci goto out; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/* Do initial setup of the DMA controller. 50362306a36Sopenharmony_ci * Reset the controller, write the ring busaddress 50462306a36Sopenharmony_ci * and switch the "enable" bit on. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_cistatic int dmacontroller_setup(struct b43legacy_dmaring *ring) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci int err = 0; 50962306a36Sopenharmony_ci u32 value; 51062306a36Sopenharmony_ci u32 addrext; 51162306a36Sopenharmony_ci u32 trans = ring->dev->dma.translation; 51262306a36Sopenharmony_ci u32 ringbase = (u32)(ring->dmabase); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (ring->tx) { 51562306a36Sopenharmony_ci addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) 51662306a36Sopenharmony_ci >> SSB_DMA_TRANSLATION_SHIFT; 51762306a36Sopenharmony_ci value = B43legacy_DMA32_TXENABLE; 51862306a36Sopenharmony_ci value |= (addrext << B43legacy_DMA32_TXADDREXT_SHIFT) 51962306a36Sopenharmony_ci & B43legacy_DMA32_TXADDREXT_MASK; 52062306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXCTL, value); 52162306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, 52262306a36Sopenharmony_ci (ringbase & ~SSB_DMA_TRANSLATION_MASK) 52362306a36Sopenharmony_ci | trans); 52462306a36Sopenharmony_ci } else { 52562306a36Sopenharmony_ci err = alloc_initial_descbuffers(ring); 52662306a36Sopenharmony_ci if (err) 52762306a36Sopenharmony_ci goto out; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci addrext = (ringbase & SSB_DMA_TRANSLATION_MASK) 53062306a36Sopenharmony_ci >> SSB_DMA_TRANSLATION_SHIFT; 53162306a36Sopenharmony_ci value = (ring->frameoffset << 53262306a36Sopenharmony_ci B43legacy_DMA32_RXFROFF_SHIFT); 53362306a36Sopenharmony_ci value |= B43legacy_DMA32_RXENABLE; 53462306a36Sopenharmony_ci value |= (addrext << B43legacy_DMA32_RXADDREXT_SHIFT) 53562306a36Sopenharmony_ci & B43legacy_DMA32_RXADDREXT_MASK; 53662306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_RXCTL, value); 53762306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, 53862306a36Sopenharmony_ci (ringbase & ~SSB_DMA_TRANSLATION_MASK) 53962306a36Sopenharmony_ci | trans); 54062306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_RXINDEX, 200); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ciout: 54462306a36Sopenharmony_ci return err; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/* Shutdown the DMA controller. */ 54862306a36Sopenharmony_cistatic void dmacontroller_cleanup(struct b43legacy_dmaring *ring) 54962306a36Sopenharmony_ci{ 55062306a36Sopenharmony_ci if (ring->tx) { 55162306a36Sopenharmony_ci b43legacy_dmacontroller_tx_reset(ring->dev, ring->mmio_base, 55262306a36Sopenharmony_ci ring->type); 55362306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_TXRING, 0); 55462306a36Sopenharmony_ci } else { 55562306a36Sopenharmony_ci b43legacy_dmacontroller_rx_reset(ring->dev, ring->mmio_base, 55662306a36Sopenharmony_ci ring->type); 55762306a36Sopenharmony_ci b43legacy_dma_write(ring, B43legacy_DMA32_RXRING, 0); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic void free_all_descbuffers(struct b43legacy_dmaring *ring) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci struct b43legacy_dmadesc_meta *meta; 56462306a36Sopenharmony_ci int i; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (!ring->used_slots) 56762306a36Sopenharmony_ci return; 56862306a36Sopenharmony_ci for (i = 0; i < ring->nr_slots; i++) { 56962306a36Sopenharmony_ci op32_idx2desc(ring, i, &meta); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (!meta->skb) { 57262306a36Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 57362306a36Sopenharmony_ci continue; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci if (ring->tx) 57662306a36Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 57762306a36Sopenharmony_ci meta->skb->len, 1); 57862306a36Sopenharmony_ci else 57962306a36Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 58062306a36Sopenharmony_ci ring->rx_buffersize, 0); 58162306a36Sopenharmony_ci free_descriptor_buffer(ring, meta, 0); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic enum b43legacy_dmatype b43legacy_engine_type(struct b43legacy_wldev *dev) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci u32 tmp; 58862306a36Sopenharmony_ci u16 mmio_base; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci mmio_base = b43legacy_dmacontroller_base(0, 0); 59162306a36Sopenharmony_ci b43legacy_write32(dev, 59262306a36Sopenharmony_ci mmio_base + B43legacy_DMA32_TXCTL, 59362306a36Sopenharmony_ci B43legacy_DMA32_TXADDREXT_MASK); 59462306a36Sopenharmony_ci tmp = b43legacy_read32(dev, mmio_base + 59562306a36Sopenharmony_ci B43legacy_DMA32_TXCTL); 59662306a36Sopenharmony_ci if (tmp & B43legacy_DMA32_TXADDREXT_MASK) 59762306a36Sopenharmony_ci return B43legacy_DMA_32BIT; 59862306a36Sopenharmony_ci return B43legacy_DMA_30BIT; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/* Main initialization function. */ 60262306a36Sopenharmony_cistatic 60362306a36Sopenharmony_cistruct b43legacy_dmaring *b43legacy_setup_dmaring(struct b43legacy_wldev *dev, 60462306a36Sopenharmony_ci int controller_index, 60562306a36Sopenharmony_ci int for_tx, 60662306a36Sopenharmony_ci enum b43legacy_dmatype type) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct b43legacy_dmaring *ring; 60962306a36Sopenharmony_ci int err; 61062306a36Sopenharmony_ci int nr_slots; 61162306a36Sopenharmony_ci dma_addr_t dma_test; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci ring = kzalloc(sizeof(*ring), GFP_KERNEL); 61462306a36Sopenharmony_ci if (!ring) 61562306a36Sopenharmony_ci goto out; 61662306a36Sopenharmony_ci ring->type = type; 61762306a36Sopenharmony_ci ring->dev = dev; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci nr_slots = B43legacy_RXRING_SLOTS; 62062306a36Sopenharmony_ci if (for_tx) 62162306a36Sopenharmony_ci nr_slots = B43legacy_TXRING_SLOTS; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci ring->meta = kcalloc(nr_slots, sizeof(struct b43legacy_dmadesc_meta), 62462306a36Sopenharmony_ci GFP_KERNEL); 62562306a36Sopenharmony_ci if (!ring->meta) 62662306a36Sopenharmony_ci goto err_kfree_ring; 62762306a36Sopenharmony_ci if (for_tx) { 62862306a36Sopenharmony_ci ring->txhdr_cache = kcalloc(nr_slots, 62962306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 63062306a36Sopenharmony_ci GFP_KERNEL); 63162306a36Sopenharmony_ci if (!ring->txhdr_cache) 63262306a36Sopenharmony_ci goto err_kfree_meta; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* test for ability to dma to txhdr_cache */ 63562306a36Sopenharmony_ci dma_test = dma_map_single(dev->dev->dma_dev, ring->txhdr_cache, 63662306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 63762306a36Sopenharmony_ci DMA_TO_DEVICE); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, dma_test, 64062306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1)) { 64162306a36Sopenharmony_ci /* ugh realloc */ 64262306a36Sopenharmony_ci kfree(ring->txhdr_cache); 64362306a36Sopenharmony_ci ring->txhdr_cache = kcalloc(nr_slots, 64462306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 64562306a36Sopenharmony_ci GFP_KERNEL | GFP_DMA); 64662306a36Sopenharmony_ci if (!ring->txhdr_cache) 64762306a36Sopenharmony_ci goto err_kfree_meta; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci dma_test = dma_map_single(dev->dev->dma_dev, 65062306a36Sopenharmony_ci ring->txhdr_cache, 65162306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 65262306a36Sopenharmony_ci DMA_TO_DEVICE); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, dma_test, 65562306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1)) 65662306a36Sopenharmony_ci goto err_kfree_txhdr_cache; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci dma_unmap_single(dev->dev->dma_dev, dma_test, 66062306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 66162306a36Sopenharmony_ci DMA_TO_DEVICE); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci ring->nr_slots = nr_slots; 66562306a36Sopenharmony_ci ring->mmio_base = b43legacy_dmacontroller_base(type, controller_index); 66662306a36Sopenharmony_ci ring->index = controller_index; 66762306a36Sopenharmony_ci if (for_tx) { 66862306a36Sopenharmony_ci ring->tx = true; 66962306a36Sopenharmony_ci ring->current_slot = -1; 67062306a36Sopenharmony_ci } else { 67162306a36Sopenharmony_ci if (ring->index == 0) { 67262306a36Sopenharmony_ci ring->rx_buffersize = B43legacy_DMA0_RX_BUFFERSIZE; 67362306a36Sopenharmony_ci ring->frameoffset = B43legacy_DMA0_RX_FRAMEOFFSET; 67462306a36Sopenharmony_ci } else if (ring->index == 3) { 67562306a36Sopenharmony_ci ring->rx_buffersize = B43legacy_DMA3_RX_BUFFERSIZE; 67662306a36Sopenharmony_ci ring->frameoffset = B43legacy_DMA3_RX_FRAMEOFFSET; 67762306a36Sopenharmony_ci } else 67862306a36Sopenharmony_ci B43legacy_WARN_ON(1); 67962306a36Sopenharmony_ci } 68062306a36Sopenharmony_ci#ifdef CONFIG_B43LEGACY_DEBUG 68162306a36Sopenharmony_ci ring->last_injected_overflow = jiffies; 68262306a36Sopenharmony_ci#endif 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci err = alloc_ringmemory(ring); 68562306a36Sopenharmony_ci if (err) 68662306a36Sopenharmony_ci goto err_kfree_txhdr_cache; 68762306a36Sopenharmony_ci err = dmacontroller_setup(ring); 68862306a36Sopenharmony_ci if (err) 68962306a36Sopenharmony_ci goto err_free_ringmemory; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ciout: 69262306a36Sopenharmony_ci return ring; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cierr_free_ringmemory: 69562306a36Sopenharmony_ci free_ringmemory(ring); 69662306a36Sopenharmony_cierr_kfree_txhdr_cache: 69762306a36Sopenharmony_ci kfree(ring->txhdr_cache); 69862306a36Sopenharmony_cierr_kfree_meta: 69962306a36Sopenharmony_ci kfree(ring->meta); 70062306a36Sopenharmony_cierr_kfree_ring: 70162306a36Sopenharmony_ci kfree(ring); 70262306a36Sopenharmony_ci ring = NULL; 70362306a36Sopenharmony_ci goto out; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci/* Main cleanup function. */ 70762306a36Sopenharmony_cistatic void b43legacy_destroy_dmaring(struct b43legacy_dmaring *ring) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci if (!ring) 71062306a36Sopenharmony_ci return; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci b43legacydbg(ring->dev->wl, "DMA-%u 0x%04X (%s) max used slots:" 71362306a36Sopenharmony_ci " %d/%d\n", (unsigned int)(ring->type), ring->mmio_base, 71462306a36Sopenharmony_ci (ring->tx) ? "TX" : "RX", ring->max_used_slots, 71562306a36Sopenharmony_ci ring->nr_slots); 71662306a36Sopenharmony_ci /* Device IRQs are disabled prior entering this function, 71762306a36Sopenharmony_ci * so no need to take care of concurrency with rx handler stuff. 71862306a36Sopenharmony_ci */ 71962306a36Sopenharmony_ci dmacontroller_cleanup(ring); 72062306a36Sopenharmony_ci free_all_descbuffers(ring); 72162306a36Sopenharmony_ci free_ringmemory(ring); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci kfree(ring->txhdr_cache); 72462306a36Sopenharmony_ci kfree(ring->meta); 72562306a36Sopenharmony_ci kfree(ring); 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_civoid b43legacy_dma_free(struct b43legacy_wldev *dev) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct b43legacy_dma *dma; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (b43legacy_using_pio(dev)) 73362306a36Sopenharmony_ci return; 73462306a36Sopenharmony_ci dma = &dev->dma; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->rx_ring3); 73762306a36Sopenharmony_ci dma->rx_ring3 = NULL; 73862306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->rx_ring0); 73962306a36Sopenharmony_ci dma->rx_ring0 = NULL; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring5); 74262306a36Sopenharmony_ci dma->tx_ring5 = NULL; 74362306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring4); 74462306a36Sopenharmony_ci dma->tx_ring4 = NULL; 74562306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring3); 74662306a36Sopenharmony_ci dma->tx_ring3 = NULL; 74762306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring2); 74862306a36Sopenharmony_ci dma->tx_ring2 = NULL; 74962306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring1); 75062306a36Sopenharmony_ci dma->tx_ring1 = NULL; 75162306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring0); 75262306a36Sopenharmony_ci dma->tx_ring0 = NULL; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ciint b43legacy_dma_init(struct b43legacy_wldev *dev) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct b43legacy_dma *dma = &dev->dma; 75862306a36Sopenharmony_ci struct b43legacy_dmaring *ring; 75962306a36Sopenharmony_ci enum b43legacy_dmatype type = b43legacy_engine_type(dev); 76062306a36Sopenharmony_ci int err; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci err = dma_set_mask_and_coherent(dev->dev->dma_dev, DMA_BIT_MASK(type)); 76362306a36Sopenharmony_ci if (err) { 76462306a36Sopenharmony_ci#ifdef CONFIG_B43LEGACY_PIO 76562306a36Sopenharmony_ci b43legacywarn(dev->wl, "DMA for this device not supported. " 76662306a36Sopenharmony_ci "Falling back to PIO\n"); 76762306a36Sopenharmony_ci dev->__using_pio = true; 76862306a36Sopenharmony_ci return -EAGAIN; 76962306a36Sopenharmony_ci#else 77062306a36Sopenharmony_ci b43legacyerr(dev->wl, "DMA for this device not supported and " 77162306a36Sopenharmony_ci "no PIO support compiled in\n"); 77262306a36Sopenharmony_ci return -EOPNOTSUPP; 77362306a36Sopenharmony_ci#endif 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci dma->translation = ssb_dma_translation(dev->dev); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci err = -ENOMEM; 77862306a36Sopenharmony_ci /* setup TX DMA channels. */ 77962306a36Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 0, 1, type); 78062306a36Sopenharmony_ci if (!ring) 78162306a36Sopenharmony_ci goto out; 78262306a36Sopenharmony_ci dma->tx_ring0 = ring; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 1, 1, type); 78562306a36Sopenharmony_ci if (!ring) 78662306a36Sopenharmony_ci goto err_destroy_tx0; 78762306a36Sopenharmony_ci dma->tx_ring1 = ring; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 2, 1, type); 79062306a36Sopenharmony_ci if (!ring) 79162306a36Sopenharmony_ci goto err_destroy_tx1; 79262306a36Sopenharmony_ci dma->tx_ring2 = ring; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 3, 1, type); 79562306a36Sopenharmony_ci if (!ring) 79662306a36Sopenharmony_ci goto err_destroy_tx2; 79762306a36Sopenharmony_ci dma->tx_ring3 = ring; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 4, 1, type); 80062306a36Sopenharmony_ci if (!ring) 80162306a36Sopenharmony_ci goto err_destroy_tx3; 80262306a36Sopenharmony_ci dma->tx_ring4 = ring; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 5, 1, type); 80562306a36Sopenharmony_ci if (!ring) 80662306a36Sopenharmony_ci goto err_destroy_tx4; 80762306a36Sopenharmony_ci dma->tx_ring5 = ring; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* setup RX DMA channels. */ 81062306a36Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 0, 0, type); 81162306a36Sopenharmony_ci if (!ring) 81262306a36Sopenharmony_ci goto err_destroy_tx5; 81362306a36Sopenharmony_ci dma->rx_ring0 = ring; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (dev->dev->id.revision < 5) { 81662306a36Sopenharmony_ci ring = b43legacy_setup_dmaring(dev, 3, 0, type); 81762306a36Sopenharmony_ci if (!ring) 81862306a36Sopenharmony_ci goto err_destroy_rx0; 81962306a36Sopenharmony_ci dma->rx_ring3 = ring; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci b43legacydbg(dev->wl, "%u-bit DMA initialized\n", (unsigned int)type); 82362306a36Sopenharmony_ci err = 0; 82462306a36Sopenharmony_ciout: 82562306a36Sopenharmony_ci return err; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cierr_destroy_rx0: 82862306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->rx_ring0); 82962306a36Sopenharmony_ci dma->rx_ring0 = NULL; 83062306a36Sopenharmony_cierr_destroy_tx5: 83162306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring5); 83262306a36Sopenharmony_ci dma->tx_ring5 = NULL; 83362306a36Sopenharmony_cierr_destroy_tx4: 83462306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring4); 83562306a36Sopenharmony_ci dma->tx_ring4 = NULL; 83662306a36Sopenharmony_cierr_destroy_tx3: 83762306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring3); 83862306a36Sopenharmony_ci dma->tx_ring3 = NULL; 83962306a36Sopenharmony_cierr_destroy_tx2: 84062306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring2); 84162306a36Sopenharmony_ci dma->tx_ring2 = NULL; 84262306a36Sopenharmony_cierr_destroy_tx1: 84362306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring1); 84462306a36Sopenharmony_ci dma->tx_ring1 = NULL; 84562306a36Sopenharmony_cierr_destroy_tx0: 84662306a36Sopenharmony_ci b43legacy_destroy_dmaring(dma->tx_ring0); 84762306a36Sopenharmony_ci dma->tx_ring0 = NULL; 84862306a36Sopenharmony_ci goto out; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci/* Generate a cookie for the TX header. */ 85262306a36Sopenharmony_cistatic u16 generate_cookie(struct b43legacy_dmaring *ring, 85362306a36Sopenharmony_ci int slot) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci u16 cookie = 0x1000; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* Use the upper 4 bits of the cookie as 85862306a36Sopenharmony_ci * DMA controller ID and store the slot number 85962306a36Sopenharmony_ci * in the lower 12 bits. 86062306a36Sopenharmony_ci * Note that the cookie must never be 0, as this 86162306a36Sopenharmony_ci * is a special value used in RX path. 86262306a36Sopenharmony_ci */ 86362306a36Sopenharmony_ci switch (ring->index) { 86462306a36Sopenharmony_ci case 0: 86562306a36Sopenharmony_ci cookie = 0xA000; 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci case 1: 86862306a36Sopenharmony_ci cookie = 0xB000; 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci case 2: 87162306a36Sopenharmony_ci cookie = 0xC000; 87262306a36Sopenharmony_ci break; 87362306a36Sopenharmony_ci case 3: 87462306a36Sopenharmony_ci cookie = 0xD000; 87562306a36Sopenharmony_ci break; 87662306a36Sopenharmony_ci case 4: 87762306a36Sopenharmony_ci cookie = 0xE000; 87862306a36Sopenharmony_ci break; 87962306a36Sopenharmony_ci case 5: 88062306a36Sopenharmony_ci cookie = 0xF000; 88162306a36Sopenharmony_ci break; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci B43legacy_WARN_ON(!(((u16)slot & 0xF000) == 0x0000)); 88462306a36Sopenharmony_ci cookie |= (u16)slot; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci return cookie; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci/* Inspect a cookie and find out to which controller/slot it belongs. */ 89062306a36Sopenharmony_cistatic 89162306a36Sopenharmony_cistruct b43legacy_dmaring *parse_cookie(struct b43legacy_wldev *dev, 89262306a36Sopenharmony_ci u16 cookie, int *slot) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci struct b43legacy_dma *dma = &dev->dma; 89562306a36Sopenharmony_ci struct b43legacy_dmaring *ring = NULL; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci switch (cookie & 0xF000) { 89862306a36Sopenharmony_ci case 0xA000: 89962306a36Sopenharmony_ci ring = dma->tx_ring0; 90062306a36Sopenharmony_ci break; 90162306a36Sopenharmony_ci case 0xB000: 90262306a36Sopenharmony_ci ring = dma->tx_ring1; 90362306a36Sopenharmony_ci break; 90462306a36Sopenharmony_ci case 0xC000: 90562306a36Sopenharmony_ci ring = dma->tx_ring2; 90662306a36Sopenharmony_ci break; 90762306a36Sopenharmony_ci case 0xD000: 90862306a36Sopenharmony_ci ring = dma->tx_ring3; 90962306a36Sopenharmony_ci break; 91062306a36Sopenharmony_ci case 0xE000: 91162306a36Sopenharmony_ci ring = dma->tx_ring4; 91262306a36Sopenharmony_ci break; 91362306a36Sopenharmony_ci case 0xF000: 91462306a36Sopenharmony_ci ring = dma->tx_ring5; 91562306a36Sopenharmony_ci break; 91662306a36Sopenharmony_ci default: 91762306a36Sopenharmony_ci B43legacy_WARN_ON(1); 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci *slot = (cookie & 0x0FFF); 92062306a36Sopenharmony_ci B43legacy_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots)); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return ring; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic int dma_tx_fragment(struct b43legacy_dmaring *ring, 92662306a36Sopenharmony_ci struct sk_buff **in_skb) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci struct sk_buff *skb = *in_skb; 92962306a36Sopenharmony_ci struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); 93062306a36Sopenharmony_ci u8 *header; 93162306a36Sopenharmony_ci int slot, old_top_slot, old_used_slots; 93262306a36Sopenharmony_ci int err; 93362306a36Sopenharmony_ci struct b43legacy_dmadesc32 *desc; 93462306a36Sopenharmony_ci struct b43legacy_dmadesc_meta *meta; 93562306a36Sopenharmony_ci struct b43legacy_dmadesc_meta *meta_hdr; 93662306a36Sopenharmony_ci struct sk_buff *bounce_skb; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci#define SLOTS_PER_PACKET 2 93962306a36Sopenharmony_ci B43legacy_WARN_ON(skb_shinfo(skb)->nr_frags != 0); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci old_top_slot = ring->current_slot; 94262306a36Sopenharmony_ci old_used_slots = ring->used_slots; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci /* Get a slot for the header. */ 94562306a36Sopenharmony_ci slot = request_slot(ring); 94662306a36Sopenharmony_ci desc = op32_idx2desc(ring, slot, &meta_hdr); 94762306a36Sopenharmony_ci memset(meta_hdr, 0, sizeof(*meta_hdr)); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci header = &(ring->txhdr_cache[slot * sizeof( 95062306a36Sopenharmony_ci struct b43legacy_txhdr_fw3)]); 95162306a36Sopenharmony_ci err = b43legacy_generate_txhdr(ring->dev, header, 95262306a36Sopenharmony_ci skb->data, skb->len, info, 95362306a36Sopenharmony_ci generate_cookie(ring, slot)); 95462306a36Sopenharmony_ci if (unlikely(err)) { 95562306a36Sopenharmony_ci ring->current_slot = old_top_slot; 95662306a36Sopenharmony_ci ring->used_slots = old_used_slots; 95762306a36Sopenharmony_ci return err; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci meta_hdr->dmaaddr = map_descbuffer(ring, (unsigned char *)header, 96162306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1); 96262306a36Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, meta_hdr->dmaaddr, 96362306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1)) { 96462306a36Sopenharmony_ci ring->current_slot = old_top_slot; 96562306a36Sopenharmony_ci ring->used_slots = old_used_slots; 96662306a36Sopenharmony_ci return -EIO; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci op32_fill_descriptor(ring, desc, meta_hdr->dmaaddr, 96962306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1, 0, 0); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* Get a slot for the payload. */ 97262306a36Sopenharmony_ci slot = request_slot(ring); 97362306a36Sopenharmony_ci desc = op32_idx2desc(ring, slot, &meta); 97462306a36Sopenharmony_ci memset(meta, 0, sizeof(*meta)); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci meta->skb = skb; 97762306a36Sopenharmony_ci meta->is_last_fragment = true; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); 98062306a36Sopenharmony_ci /* create a bounce buffer in zone_dma on mapping failure. */ 98162306a36Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { 98262306a36Sopenharmony_ci bounce_skb = alloc_skb(skb->len, GFP_KERNEL | GFP_DMA); 98362306a36Sopenharmony_ci if (!bounce_skb) { 98462306a36Sopenharmony_ci ring->current_slot = old_top_slot; 98562306a36Sopenharmony_ci ring->used_slots = old_used_slots; 98662306a36Sopenharmony_ci err = -ENOMEM; 98762306a36Sopenharmony_ci goto out_unmap_hdr; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci skb_put_data(bounce_skb, skb->data, skb->len); 99162306a36Sopenharmony_ci memcpy(bounce_skb->cb, skb->cb, sizeof(skb->cb)); 99262306a36Sopenharmony_ci bounce_skb->dev = skb->dev; 99362306a36Sopenharmony_ci skb_set_queue_mapping(bounce_skb, skb_get_queue_mapping(skb)); 99462306a36Sopenharmony_ci info = IEEE80211_SKB_CB(bounce_skb); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 99762306a36Sopenharmony_ci skb = bounce_skb; 99862306a36Sopenharmony_ci *in_skb = bounce_skb; 99962306a36Sopenharmony_ci meta->skb = skb; 100062306a36Sopenharmony_ci meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); 100162306a36Sopenharmony_ci if (b43legacy_dma_mapping_error(ring, meta->dmaaddr, skb->len, 1)) { 100262306a36Sopenharmony_ci ring->current_slot = old_top_slot; 100362306a36Sopenharmony_ci ring->used_slots = old_used_slots; 100462306a36Sopenharmony_ci err = -EIO; 100562306a36Sopenharmony_ci goto out_free_bounce; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci op32_fill_descriptor(ring, desc, meta->dmaaddr, 101062306a36Sopenharmony_ci skb->len, 0, 1, 1); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci wmb(); /* previous stuff MUST be done */ 101362306a36Sopenharmony_ci /* Now transfer the whole frame. */ 101462306a36Sopenharmony_ci op32_poke_tx(ring, next_slot(ring, slot)); 101562306a36Sopenharmony_ci return 0; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ciout_free_bounce: 101862306a36Sopenharmony_ci dev_kfree_skb_any(skb); 101962306a36Sopenharmony_ciout_unmap_hdr: 102062306a36Sopenharmony_ci unmap_descbuffer(ring, meta_hdr->dmaaddr, 102162306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 1); 102262306a36Sopenharmony_ci return err; 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic inline 102662306a36Sopenharmony_ciint should_inject_overflow(struct b43legacy_dmaring *ring) 102762306a36Sopenharmony_ci{ 102862306a36Sopenharmony_ci#ifdef CONFIG_B43LEGACY_DEBUG 102962306a36Sopenharmony_ci if (unlikely(b43legacy_debug(ring->dev, 103062306a36Sopenharmony_ci B43legacy_DBG_DMAOVERFLOW))) { 103162306a36Sopenharmony_ci /* Check if we should inject another ringbuffer overflow 103262306a36Sopenharmony_ci * to test handling of this situation in the stack. */ 103362306a36Sopenharmony_ci unsigned long next_overflow; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci next_overflow = ring->last_injected_overflow + HZ; 103662306a36Sopenharmony_ci if (time_after(jiffies, next_overflow)) { 103762306a36Sopenharmony_ci ring->last_injected_overflow = jiffies; 103862306a36Sopenharmony_ci b43legacydbg(ring->dev->wl, 103962306a36Sopenharmony_ci "Injecting TX ring overflow on " 104062306a36Sopenharmony_ci "DMA controller %d\n", ring->index); 104162306a36Sopenharmony_ci return 1; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci#endif /* CONFIG_B43LEGACY_DEBUG */ 104562306a36Sopenharmony_ci return 0; 104662306a36Sopenharmony_ci} 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ciint b43legacy_dma_tx(struct b43legacy_wldev *dev, 104962306a36Sopenharmony_ci struct sk_buff *skb) 105062306a36Sopenharmony_ci{ 105162306a36Sopenharmony_ci struct b43legacy_dmaring *ring; 105262306a36Sopenharmony_ci int err = 0; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci ring = priority_to_txring(dev, skb_get_queue_mapping(skb)); 105562306a36Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (unlikely(ring->stopped)) { 105862306a36Sopenharmony_ci /* We get here only because of a bug in mac80211. 105962306a36Sopenharmony_ci * Because of a race, one packet may be queued after 106062306a36Sopenharmony_ci * the queue is stopped, thus we got called when we shouldn't. 106162306a36Sopenharmony_ci * For now, just refuse the transmit. */ 106262306a36Sopenharmony_ci if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) 106362306a36Sopenharmony_ci b43legacyerr(dev->wl, "Packet after queue stopped\n"); 106462306a36Sopenharmony_ci return -ENOSPC; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (WARN_ON(free_slots(ring) < SLOTS_PER_PACKET)) { 106862306a36Sopenharmony_ci /* If we get here, we have a real error with the queue 106962306a36Sopenharmony_ci * full, but queues not stopped. */ 107062306a36Sopenharmony_ci b43legacyerr(dev->wl, "DMA queue overflow\n"); 107162306a36Sopenharmony_ci return -ENOSPC; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* dma_tx_fragment might reallocate the skb, so invalidate pointers pointing 107562306a36Sopenharmony_ci * into the skb data or cb now. */ 107662306a36Sopenharmony_ci err = dma_tx_fragment(ring, &skb); 107762306a36Sopenharmony_ci if (unlikely(err == -ENOKEY)) { 107862306a36Sopenharmony_ci /* Drop this packet, as we don't have the encryption key 107962306a36Sopenharmony_ci * anymore and must not transmit it unencrypted. */ 108062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 108162306a36Sopenharmony_ci return 0; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci if (unlikely(err)) { 108462306a36Sopenharmony_ci b43legacyerr(dev->wl, "DMA tx mapping failure\n"); 108562306a36Sopenharmony_ci return err; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci if ((free_slots(ring) < SLOTS_PER_PACKET) || 108862306a36Sopenharmony_ci should_inject_overflow(ring)) { 108962306a36Sopenharmony_ci /* This TX ring is full. */ 109062306a36Sopenharmony_ci unsigned int skb_mapping = skb_get_queue_mapping(skb); 109162306a36Sopenharmony_ci ieee80211_stop_queue(dev->wl->hw, skb_mapping); 109262306a36Sopenharmony_ci dev->wl->tx_queue_stopped[skb_mapping] = 1; 109362306a36Sopenharmony_ci ring->stopped = true; 109462306a36Sopenharmony_ci if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) 109562306a36Sopenharmony_ci b43legacydbg(dev->wl, "Stopped TX ring %d\n", 109662306a36Sopenharmony_ci ring->index); 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci return err; 109962306a36Sopenharmony_ci} 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_civoid b43legacy_dma_handle_txstatus(struct b43legacy_wldev *dev, 110262306a36Sopenharmony_ci const struct b43legacy_txstatus *status) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci struct b43legacy_dmaring *ring; 110562306a36Sopenharmony_ci struct b43legacy_dmadesc_meta *meta; 110662306a36Sopenharmony_ci int retry_limit; 110762306a36Sopenharmony_ci int slot; 110862306a36Sopenharmony_ci int firstused; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci ring = parse_cookie(dev, status->cookie, &slot); 111162306a36Sopenharmony_ci if (unlikely(!ring)) 111262306a36Sopenharmony_ci return; 111362306a36Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* Sanity check: TX packets are processed in-order on one ring. 111662306a36Sopenharmony_ci * Check if the slot deduced from the cookie really is the first 111762306a36Sopenharmony_ci * used slot. */ 111862306a36Sopenharmony_ci firstused = ring->current_slot - ring->used_slots + 1; 111962306a36Sopenharmony_ci if (firstused < 0) 112062306a36Sopenharmony_ci firstused = ring->nr_slots + firstused; 112162306a36Sopenharmony_ci if (unlikely(slot != firstused)) { 112262306a36Sopenharmony_ci /* This possibly is a firmware bug and will result in 112362306a36Sopenharmony_ci * malfunction, memory leaks and/or stall of DMA functionality. 112462306a36Sopenharmony_ci */ 112562306a36Sopenharmony_ci b43legacydbg(dev->wl, "Out of order TX status report on DMA " 112662306a36Sopenharmony_ci "ring %d. Expected %d, but got %d\n", 112762306a36Sopenharmony_ci ring->index, firstused, slot); 112862306a36Sopenharmony_ci return; 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci while (1) { 113262306a36Sopenharmony_ci B43legacy_WARN_ON(!(slot >= 0 && slot < ring->nr_slots)); 113362306a36Sopenharmony_ci op32_idx2desc(ring, slot, &meta); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (meta->skb) 113662306a36Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 113762306a36Sopenharmony_ci meta->skb->len, 1); 113862306a36Sopenharmony_ci else 113962306a36Sopenharmony_ci unmap_descbuffer(ring, meta->dmaaddr, 114062306a36Sopenharmony_ci sizeof(struct b43legacy_txhdr_fw3), 114162306a36Sopenharmony_ci 1); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (meta->is_last_fragment) { 114462306a36Sopenharmony_ci struct ieee80211_tx_info *info; 114562306a36Sopenharmony_ci BUG_ON(!meta->skb); 114662306a36Sopenharmony_ci info = IEEE80211_SKB_CB(meta->skb); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci /* preserve the confiured retry limit before clearing the status 114962306a36Sopenharmony_ci * The xmit function has overwritten the rc's value with the actual 115062306a36Sopenharmony_ci * retry limit done by the hardware */ 115162306a36Sopenharmony_ci retry_limit = info->status.rates[0].count; 115262306a36Sopenharmony_ci ieee80211_tx_info_clear_status(info); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (status->acked) 115562306a36Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (status->rts_count > dev->wl->hw->conf.short_frame_max_tx_count) { 115862306a36Sopenharmony_ci /* 115962306a36Sopenharmony_ci * If the short retries (RTS, not data frame) have exceeded 116062306a36Sopenharmony_ci * the limit, the hw will not have tried the selected rate, 116162306a36Sopenharmony_ci * but will have used the fallback rate instead. 116262306a36Sopenharmony_ci * Don't let the rate control count attempts for the selected 116362306a36Sopenharmony_ci * rate in this case, otherwise the statistics will be off. 116462306a36Sopenharmony_ci */ 116562306a36Sopenharmony_ci info->status.rates[0].count = 0; 116662306a36Sopenharmony_ci info->status.rates[1].count = status->frame_count; 116762306a36Sopenharmony_ci } else { 116862306a36Sopenharmony_ci if (status->frame_count > retry_limit) { 116962306a36Sopenharmony_ci info->status.rates[0].count = retry_limit; 117062306a36Sopenharmony_ci info->status.rates[1].count = status->frame_count - 117162306a36Sopenharmony_ci retry_limit; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci } else { 117462306a36Sopenharmony_ci info->status.rates[0].count = status->frame_count; 117562306a36Sopenharmony_ci info->status.rates[1].idx = -1; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* Call back to inform the ieee80211 subsystem about the 118062306a36Sopenharmony_ci * status of the transmission. 118162306a36Sopenharmony_ci * Some fields of txstat are already filled in dma_tx(). 118262306a36Sopenharmony_ci */ 118362306a36Sopenharmony_ci ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb); 118462306a36Sopenharmony_ci /* skb is freed by ieee80211_tx_status_irqsafe() */ 118562306a36Sopenharmony_ci meta->skb = NULL; 118662306a36Sopenharmony_ci } else { 118762306a36Sopenharmony_ci /* No need to call free_descriptor_buffer here, as 118862306a36Sopenharmony_ci * this is only the txhdr, which is not allocated. 118962306a36Sopenharmony_ci */ 119062306a36Sopenharmony_ci B43legacy_WARN_ON(meta->skb != NULL); 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci /* Everything unmapped and free'd. So it's not used anymore. */ 119462306a36Sopenharmony_ci ring->used_slots--; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (meta->is_last_fragment) 119762306a36Sopenharmony_ci break; 119862306a36Sopenharmony_ci slot = next_slot(ring, slot); 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci dev->stats.last_tx = jiffies; 120162306a36Sopenharmony_ci if (ring->stopped) { 120262306a36Sopenharmony_ci B43legacy_WARN_ON(free_slots(ring) < SLOTS_PER_PACKET); 120362306a36Sopenharmony_ci ring->stopped = false; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci if (dev->wl->tx_queue_stopped[ring->queue_prio]) { 120762306a36Sopenharmony_ci dev->wl->tx_queue_stopped[ring->queue_prio] = 0; 120862306a36Sopenharmony_ci } else { 120962306a36Sopenharmony_ci /* If the driver queue is running wake the corresponding 121062306a36Sopenharmony_ci * mac80211 queue. */ 121162306a36Sopenharmony_ci ieee80211_wake_queue(dev->wl->hw, ring->queue_prio); 121262306a36Sopenharmony_ci if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) 121362306a36Sopenharmony_ci b43legacydbg(dev->wl, "Woke up TX ring %d\n", 121462306a36Sopenharmony_ci ring->index); 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci /* Add work to the queue. */ 121762306a36Sopenharmony_ci ieee80211_queue_work(dev->wl->hw, &dev->wl->tx_work); 121862306a36Sopenharmony_ci} 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_cistatic void dma_rx(struct b43legacy_dmaring *ring, 122162306a36Sopenharmony_ci int *slot) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci struct b43legacy_dmadesc32 *desc; 122462306a36Sopenharmony_ci struct b43legacy_dmadesc_meta *meta; 122562306a36Sopenharmony_ci struct b43legacy_rxhdr_fw3 *rxhdr; 122662306a36Sopenharmony_ci struct sk_buff *skb; 122762306a36Sopenharmony_ci u16 len; 122862306a36Sopenharmony_ci int err; 122962306a36Sopenharmony_ci dma_addr_t dmaaddr; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci desc = op32_idx2desc(ring, *slot, &meta); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); 123462306a36Sopenharmony_ci skb = meta->skb; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci if (ring->index == 3) { 123762306a36Sopenharmony_ci /* We received an xmit status. */ 123862306a36Sopenharmony_ci struct b43legacy_hwtxstatus *hw = 123962306a36Sopenharmony_ci (struct b43legacy_hwtxstatus *)skb->data; 124062306a36Sopenharmony_ci int i = 0; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci while (hw->cookie == 0) { 124362306a36Sopenharmony_ci if (i > 100) 124462306a36Sopenharmony_ci break; 124562306a36Sopenharmony_ci i++; 124662306a36Sopenharmony_ci udelay(2); 124762306a36Sopenharmony_ci barrier(); 124862306a36Sopenharmony_ci } 124962306a36Sopenharmony_ci b43legacy_handle_hwtxstatus(ring->dev, hw); 125062306a36Sopenharmony_ci /* recycle the descriptor buffer. */ 125162306a36Sopenharmony_ci sync_descbuffer_for_device(ring, meta->dmaaddr, 125262306a36Sopenharmony_ci ring->rx_buffersize); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci return; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci rxhdr = (struct b43legacy_rxhdr_fw3 *)skb->data; 125762306a36Sopenharmony_ci len = le16_to_cpu(rxhdr->frame_len); 125862306a36Sopenharmony_ci if (len == 0) { 125962306a36Sopenharmony_ci int i = 0; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci do { 126262306a36Sopenharmony_ci udelay(2); 126362306a36Sopenharmony_ci barrier(); 126462306a36Sopenharmony_ci len = le16_to_cpu(rxhdr->frame_len); 126562306a36Sopenharmony_ci } while (len == 0 && i++ < 5); 126662306a36Sopenharmony_ci if (unlikely(len == 0)) { 126762306a36Sopenharmony_ci /* recycle the descriptor buffer. */ 126862306a36Sopenharmony_ci sync_descbuffer_for_device(ring, meta->dmaaddr, 126962306a36Sopenharmony_ci ring->rx_buffersize); 127062306a36Sopenharmony_ci goto drop; 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci if (unlikely(len > ring->rx_buffersize)) { 127462306a36Sopenharmony_ci /* The data did not fit into one descriptor buffer 127562306a36Sopenharmony_ci * and is split over multiple buffers. 127662306a36Sopenharmony_ci * This should never happen, as we try to allocate buffers 127762306a36Sopenharmony_ci * big enough. So simply ignore this packet. 127862306a36Sopenharmony_ci */ 127962306a36Sopenharmony_ci int cnt = 0; 128062306a36Sopenharmony_ci s32 tmp = len; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci while (1) { 128362306a36Sopenharmony_ci desc = op32_idx2desc(ring, *slot, &meta); 128462306a36Sopenharmony_ci /* recycle the descriptor buffer. */ 128562306a36Sopenharmony_ci sync_descbuffer_for_device(ring, meta->dmaaddr, 128662306a36Sopenharmony_ci ring->rx_buffersize); 128762306a36Sopenharmony_ci *slot = next_slot(ring, *slot); 128862306a36Sopenharmony_ci cnt++; 128962306a36Sopenharmony_ci tmp -= ring->rx_buffersize; 129062306a36Sopenharmony_ci if (tmp <= 0) 129162306a36Sopenharmony_ci break; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci b43legacyerr(ring->dev->wl, "DMA RX buffer too small " 129462306a36Sopenharmony_ci "(len: %u, buffer: %u, nr-dropped: %d)\n", 129562306a36Sopenharmony_ci len, ring->rx_buffersize, cnt); 129662306a36Sopenharmony_ci goto drop; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci dmaaddr = meta->dmaaddr; 130062306a36Sopenharmony_ci err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); 130162306a36Sopenharmony_ci if (unlikely(err)) { 130262306a36Sopenharmony_ci b43legacydbg(ring->dev->wl, "DMA RX: setup_rx_descbuffer()" 130362306a36Sopenharmony_ci " failed\n"); 130462306a36Sopenharmony_ci sync_descbuffer_for_device(ring, dmaaddr, 130562306a36Sopenharmony_ci ring->rx_buffersize); 130662306a36Sopenharmony_ci goto drop; 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); 131062306a36Sopenharmony_ci skb_put(skb, len + ring->frameoffset); 131162306a36Sopenharmony_ci skb_pull(skb, ring->frameoffset); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci b43legacy_rx(ring->dev, skb, rxhdr); 131462306a36Sopenharmony_cidrop: 131562306a36Sopenharmony_ci return; 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_civoid b43legacy_dma_rx(struct b43legacy_dmaring *ring) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci int slot; 132162306a36Sopenharmony_ci int current_slot; 132262306a36Sopenharmony_ci int used_slots = 0; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci B43legacy_WARN_ON(ring->tx); 132562306a36Sopenharmony_ci current_slot = op32_get_current_rxslot(ring); 132662306a36Sopenharmony_ci B43legacy_WARN_ON(!(current_slot >= 0 && current_slot < 132762306a36Sopenharmony_ci ring->nr_slots)); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci slot = ring->current_slot; 133062306a36Sopenharmony_ci for (; slot != current_slot; slot = next_slot(ring, slot)) { 133162306a36Sopenharmony_ci dma_rx(ring, &slot); 133262306a36Sopenharmony_ci update_max_used_slots(ring, ++used_slots); 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci op32_set_current_rxslot(ring, slot); 133562306a36Sopenharmony_ci ring->current_slot = slot; 133662306a36Sopenharmony_ci} 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cistatic void b43legacy_dma_tx_suspend_ring(struct b43legacy_dmaring *ring) 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 134162306a36Sopenharmony_ci op32_tx_suspend(ring); 134262306a36Sopenharmony_ci} 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_cistatic void b43legacy_dma_tx_resume_ring(struct b43legacy_dmaring *ring) 134562306a36Sopenharmony_ci{ 134662306a36Sopenharmony_ci B43legacy_WARN_ON(!ring->tx); 134762306a36Sopenharmony_ci op32_tx_resume(ring); 134862306a36Sopenharmony_ci} 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_civoid b43legacy_dma_tx_suspend(struct b43legacy_wldev *dev) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci b43legacy_power_saving_ctl_bits(dev, -1, 1); 135362306a36Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring0); 135462306a36Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring1); 135562306a36Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring2); 135662306a36Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring3); 135762306a36Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring4); 135862306a36Sopenharmony_ci b43legacy_dma_tx_suspend_ring(dev->dma.tx_ring5); 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_civoid b43legacy_dma_tx_resume(struct b43legacy_wldev *dev) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring5); 136462306a36Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring4); 136562306a36Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring3); 136662306a36Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring2); 136762306a36Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring1); 136862306a36Sopenharmony_ci b43legacy_dma_tx_resume_ring(dev->dma.tx_ring0); 136962306a36Sopenharmony_ci b43legacy_power_saving_ctl_bits(dev, -1, -1); 137062306a36Sopenharmony_ci} 1371