[PATCH] Fix cs firmware loading

Holger Schurig hs4233 at mail.mn-solutions.de
Fri Oct 17 03:38:32 EDT 2008


Here again, with line-wrapping off:

[PATCH] libertas: handle hardware events outside ISR

Signed-off-by: Holger Schurig <hs4233 at mail.mn-solutions.de>

--- linux.orig/drivers/net/wireless/libertas/dev.h
+++ linux/drivers/net/wireless/libertas/dev.h
@@ -154,6 +154,7 @@
 	/** Hardware access */
 	int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
 	void (*reset_card) (struct lbs_private *priv);
+	void (*poll_card) (struct lbs_private *priv);
 
 	/* Wake On LAN */
 	uint32_t wol_criteria;
@@ -195,6 +196,8 @@
 
 	/* Events sent from hardware to driver */
 	struct kfifo *event_fifo;
+#define LBS_EVENT_MASK 0xff0000
+#define LBS_EVENT_WAKE 0xff0001
 
 	/* nickname */
 	u8 nodename[16];
--- linux.orig/drivers/net/wireless/libertas/if_cs.c
+++ linux/drivers/net/wireless/libertas/if_cs.c
@@ -55,10 +55,17 @@
 /* Data structures                                                  */
 /********************************************************************/
 
+/* #define WITH_TASKLET */
+
 struct if_cs_card {
 	struct pcmcia_device *p_dev;
 	struct lbs_private *priv;
 	void __iomem *iobase;
+	spinlock_t lock;
+	u16 int_cause;
+#ifdef WITH_TASKLET
+	struct tasklet_struct tasklet;
+#endif
 };
 
 
@@ -238,6 +245,7 @@
  */
 #define IF_CS_CARD_STATUS		0x00000020
 #define IF_CS_CARD_STATUS_MASK		0x7f00
+#define IF_CS_CARD_STATUS_EVENT	(IF_CS_CARD_STATUS_MASK | IF_CS_BIT_EVENT)
 
 /*
  * The card int cause register is used by the card/firmware to notify us
@@ -304,22 +312,8 @@
 {
 	struct if_cs_card *card = (struct if_cs_card *)priv->card;
 	int ret = -1;
-	int loops = 0;
 
 	lbs_deb_enter(LBS_DEB_CS);
-	if_cs_disable_ints(card);
-
-	/* Is hardware ready? */
-	while (1) {
-		u16 status = if_cs_read16(card, IF_CS_CARD_STATUS);
-		if (status & IF_CS_BIT_COMMAND)
-			break;
-		if (++loops > 100) {
-			lbs_pr_err("card not ready for commands\n");
-			goto done;
-		}
-		mdelay(1);
-	}
 
 	if_cs_write16(card, IF_CS_CMD_LEN, nb);
 
@@ -337,8 +331,6 @@
 	if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND);
 	ret = 0;
 
-done:
-	if_cs_enable_ints(card);
 	lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
 	return ret;
 }
@@ -349,13 +341,8 @@
 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_CARD_STATUS);
-	BUG_ON((status & IF_CS_BIT_TX) == 0);
 
 	if_cs_write16(card, IF_CS_WRITE_LEN, nb);
 
@@ -366,30 +353,20 @@
 
 	if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_TX);
 	if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_TX);
-	if_cs_enable_ints(card);
 
 	lbs_deb_leave(LBS_DEB_CS);
 }
 
 /*
- * Get the command result out of the card.
+ * Get the command response out of the card.
  */
-static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len)
+static int if_cs_receive_resp(struct lbs_private *priv, u8 *data, u32 *len)
 {
 	unsigned long flags;
 	int ret = -1;
-	u16 status;
 
 	lbs_deb_enter(LBS_DEB_CS);
 
-	/* is hardware ready? */
-	status = if_cs_read16(priv->card, IF_CS_CARD_STATUS);
-	if ((status & IF_CS_BIT_RESP) == 0) {
-		lbs_pr_err("no cmd response in card\n");
-		*len = 0;
-		goto out;
-	}
-
 	*len = if_cs_read16(priv->card, IF_CS_RESP_LEN);
 	if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) {
 		lbs_pr_err("card cmd buffer has invalid # of bytes (%d)\n", *len);
@@ -425,6 +402,7 @@
 	lbs_deb_enter(LBS_DEB_CS);
 
 	len = if_cs_read16(priv->card, IF_CS_READ_LEN);
+	//lbs_deb_cs("rx %d\n", len);
 	if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
 		lbs_pr_err("card data buffer has invalid # of bytes (%d)\n", len);
 		priv->stats.rx_dropped++;
@@ -455,74 +433,138 @@
 static irqreturn_t if_cs_interrupt(int irq, void *data)
 {
 	struct if_cs_card *card = data;
-	struct lbs_private *priv = card->priv;
-	u16 cause;
+	//struct lbs_private *priv = card->priv;
+	unsigned long flags;
+	u16 cause, status;
 
 	lbs_deb_enter(LBS_DEB_CS);
 
-	/* Ask card interrupt cause register if there is something for us */
+	if_cs_disable_ints(card);
+
 	cause = if_cs_read16(card, IF_CS_CARD_INT_CAUSE);
 	lbs_deb_cs("cause 0x%04x\n", cause);
 
 	if (cause == 0) {
 		/* Not for us */
+		if_cs_enable_ints(card);
 		return IRQ_NONE;
 	}
 
 	if (cause == 0xffff) {
 		/* Read in junk, the card has probably been removed */
 		card->priv->surpriseremoved = 1;
+		if_cs_enable_ints(card);
 		return IRQ_HANDLED;
 	}
 
+	/* read card status to clear event bit */
+	status = if_cs_read16(card, IF_CS_CARD_STATUS);
+	lbs_deb_cs("status 0x%04x\n", status);
+
+	cause &= IF_CS_BIT_MASK;
+
+	/* Store the interupt cause in our shadow */
+	spin_lock_irqsave(&card->lock, flags);
+	card->int_cause |= cause;
+	if (!(cause && IF_CS_BIT_RX) && status & IF_CS_BIT_RX) {
+		/* We sometimes miss an RX interrupt, but we can deduce this
+		 * fact from the status */
+		lbs_deb_cs("forcing IF_CS_BIT_RX\n");
+		card->int_cause |= IF_CS_BIT_RX;
+	}
+	if (status & IF_CS_BIT_EVENT) {
+		/* Store the event bit and the event code into our
+		 * int_cause shadow */
+		lbs_deb_cs("transferring event\n");
+		card->int_cause |= (status & IF_CS_CARD_STATUS_EVENT);
+	}
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	/* Clear interrupt cause */
+	if_cs_write16(card, IF_CS_CARD_INT_CAUSE, cause);
+
+#ifdef WITH_TASKLET
+	/* Simple way to keep the main thread from sleeping :-) */
+	tasklet_schedule(&card->tasklet);
+#else
+	lbs_queue_event(card->priv, LBS_EVENT_WAKE);
+#endif
+
+	if_cs_enable_ints(card);
+
+	return IRQ_HANDLED;
+}
+
+#ifdef WITH_TASKLET
+static void if_cs_poll_status(unsigned long data)
+{
+	struct if_cs_card *card = (struct if_cs_card *)data;
+	struct lbs_private *priv = card->priv;
+#else
+static void if_cs_poll_status(struct lbs_private *priv)
+{
+	struct if_cs_card *card = (struct if_cs_card *)priv->card;
+#endif
+	unsigned long flags;
+	u16 cause;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	if (priv->surpriseremoved)
+		return;
+
+	spin_lock_irqsave(&card->lock, flags);
+	cause = card->int_cause;
+	card->int_cause = 0;
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	lbs_deb_cs("int_cause 0x%04x\n", cause);
+
+	if (!cause)
+		return;
+
+	if (cause & IF_CS_BIT_TX) {
+		lbs_deb_cs("tx\n");
+		lbs_host_to_card_done(priv);
+	}
+
 	if (cause & IF_CS_BIT_RX) {
 		struct sk_buff *skb;
-		lbs_deb_cs("rx packet\n");
 		skb = if_cs_receive_data(priv);
 		if (skb)
 			lbs_process_rxed_packet(priv, skb);
 	}
 
-	if (cause & IF_CS_BIT_TX) {
-		lbs_deb_cs("tx done\n");
-		lbs_host_to_card_done(priv);
-	}
-
 	if (cause & IF_CS_BIT_RESP) {
-		unsigned long flags;
+		unsigned long driver_flags;
 		u8 i;
 
-		lbs_deb_cs("cmd resp\n");
-		spin_lock_irqsave(&priv->driver_lock, flags);
+		lbs_deb_cs("resp\n");
+		spin_lock_irqsave(&priv->driver_lock, driver_flags);
 		i = (priv->resp_idx == 0) ? 1 : 0;
-		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		spin_unlock_irqrestore(&priv->driver_lock, driver_flags);
 
 		BUG_ON(priv->resp_len[i]);
-		if_cs_receive_cmdres(priv, priv->resp_buf[i],
+		if_cs_receive_resp(priv, priv->resp_buf[i],
 			&priv->resp_len[i]);
 
-		spin_lock_irqsave(&priv->driver_lock, flags);
+		spin_lock_irqsave(&priv->driver_lock, driver_flags);
 		lbs_notify_command_response(priv, i);
-		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		spin_unlock_irqrestore(&priv->driver_lock, driver_flags);
 	}
 
 	if (cause & IF_CS_BIT_EVENT) {
-		u16 status = if_cs_read16(priv->card, IF_CS_CARD_STATUS);
 		if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE,
 			IF_CS_BIT_EVENT);
-		lbs_queue_event(priv, (status & IF_CS_CARD_STATUS_MASK) >> 8);
+		lbs_queue_event(priv, (cause & IF_CS_CARD_STATUS_MASK) >> 8);
 	}
 
-	/* Clear interrupt cause */
-	if_cs_write16(card, IF_CS_CARD_INT_CAUSE, cause & IF_CS_BIT_MASK);
-
+	if_cs_enable_ints(card);
 	lbs_deb_leave(LBS_DEB_CS);
-	return IRQ_HANDLED;
 }
 
 
 
-
 /********************************************************************/
 /* Firmware                                                         */
 /********************************************************************/
@@ -847,6 +889,11 @@
 		}
 	}
 
+	spin_lock_init(&card->lock);
+#ifdef WITH_TASKLET
+	tasklet_init(&card->tasklet, if_cs_poll_status, (unsigned long)card);
+#endif
+
 	/* Initialize io access */
 	card->iobase = ioport_map(p_dev->io.BasePort1, p_dev->io.NumPorts1);
 	if (!card->iobase) {
@@ -903,6 +950,9 @@
 	card->priv = priv;
 	priv->card = card;
 	priv->hw_host_to_card = if_cs_host_to_card;
+#ifndef WITH_TASKLET
+	priv->poll_card = if_cs_poll_status;
+#endif
 	priv->fw_ready = 1;
 
 	/* Now actually get the IRQ */
--- linux.orig/drivers/net/wireless/libertas/main.c
+++ linux/drivers/net/wireless/libertas/main.c
@@ -706,6 +706,10 @@
 
 		add_wait_queue(&priv->waitq, &wait);
 		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (priv->poll_card)
+			priv->poll_card(priv);
+
 		spin_lock_irq(&priv->driver_lock);
 
 		if (kthread_should_stop())
@@ -812,9 +816,12 @@
 
 			__kfifo_get(priv->event_fifo, (unsigned char *) &event,
 				sizeof(event));
-			spin_unlock_irq(&priv->driver_lock);
-			lbs_process_event(priv, event);
-			spin_lock_irq(&priv->driver_lock);
+			/* ignore LBS_EVENT_xxx events */
+			if (!(event & LBS_EVENT_MASK)) {
+				spin_unlock_irq(&priv->driver_lock);
+				lbs_process_event(priv, event);
+				spin_lock_irq(&priv->driver_lock);
+			}
 		}
 		spin_unlock_irq(&priv->driver_lock);
 



More information about the libertas-dev mailing list