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