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