diff options
author | Holger Schurig <hs4233@mail.mn-solutions.de> | 2008-05-26 06:50:23 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-06-03 15:00:17 -0400 |
commit | 43d01c563d271260c1e4fe0a9383c47fae96887f (patch) | |
tree | dbc3c676e597e90438b3013a73eb56dfd4e84d17 /drivers/net | |
parent | 85319f933a703a92652a8f23339f1ec1694cd594 (diff) |
libertas: fix compact flash interrupt handling
The old code misbehaved because it polled card status and always called the
"tx over" code-path.
This also fixes a hard lockup by not allowing and card interrupts while
transferring a TX frame or a command into the card.
Signed-off-by: Holger Schurig <hs4233@mail.mn-solutions.de>
Acked-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/libertas/if_cs.c | 61 |
1 files changed, 29 insertions, 32 deletions
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index 86bb1268885a..5963d92cc94d 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c | |||
@@ -215,9 +215,21 @@ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 r | |||
215 | 215 | ||
216 | 216 | ||
217 | /********************************************************************/ | 217 | /********************************************************************/ |
218 | /* I/O */ | 218 | /* I/O and interrupt handling */ |
219 | /********************************************************************/ | 219 | /********************************************************************/ |
220 | 220 | ||
221 | static inline void if_cs_enable_ints(struct if_cs_card *card) | ||
222 | { | ||
223 | lbs_deb_enter(LBS_DEB_CS); | ||
224 | if_cs_write16(card, IF_CS_H_INT_MASK, 0); | ||
225 | } | ||
226 | |||
227 | static inline void if_cs_disable_ints(struct if_cs_card *card) | ||
228 | { | ||
229 | lbs_deb_enter(LBS_DEB_CS); | ||
230 | if_cs_write16(card, IF_CS_H_INT_MASK, IF_CS_H_IM_MASK); | ||
231 | } | ||
232 | |||
221 | /* | 233 | /* |
222 | * Called from if_cs_host_to_card to send a command to the hardware | 234 | * Called from if_cs_host_to_card to send a command to the hardware |
223 | */ | 235 | */ |
@@ -228,6 +240,7 @@ static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb) | |||
228 | int loops = 0; | 240 | int loops = 0; |
229 | 241 | ||
230 | lbs_deb_enter(LBS_DEB_CS); | 242 | lbs_deb_enter(LBS_DEB_CS); |
243 | if_cs_disable_ints(card); | ||
231 | 244 | ||
232 | /* Is hardware ready? */ | 245 | /* Is hardware ready? */ |
233 | while (1) { | 246 | while (1) { |
@@ -258,19 +271,24 @@ static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb) | |||
258 | ret = 0; | 271 | ret = 0; |
259 | 272 | ||
260 | done: | 273 | done: |
274 | if_cs_enable_ints(card); | ||
261 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); | 275 | lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); |
262 | return ret; | 276 | return ret; |
263 | } | 277 | } |
264 | 278 | ||
265 | |||
266 | /* | 279 | /* |
267 | * Called from if_cs_host_to_card to send a data to the hardware | 280 | * Called from if_cs_host_to_card to send a data to the hardware |
268 | */ | 281 | */ |
269 | static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) | 282 | static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) |
270 | { | 283 | { |
271 | struct if_cs_card *card = (struct if_cs_card *)priv->card; | 284 | struct if_cs_card *card = (struct if_cs_card *)priv->card; |
285 | u16 status; | ||
272 | 286 | ||
273 | lbs_deb_enter(LBS_DEB_CS); | 287 | lbs_deb_enter(LBS_DEB_CS); |
288 | if_cs_disable_ints(card); | ||
289 | |||
290 | status = if_cs_read16(card, IF_CS_C_STATUS); | ||
291 | BUG_ON((status & IF_CS_C_S_TX_DNLD_RDY) == 0); | ||
274 | 292 | ||
275 | if_cs_write16(card, IF_CS_H_WRITE_LEN, nb); | 293 | if_cs_write16(card, IF_CS_H_WRITE_LEN, nb); |
276 | 294 | ||
@@ -281,11 +299,11 @@ static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) | |||
281 | 299 | ||
282 | if_cs_write16(card, IF_CS_H_STATUS, IF_CS_H_STATUS_TX_OVER); | 300 | if_cs_write16(card, IF_CS_H_STATUS, IF_CS_H_STATUS_TX_OVER); |
283 | if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_STATUS_TX_OVER); | 301 | if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_STATUS_TX_OVER); |
302 | if_cs_enable_ints(card); | ||
284 | 303 | ||
285 | lbs_deb_leave(LBS_DEB_CS); | 304 | lbs_deb_leave(LBS_DEB_CS); |
286 | } | 305 | } |
287 | 306 | ||
288 | |||
289 | /* | 307 | /* |
290 | * Get the command result out of the card. | 308 | * Get the command result out of the card. |
291 | */ | 309 | */ |
@@ -330,7 +348,6 @@ out: | |||
330 | return ret; | 348 | return ret; |
331 | } | 349 | } |
332 | 350 | ||
333 | |||
334 | static struct sk_buff *if_cs_receive_data(struct lbs_private *priv) | 351 | static struct sk_buff *if_cs_receive_data(struct lbs_private *priv) |
335 | { | 352 | { |
336 | struct sk_buff *skb = NULL; | 353 | struct sk_buff *skb = NULL; |
@@ -367,25 +384,6 @@ out: | |||
367 | return skb; | 384 | return skb; |
368 | } | 385 | } |
369 | 386 | ||
370 | |||
371 | |||
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) | 387 | static irqreturn_t if_cs_interrupt(int irq, void *data) |
390 | { | 388 | { |
391 | struct if_cs_card *card = data; | 389 | struct if_cs_card *card = data; |
@@ -394,10 +392,8 @@ static irqreturn_t if_cs_interrupt(int irq, void *data) | |||
394 | 392 | ||
395 | lbs_deb_enter(LBS_DEB_CS); | 393 | lbs_deb_enter(LBS_DEB_CS); |
396 | 394 | ||
395 | /* Ask card interrupt cause register if there is something for us */ | ||
397 | cause = if_cs_read16(card, IF_CS_C_INT_CAUSE); | 396 | 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) { | 397 | if (cause == 0) { |
402 | /* Not for us */ | 398 | /* Not for us */ |
403 | return IRQ_NONE; | 399 | return IRQ_NONE; |
@@ -409,9 +405,9 @@ static irqreturn_t if_cs_interrupt(int irq, void *data) | |||
409 | return IRQ_HANDLED; | 405 | return IRQ_HANDLED; |
410 | } | 406 | } |
411 | 407 | ||
412 | /* TODO: I'm not sure what the best ordering is */ | 408 | /* Clear interrupt cause */ |
413 | 409 | if_cs_write16(card, IF_CS_C_INT_CAUSE, cause & IF_CS_C_IC_MASK); | |
414 | cause = if_cs_read16(card, IF_CS_C_STATUS) & IF_CS_C_S_MASK; | 410 | lbs_deb_cs("cause 0x%04x\n", cause); |
415 | 411 | ||
416 | if (cause & IF_CS_C_S_RX_UPLD_RDY) { | 412 | if (cause & IF_CS_C_S_RX_UPLD_RDY) { |
417 | struct sk_buff *skb; | 413 | struct sk_buff *skb; |
@@ -422,7 +418,7 @@ static irqreturn_t if_cs_interrupt(int irq, void *data) | |||
422 | } | 418 | } |
423 | 419 | ||
424 | if (cause & IF_CS_H_IC_TX_OVER) { | 420 | if (cause & IF_CS_H_IC_TX_OVER) { |
425 | lbs_deb_cs("tx over\n"); | 421 | lbs_deb_cs("tx done\n"); |
426 | lbs_host_to_card_done(priv); | 422 | lbs_host_to_card_done(priv); |
427 | } | 423 | } |
428 | 424 | ||
@@ -430,7 +426,7 @@ static irqreturn_t if_cs_interrupt(int irq, void *data) | |||
430 | unsigned long flags; | 426 | unsigned long flags; |
431 | u8 i; | 427 | u8 i; |
432 | 428 | ||
433 | lbs_deb_cs("cmd upload ready\n"); | 429 | lbs_deb_cs("cmd resp\n"); |
434 | spin_lock_irqsave(&priv->driver_lock, flags); | 430 | spin_lock_irqsave(&priv->driver_lock, flags); |
435 | i = (priv->resp_idx == 0) ? 1 : 0; | 431 | i = (priv->resp_idx == 0) ? 1 : 0; |
436 | spin_unlock_irqrestore(&priv->driver_lock, flags); | 432 | spin_unlock_irqrestore(&priv->driver_lock, flags); |
@@ -449,10 +445,11 @@ static irqreturn_t if_cs_interrupt(int irq, void *data) | |||
449 | & IF_CS_C_S_STATUS_MASK; | 445 | & IF_CS_C_S_STATUS_MASK; |
450 | if_cs_write16(priv->card, IF_CS_H_INT_CAUSE, | 446 | if_cs_write16(priv->card, IF_CS_H_INT_CAUSE, |
451 | IF_CS_H_IC_HOST_EVENT); | 447 | IF_CS_H_IC_HOST_EVENT); |
452 | lbs_deb_cs("eventcause 0x%04x\n", event); | 448 | lbs_deb_cs("host event 0x%04x\n", event); |
453 | lbs_queue_event(priv, event >> 8 & 0xff); | 449 | lbs_queue_event(priv, event >> 8 & 0xff); |
454 | } | 450 | } |
455 | 451 | ||
452 | lbs_deb_leave(LBS_DEB_CS); | ||
456 | return IRQ_HANDLED; | 453 | return IRQ_HANDLED; |
457 | } | 454 | } |
458 | 455 | ||