162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	linux/drivers/net/wireless/libertas/if_spi.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Driver for Marvell SPI WLAN cards.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *	Copyright 2008 Analog Devices Inc.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *	Authors:
1062306a36Sopenharmony_ci *	Andrey Yurovsky <andrey@cozybit.com>
1162306a36Sopenharmony_ci *	Colin McCabe <colin@cozybit.com>
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *	Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/hardirq.h>
1962306a36Sopenharmony_ci#include <linux/interrupt.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/firmware.h>
2262306a36Sopenharmony_ci#include <linux/jiffies.h>
2362306a36Sopenharmony_ci#include <linux/list.h>
2462306a36Sopenharmony_ci#include <linux/netdevice.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <linux/spi/libertas_spi.h>
2762306a36Sopenharmony_ci#include <linux/spi/spi.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "host.h"
3062306a36Sopenharmony_ci#include "decl.h"
3162306a36Sopenharmony_ci#include "defs.h"
3262306a36Sopenharmony_ci#include "dev.h"
3362306a36Sopenharmony_ci#include "if_spi.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct if_spi_packet {
3662306a36Sopenharmony_ci	struct list_head		list;
3762306a36Sopenharmony_ci	u16				blen;
3862306a36Sopenharmony_ci	u8				buffer[] __aligned(4);
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct if_spi_card {
4262306a36Sopenharmony_ci	struct spi_device		*spi;
4362306a36Sopenharmony_ci	struct lbs_private		*priv;
4462306a36Sopenharmony_ci	struct libertas_spi_platform_data *pdata;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* The card ID and card revision, as reported by the hardware. */
4762306a36Sopenharmony_ci	u16				card_id;
4862306a36Sopenharmony_ci	u8				card_rev;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	/* The last time that we initiated an SPU operation */
5162306a36Sopenharmony_ci	unsigned long			prev_xfer_time;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	int				use_dummy_writes;
5462306a36Sopenharmony_ci	unsigned long			spu_port_delay;
5562306a36Sopenharmony_ci	unsigned long			spu_reg_delay;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	/* Handles all SPI communication (except for FW load) */
5862306a36Sopenharmony_ci	struct workqueue_struct		*workqueue;
5962306a36Sopenharmony_ci	struct work_struct		packet_work;
6062306a36Sopenharmony_ci	struct work_struct		resume_work;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	u8				cmd_buffer[IF_SPI_CMD_BUF_SIZE];
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* A buffer of incoming packets from libertas core.
6562306a36Sopenharmony_ci	 * Since we can't sleep in hw_host_to_card, we have to buffer
6662306a36Sopenharmony_ci	 * them. */
6762306a36Sopenharmony_ci	struct list_head		cmd_packet_list;
6862306a36Sopenharmony_ci	struct list_head		data_packet_list;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* Protects cmd_packet_list and data_packet_list */
7162306a36Sopenharmony_ci	spinlock_t			buffer_lock;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* True is card suspended */
7462306a36Sopenharmony_ci	u8				suspended;
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void free_if_spi_card(struct if_spi_card *card)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct if_spi_packet *packet, *tmp;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	list_for_each_entry_safe(packet, tmp, &card->cmd_packet_list, list) {
8262306a36Sopenharmony_ci		list_del(&packet->list);
8362306a36Sopenharmony_ci		kfree(packet);
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci	list_for_each_entry_safe(packet, tmp, &card->data_packet_list, list) {
8662306a36Sopenharmony_ci		list_del(&packet->list);
8762306a36Sopenharmony_ci		kfree(packet);
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	kfree(card);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define MODEL_8385	0x04
9362306a36Sopenharmony_ci#define MODEL_8686	0x0b
9462306a36Sopenharmony_ci#define MODEL_8688	0x10
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic const struct lbs_fw_table fw_table[] = {
9762306a36Sopenharmony_ci	{ MODEL_8385, "libertas/gspi8385_helper.bin", "libertas/gspi8385.bin" },
9862306a36Sopenharmony_ci	{ MODEL_8385, "libertas/gspi8385_hlp.bin", "libertas/gspi8385.bin" },
9962306a36Sopenharmony_ci	{ MODEL_8686, "libertas/gspi8686_v9_helper.bin", "libertas/gspi8686_v9.bin" },
10062306a36Sopenharmony_ci	{ MODEL_8686, "libertas/gspi8686_hlp.bin", "libertas/gspi8686.bin" },
10162306a36Sopenharmony_ci	{ MODEL_8688, "libertas/gspi8688_helper.bin", "libertas/gspi8688.bin" },
10262306a36Sopenharmony_ci	{ 0, NULL, NULL }
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8385_helper.bin");
10562306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8385_hlp.bin");
10662306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8385.bin");
10762306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686_v9_helper.bin");
10862306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686_v9.bin");
10962306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686_hlp.bin");
11062306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686.bin");
11162306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8688_helper.bin");
11262306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8688.bin");
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/*
11662306a36Sopenharmony_ci * SPI Interface Unit Routines
11762306a36Sopenharmony_ci *
11862306a36Sopenharmony_ci * The SPU sits between the host and the WLAN module.
11962306a36Sopenharmony_ci * All communication with the firmware is through SPU transactions.
12062306a36Sopenharmony_ci *
12162306a36Sopenharmony_ci * First we have to put a SPU register name on the bus. Then we can
12262306a36Sopenharmony_ci * either read from or write to that register.
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci */
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void spu_transaction_init(struct if_spi_card *card)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	if (!time_after(jiffies, card->prev_xfer_time + 1)) {
12962306a36Sopenharmony_ci		/* Unfortunately, the SPU requires a delay between successive
13062306a36Sopenharmony_ci		 * transactions. If our last transaction was more than a jiffy
13162306a36Sopenharmony_ci		 * ago, we have obviously already delayed enough.
13262306a36Sopenharmony_ci		 * If not, we have to busy-wait to be on the safe side. */
13362306a36Sopenharmony_ci		ndelay(400);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void spu_transaction_finish(struct if_spi_card *card)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	card->prev_xfer_time = jiffies;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/*
14362306a36Sopenharmony_ci * Write out a byte buffer to an SPI register,
14462306a36Sopenharmony_ci * using a series of 16-bit transfers.
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_cistatic int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	int err = 0;
14962306a36Sopenharmony_ci	__le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK);
15062306a36Sopenharmony_ci	struct spi_message m;
15162306a36Sopenharmony_ci	struct spi_transfer reg_trans;
15262306a36Sopenharmony_ci	struct spi_transfer data_trans;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	spi_message_init(&m);
15562306a36Sopenharmony_ci	memset(&reg_trans, 0, sizeof(reg_trans));
15662306a36Sopenharmony_ci	memset(&data_trans, 0, sizeof(data_trans));
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	/* You must give an even number of bytes to the SPU, even if it
15962306a36Sopenharmony_ci	 * doesn't care about the last one.  */
16062306a36Sopenharmony_ci	BUG_ON(len & 0x1);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	spu_transaction_init(card);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* write SPU register index */
16562306a36Sopenharmony_ci	reg_trans.tx_buf = &reg_out;
16662306a36Sopenharmony_ci	reg_trans.len = sizeof(reg_out);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	data_trans.tx_buf = buf;
16962306a36Sopenharmony_ci	data_trans.len = len;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	spi_message_add_tail(&reg_trans, &m);
17262306a36Sopenharmony_ci	spi_message_add_tail(&data_trans, &m);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	err = spi_sync(card->spi, &m);
17562306a36Sopenharmony_ci	spu_transaction_finish(card);
17662306a36Sopenharmony_ci	return err;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	__le16 buff;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	buff = cpu_to_le16(val);
18462306a36Sopenharmony_ci	return spu_write(card, reg, (u8 *)&buff, sizeof(u16));
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic inline int spu_reg_is_port_reg(u16 reg)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	switch (reg) {
19062306a36Sopenharmony_ci	case IF_SPI_IO_RDWRPORT_REG:
19162306a36Sopenharmony_ci	case IF_SPI_CMD_RDWRPORT_REG:
19262306a36Sopenharmony_ci	case IF_SPI_DATA_RDWRPORT_REG:
19362306a36Sopenharmony_ci		return 1;
19462306a36Sopenharmony_ci	default:
19562306a36Sopenharmony_ci		return 0;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	unsigned int delay;
20262306a36Sopenharmony_ci	int err = 0;
20362306a36Sopenharmony_ci	__le16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK);
20462306a36Sopenharmony_ci	struct spi_message m;
20562306a36Sopenharmony_ci	struct spi_transfer reg_trans;
20662306a36Sopenharmony_ci	struct spi_transfer dummy_trans;
20762306a36Sopenharmony_ci	struct spi_transfer data_trans;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/*
21062306a36Sopenharmony_ci	 * You must take an even number of bytes from the SPU, even if you
21162306a36Sopenharmony_ci	 * don't care about the last one.
21262306a36Sopenharmony_ci	 */
21362306a36Sopenharmony_ci	BUG_ON(len & 0x1);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	spu_transaction_init(card);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	spi_message_init(&m);
21862306a36Sopenharmony_ci	memset(&reg_trans, 0, sizeof(reg_trans));
21962306a36Sopenharmony_ci	memset(&dummy_trans, 0, sizeof(dummy_trans));
22062306a36Sopenharmony_ci	memset(&data_trans, 0, sizeof(data_trans));
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* write SPU register index */
22362306a36Sopenharmony_ci	reg_trans.tx_buf = &reg_out;
22462306a36Sopenharmony_ci	reg_trans.len = sizeof(reg_out);
22562306a36Sopenharmony_ci	spi_message_add_tail(&reg_trans, &m);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay :
22862306a36Sopenharmony_ci						card->spu_reg_delay;
22962306a36Sopenharmony_ci	if (card->use_dummy_writes) {
23062306a36Sopenharmony_ci		/* Clock in dummy cycles while the SPU fills the FIFO */
23162306a36Sopenharmony_ci		dummy_trans.len = delay / 8;
23262306a36Sopenharmony_ci		spi_message_add_tail(&dummy_trans, &m);
23362306a36Sopenharmony_ci	} else {
23462306a36Sopenharmony_ci		/* Busy-wait while the SPU fills the FIFO */
23562306a36Sopenharmony_ci		reg_trans.delay.value =
23662306a36Sopenharmony_ci			DIV_ROUND_UP((100 + (delay * 10)), 1000);
23762306a36Sopenharmony_ci		reg_trans.delay.unit = SPI_DELAY_UNIT_USECS;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* read in data */
24162306a36Sopenharmony_ci	data_trans.rx_buf = buf;
24262306a36Sopenharmony_ci	data_trans.len = len;
24362306a36Sopenharmony_ci	spi_message_add_tail(&data_trans, &m);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	err = spi_sync(card->spi, &m);
24662306a36Sopenharmony_ci	spu_transaction_finish(card);
24762306a36Sopenharmony_ci	return err;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/* Read 16 bits from an SPI register */
25162306a36Sopenharmony_cistatic inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	__le16 buf;
25462306a36Sopenharmony_ci	int ret;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	ret = spu_read(card, reg, (u8 *)&buf, sizeof(buf));
25762306a36Sopenharmony_ci	if (ret == 0)
25862306a36Sopenharmony_ci		*val = le16_to_cpup(&buf);
25962306a36Sopenharmony_ci	return ret;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/*
26362306a36Sopenharmony_ci * Read 32 bits from an SPI register.
26462306a36Sopenharmony_ci * The low 16 bits are read first.
26562306a36Sopenharmony_ci */
26662306a36Sopenharmony_cistatic int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	__le32 buf;
26962306a36Sopenharmony_ci	int err;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	err = spu_read(card, reg, (u8 *)&buf, sizeof(buf));
27262306a36Sopenharmony_ci	if (!err)
27362306a36Sopenharmony_ci		*val = le32_to_cpup(&buf);
27462306a36Sopenharmony_ci	return err;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/*
27862306a36Sopenharmony_ci * Keep reading 16 bits from an SPI register until you get the correct result.
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * If mask = 0, the correct result is any non-zero number.
28162306a36Sopenharmony_ci * If mask != 0, the correct result is any number where
28262306a36Sopenharmony_ci * number & target_mask == target
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci * Returns -ETIMEDOUT if a second passes without the correct result.
28562306a36Sopenharmony_ci */
28662306a36Sopenharmony_cistatic int spu_wait_for_u16(struct if_spi_card *card, u16 reg,
28762306a36Sopenharmony_ci			u16 target_mask, u16 target)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	int err;
29062306a36Sopenharmony_ci	unsigned long timeout = jiffies + 5*HZ;
29162306a36Sopenharmony_ci	while (1) {
29262306a36Sopenharmony_ci		u16 val;
29362306a36Sopenharmony_ci		err = spu_read_u16(card, reg, &val);
29462306a36Sopenharmony_ci		if (err)
29562306a36Sopenharmony_ci			return err;
29662306a36Sopenharmony_ci		if (target_mask) {
29762306a36Sopenharmony_ci			if ((val & target_mask) == target)
29862306a36Sopenharmony_ci				return 0;
29962306a36Sopenharmony_ci		} else {
30062306a36Sopenharmony_ci			if (val)
30162306a36Sopenharmony_ci				return 0;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci		udelay(100);
30462306a36Sopenharmony_ci		if (time_after(jiffies, timeout)) {
30562306a36Sopenharmony_ci			pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n",
30662306a36Sopenharmony_ci			       __func__, val, target_mask, target);
30762306a36Sopenharmony_ci			return -ETIMEDOUT;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/*
31362306a36Sopenharmony_ci * Read 16 bits from an SPI register until you receive a specific value.
31462306a36Sopenharmony_ci * Returns -ETIMEDOUT if a 4 tries pass without success.
31562306a36Sopenharmony_ci */
31662306a36Sopenharmony_cistatic int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	int err, try;
31962306a36Sopenharmony_ci	for (try = 0; try < 4; ++try) {
32062306a36Sopenharmony_ci		u32 val = 0;
32162306a36Sopenharmony_ci		err = spu_read_u32(card, reg, &val);
32262306a36Sopenharmony_ci		if (err)
32362306a36Sopenharmony_ci			return err;
32462306a36Sopenharmony_ci		if (val == target)
32562306a36Sopenharmony_ci			return 0;
32662306a36Sopenharmony_ci		mdelay(100);
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	return -ETIMEDOUT;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int spu_set_interrupt_mode(struct if_spi_card *card,
33262306a36Sopenharmony_ci			   int suppress_host_int,
33362306a36Sopenharmony_ci			   int auto_int)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	int err = 0;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/*
33862306a36Sopenharmony_ci	 * We can suppress a host interrupt by clearing the appropriate
33962306a36Sopenharmony_ci	 * bit in the "host interrupt status mask" register
34062306a36Sopenharmony_ci	 */
34162306a36Sopenharmony_ci	if (suppress_host_int) {
34262306a36Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
34362306a36Sopenharmony_ci		if (err)
34462306a36Sopenharmony_ci			return err;
34562306a36Sopenharmony_ci	} else {
34662306a36Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG,
34762306a36Sopenharmony_ci			      IF_SPI_HISM_TX_DOWNLOAD_RDY |
34862306a36Sopenharmony_ci			      IF_SPI_HISM_RX_UPLOAD_RDY |
34962306a36Sopenharmony_ci			      IF_SPI_HISM_CMD_DOWNLOAD_RDY |
35062306a36Sopenharmony_ci			      IF_SPI_HISM_CARDEVENT |
35162306a36Sopenharmony_ci			      IF_SPI_HISM_CMD_UPLOAD_RDY);
35262306a36Sopenharmony_ci		if (err)
35362306a36Sopenharmony_ci			return err;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/*
35762306a36Sopenharmony_ci	 * If auto-interrupts are on, the completion of certain transactions
35862306a36Sopenharmony_ci	 * will trigger an interrupt automatically. If auto-interrupts
35962306a36Sopenharmony_ci	 * are off, we need to set the "Card Interrupt Cause" register to
36062306a36Sopenharmony_ci	 * trigger a card interrupt.
36162306a36Sopenharmony_ci	 */
36262306a36Sopenharmony_ci	if (auto_int) {
36362306a36Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG,
36462306a36Sopenharmony_ci				IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO |
36562306a36Sopenharmony_ci				IF_SPI_HICT_RX_UPLOAD_OVER_AUTO |
36662306a36Sopenharmony_ci				IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO |
36762306a36Sopenharmony_ci				IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO);
36862306a36Sopenharmony_ci		if (err)
36962306a36Sopenharmony_ci			return err;
37062306a36Sopenharmony_ci	} else {
37162306a36Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
37262306a36Sopenharmony_ci		if (err)
37362306a36Sopenharmony_ci			return err;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci	return err;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic int spu_get_chip_revision(struct if_spi_card *card,
37962306a36Sopenharmony_ci				  u16 *card_id, u8 *card_rev)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	int err = 0;
38262306a36Sopenharmony_ci	u32 dev_ctrl;
38362306a36Sopenharmony_ci	err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl);
38462306a36Sopenharmony_ci	if (err)
38562306a36Sopenharmony_ci		return err;
38662306a36Sopenharmony_ci	*card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl);
38762306a36Sopenharmony_ci	*card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl);
38862306a36Sopenharmony_ci	return err;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic int spu_set_bus_mode(struct if_spi_card *card, u16 mode)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	int err = 0;
39462306a36Sopenharmony_ci	u16 rval;
39562306a36Sopenharmony_ci	/* set bus mode */
39662306a36Sopenharmony_ci	err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode);
39762306a36Sopenharmony_ci	if (err)
39862306a36Sopenharmony_ci		return err;
39962306a36Sopenharmony_ci	/* Check that we were able to read back what we just wrote. */
40062306a36Sopenharmony_ci	err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval);
40162306a36Sopenharmony_ci	if (err)
40262306a36Sopenharmony_ci		return err;
40362306a36Sopenharmony_ci	if ((rval & 0xF) != mode) {
40462306a36Sopenharmony_ci		pr_err("Can't read bus mode register\n");
40562306a36Sopenharmony_ci		return -EIO;
40662306a36Sopenharmony_ci	}
40762306a36Sopenharmony_ci	return 0;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic int spu_init(struct if_spi_card *card, int use_dummy_writes)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	int err = 0;
41362306a36Sopenharmony_ci	u32 delay;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/*
41662306a36Sopenharmony_ci	 * We have to start up in timed delay mode so that we can safely
41762306a36Sopenharmony_ci	 * read the Delay Read Register.
41862306a36Sopenharmony_ci	 */
41962306a36Sopenharmony_ci	card->use_dummy_writes = 0;
42062306a36Sopenharmony_ci	err = spu_set_bus_mode(card,
42162306a36Sopenharmony_ci				IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
42262306a36Sopenharmony_ci				IF_SPI_BUS_MODE_DELAY_METHOD_TIMED |
42362306a36Sopenharmony_ci				IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA);
42462306a36Sopenharmony_ci	if (err)
42562306a36Sopenharmony_ci		return err;
42662306a36Sopenharmony_ci	card->spu_port_delay = 1000;
42762306a36Sopenharmony_ci	card->spu_reg_delay = 1000;
42862306a36Sopenharmony_ci	err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay);
42962306a36Sopenharmony_ci	if (err)
43062306a36Sopenharmony_ci		return err;
43162306a36Sopenharmony_ci	card->spu_port_delay = delay & 0x0000ffff;
43262306a36Sopenharmony_ci	card->spu_reg_delay = (delay & 0xffff0000) >> 16;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/* If dummy clock delay mode has been requested, switch to it now */
43562306a36Sopenharmony_ci	if (use_dummy_writes) {
43662306a36Sopenharmony_ci		card->use_dummy_writes = 1;
43762306a36Sopenharmony_ci		err = spu_set_bus_mode(card,
43862306a36Sopenharmony_ci				IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
43962306a36Sopenharmony_ci				IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK |
44062306a36Sopenharmony_ci				IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA);
44162306a36Sopenharmony_ci		if (err)
44262306a36Sopenharmony_ci			return err;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	lbs_deb_spi("Initialized SPU unit. "
44662306a36Sopenharmony_ci		    "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n",
44762306a36Sopenharmony_ci		    card->spu_port_delay, card->spu_reg_delay);
44862306a36Sopenharmony_ci	return err;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci/*
45262306a36Sopenharmony_ci * Firmware Loading
45362306a36Sopenharmony_ci */
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int if_spi_prog_helper_firmware(struct if_spi_card *card,
45662306a36Sopenharmony_ci					const struct firmware *firmware)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	int err = 0;
45962306a36Sopenharmony_ci	int bytes_remaining;
46062306a36Sopenharmony_ci	const u8 *fw;
46162306a36Sopenharmony_ci	u8 temp[HELPER_FW_LOAD_CHUNK_SZ];
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	err = spu_set_interrupt_mode(card, 1, 0);
46462306a36Sopenharmony_ci	if (err)
46562306a36Sopenharmony_ci		goto out;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	bytes_remaining = firmware->size;
46862306a36Sopenharmony_ci	fw = firmware->data;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* Load helper firmware image */
47162306a36Sopenharmony_ci	while (bytes_remaining > 0) {
47262306a36Sopenharmony_ci		/*
47362306a36Sopenharmony_ci		 * Scratch pad 1 should contain the number of bytes we
47462306a36Sopenharmony_ci		 * want to download to the firmware
47562306a36Sopenharmony_ci		 */
47662306a36Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,
47762306a36Sopenharmony_ci					HELPER_FW_LOAD_CHUNK_SZ);
47862306a36Sopenharmony_ci		if (err)
47962306a36Sopenharmony_ci			goto out;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
48262306a36Sopenharmony_ci					IF_SPI_HIST_CMD_DOWNLOAD_RDY,
48362306a36Sopenharmony_ci					IF_SPI_HIST_CMD_DOWNLOAD_RDY);
48462306a36Sopenharmony_ci		if (err)
48562306a36Sopenharmony_ci			goto out;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		/*
48862306a36Sopenharmony_ci		 * Feed the data into the command read/write port reg
48962306a36Sopenharmony_ci		 * in chunks of 64 bytes
49062306a36Sopenharmony_ci		 */
49162306a36Sopenharmony_ci		memset(temp, 0, sizeof(temp));
49262306a36Sopenharmony_ci		memcpy(temp, fw,
49362306a36Sopenharmony_ci		       min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ));
49462306a36Sopenharmony_ci		mdelay(10);
49562306a36Sopenharmony_ci		err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
49662306a36Sopenharmony_ci					temp, HELPER_FW_LOAD_CHUNK_SZ);
49762306a36Sopenharmony_ci		if (err)
49862306a36Sopenharmony_ci			goto out;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		/* Interrupt the boot code */
50162306a36Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
50262306a36Sopenharmony_ci		if (err)
50362306a36Sopenharmony_ci			goto out;
50462306a36Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
50562306a36Sopenharmony_ci				       IF_SPI_CIC_CMD_DOWNLOAD_OVER);
50662306a36Sopenharmony_ci		if (err)
50762306a36Sopenharmony_ci			goto out;
50862306a36Sopenharmony_ci		bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ;
50962306a36Sopenharmony_ci		fw += HELPER_FW_LOAD_CHUNK_SZ;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/*
51362306a36Sopenharmony_ci	 * Once the helper / single stage firmware download is complete,
51462306a36Sopenharmony_ci	 * write 0 to scratch pad 1 and interrupt the
51562306a36Sopenharmony_ci	 * bootloader. This completes the helper download.
51662306a36Sopenharmony_ci	 */
51762306a36Sopenharmony_ci	err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK);
51862306a36Sopenharmony_ci	if (err)
51962306a36Sopenharmony_ci		goto out;
52062306a36Sopenharmony_ci	err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
52162306a36Sopenharmony_ci	if (err)
52262306a36Sopenharmony_ci		goto out;
52362306a36Sopenharmony_ci	err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
52462306a36Sopenharmony_ci				IF_SPI_CIC_CMD_DOWNLOAD_OVER);
52562306a36Sopenharmony_ciout:
52662306a36Sopenharmony_ci	if (err)
52762306a36Sopenharmony_ci		pr_err("failed to load helper firmware (err=%d)\n", err);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	return err;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci/*
53362306a36Sopenharmony_ci * Returns the length of the next packet the firmware expects us to send.
53462306a36Sopenharmony_ci * Sets crc_err if the previous transfer had a CRC error.
53562306a36Sopenharmony_ci */
53662306a36Sopenharmony_cistatic int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,
53762306a36Sopenharmony_ci						int *crc_err)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	u16 len;
54062306a36Sopenharmony_ci	int err = 0;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	/*
54362306a36Sopenharmony_ci	 * wait until the host interrupt status register indicates
54462306a36Sopenharmony_ci	 * that we are ready to download
54562306a36Sopenharmony_ci	 */
54662306a36Sopenharmony_ci	err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
54762306a36Sopenharmony_ci				IF_SPI_HIST_CMD_DOWNLOAD_RDY,
54862306a36Sopenharmony_ci				IF_SPI_HIST_CMD_DOWNLOAD_RDY);
54962306a36Sopenharmony_ci	if (err) {
55062306a36Sopenharmony_ci		pr_err("timed out waiting for host_int_status\n");
55162306a36Sopenharmony_ci		return err;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/* Ask the device how many bytes of firmware it wants. */
55562306a36Sopenharmony_ci	err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
55662306a36Sopenharmony_ci	if (err)
55762306a36Sopenharmony_ci		return err;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (len > IF_SPI_CMD_BUF_SIZE) {
56062306a36Sopenharmony_ci		pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n",
56162306a36Sopenharmony_ci		       len);
56262306a36Sopenharmony_ci		return -EIO;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci	if (len & 0x1) {
56562306a36Sopenharmony_ci		lbs_deb_spi("%s: crc error\n", __func__);
56662306a36Sopenharmony_ci		len &= ~0x1;
56762306a36Sopenharmony_ci		*crc_err = 1;
56862306a36Sopenharmony_ci	} else
56962306a36Sopenharmony_ci		*crc_err = 0;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	return len;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic int if_spi_prog_main_firmware(struct if_spi_card *card,
57562306a36Sopenharmony_ci					const struct firmware *firmware)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct lbs_private *priv = card->priv;
57862306a36Sopenharmony_ci	int len, prev_len;
57962306a36Sopenharmony_ci	int bytes, crc_err = 0, err = 0;
58062306a36Sopenharmony_ci	const u8 *fw;
58162306a36Sopenharmony_ci	u16 num_crc_errs;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	err = spu_set_interrupt_mode(card, 1, 0);
58462306a36Sopenharmony_ci	if (err)
58562306a36Sopenharmony_ci		goto out;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);
58862306a36Sopenharmony_ci	if (err) {
58962306a36Sopenharmony_ci		netdev_err(priv->dev,
59062306a36Sopenharmony_ci			   "%s: timed out waiting for initial scratch reg = 0\n",
59162306a36Sopenharmony_ci			   __func__);
59262306a36Sopenharmony_ci		goto out;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	num_crc_errs = 0;
59662306a36Sopenharmony_ci	prev_len = 0;
59762306a36Sopenharmony_ci	bytes = firmware->size;
59862306a36Sopenharmony_ci	fw = firmware->data;
59962306a36Sopenharmony_ci	while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) {
60062306a36Sopenharmony_ci		if (len < 0) {
60162306a36Sopenharmony_ci			err = len;
60262306a36Sopenharmony_ci			goto out;
60362306a36Sopenharmony_ci		}
60462306a36Sopenharmony_ci		if (bytes < 0) {
60562306a36Sopenharmony_ci			/*
60662306a36Sopenharmony_ci			 * If there are no more bytes left, we would normally
60762306a36Sopenharmony_ci			 * expect to have terminated with len = 0
60862306a36Sopenharmony_ci			 */
60962306a36Sopenharmony_ci			netdev_err(priv->dev,
61062306a36Sopenharmony_ci				   "Firmware load wants more bytes than we have to offer.\n");
61162306a36Sopenharmony_ci			break;
61262306a36Sopenharmony_ci		}
61362306a36Sopenharmony_ci		if (crc_err) {
61462306a36Sopenharmony_ci			/* Previous transfer failed. */
61562306a36Sopenharmony_ci			if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) {
61662306a36Sopenharmony_ci				pr_err("Too many CRC errors encountered in firmware load.\n");
61762306a36Sopenharmony_ci				err = -EIO;
61862306a36Sopenharmony_ci				goto out;
61962306a36Sopenharmony_ci			}
62062306a36Sopenharmony_ci		} else {
62162306a36Sopenharmony_ci			/* Previous transfer succeeded. Advance counters. */
62262306a36Sopenharmony_ci			bytes -= prev_len;
62362306a36Sopenharmony_ci			fw += prev_len;
62462306a36Sopenharmony_ci		}
62562306a36Sopenharmony_ci		if (bytes < len) {
62662306a36Sopenharmony_ci			memset(card->cmd_buffer, 0, len);
62762306a36Sopenharmony_ci			memcpy(card->cmd_buffer, fw, bytes);
62862306a36Sopenharmony_ci		} else
62962306a36Sopenharmony_ci			memcpy(card->cmd_buffer, fw, len);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
63262306a36Sopenharmony_ci		if (err)
63362306a36Sopenharmony_ci			goto out;
63462306a36Sopenharmony_ci		err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
63562306a36Sopenharmony_ci				card->cmd_buffer, len);
63662306a36Sopenharmony_ci		if (err)
63762306a36Sopenharmony_ci			goto out;
63862306a36Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG ,
63962306a36Sopenharmony_ci					IF_SPI_CIC_CMD_DOWNLOAD_OVER);
64062306a36Sopenharmony_ci		if (err)
64162306a36Sopenharmony_ci			goto out;
64262306a36Sopenharmony_ci		prev_len = len;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci	if (bytes > prev_len) {
64562306a36Sopenharmony_ci		pr_err("firmware load wants fewer bytes than we have to offer\n");
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	/* Confirm firmware download */
64962306a36Sopenharmony_ci	err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG,
65062306a36Sopenharmony_ci					SUCCESSFUL_FW_DOWNLOAD_MAGIC);
65162306a36Sopenharmony_ci	if (err) {
65262306a36Sopenharmony_ci		pr_err("failed to confirm the firmware download\n");
65362306a36Sopenharmony_ci		goto out;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ciout:
65762306a36Sopenharmony_ci	if (err)
65862306a36Sopenharmony_ci		pr_err("failed to load firmware (err=%d)\n", err);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	return err;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci/*
66462306a36Sopenharmony_ci * SPI Transfer Thread
66562306a36Sopenharmony_ci *
66662306a36Sopenharmony_ci * The SPI worker handles all SPI transfers, so there is no need for a lock.
66762306a36Sopenharmony_ci */
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci/* Move a command from the card to the host */
67062306a36Sopenharmony_cistatic int if_spi_c2h_cmd(struct if_spi_card *card)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct lbs_private *priv = card->priv;
67362306a36Sopenharmony_ci	unsigned long flags;
67462306a36Sopenharmony_ci	int err = 0;
67562306a36Sopenharmony_ci	u16 len;
67662306a36Sopenharmony_ci	u8 i;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/*
67962306a36Sopenharmony_ci	 * We need a buffer big enough to handle whatever people send to
68062306a36Sopenharmony_ci	 * hw_host_to_card
68162306a36Sopenharmony_ci	 */
68262306a36Sopenharmony_ci	BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE);
68362306a36Sopenharmony_ci	BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/*
68662306a36Sopenharmony_ci	 * It's just annoying if the buffer size isn't a multiple of 4, because
68762306a36Sopenharmony_ci	 * then we might have len < IF_SPI_CMD_BUF_SIZE but
68862306a36Sopenharmony_ci	 * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE
68962306a36Sopenharmony_ci	 */
69062306a36Sopenharmony_ci	BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* How many bytes are there to read? */
69362306a36Sopenharmony_ci	err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len);
69462306a36Sopenharmony_ci	if (err)
69562306a36Sopenharmony_ci		goto out;
69662306a36Sopenharmony_ci	if (!len) {
69762306a36Sopenharmony_ci		netdev_err(priv->dev, "%s: error: card has no data for host\n",
69862306a36Sopenharmony_ci			   __func__);
69962306a36Sopenharmony_ci		err = -EINVAL;
70062306a36Sopenharmony_ci		goto out;
70162306a36Sopenharmony_ci	} else if (len > IF_SPI_CMD_BUF_SIZE) {
70262306a36Sopenharmony_ci		netdev_err(priv->dev,
70362306a36Sopenharmony_ci			   "%s: error: response packet too large: %d bytes, but maximum is %d\n",
70462306a36Sopenharmony_ci			   __func__, len, IF_SPI_CMD_BUF_SIZE);
70562306a36Sopenharmony_ci		err = -EINVAL;
70662306a36Sopenharmony_ci		goto out;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	/* Read the data from the WLAN module into our command buffer */
71062306a36Sopenharmony_ci	err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG,
71162306a36Sopenharmony_ci				card->cmd_buffer, ALIGN(len, 4));
71262306a36Sopenharmony_ci	if (err)
71362306a36Sopenharmony_ci		goto out;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	spin_lock_irqsave(&priv->driver_lock, flags);
71662306a36Sopenharmony_ci	i = (priv->resp_idx == 0) ? 1 : 0;
71762306a36Sopenharmony_ci	BUG_ON(priv->resp_len[i]);
71862306a36Sopenharmony_ci	priv->resp_len[i] = len;
71962306a36Sopenharmony_ci	memcpy(priv->resp_buf[i], card->cmd_buffer, len);
72062306a36Sopenharmony_ci	lbs_notify_command_response(priv, i);
72162306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->driver_lock, flags);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ciout:
72462306a36Sopenharmony_ci	if (err)
72562306a36Sopenharmony_ci		netdev_err(priv->dev, "%s: err=%d\n", __func__, err);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	return err;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci/* Move data from the card to the host */
73162306a36Sopenharmony_cistatic int if_spi_c2h_data(struct if_spi_card *card)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct lbs_private *priv = card->priv;
73462306a36Sopenharmony_ci	struct sk_buff *skb;
73562306a36Sopenharmony_ci	char *data;
73662306a36Sopenharmony_ci	u16 len;
73762306a36Sopenharmony_ci	int err = 0;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/* How many bytes are there to read? */
74062306a36Sopenharmony_ci	err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
74162306a36Sopenharmony_ci	if (err)
74262306a36Sopenharmony_ci		goto out;
74362306a36Sopenharmony_ci	if (!len) {
74462306a36Sopenharmony_ci		netdev_err(priv->dev, "%s: error: card has no data for host\n",
74562306a36Sopenharmony_ci			   __func__);
74662306a36Sopenharmony_ci		err = -EINVAL;
74762306a36Sopenharmony_ci		goto out;
74862306a36Sopenharmony_ci	} else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
74962306a36Sopenharmony_ci		netdev_err(priv->dev,
75062306a36Sopenharmony_ci			   "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n",
75162306a36Sopenharmony_ci			   __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
75262306a36Sopenharmony_ci		err = -EINVAL;
75362306a36Sopenharmony_ci		goto out;
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	/* TODO: should we allocate a smaller skb if we have less data? */
75762306a36Sopenharmony_ci	skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
75862306a36Sopenharmony_ci	if (!skb) {
75962306a36Sopenharmony_ci		err = -ENOBUFS;
76062306a36Sopenharmony_ci		goto out;
76162306a36Sopenharmony_ci	}
76262306a36Sopenharmony_ci	skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
76362306a36Sopenharmony_ci	data = skb_put(skb, len);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	/* Read the data from the WLAN module into our skb... */
76662306a36Sopenharmony_ci	err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 4));
76762306a36Sopenharmony_ci	if (err) {
76862306a36Sopenharmony_ci		dev_kfree_skb(skb);
76962306a36Sopenharmony_ci		goto out;
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/* pass the SKB to libertas */
77362306a36Sopenharmony_ci	err = lbs_process_rxed_packet(card->priv, skb);
77462306a36Sopenharmony_ci	/* lbs_process_rxed_packet() consumes the skb */
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ciout:
77762306a36Sopenharmony_ci	if (err)
77862306a36Sopenharmony_ci		netdev_err(priv->dev, "%s: err=%d\n", __func__, err);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	return err;
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci/* Move data or a command from the host to the card. */
78462306a36Sopenharmony_cistatic void if_spi_h2c(struct if_spi_card *card,
78562306a36Sopenharmony_ci			struct if_spi_packet *packet, int type)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct lbs_private *priv = card->priv;
78862306a36Sopenharmony_ci	int err = 0;
78962306a36Sopenharmony_ci	u16 port_reg;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	switch (type) {
79262306a36Sopenharmony_ci	case MVMS_DAT:
79362306a36Sopenharmony_ci		port_reg = IF_SPI_DATA_RDWRPORT_REG;
79462306a36Sopenharmony_ci		break;
79562306a36Sopenharmony_ci	case MVMS_CMD:
79662306a36Sopenharmony_ci		port_reg = IF_SPI_CMD_RDWRPORT_REG;
79762306a36Sopenharmony_ci		break;
79862306a36Sopenharmony_ci	default:
79962306a36Sopenharmony_ci		netdev_err(priv->dev, "can't transfer buffer of type %d\n",
80062306a36Sopenharmony_ci			   type);
80162306a36Sopenharmony_ci		err = -EINVAL;
80262306a36Sopenharmony_ci		goto out;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	/* Write the data to the card */
80662306a36Sopenharmony_ci	err = spu_write(card, port_reg, packet->buffer, packet->blen);
80762306a36Sopenharmony_ci	if (err)
80862306a36Sopenharmony_ci		goto out;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ciout:
81162306a36Sopenharmony_ci	kfree(packet);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	if (err)
81462306a36Sopenharmony_ci		netdev_err(priv->dev, "%s: error %d\n", __func__, err);
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci/* Inform the host about a card event */
81862306a36Sopenharmony_cistatic void if_spi_e2h(struct if_spi_card *card)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	int err = 0;
82162306a36Sopenharmony_ci	u32 cause;
82262306a36Sopenharmony_ci	struct lbs_private *priv = card->priv;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause);
82562306a36Sopenharmony_ci	if (err)
82662306a36Sopenharmony_ci		goto out;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	/* re-enable the card event interrupt */
82962306a36Sopenharmony_ci	err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG,
83062306a36Sopenharmony_ci			    ~IF_SPI_HICU_CARD_EVENT);
83162306a36Sopenharmony_ci	if (err)
83262306a36Sopenharmony_ci		goto out;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	/* generate a card interrupt */
83562306a36Sopenharmony_ci	err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
83662306a36Sopenharmony_ci			    IF_SPI_CIC_HOST_EVENT);
83762306a36Sopenharmony_ci	if (err)
83862306a36Sopenharmony_ci		goto out;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	lbs_queue_event(priv, cause & 0xff);
84162306a36Sopenharmony_ciout:
84262306a36Sopenharmony_ci	if (err)
84362306a36Sopenharmony_ci		netdev_err(priv->dev, "%s: error %d\n", __func__, err);
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void if_spi_host_to_card_worker(struct work_struct *work)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	int err;
84962306a36Sopenharmony_ci	struct if_spi_card *card;
85062306a36Sopenharmony_ci	u16 hiStatus;
85162306a36Sopenharmony_ci	unsigned long flags;
85262306a36Sopenharmony_ci	struct if_spi_packet *packet;
85362306a36Sopenharmony_ci	struct lbs_private *priv;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	card = container_of(work, struct if_spi_card, packet_work);
85662306a36Sopenharmony_ci	priv = card->priv;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	/*
85962306a36Sopenharmony_ci	 * Read the host interrupt status register to see what we
86062306a36Sopenharmony_ci	 * can do.
86162306a36Sopenharmony_ci	 */
86262306a36Sopenharmony_ci	err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG,
86362306a36Sopenharmony_ci				&hiStatus);
86462306a36Sopenharmony_ci	if (err) {
86562306a36Sopenharmony_ci		netdev_err(priv->dev, "I/O error\n");
86662306a36Sopenharmony_ci		goto err;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) {
87062306a36Sopenharmony_ci		err = if_spi_c2h_cmd(card);
87162306a36Sopenharmony_ci		if (err)
87262306a36Sopenharmony_ci			goto err;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci	if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) {
87562306a36Sopenharmony_ci		err = if_spi_c2h_data(card);
87662306a36Sopenharmony_ci		if (err)
87762306a36Sopenharmony_ci			goto err;
87862306a36Sopenharmony_ci	}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	/*
88162306a36Sopenharmony_ci	 * workaround: in PS mode, the card does not set the Command
88262306a36Sopenharmony_ci	 * Download Ready bit, but it sets TX Download Ready.
88362306a36Sopenharmony_ci	 */
88462306a36Sopenharmony_ci	if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY ||
88562306a36Sopenharmony_ci	   (card->priv->psstate != PS_STATE_FULL_POWER &&
88662306a36Sopenharmony_ci	    (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) {
88762306a36Sopenharmony_ci		/*
88862306a36Sopenharmony_ci		 * This means two things. First of all,
88962306a36Sopenharmony_ci		 * if there was a previous command sent, the card has
89062306a36Sopenharmony_ci		 * successfully received it.
89162306a36Sopenharmony_ci		 * Secondly, it is now ready to download another
89262306a36Sopenharmony_ci		 * command.
89362306a36Sopenharmony_ci		 */
89462306a36Sopenharmony_ci		lbs_host_to_card_done(card->priv);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci		/* Do we have any command packets from the host to send? */
89762306a36Sopenharmony_ci		packet = NULL;
89862306a36Sopenharmony_ci		spin_lock_irqsave(&card->buffer_lock, flags);
89962306a36Sopenharmony_ci		if (!list_empty(&card->cmd_packet_list)) {
90062306a36Sopenharmony_ci			packet = (struct if_spi_packet *)(card->
90162306a36Sopenharmony_ci					cmd_packet_list.next);
90262306a36Sopenharmony_ci			list_del(&packet->list);
90362306a36Sopenharmony_ci		}
90462306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->buffer_lock, flags);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		if (packet)
90762306a36Sopenharmony_ci			if_spi_h2c(card, packet, MVMS_CMD);
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci	if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) {
91062306a36Sopenharmony_ci		/* Do we have any data packets from the host to send? */
91162306a36Sopenharmony_ci		packet = NULL;
91262306a36Sopenharmony_ci		spin_lock_irqsave(&card->buffer_lock, flags);
91362306a36Sopenharmony_ci		if (!list_empty(&card->data_packet_list)) {
91462306a36Sopenharmony_ci			packet = (struct if_spi_packet *)(card->
91562306a36Sopenharmony_ci					data_packet_list.next);
91662306a36Sopenharmony_ci			list_del(&packet->list);
91762306a36Sopenharmony_ci		}
91862306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->buffer_lock, flags);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci		if (packet)
92162306a36Sopenharmony_ci			if_spi_h2c(card, packet, MVMS_DAT);
92262306a36Sopenharmony_ci	}
92362306a36Sopenharmony_ci	if (hiStatus & IF_SPI_HIST_CARD_EVENT)
92462306a36Sopenharmony_ci		if_spi_e2h(card);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_cierr:
92762306a36Sopenharmony_ci	if (err)
92862306a36Sopenharmony_ci		netdev_err(priv->dev, "%s: got error %d\n", __func__, err);
92962306a36Sopenharmony_ci}
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci/*
93262306a36Sopenharmony_ci * Host to Card
93362306a36Sopenharmony_ci *
93462306a36Sopenharmony_ci * Called from Libertas to transfer some data to the WLAN device
93562306a36Sopenharmony_ci * We can't sleep here.
93662306a36Sopenharmony_ci */
93762306a36Sopenharmony_cistatic int if_spi_host_to_card(struct lbs_private *priv,
93862306a36Sopenharmony_ci				u8 type, u8 *buf, u16 nb)
93962306a36Sopenharmony_ci{
94062306a36Sopenharmony_ci	int err = 0;
94162306a36Sopenharmony_ci	unsigned long flags;
94262306a36Sopenharmony_ci	struct if_spi_card *card = priv->card;
94362306a36Sopenharmony_ci	struct if_spi_packet *packet;
94462306a36Sopenharmony_ci	u16 blen;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	if (nb == 0) {
94762306a36Sopenharmony_ci		netdev_err(priv->dev, "%s: invalid size requested: %d\n",
94862306a36Sopenharmony_ci			   __func__, nb);
94962306a36Sopenharmony_ci		err = -EINVAL;
95062306a36Sopenharmony_ci		goto out;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci	blen = ALIGN(nb, 4);
95362306a36Sopenharmony_ci	packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC);
95462306a36Sopenharmony_ci	if (!packet) {
95562306a36Sopenharmony_ci		err = -ENOMEM;
95662306a36Sopenharmony_ci		goto out;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci	packet->blen = blen;
95962306a36Sopenharmony_ci	memcpy(packet->buffer, buf, nb);
96062306a36Sopenharmony_ci	memset(packet->buffer + nb, 0, blen - nb);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	switch (type) {
96362306a36Sopenharmony_ci	case MVMS_CMD:
96462306a36Sopenharmony_ci		priv->dnld_sent = DNLD_CMD_SENT;
96562306a36Sopenharmony_ci		spin_lock_irqsave(&card->buffer_lock, flags);
96662306a36Sopenharmony_ci		list_add_tail(&packet->list, &card->cmd_packet_list);
96762306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->buffer_lock, flags);
96862306a36Sopenharmony_ci		break;
96962306a36Sopenharmony_ci	case MVMS_DAT:
97062306a36Sopenharmony_ci		priv->dnld_sent = DNLD_DATA_SENT;
97162306a36Sopenharmony_ci		spin_lock_irqsave(&card->buffer_lock, flags);
97262306a36Sopenharmony_ci		list_add_tail(&packet->list, &card->data_packet_list);
97362306a36Sopenharmony_ci		spin_unlock_irqrestore(&card->buffer_lock, flags);
97462306a36Sopenharmony_ci		break;
97562306a36Sopenharmony_ci	default:
97662306a36Sopenharmony_ci		kfree(packet);
97762306a36Sopenharmony_ci		netdev_err(priv->dev, "can't transfer buffer of type %d\n",
97862306a36Sopenharmony_ci			   type);
97962306a36Sopenharmony_ci		err = -EINVAL;
98062306a36Sopenharmony_ci		break;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	/* Queue spi xfer work */
98462306a36Sopenharmony_ci	queue_work(card->workqueue, &card->packet_work);
98562306a36Sopenharmony_ciout:
98662306a36Sopenharmony_ci	return err;
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci/*
99062306a36Sopenharmony_ci * Host Interrupts
99162306a36Sopenharmony_ci *
99262306a36Sopenharmony_ci * Service incoming interrupts from the WLAN device. We can't sleep here, so
99362306a36Sopenharmony_ci * don't try to talk on the SPI bus, just queue the SPI xfer work.
99462306a36Sopenharmony_ci */
99562306a36Sopenharmony_cistatic irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	struct if_spi_card *card = dev_id;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	queue_work(card->workqueue, &card->packet_work);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	return IRQ_HANDLED;
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci/*
100562306a36Sopenharmony_ci * SPI callbacks
100662306a36Sopenharmony_ci */
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic int if_spi_init_card(struct if_spi_card *card)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	struct lbs_private *priv = card->priv;
101162306a36Sopenharmony_ci	int err, i;
101262306a36Sopenharmony_ci	u32 scratch;
101362306a36Sopenharmony_ci	const struct firmware *helper = NULL;
101462306a36Sopenharmony_ci	const struct firmware *mainfw = NULL;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	err = spu_init(card, card->pdata->use_dummy_writes);
101762306a36Sopenharmony_ci	if (err)
101862306a36Sopenharmony_ci		goto out;
101962306a36Sopenharmony_ci	err = spu_get_chip_revision(card, &card->card_id, &card->card_rev);
102062306a36Sopenharmony_ci	if (err)
102162306a36Sopenharmony_ci		goto out;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch);
102462306a36Sopenharmony_ci	if (err)
102562306a36Sopenharmony_ci		goto out;
102662306a36Sopenharmony_ci	if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC)
102762306a36Sopenharmony_ci		lbs_deb_spi("Firmware is already loaded for "
102862306a36Sopenharmony_ci			    "Marvell WLAN 802.11 adapter\n");
102962306a36Sopenharmony_ci	else {
103062306a36Sopenharmony_ci		/* Check if we support this card */
103162306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
103262306a36Sopenharmony_ci			if (card->card_id == fw_table[i].model)
103362306a36Sopenharmony_ci				break;
103462306a36Sopenharmony_ci		}
103562306a36Sopenharmony_ci		if (i == ARRAY_SIZE(fw_table)) {
103662306a36Sopenharmony_ci			netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n",
103762306a36Sopenharmony_ci				   card->card_id);
103862306a36Sopenharmony_ci			err = -ENODEV;
103962306a36Sopenharmony_ci			goto out;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		err = lbs_get_firmware(&card->spi->dev, card->card_id,
104362306a36Sopenharmony_ci					&fw_table[0], &helper, &mainfw);
104462306a36Sopenharmony_ci		if (err) {
104562306a36Sopenharmony_ci			netdev_err(priv->dev, "failed to find firmware (%d)\n",
104662306a36Sopenharmony_ci				   err);
104762306a36Sopenharmony_ci			goto out;
104862306a36Sopenharmony_ci		}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci		lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter "
105162306a36Sopenharmony_ci				"(chip_id = 0x%04x, chip_rev = 0x%02x) "
105262306a36Sopenharmony_ci				"attached to SPI bus_num %d, chip_select %d. "
105362306a36Sopenharmony_ci				"spi->max_speed_hz=%d\n",
105462306a36Sopenharmony_ci				card->card_id, card->card_rev,
105562306a36Sopenharmony_ci				card->spi->master->bus_num,
105662306a36Sopenharmony_ci				spi_get_chipselect(card->spi, 0),
105762306a36Sopenharmony_ci				card->spi->max_speed_hz);
105862306a36Sopenharmony_ci		err = if_spi_prog_helper_firmware(card, helper);
105962306a36Sopenharmony_ci		if (err)
106062306a36Sopenharmony_ci			goto out;
106162306a36Sopenharmony_ci		err = if_spi_prog_main_firmware(card, mainfw);
106262306a36Sopenharmony_ci		if (err)
106362306a36Sopenharmony_ci			goto out;
106462306a36Sopenharmony_ci		lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n");
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	err = spu_set_interrupt_mode(card, 0, 1);
106862306a36Sopenharmony_ci	if (err)
106962306a36Sopenharmony_ci		goto out;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ciout:
107262306a36Sopenharmony_ci	return err;
107362306a36Sopenharmony_ci}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_cistatic void if_spi_resume_worker(struct work_struct *work)
107662306a36Sopenharmony_ci{
107762306a36Sopenharmony_ci	struct if_spi_card *card;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	card = container_of(work, struct if_spi_card, resume_work);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (card->suspended) {
108262306a36Sopenharmony_ci		if (card->pdata->setup)
108362306a36Sopenharmony_ci			card->pdata->setup(card->spi);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		/* Init card ... */
108662306a36Sopenharmony_ci		if_spi_init_card(card);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci		enable_irq(card->spi->irq);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		/* And resume it ... */
109162306a36Sopenharmony_ci		lbs_resume(card->priv);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		card->suspended = 0;
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic int if_spi_probe(struct spi_device *spi)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	struct if_spi_card *card;
110062306a36Sopenharmony_ci	struct lbs_private *priv = NULL;
110162306a36Sopenharmony_ci	struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev);
110262306a36Sopenharmony_ci	int err = 0;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	if (!pdata) {
110562306a36Sopenharmony_ci		err = -EINVAL;
110662306a36Sopenharmony_ci		goto out;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	if (pdata->setup) {
111062306a36Sopenharmony_ci		err = pdata->setup(spi);
111162306a36Sopenharmony_ci		if (err)
111262306a36Sopenharmony_ci			goto out;
111362306a36Sopenharmony_ci	}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	/* Allocate card structure to represent this specific device */
111662306a36Sopenharmony_ci	card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL);
111762306a36Sopenharmony_ci	if (!card) {
111862306a36Sopenharmony_ci		err = -ENOMEM;
111962306a36Sopenharmony_ci		goto teardown;
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci	spi_set_drvdata(spi, card);
112262306a36Sopenharmony_ci	card->pdata = pdata;
112362306a36Sopenharmony_ci	card->spi = spi;
112462306a36Sopenharmony_ci	card->prev_xfer_time = jiffies;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	INIT_LIST_HEAD(&card->cmd_packet_list);
112762306a36Sopenharmony_ci	INIT_LIST_HEAD(&card->data_packet_list);
112862306a36Sopenharmony_ci	spin_lock_init(&card->buffer_lock);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	/* Initialize the SPI Interface Unit */
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	/* Firmware load */
113362306a36Sopenharmony_ci	err = if_spi_init_card(card);
113462306a36Sopenharmony_ci	if (err)
113562306a36Sopenharmony_ci		goto free_card;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	/*
113862306a36Sopenharmony_ci	 * Register our card with libertas.
113962306a36Sopenharmony_ci	 * This will call alloc_etherdev.
114062306a36Sopenharmony_ci	 */
114162306a36Sopenharmony_ci	priv = lbs_add_card(card, &spi->dev);
114262306a36Sopenharmony_ci	if (IS_ERR(priv)) {
114362306a36Sopenharmony_ci		err = PTR_ERR(priv);
114462306a36Sopenharmony_ci		goto free_card;
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci	card->priv = priv;
114762306a36Sopenharmony_ci	priv->setup_fw_on_resume = 1;
114862306a36Sopenharmony_ci	priv->card = card;
114962306a36Sopenharmony_ci	priv->hw_host_to_card = if_spi_host_to_card;
115062306a36Sopenharmony_ci	priv->enter_deep_sleep = NULL;
115162306a36Sopenharmony_ci	priv->exit_deep_sleep = NULL;
115262306a36Sopenharmony_ci	priv->reset_deep_sleep_wakeup = NULL;
115362306a36Sopenharmony_ci	priv->fw_ready = 1;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	/* Initialize interrupt handling stuff. */
115662306a36Sopenharmony_ci	card->workqueue = alloc_workqueue("libertas_spi", WQ_MEM_RECLAIM, 0);
115762306a36Sopenharmony_ci	if (!card->workqueue) {
115862306a36Sopenharmony_ci		err = -ENOMEM;
115962306a36Sopenharmony_ci		goto remove_card;
116062306a36Sopenharmony_ci	}
116162306a36Sopenharmony_ci	INIT_WORK(&card->packet_work, if_spi_host_to_card_worker);
116262306a36Sopenharmony_ci	INIT_WORK(&card->resume_work, if_spi_resume_worker);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	err = request_irq(spi->irq, if_spi_host_interrupt,
116562306a36Sopenharmony_ci			IRQF_TRIGGER_FALLING, "libertas_spi", card);
116662306a36Sopenharmony_ci	if (err) {
116762306a36Sopenharmony_ci		pr_err("can't get host irq line-- request_irq failed\n");
116862306a36Sopenharmony_ci		goto terminate_workqueue;
116962306a36Sopenharmony_ci	}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	/*
117262306a36Sopenharmony_ci	 * Start the card.
117362306a36Sopenharmony_ci	 * This will call register_netdev, and we'll start
117462306a36Sopenharmony_ci	 * getting interrupts...
117562306a36Sopenharmony_ci	 */
117662306a36Sopenharmony_ci	err = lbs_start_card(priv);
117762306a36Sopenharmony_ci	if (err)
117862306a36Sopenharmony_ci		goto release_irq;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	lbs_deb_spi("Finished initializing WLAN module.\n");
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	/* successful exit */
118362306a36Sopenharmony_ci	goto out;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_cirelease_irq:
118662306a36Sopenharmony_ci	free_irq(spi->irq, card);
118762306a36Sopenharmony_citerminate_workqueue:
118862306a36Sopenharmony_ci	destroy_workqueue(card->workqueue);
118962306a36Sopenharmony_ciremove_card:
119062306a36Sopenharmony_ci	lbs_remove_card(priv); /* will call free_netdev */
119162306a36Sopenharmony_cifree_card:
119262306a36Sopenharmony_ci	free_if_spi_card(card);
119362306a36Sopenharmony_citeardown:
119462306a36Sopenharmony_ci	if (pdata->teardown)
119562306a36Sopenharmony_ci		pdata->teardown(spi);
119662306a36Sopenharmony_ciout:
119762306a36Sopenharmony_ci	return err;
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_cistatic void libertas_spi_remove(struct spi_device *spi)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	struct if_spi_card *card = spi_get_drvdata(spi);
120362306a36Sopenharmony_ci	struct lbs_private *priv = card->priv;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	lbs_deb_spi("libertas_spi_remove\n");
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	cancel_work_sync(&card->resume_work);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	lbs_stop_card(priv);
121062306a36Sopenharmony_ci	lbs_remove_card(priv); /* will call free_netdev */
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	free_irq(spi->irq, card);
121362306a36Sopenharmony_ci	destroy_workqueue(card->workqueue);
121462306a36Sopenharmony_ci	if (card->pdata->teardown)
121562306a36Sopenharmony_ci		card->pdata->teardown(spi);
121662306a36Sopenharmony_ci	free_if_spi_card(card);
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_cistatic int if_spi_suspend(struct device *dev)
122062306a36Sopenharmony_ci{
122162306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
122262306a36Sopenharmony_ci	struct if_spi_card *card = spi_get_drvdata(spi);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (!card->suspended) {
122562306a36Sopenharmony_ci		lbs_suspend(card->priv);
122662306a36Sopenharmony_ci		flush_workqueue(card->workqueue);
122762306a36Sopenharmony_ci		disable_irq(spi->irq);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci		if (card->pdata->teardown)
123062306a36Sopenharmony_ci			card->pdata->teardown(spi);
123162306a36Sopenharmony_ci		card->suspended = 1;
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	return 0;
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic int if_spi_resume(struct device *dev)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
124062306a36Sopenharmony_ci	struct if_spi_card *card = spi_get_drvdata(spi);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	/* Schedule delayed work */
124362306a36Sopenharmony_ci	schedule_work(&card->resume_work);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	return 0;
124662306a36Sopenharmony_ci}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_cistatic const struct dev_pm_ops if_spi_pm_ops = {
124962306a36Sopenharmony_ci	.suspend	= if_spi_suspend,
125062306a36Sopenharmony_ci	.resume		= if_spi_resume,
125162306a36Sopenharmony_ci};
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cistatic struct spi_driver libertas_spi_driver = {
125462306a36Sopenharmony_ci	.probe	= if_spi_probe,
125562306a36Sopenharmony_ci	.remove = libertas_spi_remove,
125662306a36Sopenharmony_ci	.driver = {
125762306a36Sopenharmony_ci		.name	= "libertas_spi",
125862306a36Sopenharmony_ci		.pm	= &if_spi_pm_ops,
125962306a36Sopenharmony_ci	},
126062306a36Sopenharmony_ci};
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci/*
126362306a36Sopenharmony_ci * Module functions
126462306a36Sopenharmony_ci */
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_cistatic int __init if_spi_init_module(void)
126762306a36Sopenharmony_ci{
126862306a36Sopenharmony_ci	int ret = 0;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	printk(KERN_INFO "libertas_spi: Libertas SPI driver\n");
127162306a36Sopenharmony_ci	ret = spi_register_driver(&libertas_spi_driver);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	return ret;
127462306a36Sopenharmony_ci}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_cistatic void __exit if_spi_exit_module(void)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	spi_unregister_driver(&libertas_spi_driver);
127962306a36Sopenharmony_ci}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_cimodule_init(if_spi_init_module);
128262306a36Sopenharmony_cimodule_exit(if_spi_exit_module);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ciMODULE_DESCRIPTION("Libertas SPI WLAN Driver");
128562306a36Sopenharmony_ciMODULE_AUTHOR("Andrey Yurovsky <andrey@cozybit.com>, "
128662306a36Sopenharmony_ci	      "Colin McCabe <colin@cozybit.com>");
128762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
128862306a36Sopenharmony_ciMODULE_ALIAS("spi:libertas_spi");
1289