1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Broadcom B43 wireless driver
5 * SDIO over Sonics Silicon Backplane bus glue for b43.
7 * Copyright (C) 2009 Albert Herranz
8 * Copyright (C) 2009 Michael Buesch <m@bues.ch>
11 #include <linux/kernel.h>
12 #include <linux/mmc/card.h>
13 #include <linux/mmc/sdio_func.h>
14 #include <linux/mmc/sdio_ids.h>
15 #include <linux/slab.h>
16 #include <linux/ssb/ssb.h>
22 #define HNBU_CHIPID 0x01 /* vendor & device id */
24 #define B43_SDIO_BLOCK_SIZE 64 /* rx fifo max size in bytes */
27 static const struct b43_sdio_quirk
{
31 } b43_sdio_quirks
[] = {
32 { 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32
, },
37 static unsigned int b43_sdio_get_quirks(u16 vendor
, u16 device
)
39 const struct b43_sdio_quirk
*q
;
41 for (q
= b43_sdio_quirks
; q
->quirks
; q
++) {
42 if (vendor
== q
->vendor
&& device
== q
->device
)
49 static void b43_sdio_interrupt_dispatcher(struct sdio_func
*func
)
51 struct b43_sdio
*sdio
= sdio_get_drvdata(func
);
52 struct b43_wldev
*dev
= sdio
->irq_handler_opaque
;
54 if (unlikely(b43_status(dev
) < B43_STAT_STARTED
))
57 sdio_release_host(func
);
58 sdio
->irq_handler(dev
);
59 sdio_claim_host(func
);
62 int b43_sdio_request_irq(struct b43_wldev
*dev
,
63 void (*handler
)(struct b43_wldev
*dev
))
65 struct ssb_bus
*bus
= dev
->dev
->sdev
->bus
;
66 struct sdio_func
*func
= bus
->host_sdio
;
67 struct b43_sdio
*sdio
= sdio_get_drvdata(func
);
70 sdio
->irq_handler_opaque
= dev
;
71 sdio
->irq_handler
= handler
;
72 sdio_claim_host(func
);
73 err
= sdio_claim_irq(func
, b43_sdio_interrupt_dispatcher
);
74 sdio_release_host(func
);
79 void b43_sdio_free_irq(struct b43_wldev
*dev
)
81 struct ssb_bus
*bus
= dev
->dev
->sdev
->bus
;
82 struct sdio_func
*func
= bus
->host_sdio
;
83 struct b43_sdio
*sdio
= sdio_get_drvdata(func
);
85 sdio_claim_host(func
);
86 sdio_release_irq(func
);
87 sdio_release_host(func
);
88 sdio
->irq_handler_opaque
= NULL
;
89 sdio
->irq_handler
= NULL
;
92 static int b43_sdio_probe(struct sdio_func
*func
,
93 const struct sdio_device_id
*id
)
95 struct b43_sdio
*sdio
;
96 struct sdio_func_tuple
*tuple
;
97 u16 vendor
= 0, device
= 0;
100 /* Look for the card chip identifier. */
101 tuple
= func
->tuples
;
103 switch (tuple
->code
) {
105 switch (tuple
->data
[0]) {
107 if (tuple
->size
!= 5)
109 vendor
= tuple
->data
[1] | (tuple
->data
[2]<<8);
110 device
= tuple
->data
[3] | (tuple
->data
[4]<<8);
111 dev_info(&func
->dev
, "Chip ID %04x:%04x\n",
123 if (!vendor
|| !device
) {
128 sdio_claim_host(func
);
129 error
= sdio_set_block_size(func
, B43_SDIO_BLOCK_SIZE
);
131 dev_err(&func
->dev
, "failed to set block size to %u bytes,"
132 " error %d\n", B43_SDIO_BLOCK_SIZE
, error
);
133 goto err_release_host
;
135 error
= sdio_enable_func(func
);
137 dev_err(&func
->dev
, "failed to enable func, error %d\n", error
);
138 goto err_release_host
;
140 sdio_release_host(func
);
142 sdio
= kzalloc(sizeof(*sdio
), GFP_KERNEL
);
145 dev_err(&func
->dev
, "failed to allocate ssb bus\n");
146 goto err_disable_func
;
148 error
= ssb_bus_sdiobus_register(&sdio
->ssb
, func
,
149 b43_sdio_get_quirks(vendor
, device
));
151 dev_err(&func
->dev
, "failed to register ssb sdio bus,"
152 " error %d\n", error
);
155 sdio_set_drvdata(func
, sdio
);
162 sdio_claim_host(func
);
163 sdio_disable_func(func
);
165 sdio_release_host(func
);
170 static void b43_sdio_remove(struct sdio_func
*func
)
172 struct b43_sdio
*sdio
= sdio_get_drvdata(func
);
174 ssb_bus_unregister(&sdio
->ssb
);
175 sdio_claim_host(func
);
176 sdio_disable_func(func
);
177 sdio_release_host(func
);
179 sdio_set_drvdata(func
, NULL
);
182 static const struct sdio_device_id b43_sdio_ids
[] = {
183 { SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */
184 { SDIO_DEVICE(0x0092, 0x0004) }, /* C-guys, Inc. EW-CG1102GC */
188 static struct sdio_driver b43_sdio_driver
= {
190 .id_table
= b43_sdio_ids
,
191 .probe
= b43_sdio_probe
,
192 .remove
= b43_sdio_remove
,
195 int b43_sdio_init(void)
197 return sdio_register_driver(&b43_sdio_driver
);
200 void b43_sdio_exit(void)
202 sdio_unregister_driver(&b43_sdio_driver
);