Skip to content

Commit

Permalink
libertas: fix compact flash interrupt handling
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Holger Schurig authored and John W. Linville committed Jun 3, 2008
1 parent 85319f9 commit 43d01c5
Showing 1 changed file with 29 additions and 32 deletions.
61 changes: 29 additions & 32 deletions drivers/net/wireless/libertas/if_cs.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,21 @@ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 r


/********************************************************************/
/* I/O */
/* I/O and interrupt handling */
/********************************************************************/

static inline void if_cs_enable_ints(struct if_cs_card *card)
{
lbs_deb_enter(LBS_DEB_CS);
if_cs_write16(card, IF_CS_H_INT_MASK, 0);
}

static inline void if_cs_disable_ints(struct if_cs_card *card)
{
lbs_deb_enter(LBS_DEB_CS);
if_cs_write16(card, IF_CS_H_INT_MASK, IF_CS_H_IM_MASK);
}

/*
* Called from if_cs_host_to_card to send a command to the hardware
*/
Expand All @@ -228,6 +240,7 @@ static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb)
int loops = 0;

lbs_deb_enter(LBS_DEB_CS);
if_cs_disable_ints(card);

/* Is hardware ready? */
while (1) {
Expand Down Expand Up @@ -258,19 +271,24 @@ static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb)
ret = 0;

done:
if_cs_enable_ints(card);
lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
return ret;
}


/*
* Called from if_cs_host_to_card to send a data to the hardware
*/
static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb)
{
struct if_cs_card *card = (struct if_cs_card *)priv->card;
u16 status;

lbs_deb_enter(LBS_DEB_CS);
if_cs_disable_ints(card);

status = if_cs_read16(card, IF_CS_C_STATUS);
BUG_ON((status & IF_CS_C_S_TX_DNLD_RDY) == 0);

if_cs_write16(card, IF_CS_H_WRITE_LEN, nb);

Expand All @@ -281,11 +299,11 @@ static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb)

if_cs_write16(card, IF_CS_H_STATUS, IF_CS_H_STATUS_TX_OVER);
if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_STATUS_TX_OVER);
if_cs_enable_ints(card);

lbs_deb_leave(LBS_DEB_CS);
}


/*
* Get the command result out of the card.
*/
Expand Down Expand Up @@ -330,7 +348,6 @@ static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len)
return ret;
}


static struct sk_buff *if_cs_receive_data(struct lbs_private *priv)
{
struct sk_buff *skb = NULL;
Expand Down Expand Up @@ -367,25 +384,6 @@ static struct sk_buff *if_cs_receive_data(struct lbs_private *priv)
return skb;
}



/********************************************************************/
/* Interrupts */
/********************************************************************/

static inline void if_cs_enable_ints(struct if_cs_card *card)
{
lbs_deb_enter(LBS_DEB_CS);
if_cs_write16(card, IF_CS_H_INT_MASK, 0);
}

static inline void if_cs_disable_ints(struct if_cs_card *card)
{
lbs_deb_enter(LBS_DEB_CS);
if_cs_write16(card, IF_CS_H_INT_MASK, IF_CS_H_IM_MASK);
}


static irqreturn_t if_cs_interrupt(int irq, void *data)
{
struct if_cs_card *card = data;
Expand All @@ -394,10 +392,8 @@ static irqreturn_t if_cs_interrupt(int irq, void *data)

lbs_deb_enter(LBS_DEB_CS);

/* Ask card interrupt cause register if there is something for us */
cause = if_cs_read16(card, IF_CS_C_INT_CAUSE);
if_cs_write16(card, IF_CS_C_INT_CAUSE, cause & IF_CS_C_IC_MASK);

lbs_deb_cs("cause 0x%04x\n", cause);
if (cause == 0) {
/* Not for us */
return IRQ_NONE;
Expand All @@ -409,9 +405,9 @@ static irqreturn_t if_cs_interrupt(int irq, void *data)
return IRQ_HANDLED;
}

/* TODO: I'm not sure what the best ordering is */

cause = if_cs_read16(card, IF_CS_C_STATUS) & IF_CS_C_S_MASK;
/* Clear interrupt cause */
if_cs_write16(card, IF_CS_C_INT_CAUSE, cause & IF_CS_C_IC_MASK);
lbs_deb_cs("cause 0x%04x\n", cause);

if (cause & IF_CS_C_S_RX_UPLD_RDY) {
struct sk_buff *skb;
Expand All @@ -422,15 +418,15 @@ static irqreturn_t if_cs_interrupt(int irq, void *data)
}

if (cause & IF_CS_H_IC_TX_OVER) {
lbs_deb_cs("tx over\n");
lbs_deb_cs("tx done\n");
lbs_host_to_card_done(priv);
}

if (cause & IF_CS_C_S_CMD_UPLD_RDY) {
unsigned long flags;
u8 i;

lbs_deb_cs("cmd upload ready\n");
lbs_deb_cs("cmd resp\n");
spin_lock_irqsave(&priv->driver_lock, flags);
i = (priv->resp_idx == 0) ? 1 : 0;
spin_unlock_irqrestore(&priv->driver_lock, flags);
Expand All @@ -449,10 +445,11 @@ static irqreturn_t if_cs_interrupt(int irq, void *data)
& IF_CS_C_S_STATUS_MASK;
if_cs_write16(priv->card, IF_CS_H_INT_CAUSE,
IF_CS_H_IC_HOST_EVENT);
lbs_deb_cs("eventcause 0x%04x\n", event);
lbs_deb_cs("host event 0x%04x\n", event);
lbs_queue_event(priv, event >> 8 & 0xff);
}

lbs_deb_leave(LBS_DEB_CS);
return IRQ_HANDLED;
}

Expand Down

0 comments on commit 43d01c5

Please sign in to comment.