aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorHolger Schurig <hs4233@mail.mn-solutions.de>2008-05-26 06:50:23 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-06-03 15:00:17 -0400
commit43d01c563d271260c1e4fe0a9383c47fae96887f (patch)
treedbc3c676e597e90438b3013a73eb56dfd4e84d17 /drivers/net
parent85319f933a703a92652a8f23339f1ec1694cd594 (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.c61
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
221static 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
227static 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
260done: 273done:
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 */
269static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb) 282static 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
334static struct sk_buff *if_cs_receive_data(struct lbs_private *priv) 351static 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
376static 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
382static 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
389static irqreturn_t if_cs_interrupt(int irq, void *data) 387static 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