Commit 5100d5ac authored by Michael Buesch's avatar Michael Buesch Committed by John W. Linville
Browse files

b43: Add PIO support for PCMCIA devices



This adds PIO support back (D'oh!) for PCMCIA devices.
This is a complete rewrite of the old PIO code. It does actually work
and we get reasonable performance out of it on a modern machine.
On a PowerBook G4 I get a few MBit for TX and a few more for RX.
So it doesn't work as well as DMA (of course), but it's a _lot_ faster
than the old PIO code (only got a few kBit with that).

The limiting factor is the host CPU speed. So it will generate 100%
CPU usage when the network interface is heavily loaded. A voluntary preemption
point in the RX path makes sure Desktop Latency isn't hurt.

PIO is needed for 16bit PCMCIA devices, as we really don't want to poke with
the braindead DMA mechanisms on PCMCIA sockets. Additionally, not all
PCMCIA sockets do actually support DMA in 16bit mode (mine doesn't).
Signed-off-by: default avatarMichael Buesch <mb@bu3sch.de>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 3109ece1
......@@ -62,6 +62,13 @@ config B43_PCMCIA
If unsure, say N.
# Data transfers to the device via PIO
# This is only needed on PCMCIA devices. All others can do DMA properly.
config B43_PIO
bool
depends on B43 && (B43_PCMCIA || B43_FORCE_PIO)
default y
config B43_NPHY
bool "Pre IEEE 802.11n support (BROKEN)"
depends on B43 && EXPERIMENTAL && BROKEN
......@@ -94,3 +101,13 @@ config B43_DEBUG
Say Y, if you want to find out why the driver does not
work for you.
config B43_FORCE_PIO
bool "Force usage of PIO instead of DMA"
depends on B43 && B43_DEBUG
---help---
This will disable DMA and always enable PIO instead.
Say N!
This is only for debugging the PIO engine code. You do
_NOT_ want to enable this.
......@@ -8,6 +8,7 @@ b43-y += xmit.o
b43-y += lo.o
b43-y += wa.o
b43-y += dma.o
b43-$(CONFIG_B43_PIO) += pio.o
b43-$(CONFIG_B43_RFKILL) += rfkill.o
b43-$(CONFIG_B43_LEDS) += leds.o
b43-$(CONFIG_B43_PCMCIA) += pcmcia.o
......
......@@ -75,6 +75,23 @@
#define B43_MMIO_DMA64_BASE4 0x300
#define B43_MMIO_DMA64_BASE5 0x340
/* PIO on core rev < 11 */
#define B43_MMIO_PIO_BASE0 0x300
#define B43_MMIO_PIO_BASE1 0x310
#define B43_MMIO_PIO_BASE2 0x320
#define B43_MMIO_PIO_BASE3 0x330
#define B43_MMIO_PIO_BASE4 0x340
#define B43_MMIO_PIO_BASE5 0x350
#define B43_MMIO_PIO_BASE6 0x360
#define B43_MMIO_PIO_BASE7 0x370
/* PIO on core rev >= 11 */
#define B43_MMIO_PIO11_BASE0 0x200
#define B43_MMIO_PIO11_BASE1 0x240
#define B43_MMIO_PIO11_BASE2 0x280
#define B43_MMIO_PIO11_BASE3 0x2C0
#define B43_MMIO_PIO11_BASE4 0x300
#define B43_MMIO_PIO11_BASE5 0x340
#define B43_MMIO_PHY_VER 0x3E0
#define B43_MMIO_PHY_RADIO 0x3E2
#define B43_MMIO_PHY0 0x3E6
......@@ -442,7 +459,6 @@ enum {
};
struct b43_dmaring;
struct b43_pioqueue;
/* The firmware file header */
#define B43_FW_TYPE_UCODE 'u'
......@@ -598,6 +614,20 @@ struct b43_dma {
struct b43_dmaring *rx_ring;
};
struct b43_pio_txqueue;
struct b43_pio_rxqueue;
/* Data structures for PIO transmission, per 80211 core. */
struct b43_pio {
struct b43_pio_txqueue *tx_queue_AC_BK; /* Background */
struct b43_pio_txqueue *tx_queue_AC_BE; /* Best Effort */
struct b43_pio_txqueue *tx_queue_AC_VI; /* Video */
struct b43_pio_txqueue *tx_queue_AC_VO; /* Voice */
struct b43_pio_txqueue *tx_queue_mcast; /* Multicast */
struct b43_pio_rxqueue *rx_queue;
};
/* Context information for a noise calculation (Link Quality). */
struct b43_noise_calculation {
u8 channel_at_start;
......@@ -773,8 +803,15 @@ struct b43_wldev {
/* PHY/Radio device. */
struct b43_phy phy;
/* DMA engines. */
struct b43_dma dma;
union {
/* DMA engines. */
struct b43_dma dma;
/* PIO engines. */
struct b43_pio pio;
};
/* Use b43_using_pio_transfers() to check whether we are using
* DMA or PIO data transfers. */
bool __using_pio_transfers;
/* Various statistics about the physical device. */
struct b43_stats stats;
......@@ -858,6 +895,22 @@ static inline void b43_write32(struct b43_wldev *dev, u16 offset, u32 value)
ssb_write32(dev->dev, offset, value);
}
static inline bool b43_using_pio_transfers(struct b43_wldev *dev)
{
#ifdef CONFIG_B43_PIO
return dev->__using_pio_transfers;
#else
return 0;
#endif
}
#ifdef CONFIG_B43_FORCE_PIO
# define B43_FORCE_PIO 1
#else
# define B43_FORCE_PIO 0
#endif
/* Message printing */
void b43info(struct b43_wl *wl, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
......
......@@ -550,7 +550,6 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring,
struct b43_dmadesc_meta *meta, gfp_t gfp_flags)
{
struct b43_rxhdr_fw4 *rxhdr;
struct b43_hwtxstatus *txstat;
dma_addr_t dmaaddr;
struct sk_buff *skb;
......@@ -586,8 +585,6 @@ static int setup_rx_descbuffer(struct b43_dmaring *ring,
rxhdr = (struct b43_rxhdr_fw4 *)(skb->data);
rxhdr->frame_len = 0;
txstat = (struct b43_hwtxstatus *)(skb->data);
txstat->cookie = 0;
return 0;
}
......@@ -776,6 +773,18 @@ static u64 supported_dma_mask(struct b43_wldev *dev)
return DMA_30BIT_MASK;
}
static enum b43_dmatype dma_mask_to_engine_type(u64 dmamask)
{
if (dmamask == DMA_30BIT_MASK)
return B43_DMA_30BIT;
if (dmamask == DMA_32BIT_MASK)
return B43_DMA_32BIT;
if (dmamask == DMA_64BIT_MASK)
return B43_DMA_64BIT;
B43_WARN_ON(1);
return B43_DMA_30BIT;
}
/* Main initialization function. */
static
struct b43_dmaring *b43_setup_dmaring(struct b43_wldev *dev,
......@@ -956,7 +965,11 @@ static void b43_destroy_dmaring(struct b43_dmaring *ring,
void b43_dma_free(struct b43_wldev *dev)
{
struct b43_dma *dma = &dev->dma;
struct b43_dma *dma;
if (b43_using_pio_transfers(dev))
return;
dma = &dev->dma;
destroy_ring(dma, rx_ring);
destroy_ring(dma, tx_ring_AC_BK);
......@@ -974,19 +987,7 @@ int b43_dma_init(struct b43_wldev *dev)
enum b43_dmatype type;
dmamask = supported_dma_mask(dev);
switch (dmamask) {
default:
B43_WARN_ON(1);
case DMA_30BIT_MASK:
type = B43_DMA_30BIT;
break;
case DMA_32BIT_MASK:
type = B43_DMA_32BIT;
break;
case DMA_64BIT_MASK:
type = B43_DMA_64BIT;
break;
}
type = dma_mask_to_engine_type(dmamask);
err = ssb_dma_set_mask(dev->dev, dmamask);
if (err) {
b43err(dev->wl, "The machine/kernel does not support "
......@@ -1113,7 +1114,6 @@ static int dma_tx_fragment(struct b43_dmaring *ring,
size_t hdrsize = b43_txhdr_size(ring->dev);
#define SLOTS_PER_PACKET 2
B43_WARN_ON(skb_shinfo(skb)->nr_frags);
old_top_slot = ring->current_slot;
old_used_slots = ring->used_slots;
......@@ -1257,11 +1257,6 @@ int b43_dma_tx(struct b43_wldev *dev,
int err = 0;
unsigned long flags;
if (unlikely(skb->len < 2 + 2 + 6)) {
/* Too short, this can't be a valid frame. */
return -EINVAL;
}
hdr = (struct ieee80211_hdr *)skb->data;
if (ctl->flags & IEEE80211_TXCTL_SEND_AFTER_DTIM) {
/* The multicast ring will be sent after the DTIM */
......@@ -1319,38 +1314,6 @@ out_unlock:
return err;
}
static void b43_fill_txstatus_report(struct b43_dmaring *ring,
struct ieee80211_tx_status *report,
const struct b43_txstatus *status)
{
bool frame_failed = 0;
if (status->acked) {
/* The frame was ACKed. */
report->flags |= IEEE80211_TX_STATUS_ACK;
} else {
/* The frame was not ACKed... */
if (!(report->control.flags & IEEE80211_TXCTL_NO_ACK)) {
/* ...but we expected an ACK. */
frame_failed = 1;
report->excessive_retries = 1;
}
}
if (status->frame_count == 0) {
/* The frame was not transmitted at all. */
report->retry_count = 0;
} else {
report->retry_count = status->frame_count - 1;
#ifdef CONFIG_B43_DEBUG
if (frame_failed)
ring->nr_failed_tx_packets++;
else
ring->nr_succeed_tx_packets++;
ring->nr_total_packet_tries += status->frame_count;
#endif /* DEBUG */
}
}
/* Called with IRQs disabled. */
void b43_dma_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status)
......@@ -1360,6 +1323,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
struct b43_dmadesc_generic *desc;
struct b43_dmadesc_meta *meta;
int slot;
bool frame_succeed;
ring = parse_cookie(dev, status->cookie, &slot);
if (unlikely(!ring))
......@@ -1386,7 +1350,15 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
* status of the transmission.
* Some fields of txstat are already filled in dma_tx().
*/
b43_fill_txstatus_report(ring, &(meta->txstat), status);
frame_succeed = b43_fill_txstatus_report(
&(meta->txstat), status);
#ifdef CONFIG_B43_DEBUG
if (frame_succeed)
ring->nr_succeed_tx_packets++;
else
ring->nr_failed_tx_packets++;
ring->nr_total_packet_tries += status->frame_count;
#endif /* DEBUG */
ieee80211_tx_status_irqsafe(dev->wl->hw, meta->skb,
&(meta->txstat));
/* skb is freed by ieee80211_tx_status_irqsafe() */
......@@ -1573,3 +1545,39 @@ void b43_dma_tx_resume(struct b43_wldev *dev)
b43_dma_tx_resume_ring(dev->dma.tx_ring_AC_BK);
b43_power_saving_ctl_bits(dev, 0);
}
#ifdef CONFIG_B43_PIO
static void direct_fifo_rx(struct b43_wldev *dev, enum b43_dmatype type,
u16 mmio_base, bool enable)
{
u32 ctl;
if (type == B43_DMA_64BIT) {
ctl = b43_read32(dev, mmio_base + B43_DMA64_RXCTL);
ctl &= ~B43_DMA64_RXDIRECTFIFO;
if (enable)
ctl |= B43_DMA64_RXDIRECTFIFO;
b43_write32(dev, mmio_base + B43_DMA64_RXCTL, ctl);
} else {
ctl = b43_read32(dev, mmio_base + B43_DMA32_RXCTL);
ctl &= ~B43_DMA32_RXDIRECTFIFO;
if (enable)
ctl |= B43_DMA32_RXDIRECTFIFO;
b43_write32(dev, mmio_base + B43_DMA32_RXCTL, ctl);
}
}
/* Enable/Disable Direct FIFO Receive Mode (PIO) on a RX engine.
* This is called from PIO code, so DMA structures are not available. */
void b43_dma_direct_fifo_rx(struct b43_wldev *dev,
unsigned int engine_index, bool enable)
{
enum b43_dmatype type;
u16 mmio_base;
type = dma_mask_to_engine_type(supported_dma_mask(dev));
mmio_base = b43_dmacontroller_base(type, engine_index);
direct_fifo_rx(dev, type, mmio_base, enable);
}
#endif /* CONFIG_B43_PIO */
......@@ -291,4 +291,7 @@ void b43_dma_handle_txstatus(struct b43_wldev *dev,
void b43_dma_rx(struct b43_dmaring *ring);
void b43_dma_direct_fifo_rx(struct b43_wldev *dev,
unsigned int engine_index, bool enable);
#endif /* B43_DMA_H_ */
......@@ -47,6 +47,7 @@
#include "debugfs.h"
#include "phy.h"
#include "dma.h"
#include "pio.h"
#include "sysfs.h"
#include "xmit.h"
#include "lo.h"
......@@ -1593,8 +1594,12 @@ static void b43_interrupt_tasklet(struct b43_wldev *dev)
handle_irq_noise(dev);
/* Check the DMA reason registers for received data. */
if (dma_reason[0] & B43_DMAIRQ_RX_DONE)
b43_dma_rx(dev->dma.rx_ring);
if (dma_reason[0] & B43_DMAIRQ_RX_DONE) {
if (b43_using_pio_transfers(dev))
b43_pio_rx(dev->pio.rx_queue);
else
b43_dma_rx(dev->dma.rx_ring);
}
B43_WARN_ON(dma_reason[1] & B43_DMAIRQ_RX_DONE);
B43_WARN_ON(dma_reason[2] & B43_DMAIRQ_RX_DONE);
B43_WARN_ON(dma_reason[3] & B43_DMAIRQ_RX_DONE);
......@@ -2698,12 +2703,21 @@ static int b43_op_tx(struct ieee80211_hw *hw,
struct b43_wldev *dev = wl->current_dev;
int err = -ENODEV;
if (unlikely(skb->len < 2 + 2 + 6)) {
/* Too short, this can't be a valid frame. */
return -EINVAL;
}
B43_WARN_ON(skb_shinfo(skb)->nr_frags);
if (unlikely(!dev))
goto out;
if (unlikely(b43_status(dev) < B43_STAT_STARTED))
goto out;
/* DMA-TX is done without a global lock. */
err = b43_dma_tx(dev, skb, ctl);
/* TX is done without a global lock. */
if (b43_using_pio_transfers(dev))
err = b43_pio_tx(dev, skb, ctl);
else
err = b43_dma_tx(dev, skb, ctl);
out:
if (unlikely(err))
return NETDEV_TX_BUSY;
......@@ -2897,7 +2911,10 @@ static int b43_op_get_tx_stats(struct ieee80211_hw *hw,
goto out;
spin_lock_irqsave(&wl->irq_lock, flags);
if (likely(b43_status(dev) >= B43_STAT_STARTED)) {
b43_dma_get_tx_stats(dev, stats);
if (b43_using_pio_transfers(dev))
b43_pio_get_tx_stats(dev, stats);
else
b43_dma_get_tx_stats(dev, stats);
err = 0;
}
spin_unlock_irqrestore(&wl->irq_lock, flags);
......@@ -3366,6 +3383,7 @@ static void b43_wireless_core_stop(struct b43_wldev *dev)
b43_set_status(dev, B43_STAT_INITIALIZED);
b43_pio_stop(dev);
mutex_unlock(&wl->mutex);
/* Must unlock as it would otherwise deadlock. No races here.
* Cancel the possibly running self-rearming periodic work. */
......@@ -3683,6 +3701,7 @@ static void b43_wireless_core_exit(struct b43_wldev *dev)
b43_rng_exit(dev->wl, false);
}
b43_dma_free(dev);
b43_pio_free(dev);
b43_chip_exit(dev);
b43_radio_turn_off(dev, 1);
b43_switch_analog(dev, 0);
......@@ -3780,7 +3799,13 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
/* Maximum Contention Window */
b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF);
err = b43_dma_init(dev);
if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || B43_FORCE_PIO) {
dev->__using_pio_transfers = 1;
err = b43_pio_init(dev);
} else {
dev->__using_pio_transfers = 0;
err = b43_dma_init(dev);
}
if (err)
goto err_chip_exit;
b43_qos_init(dev);
......
This diff is collapsed.
#ifndef B43_PIO_H_
#define B43_PIO_H_
#include "b43.h"
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/list.h>
#include <linux/skbuff.h>
/*** Registers for PIO queues up to revision 7. ***/
/* TX queue. */
#define B43_PIO_TXCTL 0x00
#define B43_PIO_TXCTL_WRITELO 0x0001
#define B43_PIO_TXCTL_WRITEHI 0x0002
#define B43_PIO_TXCTL_EOF 0x0004
#define B43_PIO_TXCTL_FREADY 0x0008
#define B43_PIO_TXCTL_FLUSHREQ 0x0020
#define B43_PIO_TXCTL_FLUSHPEND 0x0040
#define B43_PIO_TXCTL_SUSPREQ 0x0080
#define B43_PIO_TXCTL_QSUSP 0x0100
#define B43_PIO_TXCTL_COMMCNT 0xFC00
#define B43_PIO_TXCTL_COMMCNT_SHIFT 10
#define B43_PIO_TXDATA 0x02
#define B43_PIO_TXQBUFSIZE 0x04
/* RX queue. */
#define B43_PIO_RXCTL 0x00
#define B43_PIO_RXCTL_FRAMERDY 0x0001
#define B43_PIO_RXCTL_DATARDY 0x0002
#define B43_PIO_RXDATA 0x02
/*** Registers for PIO queues revision 8 and later. ***/
/* TX queue */
#define B43_PIO8_TXCTL 0x00
#define B43_PIO8_TXCTL_0_7 0x00000001
#define B43_PIO8_TXCTL_8_15 0x00000002
#define B43_PIO8_TXCTL_16_23 0x00000004
#define B43_PIO8_TXCTL_24_31 0x00000008
#define B43_PIO8_TXCTL_EOF 0x00000010
#define B43_PIO8_TXCTL_FREADY 0x00000080
#define B43_PIO8_TXCTL_SUSPREQ 0x00000100
#define B43_PIO8_TXCTL_QSUSP 0x00000200
#define B43_PIO8_TXCTL_FLUSHREQ 0x00000400
#define B43_PIO8_TXCTL_FLUSHPEND 0x00000800
#define B43_PIO8_TXDATA 0x04
/* RX queue */
#define B43_PIO8_RXCTL 0x00
#define B43_PIO8_RXCTL_FRAMERDY 0x00000001
#define B43_PIO8_RXCTL_DATARDY 0x00000002
#define B43_PIO8_RXDATA 0x04
/* The maximum number of TX-packets the HW can handle. */
#define B43_PIO_MAX_NR_TXPACKETS 32
#ifdef CONFIG_B43_PIO
struct b43_pio_txpacket {
/* Pointer to the TX queue we belong to. */
struct b43_pio_txqueue *queue;
/* The TX data packet. */
struct sk_buff *skb;
/* The status meta data. */
struct ieee80211_tx_status txstat;
/* Index in the (struct b43_pio_txqueue)->packets array. */
u8 index;
struct list_head list;
};
struct b43_pio_txqueue {
struct b43_wldev *dev;
spinlock_t lock;
u16 mmio_base;
/* The device queue buffer size in bytes. */
u16 buffer_size;
/* The number of used bytes in the device queue buffer. */
u16 buffer_used;
/* The number of packets that can still get queued.
* This is decremented on queueing a packet and incremented
* after receiving the transmit status. */
u16 free_packet_slots;
/* True, if the mac80211 queue was stopped due to overflow at TX. */
bool stopped;
/* Our b43 queue index number */
u8 index;
/* The mac80211 QoS queue priority. */
u8 queue_prio;
/* Buffer for TX packet meta data. */
struct b43_pio_txpacket packets[B43_PIO_MAX_NR_TXPACKETS];
struct list_head packets_list;
/* Total number of transmitted packets. */
unsigned int nr_tx_packets;
/* Shortcut to the 802.11 core revision. This is to
* avoid horrible pointer dereferencing in the fastpaths. */
u8 rev;
};
struct b43_pio_rxqueue {
struct b43_wldev *dev;
spinlock_t lock;
u16 mmio_base;
/* Work to reduce latency issues on RX. */
struct work_struct rx_work;
/* Shortcut to the 802.11 core revision. This is to
* avoid horrible pointer dereferencing in the fastpaths. */
u8 rev;
};
static inline u16 b43_piotx_read16(struct b43_pio_txqueue *q, u16 offset)
{
return b43_read16(q->dev, q->mmio_base + offset);
}
static inline u32 b43_piotx_read32(struct b43_pio_txqueue *q, u16 offset)
{
return b43_read32(q->dev, q->mmio_base + offset);
}
static inline void b43_piotx_write16(struct b43_pio_txqueue *q,
u16 offset, u16 value)
{
b43_write16(q->dev, q->mmio_base + offset, value);
}
static inline void b43_piotx_write32(struct b43_pio_txqueue *q,
u16 offset, u32 value)
{
b43_write32(q->dev, q->mmio_base + offset, value);
}
static inline u16 b43_piorx_read16(struct b43_pio_rxqueue *q, u16 offset)
{
return b43_read16(q->dev, q->mmio_base + offset);
}
static inline u32 b43_piorx_read32(struct b43_pio_rxqueue *q, u16 offset)
{
return b43_read32(q->dev, q->mmio_base + offset);
}
static inline void b43_piorx_write16(struct b43_pio_rxqueue *q,
u16 offset, u16 value)
{
b43_write16(q->dev, q->mmio_base + offset, value);
}
static inline void b43_piorx_write32(struct b43_pio_rxqueue *q,
u16 offset, u32 value)
{
b43_write32(q->dev, q->mmio_base + offset, value);
}
int b43_pio_init(struct b43_wldev *dev);
void b43_pio_stop(struct b43_wldev *dev);
void b43_pio_free(struct b43_wldev *dev);
int b43_pio_tx(struct b43_wldev *dev,
struct sk_buff *skb, struct ieee80211_tx_control *ctl);
void b43_pio_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status);
void b43_pio_get_tx_stats(struct b43_wldev *dev,
struct ieee80211_tx_queue_stats *stats);
void b43_pio_rx(struct b43_pio_rxqueue *q);
void b43_pio_tx_suspend(struct b43_wldev *dev);
void b43_pio_tx_resume(struct b43_wldev *dev);
#else /* CONFIG_B43_PIO */
static inline int b43_pio_init(struct b43_wldev *dev)
{
return 0;
}
static inline void b43_pio_free(struct b43_wldev *dev)
{
}
static inline void b43_pio_stop(struct b43_wldev *dev)
{
}
static inline int b43_pio_tx(struct b43_wldev *dev,
struct sk_buff *skb,
struct ieee80211_tx_control *ctl)
{
return 0;
}
static inline void b43_pio_handle_txstatus(struct b43_wldev *dev,
const struct b43_txstatus *status)
{
}
static inline void b43_pio_get_tx_stats(struct b43_wldev *dev,