diff options
author | Holger Schurig <hs4233@mail.mn-solutions.de> | 2008-04-01 08:50:43 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-04-16 15:59:56 -0400 |
commit | 7919b89c8276d657976d4d4d6b7cb58ea1aa08c3 (patch) | |
tree | 31fc24e2f8b7d8eeee67347333e078591796d4b7 /drivers/net/wireless/libertas/if_cs.c | |
parent | 98dd6a575928ed9c42130d208e6bfb0f7a914d5a (diff) |
libertas: convert libertas driver to use an event/cmdresp queue
This patch (co-developed by Dan Williams and Holger Schurig) uses a kfifo
object for events and a swapping buffer scheme for the command response to
preserve the zero-copy semantics of the CF driver and keep memory usage low.
The main thread should only ever touch the buffer indexed by priv->resp_idx,
while the interface code is free to write to the second buffer, then swap
priv->resp_idx under the driver spinlock. The firmware specs only permit
one in-flight command, so there will only ever be one command response to
process at a time.
Signed-off-by: Holger Schurig <hs4233@mail.mn-solutions.de>
Signed-off-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas/if_cs.c')
-rw-r--r-- | drivers/net/wireless/libertas/if_cs.c | 244 |
1 files changed, 105 insertions, 139 deletions
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index 53afebac7146..54280e292ea5 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c | |||
@@ -83,14 +83,14 @@ static inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg) | |||
83 | { | 83 | { |
84 | unsigned int val = ioread8(card->iobase + reg); | 84 | unsigned int val = ioread8(card->iobase + reg); |
85 | if (debug_output) | 85 | if (debug_output) |
86 | printk(KERN_INFO "##inb %08x<%02x\n", reg, val); | 86 | printk(KERN_INFO "inb %08x<%02x\n", reg, val); |
87 | return val; | 87 | return val; |
88 | } | 88 | } |
89 | static inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg) | 89 | static inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg) |
90 | { | 90 | { |
91 | unsigned int val = ioread16(card->iobase + reg); | 91 | unsigned int val = ioread16(card->iobase + reg); |
92 | if (debug_output) | 92 | if (debug_output) |
93 | printk(KERN_INFO "##inw %08x<%04x\n", reg, val); | 93 | printk(KERN_INFO "inw %08x<%04x\n", reg, val); |
94 | return val; | 94 | return val; |
95 | } | 95 | } |
96 | static inline void if_cs_read16_rep( | 96 | static inline void if_cs_read16_rep( |
@@ -100,7 +100,7 @@ static inline void if_cs_read16_rep( | |||
100 | unsigned long count) | 100 | unsigned long count) |
101 | { | 101 | { |
102 | if (debug_output) | 102 | if (debug_output) |
103 | printk(KERN_INFO "##insw %08x<(0x%lx words)\n", | 103 | printk(KERN_INFO "insw %08x<(0x%lx words)\n", |
104 | reg, count); | 104 | reg, count); |
105 | ioread16_rep(card->iobase + reg, buf, count); | 105 | ioread16_rep(card->iobase + reg, buf, count); |
106 | } | 106 | } |
@@ -108,14 +108,14 @@ static inline void if_cs_read16_rep( | |||
108 | static inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val) | 108 | static inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val) |
109 | { | 109 | { |
110 | if (debug_output) | 110 | if (debug_output) |
111 | printk(KERN_INFO "##outb %08x>%02x\n", reg, val); | 111 | printk(KERN_INFO "outb %08x>%02x\n", reg, val); |
112 | iowrite8(val, card->iobase + reg); | 112 | iowrite8(val, card->iobase + reg); |
113 | } | 113 | } |
114 | 114 | ||
115 | static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val) | 115 | static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val) |
116 | { | 116 | { |
117 | if (debug_output) | 117 | if (debug_output) |
118 | printk(KERN_INFO "##outw %08x>%04x\n", reg, val); | 118 | printk(KERN_INFO "outw %08x>%04x\n", reg, val); |
119 | iowrite16(val, card->iobase + reg); | 119 | iowrite16(val, card->iobase + reg); |
120 | } | 120 | } |
121 | 121 | ||
@@ -126,7 +126,7 @@ static inline void if_cs_write16_rep( | |||
126 | unsigned long count) | 126 | unsigned long count) |
127 | { | 127 | { |
128 | if (debug_output) | 128 | if (debug_output) |
129 | printk(KERN_INFO "##outsw %08x>(0x%lx words)\n", | 129 | printk(KERN_INFO "outsw %08x>(0x%lx words)\n", |
130 | reg, count); | 130 | reg, count); |
131 | iowrite16_rep(card->iobase + reg, buf, count); | 131 | iowrite16_rep(card->iobase + reg, buf, count); |
132 | } | 132 | } |
@@ -199,17 +199,6 @@ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 r | |||
199 | #define IF_CS_C_S_CARDEVENT 0x0010 | 199 | #define IF_CS_C_S_CARDEVENT 0x0010 |
200 | #define IF_CS_C_S_MASK 0x001f | 200 | #define IF_CS_C_S_MASK 0x001f |
201 | #define IF_CS_C_S_STATUS_MASK 0x7f00 | 201 | #define IF_CS_C_S_STATUS_MASK 0x7f00 |
202 | /* The following definitions should be the same as the MRVDRV_ ones */ | ||
203 | |||
204 | #if MRVDRV_CMD_DNLD_RDY != IF_CS_C_S_CMD_DNLD_RDY | ||
205 | #error MRVDRV_CMD_DNLD_RDY and IF_CS_C_S_CMD_DNLD_RDY not in sync | ||
206 | #endif | ||
207 | #if MRVDRV_CMD_UPLD_RDY != IF_CS_C_S_CMD_UPLD_RDY | ||
208 | #error MRVDRV_CMD_UPLD_RDY and IF_CS_C_S_CMD_UPLD_RDY not in sync | ||
209 | #endif | ||
210 | #if MRVDRV_CARDEVENT != IF_CS_C_S_CARDEVENT | ||
211 | #error MRVDRV_CARDEVENT and IF_CS_C_S_CARDEVENT not in sync | ||
212 | #endif | ||
213 | 202 | ||
214 | #define IF_CS_C_INT_CAUSE 0x00000022 | 203 | #define IF_CS_C_INT_CAUSE 0x00000022 |
215 | #define IF_CS_C_IC_MASK 0x001f | 204 | #define IF_CS_C_IC_MASK 0x001f |
@@ -226,55 +215,6 @@ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 r | |||
226 | 215 | ||
227 | 216 | ||
228 | /********************************************************************/ | 217 | /********************************************************************/ |
229 | /* Interrupts */ | ||
230 | /********************************************************************/ | ||
231 | |||
232 | static inline void if_cs_enable_ints(struct if_cs_card *card) | ||
233 | { | ||
234 | lbs_deb_enter(LBS_DEB_CS); | ||
235 | if_cs_write16(card, IF_CS_H_INT_MASK, 0); | ||
236 | } | ||
237 | |||
238 | static inline void if_cs_disable_ints(struct if_cs_card *card) | ||
239 | { | ||
240 | lbs_deb_enter(LBS_DEB_CS); | ||
241 | if_cs_write16(card, IF_CS_H_INT_MASK, IF_CS_H_IM_MASK); | ||
242 | } | ||
243 | |||
244 | static irqreturn_t if_cs_interrupt(int irq, void *data) | ||
245 | { | ||
246 | struct if_cs_card *card = data; | ||
247 | u16 int_cause; | ||
248 | |||
249 | lbs_deb_enter(LBS_DEB_CS); | ||
250 | |||
251 | int_cause = if_cs_read16(card, IF_CS_C_INT_CAUSE); | ||
252 | if (int_cause == 0x0) { | ||
253 | /* Not for us */ | ||
254 | return IRQ_NONE; | ||
255 | |||
256 | } else if (int_cause == 0xffff) { | ||
257 | /* Read in junk, the card has probably been removed */ | ||
258 | card->priv->surpriseremoved = 1; | ||
259 | return IRQ_HANDLED; | ||
260 | } else { | ||
261 | if (int_cause & IF_CS_H_IC_TX_OVER) | ||
262 | lbs_host_to_card_done(card->priv); | ||
263 | |||
264 | /* clear interrupt */ | ||
265 | if_cs_write16(card, IF_CS_C_INT_CAUSE, int_cause & IF_CS_C_IC_MASK); | ||
266 | } | ||
267 | spin_lock(&card->priv->driver_lock); | ||
268 | lbs_interrupt(card->priv); | ||
269 | spin_unlock(&card->priv->driver_lock); | ||
270 | |||
271 | return IRQ_HANDLED; | ||
272 | } | ||
273 | |||
274 | |||
275 | |||
276 | |||
277 | /********************************************************************/ | ||
278 | /* I/O */ | 218 | /* I/O */ |
279 | /********************************************************************/ | 219 | /********************************************************************/ |
280 | 220 | ||
@@ -351,6 +291,7 @@ static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) | |||
351 | */ | 291 | */ |
352 | static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len) | 292 | static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len) |
353 | { | 293 | { |
294 | unsigned long flags; | ||
354 | int ret = -1; | 295 | int ret = -1; |
355 | u16 val; | 296 | u16 val; |
356 | 297 | ||
@@ -378,6 +319,12 @@ static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len) | |||
378 | * bytes */ | 319 | * bytes */ |
379 | *len -= 8; | 320 | *len -= 8; |
380 | ret = 0; | 321 | ret = 0; |
322 | |||
323 | /* Clear this flag again */ | ||
324 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
325 | priv->dnld_sent = DNLD_RES_RECEIVED; | ||
326 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
327 | |||
381 | out: | 328 | out: |
382 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len); | 329 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len); |
383 | return ret; | 330 | return ret; |
@@ -396,11 +343,9 @@ static struct sk_buff *if_cs_receive_data(struct lbs_private *priv) | |||
396 | if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { | 343 | if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { |
397 | lbs_pr_err("card data buffer has invalid # of bytes (%d)\n", len); | 344 | lbs_pr_err("card data buffer has invalid # of bytes (%d)\n", len); |
398 | priv->stats.rx_dropped++; | 345 | priv->stats.rx_dropped++; |
399 | printk(KERN_INFO "##HS %s:%d TODO\n", __FUNCTION__, __LINE__); | ||
400 | goto dat_err; | 346 | goto dat_err; |
401 | } | 347 | } |
402 | 348 | ||
403 | //TODO: skb = dev_alloc_skb(len+ETH_FRAME_LEN+MRVDRV_SNAP_HEADER_LEN+EXTRA_LEN); | ||
404 | skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2); | 349 | skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2); |
405 | if (!skb) | 350 | if (!skb) |
406 | goto out; | 351 | goto out; |
@@ -425,6 +370,96 @@ out: | |||
425 | 370 | ||
426 | 371 | ||
427 | /********************************************************************/ | 372 | /********************************************************************/ |
373 | /* Interrupts */ | ||
374 | /********************************************************************/ | ||
375 | |||
376 | static inline void if_cs_enable_ints(struct if_cs_card *card) | ||
377 | { | ||
378 | lbs_deb_enter(LBS_DEB_CS); | ||
379 | if_cs_write16(card, IF_CS_H_INT_MASK, 0); | ||
380 | } | ||
381 | |||
382 | static inline void if_cs_disable_ints(struct if_cs_card *card) | ||
383 | { | ||
384 | lbs_deb_enter(LBS_DEB_CS); | ||
385 | if_cs_write16(card, IF_CS_H_INT_MASK, IF_CS_H_IM_MASK); | ||
386 | } | ||
387 | |||
388 | |||
389 | static irqreturn_t if_cs_interrupt(int irq, void *data) | ||
390 | { | ||
391 | struct if_cs_card *card = data; | ||
392 | struct lbs_private *priv = card->priv; | ||
393 | u16 cause; | ||
394 | |||
395 | lbs_deb_enter(LBS_DEB_CS); | ||
396 | |||
397 | cause = if_cs_read16(card, IF_CS_C_INT_CAUSE); | ||
398 | if_cs_write16(card, IF_CS_C_INT_CAUSE, cause & IF_CS_C_IC_MASK); | ||
399 | |||
400 | lbs_deb_cs("cause 0x%04x\n", cause); | ||
401 | if (cause == 0) { | ||
402 | /* Not for us */ | ||
403 | return IRQ_NONE; | ||
404 | } | ||
405 | |||
406 | if (cause == 0xffff) { | ||
407 | /* Read in junk, the card has probably been removed */ | ||
408 | card->priv->surpriseremoved = 1; | ||
409 | return IRQ_HANDLED; | ||
410 | } | ||
411 | |||
412 | /* TODO: I'm not sure what the best ordering is */ | ||
413 | |||
414 | cause = if_cs_read16(card, IF_CS_C_STATUS) & IF_CS_C_S_MASK; | ||
415 | |||
416 | if (cause & IF_CS_C_S_RX_UPLD_RDY) { | ||
417 | struct sk_buff *skb; | ||
418 | lbs_deb_cs("rx packet\n"); | ||
419 | skb = if_cs_receive_data(priv); | ||
420 | if (skb) | ||
421 | lbs_process_rxed_packet(priv, skb); | ||
422 | } | ||
423 | |||
424 | if (cause & IF_CS_H_IC_TX_OVER) { | ||
425 | lbs_deb_cs("tx over\n"); | ||
426 | lbs_host_to_card_done(priv); | ||
427 | } | ||
428 | |||
429 | if (cause & IF_CS_C_S_CMD_UPLD_RDY) { | ||
430 | unsigned long flags; | ||
431 | u8 i; | ||
432 | |||
433 | lbs_deb_cs("cmd upload ready\n"); | ||
434 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
435 | i = (priv->resp_idx == 0) ? 1 : 0; | ||
436 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
437 | |||
438 | BUG_ON(priv->resp_len[i]); | ||
439 | if_cs_receive_cmdres(priv, priv->resp_buf[i], | ||
440 | &priv->resp_len[i]); | ||
441 | |||
442 | spin_lock_irqsave(&priv->driver_lock, flags); | ||
443 | lbs_notify_command_response(priv, i); | ||
444 | spin_unlock_irqrestore(&priv->driver_lock, flags); | ||
445 | } | ||
446 | |||
447 | if (cause & IF_CS_H_IC_HOST_EVENT) { | ||
448 | u16 event = if_cs_read16(priv->card, IF_CS_C_STATUS) | ||
449 | & IF_CS_C_S_STATUS_MASK; | ||
450 | if_cs_write16(priv->card, IF_CS_H_INT_CAUSE, | ||
451 | IF_CS_H_IC_HOST_EVENT); | ||
452 | lbs_deb_cs("eventcause 0x%04x\n", event); | ||
453 | lbs_queue_event(priv, event >> 8 & 0xff); | ||
454 | } | ||
455 | |||
456 | return IRQ_HANDLED; | ||
457 | } | ||
458 | |||
459 | |||
460 | |||
461 | |||
462 | /********************************************************************/ | ||
428 | /* Firmware */ | 463 | /* Firmware */ |
429 | /********************************************************************/ | 464 | /********************************************************************/ |
430 | 465 | ||
@@ -476,8 +511,6 @@ static int if_cs_prog_helper(struct if_cs_card *card) | |||
476 | 511 | ||
477 | if (remain < count) | 512 | if (remain < count) |
478 | count = remain; | 513 | count = remain; |
479 | /* printk(KERN_INFO "//HS %d loading %d of %d bytes\n", | ||
480 | __LINE__, sent, fw->size); */ | ||
481 | 514 | ||
482 | /* "write the number of bytes to be sent to the I/O Command | 515 | /* "write the number of bytes to be sent to the I/O Command |
483 | * write length register" */ | 516 | * write length register" */ |
@@ -544,18 +577,12 @@ static int if_cs_prog_real(struct if_cs_card *card) | |||
544 | 577 | ||
545 | ret = if_cs_poll_while_fw_download(card, IF_CS_C_SQ_READ_LOW, IF_CS_C_SQ_HELPER_OK); | 578 | ret = if_cs_poll_while_fw_download(card, IF_CS_C_SQ_READ_LOW, IF_CS_C_SQ_HELPER_OK); |
546 | if (ret < 0) { | 579 | if (ret < 0) { |
547 | int i; | ||
548 | lbs_pr_err("helper firmware doesn't answer\n"); | 580 | lbs_pr_err("helper firmware doesn't answer\n"); |
549 | for (i = 0; i < 0x50; i += 2) | ||
550 | printk(KERN_INFO "## HS %02x: %04x\n", | ||
551 | i, if_cs_read16(card, i)); | ||
552 | goto err_release; | 581 | goto err_release; |
553 | } | 582 | } |
554 | 583 | ||
555 | for (sent = 0; sent < fw->size; sent += len) { | 584 | for (sent = 0; sent < fw->size; sent += len) { |
556 | len = if_cs_read16(card, IF_CS_C_SQ_READ_LOW); | 585 | len = if_cs_read16(card, IF_CS_C_SQ_READ_LOW); |
557 | /* printk(KERN_INFO "//HS %d loading %d of %d bytes\n", | ||
558 | __LINE__, sent, fw->size); */ | ||
559 | if (len & 1) { | 586 | if (len & 1) { |
560 | retry++; | 587 | retry++; |
561 | lbs_pr_info("odd, need to retry this firmware block\n"); | 588 | lbs_pr_info("odd, need to retry this firmware block\n"); |
@@ -642,64 +669,6 @@ static int if_cs_host_to_card(struct lbs_private *priv, | |||
642 | } | 669 | } |
643 | 670 | ||
644 | 671 | ||
645 | static int if_cs_get_int_status(struct lbs_private *priv, u8 *ireg) | ||
646 | { | ||
647 | struct if_cs_card *card = (struct if_cs_card *)priv->card; | ||
648 | int ret = 0; | ||
649 | u16 int_cause; | ||
650 | *ireg = 0; | ||
651 | |||
652 | lbs_deb_enter(LBS_DEB_CS); | ||
653 | |||
654 | if (priv->surpriseremoved) | ||
655 | goto out; | ||
656 | |||
657 | int_cause = if_cs_read16(card, IF_CS_C_INT_CAUSE) & IF_CS_C_IC_MASK; | ||
658 | if_cs_write16(card, IF_CS_C_INT_CAUSE, int_cause); | ||
659 | |||
660 | *ireg = if_cs_read16(card, IF_CS_C_STATUS) & IF_CS_C_S_MASK; | ||
661 | |||
662 | if (!*ireg) | ||
663 | goto sbi_get_int_status_exit; | ||
664 | |||
665 | sbi_get_int_status_exit: | ||
666 | |||
667 | /* is there a data packet for us? */ | ||
668 | if (*ireg & IF_CS_C_S_RX_UPLD_RDY) { | ||
669 | struct sk_buff *skb = if_cs_receive_data(priv); | ||
670 | lbs_process_rxed_packet(priv, skb); | ||
671 | *ireg &= ~IF_CS_C_S_RX_UPLD_RDY; | ||
672 | } | ||
673 | |||
674 | if (*ireg & IF_CS_C_S_TX_DNLD_RDY) { | ||
675 | priv->dnld_sent = DNLD_RES_RECEIVED; | ||
676 | } | ||
677 | |||
678 | /* Card has a command result for us */ | ||
679 | if (*ireg & IF_CS_C_S_CMD_UPLD_RDY) { | ||
680 | ret = if_cs_receive_cmdres(priv, priv->upld_buf, &priv->upld_len); | ||
681 | if (ret < 0) | ||
682 | lbs_pr_err("could not receive cmd from card\n"); | ||
683 | } | ||
684 | |||
685 | out: | ||
686 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d, ireg 0x%x, hisregcpy 0x%x", ret, *ireg, priv->hisregcpy); | ||
687 | return ret; | ||
688 | } | ||
689 | |||
690 | |||
691 | static int if_cs_read_event_cause(struct lbs_private *priv) | ||
692 | { | ||
693 | lbs_deb_enter(LBS_DEB_CS); | ||
694 | |||
695 | priv->eventcause = (if_cs_read16(priv->card, IF_CS_C_STATUS) & IF_CS_C_S_STATUS_MASK) >> 5; | ||
696 | if_cs_write16(priv->card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_HOST_EVENT); | ||
697 | |||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | |||
702 | |||
703 | /********************************************************************/ | 672 | /********************************************************************/ |
704 | /* Card Services */ | 673 | /* Card Services */ |
705 | /********************************************************************/ | 674 | /********************************************************************/ |
@@ -852,13 +821,10 @@ static int if_cs_probe(struct pcmcia_device *p_dev) | |||
852 | goto out2; | 821 | goto out2; |
853 | } | 822 | } |
854 | 823 | ||
855 | /* Store pointers to our call-back functions */ | 824 | /* Finish setting up fields in lbs_private */ |
856 | card->priv = priv; | 825 | card->priv = priv; |
857 | priv->card = card; | 826 | priv->card = card; |
858 | priv->hw_host_to_card = if_cs_host_to_card; | 827 | priv->hw_host_to_card = if_cs_host_to_card; |
859 | priv->hw_get_int_status = if_cs_get_int_status; | ||
860 | priv->hw_read_event_cause = if_cs_read_event_cause; | ||
861 | |||
862 | priv->fw_ready = 1; | 828 | priv->fw_ready = 1; |
863 | 829 | ||
864 | /* Now actually get the IRQ */ | 830 | /* Now actually get the IRQ */ |