diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2007-10-17 16:24:24 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2007-10-17 16:51:13 -0400 |
commit | 727c26ed78b8f2b07452cf8bc9a07ff3f302ab48 (patch) | |
tree | 443606f29eb2b0044a4e1aa1ab4aebf52ce67896 /drivers/net/wireless | |
parent | 9e3866b54867c401da8d9a665d228cf0977ab5be (diff) |
net: libertas sdio driver
Add driver for Marvell's Libertas 8385 and 8686 wifi chips.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Acked-by: Dan Williams <dcbw@redhat.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/Kconfig | 6 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/defs.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/if_sdio.c | 1079 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/if_sdio.h | 45 |
5 files changed, 1134 insertions, 0 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 5a6fdfd0f140..dae5c8d5a318 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig | |||
@@ -282,6 +282,12 @@ config LIBERTAS_CS | |||
282 | ---help--- | 282 | ---help--- |
283 | A driver for Marvell Libertas 8385 CompactFlash devices. | 283 | A driver for Marvell Libertas 8385 CompactFlash devices. |
284 | 284 | ||
285 | config LIBERTAS_SDIO | ||
286 | tristate "Marvell Libertas 8385 and 8686 SDIO 802.11b/g cards" | ||
287 | depends on LIBERTAS && MMC | ||
288 | ---help--- | ||
289 | A driver for Marvell Libertas 8385 and 8686 SDIO devices. | ||
290 | |||
285 | config LIBERTAS_DEBUG | 291 | config LIBERTAS_DEBUG |
286 | bool "Enable full debugging output in the Libertas module." | 292 | bool "Enable full debugging output in the Libertas module." |
287 | depends on LIBERTAS | 293 | depends on LIBERTAS |
diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile index c469d569f090..0e2787691f96 100644 --- a/drivers/net/wireless/libertas/Makefile +++ b/drivers/net/wireless/libertas/Makefile | |||
@@ -7,7 +7,9 @@ libertas-objs := main.o wext.o \ | |||
7 | 7 | ||
8 | usb8xxx-objs += if_usb.o | 8 | usb8xxx-objs += if_usb.o |
9 | libertas_cs-objs += if_cs.o | 9 | libertas_cs-objs += if_cs.o |
10 | libertas_sdio-objs += if_sdio.o | ||
10 | 11 | ||
11 | obj-$(CONFIG_LIBERTAS) += libertas.o | 12 | obj-$(CONFIG_LIBERTAS) += libertas.o |
12 | obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o | 13 | obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o |
13 | obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o | 14 | obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o |
15 | obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o | ||
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index 7c5b7f7b45db..3a0c9beefcf8 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h | |||
@@ -39,6 +39,7 @@ | |||
39 | #define LBS_DEB_FW 0x00080000 | 39 | #define LBS_DEB_FW 0x00080000 |
40 | #define LBS_DEB_THREAD 0x00100000 | 40 | #define LBS_DEB_THREAD 0x00100000 |
41 | #define LBS_DEB_HEX 0x00200000 | 41 | #define LBS_DEB_HEX 0x00200000 |
42 | #define LBS_DEB_SDIO 0x00400000 | ||
42 | 43 | ||
43 | extern unsigned int libertas_debug; | 44 | extern unsigned int libertas_debug; |
44 | 45 | ||
@@ -80,6 +81,7 @@ do { if ((libertas_debug & (grp)) == (grp)) \ | |||
80 | #define lbs_deb_usbd(dev, fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usbd", "%s:" fmt, (dev)->bus_id, ##args) | 81 | #define lbs_deb_usbd(dev, fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usbd", "%s:" fmt, (dev)->bus_id, ##args) |
81 | #define lbs_deb_cs(fmt, args...) LBS_DEB_LL(LBS_DEB_CS, " cs", fmt, ##args) | 82 | #define lbs_deb_cs(fmt, args...) LBS_DEB_LL(LBS_DEB_CS, " cs", fmt, ##args) |
82 | #define lbs_deb_thread(fmt, args...) LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args) | 83 | #define lbs_deb_thread(fmt, args...) LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args) |
84 | #define lbs_deb_sdio(fmt, args...) LBS_DEB_LL(LBS_DEB_SDIO, " thread", fmt, ##args) | ||
83 | 85 | ||
84 | #define lbs_pr_info(format, args...) \ | 86 | #define lbs_pr_info(format, args...) \ |
85 | printk(KERN_INFO DRV_NAME": " format, ## args) | 87 | printk(KERN_INFO DRV_NAME": " format, ## args) |
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c new file mode 100644 index 000000000000..a8e17076e7de --- /dev/null +++ b/drivers/net/wireless/libertas/if_sdio.c | |||
@@ -0,0 +1,1079 @@ | |||
1 | /* | ||
2 | * linux/drivers/net/wireless/libertas/if_sdio.c | ||
3 | * | ||
4 | * Copyright 2007 Pierre Ossman | ||
5 | * | ||
6 | * Inspired by if_cs.c, Copyright 2007 Holger Schurig | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or (at | ||
11 | * your option) any later version. | ||
12 | * | ||
13 | * This hardware has more or less no CMD53 support, so all registers | ||
14 | * must be accessed using sdio_readb()/sdio_writeb(). | ||
15 | * | ||
16 | * Transfers must be in one transaction or the firmware goes bonkers. | ||
17 | * This means that the transfer must either be small enough to do a | ||
18 | * byte based transfer or it must be padded to a multiple of the | ||
19 | * current block size. | ||
20 | * | ||
21 | * As SDIO is still new to the kernel, it is unfortunately common with | ||
22 | * bugs in the host controllers related to that. One such bug is that | ||
23 | * controllers cannot do transfers that aren't a multiple of 4 bytes. | ||
24 | * If you don't have time to fix the host controller driver, you can | ||
25 | * work around the problem by modifying if_sdio_host_to_card() and | ||
26 | * if_sdio_card_to_host() to pad the data. | ||
27 | */ | ||
28 | |||
29 | #include <linux/moduleparam.h> | ||
30 | #include <linux/firmware.h> | ||
31 | #include <linux/netdevice.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/mmc/card.h> | ||
34 | #include <linux/mmc/sdio_func.h> | ||
35 | #include <linux/mmc/sdio_ids.h> | ||
36 | |||
37 | #include "host.h" | ||
38 | #include "decl.h" | ||
39 | #include "defs.h" | ||
40 | #include "dev.h" | ||
41 | #include "if_sdio.h" | ||
42 | |||
43 | static char *libertas_helper_name = NULL; | ||
44 | module_param_named(helper_name, libertas_helper_name, charp, 0644); | ||
45 | |||
46 | static char *libertas_fw_name = NULL; | ||
47 | module_param_named(fw_name, libertas_fw_name, charp, 0644); | ||
48 | |||
49 | static const struct sdio_device_id if_sdio_ids[] = { | ||
50 | { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_LIBERTAS) }, | ||
51 | { /* end: all zeroes */ }, | ||
52 | }; | ||
53 | |||
54 | MODULE_DEVICE_TABLE(sdio, if_sdio_ids); | ||
55 | |||
56 | struct if_sdio_model { | ||
57 | int model; | ||
58 | const char *helper; | ||
59 | const char *firmware; | ||
60 | }; | ||
61 | |||
62 | static struct if_sdio_model if_sdio_models[] = { | ||
63 | { | ||
64 | /* 8385 */ | ||
65 | .model = 0x04, | ||
66 | .helper = "sd8385_helper.bin", | ||
67 | .firmware = "sd8385.bin", | ||
68 | }, | ||
69 | { | ||
70 | /* 8686 */ | ||
71 | .model = 0x0B, | ||
72 | .helper = "sd8686_helper.bin", | ||
73 | .firmware = "sd8686.bin", | ||
74 | }, | ||
75 | }; | ||
76 | |||
77 | struct if_sdio_packet { | ||
78 | struct if_sdio_packet *next; | ||
79 | u16 nb; | ||
80 | u8 buffer[0] __attribute__((aligned(4))); | ||
81 | }; | ||
82 | |||
83 | struct if_sdio_card { | ||
84 | struct sdio_func *func; | ||
85 | wlan_private *priv; | ||
86 | |||
87 | int model; | ||
88 | unsigned long ioport; | ||
89 | |||
90 | const char *helper; | ||
91 | const char *firmware; | ||
92 | |||
93 | u8 buffer[65536]; | ||
94 | u8 int_cause; | ||
95 | u32 event; | ||
96 | |||
97 | spinlock_t lock; | ||
98 | struct if_sdio_packet *packets; | ||
99 | struct work_struct packet_worker; | ||
100 | }; | ||
101 | |||
102 | /********************************************************************/ | ||
103 | /* I/O */ | ||
104 | /********************************************************************/ | ||
105 | |||
106 | static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err) | ||
107 | { | ||
108 | int ret, reg; | ||
109 | u16 scratch; | ||
110 | |||
111 | if (card->model == 0x04) | ||
112 | reg = IF_SDIO_SCRATCH_OLD; | ||
113 | else | ||
114 | reg = IF_SDIO_SCRATCH; | ||
115 | |||
116 | scratch = sdio_readb(card->func, reg, &ret); | ||
117 | if (!ret) | ||
118 | scratch |= sdio_readb(card->func, reg + 1, &ret) << 8; | ||
119 | |||
120 | if (err) | ||
121 | *err = ret; | ||
122 | |||
123 | if (ret) | ||
124 | return 0xffff; | ||
125 | |||
126 | return scratch; | ||
127 | } | ||
128 | |||
129 | static int if_sdio_handle_cmd(struct if_sdio_card *card, | ||
130 | u8 *buffer, unsigned size) | ||
131 | { | ||
132 | int ret; | ||
133 | unsigned long flags; | ||
134 | |||
135 | lbs_deb_enter(LBS_DEB_SDIO); | ||
136 | |||
137 | spin_lock_irqsave(&card->priv->adapter->driver_lock, flags); | ||
138 | |||
139 | if (!card->priv->adapter->cur_cmd) { | ||
140 | lbs_deb_sdio("discarding spurious response\n"); | ||
141 | ret = 0; | ||
142 | goto out; | ||
143 | } | ||
144 | |||
145 | if (size > MRVDRV_SIZE_OF_CMD_BUFFER) { | ||
146 | lbs_deb_sdio("response packet too large (%d bytes)\n", | ||
147 | (int)size); | ||
148 | ret = -E2BIG; | ||
149 | goto out; | ||
150 | } | ||
151 | |||
152 | memcpy(card->priv->adapter->cur_cmd->bufvirtualaddr, buffer, size); | ||
153 | card->priv->upld_len = size; | ||
154 | |||
155 | card->int_cause |= MRVDRV_CMD_UPLD_RDY; | ||
156 | |||
157 | libertas_interrupt(card->priv->dev); | ||
158 | |||
159 | ret = 0; | ||
160 | |||
161 | out: | ||
162 | spin_unlock_irqrestore(&card->priv->adapter->driver_lock, flags); | ||
163 | |||
164 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
165 | |||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | static int if_sdio_handle_data(struct if_sdio_card *card, | ||
170 | u8 *buffer, unsigned size) | ||
171 | { | ||
172 | int ret; | ||
173 | struct sk_buff *skb; | ||
174 | char *data; | ||
175 | |||
176 | lbs_deb_enter(LBS_DEB_SDIO); | ||
177 | |||
178 | if (size > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { | ||
179 | lbs_deb_sdio("response packet too large (%d bytes)\n", | ||
180 | (int)size); | ||
181 | ret = -E2BIG; | ||
182 | goto out; | ||
183 | } | ||
184 | |||
185 | skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); | ||
186 | if (!skb) { | ||
187 | ret = -ENOMEM; | ||
188 | goto out; | ||
189 | } | ||
190 | |||
191 | data = skb_put(skb, size); | ||
192 | |||
193 | memcpy(data, buffer, size); | ||
194 | |||
195 | libertas_process_rxed_packet(card->priv, skb); | ||
196 | |||
197 | ret = 0; | ||
198 | |||
199 | out: | ||
200 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
201 | |||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | static int if_sdio_handle_event(struct if_sdio_card *card, | ||
206 | u8 *buffer, unsigned size) | ||
207 | { | ||
208 | int ret; | ||
209 | unsigned long flags; | ||
210 | u32 event; | ||
211 | |||
212 | lbs_deb_enter(LBS_DEB_SDIO); | ||
213 | |||
214 | if (card->model == 0x04) { | ||
215 | event = sdio_readb(card->func, IF_SDIO_EVENT, &ret); | ||
216 | if (ret) | ||
217 | goto out; | ||
218 | } else { | ||
219 | if (size < 4) { | ||
220 | lbs_deb_sdio("event packet too small (%d bytes)\n", | ||
221 | (int)size); | ||
222 | ret = -EINVAL; | ||
223 | goto out; | ||
224 | } | ||
225 | event = buffer[3] << 24; | ||
226 | event |= buffer[2] << 16; | ||
227 | event |= buffer[1] << 8; | ||
228 | event |= buffer[0] << 0; | ||
229 | event <<= SBI_EVENT_CAUSE_SHIFT; | ||
230 | } | ||
231 | |||
232 | spin_lock_irqsave(&card->priv->adapter->driver_lock, flags); | ||
233 | |||
234 | card->event = event; | ||
235 | card->int_cause |= MRVDRV_CARDEVENT; | ||
236 | |||
237 | libertas_interrupt(card->priv->dev); | ||
238 | |||
239 | spin_unlock_irqrestore(&card->priv->adapter->driver_lock, flags); | ||
240 | |||
241 | ret = 0; | ||
242 | |||
243 | out: | ||
244 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
245 | |||
246 | return ret; | ||
247 | } | ||
248 | |||
249 | static int if_sdio_card_to_host(struct if_sdio_card *card) | ||
250 | { | ||
251 | int ret; | ||
252 | u8 status; | ||
253 | u16 size, type, chunk; | ||
254 | unsigned long timeout; | ||
255 | |||
256 | lbs_deb_enter(LBS_DEB_SDIO); | ||
257 | |||
258 | size = if_sdio_read_scratch(card, &ret); | ||
259 | if (ret) | ||
260 | goto out; | ||
261 | |||
262 | if (size < 4) { | ||
263 | lbs_deb_sdio("invalid packet size (%d bytes) from firmware\n", | ||
264 | (int)size); | ||
265 | ret = -EINVAL; | ||
266 | goto out; | ||
267 | } | ||
268 | |||
269 | timeout = jiffies + HZ; | ||
270 | while (1) { | ||
271 | status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); | ||
272 | if (ret) | ||
273 | goto out; | ||
274 | if (status & IF_SDIO_IO_RDY) | ||
275 | break; | ||
276 | if (time_after(jiffies, timeout)) { | ||
277 | ret = -ETIMEDOUT; | ||
278 | goto out; | ||
279 | } | ||
280 | mdelay(1); | ||
281 | } | ||
282 | |||
283 | /* | ||
284 | * The transfer must be in one transaction or the firmware | ||
285 | * goes suicidal. | ||
286 | */ | ||
287 | chunk = size; | ||
288 | if ((chunk > card->func->cur_blksize) || (chunk > 512)) { | ||
289 | chunk = (chunk + card->func->cur_blksize - 1) / | ||
290 | card->func->cur_blksize * card->func->cur_blksize; | ||
291 | } | ||
292 | |||
293 | ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk); | ||
294 | if (ret) | ||
295 | goto out; | ||
296 | |||
297 | chunk = card->buffer[0] | (card->buffer[1] << 8); | ||
298 | type = card->buffer[2] | (card->buffer[3] << 8); | ||
299 | |||
300 | lbs_deb_sdio("packet of type %d and size %d bytes\n", | ||
301 | (int)type, (int)chunk); | ||
302 | |||
303 | if (chunk > size) { | ||
304 | lbs_deb_sdio("packet fragment (%d > %d)\n", | ||
305 | (int)chunk, (int)size); | ||
306 | ret = -EINVAL; | ||
307 | goto out; | ||
308 | } | ||
309 | |||
310 | if (chunk < size) { | ||
311 | lbs_deb_sdio("packet fragment (%d < %d)\n", | ||
312 | (int)chunk, (int)size); | ||
313 | } | ||
314 | |||
315 | switch (type) { | ||
316 | case MVMS_CMD: | ||
317 | ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4); | ||
318 | if (ret) | ||
319 | goto out; | ||
320 | break; | ||
321 | case MVMS_DAT: | ||
322 | ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4); | ||
323 | if (ret) | ||
324 | goto out; | ||
325 | break; | ||
326 | case MVMS_EVENT: | ||
327 | ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4); | ||
328 | if (ret) | ||
329 | goto out; | ||
330 | break; | ||
331 | default: | ||
332 | lbs_deb_sdio("invalid type (%d) from firmware\n", | ||
333 | (int)type); | ||
334 | ret = -EINVAL; | ||
335 | goto out; | ||
336 | } | ||
337 | |||
338 | out: | ||
339 | if (ret) | ||
340 | lbs_pr_err("problem fetching packet from firmware\n"); | ||
341 | |||
342 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
343 | |||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | static void if_sdio_host_to_card_worker(struct work_struct *work) | ||
348 | { | ||
349 | struct if_sdio_card *card; | ||
350 | struct if_sdio_packet *packet; | ||
351 | unsigned long timeout; | ||
352 | u8 status; | ||
353 | int ret; | ||
354 | unsigned long flags; | ||
355 | |||
356 | lbs_deb_enter(LBS_DEB_SDIO); | ||
357 | |||
358 | card = container_of(work, struct if_sdio_card, packet_worker); | ||
359 | |||
360 | while (1) { | ||
361 | spin_lock_irqsave(&card->lock, flags); | ||
362 | packet = card->packets; | ||
363 | if (packet) | ||
364 | card->packets = packet->next; | ||
365 | spin_unlock_irqrestore(&card->lock, flags); | ||
366 | |||
367 | if (!packet) | ||
368 | break; | ||
369 | |||
370 | sdio_claim_host(card->func); | ||
371 | |||
372 | timeout = jiffies + HZ; | ||
373 | while (1) { | ||
374 | status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); | ||
375 | if (ret) | ||
376 | goto release; | ||
377 | if (status & IF_SDIO_IO_RDY) | ||
378 | break; | ||
379 | if (time_after(jiffies, timeout)) { | ||
380 | ret = -ETIMEDOUT; | ||
381 | goto release; | ||
382 | } | ||
383 | mdelay(1); | ||
384 | } | ||
385 | |||
386 | ret = sdio_writesb(card->func, card->ioport, | ||
387 | packet->buffer, packet->nb); | ||
388 | if (ret) | ||
389 | goto release; | ||
390 | release: | ||
391 | sdio_release_host(card->func); | ||
392 | |||
393 | kfree(packet); | ||
394 | } | ||
395 | |||
396 | lbs_deb_leave(LBS_DEB_SDIO); | ||
397 | } | ||
398 | |||
399 | /********************************************************************/ | ||
400 | /* Firmware */ | ||
401 | /********************************************************************/ | ||
402 | |||
403 | static int if_sdio_prog_helper(struct if_sdio_card *card) | ||
404 | { | ||
405 | int ret; | ||
406 | u8 status; | ||
407 | const struct firmware *fw; | ||
408 | unsigned long timeout; | ||
409 | u8 *chunk_buffer; | ||
410 | u32 chunk_size; | ||
411 | u8 *firmware; | ||
412 | size_t size; | ||
413 | |||
414 | lbs_deb_enter(LBS_DEB_SDIO); | ||
415 | |||
416 | ret = request_firmware(&fw, card->helper, &card->func->dev); | ||
417 | if (ret) { | ||
418 | lbs_pr_err("can't load helper firmware\n"); | ||
419 | goto out; | ||
420 | } | ||
421 | |||
422 | chunk_buffer = kzalloc(64, GFP_KERNEL); | ||
423 | if (!chunk_buffer) { | ||
424 | ret = -ENOMEM; | ||
425 | goto release_fw; | ||
426 | } | ||
427 | |||
428 | sdio_claim_host(card->func); | ||
429 | |||
430 | ret = sdio_set_block_size(card->func, 32); | ||
431 | if (ret) | ||
432 | goto release; | ||
433 | |||
434 | firmware = fw->data; | ||
435 | size = fw->size; | ||
436 | |||
437 | while (size) { | ||
438 | timeout = jiffies + HZ; | ||
439 | while (1) { | ||
440 | status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); | ||
441 | if (ret) | ||
442 | goto release; | ||
443 | if ((status & IF_SDIO_IO_RDY) && | ||
444 | (status & IF_SDIO_DL_RDY)) | ||
445 | break; | ||
446 | if (time_after(jiffies, timeout)) { | ||
447 | ret = -ETIMEDOUT; | ||
448 | goto release; | ||
449 | } | ||
450 | mdelay(1); | ||
451 | } | ||
452 | |||
453 | chunk_size = min(size, (size_t)60); | ||
454 | |||
455 | *((u32*)chunk_buffer) = cpu_to_le32(chunk_size); | ||
456 | memcpy(chunk_buffer + 4, firmware, chunk_size); | ||
457 | /* | ||
458 | lbs_deb_sdio("sending %d bytes chunk\n", chunk_size); | ||
459 | */ | ||
460 | ret = sdio_writesb(card->func, card->ioport, | ||
461 | chunk_buffer, 64); | ||
462 | if (ret) | ||
463 | goto release; | ||
464 | |||
465 | firmware += chunk_size; | ||
466 | size -= chunk_size; | ||
467 | } | ||
468 | |||
469 | /* an empty block marks the end of the transfer */ | ||
470 | memset(chunk_buffer, 0, 4); | ||
471 | ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64); | ||
472 | if (ret) | ||
473 | goto release; | ||
474 | |||
475 | lbs_deb_sdio("waiting for helper to boot...\n"); | ||
476 | |||
477 | /* wait for the helper to boot by looking at the size register */ | ||
478 | timeout = jiffies + HZ; | ||
479 | while (1) { | ||
480 | u16 req_size; | ||
481 | |||
482 | req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); | ||
483 | if (ret) | ||
484 | goto release; | ||
485 | |||
486 | req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; | ||
487 | if (ret) | ||
488 | goto release; | ||
489 | |||
490 | if (req_size != 0) | ||
491 | break; | ||
492 | |||
493 | if (time_after(jiffies, timeout)) { | ||
494 | ret = -ETIMEDOUT; | ||
495 | goto release; | ||
496 | } | ||
497 | |||
498 | msleep(10); | ||
499 | } | ||
500 | |||
501 | ret = 0; | ||
502 | |||
503 | release: | ||
504 | sdio_set_block_size(card->func, 0); | ||
505 | sdio_release_host(card->func); | ||
506 | kfree(chunk_buffer); | ||
507 | release_fw: | ||
508 | release_firmware(fw); | ||
509 | |||
510 | out: | ||
511 | if (ret) | ||
512 | lbs_pr_err("failed to load helper firmware\n"); | ||
513 | |||
514 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
515 | |||
516 | return ret; | ||
517 | } | ||
518 | |||
519 | static int if_sdio_prog_real(struct if_sdio_card *card) | ||
520 | { | ||
521 | int ret; | ||
522 | u8 status; | ||
523 | const struct firmware *fw; | ||
524 | unsigned long timeout; | ||
525 | u8 *chunk_buffer; | ||
526 | u32 chunk_size; | ||
527 | u8 *firmware; | ||
528 | size_t size, req_size; | ||
529 | |||
530 | lbs_deb_enter(LBS_DEB_SDIO); | ||
531 | |||
532 | ret = request_firmware(&fw, card->firmware, &card->func->dev); | ||
533 | if (ret) { | ||
534 | lbs_pr_err("can't load firmware\n"); | ||
535 | goto out; | ||
536 | } | ||
537 | |||
538 | chunk_buffer = kzalloc(512, GFP_KERNEL); | ||
539 | if (!chunk_buffer) { | ||
540 | ret = -ENOMEM; | ||
541 | goto release_fw; | ||
542 | } | ||
543 | |||
544 | sdio_claim_host(card->func); | ||
545 | |||
546 | ret = sdio_set_block_size(card->func, 32); | ||
547 | if (ret) | ||
548 | goto release; | ||
549 | |||
550 | firmware = fw->data; | ||
551 | size = fw->size; | ||
552 | |||
553 | while (size) { | ||
554 | timeout = jiffies + HZ; | ||
555 | while (1) { | ||
556 | status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); | ||
557 | if (ret) | ||
558 | goto release; | ||
559 | if ((status & IF_SDIO_IO_RDY) && | ||
560 | (status & IF_SDIO_DL_RDY)) | ||
561 | break; | ||
562 | if (time_after(jiffies, timeout)) { | ||
563 | ret = -ETIMEDOUT; | ||
564 | goto release; | ||
565 | } | ||
566 | mdelay(1); | ||
567 | } | ||
568 | |||
569 | req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); | ||
570 | if (ret) | ||
571 | goto release; | ||
572 | |||
573 | req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; | ||
574 | if (ret) | ||
575 | goto release; | ||
576 | /* | ||
577 | lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size); | ||
578 | */ | ||
579 | if (req_size == 0) { | ||
580 | lbs_deb_sdio("firmware helper gave up early\n"); | ||
581 | ret = -EIO; | ||
582 | goto release; | ||
583 | } | ||
584 | |||
585 | if (req_size & 0x01) { | ||
586 | lbs_deb_sdio("firmware helper signalled error\n"); | ||
587 | ret = -EIO; | ||
588 | goto release; | ||
589 | } | ||
590 | |||
591 | if (req_size > size) | ||
592 | req_size = size; | ||
593 | |||
594 | while (req_size) { | ||
595 | chunk_size = min(req_size, (size_t)512); | ||
596 | |||
597 | memcpy(chunk_buffer, firmware, chunk_size); | ||
598 | /* | ||
599 | lbs_deb_sdio("sending %d bytes (%d bytes) chunk\n", | ||
600 | chunk_size, (chunk_size + 31) / 32 * 32); | ||
601 | */ | ||
602 | ret = sdio_writesb(card->func, card->ioport, | ||
603 | chunk_buffer, (chunk_size + 31) / 32 * 32); | ||
604 | if (ret) | ||
605 | goto release; | ||
606 | |||
607 | firmware += chunk_size; | ||
608 | size -= chunk_size; | ||
609 | req_size -= chunk_size; | ||
610 | } | ||
611 | } | ||
612 | |||
613 | ret = 0; | ||
614 | |||
615 | lbs_deb_sdio("waiting for firmware to boot...\n"); | ||
616 | |||
617 | /* wait for the firmware to boot */ | ||
618 | timeout = jiffies + HZ; | ||
619 | while (1) { | ||
620 | u16 scratch; | ||
621 | |||
622 | scratch = if_sdio_read_scratch(card, &ret); | ||
623 | if (ret) | ||
624 | goto release; | ||
625 | |||
626 | if (scratch == IF_SDIO_FIRMWARE_OK) | ||
627 | break; | ||
628 | |||
629 | if (time_after(jiffies, timeout)) { | ||
630 | ret = -ETIMEDOUT; | ||
631 | goto release; | ||
632 | } | ||
633 | |||
634 | msleep(10); | ||
635 | } | ||
636 | |||
637 | ret = 0; | ||
638 | |||
639 | release: | ||
640 | sdio_set_block_size(card->func, 0); | ||
641 | sdio_release_host(card->func); | ||
642 | kfree(chunk_buffer); | ||
643 | release_fw: | ||
644 | release_firmware(fw); | ||
645 | |||
646 | out: | ||
647 | if (ret) | ||
648 | lbs_pr_err("failed to load firmware\n"); | ||
649 | |||
650 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
651 | |||
652 | return ret; | ||
653 | } | ||
654 | |||
655 | static int if_sdio_prog_firmware(struct if_sdio_card *card) | ||
656 | { | ||
657 | int ret; | ||
658 | u16 scratch; | ||
659 | |||
660 | lbs_deb_enter(LBS_DEB_SDIO); | ||
661 | |||
662 | sdio_claim_host(card->func); | ||
663 | scratch = if_sdio_read_scratch(card, &ret); | ||
664 | sdio_release_host(card->func); | ||
665 | |||
666 | if (ret) | ||
667 | goto out; | ||
668 | |||
669 | if (scratch == IF_SDIO_FIRMWARE_OK) { | ||
670 | lbs_deb_sdio("firmware already loaded\n"); | ||
671 | goto success; | ||
672 | } | ||
673 | |||
674 | ret = if_sdio_prog_helper(card); | ||
675 | if (ret) | ||
676 | goto out; | ||
677 | |||
678 | ret = if_sdio_prog_real(card); | ||
679 | if (ret) | ||
680 | goto out; | ||
681 | |||
682 | success: | ||
683 | ret = 0; | ||
684 | |||
685 | out: | ||
686 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
687 | |||
688 | return ret; | ||
689 | } | ||
690 | |||
691 | /*******************************************************************/ | ||
692 | /* Libertas callbacks */ | ||
693 | /*******************************************************************/ | ||
694 | |||
695 | static int if_sdio_host_to_card(wlan_private *priv, u8 type, u8 *buf, u16 nb) | ||
696 | { | ||
697 | int ret; | ||
698 | struct if_sdio_card *card; | ||
699 | struct if_sdio_packet *packet, *cur; | ||
700 | u16 size; | ||
701 | unsigned long flags; | ||
702 | |||
703 | lbs_deb_enter_args(LBS_DEB_SDIO, "type %d, bytes %d", type, nb); | ||
704 | |||
705 | card = priv->card; | ||
706 | |||
707 | if (nb > (65536 - sizeof(struct if_sdio_packet) - 4)) { | ||
708 | ret = -EINVAL; | ||
709 | goto out; | ||
710 | } | ||
711 | |||
712 | /* | ||
713 | * The transfer must be in one transaction or the firmware | ||
714 | * goes suicidal. | ||
715 | */ | ||
716 | size = nb + 4; | ||
717 | if ((size > card->func->cur_blksize) || (size > 512)) { | ||
718 | size = (size + card->func->cur_blksize - 1) / | ||
719 | card->func->cur_blksize * card->func->cur_blksize; | ||
720 | } | ||
721 | |||
722 | packet = kzalloc(sizeof(struct if_sdio_packet) + size, | ||
723 | GFP_ATOMIC); | ||
724 | if (!packet) { | ||
725 | ret = -ENOMEM; | ||
726 | goto out; | ||
727 | } | ||
728 | |||
729 | packet->next = NULL; | ||
730 | packet->nb = size; | ||
731 | |||
732 | /* | ||
733 | * SDIO specific header. | ||
734 | */ | ||
735 | packet->buffer[0] = (nb + 4) & 0xff; | ||
736 | packet->buffer[1] = ((nb + 4) >> 8) & 0xff; | ||
737 | packet->buffer[2] = type; | ||
738 | packet->buffer[3] = 0; | ||
739 | |||
740 | memcpy(packet->buffer + 4, buf, nb); | ||
741 | |||
742 | spin_lock_irqsave(&card->lock, flags); | ||
743 | |||
744 | if (!card->packets) | ||
745 | card->packets = packet; | ||
746 | else { | ||
747 | cur = card->packets; | ||
748 | while (cur->next) | ||
749 | cur = cur->next; | ||
750 | cur->next = packet; | ||
751 | } | ||
752 | |||
753 | switch (type) { | ||
754 | case MVMS_CMD: | ||
755 | priv->dnld_sent = DNLD_CMD_SENT; | ||
756 | break; | ||
757 | case MVMS_DAT: | ||
758 | priv->dnld_sent = DNLD_DATA_SENT; | ||
759 | break; | ||
760 | default: | ||
761 | lbs_deb_sdio("unknown packet type %d\n", (int)type); | ||
762 | } | ||
763 | |||
764 | spin_unlock_irqrestore(&card->lock, flags); | ||
765 | |||
766 | schedule_work(&card->packet_worker); | ||
767 | |||
768 | ret = 0; | ||
769 | |||
770 | out: | ||
771 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
772 | |||
773 | return ret; | ||
774 | } | ||
775 | |||
776 | static int if_sdio_get_int_status(wlan_private *priv, u8 *ireg) | ||
777 | { | ||
778 | struct if_sdio_card *card; | ||
779 | |||
780 | lbs_deb_enter(LBS_DEB_SDIO); | ||
781 | |||
782 | card = priv->card; | ||
783 | |||
784 | *ireg = card->int_cause; | ||
785 | card->int_cause = 0; | ||
786 | |||
787 | lbs_deb_leave(LBS_DEB_SDIO); | ||
788 | |||
789 | return 0; | ||
790 | } | ||
791 | |||
792 | static int if_sdio_read_event_cause(wlan_private *priv) | ||
793 | { | ||
794 | struct if_sdio_card *card; | ||
795 | |||
796 | lbs_deb_enter(LBS_DEB_SDIO); | ||
797 | |||
798 | card = priv->card; | ||
799 | |||
800 | priv->adapter->eventcause = card->event; | ||
801 | |||
802 | lbs_deb_leave(LBS_DEB_SDIO); | ||
803 | |||
804 | return 0; | ||
805 | } | ||
806 | |||
807 | /*******************************************************************/ | ||
808 | /* SDIO callbacks */ | ||
809 | /*******************************************************************/ | ||
810 | |||
811 | static void if_sdio_interrupt(struct sdio_func *func) | ||
812 | { | ||
813 | int ret; | ||
814 | struct if_sdio_card *card; | ||
815 | u8 cause; | ||
816 | |||
817 | lbs_deb_enter(LBS_DEB_SDIO); | ||
818 | |||
819 | card = sdio_get_drvdata(func); | ||
820 | |||
821 | cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret); | ||
822 | if (ret) | ||
823 | goto out; | ||
824 | |||
825 | lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause); | ||
826 | |||
827 | sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret); | ||
828 | if (ret) | ||
829 | goto out; | ||
830 | |||
831 | /* | ||
832 | * Ignore the define name, this really means the card has | ||
833 | * successfully received the command. | ||
834 | */ | ||
835 | if (cause & IF_SDIO_H_INT_DNLD) { | ||
836 | if ((card->priv->dnld_sent == DNLD_DATA_SENT) && | ||
837 | (card->priv->adapter->connect_status == LIBERTAS_CONNECTED)) | ||
838 | netif_wake_queue(card->priv->dev); | ||
839 | card->priv->dnld_sent = DNLD_RES_RECEIVED; | ||
840 | } | ||
841 | |||
842 | if (cause & IF_SDIO_H_INT_UPLD) { | ||
843 | ret = if_sdio_card_to_host(card); | ||
844 | if (ret) | ||
845 | goto out; | ||
846 | } | ||
847 | |||
848 | ret = 0; | ||
849 | |||
850 | out: | ||
851 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
852 | } | ||
853 | |||
854 | static int if_sdio_probe(struct sdio_func *func, | ||
855 | const struct sdio_device_id *id) | ||
856 | { | ||
857 | struct if_sdio_card *card; | ||
858 | wlan_private *priv; | ||
859 | int ret, i; | ||
860 | unsigned int model; | ||
861 | struct if_sdio_packet *packet; | ||
862 | |||
863 | lbs_deb_enter(LBS_DEB_SDIO); | ||
864 | |||
865 | for (i = 0;i < func->card->num_info;i++) { | ||
866 | if (sscanf(func->card->info[i], | ||
867 | "802.11 SDIO ID: %x", &model) == 1) | ||
868 | break; | ||
869 | if (sscanf(func->card->info[i], | ||
870 | "ID: %x", &model) == 1) | ||
871 | break; | ||
872 | } | ||
873 | |||
874 | if (i == func->card->num_info) { | ||
875 | lbs_pr_err("unable to identify card model\n"); | ||
876 | return -ENODEV; | ||
877 | } | ||
878 | |||
879 | card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL); | ||
880 | if (!card) | ||
881 | return -ENOMEM; | ||
882 | |||
883 | card->func = func; | ||
884 | card->model = model; | ||
885 | spin_lock_init(&card->lock); | ||
886 | INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); | ||
887 | |||
888 | for (i = 0;i < ARRAY_SIZE(if_sdio_models);i++) { | ||
889 | if (card->model == if_sdio_models[i].model) | ||
890 | break; | ||
891 | } | ||
892 | |||
893 | if (i == ARRAY_SIZE(if_sdio_models)) { | ||
894 | lbs_pr_err("unkown card model 0x%x\n", card->model); | ||
895 | ret = -ENODEV; | ||
896 | goto free; | ||
897 | } | ||
898 | |||
899 | card->helper = if_sdio_models[i].helper; | ||
900 | card->firmware = if_sdio_models[i].firmware; | ||
901 | |||
902 | if (libertas_helper_name) { | ||
903 | lbs_deb_sdio("overriding helper firmware: %s\n", | ||
904 | libertas_helper_name); | ||
905 | card->helper = libertas_helper_name; | ||
906 | } | ||
907 | |||
908 | if (libertas_fw_name) { | ||
909 | lbs_deb_sdio("overriding firmware: %s\n", libertas_fw_name); | ||
910 | card->firmware = libertas_fw_name; | ||
911 | } | ||
912 | |||
913 | sdio_claim_host(func); | ||
914 | |||
915 | ret = sdio_enable_func(func); | ||
916 | if (ret) | ||
917 | goto release; | ||
918 | |||
919 | ret = sdio_claim_irq(func, if_sdio_interrupt); | ||
920 | if (ret) | ||
921 | goto disable; | ||
922 | |||
923 | card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); | ||
924 | if (ret) | ||
925 | goto release_int; | ||
926 | |||
927 | card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; | ||
928 | if (ret) | ||
929 | goto release_int; | ||
930 | |||
931 | card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; | ||
932 | if (ret) | ||
933 | goto release_int; | ||
934 | |||
935 | sdio_release_host(func); | ||
936 | |||
937 | sdio_set_drvdata(func, card); | ||
938 | |||
939 | lbs_deb_sdio("class = 0x%X, vendor = 0x%X, " | ||
940 | "device = 0x%X, model = 0x%X, ioport = 0x%X\n", | ||
941 | func->class, func->vendor, func->device, | ||
942 | model, (unsigned)card->ioport); | ||
943 | |||
944 | ret = if_sdio_prog_firmware(card); | ||
945 | if (ret) | ||
946 | goto reclaim; | ||
947 | |||
948 | priv = libertas_add_card(card, &func->dev); | ||
949 | if (!priv) { | ||
950 | ret = -ENOMEM; | ||
951 | goto reclaim; | ||
952 | } | ||
953 | |||
954 | card->priv = priv; | ||
955 | |||
956 | priv->card = card; | ||
957 | priv->hw_host_to_card = if_sdio_host_to_card; | ||
958 | priv->hw_get_int_status = if_sdio_get_int_status; | ||
959 | priv->hw_read_event_cause = if_sdio_read_event_cause; | ||
960 | |||
961 | priv->adapter->fw_ready = 1; | ||
962 | |||
963 | /* | ||
964 | * Enable interrupts now that everything is set up | ||
965 | */ | ||
966 | sdio_claim_host(func); | ||
967 | sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); | ||
968 | sdio_release_host(func); | ||
969 | if (ret) | ||
970 | goto reclaim; | ||
971 | |||
972 | ret = libertas_start_card(priv); | ||
973 | if (ret) | ||
974 | goto err_activate_card; | ||
975 | |||
976 | out: | ||
977 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
978 | |||
979 | return ret; | ||
980 | |||
981 | err_activate_card: | ||
982 | flush_scheduled_work(); | ||
983 | free_netdev(priv->dev); | ||
984 | kfree(priv->adapter); | ||
985 | reclaim: | ||
986 | sdio_claim_host(func); | ||
987 | release_int: | ||
988 | sdio_release_irq(func); | ||
989 | disable: | ||
990 | sdio_disable_func(func); | ||
991 | release: | ||
992 | sdio_release_host(func); | ||
993 | free: | ||
994 | while (card->packets) { | ||
995 | packet = card->packets; | ||
996 | card->packets = card->packets->next; | ||
997 | kfree(packet); | ||
998 | } | ||
999 | |||
1000 | kfree(card); | ||
1001 | |||
1002 | goto out; | ||
1003 | } | ||
1004 | |||
1005 | static void if_sdio_remove(struct sdio_func *func) | ||
1006 | { | ||
1007 | struct if_sdio_card *card; | ||
1008 | struct if_sdio_packet *packet; | ||
1009 | |||
1010 | lbs_deb_enter(LBS_DEB_SDIO); | ||
1011 | |||
1012 | card = sdio_get_drvdata(func); | ||
1013 | |||
1014 | card->priv->adapter->surpriseremoved = 1; | ||
1015 | |||
1016 | lbs_deb_sdio("call remove card\n"); | ||
1017 | libertas_stop_card(card->priv); | ||
1018 | libertas_remove_card(card->priv); | ||
1019 | |||
1020 | flush_scheduled_work(); | ||
1021 | |||
1022 | sdio_claim_host(func); | ||
1023 | sdio_release_irq(func); | ||
1024 | sdio_disable_func(func); | ||
1025 | sdio_release_host(func); | ||
1026 | |||
1027 | while (card->packets) { | ||
1028 | packet = card->packets; | ||
1029 | card->packets = card->packets->next; | ||
1030 | kfree(packet); | ||
1031 | } | ||
1032 | |||
1033 | kfree(card); | ||
1034 | |||
1035 | lbs_deb_leave(LBS_DEB_SDIO); | ||
1036 | } | ||
1037 | |||
1038 | static struct sdio_driver if_sdio_driver = { | ||
1039 | .name = "libertas_sdio", | ||
1040 | .id_table = if_sdio_ids, | ||
1041 | .probe = if_sdio_probe, | ||
1042 | .remove = if_sdio_remove, | ||
1043 | }; | ||
1044 | |||
1045 | /*******************************************************************/ | ||
1046 | /* Module functions */ | ||
1047 | /*******************************************************************/ | ||
1048 | |||
1049 | static int if_sdio_init_module(void) | ||
1050 | { | ||
1051 | int ret = 0; | ||
1052 | |||
1053 | lbs_deb_enter(LBS_DEB_SDIO); | ||
1054 | |||
1055 | printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n"); | ||
1056 | printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n"); | ||
1057 | |||
1058 | ret = sdio_register_driver(&if_sdio_driver); | ||
1059 | |||
1060 | lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); | ||
1061 | |||
1062 | return ret; | ||
1063 | } | ||
1064 | |||
1065 | static void if_sdio_exit_module(void) | ||
1066 | { | ||
1067 | lbs_deb_enter(LBS_DEB_SDIO); | ||
1068 | |||
1069 | sdio_unregister_driver(&if_sdio_driver); | ||
1070 | |||
1071 | lbs_deb_leave(LBS_DEB_SDIO); | ||
1072 | } | ||
1073 | |||
1074 | module_init(if_sdio_init_module); | ||
1075 | module_exit(if_sdio_exit_module); | ||
1076 | |||
1077 | MODULE_DESCRIPTION("Libertas SDIO WLAN Driver"); | ||
1078 | MODULE_AUTHOR("Pierre Ossman"); | ||
1079 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h new file mode 100644 index 000000000000..dfcaea7b168f --- /dev/null +++ b/drivers/net/wireless/libertas/if_sdio.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* | ||
2 | * linux/drivers/net/wireless/libertas/if_sdio.h | ||
3 | * | ||
4 | * Copyright 2007 Pierre Ossman | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or (at | ||
9 | * your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #ifndef LIBERTAS_IF_SDIO_H | ||
13 | #define LIBERTAS_IF_SDIO_H | ||
14 | |||
15 | #define IF_SDIO_IOPORT 0x00 | ||
16 | |||
17 | #define IF_SDIO_H_INT_MASK 0x04 | ||
18 | #define IF_SDIO_H_INT_OFLOW 0x08 | ||
19 | #define IF_SDIO_H_INT_UFLOW 0x04 | ||
20 | #define IF_SDIO_H_INT_DNLD 0x02 | ||
21 | #define IF_SDIO_H_INT_UPLD 0x01 | ||
22 | |||
23 | #define IF_SDIO_H_INT_STATUS 0x05 | ||
24 | #define IF_SDIO_H_INT_RSR 0x06 | ||
25 | #define IF_SDIO_H_INT_STATUS2 0x07 | ||
26 | |||
27 | #define IF_SDIO_RD_BASE 0x10 | ||
28 | |||
29 | #define IF_SDIO_STATUS 0x20 | ||
30 | #define IF_SDIO_IO_RDY 0x08 | ||
31 | #define IF_SDIO_CIS_RDY 0x04 | ||
32 | #define IF_SDIO_UL_RDY 0x02 | ||
33 | #define IF_SDIO_DL_RDY 0x01 | ||
34 | |||
35 | #define IF_SDIO_C_INT_MASK 0x24 | ||
36 | #define IF_SDIO_C_INT_STATUS 0x28 | ||
37 | #define IF_SDIO_C_INT_RSR 0x2C | ||
38 | |||
39 | #define IF_SDIO_SCRATCH 0x34 | ||
40 | #define IF_SDIO_SCRATCH_OLD 0x80fe | ||
41 | #define IF_SDIO_FIRMWARE_OK 0xfedc | ||
42 | |||
43 | #define IF_SDIO_EVENT 0x80fc | ||
44 | |||
45 | #endif | ||