diff options
author | Holger Schurig <hs4233@mail.mn-solutions.de> | 2007-10-03 20:25:41 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-10 19:49:56 -0400 |
commit | 27590d06e136167101c8c6347e7c2885c7f014b9 (patch) | |
tree | 93c2072779619d4559db24f95a69ecca1663eb0a /drivers/net/wireless | |
parent | 9cdc6d295b97dc4a36569ddd0b0540aeba351149 (diff) |
[PATCH] add support for Marvell 8385 CF cards
This patch adds support for Marvell based 8385 compact flash cards.
Signed-off-by: Holger Schurig <hs4233@mail.mn-solutions.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/if_cs.c | 1005 |
3 files changed, 1014 insertions, 0 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index adf0d207da88..8d1541bbe257 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig | |||
@@ -275,6 +275,13 @@ config LIBERTAS_USB | |||
275 | ---help--- | 275 | ---help--- |
276 | A driver for Marvell Libertas 8388 USB devices. | 276 | A driver for Marvell Libertas 8388 USB devices. |
277 | 277 | ||
278 | config LIBERTAS_CS | ||
279 | tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards" | ||
280 | depends on LIBERTAS && PCMCIA && EXPERIMENTAL | ||
281 | select FW_LOADER | ||
282 | ---help--- | ||
283 | A driver for Marvell Libertas 8385 CompactFlash devices. | ||
284 | |||
278 | config LIBERTAS_DEBUG | 285 | config LIBERTAS_DEBUG |
279 | bool "Enable full debugging output in the Libertas module." | 286 | bool "Enable full debugging output in the Libertas module." |
280 | depends on LIBERTAS | 287 | depends on LIBERTAS |
diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile index b56b4c55de31..c469d569f090 100644 --- a/drivers/net/wireless/libertas/Makefile +++ b/drivers/net/wireless/libertas/Makefile | |||
@@ -6,6 +6,8 @@ libertas-objs := main.o wext.o \ | |||
6 | ethtool.o assoc.o | 6 | ethtool.o assoc.o |
7 | 7 | ||
8 | usb8xxx-objs += if_usb.o | 8 | usb8xxx-objs += if_usb.o |
9 | libertas_cs-objs += if_cs.o | ||
9 | 10 | ||
10 | obj-$(CONFIG_LIBERTAS) += libertas.o | 11 | obj-$(CONFIG_LIBERTAS) += libertas.o |
11 | obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o | 12 | obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o |
13 | obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o | ||
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c new file mode 100644 index 000000000000..888f023a74e1 --- /dev/null +++ b/drivers/net/wireless/libertas/if_cs.c | |||
@@ -0,0 +1,1005 @@ | |||
1 | /* | ||
2 | |||
3 | Driver for the Marvell 8385 based compact flash WLAN cards. | ||
4 | |||
5 | (C) 2007 by Holger Schurig <hs4233@mail.mn-solutions.de> | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or | ||
10 | (at your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, | ||
20 | Boston, MA 02110-1301, USA. | ||
21 | |||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/moduleparam.h> | ||
27 | #include <linux/firmware.h> | ||
28 | #include <linux/netdevice.h> | ||
29 | |||
30 | #include <pcmcia/cs_types.h> | ||
31 | #include <pcmcia/cs.h> | ||
32 | #include <pcmcia/cistpl.h> | ||
33 | #include <pcmcia/ds.h> | ||
34 | |||
35 | #include <linux/io.h> | ||
36 | |||
37 | #define DRV_NAME "libertas_cs" | ||
38 | |||
39 | #include "decl.h" | ||
40 | #include "defs.h" | ||
41 | #include "dev.h" | ||
42 | |||
43 | |||
44 | /********************************************************************/ | ||
45 | /* Module stuff */ | ||
46 | /********************************************************************/ | ||
47 | |||
48 | MODULE_AUTHOR("Holger Schurig <hs4233@mail.mn-solutions.de>"); | ||
49 | MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards"); | ||
50 | MODULE_LICENSE("GPL"); | ||
51 | |||
52 | |||
53 | |||
54 | /********************************************************************/ | ||
55 | /* Data structures */ | ||
56 | /********************************************************************/ | ||
57 | |||
58 | struct if_cs_card { | ||
59 | struct pcmcia_device *p_dev; | ||
60 | wlan_private *priv; | ||
61 | void __iomem *iobase; | ||
62 | }; | ||
63 | |||
64 | |||
65 | |||
66 | /********************************************************************/ | ||
67 | /* Hardware access */ | ||
68 | /********************************************************************/ | ||
69 | |||
70 | /* This define enables wrapper functions which allow you | ||
71 | to dump all register accesses. You normally won't this, | ||
72 | except for development */ | ||
73 | /* #define DEBUG_IO */ | ||
74 | |||
75 | #ifdef DEBUG_IO | ||
76 | static int debug_output = 0; | ||
77 | #else | ||
78 | /* This way the compiler optimizes the printk's away */ | ||
79 | #define debug_output 0 | ||
80 | #endif | ||
81 | |||
82 | static inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg) | ||
83 | { | ||
84 | unsigned int val = ioread8(card->iobase + reg); | ||
85 | if (debug_output) | ||
86 | printk(KERN_INFO "##inb %08x<%02x\n", reg, val); | ||
87 | return val; | ||
88 | } | ||
89 | static inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg) | ||
90 | { | ||
91 | unsigned int val = ioread16(card->iobase + reg); | ||
92 | if (debug_output) | ||
93 | printk(KERN_INFO "##inw %08x<%04x\n", reg, val); | ||
94 | return val; | ||
95 | } | ||
96 | static inline void if_cs_read16_rep( | ||
97 | struct if_cs_card *card, | ||
98 | uint reg, | ||
99 | void *buf, | ||
100 | unsigned long count) | ||
101 | { | ||
102 | if (debug_output) | ||
103 | printk(KERN_INFO "##insw %08x<(0x%lx words)\n", | ||
104 | reg, count); | ||
105 | ioread16_rep(card->iobase + reg, buf, count); | ||
106 | } | ||
107 | |||
108 | static inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val) | ||
109 | { | ||
110 | if (debug_output) | ||
111 | printk(KERN_INFO "##outb %08x>%02x\n", reg, val); | ||
112 | iowrite8(val, card->iobase + reg); | ||
113 | } | ||
114 | |||
115 | static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val) | ||
116 | { | ||
117 | if (debug_output) | ||
118 | printk(KERN_INFO "##outw %08x>%04x\n", reg, val); | ||
119 | iowrite16(val, card->iobase + reg); | ||
120 | } | ||
121 | |||
122 | static inline void if_cs_write16_rep( | ||
123 | struct if_cs_card *card, | ||
124 | uint reg, | ||
125 | void *buf, | ||
126 | unsigned long count) | ||
127 | { | ||
128 | if (debug_output) | ||
129 | printk(KERN_INFO "##outsw %08x>(0x%lx words)\n", | ||
130 | reg, count); | ||
131 | iowrite16_rep(card->iobase + reg, buf, count); | ||
132 | } | ||
133 | |||
134 | |||
135 | /* | ||
136 | * I know that polling/delaying is frowned upon. However, this procedure | ||
137 | * with polling is needed while downloading the firmware. At this stage, | ||
138 | * the hardware does unfortunately not create any interrupts. | ||
139 | * | ||
140 | * Fortunately, this function is never used once the firmware is in | ||
141 | * the card. :-) | ||
142 | * | ||
143 | * As a reference, see the "Firmware Specification v5.1", page 18 | ||
144 | * and 19. I did not follow their suggested timing to the word, | ||
145 | * but this works nice & fast anyway. | ||
146 | */ | ||
147 | static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 reg) | ||
148 | { | ||
149 | int i; | ||
150 | |||
151 | for (i = 0; i < 500; i++) { | ||
152 | u8 val = if_cs_read8(card, addr); | ||
153 | if (val == reg) | ||
154 | return i; | ||
155 | udelay(100); | ||
156 | } | ||
157 | return -ETIME; | ||
158 | } | ||
159 | |||
160 | |||
161 | |||
162 | /* Host control registers and their bit definitions */ | ||
163 | |||
164 | #define IF_CS_H_STATUS 0x00000000 | ||
165 | #define IF_CS_H_STATUS_TX_OVER 0x0001 | ||
166 | #define IF_CS_H_STATUS_RX_OVER 0x0002 | ||
167 | #define IF_CS_H_STATUS_DNLD_OVER 0x0004 | ||
168 | |||
169 | #define IF_CS_H_INT_CAUSE 0x00000002 | ||
170 | #define IF_CS_H_IC_TX_OVER 0x0001 | ||
171 | #define IF_CS_H_IC_RX_OVER 0x0002 | ||
172 | #define IF_CS_H_IC_DNLD_OVER 0x0004 | ||
173 | #define IF_CS_H_IC_HOST_EVENT 0x0008 | ||
174 | #define IF_CS_H_IC_MASK 0x001f | ||
175 | |||
176 | #define IF_CS_H_INT_MASK 0x00000004 | ||
177 | #define IF_CS_H_IM_MASK 0x001f | ||
178 | |||
179 | #define IF_CS_H_WRITE_LEN 0x00000014 | ||
180 | |||
181 | #define IF_CS_H_WRITE 0x00000016 | ||
182 | |||
183 | #define IF_CS_H_CMD_LEN 0x00000018 | ||
184 | |||
185 | #define IF_CS_H_CMD 0x0000001A | ||
186 | |||
187 | #define IF_CS_C_READ_LEN 0x00000024 | ||
188 | |||
189 | #define IF_CS_H_READ 0x00000010 | ||
190 | |||
191 | /* Card control registers and their bit definitions */ | ||
192 | |||
193 | #define IF_CS_C_STATUS 0x00000020 | ||
194 | #define IF_CS_C_S_TX_DNLD_RDY 0x0001 | ||
195 | #define IF_CS_C_S_RX_UPLD_RDY 0x0002 | ||
196 | #define IF_CS_C_S_CMD_DNLD_RDY 0x0004 | ||
197 | #define IF_CS_C_S_CMD_UPLD_RDY 0x0008 | ||
198 | #define IF_CS_C_S_CARDEVENT 0x0010 | ||
199 | #define IF_CS_C_S_MASK 0x001f | ||
200 | #define IF_CS_C_S_STATUS_MASK 0x7f00 | ||
201 | /* The following definitions should be the same as the MRVDRV_ ones */ | ||
202 | |||
203 | #if MRVDRV_CMD_DNLD_RDY != IF_CS_C_S_CMD_DNLD_RDY | ||
204 | #error MRVDRV_CMD_DNLD_RDY and IF_CS_C_S_CMD_DNLD_RDY not in sync | ||
205 | #endif | ||
206 | #if MRVDRV_CMD_UPLD_RDY != IF_CS_C_S_CMD_UPLD_RDY | ||
207 | #error MRVDRV_CMD_UPLD_RDY and IF_CS_C_S_CMD_UPLD_RDY not in sync | ||
208 | #endif | ||
209 | #if MRVDRV_CARDEVENT != IF_CS_C_S_CARDEVENT | ||
210 | #error MRVDRV_CARDEVENT and IF_CS_C_S_CARDEVENT not in sync | ||
211 | #endif | ||
212 | |||
213 | #define IF_CS_C_INT_CAUSE 0x00000022 | ||
214 | #define IF_CS_C_IC_MASK 0x001f | ||
215 | |||
216 | #define IF_CS_C_SQ_READ_LOW 0x00000028 | ||
217 | #define IF_CS_C_SQ_HELPER_OK 0x10 | ||
218 | |||
219 | #define IF_CS_C_CMD_LEN 0x00000030 | ||
220 | |||
221 | #define IF_CS_C_CMD 0x00000012 | ||
222 | |||
223 | #define IF_CS_SCRATCH 0x0000003F | ||
224 | |||
225 | |||
226 | |||
227 | /********************************************************************/ | ||
228 | /* Interrupts */ | ||
229 | /********************************************************************/ | ||
230 | |||
231 | static inline void if_cs_enable_ints(struct if_cs_card *card) | ||
232 | { | ||
233 | lbs_deb_enter(LBS_DEB_CS); | ||
234 | if_cs_write16(card, IF_CS_H_INT_MASK, 0); | ||
235 | } | ||
236 | |||
237 | static inline void if_cs_disable_ints(struct if_cs_card *card) | ||
238 | { | ||
239 | lbs_deb_enter(LBS_DEB_CS); | ||
240 | if_cs_write16(card, IF_CS_H_INT_MASK, IF_CS_H_IM_MASK); | ||
241 | } | ||
242 | |||
243 | static irqreturn_t if_cs_interrupt(int irq, void *data) | ||
244 | { | ||
245 | struct if_cs_card *card = (struct if_cs_card *)data; | ||
246 | u16 int_cause; | ||
247 | |||
248 | lbs_deb_enter(LBS_DEB_CS); | ||
249 | |||
250 | int_cause = if_cs_read16(card, IF_CS_C_INT_CAUSE); | ||
251 | switch (int_cause) { | ||
252 | case 0x0000: | ||
253 | /* not for us */ | ||
254 | return IRQ_NONE; | ||
255 | case 0xffff: | ||
256 | /* if one reads junk, then probably the card was removed */ | ||
257 | card->priv->adapter->surpriseremoved = 1; | ||
258 | break; | ||
259 | case IF_CS_H_IC_TX_OVER: | ||
260 | if (card->priv->adapter->connect_status == LIBERTAS_CONNECTED) | ||
261 | netif_wake_queue(card->priv->dev); | ||
262 | /* fallthrought */ | ||
263 | default: | ||
264 | /* clear interrupt */ | ||
265 | if_cs_write16(card, IF_CS_C_INT_CAUSE, int_cause & IF_CS_C_IC_MASK); | ||
266 | if_cs_disable_ints(card); | ||
267 | } | ||
268 | |||
269 | libertas_interrupt(card->priv->dev); | ||
270 | |||
271 | return IRQ_HANDLED; | ||
272 | } | ||
273 | |||
274 | |||
275 | |||
276 | |||
277 | /********************************************************************/ | ||
278 | /* I/O */ | ||
279 | /********************************************************************/ | ||
280 | |||
281 | /* | ||
282 | * Called from if_cs_host_to_card to send a command to the hardware | ||
283 | */ | ||
284 | static int if_cs_send_cmd(wlan_private *priv, u8 *buf, u16 nb) | ||
285 | { | ||
286 | struct if_cs_card *card = (struct if_cs_card *)priv->card; | ||
287 | int ret = -1; | ||
288 | int loops = 0; | ||
289 | |||
290 | lbs_deb_enter(LBS_DEB_CS); | ||
291 | |||
292 | /* Is hardware ready? */ | ||
293 | while (1) { | ||
294 | u16 val = if_cs_read16(card, IF_CS_C_STATUS); | ||
295 | if (val & IF_CS_C_S_CMD_DNLD_RDY) | ||
296 | break; | ||
297 | if (++loops > 100) { | ||
298 | lbs_pr_err("card not ready for commands\n"); | ||
299 | goto done; | ||
300 | } | ||
301 | mdelay(1); | ||
302 | } | ||
303 | |||
304 | if_cs_write16(card, IF_CS_H_CMD_LEN, nb); | ||
305 | |||
306 | if_cs_write16_rep(card, IF_CS_H_CMD, buf, nb / 2); | ||
307 | /* Are we supposed to transfer an odd amount of bytes? */ | ||
308 | if (nb & 1) | ||
309 | if_cs_write8(card, IF_CS_H_CMD, buf[nb-1]); | ||
310 | |||
311 | /* "Assert the download over interrupt command in the Host | ||
312 | * status register" */ | ||
313 | if_cs_write16(card, IF_CS_H_STATUS, IF_CS_H_STATUS_DNLD_OVER); | ||
314 | |||
315 | /* "Assert the download over interrupt command in the Card | ||
316 | * interrupt case register" */ | ||
317 | if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_DNLD_OVER); | ||
318 | ret = 0; | ||
319 | |||
320 | done: | ||
321 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); | ||
322 | return ret; | ||
323 | } | ||
324 | |||
325 | |||
326 | /* | ||
327 | * Called from if_cs_host_to_card to send a data to the hardware | ||
328 | */ | ||
329 | static void if_cs_send_data(wlan_private *priv, u8 *buf, u16 nb) | ||
330 | { | ||
331 | struct if_cs_card *card = (struct if_cs_card *)priv->card; | ||
332 | |||
333 | lbs_deb_enter(LBS_DEB_CS); | ||
334 | |||
335 | if_cs_write16(card, IF_CS_H_WRITE_LEN, nb); | ||
336 | |||
337 | /* write even number of bytes, then odd byte if necessary */ | ||
338 | if_cs_write16_rep(card, IF_CS_H_WRITE, buf, nb / 2); | ||
339 | if (nb & 1) | ||
340 | if_cs_write8(card, IF_CS_H_WRITE, buf[nb-1]); | ||
341 | |||
342 | if_cs_write16(card, IF_CS_H_STATUS, IF_CS_H_STATUS_TX_OVER); | ||
343 | if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_STATUS_TX_OVER); | ||
344 | |||
345 | lbs_deb_leave(LBS_DEB_CS); | ||
346 | } | ||
347 | |||
348 | |||
349 | /* | ||
350 | * Get the command result out of the card. | ||
351 | */ | ||
352 | static int if_cs_receive_cmdres(wlan_private *priv, u8* data, u32 *len) | ||
353 | { | ||
354 | int ret = -1; | ||
355 | u16 val; | ||
356 | |||
357 | lbs_deb_enter(LBS_DEB_CS); | ||
358 | |||
359 | /* is hardware ready? */ | ||
360 | val = if_cs_read16(priv->card, IF_CS_C_STATUS); | ||
361 | if ((val & IF_CS_C_S_CMD_UPLD_RDY) == 0) { | ||
362 | lbs_pr_err("card not ready for CMD\n"); | ||
363 | goto out; | ||
364 | } | ||
365 | |||
366 | *len = if_cs_read16(priv->card, IF_CS_C_CMD_LEN); | ||
367 | if ((*len == 0) || (*len > MRVDRV_SIZE_OF_CMD_BUFFER)) { | ||
368 | lbs_pr_err("card cmd buffer has invalid # of bytes (%d)\n", *len); | ||
369 | goto out; | ||
370 | } | ||
371 | |||
372 | /* read even number of bytes, then odd byte if necessary */ | ||
373 | if_cs_read16_rep(priv->card, IF_CS_C_CMD, data, *len/sizeof(u16)); | ||
374 | if (*len & 1) | ||
375 | data[*len-1] = if_cs_read8(priv->card, IF_CS_C_CMD); | ||
376 | |||
377 | ret = 0; | ||
378 | out: | ||
379 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len); | ||
380 | return ret; | ||
381 | } | ||
382 | |||
383 | |||
384 | static struct sk_buff *if_cs_receive_data(wlan_private *priv) | ||
385 | { | ||
386 | struct sk_buff *skb = NULL; | ||
387 | u16 len; | ||
388 | u8 *data; | ||
389 | |||
390 | lbs_deb_enter(LBS_DEB_CS); | ||
391 | |||
392 | len = if_cs_read16(priv->card, IF_CS_C_READ_LEN); | ||
393 | if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { | ||
394 | lbs_pr_err("card data buffer has invalid # of bytes (%d)\n", len); | ||
395 | priv->stats.rx_dropped++; | ||
396 | printk(KERN_INFO "##HS %s:%d TODO\n", __FUNCTION__, __LINE__); | ||
397 | goto dat_err; | ||
398 | } | ||
399 | |||
400 | //TODO: skb = dev_alloc_skb(len+ETH_FRAME_LEN+MRVDRV_SNAP_HEADER_LEN+EXTRA_LEN); | ||
401 | skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); | ||
402 | if (!skb) | ||
403 | goto out; | ||
404 | data = skb_put(skb, len); | ||
405 | |||
406 | /* read even number of bytes, then odd byte if necessary */ | ||
407 | if_cs_read16_rep(priv->card, IF_CS_H_READ, data, len/sizeof(u16)); | ||
408 | if (len & 1) | ||
409 | data[len-1] = if_cs_read8(priv->card, IF_CS_H_READ); | ||
410 | |||
411 | dat_err: | ||
412 | if_cs_write16(priv->card, IF_CS_H_STATUS, IF_CS_H_STATUS_RX_OVER); | ||
413 | if_cs_write16(priv->card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_RX_OVER); | ||
414 | |||
415 | out: | ||
416 | lbs_deb_leave_args(LBS_DEB_CS, "ret %p", skb); | ||
417 | return skb; | ||
418 | } | ||
419 | |||
420 | |||
421 | |||
422 | /********************************************************************/ | ||
423 | /* Firmware */ | ||
424 | /********************************************************************/ | ||
425 | |||
426 | /* | ||
427 | * Tries to program the helper firmware. | ||
428 | * | ||
429 | * Return 0 on success | ||
430 | */ | ||
431 | static int if_cs_prog_helper(struct if_cs_card *card) | ||
432 | { | ||
433 | int ret = 0; | ||
434 | int sent = 0; | ||
435 | u8 scratch; | ||
436 | const struct firmware *fw; | ||
437 | |||
438 | lbs_deb_enter(LBS_DEB_CS); | ||
439 | |||
440 | scratch = if_cs_read8(card, IF_CS_SCRATCH); | ||
441 | |||
442 | /* "If the value is 0x5a, the firmware is already | ||
443 | * downloaded successfully" | ||
444 | */ | ||
445 | if (scratch == 0x5a) | ||
446 | goto done; | ||
447 | |||
448 | /* "If the value is != 00, it is invalid value of register */ | ||
449 | if (scratch != 0x00) { | ||
450 | ret = -ENODEV; | ||
451 | goto done; | ||
452 | } | ||
453 | |||
454 | /* TODO: make firmware file configurable */ | ||
455 | ret = request_firmware(&fw, "libertas_cs_helper.fw", | ||
456 | &handle_to_dev(card->p_dev)); | ||
457 | if (ret) { | ||
458 | lbs_pr_err("can't load helper firmware\n"); | ||
459 | ret = -ENODEV; | ||
460 | goto done; | ||
461 | } | ||
462 | lbs_deb_cs("helper size %d\n", fw->size); | ||
463 | |||
464 | /* "Set the 5 bytes of the helper image to 0" */ | ||
465 | /* Not needed, this contains an ARM branch instruction */ | ||
466 | |||
467 | for (;;) { | ||
468 | /* "the number of bytes to send is 256" */ | ||
469 | int count = 256; | ||
470 | int remain = fw->size - sent; | ||
471 | |||
472 | if (remain < count) | ||
473 | count = remain; | ||
474 | /* printk(KERN_INFO "//HS %d loading %d of %d bytes\n", | ||
475 | __LINE__, sent, fw->size); */ | ||
476 | |||
477 | /* "write the number of bytes to be sent to the I/O Command | ||
478 | * write length register" */ | ||
479 | if_cs_write16(card, IF_CS_H_CMD_LEN, count); | ||
480 | |||
481 | /* "write this to I/O Command port register as 16 bit writes */ | ||
482 | if (count) | ||
483 | if_cs_write16_rep(card, IF_CS_H_CMD, | ||
484 | &fw->data[sent], | ||
485 | count >> 1); | ||
486 | |||
487 | /* "Assert the download over interrupt command in the Host | ||
488 | * status register" */ | ||
489 | if_cs_write8(card, IF_CS_H_STATUS, IF_CS_H_STATUS_DNLD_OVER); | ||
490 | |||
491 | /* "Assert the download over interrupt command in the Card | ||
492 | * interrupt case register" */ | ||
493 | if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_DNLD_OVER); | ||
494 | |||
495 | /* "The host polls the Card Status register ... for 50 ms before | ||
496 | declaring a failure */ | ||
497 | ret = if_cs_poll_while_fw_download(card, IF_CS_C_STATUS, | ||
498 | IF_CS_C_S_CMD_DNLD_RDY); | ||
499 | if (ret < 0) { | ||
500 | lbs_pr_err("can't download helper at 0x%x, ret %d\n", | ||
501 | sent, ret); | ||
502 | goto done; | ||
503 | } | ||
504 | |||
505 | if (count == 0) | ||
506 | break; | ||
507 | |||
508 | sent += count; | ||
509 | } | ||
510 | |||
511 | release_firmware(fw); | ||
512 | ret = 0; | ||
513 | |||
514 | done: | ||
515 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); | ||
516 | return ret; | ||
517 | } | ||
518 | |||
519 | |||
520 | static int if_cs_prog_real(struct if_cs_card *card) | ||
521 | { | ||
522 | const struct firmware *fw; | ||
523 | int ret = 0; | ||
524 | int retry = 0; | ||
525 | int len = 0; | ||
526 | int sent; | ||
527 | |||
528 | lbs_deb_enter(LBS_DEB_CS); | ||
529 | |||
530 | /* TODO: make firmware file configurable */ | ||
531 | ret = request_firmware(&fw, "libertas_cs.fw", | ||
532 | &handle_to_dev(card->p_dev)); | ||
533 | if (ret) { | ||
534 | lbs_pr_err("can't load firmware\n"); | ||
535 | ret = -ENODEV; | ||
536 | goto done; | ||
537 | } | ||
538 | lbs_deb_cs("fw size %d\n", fw->size); | ||
539 | |||
540 | ret = if_cs_poll_while_fw_download(card, IF_CS_C_SQ_READ_LOW, IF_CS_C_SQ_HELPER_OK); | ||
541 | if (ret < 0) { | ||
542 | int i; | ||
543 | lbs_pr_err("helper firmware doesn't answer\n"); | ||
544 | for (i = 0; i < 0x50; i += 2) | ||
545 | printk(KERN_INFO "## HS %02x: %04x\n", | ||
546 | i, if_cs_read16(card, i)); | ||
547 | goto err_release; | ||
548 | } | ||
549 | |||
550 | for (sent = 0; sent < fw->size; sent += len) { | ||
551 | len = if_cs_read16(card, IF_CS_C_SQ_READ_LOW); | ||
552 | /* printk(KERN_INFO "//HS %d loading %d of %d bytes\n", | ||
553 | __LINE__, sent, fw->size); */ | ||
554 | if (len & 1) { | ||
555 | retry++; | ||
556 | lbs_pr_info("odd, need to retry this firmware block\n"); | ||
557 | } else { | ||
558 | retry = 0; | ||
559 | } | ||
560 | |||
561 | if (retry > 20) { | ||
562 | lbs_pr_err("could not download firmware\n"); | ||
563 | ret = -ENODEV; | ||
564 | goto err_release; | ||
565 | } | ||
566 | if (retry) { | ||
567 | sent -= len; | ||
568 | } | ||
569 | |||
570 | |||
571 | if_cs_write16(card, IF_CS_H_CMD_LEN, len); | ||
572 | |||
573 | if_cs_write16_rep(card, IF_CS_H_CMD, | ||
574 | &fw->data[sent], | ||
575 | (len+1) >> 1); | ||
576 | if_cs_write8(card, IF_CS_H_STATUS, IF_CS_H_STATUS_DNLD_OVER); | ||
577 | if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_DNLD_OVER); | ||
578 | |||
579 | ret = if_cs_poll_while_fw_download(card, IF_CS_C_STATUS, | ||
580 | IF_CS_C_S_CMD_DNLD_RDY); | ||
581 | if (ret < 0) { | ||
582 | lbs_pr_err("can't download firmware at 0x%x\n", sent); | ||
583 | goto err_release; | ||
584 | } | ||
585 | } | ||
586 | |||
587 | ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a); | ||
588 | if (ret < 0) { | ||
589 | lbs_pr_err("firmware download failed\n"); | ||
590 | goto err_release; | ||
591 | } | ||
592 | |||
593 | ret = 0; | ||
594 | goto done; | ||
595 | |||
596 | |||
597 | err_release: | ||
598 | release_firmware(fw); | ||
599 | |||
600 | done: | ||
601 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); | ||
602 | return ret; | ||
603 | } | ||
604 | |||
605 | |||
606 | |||
607 | /********************************************************************/ | ||
608 | /* Callback functions for libertas.ko */ | ||
609 | /********************************************************************/ | ||
610 | |||
611 | static int if_cs_register_dev(wlan_private *priv) | ||
612 | { | ||
613 | struct if_cs_card *card = (struct if_cs_card *)priv->card; | ||
614 | |||
615 | lbs_deb_enter(LBS_DEB_CS); | ||
616 | |||
617 | card->priv = priv; | ||
618 | |||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | |||
623 | static int if_cs_unregister_dev(wlan_private *priv) | ||
624 | { | ||
625 | lbs_deb_enter(LBS_DEB_CS); | ||
626 | |||
627 | /* | ||
628 | * Nothing special here. Because the device's power gets turned off | ||
629 | * anyway, there's no need to send a RESET command like in if_usb.c | ||
630 | */ | ||
631 | |||
632 | return 0; | ||
633 | } | ||
634 | |||
635 | |||
636 | /* | ||
637 | * This callback is a dummy. The reason is that the USB code needs | ||
638 | * to have various things set up in order to be able to download the | ||
639 | * firmware. That's not needed in our case. | ||
640 | * | ||
641 | * On the contrary, if libertas_add_card() has been called and we're | ||
642 | * then later called via libertas_activate_card(), but without a valid | ||
643 | * firmware, then it's quite tedious to tear down the half-installed | ||
644 | * card. Therefore, we download the firmware before calling adding/ | ||
645 | * activating the card in the first place. If that doesn't work, we | ||
646 | * won't call into libertas.ko at all. | ||
647 | */ | ||
648 | |||
649 | static int if_cs_prog_firmware(wlan_private *priv) | ||
650 | { | ||
651 | priv->adapter->fw_ready = 1; | ||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | |||
656 | /* Send commands or data packets to the card */ | ||
657 | static int if_cs_host_to_card(wlan_private *priv, u8 type, u8 *buf, u16 nb) | ||
658 | { | ||
659 | int ret = -1; | ||
660 | |||
661 | lbs_deb_enter_args(LBS_DEB_CS, "type %d, bytes %d", type, nb); | ||
662 | |||
663 | switch (type) { | ||
664 | case MVMS_DAT: | ||
665 | priv->dnld_sent = DNLD_CMD_SENT; | ||
666 | if_cs_send_data(priv, buf, nb); | ||
667 | ret = 0; | ||
668 | break; | ||
669 | case MVMS_CMD: | ||
670 | ret = if_cs_send_cmd(priv, buf, nb); | ||
671 | break; | ||
672 | default: | ||
673 | lbs_pr_err("%s: unsupported type %d\n", __FUNCTION__, type); | ||
674 | } | ||
675 | |||
676 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); | ||
677 | return ret; | ||
678 | } | ||
679 | |||
680 | |||
681 | static int if_cs_get_int_status(wlan_private *priv, u8 *ireg) | ||
682 | { | ||
683 | struct if_cs_card *card = (struct if_cs_card *)priv->card; | ||
684 | //wlan_adapter *adapter = priv->adapter; | ||
685 | int ret = 0; | ||
686 | u16 int_cause; | ||
687 | u8 *cmdbuf; | ||
688 | *ireg = 0; | ||
689 | |||
690 | lbs_deb_enter(LBS_DEB_CS); | ||
691 | |||
692 | if (priv->adapter->surpriseremoved) | ||
693 | goto out; | ||
694 | |||
695 | int_cause = if_cs_read16(card, IF_CS_C_INT_CAUSE) & IF_CS_C_IC_MASK; | ||
696 | if_cs_write16(card, IF_CS_C_INT_CAUSE, int_cause); | ||
697 | |||
698 | *ireg = if_cs_read16(card, IF_CS_C_STATUS) & IF_CS_C_S_MASK; | ||
699 | if_cs_enable_ints(card); | ||
700 | |||
701 | if (!*ireg) | ||
702 | goto sbi_get_int_status_exit; | ||
703 | |||
704 | sbi_get_int_status_exit: | ||
705 | |||
706 | /* is there a data packet for us? */ | ||
707 | if (*ireg & IF_CS_C_S_RX_UPLD_RDY) { | ||
708 | struct sk_buff *skb = if_cs_receive_data(priv); | ||
709 | libertas_process_rxed_packet(priv, skb); | ||
710 | *ireg &= ~IF_CS_C_S_RX_UPLD_RDY; | ||
711 | } | ||
712 | |||
713 | if (*ireg & IF_CS_C_S_TX_DNLD_RDY) { | ||
714 | priv->dnld_sent = DNLD_RES_RECEIVED; | ||
715 | } | ||
716 | |||
717 | /* Card has a command result for us */ | ||
718 | if (*ireg & IF_CS_C_S_CMD_UPLD_RDY) { | ||
719 | spin_lock(&priv->adapter->driver_lock); | ||
720 | if (!priv->adapter->cur_cmd) { | ||
721 | cmdbuf = priv->upld_buf; | ||
722 | priv->adapter->hisregcpy &= ~IF_CS_C_S_RX_UPLD_RDY; | ||
723 | } else { | ||
724 | cmdbuf = priv->adapter->cur_cmd->bufvirtualaddr; | ||
725 | } | ||
726 | |||
727 | ret = if_cs_receive_cmdres(priv, cmdbuf, &priv->upld_len); | ||
728 | spin_unlock(&priv->adapter->driver_lock); | ||
729 | if (ret < 0) | ||
730 | lbs_pr_err("could not receive cmd from card\n"); | ||
731 | } | ||
732 | |||
733 | out: | ||
734 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d, ireg 0x%x, hisregcpy 0x%x", ret, *ireg, priv->adapter->hisregcpy); | ||
735 | return ret; | ||
736 | } | ||
737 | |||
738 | |||
739 | static int if_cs_read_event_cause(wlan_private *priv) | ||
740 | { | ||
741 | lbs_deb_enter(LBS_DEB_CS); | ||
742 | |||
743 | priv->adapter->eventcause = (if_cs_read16(priv->card, IF_CS_C_STATUS) & IF_CS_C_S_STATUS_MASK) >> 5; | ||
744 | if_cs_write16(priv->card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_HOST_EVENT); | ||
745 | |||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | |||
750 | |||
751 | /********************************************************************/ | ||
752 | /* Card Services */ | ||
753 | /********************************************************************/ | ||
754 | |||
755 | /* | ||
756 | * After a card is removed, if_cs_release() will unregister the | ||
757 | * device, and release the PCMCIA configuration. If the device is | ||
758 | * still open, this will be postponed until it is closed. | ||
759 | */ | ||
760 | static void if_cs_release(struct pcmcia_device *p_dev) | ||
761 | { | ||
762 | struct if_cs_card *card = p_dev->priv; | ||
763 | |||
764 | lbs_deb_enter(LBS_DEB_CS); | ||
765 | |||
766 | pcmcia_disable_device(p_dev); | ||
767 | free_irq(p_dev->irq.AssignedIRQ, card); | ||
768 | if (card->iobase) | ||
769 | ioport_unmap(card->iobase); | ||
770 | |||
771 | lbs_deb_leave(LBS_DEB_CS); | ||
772 | } | ||
773 | |||
774 | |||
775 | /* | ||
776 | * This creates an "instance" of the driver, allocating local data | ||
777 | * structures for one device. The device is registered with Card | ||
778 | * Services. | ||
779 | * | ||
780 | * The dev_link structure is initialized, but we don't actually | ||
781 | * configure the card at this point -- we wait until we receive a card | ||
782 | * insertion event. | ||
783 | */ | ||
784 | static int if_cs_probe(struct pcmcia_device *p_dev) | ||
785 | { | ||
786 | int ret = -ENOMEM; | ||
787 | wlan_private *priv; | ||
788 | struct if_cs_card *card; | ||
789 | /* CIS parsing */ | ||
790 | tuple_t tuple; | ||
791 | cisparse_t parse; | ||
792 | cistpl_cftable_entry_t *cfg = &parse.cftable_entry; | ||
793 | cistpl_io_t *io = &cfg->io; | ||
794 | u_char buf[64]; | ||
795 | |||
796 | lbs_deb_enter(LBS_DEB_CS); | ||
797 | |||
798 | card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL); | ||
799 | if (!card) { | ||
800 | lbs_pr_err("error in kzalloc\n"); | ||
801 | goto out; | ||
802 | } | ||
803 | card->p_dev = p_dev; | ||
804 | p_dev->priv = card; | ||
805 | |||
806 | p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; | ||
807 | p_dev->irq.Handler = NULL; | ||
808 | p_dev->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; | ||
809 | |||
810 | p_dev->conf.Attributes = 0; | ||
811 | p_dev->conf.IntType = INT_MEMORY_AND_IO; | ||
812 | |||
813 | tuple.Attributes = 0; | ||
814 | tuple.TupleData = buf; | ||
815 | tuple.TupleDataMax = sizeof(buf); | ||
816 | tuple.TupleOffset = 0; | ||
817 | |||
818 | tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; | ||
819 | if ((ret = pcmcia_get_first_tuple(p_dev, &tuple)) != 0 || | ||
820 | (ret = pcmcia_get_tuple_data(p_dev, &tuple)) != 0 || | ||
821 | (ret = pcmcia_parse_tuple(p_dev, &tuple, &parse)) != 0) | ||
822 | { | ||
823 | lbs_pr_err("error in pcmcia_get_first_tuple etc\n"); | ||
824 | goto out1; | ||
825 | } | ||
826 | |||
827 | p_dev->conf.ConfigIndex = cfg->index; | ||
828 | |||
829 | /* Do we need to allocate an interrupt? */ | ||
830 | if (cfg->irq.IRQInfo1) { | ||
831 | p_dev->conf.Attributes |= CONF_ENABLE_IRQ; | ||
832 | } | ||
833 | |||
834 | /* IO window settings */ | ||
835 | if (cfg->io.nwin != 1) { | ||
836 | lbs_pr_err("wrong CIS (check number of IO windows)\n"); | ||
837 | ret = -ENODEV; | ||
838 | goto out1; | ||
839 | } | ||
840 | p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; | ||
841 | p_dev->io.BasePort1 = io->win[0].base; | ||
842 | p_dev->io.NumPorts1 = io->win[0].len; | ||
843 | |||
844 | /* This reserves IO space but doesn't actually enable it */ | ||
845 | ret = pcmcia_request_io(p_dev, &p_dev->io); | ||
846 | if (ret) { | ||
847 | lbs_pr_err("error in pcmcia_request_io\n"); | ||
848 | goto out1; | ||
849 | } | ||
850 | |||
851 | /* | ||
852 | * Allocate an interrupt line. Note that this does not assign | ||
853 | * a handler to the interrupt, unless the 'Handler' member of | ||
854 | * the irq structure is initialized. | ||
855 | */ | ||
856 | if (p_dev->conf.Attributes & CONF_ENABLE_IRQ) { | ||
857 | ret = pcmcia_request_irq(p_dev, &p_dev->irq); | ||
858 | if (ret) { | ||
859 | lbs_pr_err("error in pcmcia_request_irq\n"); | ||
860 | goto out1; | ||
861 | } | ||
862 | } | ||
863 | |||
864 | /* Initialize io access */ | ||
865 | card->iobase = ioport_map(p_dev->io.BasePort1, p_dev->io.NumPorts1); | ||
866 | if (!card->iobase) { | ||
867 | lbs_pr_err("error in ioport_map\n"); | ||
868 | ret = -EIO; | ||
869 | goto out1; | ||
870 | } | ||
871 | |||
872 | /* | ||
873 | * This actually configures the PCMCIA socket -- setting up | ||
874 | * the I/O windows and the interrupt mapping, and putting the | ||
875 | * card and host interface into "Memory and IO" mode. | ||
876 | */ | ||
877 | ret = pcmcia_request_configuration(p_dev, &p_dev->conf); | ||
878 | if (ret) { | ||
879 | lbs_pr_err("error in pcmcia_request_configuration\n"); | ||
880 | goto out2; | ||
881 | } | ||
882 | |||
883 | /* Finally, report what we've done */ | ||
884 | lbs_deb_cs("irq %d, io 0x%04x-0x%04x\n", | ||
885 | p_dev->irq.AssignedIRQ, p_dev->io.BasePort1, | ||
886 | p_dev->io.BasePort1 + p_dev->io.NumPorts1 - 1); | ||
887 | |||
888 | if_cs_enable_ints(card); | ||
889 | |||
890 | /* Load the firmware early, before calling into libertas.ko */ | ||
891 | ret = if_cs_prog_helper(card); | ||
892 | if (ret == 0) | ||
893 | ret = if_cs_prog_real(card); | ||
894 | if (ret) | ||
895 | goto out2; | ||
896 | |||
897 | /* Make this card known to the libertas driver */ | ||
898 | priv = libertas_add_card(card, &p_dev->dev); | ||
899 | if (!priv) { | ||
900 | ret = -ENOMEM; | ||
901 | goto out2; | ||
902 | } | ||
903 | |||
904 | /* Store pointers to our call-back functions */ | ||
905 | priv->card = card; | ||
906 | priv->hw_register_dev = if_cs_register_dev; | ||
907 | priv->hw_unregister_dev = if_cs_unregister_dev; | ||
908 | priv->hw_prog_firmware = if_cs_prog_firmware; | ||
909 | priv->hw_host_to_card = if_cs_host_to_card; | ||
910 | priv->hw_get_int_status = if_cs_get_int_status; | ||
911 | priv->hw_read_event_cause = if_cs_read_event_cause; | ||
912 | |||
913 | /* Now actually get the IRQ */ | ||
914 | ret = request_irq(p_dev->irq.AssignedIRQ, if_cs_interrupt, | ||
915 | IRQF_SHARED, DRV_NAME, card); | ||
916 | if (ret) { | ||
917 | lbs_pr_err("error in request_irq\n"); | ||
918 | goto out3; | ||
919 | } | ||
920 | |||
921 | /* And finally bring the card up */ | ||
922 | if (libertas_activate_card(priv) != 0) { | ||
923 | lbs_pr_err("could not activate card\n"); | ||
924 | goto out3; | ||
925 | } | ||
926 | |||
927 | ret = 0; | ||
928 | goto out; | ||
929 | |||
930 | out3: | ||
931 | libertas_remove_card(priv); | ||
932 | out2: | ||
933 | ioport_unmap(card->iobase); | ||
934 | out1: | ||
935 | pcmcia_disable_device(p_dev); | ||
936 | out: | ||
937 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); | ||
938 | return ret; | ||
939 | } | ||
940 | |||
941 | |||
942 | /* | ||
943 | * This deletes a driver "instance". The device is de-registered with | ||
944 | * Card Services. If it has been released, all local data structures | ||
945 | * are freed. Otherwise, the structures will be freed when the device | ||
946 | * is released. | ||
947 | */ | ||
948 | static void if_cs_detach(struct pcmcia_device *p_dev) | ||
949 | { | ||
950 | struct if_cs_card *card = p_dev->priv; | ||
951 | |||
952 | lbs_deb_enter(LBS_DEB_CS); | ||
953 | |||
954 | libertas_remove_card(card->priv); | ||
955 | if_cs_release(p_dev); | ||
956 | kfree(card); | ||
957 | |||
958 | lbs_deb_leave(LBS_DEB_CS); | ||
959 | } | ||
960 | |||
961 | |||
962 | |||
963 | /********************************************************************/ | ||
964 | /* Module initialization */ | ||
965 | /********************************************************************/ | ||
966 | |||
967 | static struct pcmcia_device_id if_cs_ids[] = { | ||
968 | PCMCIA_DEVICE_MANF_CARD(0x02df, 0x8103), | ||
969 | PCMCIA_DEVICE_NULL, | ||
970 | }; | ||
971 | MODULE_DEVICE_TABLE(pcmcia, if_cs_ids); | ||
972 | |||
973 | |||
974 | static struct pcmcia_driver libertas_driver = { | ||
975 | .owner = THIS_MODULE, | ||
976 | .drv = { | ||
977 | .name = DRV_NAME, | ||
978 | }, | ||
979 | .probe = if_cs_probe, | ||
980 | .remove = if_cs_detach, | ||
981 | .id_table = if_cs_ids, | ||
982 | }; | ||
983 | |||
984 | |||
985 | static int __init if_cs_init(void) | ||
986 | { | ||
987 | int ret; | ||
988 | |||
989 | lbs_deb_enter(LBS_DEB_CS); | ||
990 | ret = pcmcia_register_driver(&libertas_driver); | ||
991 | lbs_deb_leave(LBS_DEB_CS); | ||
992 | return ret; | ||
993 | } | ||
994 | |||
995 | |||
996 | static void __exit if_cs_exit(void) | ||
997 | { | ||
998 | lbs_deb_enter(LBS_DEB_CS); | ||
999 | pcmcia_unregister_driver(&libertas_driver); | ||
1000 | lbs_deb_leave(LBS_DEB_CS); | ||
1001 | } | ||
1002 | |||
1003 | |||
1004 | module_init(if_cs_init); | ||
1005 | module_exit(if_cs_exit); | ||