Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Albert Herranz and committed by
John W. Linville
3dbba8e2 a78b3bb2

+324 -16
+19 -2
drivers/net/wireless/b43/Kconfig
··· 61 61 62 62 If unsure, say N. 63 63 64 + config B43_SDIO 65 + bool "Broadcom 43xx SDIO device support (EXPERIMENTAL)" 66 + depends on B43 && SSB_SDIOHOST_POSSIBLE && EXPERIMENTAL && BROKEN 67 + select SSB_SDIOHOST 68 + ---help--- 69 + Broadcom 43xx device support for Soft-MAC SDIO devices. 70 + 71 + With this config option you can drive Soft-MAC b43 cards with a 72 + Secure Digital I/O interface. 73 + This includes the WLAN daughter card found on the Nintendo Wii 74 + video game console. 75 + Note that this does not support Broadcom 43xx Full-MAC devices. 76 + 77 + It's safe to select Y here, even if you don't have a B43 SDIO device. 78 + 79 + If unsure, say N. 80 + 64 81 # Data transfers to the device via PIO 65 - # This is only needed on PCMCIA devices. All others can do DMA properly. 82 + # This is only needed on PCMCIA and SDIO devices. All others can do DMA properly. 66 83 config B43_PIO 67 84 bool 68 - depends on B43 && (B43_PCMCIA || B43_FORCE_PIO) 85 + depends on B43 && (B43_SDIO || B43_PCMCIA || B43_FORCE_PIO) 69 86 select SSB_BLOCKIO 70 87 default y 71 88
+1
drivers/net/wireless/b43/Makefile
··· 16 16 b43-y += rfkill.o 17 17 b43-$(CONFIG_B43_LEDS) += leds.o 18 18 b43-$(CONFIG_B43_PCMCIA) += pcmcia.o 19 + b43-$(CONFIG_B43_SDIO) += sdio.o 19 20 b43-$(CONFIG_B43_DEBUG) += debugfs.o 20 21 21 22 obj-$(CONFIG_B43) += b43.o
+62 -14
drivers/net/wireless/b43/main.c
··· 8 8 Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org> 9 9 Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch> 10 10 11 + SDIO support 12 + Copyright (c) 2009 Albert Herranz <albert_herranz@yahoo.es> 13 + 11 14 Some parts of the code in this file are derived from the ipw2200 12 15 driver Copyright(c) 2003 - 2004 Intel Corporation. 13 16 ··· 56 53 #include "xmit.h" 57 54 #include "lo.h" 58 55 #include "pcmcia.h" 56 + #include "sdio.h" 57 + #include <linux/mmc/sdio_func.h> 59 58 60 59 MODULE_DESCRIPTION("Broadcom B43 wireless driver"); 61 60 MODULE_AUTHOR("Martin Langer"); ··· 1592 1587 mutex_lock(&wl->mutex); 1593 1588 dev = wl->current_dev; 1594 1589 if (likely(dev && (b43_status(dev) >= B43_STAT_INITIALIZED))) { 1595 - if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) { 1590 + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { 1596 1591 /* wl->mutex is enough. */ 1597 1592 b43_do_beacon_update_trigger_work(dev); 1598 1593 mmiowb(); ··· 1908 1903 spin_unlock(&dev->wl->hardirq_lock); 1909 1904 1910 1905 return ret; 1906 + } 1907 + 1908 + /* SDIO interrupt handler. This runs in process context. */ 1909 + static void b43_sdio_interrupt_handler(struct b43_wldev *dev) 1910 + { 1911 + struct b43_wl *wl = dev->wl; 1912 + struct sdio_func *func = dev->dev->bus->host_sdio; 1913 + irqreturn_t ret; 1914 + 1915 + if (unlikely(b43_status(dev) < B43_STAT_STARTED)) 1916 + return; 1917 + 1918 + mutex_lock(&wl->mutex); 1919 + sdio_release_host(func); 1920 + 1921 + ret = b43_do_interrupt(dev); 1922 + if (ret == IRQ_WAKE_THREAD) 1923 + b43_do_interrupt_thread(dev); 1924 + 1925 + sdio_claim_host(func); 1926 + mutex_unlock(&wl->mutex); 1911 1927 } 1912 1928 1913 1929 void b43_do_release_fw(struct b43_firmware_file *fw) ··· 3850 3824 3851 3825 /* Disable interrupts on the device. */ 3852 3826 b43_set_status(dev, B43_STAT_INITIALIZED); 3853 - if (0 /*FIXME dev->dev->bus->bustype == SSB_BUSTYPE_SDIO*/) { 3827 + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { 3854 3828 /* wl->mutex is locked. That is enough. */ 3855 3829 b43_write32(dev, B43_MMIO_GEN_IRQ_MASK, 0); 3856 3830 b43_read32(dev, B43_MMIO_GEN_IRQ_MASK); /* Flush */ ··· 3880 3854 dev_kfree_skb(skb_dequeue(&wl->tx_queue)); 3881 3855 3882 3856 b43_mac_suspend(dev); 3883 - free_irq(dev->dev->irq, dev); 3857 + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) 3858 + b43_sdio_free_irq(dev); 3859 + else 3860 + free_irq(dev->dev->irq, dev); 3884 3861 b43_leds_exit(dev); 3885 3862 b43dbg(wl, "Wireless interface stopped\n"); 3886 3863 ··· 3898 3869 B43_WARN_ON(b43_status(dev) != B43_STAT_INITIALIZED); 3899 3870 3900 3871 drain_txstatus_queue(dev); 3901 - err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, 3902 - b43_interrupt_thread_handler, 3903 - IRQF_SHARED, KBUILD_MODNAME, dev); 3904 - if (err) { 3905 - b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); 3906 - goto out; 3872 + if (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) { 3873 + err = b43_sdio_request_irq(dev, b43_sdio_interrupt_handler); 3874 + if (err) { 3875 + b43err(dev->wl, "Cannot request SDIO IRQ\n"); 3876 + goto out; 3877 + } 3878 + } else { 3879 + err = request_threaded_irq(dev->dev->irq, b43_interrupt_handler, 3880 + b43_interrupt_thread_handler, 3881 + IRQF_SHARED, KBUILD_MODNAME, dev); 3882 + if (err) { 3883 + b43err(dev->wl, "Cannot request IRQ-%d\n", dev->dev->irq); 3884 + goto out; 3885 + } 3907 3886 } 3908 3887 3909 3888 /* We are ready to run. */ ··· 4303 4266 /* Maximum Contention Window */ 4304 4267 b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF); 4305 4268 4306 - if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || B43_FORCE_PIO) { 4269 + if ((dev->dev->bus->bustype == SSB_BUSTYPE_PCMCIA) || 4270 + (dev->dev->bus->bustype == SSB_BUSTYPE_SDIO) || 4271 + B43_FORCE_PIO) { 4307 4272 dev->__using_pio_transfers = 1; 4308 4273 err = b43_pio_init(dev); 4309 4274 } else { ··· 4981 4942 static void b43_print_driverinfo(void) 4982 4943 { 4983 4944 const char *feat_pci = "", *feat_pcmcia = "", *feat_nphy = "", 4984 - *feat_leds = ""; 4945 + *feat_leds = "", *feat_sdio = ""; 4985 4946 4986 4947 #ifdef CONFIG_B43_PCI_AUTOSELECT 4987 4948 feat_pci = "P"; ··· 4995 4956 #ifdef CONFIG_B43_LEDS 4996 4957 feat_leds = "L"; 4997 4958 #endif 4959 + #ifdef CONFIG_B43_SDIO 4960 + feat_sdio = "S"; 4961 + #endif 4998 4962 printk(KERN_INFO "Broadcom 43xx driver loaded " 4999 - "[ Features: %s%s%s%s, Firmware-ID: " 4963 + "[ Features: %s%s%s%s%s, Firmware-ID: " 5000 4964 B43_SUPPORTED_FIRMWARE_ID " ]\n", 5001 4965 feat_pci, feat_pcmcia, feat_nphy, 5002 - feat_leds); 4966 + feat_leds, feat_sdio); 5003 4967 } 5004 4968 5005 4969 static int __init b43_init(void) ··· 5013 4971 err = b43_pcmcia_init(); 5014 4972 if (err) 5015 4973 goto err_dfs_exit; 5016 - err = ssb_driver_register(&b43_ssb_driver); 4974 + err = b43_sdio_init(); 5017 4975 if (err) 5018 4976 goto err_pcmcia_exit; 4977 + err = ssb_driver_register(&b43_ssb_driver); 4978 + if (err) 4979 + goto err_sdio_exit; 5019 4980 b43_print_driverinfo(); 5020 4981 5021 4982 return err; 5022 4983 4984 + err_sdio_exit: 4985 + b43_sdio_exit(); 5023 4986 err_pcmcia_exit: 5024 4987 b43_pcmcia_exit(); 5025 4988 err_dfs_exit: ··· 5035 4988 static void __exit b43_exit(void) 5036 4989 { 5037 4990 ssb_driver_unregister(&b43_ssb_driver); 4991 + b43_sdio_exit(); 5038 4992 b43_pcmcia_exit(); 5039 4993 b43_debugfs_exit(); 5040 4994 }
+197
drivers/net/wireless/b43/sdio.c
··· 1 + /* 2 + * Broadcom B43 wireless driver 3 + * 4 + * SDIO over Sonics Silicon Backplane bus glue for b43. 5 + * 6 + * Copyright (C) 2009 Albert Herranz 7 + * Copyright (C) 2009 Michael Buesch <mb@bu3sch.de> 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License as published by 11 + * the Free Software Foundation; either version 2 of the License, or (at 12 + * your option) any later version. 13 + */ 14 + 15 + #include <linux/kernel.h> 16 + #include <linux/mmc/card.h> 17 + #include <linux/mmc/sdio_func.h> 18 + #include <linux/mmc/sdio_ids.h> 19 + #include <linux/ssb/ssb.h> 20 + 21 + #include "sdio.h" 22 + #include "b43.h" 23 + 24 + 25 + #define HNBU_CHIPID 0x01 /* vendor & device id */ 26 + 27 + #define B43_SDIO_BLOCK_SIZE 64 /* rx fifo max size in bytes */ 28 + 29 + 30 + static const struct b43_sdio_quirk { 31 + u16 vendor; 32 + u16 device; 33 + unsigned int quirks; 34 + } b43_sdio_quirks[] = { 35 + { 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32, }, 36 + { }, 37 + }; 38 + 39 + 40 + static unsigned int b43_sdio_get_quirks(u16 vendor, u16 device) 41 + { 42 + const struct b43_sdio_quirk *q; 43 + 44 + for (q = b43_sdio_quirks; q->quirks; q++) { 45 + if (vendor == q->vendor && device == q->device) 46 + return q->quirks; 47 + } 48 + 49 + return 0; 50 + } 51 + 52 + static void b43_sdio_interrupt_dispatcher(struct sdio_func *func) 53 + { 54 + struct b43_sdio *sdio = sdio_get_drvdata(func); 55 + struct b43_wldev *dev = sdio->irq_handler_opaque; 56 + 57 + sdio->irq_handler(dev); 58 + } 59 + 60 + int b43_sdio_request_irq(struct b43_wldev *dev, 61 + void (*handler)(struct b43_wldev *dev)) 62 + { 63 + struct ssb_bus *bus = dev->dev->bus; 64 + struct sdio_func *func = bus->host_sdio; 65 + struct b43_sdio *sdio = sdio_get_drvdata(func); 66 + int err; 67 + 68 + sdio->irq_handler_opaque = dev; 69 + sdio->irq_handler = handler; 70 + sdio_claim_host(func); 71 + err = sdio_claim_irq(func, b43_sdio_interrupt_dispatcher); 72 + sdio_release_host(func); 73 + 74 + return err; 75 + } 76 + 77 + void b43_sdio_free_irq(struct b43_wldev *dev) 78 + { 79 + struct ssb_bus *bus = dev->dev->bus; 80 + struct sdio_func *func = bus->host_sdio; 81 + struct b43_sdio *sdio = sdio_get_drvdata(func); 82 + 83 + sdio_claim_host(func); 84 + sdio_release_irq(func); 85 + sdio_release_host(func); 86 + sdio->irq_handler_opaque = NULL; 87 + sdio->irq_handler = NULL; 88 + } 89 + 90 + static int b43_sdio_probe(struct sdio_func *func, 91 + const struct sdio_device_id *id) 92 + { 93 + struct b43_sdio *sdio; 94 + struct sdio_func_tuple *tuple; 95 + u16 vendor = 0, device = 0; 96 + int error; 97 + 98 + /* Look for the card chip identifier. */ 99 + tuple = func->tuples; 100 + while (tuple) { 101 + switch (tuple->code) { 102 + case 0x80: 103 + switch (tuple->data[0]) { 104 + case HNBU_CHIPID: 105 + if (tuple->size != 5) 106 + break; 107 + vendor = tuple->data[1] | (tuple->data[2]<<8); 108 + device = tuple->data[3] | (tuple->data[4]<<8); 109 + dev_info(&func->dev, "Chip ID %04x:%04x\n", 110 + vendor, device); 111 + break; 112 + default: 113 + break; 114 + } 115 + break; 116 + default: 117 + break; 118 + } 119 + tuple = tuple->next; 120 + } 121 + if (!vendor || !device) { 122 + error = -ENODEV; 123 + goto out; 124 + } 125 + 126 + sdio_claim_host(func); 127 + error = sdio_set_block_size(func, B43_SDIO_BLOCK_SIZE); 128 + if (error) { 129 + dev_err(&func->dev, "failed to set block size to %u bytes," 130 + " error %d\n", B43_SDIO_BLOCK_SIZE, error); 131 + goto err_release_host; 132 + } 133 + error = sdio_enable_func(func); 134 + if (error) { 135 + dev_err(&func->dev, "failed to enable func, error %d\n", error); 136 + goto err_release_host; 137 + } 138 + sdio_release_host(func); 139 + 140 + sdio = kzalloc(sizeof(*sdio), GFP_KERNEL); 141 + if (!sdio) { 142 + error = -ENOMEM; 143 + dev_err(&func->dev, "failed to allocate ssb bus\n"); 144 + goto err_disable_func; 145 + } 146 + error = ssb_bus_sdiobus_register(&sdio->ssb, func, 147 + b43_sdio_get_quirks(vendor, device)); 148 + if (error) { 149 + dev_err(&func->dev, "failed to register ssb sdio bus," 150 + " error %d\n", error); 151 + goto err_free_ssb; 152 + } 153 + sdio_set_drvdata(func, sdio); 154 + 155 + return 0; 156 + 157 + err_free_ssb: 158 + kfree(sdio); 159 + err_disable_func: 160 + sdio_disable_func(func); 161 + err_release_host: 162 + sdio_release_host(func); 163 + out: 164 + return error; 165 + } 166 + 167 + static void b43_sdio_remove(struct sdio_func *func) 168 + { 169 + struct b43_sdio *sdio = sdio_get_drvdata(func); 170 + 171 + ssb_bus_unregister(&sdio->ssb); 172 + sdio_disable_func(func); 173 + kfree(sdio); 174 + sdio_set_drvdata(func, NULL); 175 + } 176 + 177 + static const struct sdio_device_id b43_sdio_ids[] = { 178 + { SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */ 179 + { }, 180 + }; 181 + 182 + static struct sdio_driver b43_sdio_driver = { 183 + .name = "b43-sdio", 184 + .id_table = b43_sdio_ids, 185 + .probe = b43_sdio_probe, 186 + .remove = b43_sdio_remove, 187 + }; 188 + 189 + int b43_sdio_init(void) 190 + { 191 + return sdio_register_driver(&b43_sdio_driver); 192 + } 193 + 194 + void b43_sdio_exit(void) 195 + { 196 + sdio_unregister_driver(&b43_sdio_driver); 197 + }
+45
drivers/net/wireless/b43/sdio.h
··· 1 + #ifndef B43_SDIO_H_ 2 + #define B43_SDIO_H_ 3 + 4 + #include <linux/ssb/ssb.h> 5 + 6 + struct b43_wldev; 7 + 8 + 9 + #ifdef CONFIG_B43_SDIO 10 + 11 + struct b43_sdio { 12 + struct ssb_bus ssb; 13 + void *irq_handler_opaque; 14 + void (*irq_handler)(struct b43_wldev *dev); 15 + }; 16 + 17 + int b43_sdio_request_irq(struct b43_wldev *dev, 18 + void (*handler)(struct b43_wldev *dev)); 19 + void b43_sdio_free_irq(struct b43_wldev *dev); 20 + 21 + int b43_sdio_init(void); 22 + void b43_sdio_exit(void); 23 + 24 + 25 + #else /* CONFIG_B43_SDIO */ 26 + 27 + 28 + int b43_sdio_request_irq(struct b43_wldev *dev, 29 + void (*handler)(struct b43_wldev *dev)) 30 + { 31 + return -ENODEV; 32 + } 33 + void b43_sdio_free_irq(struct b43_wldev *dev) 34 + { 35 + } 36 + static inline int b43_sdio_init(void) 37 + { 38 + return 0; 39 + } 40 + static inline void b43_sdio_exit(void) 41 + { 42 + } 43 + 44 + #endif /* CONFIG_B43_SDIO */ 45 + #endif /* B43_SDIO_H_ */