// SPDX-License-Identifier: GPL-2.0-or-later
/*
Broadcom B43 wireless driver
PIO data transfer
Copyright (c) 2005-2008 Michael Buesch <m@bues.ch>
*/
#include "b43.h"
#include "pio.h"
#include "dma.h"
#include "main.h"
#include "xmit.h"
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
static u16 generate_cookie(struct b43_pio_txqueue *q,
struct b43_pio_txpacket *pack)
{
u16 cookie;
/* Use the upper 4 bits of the cookie as
* PIO controller ID and store the packet index number
* in the lower 12 bits.
* Note that the cookie must never be 0, as this
* is a special value used in RX path.
* It can also not be 0xFFFF because that is special
* for multicast frames.
*/
cookie = (((u16)q->index + 1) << 12);
cookie |= pack->index;
return cookie;
}
static
struct b43_pio_txqueue *parse_cookie(struct b43_wldev *dev,
u16 cookie,
struct b43_pio_txpacket **pack)
{
struct b43_pio *pio = &dev->pio;
struct b43_pio_txqueue *q = NULL;
unsigned int pack_index;
switch (cookie & 0xF000) {
case 0x1000:
q = pio->tx_queue_AC_BK;
break;
case 0x2000:
q = pio->tx_queue_AC_BE;
break;
case 0x3000:
q = pio->tx_queue_AC_VI;
break;
case 0x4000:
q = pio->tx_queue_AC_VO;
break;
case 0x5000:
q = pio->tx_queue_mcast;
break;
}
if (B43_WARN_ON(!q))
return NULL;
pack_index = (cookie & 0x0FFF);
if (B43_WARN_ON(pack_index >= ARRAY_SIZE(q->packets)))
return NULL;
*pack = &q->packets[pack_index];
return q;
}
static u16 index_to_pioqueue_base(struct b43_wldev *dev,
unsigned int index)
{
static const u16 bases[] = {
B43_MMIO_PIO_BASE0,
B43_MMIO_PIO_BASE1,
B43_MMIO_PIO_BASE2,
B43_MMIO_PIO_BASE3,
B43_MMIO_PIO_BASE4,
B43_MMIO_PIO_BASE5,
B43_MMIO_PIO_BASE6,
B43_MMIO_PIO_BASE7,
};
static const u16 bases_rev11[] = {
B43_MMIO_PIO11_BASE0,
B43_MMIO_PIO11_BASE1,
B43_MMIO_PIO11_BASE2,
B43_MMIO_PIO11_BASE3,
B43_MMIO_PIO11_BASE4,
B43_MMIO_PIO11_BASE5,
};
if (dev->dev->core_rev >= 11) {
B43_WARN_ON(index >= ARRAY_SIZE(bases_rev11));
return bases_rev11[index];
}
B43_WARN_ON(index >= ARRAY_SIZE(bases));
return bases[index];
}
static u16 pio_txqueue_offset(struct b43_wldev *dev)
{
if (dev->dev->core_rev >= 11)
return 0x18;
return 0;
}
static u16 pio_rxqueue_offset(struct b43_wldev *dev)
{
if (dev->dev->core_rev >= 11)
return 0x38;
return 8;
}
static struct b43_pio_txqueue *b43_setup_pioqueue_tx(struct b43_wldev *dev,
unsigned int index)
{
struct b43_pio_txqueue *q;
struct b43_pio_txpacket *p;
unsigned int i;
q = kzalloc_obj(*q);
if (!q)
return NULL;
q->dev = dev;
q->rev = dev->dev->core_rev;
q->mmio_base = index_to_pioqueue_base(dev, index) +
pio_txqueue_offset(dev);
q->index = index;
q->free_packet_slots = B43_PIO_MAX_NR_TXPACKETS;
if (q->rev >= 8) {
q->buffer_size = 1920; //FIXME this constant is wrong.
} else {
q->buffer_size = b43_piotx_read16(q, B43_PIO_TXQBUFSIZE);
q->buffer_size -= 80;
}
INIT_LIST_HEAD(&q->packets_list);
for (i = 0; i < ARRAY_SIZE(q->packets); i++) {
p = &(q->packets[i]);
INIT_LIST_HEAD(&p->list);
p->index = i;
p->queue = q;
list_add(&p->list, &q->packets_list);
}
return q;
}
static struct b43_pio_rxqueue *b43_setup_pioqueue_rx(struct b43_wldev *dev,
unsigned int index)
{
struct b43_pio_rxqueue *q;
q = kzalloc_obj(*q);
if (!q)
return NULL;
q->dev = dev;
q->rev = dev->dev->core_rev;
q->mmio_base = index_to_pioqueue_base(dev, index) +
pio_rxqueue_offset(dev);
/* Enable Direct FIFO RX (PIO) on the engine. */
b43_dma_direct_fifo_rx(dev, index, 1);
return q;
}
static void b43_pio_cancel_tx_packets(struct b43_pio_txqueue *q)
{
struct b43_pio_txpacket *pack;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(q->packets); i++) {
pack = &(q->packets[i]);
if (pack->skb) {
ieee80211_free_txskb(q->dev->wl->hw,