b43: Add Soft-MAC SDIO device support

This adds support for Soft-MAC SDIO devices to b43.
The driver still lacks some fixes for SDIO devices, so it's currently
marked as BROKEN.

Signed-off-by: Albert Herranz <albert_herranz@yahoo.es>
Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Albert Herranz 2009-09-10 19:34:49 +02:00 committed by John W. Linville
parent a78b3bb2f3
commit 3dbba8e281
5 changed files with 324 additions and 16 deletions

View File

@ -61,11 +61,28 @@ config B43_PCMCIA
If unsure, say N.
config B43_SDIO
bool "Broadcom 43xx SDIO device support (EXPERIMENTAL)"
depends on B43 && SSB_SDIOHOST_POSSIBLE && EXPERIMENTAL && BROKEN
select SSB_SDIOHOST
---help---
Broadcom 43xx device support for Soft-MAC SDIO devices.
With this config option you can drive Soft-MAC b43 cards with a
Secure Digital I/O interface.
This includes the WLAN daughter card found on the Nintendo Wii
video game console.
Note that this does not support Broadcom 43xx Full-MAC devices.
It's safe to select Y here, even if you don't have a B43 SDIO device.
If unsure, say N.
# Data transfers to the device via PIO
# This is only needed on PCMCIA devices. All others can do DMA properly.
# This is only needed on PCMCIA and SDIO devices. All others can do DMA properly.
config B43_PIO
bool
depends on B43 && (B43_PCMCIA || B43_FORCE_PIO)
depends on B43 && (B43_SDIO || B43_PCMCIA || B43_FORCE_PIO)
select SSB_BLOCKIO
default y

View File

@ -16,6 +16,7 @@ b43-$(CONFIG_B43_PIO) += pio.o
b43-y += rfkill.o
b43-$(CONFIG_B43_LEDS) += leds.o
b43-$(CONFIG_B43_PCMCIA) += pcmcia.o
b43-$(CONFIG_B43_SDIO) += sdio.o
b43-$(CONFIG_B43_DEBUG) += debugfs.o
obj-$(CONFIG_B43) += b43.o

View File

@ -8,6 +8,9 @@
Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
SDIO support
Copyright (c) 2009 Albert Herranz <albert_herranz@yahoo.es>
Some parts of the code in this file are derived from the ipw2200
driver Copyright(c) 2003 - 2004 Intel Corporation.
@ -53,6 +56,8 @@
#include "xmit.h"
#include "lo.h"
#include "pcmcia.h"
#include "sdio.h"
#include <linux/mmc/sdio_func.h>
MODULE_DESCRIPTION("Broadcom B43 wireless driver");
MODULE_AUTHOR("Martin Langer");
@ -1587,7 +1592,7 @@ static void b43_beacon_update_trigger_work(struct work_struct *work)
mutex_lock(&wl->mutex);
dev = wl->current_dev;
if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) {
if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) {
if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) {
/* wl->mutex is enough. */
b43_do_beacon_update_trigger_work(dev);
mmiowb();
@ -1905,6 +1910,27 @@ static irqreturn_t b43_interrupt_handler(int irq, void *dev_id)
return ret;
}
/* SDIO interrupt handler. This runs in process context. */
static void b43_sdio_interrupt_handler(struct b43_wldev *dev)
{
struct b43_wl *wl = dev->wl;
struct sdio_func *func = dev->dev->bus->host_sdio;
irqreturn_t ret;
if (unlikely(b43_status(dev) < B43_STAT_STARTED))
return;
mutex_lock(&wl->mutex);
sdio_release_host(func);
ret = b43_do_interrupt(dev);
if (ret == IRQ_WAKE_THREAD)
b43_do_interrupt_thread(dev);
sdio_claim_host(func);
mutex_unlock(&wl->mutex);
}
void b43_do_release_fw(struct b43_firmware_file *fw)
{
release_firmware(fw->data);
@ -3824,7 +3850,7 @@ redo:
/* Disable interrupts on the device. */
b43_set_status(dev, B43_STAT_INITIALIZED);
if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) {
if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) {
/* wl->mutex is locked. That is enough. */
b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0);
b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */
@ -3854,7 +3880,10 @@ redo:
dev_kfree_skb(skb_dequeue(&wl->tx_queue));
b43_mac_suspend(dev);
free_irq(dev->dev->irq, dev);
if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO)
b43_sdio_free_irq(dev);
else
free_irq(dev->dev->irq, dev);
b43_leds_exit(dev);
b43dbg(wl, "Wireless interface stopped\n");
@ -3869,12 +3898,20 @@ static int b43_wireless_core_start(struct b43_wldev *dev)
B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED);
drain_txstatus_queue(dev);
err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler,
b43_interrupt_thread_handler,
IRQF_SHARED, KBUILD_MODNAME, dev);
if (err) {
b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq);
goto out;
if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) {
err = b43_sdio_request_irq(dev, b43_sdio_interrupt_handler);
if (err) {
b43err(dev->wl, "Cannot request SDIO IRQ\n");
goto out;
}
} else {
err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler,
b43_interrupt_thread_handler,
IRQF_SHARED, KBUILD_MODNAME, dev);
if (err) {
b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq);
goto out;
}
}
/* We are ready to run. */
@ -4266,7 +4303,9 @@ 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);
if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || B43_FORCE_PIO) {
if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) ||
(dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) ||
B43_FORCE_PIO) {
dev->__using_pio_transfers = 1;
err = b43_pio_init(dev);
} else {
@ -4942,7 +4981,7 @@ static struct ssb_driver b43_ssb_driver = {
static void b43_print_driverinfo(void)
{
const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "",
*feat_leds = "";
*feat_leds = "", *feat_sdio = "";
#ifdef CONFIG_B43_PCI_AUTOSELECT
feat_pci = "P";
@ -4955,12 +4994,15 @@ static void b43_print_driverinfo(void)
#endif
#ifdef CONFIG_B43_LEDS
feat_leds = "L";
#endif
#ifdef CONFIG_B43_SDIO
feat_sdio = "S";
#endif
printk(KERN_INFO "Broadcom 43xx driver loaded "
"[ Features: %s%s%s%s, Firmware-ID: "
"[ Features: %s%s%s%s%s, Firmware-ID: "
B43_SUPPORTED_FIRMWARE_ID " ]\n",
feat_pci, feat_pcmcia, feat_nphy,
feat_leds);
feat_leds, feat_sdio);
}
static int __init b43_init(void)
@ -4971,13 +5013,18 @@ static int __init b43_init(void)
err = b43_pcmcia_init();
if (err)
goto err_dfs_exit;
err = ssb_driver_register(&b43_ssb_driver);
err = b43_sdio_init();
if (err)
goto err_pcmcia_exit;
err = ssb_driver_register(&b43_ssb_driver);
if (err)
goto err_sdio_exit;
b43_print_driverinfo();
return err;
err_sdio_exit:
b43_sdio_exit();
err_pcmcia_exit:
b43_pcmcia_exit();
err_dfs_exit:
@ -4988,6 +5035,7 @@ err_dfs_exit:
static void __exit b43_exit(void)
{
ssb_driver_unregister(&b43_ssb_driver);
b43_sdio_exit();
b43_pcmcia_exit();
b43_debugfs_exit();
}

View File

@ -0,0 +1,197 @@
/*
* Broadcom B43 wireless driver
*
* SDIO over Sonics Silicon Backplane bus glue for b43.
*
* Copyright (C) 2009 Albert Herranz
* Copyright (C) 2009 Michael Buesch <mb@bu3sch.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/ssb/ssb.h>
#include "sdio.h"
#include "b43.h"
#define HNBU_CHIPID 0x01 /* vendor & device id */
#define B43_SDIO_BLOCK_SIZE 64 /* rx fifo max size in bytes */
static const struct b43_sdio_quirk {
u16 vendor;
u16 device;
unsigned int quirks;
} b43_sdio_quirks[] = {
{ 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32, },
{ },
};
static unsigned int b43_sdio_get_quirks(u16 vendor, u16 device)
{
const struct b43_sdio_quirk *q;
for (q = b43_sdio_quirks; q->quirks; q++) {
if (vendor == q->vendor && device == q->device)
return q->quirks;
}
return 0;
}
static void b43_sdio_interrupt_dispatcher(struct sdio_func *func)
{
struct b43_sdio *sdio = sdio_get_drvdata(func);
struct b43_wldev *dev = sdio->irq_handler_opaque;
sdio->irq_handler(dev);
}
int b43_sdio_request_irq(struct b43_wldev *dev,
void (*handler)(struct b43_wldev *dev))
{
struct ssb_bus *bus = dev->dev->bus;
struct sdio_func *func = bus->host_sdio;
struct b43_sdio *sdio = sdio_get_drvdata(func);
int err;
sdio->irq_handler_opaque = dev;
sdio->irq_handler = handler;
sdio_claim_host(func);
err = sdio_claim_irq(func, b43_sdio_interrupt_dispatcher);
sdio_release_host(func);
return err;
}
void b43_sdio_free_irq(struct b43_wldev *dev)
{
struct ssb_bus *bus = dev->dev->bus;
struct sdio_func *func = bus->host_sdio;
struct b43_sdio *sdio = sdio_get_drvdata(func);
sdio_claim_host(func);
sdio_release_irq(func);
sdio_release_host(func);
sdio->irq_handler_opaque = NULL;
sdio->irq_handler = NULL;
}
static int b43_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct b43_sdio *sdio;
struct sdio_func_tuple *tuple;
u16 vendor = 0, device = 0;
int error;
/* Look for the card chip identifier. */
tuple = func->tuples;
while (tuple) {
switch (tuple->code) {
case 0x80:
switch (tuple->data[0]) {
case HNBU_CHIPID:
if (tuple->size != 5)
break;
vendor = tuple->data[1] | (tuple->data[2]<<8);
device = tuple->data[3] | (tuple->data[4]<<8);
dev_info(&func->dev, "Chip ID %04x:%04x\n",
vendor, device);
break;
default:
break;
}
break;
default:
break;
}
tuple = tuple->next;
}
if (!vendor || !device) {
error = -ENODEV;
goto out;
}
sdio_claim_host(func);
error = sdio_set_block_size(func, B43_SDIO_BLOCK_SIZE);
if (error) {
dev_err(&func->dev, "failed to set block size to %u bytes,"
" error %d\n", B43_SDIO_BLOCK_SIZE, error);
goto err_release_host;
}
error = sdio_enable_func(func);
if (error) {
dev_err(&func->dev, "failed to enable func, error %d\n", error);
goto err_release_host;
}
sdio_release_host(func);
sdio = kzalloc(sizeof(*sdio), GFP_KERNEL);
if (!sdio) {
error = -ENOMEM;
dev_err(&func->dev, "failed to allocate ssb bus\n");
goto err_disable_func;
}
error = ssb_bus_sdiobus_register(&sdio->ssb, func,
b43_sdio_get_quirks(vendor, device));
if (error) {
dev_err(&func->dev, "failed to register ssb sdio bus,"
" error %d\n", error);
goto err_free_ssb;
}
sdio_set_drvdata(func, sdio);
return 0;
err_free_ssb:
kfree(sdio);
err_disable_func:
sdio_disable_func(func);
err_release_host:
sdio_release_host(func);
out:
return error;
}
static void b43_sdio_remove(struct sdio_func *func)
{
struct b43_sdio *sdio = sdio_get_drvdata(func);
ssb_bus_unregister(&sdio->ssb);
sdio_disable_func(func);
kfree(sdio);
sdio_set_drvdata(func, NULL);
}
static const struct sdio_device_id b43_sdio_ids[] = {
{ SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */
{ },
};
static struct sdio_driver b43_sdio_driver = {
.name = "b43-sdio",
.id_table = b43_sdio_ids,
.probe = b43_sdio_probe,
.remove = b43_sdio_remove,
};
int b43_sdio_init(void)
{
return sdio_register_driver(&b43_sdio_driver);
}
void b43_sdio_exit(void)
{
sdio_unregister_driver(&b43_sdio_driver);
}

View File

@ -0,0 +1,45 @@
#ifndef B43_SDIO_H_
#define B43_SDIO_H_
#include <linux/ssb/ssb.h>
struct b43_wldev;
#ifdef CONFIG_B43_SDIO
struct b43_sdio {
struct ssb_bus ssb;
void *irq_handler_opaque;
void (*irq_handler)(struct b43_wldev *dev);
};
int b43_sdio_request_irq(struct b43_wldev *dev,
void (*handler)(struct b43_wldev *dev));
void b43_sdio_free_irq(struct b43_wldev *dev);
int b43_sdio_init(void);
void b43_sdio_exit(void);
#else /* CONFIG_B43_SDIO */
int b43_sdio_request_irq(struct b43_wldev *dev,
void (*handler)(struct b43_wldev *dev))
{
return -ENODEV;
}
void b43_sdio_free_irq(struct b43_wldev *dev)
{
}
static inline int b43_sdio_init(void)
{
return 0;
}
static inline void b43_sdio_exit(void)
{
}
#endif /* CONFIG_B43_SDIO */
#endif /* B43_SDIO_H_ */