diff options
Diffstat (limited to 'drivers/net/wireless/libertas/if_spi.c')
| -rw-r--r-- | drivers/net/wireless/libertas/if_spi.c | 641 | 
1 files changed, 427 insertions, 214 deletions
diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index 79bcb4e5d2c..f11728a866f 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -17,13 +17,15 @@   * (at your option) any later version.   */ -#include <linux/moduleparam.h> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/hardirq.h> +#include <linux/interrupt.h> +#include <linux/module.h>  #include <linux/firmware.h>  #include <linux/jiffies.h> -#include <linux/kthread.h>  #include <linux/list.h>  #include <linux/netdevice.h> -#include <linux/semaphore.h>  #include <linux/slab.h>  #include <linux/spi/libertas_spi.h>  #include <linux/spi/spi.h> @@ -34,6 +36,12 @@  #include "dev.h"  #include "if_spi.h" +struct if_spi_packet { +	struct list_head		list; +	u16				blen; +	u8				buffer[0] __attribute__((aligned(4))); +}; +  struct if_spi_card {  	struct spi_device		*spi;  	struct lbs_private		*priv; @@ -51,19 +59,40 @@ struct if_spi_card {  	unsigned long			spu_reg_delay;  	/* Handles all SPI communication (except for FW load) */ -	struct task_struct		*spi_thread; -	int				run_thread; - -	/* Used to wake up the spi_thread */ -	struct semaphore		spi_ready; -	struct semaphore		spi_thread_terminated; +	struct workqueue_struct		*workqueue; +	struct work_struct		packet_work; +	struct work_struct		resume_work;  	u8				cmd_buffer[IF_SPI_CMD_BUF_SIZE]; + +	/* A buffer of incoming packets from libertas core. +	 * Since we can't sleep in hw_host_to_card, we have to buffer +	 * them. */ +	struct list_head		cmd_packet_list; +	struct list_head		data_packet_list; + +	/* Protects cmd_packet_list and data_packet_list */ +	spinlock_t			buffer_lock; + +	/* True is card suspended */ +	u8				suspended;  };  static void free_if_spi_card(struct if_spi_card *card)  { -	spi_set_drvdata(card->spi, NULL); +	struct list_head *cursor, *next; +	struct if_spi_packet *packet; + +	list_for_each_safe(cursor, next, &card->cmd_packet_list) { +		packet = container_of(cursor, struct if_spi_packet, list); +		list_del(&packet->list); +		kfree(packet); +	} +	list_for_each_safe(cursor, next, &card->data_packet_list) { +		packet = container_of(cursor, struct if_spi_packet, list); +		list_del(&packet->list); +		kfree(packet); +	}  	kfree(card);  } @@ -117,8 +146,10 @@ static void spu_transaction_finish(struct if_spi_card *card)  	card->prev_xfer_time = jiffies;  } -/* Write out a byte buffer to an SPI register, - * using a series of 16-bit transfers. */ +/* + * Write out a byte buffer to an SPI register, + * using a series of 16-bit transfers. + */  static int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len)  {  	int err = 0; @@ -182,8 +213,10 @@ static int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len)  	struct spi_transfer dummy_trans;  	struct spi_transfer data_trans; -	/* You must take an even number of bytes from the SPU, even if you -	 * don't care about the last one.  */ +	/* +	 * You must take an even number of bytes from the SPU, even if you +	 * don't care about the last one. +	 */  	BUG_ON(len & 0x1);  	spu_transaction_init(card); @@ -232,8 +265,10 @@ static inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val)  	return ret;  } -/* Read 32 bits from an SPI register. - * The low 16 bits are read first. */ +/* + * Read 32 bits from an SPI register. + * The low 16 bits are read first. + */  static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val)  {  	__le32 buf; @@ -245,13 +280,15 @@ static int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val)  	return err;  } -/* Keep reading 16 bits from an SPI register until you get the correct result. +/* + * Keep reading 16 bits from an SPI register until you get the correct result.   *   * If mask = 0, the correct result is any non-zero number.   * If mask != 0, the correct result is any number where   * number & target_mask == target   * - * Returns -ETIMEDOUT if a second passes without the correct result. */ + * Returns -ETIMEDOUT if a second passes without the correct result. + */  static int spu_wait_for_u16(struct if_spi_card *card, u16 reg,  			u16 target_mask, u16 target)  { @@ -271,16 +308,17 @@ static int spu_wait_for_u16(struct if_spi_card *card, u16 reg,  		}  		udelay(100);  		if (time_after(jiffies, timeout)) { -			lbs_pr_err("%s: timeout with val=%02x, " -			       "target_mask=%02x, target=%02x\n", +			pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n",  			       __func__, val, target_mask, target);  			return -ETIMEDOUT;  		}  	}  } -/* Read 16 bits from an SPI register until you receive a specific value. - * Returns -ETIMEDOUT if a 4 tries pass without success. */ +/* + * Read 16 bits from an SPI register until you receive a specific value. + * Returns -ETIMEDOUT if a 4 tries pass without success. + */  static int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target)  {  	int err, try; @@ -302,8 +340,10 @@ static int spu_set_interrupt_mode(struct if_spi_card *card,  {  	int err = 0; -	/* We can suppress a host interrupt by clearing the appropriate -	 * bit in the "host interrupt status mask" register */ +	/* +	 * We can suppress a host interrupt by clearing the appropriate +	 * bit in the "host interrupt status mask" register +	 */  	if (suppress_host_int) {  		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);  		if (err) @@ -319,10 +359,12 @@ static int spu_set_interrupt_mode(struct if_spi_card *card,  			return err;  	} -	/* If auto-interrupts are on, the completion of certain transactions +	/* +	 * If auto-interrupts are on, the completion of certain transactions  	 * will trigger an interrupt automatically. If auto-interrupts  	 * are off, we need to set the "Card Interrupt Cause" register to -	 * trigger a card interrupt. */ +	 * trigger a card interrupt. +	 */  	if (auto_int) {  		err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG,  				IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO | @@ -365,7 +407,7 @@ static int spu_set_bus_mode(struct if_spi_card *card, u16 mode)  	if (err)  		return err;  	if ((rval & 0xF) != mode) { -		lbs_pr_err("Can't read bus mode register.\n"); +		pr_err("Can't read bus mode register\n");  		return -EIO;  	}  	return 0; @@ -376,8 +418,10 @@ static int spu_init(struct if_spi_card *card, int use_dummy_writes)  	int err = 0;  	u32 delay; -	/* We have to start up in timed delay mode so that we can safely -	 * read the Delay Read Register. */ +	/* +	 * We have to start up in timed delay mode so that we can safely +	 * read the Delay Read Register. +	 */  	card->use_dummy_writes = 0;  	err = spu_set_bus_mode(card,  				IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING | @@ -433,8 +477,10 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card,  	/* Load helper firmware image */  	while (bytes_remaining > 0) { -		/* Scratch pad 1 should contain the number of bytes we -		 * want to download to the firmware */ +		/* +		 * Scratch pad 1 should contain the number of bytes we +		 * want to download to the firmware +		 */  		err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,  					HELPER_FW_LOAD_CHUNK_SZ);  		if (err) @@ -446,8 +492,10 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card,  		if (err)  			goto out; -		/* Feed the data into the command read/write port reg -		 * in chunks of 64 bytes */ +		/* +		 * Feed the data into the command read/write port reg +		 * in chunks of 64 bytes +		 */  		memset(temp, 0, sizeof(temp));  		memcpy(temp, fw,  		       min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ)); @@ -469,9 +517,11 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card,  		fw += HELPER_FW_LOAD_CHUNK_SZ;  	} -	/* Once the helper / single stage firmware download is complete, +	/* +	 * Once the helper / single stage firmware download is complete,  	 * write 0 to scratch pad 1 and interrupt the -	 * bootloader. This completes the helper download. */ +	 * bootloader. This completes the helper download. +	 */  	err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK);  	if (err)  		goto out; @@ -480,32 +530,32 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card,  		goto out;  	err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,  				IF_SPI_CIC_CMD_DOWNLOAD_OVER); -		goto out; - -	lbs_deb_spi("waiting for helper to boot...\n"); -  out:  	if (err) -		lbs_pr_err("failed to load helper firmware (err=%d)\n", err); +		pr_err("failed to load helper firmware (err=%d)\n", err);  	lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err);  	return err;  } -/* Returns the length of the next packet the firmware expects us to send - * Sets crc_err if the previous transfer had a CRC error. */ +/* + * Returns the length of the next packet the firmware expects us to send. + * Sets crc_err if the previous transfer had a CRC error. + */  static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,  						int *crc_err)  {  	u16 len;  	int err = 0; -	/* wait until the host interrupt status register indicates -	 * that we are ready to download */ +	/* +	 * wait until the host interrupt status register indicates +	 * that we are ready to download +	 */  	err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,  				IF_SPI_HIST_CMD_DOWNLOAD_RDY,  				IF_SPI_HIST_CMD_DOWNLOAD_RDY);  	if (err) { -		lbs_pr_err("timed out waiting for host_int_status\n"); +		pr_err("timed out waiting for host_int_status\n");  		return err;  	} @@ -515,9 +565,8 @@ static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,  		return err;  	if (len > IF_SPI_CMD_BUF_SIZE) { -		lbs_pr_err("firmware load device requested a larger " -			   "tranfer than we are prepared to " -			   "handle. (len = %d)\n", len); +		pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n", +		       len);  		return -EIO;  	}  	if (len & 0x1) { @@ -533,6 +582,7 @@ static int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,  static int if_spi_prog_main_firmware(struct if_spi_card *card,  					const struct firmware *firmware)  { +	struct lbs_private *priv = card->priv;  	int len, prev_len;  	int bytes, crc_err = 0, err = 0;  	const u8 *fw; @@ -546,8 +596,9 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card,  	err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);  	if (err) { -		lbs_pr_err("%s: timed out waiting for initial " -			   "scratch reg = 0\n", __func__); +		netdev_err(priv->dev, +			   "%s: timed out waiting for initial scratch reg = 0\n", +			   __func__);  		goto out;  	} @@ -561,17 +612,18 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card,  			goto out;  		}  		if (bytes < 0) { -			/* If there are no more bytes left, we would normally -			 * expect to have terminated with len = 0 */ -			lbs_pr_err("Firmware load wants more bytes " -				   "than we have to offer.\n"); +			/* +			 * If there are no more bytes left, we would normally +			 * expect to have terminated with len = 0 +			 */ +			netdev_err(priv->dev, +				   "Firmware load wants more bytes than we have to offer.\n");  			break;  		}  		if (crc_err) {  			/* Previous transfer failed. */  			if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) { -				lbs_pr_err("Too many CRC errors encountered " -					   "in firmware load.\n"); +				pr_err("Too many CRC errors encountered in firmware load.\n");  				err = -EIO;  				goto out;  			} @@ -600,21 +652,20 @@ static int if_spi_prog_main_firmware(struct if_spi_card *card,  		prev_len = len;  	}  	if (bytes > prev_len) { -		lbs_pr_err("firmware load wants fewer bytes than " -			   "we have to offer.\n"); +		pr_err("firmware load wants fewer bytes than we have to offer\n");  	}  	/* Confirm firmware download */  	err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG,  					SUCCESSFUL_FW_DOWNLOAD_MAGIC);  	if (err) { -		lbs_pr_err("failed to confirm the firmware download\n"); +		pr_err("failed to confirm the firmware download\n");  		goto out;  	}  out:  	if (err) -		lbs_pr_err("failed to load firmware (err=%d)\n", err); +		pr_err("failed to load firmware (err=%d)\n", err);  	lbs_deb_leave_args(LBS_DEB_SPI, "err %d", err);  	return err;  } @@ -622,7 +673,7 @@ out:  /*   * SPI Transfer Thread   * - * The SPI thread handles all SPI transfers, so there is no need for a lock. + * The SPI worker handles all SPI transfers, so there is no need for a lock.   */  /* Move a command from the card to the host */ @@ -634,14 +685,18 @@ static int if_spi_c2h_cmd(struct if_spi_card *card)  	u16 len;  	u8 i; -	/* We need a buffer big enough to handle whatever people send to -	 * hw_host_to_card */ +	/* +	 * We need a buffer big enough to handle whatever people send to +	 * hw_host_to_card +	 */  	BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE);  	BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE); -	/* It's just annoying if the buffer size isn't a multiple of 4, because -	 * then we might have len <  IF_SPI_CMD_BUF_SIZE but -	 * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE */ +	/* +	 * It's just annoying if the buffer size isn't a multiple of 4, because +	 * then we might have len < IF_SPI_CMD_BUF_SIZE but +	 * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE +	 */  	BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0);  	lbs_deb_enter(LBS_DEB_SPI); @@ -651,13 +706,13 @@ static int if_spi_c2h_cmd(struct if_spi_card *card)  	if (err)  		goto out;  	if (!len) { -		lbs_pr_err("%s: error: card has no data for host\n", +		netdev_err(priv->dev, "%s: error: card has no data for host\n",  			   __func__);  		err = -EINVAL;  		goto out;  	} else if (len > IF_SPI_CMD_BUF_SIZE) { -		lbs_pr_err("%s: error: response packet too large: " -			   "%d bytes, but maximum is %d\n", +		netdev_err(priv->dev, +			   "%s: error: response packet too large: %d bytes, but maximum is %d\n",  			   __func__, len, IF_SPI_CMD_BUF_SIZE);  		err = -EINVAL;  		goto out; @@ -679,7 +734,7 @@ static int if_spi_c2h_cmd(struct if_spi_card *card)  out:  	if (err) -		lbs_pr_err("%s: err=%d\n", __func__, err); +		netdev_err(priv->dev, "%s: err=%d\n", __func__, err);  	lbs_deb_leave(LBS_DEB_SPI);  	return err;  } @@ -687,6 +742,7 @@ out:  /* Move data from the card to the host */  static int if_spi_c2h_data(struct if_spi_card *card)  { +	struct lbs_private *priv = card->priv;  	struct sk_buff *skb;  	char *data;  	u16 len; @@ -699,13 +755,13 @@ static int if_spi_c2h_data(struct if_spi_card *card)  	if (err)  		goto out;  	if (!len) { -		lbs_pr_err("%s: error: card has no data for host\n", +		netdev_err(priv->dev, "%s: error: card has no data for host\n",  			   __func__);  		err = -EINVAL;  		goto out;  	} else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { -		lbs_pr_err("%s: error: card has %d bytes of data, but " -			   "our maximum skb size is %zu\n", +		netdev_err(priv->dev, +			   "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n",  			   __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);  		err = -EINVAL;  		goto out; @@ -737,11 +793,47 @@ free_skb:  	dev_kfree_skb(skb);  out:  	if (err) -		lbs_pr_err("%s: err=%d\n", __func__, err); +		netdev_err(priv->dev, "%s: err=%d\n", __func__, err);  	lbs_deb_leave(LBS_DEB_SPI);  	return err;  } +/* Move data or a command from the host to the card. */ +static void if_spi_h2c(struct if_spi_card *card, +			struct if_spi_packet *packet, int type) +{ +	struct lbs_private *priv = card->priv; +	int err = 0; +	u16 int_type, port_reg; + +	switch (type) { +	case MVMS_DAT: +		int_type = IF_SPI_CIC_TX_DOWNLOAD_OVER; +		port_reg = IF_SPI_DATA_RDWRPORT_REG; +		break; +	case MVMS_CMD: +		int_type = IF_SPI_CIC_CMD_DOWNLOAD_OVER; +		port_reg = IF_SPI_CMD_RDWRPORT_REG; +		break; +	default: +		netdev_err(priv->dev, "can't transfer buffer of type %d\n", +			   type); +		err = -EINVAL; +		goto out; +	} + +	/* Write the data to the card */ +	err = spu_write(card, port_reg, packet->buffer, packet->blen); +	if (err) +		goto out; + +out: +	kfree(packet); + +	if (err) +		netdev_err(priv->dev, "%s: error %d\n", __func__, err); +} +  /* Inform the host about a card event */  static void if_spi_e2h(struct if_spi_card *card)  { @@ -763,104 +855,155 @@ static void if_spi_e2h(struct if_spi_card *card)  	lbs_queue_event(priv, cause & 0xff);  out:  	if (err) -		lbs_pr_err("%s: error %d\n", __func__, err); +		netdev_err(priv->dev, "%s: error %d\n", __func__, err);  } -static int lbs_spi_thread(void *data) +static void if_spi_host_to_card_worker(struct work_struct *work)  {  	int err; -	struct if_spi_card *card = data; +	struct if_spi_card *card;  	u16 hiStatus; +	unsigned long flags; +	struct if_spi_packet *packet; +	struct lbs_private *priv; -	while (1) { -		/* Wait to be woken up by one of two things.  First, our ISR -		 * could tell us that something happened on the WLAN. -		 * Secondly, libertas could call hw_host_to_card with more -		 * data, which we might be able to send. -		 */ -		do { -			err = down_interruptible(&card->spi_ready); -			if (!card->run_thread) { -				up(&card->spi_thread_terminated); -				do_exit(0); -			} -		} while (err == EINTR); +	card = container_of(work, struct if_spi_card, packet_work); +	priv = card->priv; -		/* Read the host interrupt status register to see what we -		 * can do. */ -		err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, -					&hiStatus); -		if (err) { -			lbs_pr_err("I/O error\n"); +	lbs_deb_enter(LBS_DEB_SPI); + +	/* +	 * Read the host interrupt status register to see what we +	 * can do. +	 */ +	err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG, +				&hiStatus); +	if (err) { +		netdev_err(priv->dev, "I/O error\n"); +		goto err; +	} + +	if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) { +		err = if_spi_c2h_cmd(card); +		if (err)  			goto err; -		} +	} +	if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) { +		err = if_spi_c2h_data(card); +		if (err) +			goto err; +	} -		if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) { -			err = if_spi_c2h_cmd(card); -			if (err) -				goto err; -		} -		if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) { -			err = if_spi_c2h_data(card); -			if (err) -				goto err; +	/* +	 * workaround: in PS mode, the card does not set the Command +	 * Download Ready bit, but it sets TX Download Ready. +	 */ +	if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY || +	   (card->priv->psstate != PS_STATE_FULL_POWER && +	    (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { +		/* +		 * This means two things. First of all, +		 * if there was a previous command sent, the card has +		 * successfully received it. +		 * Secondly, it is now ready to download another +		 * command. +		 */ +		lbs_host_to_card_done(card->priv); + +		/* Do we have any command packets from the host to send? */ +		packet = NULL; +		spin_lock_irqsave(&card->buffer_lock, flags); +		if (!list_empty(&card->cmd_packet_list)) { +			packet = (struct if_spi_packet *)(card-> +					cmd_packet_list.next); +			list_del(&packet->list);  		} +		spin_unlock_irqrestore(&card->buffer_lock, flags); -		/* workaround: in PS mode, the card does not set the Command -		 * Download Ready bit, but it sets TX Download Ready. */ -		if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY || -		   (card->priv->psstate != PS_STATE_FULL_POWER && -		    (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) { -			lbs_host_to_card_done(card->priv); +		if (packet) +			if_spi_h2c(card, packet, MVMS_CMD); +	} +	if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) { +		/* Do we have any data packets from the host to send? */ +		packet = NULL; +		spin_lock_irqsave(&card->buffer_lock, flags); +		if (!list_empty(&card->data_packet_list)) { +			packet = (struct if_spi_packet *)(card-> +					data_packet_list.next); +			list_del(&packet->list);  		} +		spin_unlock_irqrestore(&card->buffer_lock, flags); -		if (hiStatus & IF_SPI_HIST_CARD_EVENT) -			if_spi_e2h(card); +		if (packet) +			if_spi_h2c(card, packet, MVMS_DAT); +	} +	if (hiStatus & IF_SPI_HIST_CARD_EVENT) +		if_spi_e2h(card);  err: -		if (err) -			lbs_pr_err("%s: got error %d\n", __func__, err); -	} -} +	if (err) +		netdev_err(priv->dev, "%s: got error %d\n", __func__, err); -/* Block until lbs_spi_thread thread has terminated */ -static void if_spi_terminate_spi_thread(struct if_spi_card *card) -{ -	/* It would be nice to use kthread_stop here, but that function -	 * can't wake threads waiting for a semaphore. */ -	card->run_thread = 0; -	up(&card->spi_ready); -	down(&card->spi_thread_terminated); +	lbs_deb_leave(LBS_DEB_SPI);  }  /*   * Host to Card   *   * Called from Libertas to transfer some data to the WLAN device - * We can't sleep here. */ + * We can't sleep here. + */  static int if_spi_host_to_card(struct lbs_private *priv,  				u8 type, u8 *buf, u16 nb)  {  	int err = 0; +	unsigned long flags;  	struct if_spi_card *card = priv->card; +	struct if_spi_packet *packet; +	u16 blen;  	lbs_deb_enter_args(LBS_DEB_SPI, "type %d, bytes %d", type, nb); -	nb = ALIGN(nb, 4); +	if (nb == 0) { +		netdev_err(priv->dev, "%s: invalid size requested: %d\n", +			   __func__, nb); +		err = -EINVAL; +		goto out; +	} +	blen = ALIGN(nb, 4); +	packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC); +	if (!packet) { +		err = -ENOMEM; +		goto out; +	} +	packet->blen = blen; +	memcpy(packet->buffer, buf, nb); +	memset(packet->buffer + nb, 0, blen - nb);  	switch (type) {  	case MVMS_CMD: -		err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG, buf, nb); +		priv->dnld_sent = DNLD_CMD_SENT; +		spin_lock_irqsave(&card->buffer_lock, flags); +		list_add_tail(&packet->list, &card->cmd_packet_list); +		spin_unlock_irqrestore(&card->buffer_lock, flags);  		break;  	case MVMS_DAT: -		err = spu_write(card, IF_SPI_DATA_RDWRPORT_REG, buf, nb); +		priv->dnld_sent = DNLD_DATA_SENT; +		spin_lock_irqsave(&card->buffer_lock, flags); +		list_add_tail(&packet->list, &card->data_packet_list); +		spin_unlock_irqrestore(&card->buffer_lock, flags);  		break;  	default: -		lbs_pr_err("can't transfer buffer of type %d", type); +		kfree(packet); +		netdev_err(priv->dev, "can't transfer buffer of type %d\n", +			   type);  		err = -EINVAL;  		break;  	} +	/* Queue spi xfer work */ +	queue_work(card->workqueue, &card->packet_work); +out:  	lbs_deb_leave_args(LBS_DEB_SPI, "err=%d", err);  	return err;  } @@ -869,13 +1012,14 @@ static int if_spi_host_to_card(struct lbs_private *priv,   * Host Interrupts   *   * Service incoming interrupts from the WLAN device. We can't sleep here, so - * don't try to talk on the SPI bus, just wake up the SPI thread. + * don't try to talk on the SPI bus, just queue the SPI xfer work.   */  static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)  {  	struct if_spi_card *card = dev_id; -	up(&card->spi_ready); +	queue_work(card->workqueue, &card->packet_work); +  	return IRQ_HANDLED;  } @@ -883,56 +1027,26 @@ static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)   * SPI callbacks   */ -static int __devinit if_spi_probe(struct spi_device *spi) +static int if_spi_init_card(struct if_spi_card *card)  { -	struct if_spi_card *card; -	struct lbs_private *priv = NULL; -	struct libertas_spi_platform_data *pdata = spi->dev.platform_data; -	int err = 0, i; +	struct lbs_private *priv = card->priv; +	int err, i;  	u32 scratch; -	struct sched_param param = { .sched_priority = 1 };  	const struct firmware *helper = NULL;  	const struct firmware *mainfw = NULL;  	lbs_deb_enter(LBS_DEB_SPI); -	if (!pdata) { -		err = -EINVAL; -		goto out; -	} - -	if (pdata->setup) { -		err = pdata->setup(spi); -		if (err) -			goto out; -	} - -	/* Allocate card structure to represent this specific device */ -	card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL); -	if (!card) { -		err = -ENOMEM; -		goto out; -	} -	spi_set_drvdata(spi, card); -	card->pdata = pdata; -	card->spi = spi; -	card->prev_xfer_time = jiffies; - -	sema_init(&card->spi_ready, 0); -	sema_init(&card->spi_thread_terminated, 0); - -	/* Initialize the SPI Interface Unit */ -	err = spu_init(card, pdata->use_dummy_writes); +	err = spu_init(card, card->pdata->use_dummy_writes);  	if (err) -		goto free_card; +		goto out;  	err = spu_get_chip_revision(card, &card->card_id, &card->card_rev);  	if (err) -		goto free_card; +		goto out; -	/* Firmware load */  	err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch);  	if (err) -		goto free_card; +		goto out;  	if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC)  		lbs_deb_spi("Firmware is already loaded for "  			    "Marvell WLAN 802.11 adapter\n"); @@ -943,18 +1057,18 @@ static int __devinit if_spi_probe(struct spi_device *spi)  				break;  		}  		if (i == ARRAY_SIZE(fw_table)) { -			lbs_pr_err("Unsupported chip_id: 0x%02x\n", -					card->card_id); +			netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n", +				   card->card_id);  			err = -ENODEV; -			goto free_card; +			goto out;  		} -		err = lbs_get_firmware(&card->spi->dev, NULL, NULL, -					card->card_id, &fw_table[0], &helper, -					&mainfw); +		err = lbs_get_firmware(&card->spi->dev, card->card_id, +					&fw_table[0], &helper, &mainfw);  		if (err) { -			lbs_pr_err("failed to find firmware (%d)\n", err); -			goto free_card; +			netdev_err(priv->dev, "failed to find firmware (%d)\n", +				   err); +			goto out;  		}  		lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter " @@ -962,29 +1076,102 @@ static int __devinit if_spi_probe(struct spi_device *spi)  				"attached to SPI bus_num %d, chip_select %d. "  				"spi->max_speed_hz=%d\n",  				card->card_id, card->card_rev, -				spi->master->bus_num, spi->chip_select, -				spi->max_speed_hz); +				card->spi->master->bus_num, +				card->spi->chip_select, +				card->spi->max_speed_hz);  		err = if_spi_prog_helper_firmware(card, helper);  		if (err) -			goto free_card; +			goto out;  		err = if_spi_prog_main_firmware(card, mainfw);  		if (err) -			goto free_card; +			goto out;  		lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n");  	}  	err = spu_set_interrupt_mode(card, 0, 1);  	if (err) +		goto out; + +out: +	lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); +	return err; +} + +static void if_spi_resume_worker(struct work_struct *work) +{ +	struct if_spi_card *card; + +	card = container_of(work, struct if_spi_card, resume_work); + +	if (card->suspended) { +		if (card->pdata->setup) +			card->pdata->setup(card->spi); + +		/* Init card ... */ +		if_spi_init_card(card); + +		enable_irq(card->spi->irq); + +		/* And resume it ... */ +		lbs_resume(card->priv); + +		card->suspended = 0; +	} +} + +static int if_spi_probe(struct spi_device *spi) +{ +	struct if_spi_card *card; +	struct lbs_private *priv = NULL; +	struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev); +	int err = 0; + +	lbs_deb_enter(LBS_DEB_SPI); + +	if (!pdata) { +		err = -EINVAL; +		goto out; +	} + +	if (pdata->setup) { +		err = pdata->setup(spi); +		if (err) +			goto out; +	} + +	/* Allocate card structure to represent this specific device */ +	card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL); +	if (!card) { +		err = -ENOMEM; +		goto teardown; +	} +	spi_set_drvdata(spi, card); +	card->pdata = pdata; +	card->spi = spi; +	card->prev_xfer_time = jiffies; + +	INIT_LIST_HEAD(&card->cmd_packet_list); +	INIT_LIST_HEAD(&card->data_packet_list); +	spin_lock_init(&card->buffer_lock); + +	/* Initialize the SPI Interface Unit */ + +	/* Firmware load */ +	err = if_spi_init_card(card); +	if (err)  		goto free_card; -	/* Register our card with libertas. -	 * This will call alloc_etherdev */ +	/* +	 * Register our card with libertas. +	 * This will call alloc_etherdev. +	 */  	priv = lbs_add_card(card, &spi->dev);  	if (!priv) {  		err = -ENOMEM;  		goto free_card;  	}  	card->priv = priv; +	priv->setup_fw_on_resume = 1;  	priv->card = card;  	priv->hw_host_to_card = if_spi_host_to_card;  	priv->enter_deep_sleep = NULL; @@ -993,30 +1180,22 @@ static int __devinit if_spi_probe(struct spi_device *spi)  	priv->fw_ready = 1;  	/* Initialize interrupt handling stuff. */ -	card->run_thread = 1; -	card->spi_thread = kthread_run(lbs_spi_thread, card, "lbs_spi_thread"); -	if (IS_ERR(card->spi_thread)) { -		card->run_thread = 0; -		err = PTR_ERR(card->spi_thread); -		lbs_pr_err("error creating SPI thread: err=%d\n", err); -		goto remove_card; -	} -	if (sched_setscheduler(card->spi_thread, SCHED_FIFO, ¶m)) -		lbs_pr_err("Error setting scheduler, using default.\n"); +	card->workqueue = create_workqueue("libertas_spi"); +	INIT_WORK(&card->packet_work, if_spi_host_to_card_worker); +	INIT_WORK(&card->resume_work, if_spi_resume_worker);  	err = request_irq(spi->irq, if_spi_host_interrupt,  			IRQF_TRIGGER_FALLING, "libertas_spi", card);  	if (err) { -		lbs_pr_err("can't get host irq line-- request_irq failed\n"); -		goto terminate_thread; +		pr_err("can't get host irq line-- request_irq failed\n"); +		goto terminate_workqueue;  	} -	/* poke the IRQ handler so that we don't miss the first interrupt */ -	up(&card->spi_ready); - -	/* Start the card. +	/* +	 * Start the card.  	 * This will call register_netdev, and we'll start -	 * getting interrupts... */ +	 * getting interrupts... +	 */  	err = lbs_start_card(priv);  	if (err)  		goto release_irq; @@ -1028,23 +1207,21 @@ static int __devinit if_spi_probe(struct spi_device *spi)  release_irq:  	free_irq(spi->irq, card); -terminate_thread: -	if_spi_terminate_spi_thread(card); -remove_card: +terminate_workqueue: +	flush_workqueue(card->workqueue); +	destroy_workqueue(card->workqueue);  	lbs_remove_card(priv); /* will call free_netdev */  free_card:  	free_if_spi_card(card); +teardown: +	if (pdata->teardown) +		pdata->teardown(spi);  out: -	if (helper) -		release_firmware(helper); -	if (mainfw) -		release_firmware(mainfw); -  	lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);  	return err;  } -static int __devexit libertas_spi_remove(struct spi_device *spi) +static int libertas_spi_remove(struct spi_device *spi)  {  	struct if_spi_card *card = spi_get_drvdata(spi);  	struct lbs_private *priv = card->priv; @@ -1052,12 +1229,14 @@ static int __devexit libertas_spi_remove(struct spi_device *spi)  	lbs_deb_spi("libertas_spi_remove\n");  	lbs_deb_enter(LBS_DEB_SPI); +	cancel_work_sync(&card->resume_work); +  	lbs_stop_card(priv);  	lbs_remove_card(priv); /* will call free_netdev */ -	priv->surpriseremoved = 1;  	free_irq(spi->irq, card); -	if_spi_terminate_spi_thread(card); +	flush_workqueue(card->workqueue); +	destroy_workqueue(card->workqueue);  	if (card->pdata->teardown)  		card->pdata->teardown(spi);  	free_if_spi_card(card); @@ -1065,13 +1244,47 @@ static int __devexit libertas_spi_remove(struct spi_device *spi)  	return 0;  } +static int if_spi_suspend(struct device *dev) +{ +	struct spi_device *spi = to_spi_device(dev); +	struct if_spi_card *card = spi_get_drvdata(spi); + +	if (!card->suspended) { +		lbs_suspend(card->priv); +		flush_workqueue(card->workqueue); +		disable_irq(spi->irq); + +		if (card->pdata->teardown) +			card->pdata->teardown(spi); +		card->suspended = 1; +	} + +	return 0; +} + +static int if_spi_resume(struct device *dev) +{ +	struct spi_device *spi = to_spi_device(dev); +	struct if_spi_card *card = spi_get_drvdata(spi); + +	/* Schedule delayed work */ +	schedule_work(&card->resume_work); + +	return 0; +} + +static const struct dev_pm_ops if_spi_pm_ops = { +	.suspend	= if_spi_suspend, +	.resume		= if_spi_resume, +}; +  static struct spi_driver libertas_spi_driver = {  	.probe	= if_spi_probe, -	.remove = __devexit_p(libertas_spi_remove), +	.remove = libertas_spi_remove,  	.driver = {  		.name	= "libertas_spi", -		.bus	= &spi_bus_type,  		.owner	= THIS_MODULE, +		.pm	= &if_spi_pm_ops,  	},  };  | 
