18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	linux/drivers/net/wireless/libertas/if_spi.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	Driver for Marvell SPI WLAN cards.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *	Copyright 2008 Analog Devices Inc.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *	Authors:
108c2ecf20Sopenharmony_ci *	Andrey Yurovsky <andrey@cozybit.com>
118c2ecf20Sopenharmony_ci *	Colin McCabe <colin@cozybit.com>
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *	Inspired by if_sdio.c, Copyright 2007-2008 Pierre Ossman
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/hardirq.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/firmware.h>
228c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
238c2ecf20Sopenharmony_ci#include <linux/list.h>
248c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
258c2ecf20Sopenharmony_ci#include <linux/slab.h>
268c2ecf20Sopenharmony_ci#include <linux/spi/libertas_spi.h>
278c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include "host.h"
308c2ecf20Sopenharmony_ci#include "decl.h"
318c2ecf20Sopenharmony_ci#include "defs.h"
328c2ecf20Sopenharmony_ci#include "dev.h"
338c2ecf20Sopenharmony_ci#include "if_spi.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct if_spi_packet {
368c2ecf20Sopenharmony_ci	struct list_head		list;
378c2ecf20Sopenharmony_ci	u16				blen;
388c2ecf20Sopenharmony_ci	u8				buffer[] __aligned(4);
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct if_spi_card {
428c2ecf20Sopenharmony_ci	struct spi_device		*spi;
438c2ecf20Sopenharmony_ci	struct lbs_private		*priv;
448c2ecf20Sopenharmony_ci	struct libertas_spi_platform_data *pdata;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/* The card ID and card revision, as reported by the hardware. */
478c2ecf20Sopenharmony_ci	u16				card_id;
488c2ecf20Sopenharmony_ci	u8				card_rev;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* The last time that we initiated an SPU operation */
518c2ecf20Sopenharmony_ci	unsigned long			prev_xfer_time;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	int				use_dummy_writes;
548c2ecf20Sopenharmony_ci	unsigned long			spu_port_delay;
558c2ecf20Sopenharmony_ci	unsigned long			spu_reg_delay;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* Handles all SPI communication (except for FW load) */
588c2ecf20Sopenharmony_ci	struct workqueue_struct		*workqueue;
598c2ecf20Sopenharmony_ci	struct work_struct		packet_work;
608c2ecf20Sopenharmony_ci	struct work_struct		resume_work;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	u8				cmd_buffer[IF_SPI_CMD_BUF_SIZE];
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/* A buffer of incoming packets from libertas core.
658c2ecf20Sopenharmony_ci	 * Since we can't sleep in hw_host_to_card, we have to buffer
668c2ecf20Sopenharmony_ci	 * them. */
678c2ecf20Sopenharmony_ci	struct list_head		cmd_packet_list;
688c2ecf20Sopenharmony_ci	struct list_head		data_packet_list;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* Protects cmd_packet_list and data_packet_list */
718c2ecf20Sopenharmony_ci	spinlock_t			buffer_lock;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* True is card suspended */
748c2ecf20Sopenharmony_ci	u8				suspended;
758c2ecf20Sopenharmony_ci};
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic void free_if_spi_card(struct if_spi_card *card)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct list_head *cursor, *next;
808c2ecf20Sopenharmony_ci	struct if_spi_packet *packet;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	list_for_each_safe(cursor, next, &card->cmd_packet_list) {
838c2ecf20Sopenharmony_ci		packet = container_of(cursor, struct if_spi_packet, list);
848c2ecf20Sopenharmony_ci		list_del(&packet->list);
858c2ecf20Sopenharmony_ci		kfree(packet);
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	list_for_each_safe(cursor, next, &card->data_packet_list) {
888c2ecf20Sopenharmony_ci		packet = container_of(cursor, struct if_spi_packet, list);
898c2ecf20Sopenharmony_ci		list_del(&packet->list);
908c2ecf20Sopenharmony_ci		kfree(packet);
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci	kfree(card);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define MODEL_8385	0x04
968c2ecf20Sopenharmony_ci#define MODEL_8686	0x0b
978c2ecf20Sopenharmony_ci#define MODEL_8688	0x10
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic const struct lbs_fw_table fw_table[] = {
1008c2ecf20Sopenharmony_ci	{ MODEL_8385, "libertas/gspi8385_helper.bin", "libertas/gspi8385.bin" },
1018c2ecf20Sopenharmony_ci	{ MODEL_8385, "libertas/gspi8385_hlp.bin", "libertas/gspi8385.bin" },
1028c2ecf20Sopenharmony_ci	{ MODEL_8686, "libertas/gspi8686_v9_helper.bin", "libertas/gspi8686_v9.bin" },
1038c2ecf20Sopenharmony_ci	{ MODEL_8686, "libertas/gspi8686_hlp.bin", "libertas/gspi8686.bin" },
1048c2ecf20Sopenharmony_ci	{ MODEL_8688, "libertas/gspi8688_helper.bin", "libertas/gspi8688.bin" },
1058c2ecf20Sopenharmony_ci	{ 0, NULL, NULL }
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8385_helper.bin");
1088c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8385_hlp.bin");
1098c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8385.bin");
1108c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686_v9_helper.bin");
1118c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686_v9.bin");
1128c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686_hlp.bin");
1138c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8686.bin");
1148c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8688_helper.bin");
1158c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/gspi8688.bin");
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/*
1198c2ecf20Sopenharmony_ci * SPI Interface Unit Routines
1208c2ecf20Sopenharmony_ci *
1218c2ecf20Sopenharmony_ci * The SPU sits between the host and the WLAN module.
1228c2ecf20Sopenharmony_ci * All communication with the firmware is through SPU transactions.
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * First we have to put a SPU register name on the bus. Then we can
1258c2ecf20Sopenharmony_ci * either read from or write to that register.
1268c2ecf20Sopenharmony_ci *
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic void spu_transaction_init(struct if_spi_card *card)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	if (!time_after(jiffies, card->prev_xfer_time + 1)) {
1328c2ecf20Sopenharmony_ci		/* Unfortunately, the SPU requires a delay between successive
1338c2ecf20Sopenharmony_ci		 * transactions. If our last transaction was more than a jiffy
1348c2ecf20Sopenharmony_ci		 * ago, we have obviously already delayed enough.
1358c2ecf20Sopenharmony_ci		 * If not, we have to busy-wait to be on the safe side. */
1368c2ecf20Sopenharmony_ci		ndelay(400);
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void spu_transaction_finish(struct if_spi_card *card)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	card->prev_xfer_time = jiffies;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/*
1468c2ecf20Sopenharmony_ci * Write out a byte buffer to an SPI register,
1478c2ecf20Sopenharmony_ci * using a series of 16-bit transfers.
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_cistatic int spu_write(struct if_spi_card *card, u16 reg, const u8 *buf, int len)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	int err = 0;
1528c2ecf20Sopenharmony_ci	__le16 reg_out = cpu_to_le16(reg | IF_SPI_WRITE_OPERATION_MASK);
1538c2ecf20Sopenharmony_ci	struct spi_message m;
1548c2ecf20Sopenharmony_ci	struct spi_transfer reg_trans;
1558c2ecf20Sopenharmony_ci	struct spi_transfer data_trans;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	spi_message_init(&m);
1588c2ecf20Sopenharmony_ci	memset(&reg_trans, 0, sizeof(reg_trans));
1598c2ecf20Sopenharmony_ci	memset(&data_trans, 0, sizeof(data_trans));
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* You must give an even number of bytes to the SPU, even if it
1628c2ecf20Sopenharmony_ci	 * doesn't care about the last one.  */
1638c2ecf20Sopenharmony_ci	BUG_ON(len & 0x1);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	spu_transaction_init(card);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* write SPU register index */
1688c2ecf20Sopenharmony_ci	reg_trans.tx_buf = &reg_out;
1698c2ecf20Sopenharmony_ci	reg_trans.len = sizeof(reg_out);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	data_trans.tx_buf = buf;
1728c2ecf20Sopenharmony_ci	data_trans.len = len;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	spi_message_add_tail(&reg_trans, &m);
1758c2ecf20Sopenharmony_ci	spi_message_add_tail(&data_trans, &m);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	err = spi_sync(card->spi, &m);
1788c2ecf20Sopenharmony_ci	spu_transaction_finish(card);
1798c2ecf20Sopenharmony_ci	return err;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic inline int spu_write_u16(struct if_spi_card *card, u16 reg, u16 val)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	__le16 buff;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	buff = cpu_to_le16(val);
1878c2ecf20Sopenharmony_ci	return spu_write(card, reg, (u8 *)&buff, sizeof(u16));
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic inline int spu_reg_is_port_reg(u16 reg)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	switch (reg) {
1938c2ecf20Sopenharmony_ci	case IF_SPI_IO_RDWRPORT_REG:
1948c2ecf20Sopenharmony_ci	case IF_SPI_CMD_RDWRPORT_REG:
1958c2ecf20Sopenharmony_ci	case IF_SPI_DATA_RDWRPORT_REG:
1968c2ecf20Sopenharmony_ci		return 1;
1978c2ecf20Sopenharmony_ci	default:
1988c2ecf20Sopenharmony_ci		return 0;
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic int spu_read(struct if_spi_card *card, u16 reg, u8 *buf, int len)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	unsigned int delay;
2058c2ecf20Sopenharmony_ci	int err = 0;
2068c2ecf20Sopenharmony_ci	__le16 reg_out = cpu_to_le16(reg | IF_SPI_READ_OPERATION_MASK);
2078c2ecf20Sopenharmony_ci	struct spi_message m;
2088c2ecf20Sopenharmony_ci	struct spi_transfer reg_trans;
2098c2ecf20Sopenharmony_ci	struct spi_transfer dummy_trans;
2108c2ecf20Sopenharmony_ci	struct spi_transfer data_trans;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * You must take an even number of bytes from the SPU, even if you
2148c2ecf20Sopenharmony_ci	 * don't care about the last one.
2158c2ecf20Sopenharmony_ci	 */
2168c2ecf20Sopenharmony_ci	BUG_ON(len & 0x1);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	spu_transaction_init(card);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	spi_message_init(&m);
2218c2ecf20Sopenharmony_ci	memset(&reg_trans, 0, sizeof(reg_trans));
2228c2ecf20Sopenharmony_ci	memset(&dummy_trans, 0, sizeof(dummy_trans));
2238c2ecf20Sopenharmony_ci	memset(&data_trans, 0, sizeof(data_trans));
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* write SPU register index */
2268c2ecf20Sopenharmony_ci	reg_trans.tx_buf = &reg_out;
2278c2ecf20Sopenharmony_ci	reg_trans.len = sizeof(reg_out);
2288c2ecf20Sopenharmony_ci	spi_message_add_tail(&reg_trans, &m);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	delay = spu_reg_is_port_reg(reg) ? card->spu_port_delay :
2318c2ecf20Sopenharmony_ci						card->spu_reg_delay;
2328c2ecf20Sopenharmony_ci	if (card->use_dummy_writes) {
2338c2ecf20Sopenharmony_ci		/* Clock in dummy cycles while the SPU fills the FIFO */
2348c2ecf20Sopenharmony_ci		dummy_trans.len = delay / 8;
2358c2ecf20Sopenharmony_ci		spi_message_add_tail(&dummy_trans, &m);
2368c2ecf20Sopenharmony_ci	} else {
2378c2ecf20Sopenharmony_ci		/* Busy-wait while the SPU fills the FIFO */
2388c2ecf20Sopenharmony_ci		reg_trans.delay.value =
2398c2ecf20Sopenharmony_ci			DIV_ROUND_UP((100 + (delay * 10)), 1000);
2408c2ecf20Sopenharmony_ci		reg_trans.delay.unit = SPI_DELAY_UNIT_USECS;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/* read in data */
2448c2ecf20Sopenharmony_ci	data_trans.rx_buf = buf;
2458c2ecf20Sopenharmony_ci	data_trans.len = len;
2468c2ecf20Sopenharmony_ci	spi_message_add_tail(&data_trans, &m);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	err = spi_sync(card->spi, &m);
2498c2ecf20Sopenharmony_ci	spu_transaction_finish(card);
2508c2ecf20Sopenharmony_ci	return err;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/* Read 16 bits from an SPI register */
2548c2ecf20Sopenharmony_cistatic inline int spu_read_u16(struct if_spi_card *card, u16 reg, u16 *val)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	__le16 buf;
2578c2ecf20Sopenharmony_ci	int ret;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	ret = spu_read(card, reg, (u8 *)&buf, sizeof(buf));
2608c2ecf20Sopenharmony_ci	if (ret == 0)
2618c2ecf20Sopenharmony_ci		*val = le16_to_cpup(&buf);
2628c2ecf20Sopenharmony_ci	return ret;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci/*
2668c2ecf20Sopenharmony_ci * Read 32 bits from an SPI register.
2678c2ecf20Sopenharmony_ci * The low 16 bits are read first.
2688c2ecf20Sopenharmony_ci */
2698c2ecf20Sopenharmony_cistatic int spu_read_u32(struct if_spi_card *card, u16 reg, u32 *val)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	__le32 buf;
2728c2ecf20Sopenharmony_ci	int err;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	err = spu_read(card, reg, (u8 *)&buf, sizeof(buf));
2758c2ecf20Sopenharmony_ci	if (!err)
2768c2ecf20Sopenharmony_ci		*val = le32_to_cpup(&buf);
2778c2ecf20Sopenharmony_ci	return err;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci/*
2818c2ecf20Sopenharmony_ci * Keep reading 16 bits from an SPI register until you get the correct result.
2828c2ecf20Sopenharmony_ci *
2838c2ecf20Sopenharmony_ci * If mask = 0, the correct result is any non-zero number.
2848c2ecf20Sopenharmony_ci * If mask != 0, the correct result is any number where
2858c2ecf20Sopenharmony_ci * number & target_mask == target
2868c2ecf20Sopenharmony_ci *
2878c2ecf20Sopenharmony_ci * Returns -ETIMEDOUT if a second passes without the correct result.
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_cistatic int spu_wait_for_u16(struct if_spi_card *card, u16 reg,
2908c2ecf20Sopenharmony_ci			u16 target_mask, u16 target)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	int err;
2938c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + 5*HZ;
2948c2ecf20Sopenharmony_ci	while (1) {
2958c2ecf20Sopenharmony_ci		u16 val;
2968c2ecf20Sopenharmony_ci		err = spu_read_u16(card, reg, &val);
2978c2ecf20Sopenharmony_ci		if (err)
2988c2ecf20Sopenharmony_ci			return err;
2998c2ecf20Sopenharmony_ci		if (target_mask) {
3008c2ecf20Sopenharmony_ci			if ((val & target_mask) == target)
3018c2ecf20Sopenharmony_ci				return 0;
3028c2ecf20Sopenharmony_ci		} else {
3038c2ecf20Sopenharmony_ci			if (val)
3048c2ecf20Sopenharmony_ci				return 0;
3058c2ecf20Sopenharmony_ci		}
3068c2ecf20Sopenharmony_ci		udelay(100);
3078c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
3088c2ecf20Sopenharmony_ci			pr_err("%s: timeout with val=%02x, target_mask=%02x, target=%02x\n",
3098c2ecf20Sopenharmony_ci			       __func__, val, target_mask, target);
3108c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
3118c2ecf20Sopenharmony_ci		}
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/*
3168c2ecf20Sopenharmony_ci * Read 16 bits from an SPI register until you receive a specific value.
3178c2ecf20Sopenharmony_ci * Returns -ETIMEDOUT if a 4 tries pass without success.
3188c2ecf20Sopenharmony_ci */
3198c2ecf20Sopenharmony_cistatic int spu_wait_for_u32(struct if_spi_card *card, u32 reg, u32 target)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	int err, try;
3228c2ecf20Sopenharmony_ci	for (try = 0; try < 4; ++try) {
3238c2ecf20Sopenharmony_ci		u32 val = 0;
3248c2ecf20Sopenharmony_ci		err = spu_read_u32(card, reg, &val);
3258c2ecf20Sopenharmony_ci		if (err)
3268c2ecf20Sopenharmony_ci			return err;
3278c2ecf20Sopenharmony_ci		if (val == target)
3288c2ecf20Sopenharmony_ci			return 0;
3298c2ecf20Sopenharmony_ci		mdelay(100);
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_cistatic int spu_set_interrupt_mode(struct if_spi_card *card,
3358c2ecf20Sopenharmony_ci			   int suppress_host_int,
3368c2ecf20Sopenharmony_ci			   int auto_int)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	int err = 0;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/*
3418c2ecf20Sopenharmony_ci	 * We can suppress a host interrupt by clearing the appropriate
3428c2ecf20Sopenharmony_ci	 * bit in the "host interrupt status mask" register
3438c2ecf20Sopenharmony_ci	 */
3448c2ecf20Sopenharmony_ci	if (suppress_host_int) {
3458c2ecf20Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
3468c2ecf20Sopenharmony_ci		if (err)
3478c2ecf20Sopenharmony_ci			return err;
3488c2ecf20Sopenharmony_ci	} else {
3498c2ecf20Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG,
3508c2ecf20Sopenharmony_ci			      IF_SPI_HISM_TX_DOWNLOAD_RDY |
3518c2ecf20Sopenharmony_ci			      IF_SPI_HISM_RX_UPLOAD_RDY |
3528c2ecf20Sopenharmony_ci			      IF_SPI_HISM_CMD_DOWNLOAD_RDY |
3538c2ecf20Sopenharmony_ci			      IF_SPI_HISM_CARDEVENT |
3548c2ecf20Sopenharmony_ci			      IF_SPI_HISM_CMD_UPLOAD_RDY);
3558c2ecf20Sopenharmony_ci		if (err)
3568c2ecf20Sopenharmony_ci			return err;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/*
3608c2ecf20Sopenharmony_ci	 * If auto-interrupts are on, the completion of certain transactions
3618c2ecf20Sopenharmony_ci	 * will trigger an interrupt automatically. If auto-interrupts
3628c2ecf20Sopenharmony_ci	 * are off, we need to set the "Card Interrupt Cause" register to
3638c2ecf20Sopenharmony_ci	 * trigger a card interrupt.
3648c2ecf20Sopenharmony_ci	 */
3658c2ecf20Sopenharmony_ci	if (auto_int) {
3668c2ecf20Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_CTRL_REG,
3678c2ecf20Sopenharmony_ci				IF_SPI_HICT_TX_DOWNLOAD_OVER_AUTO |
3688c2ecf20Sopenharmony_ci				IF_SPI_HICT_RX_UPLOAD_OVER_AUTO |
3698c2ecf20Sopenharmony_ci				IF_SPI_HICT_CMD_DOWNLOAD_OVER_AUTO |
3708c2ecf20Sopenharmony_ci				IF_SPI_HICT_CMD_UPLOAD_OVER_AUTO);
3718c2ecf20Sopenharmony_ci		if (err)
3728c2ecf20Sopenharmony_ci			return err;
3738c2ecf20Sopenharmony_ci	} else {
3748c2ecf20Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_MASK_REG, 0);
3758c2ecf20Sopenharmony_ci		if (err)
3768c2ecf20Sopenharmony_ci			return err;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci	return err;
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic int spu_get_chip_revision(struct if_spi_card *card,
3828c2ecf20Sopenharmony_ci				  u16 *card_id, u8 *card_rev)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	int err = 0;
3858c2ecf20Sopenharmony_ci	u32 dev_ctrl;
3868c2ecf20Sopenharmony_ci	err = spu_read_u32(card, IF_SPI_DEVICEID_CTRL_REG, &dev_ctrl);
3878c2ecf20Sopenharmony_ci	if (err)
3888c2ecf20Sopenharmony_ci		return err;
3898c2ecf20Sopenharmony_ci	*card_id = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_ID(dev_ctrl);
3908c2ecf20Sopenharmony_ci	*card_rev = IF_SPI_DEVICEID_CTRL_REG_TO_CARD_REV(dev_ctrl);
3918c2ecf20Sopenharmony_ci	return err;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int spu_set_bus_mode(struct if_spi_card *card, u16 mode)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	int err = 0;
3978c2ecf20Sopenharmony_ci	u16 rval;
3988c2ecf20Sopenharmony_ci	/* set bus mode */
3998c2ecf20Sopenharmony_ci	err = spu_write_u16(card, IF_SPI_SPU_BUS_MODE_REG, mode);
4008c2ecf20Sopenharmony_ci	if (err)
4018c2ecf20Sopenharmony_ci		return err;
4028c2ecf20Sopenharmony_ci	/* Check that we were able to read back what we just wrote. */
4038c2ecf20Sopenharmony_ci	err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval);
4048c2ecf20Sopenharmony_ci	if (err)
4058c2ecf20Sopenharmony_ci		return err;
4068c2ecf20Sopenharmony_ci	if ((rval & 0xF) != mode) {
4078c2ecf20Sopenharmony_ci		pr_err("Can't read bus mode register\n");
4088c2ecf20Sopenharmony_ci		return -EIO;
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci	return 0;
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic int spu_init(struct if_spi_card *card, int use_dummy_writes)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	int err = 0;
4168c2ecf20Sopenharmony_ci	u32 delay;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/*
4198c2ecf20Sopenharmony_ci	 * We have to start up in timed delay mode so that we can safely
4208c2ecf20Sopenharmony_ci	 * read the Delay Read Register.
4218c2ecf20Sopenharmony_ci	 */
4228c2ecf20Sopenharmony_ci	card->use_dummy_writes = 0;
4238c2ecf20Sopenharmony_ci	err = spu_set_bus_mode(card,
4248c2ecf20Sopenharmony_ci				IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
4258c2ecf20Sopenharmony_ci				IF_SPI_BUS_MODE_DELAY_METHOD_TIMED |
4268c2ecf20Sopenharmony_ci				IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA);
4278c2ecf20Sopenharmony_ci	if (err)
4288c2ecf20Sopenharmony_ci		return err;
4298c2ecf20Sopenharmony_ci	card->spu_port_delay = 1000;
4308c2ecf20Sopenharmony_ci	card->spu_reg_delay = 1000;
4318c2ecf20Sopenharmony_ci	err = spu_read_u32(card, IF_SPI_DELAY_READ_REG, &delay);
4328c2ecf20Sopenharmony_ci	if (err)
4338c2ecf20Sopenharmony_ci		return err;
4348c2ecf20Sopenharmony_ci	card->spu_port_delay = delay & 0x0000ffff;
4358c2ecf20Sopenharmony_ci	card->spu_reg_delay = (delay & 0xffff0000) >> 16;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	/* If dummy clock delay mode has been requested, switch to it now */
4388c2ecf20Sopenharmony_ci	if (use_dummy_writes) {
4398c2ecf20Sopenharmony_ci		card->use_dummy_writes = 1;
4408c2ecf20Sopenharmony_ci		err = spu_set_bus_mode(card,
4418c2ecf20Sopenharmony_ci				IF_SPI_BUS_MODE_SPI_CLOCK_PHASE_RISING |
4428c2ecf20Sopenharmony_ci				IF_SPI_BUS_MODE_DELAY_METHOD_DUMMY_CLOCK |
4438c2ecf20Sopenharmony_ci				IF_SPI_BUS_MODE_16_BIT_ADDRESS_16_BIT_DATA);
4448c2ecf20Sopenharmony_ci		if (err)
4458c2ecf20Sopenharmony_ci			return err;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	lbs_deb_spi("Initialized SPU unit. "
4498c2ecf20Sopenharmony_ci		    "spu_port_delay=0x%04lx, spu_reg_delay=0x%04lx\n",
4508c2ecf20Sopenharmony_ci		    card->spu_port_delay, card->spu_reg_delay);
4518c2ecf20Sopenharmony_ci	return err;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci/*
4558c2ecf20Sopenharmony_ci * Firmware Loading
4568c2ecf20Sopenharmony_ci */
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_cistatic int if_spi_prog_helper_firmware(struct if_spi_card *card,
4598c2ecf20Sopenharmony_ci					const struct firmware *firmware)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	int err = 0;
4628c2ecf20Sopenharmony_ci	int bytes_remaining;
4638c2ecf20Sopenharmony_ci	const u8 *fw;
4648c2ecf20Sopenharmony_ci	u8 temp[HELPER_FW_LOAD_CHUNK_SZ];
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	err = spu_set_interrupt_mode(card, 1, 0);
4678c2ecf20Sopenharmony_ci	if (err)
4688c2ecf20Sopenharmony_ci		goto out;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	bytes_remaining = firmware->size;
4718c2ecf20Sopenharmony_ci	fw = firmware->data;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Load helper firmware image */
4748c2ecf20Sopenharmony_ci	while (bytes_remaining > 0) {
4758c2ecf20Sopenharmony_ci		/*
4768c2ecf20Sopenharmony_ci		 * Scratch pad 1 should contain the number of bytes we
4778c2ecf20Sopenharmony_ci		 * want to download to the firmware
4788c2ecf20Sopenharmony_ci		 */
4798c2ecf20Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG,
4808c2ecf20Sopenharmony_ci					HELPER_FW_LOAD_CHUNK_SZ);
4818c2ecf20Sopenharmony_ci		if (err)
4828c2ecf20Sopenharmony_ci			goto out;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
4858c2ecf20Sopenharmony_ci					IF_SPI_HIST_CMD_DOWNLOAD_RDY,
4868c2ecf20Sopenharmony_ci					IF_SPI_HIST_CMD_DOWNLOAD_RDY);
4878c2ecf20Sopenharmony_ci		if (err)
4888c2ecf20Sopenharmony_ci			goto out;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		/*
4918c2ecf20Sopenharmony_ci		 * Feed the data into the command read/write port reg
4928c2ecf20Sopenharmony_ci		 * in chunks of 64 bytes
4938c2ecf20Sopenharmony_ci		 */
4948c2ecf20Sopenharmony_ci		memset(temp, 0, sizeof(temp));
4958c2ecf20Sopenharmony_ci		memcpy(temp, fw,
4968c2ecf20Sopenharmony_ci		       min(bytes_remaining, HELPER_FW_LOAD_CHUNK_SZ));
4978c2ecf20Sopenharmony_ci		mdelay(10);
4988c2ecf20Sopenharmony_ci		err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
4998c2ecf20Sopenharmony_ci					temp, HELPER_FW_LOAD_CHUNK_SZ);
5008c2ecf20Sopenharmony_ci		if (err)
5018c2ecf20Sopenharmony_ci			goto out;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci		/* Interrupt the boot code */
5048c2ecf20Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
5058c2ecf20Sopenharmony_ci		if (err)
5068c2ecf20Sopenharmony_ci			goto out;
5078c2ecf20Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
5088c2ecf20Sopenharmony_ci				       IF_SPI_CIC_CMD_DOWNLOAD_OVER);
5098c2ecf20Sopenharmony_ci		if (err)
5108c2ecf20Sopenharmony_ci			goto out;
5118c2ecf20Sopenharmony_ci		bytes_remaining -= HELPER_FW_LOAD_CHUNK_SZ;
5128c2ecf20Sopenharmony_ci		fw += HELPER_FW_LOAD_CHUNK_SZ;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	/*
5168c2ecf20Sopenharmony_ci	 * Once the helper / single stage firmware download is complete,
5178c2ecf20Sopenharmony_ci	 * write 0 to scratch pad 1 and interrupt the
5188c2ecf20Sopenharmony_ci	 * bootloader. This completes the helper download.
5198c2ecf20Sopenharmony_ci	 */
5208c2ecf20Sopenharmony_ci	err = spu_write_u16(card, IF_SPI_SCRATCH_1_REG, FIRMWARE_DNLD_OK);
5218c2ecf20Sopenharmony_ci	if (err)
5228c2ecf20Sopenharmony_ci		goto out;
5238c2ecf20Sopenharmony_ci	err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
5248c2ecf20Sopenharmony_ci	if (err)
5258c2ecf20Sopenharmony_ci		goto out;
5268c2ecf20Sopenharmony_ci	err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
5278c2ecf20Sopenharmony_ci				IF_SPI_CIC_CMD_DOWNLOAD_OVER);
5288c2ecf20Sopenharmony_ciout:
5298c2ecf20Sopenharmony_ci	if (err)
5308c2ecf20Sopenharmony_ci		pr_err("failed to load helper firmware (err=%d)\n", err);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return err;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci/*
5368c2ecf20Sopenharmony_ci * Returns the length of the next packet the firmware expects us to send.
5378c2ecf20Sopenharmony_ci * Sets crc_err if the previous transfer had a CRC error.
5388c2ecf20Sopenharmony_ci */
5398c2ecf20Sopenharmony_cistatic int if_spi_prog_main_firmware_check_len(struct if_spi_card *card,
5408c2ecf20Sopenharmony_ci						int *crc_err)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	u16 len;
5438c2ecf20Sopenharmony_ci	int err = 0;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/*
5468c2ecf20Sopenharmony_ci	 * wait until the host interrupt status register indicates
5478c2ecf20Sopenharmony_ci	 * that we are ready to download
5488c2ecf20Sopenharmony_ci	 */
5498c2ecf20Sopenharmony_ci	err = spu_wait_for_u16(card, IF_SPI_HOST_INT_STATUS_REG,
5508c2ecf20Sopenharmony_ci				IF_SPI_HIST_CMD_DOWNLOAD_RDY,
5518c2ecf20Sopenharmony_ci				IF_SPI_HIST_CMD_DOWNLOAD_RDY);
5528c2ecf20Sopenharmony_ci	if (err) {
5538c2ecf20Sopenharmony_ci		pr_err("timed out waiting for host_int_status\n");
5548c2ecf20Sopenharmony_ci		return err;
5558c2ecf20Sopenharmony_ci	}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/* Ask the device how many bytes of firmware it wants. */
5588c2ecf20Sopenharmony_ci	err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
5598c2ecf20Sopenharmony_ci	if (err)
5608c2ecf20Sopenharmony_ci		return err;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (len > IF_SPI_CMD_BUF_SIZE) {
5638c2ecf20Sopenharmony_ci		pr_err("firmware load device requested a larger transfer than we are prepared to handle (len = %d)\n",
5648c2ecf20Sopenharmony_ci		       len);
5658c2ecf20Sopenharmony_ci		return -EIO;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci	if (len & 0x1) {
5688c2ecf20Sopenharmony_ci		lbs_deb_spi("%s: crc error\n", __func__);
5698c2ecf20Sopenharmony_ci		len &= ~0x1;
5708c2ecf20Sopenharmony_ci		*crc_err = 1;
5718c2ecf20Sopenharmony_ci	} else
5728c2ecf20Sopenharmony_ci		*crc_err = 0;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	return len;
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic int if_spi_prog_main_firmware(struct if_spi_card *card,
5788c2ecf20Sopenharmony_ci					const struct firmware *firmware)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	struct lbs_private *priv = card->priv;
5818c2ecf20Sopenharmony_ci	int len, prev_len;
5828c2ecf20Sopenharmony_ci	int bytes, crc_err = 0, err = 0;
5838c2ecf20Sopenharmony_ci	const u8 *fw;
5848c2ecf20Sopenharmony_ci	u16 num_crc_errs;
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	err = spu_set_interrupt_mode(card, 1, 0);
5878c2ecf20Sopenharmony_ci	if (err)
5888c2ecf20Sopenharmony_ci		goto out;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	err = spu_wait_for_u16(card, IF_SPI_SCRATCH_1_REG, 0, 0);
5918c2ecf20Sopenharmony_ci	if (err) {
5928c2ecf20Sopenharmony_ci		netdev_err(priv->dev,
5938c2ecf20Sopenharmony_ci			   "%s: timed out waiting for initial scratch reg = 0\n",
5948c2ecf20Sopenharmony_ci			   __func__);
5958c2ecf20Sopenharmony_ci		goto out;
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	num_crc_errs = 0;
5998c2ecf20Sopenharmony_ci	prev_len = 0;
6008c2ecf20Sopenharmony_ci	bytes = firmware->size;
6018c2ecf20Sopenharmony_ci	fw = firmware->data;
6028c2ecf20Sopenharmony_ci	while ((len = if_spi_prog_main_firmware_check_len(card, &crc_err))) {
6038c2ecf20Sopenharmony_ci		if (len < 0) {
6048c2ecf20Sopenharmony_ci			err = len;
6058c2ecf20Sopenharmony_ci			goto out;
6068c2ecf20Sopenharmony_ci		}
6078c2ecf20Sopenharmony_ci		if (bytes < 0) {
6088c2ecf20Sopenharmony_ci			/*
6098c2ecf20Sopenharmony_ci			 * If there are no more bytes left, we would normally
6108c2ecf20Sopenharmony_ci			 * expect to have terminated with len = 0
6118c2ecf20Sopenharmony_ci			 */
6128c2ecf20Sopenharmony_ci			netdev_err(priv->dev,
6138c2ecf20Sopenharmony_ci				   "Firmware load wants more bytes than we have to offer.\n");
6148c2ecf20Sopenharmony_ci			break;
6158c2ecf20Sopenharmony_ci		}
6168c2ecf20Sopenharmony_ci		if (crc_err) {
6178c2ecf20Sopenharmony_ci			/* Previous transfer failed. */
6188c2ecf20Sopenharmony_ci			if (++num_crc_errs > MAX_MAIN_FW_LOAD_CRC_ERR) {
6198c2ecf20Sopenharmony_ci				pr_err("Too many CRC errors encountered in firmware load.\n");
6208c2ecf20Sopenharmony_ci				err = -EIO;
6218c2ecf20Sopenharmony_ci				goto out;
6228c2ecf20Sopenharmony_ci			}
6238c2ecf20Sopenharmony_ci		} else {
6248c2ecf20Sopenharmony_ci			/* Previous transfer succeeded. Advance counters. */
6258c2ecf20Sopenharmony_ci			bytes -= prev_len;
6268c2ecf20Sopenharmony_ci			fw += prev_len;
6278c2ecf20Sopenharmony_ci		}
6288c2ecf20Sopenharmony_ci		if (bytes < len) {
6298c2ecf20Sopenharmony_ci			memset(card->cmd_buffer, 0, len);
6308c2ecf20Sopenharmony_ci			memcpy(card->cmd_buffer, fw, bytes);
6318c2ecf20Sopenharmony_ci		} else
6328c2ecf20Sopenharmony_ci			memcpy(card->cmd_buffer, fw, len);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG, 0);
6358c2ecf20Sopenharmony_ci		if (err)
6368c2ecf20Sopenharmony_ci			goto out;
6378c2ecf20Sopenharmony_ci		err = spu_write(card, IF_SPI_CMD_RDWRPORT_REG,
6388c2ecf20Sopenharmony_ci				card->cmd_buffer, len);
6398c2ecf20Sopenharmony_ci		if (err)
6408c2ecf20Sopenharmony_ci			goto out;
6418c2ecf20Sopenharmony_ci		err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG ,
6428c2ecf20Sopenharmony_ci					IF_SPI_CIC_CMD_DOWNLOAD_OVER);
6438c2ecf20Sopenharmony_ci		if (err)
6448c2ecf20Sopenharmony_ci			goto out;
6458c2ecf20Sopenharmony_ci		prev_len = len;
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci	if (bytes > prev_len) {
6488c2ecf20Sopenharmony_ci		pr_err("firmware load wants fewer bytes than we have to offer\n");
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	/* Confirm firmware download */
6528c2ecf20Sopenharmony_ci	err = spu_wait_for_u32(card, IF_SPI_SCRATCH_4_REG,
6538c2ecf20Sopenharmony_ci					SUCCESSFUL_FW_DOWNLOAD_MAGIC);
6548c2ecf20Sopenharmony_ci	if (err) {
6558c2ecf20Sopenharmony_ci		pr_err("failed to confirm the firmware download\n");
6568c2ecf20Sopenharmony_ci		goto out;
6578c2ecf20Sopenharmony_ci	}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ciout:
6608c2ecf20Sopenharmony_ci	if (err)
6618c2ecf20Sopenharmony_ci		pr_err("failed to load firmware (err=%d)\n", err);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	return err;
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci/*
6678c2ecf20Sopenharmony_ci * SPI Transfer Thread
6688c2ecf20Sopenharmony_ci *
6698c2ecf20Sopenharmony_ci * The SPI worker handles all SPI transfers, so there is no need for a lock.
6708c2ecf20Sopenharmony_ci */
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci/* Move a command from the card to the host */
6738c2ecf20Sopenharmony_cistatic int if_spi_c2h_cmd(struct if_spi_card *card)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	struct lbs_private *priv = card->priv;
6768c2ecf20Sopenharmony_ci	unsigned long flags;
6778c2ecf20Sopenharmony_ci	int err = 0;
6788c2ecf20Sopenharmony_ci	u16 len;
6798c2ecf20Sopenharmony_ci	u8 i;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	/*
6828c2ecf20Sopenharmony_ci	 * We need a buffer big enough to handle whatever people send to
6838c2ecf20Sopenharmony_ci	 * hw_host_to_card
6848c2ecf20Sopenharmony_ci	 */
6858c2ecf20Sopenharmony_ci	BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_CMD_BUFFER_SIZE);
6868c2ecf20Sopenharmony_ci	BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE < LBS_UPLD_SIZE);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	/*
6898c2ecf20Sopenharmony_ci	 * It's just annoying if the buffer size isn't a multiple of 4, because
6908c2ecf20Sopenharmony_ci	 * then we might have len < IF_SPI_CMD_BUF_SIZE but
6918c2ecf20Sopenharmony_ci	 * ALIGN(len, 4) > IF_SPI_CMD_BUF_SIZE
6928c2ecf20Sopenharmony_ci	 */
6938c2ecf20Sopenharmony_ci	BUILD_BUG_ON(IF_SPI_CMD_BUF_SIZE % 4 != 0);
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/* How many bytes are there to read? */
6968c2ecf20Sopenharmony_ci	err = spu_read_u16(card, IF_SPI_SCRATCH_2_REG, &len);
6978c2ecf20Sopenharmony_ci	if (err)
6988c2ecf20Sopenharmony_ci		goto out;
6998c2ecf20Sopenharmony_ci	if (!len) {
7008c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "%s: error: card has no data for host\n",
7018c2ecf20Sopenharmony_ci			   __func__);
7028c2ecf20Sopenharmony_ci		err = -EINVAL;
7038c2ecf20Sopenharmony_ci		goto out;
7048c2ecf20Sopenharmony_ci	} else if (len > IF_SPI_CMD_BUF_SIZE) {
7058c2ecf20Sopenharmony_ci		netdev_err(priv->dev,
7068c2ecf20Sopenharmony_ci			   "%s: error: response packet too large: %d bytes, but maximum is %d\n",
7078c2ecf20Sopenharmony_ci			   __func__, len, IF_SPI_CMD_BUF_SIZE);
7088c2ecf20Sopenharmony_ci		err = -EINVAL;
7098c2ecf20Sopenharmony_ci		goto out;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	/* Read the data from the WLAN module into our command buffer */
7138c2ecf20Sopenharmony_ci	err = spu_read(card, IF_SPI_CMD_RDWRPORT_REG,
7148c2ecf20Sopenharmony_ci				card->cmd_buffer, ALIGN(len, 4));
7158c2ecf20Sopenharmony_ci	if (err)
7168c2ecf20Sopenharmony_ci		goto out;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->driver_lock, flags);
7198c2ecf20Sopenharmony_ci	i = (priv->resp_idx == 0) ? 1 : 0;
7208c2ecf20Sopenharmony_ci	BUG_ON(priv->resp_len[i]);
7218c2ecf20Sopenharmony_ci	priv->resp_len[i] = len;
7228c2ecf20Sopenharmony_ci	memcpy(priv->resp_buf[i], card->cmd_buffer, len);
7238c2ecf20Sopenharmony_ci	lbs_notify_command_response(priv, i);
7248c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->driver_lock, flags);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ciout:
7278c2ecf20Sopenharmony_ci	if (err)
7288c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "%s: err=%d\n", __func__, err);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	return err;
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci/* Move data from the card to the host */
7348c2ecf20Sopenharmony_cistatic int if_spi_c2h_data(struct if_spi_card *card)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	struct lbs_private *priv = card->priv;
7378c2ecf20Sopenharmony_ci	struct sk_buff *skb;
7388c2ecf20Sopenharmony_ci	char *data;
7398c2ecf20Sopenharmony_ci	u16 len;
7408c2ecf20Sopenharmony_ci	int err = 0;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	/* How many bytes are there to read? */
7438c2ecf20Sopenharmony_ci	err = spu_read_u16(card, IF_SPI_SCRATCH_1_REG, &len);
7448c2ecf20Sopenharmony_ci	if (err)
7458c2ecf20Sopenharmony_ci		goto out;
7468c2ecf20Sopenharmony_ci	if (!len) {
7478c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "%s: error: card has no data for host\n",
7488c2ecf20Sopenharmony_ci			   __func__);
7498c2ecf20Sopenharmony_ci		err = -EINVAL;
7508c2ecf20Sopenharmony_ci		goto out;
7518c2ecf20Sopenharmony_ci	} else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
7528c2ecf20Sopenharmony_ci		netdev_err(priv->dev,
7538c2ecf20Sopenharmony_ci			   "%s: error: card has %d bytes of data, but our maximum skb size is %zu\n",
7548c2ecf20Sopenharmony_ci			   __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
7558c2ecf20Sopenharmony_ci		err = -EINVAL;
7568c2ecf20Sopenharmony_ci		goto out;
7578c2ecf20Sopenharmony_ci	}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/* TODO: should we allocate a smaller skb if we have less data? */
7608c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE);
7618c2ecf20Sopenharmony_ci	if (!skb) {
7628c2ecf20Sopenharmony_ci		err = -ENOBUFS;
7638c2ecf20Sopenharmony_ci		goto out;
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci	skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
7668c2ecf20Sopenharmony_ci	data = skb_put(skb, len);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	/* Read the data from the WLAN module into our skb... */
7698c2ecf20Sopenharmony_ci	err = spu_read(card, IF_SPI_DATA_RDWRPORT_REG, data, ALIGN(len, 4));
7708c2ecf20Sopenharmony_ci	if (err) {
7718c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
7728c2ecf20Sopenharmony_ci		goto out;
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	/* pass the SKB to libertas */
7768c2ecf20Sopenharmony_ci	err = lbs_process_rxed_packet(card->priv, skb);
7778c2ecf20Sopenharmony_ci	/* lbs_process_rxed_packet() consumes the skb */
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ciout:
7808c2ecf20Sopenharmony_ci	if (err)
7818c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "%s: err=%d\n", __func__, err);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	return err;
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci/* Move data or a command from the host to the card. */
7878c2ecf20Sopenharmony_cistatic void if_spi_h2c(struct if_spi_card *card,
7888c2ecf20Sopenharmony_ci			struct if_spi_packet *packet, int type)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	struct lbs_private *priv = card->priv;
7918c2ecf20Sopenharmony_ci	int err = 0;
7928c2ecf20Sopenharmony_ci	u16 port_reg;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	switch (type) {
7958c2ecf20Sopenharmony_ci	case MVMS_DAT:
7968c2ecf20Sopenharmony_ci		port_reg = IF_SPI_DATA_RDWRPORT_REG;
7978c2ecf20Sopenharmony_ci		break;
7988c2ecf20Sopenharmony_ci	case MVMS_CMD:
7998c2ecf20Sopenharmony_ci		port_reg = IF_SPI_CMD_RDWRPORT_REG;
8008c2ecf20Sopenharmony_ci		break;
8018c2ecf20Sopenharmony_ci	default:
8028c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "can't transfer buffer of type %d\n",
8038c2ecf20Sopenharmony_ci			   type);
8048c2ecf20Sopenharmony_ci		err = -EINVAL;
8058c2ecf20Sopenharmony_ci		goto out;
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	/* Write the data to the card */
8098c2ecf20Sopenharmony_ci	err = spu_write(card, port_reg, packet->buffer, packet->blen);
8108c2ecf20Sopenharmony_ci	if (err)
8118c2ecf20Sopenharmony_ci		goto out;
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ciout:
8148c2ecf20Sopenharmony_ci	kfree(packet);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (err)
8178c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "%s: error %d\n", __func__, err);
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci/* Inform the host about a card event */
8218c2ecf20Sopenharmony_cistatic void if_spi_e2h(struct if_spi_card *card)
8228c2ecf20Sopenharmony_ci{
8238c2ecf20Sopenharmony_ci	int err = 0;
8248c2ecf20Sopenharmony_ci	u32 cause;
8258c2ecf20Sopenharmony_ci	struct lbs_private *priv = card->priv;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	err = spu_read_u32(card, IF_SPI_SCRATCH_3_REG, &cause);
8288c2ecf20Sopenharmony_ci	if (err)
8298c2ecf20Sopenharmony_ci		goto out;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	/* re-enable the card event interrupt */
8328c2ecf20Sopenharmony_ci	spu_write_u16(card, IF_SPI_HOST_INT_STATUS_REG,
8338c2ecf20Sopenharmony_ci			~IF_SPI_HICU_CARD_EVENT);
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	/* generate a card interrupt */
8368c2ecf20Sopenharmony_ci	spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG, IF_SPI_CIC_HOST_EVENT);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	lbs_queue_event(priv, cause & 0xff);
8398c2ecf20Sopenharmony_ciout:
8408c2ecf20Sopenharmony_ci	if (err)
8418c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "%s: error %d\n", __func__, err);
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_cistatic void if_spi_host_to_card_worker(struct work_struct *work)
8458c2ecf20Sopenharmony_ci{
8468c2ecf20Sopenharmony_ci	int err;
8478c2ecf20Sopenharmony_ci	struct if_spi_card *card;
8488c2ecf20Sopenharmony_ci	u16 hiStatus;
8498c2ecf20Sopenharmony_ci	unsigned long flags;
8508c2ecf20Sopenharmony_ci	struct if_spi_packet *packet;
8518c2ecf20Sopenharmony_ci	struct lbs_private *priv;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	card = container_of(work, struct if_spi_card, packet_work);
8548c2ecf20Sopenharmony_ci	priv = card->priv;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	/*
8578c2ecf20Sopenharmony_ci	 * Read the host interrupt status register to see what we
8588c2ecf20Sopenharmony_ci	 * can do.
8598c2ecf20Sopenharmony_ci	 */
8608c2ecf20Sopenharmony_ci	err = spu_read_u16(card, IF_SPI_HOST_INT_STATUS_REG,
8618c2ecf20Sopenharmony_ci				&hiStatus);
8628c2ecf20Sopenharmony_ci	if (err) {
8638c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "I/O error\n");
8648c2ecf20Sopenharmony_ci		goto err;
8658c2ecf20Sopenharmony_ci	}
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	if (hiStatus & IF_SPI_HIST_CMD_UPLOAD_RDY) {
8688c2ecf20Sopenharmony_ci		err = if_spi_c2h_cmd(card);
8698c2ecf20Sopenharmony_ci		if (err)
8708c2ecf20Sopenharmony_ci			goto err;
8718c2ecf20Sopenharmony_ci	}
8728c2ecf20Sopenharmony_ci	if (hiStatus & IF_SPI_HIST_RX_UPLOAD_RDY) {
8738c2ecf20Sopenharmony_ci		err = if_spi_c2h_data(card);
8748c2ecf20Sopenharmony_ci		if (err)
8758c2ecf20Sopenharmony_ci			goto err;
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	/*
8798c2ecf20Sopenharmony_ci	 * workaround: in PS mode, the card does not set the Command
8808c2ecf20Sopenharmony_ci	 * Download Ready bit, but it sets TX Download Ready.
8818c2ecf20Sopenharmony_ci	 */
8828c2ecf20Sopenharmony_ci	if (hiStatus & IF_SPI_HIST_CMD_DOWNLOAD_RDY ||
8838c2ecf20Sopenharmony_ci	   (card->priv->psstate != PS_STATE_FULL_POWER &&
8848c2ecf20Sopenharmony_ci	    (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY))) {
8858c2ecf20Sopenharmony_ci		/*
8868c2ecf20Sopenharmony_ci		 * This means two things. First of all,
8878c2ecf20Sopenharmony_ci		 * if there was a previous command sent, the card has
8888c2ecf20Sopenharmony_ci		 * successfully received it.
8898c2ecf20Sopenharmony_ci		 * Secondly, it is now ready to download another
8908c2ecf20Sopenharmony_ci		 * command.
8918c2ecf20Sopenharmony_ci		 */
8928c2ecf20Sopenharmony_ci		lbs_host_to_card_done(card->priv);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci		/* Do we have any command packets from the host to send? */
8958c2ecf20Sopenharmony_ci		packet = NULL;
8968c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->buffer_lock, flags);
8978c2ecf20Sopenharmony_ci		if (!list_empty(&card->cmd_packet_list)) {
8988c2ecf20Sopenharmony_ci			packet = (struct if_spi_packet *)(card->
8998c2ecf20Sopenharmony_ci					cmd_packet_list.next);
9008c2ecf20Sopenharmony_ci			list_del(&packet->list);
9018c2ecf20Sopenharmony_ci		}
9028c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->buffer_lock, flags);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		if (packet)
9058c2ecf20Sopenharmony_ci			if_spi_h2c(card, packet, MVMS_CMD);
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci	if (hiStatus & IF_SPI_HIST_TX_DOWNLOAD_RDY) {
9088c2ecf20Sopenharmony_ci		/* Do we have any data packets from the host to send? */
9098c2ecf20Sopenharmony_ci		packet = NULL;
9108c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->buffer_lock, flags);
9118c2ecf20Sopenharmony_ci		if (!list_empty(&card->data_packet_list)) {
9128c2ecf20Sopenharmony_ci			packet = (struct if_spi_packet *)(card->
9138c2ecf20Sopenharmony_ci					data_packet_list.next);
9148c2ecf20Sopenharmony_ci			list_del(&packet->list);
9158c2ecf20Sopenharmony_ci		}
9168c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->buffer_lock, flags);
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci		if (packet)
9198c2ecf20Sopenharmony_ci			if_spi_h2c(card, packet, MVMS_DAT);
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci	if (hiStatus & IF_SPI_HIST_CARD_EVENT)
9228c2ecf20Sopenharmony_ci		if_spi_e2h(card);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_cierr:
9258c2ecf20Sopenharmony_ci	if (err)
9268c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "%s: got error %d\n", __func__, err);
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci/*
9308c2ecf20Sopenharmony_ci * Host to Card
9318c2ecf20Sopenharmony_ci *
9328c2ecf20Sopenharmony_ci * Called from Libertas to transfer some data to the WLAN device
9338c2ecf20Sopenharmony_ci * We can't sleep here.
9348c2ecf20Sopenharmony_ci */
9358c2ecf20Sopenharmony_cistatic int if_spi_host_to_card(struct lbs_private *priv,
9368c2ecf20Sopenharmony_ci				u8 type, u8 *buf, u16 nb)
9378c2ecf20Sopenharmony_ci{
9388c2ecf20Sopenharmony_ci	int err = 0;
9398c2ecf20Sopenharmony_ci	unsigned long flags;
9408c2ecf20Sopenharmony_ci	struct if_spi_card *card = priv->card;
9418c2ecf20Sopenharmony_ci	struct if_spi_packet *packet;
9428c2ecf20Sopenharmony_ci	u16 blen;
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_ci	if (nb == 0) {
9458c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "%s: invalid size requested: %d\n",
9468c2ecf20Sopenharmony_ci			   __func__, nb);
9478c2ecf20Sopenharmony_ci		err = -EINVAL;
9488c2ecf20Sopenharmony_ci		goto out;
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci	blen = ALIGN(nb, 4);
9518c2ecf20Sopenharmony_ci	packet = kzalloc(sizeof(struct if_spi_packet) + blen, GFP_ATOMIC);
9528c2ecf20Sopenharmony_ci	if (!packet) {
9538c2ecf20Sopenharmony_ci		err = -ENOMEM;
9548c2ecf20Sopenharmony_ci		goto out;
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci	packet->blen = blen;
9578c2ecf20Sopenharmony_ci	memcpy(packet->buffer, buf, nb);
9588c2ecf20Sopenharmony_ci	memset(packet->buffer + nb, 0, blen - nb);
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	switch (type) {
9618c2ecf20Sopenharmony_ci	case MVMS_CMD:
9628c2ecf20Sopenharmony_ci		priv->dnld_sent = DNLD_CMD_SENT;
9638c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->buffer_lock, flags);
9648c2ecf20Sopenharmony_ci		list_add_tail(&packet->list, &card->cmd_packet_list);
9658c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->buffer_lock, flags);
9668c2ecf20Sopenharmony_ci		break;
9678c2ecf20Sopenharmony_ci	case MVMS_DAT:
9688c2ecf20Sopenharmony_ci		priv->dnld_sent = DNLD_DATA_SENT;
9698c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->buffer_lock, flags);
9708c2ecf20Sopenharmony_ci		list_add_tail(&packet->list, &card->data_packet_list);
9718c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->buffer_lock, flags);
9728c2ecf20Sopenharmony_ci		break;
9738c2ecf20Sopenharmony_ci	default:
9748c2ecf20Sopenharmony_ci		kfree(packet);
9758c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "can't transfer buffer of type %d\n",
9768c2ecf20Sopenharmony_ci			   type);
9778c2ecf20Sopenharmony_ci		err = -EINVAL;
9788c2ecf20Sopenharmony_ci		break;
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	/* Queue spi xfer work */
9828c2ecf20Sopenharmony_ci	queue_work(card->workqueue, &card->packet_work);
9838c2ecf20Sopenharmony_ciout:
9848c2ecf20Sopenharmony_ci	return err;
9858c2ecf20Sopenharmony_ci}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci/*
9888c2ecf20Sopenharmony_ci * Host Interrupts
9898c2ecf20Sopenharmony_ci *
9908c2ecf20Sopenharmony_ci * Service incoming interrupts from the WLAN device. We can't sleep here, so
9918c2ecf20Sopenharmony_ci * don't try to talk on the SPI bus, just queue the SPI xfer work.
9928c2ecf20Sopenharmony_ci */
9938c2ecf20Sopenharmony_cistatic irqreturn_t if_spi_host_interrupt(int irq, void *dev_id)
9948c2ecf20Sopenharmony_ci{
9958c2ecf20Sopenharmony_ci	struct if_spi_card *card = dev_id;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	queue_work(card->workqueue, &card->packet_work);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
10008c2ecf20Sopenharmony_ci}
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci/*
10038c2ecf20Sopenharmony_ci * SPI callbacks
10048c2ecf20Sopenharmony_ci */
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_cistatic int if_spi_init_card(struct if_spi_card *card)
10078c2ecf20Sopenharmony_ci{
10088c2ecf20Sopenharmony_ci	struct lbs_private *priv = card->priv;
10098c2ecf20Sopenharmony_ci	int err, i;
10108c2ecf20Sopenharmony_ci	u32 scratch;
10118c2ecf20Sopenharmony_ci	const struct firmware *helper = NULL;
10128c2ecf20Sopenharmony_ci	const struct firmware *mainfw = NULL;
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	err = spu_init(card, card->pdata->use_dummy_writes);
10158c2ecf20Sopenharmony_ci	if (err)
10168c2ecf20Sopenharmony_ci		goto out;
10178c2ecf20Sopenharmony_ci	err = spu_get_chip_revision(card, &card->card_id, &card->card_rev);
10188c2ecf20Sopenharmony_ci	if (err)
10198c2ecf20Sopenharmony_ci		goto out;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	err = spu_read_u32(card, IF_SPI_SCRATCH_4_REG, &scratch);
10228c2ecf20Sopenharmony_ci	if (err)
10238c2ecf20Sopenharmony_ci		goto out;
10248c2ecf20Sopenharmony_ci	if (scratch == SUCCESSFUL_FW_DOWNLOAD_MAGIC)
10258c2ecf20Sopenharmony_ci		lbs_deb_spi("Firmware is already loaded for "
10268c2ecf20Sopenharmony_ci			    "Marvell WLAN 802.11 adapter\n");
10278c2ecf20Sopenharmony_ci	else {
10288c2ecf20Sopenharmony_ci		/* Check if we support this card */
10298c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
10308c2ecf20Sopenharmony_ci			if (card->card_id == fw_table[i].model)
10318c2ecf20Sopenharmony_ci				break;
10328c2ecf20Sopenharmony_ci		}
10338c2ecf20Sopenharmony_ci		if (i == ARRAY_SIZE(fw_table)) {
10348c2ecf20Sopenharmony_ci			netdev_err(priv->dev, "Unsupported chip_id: 0x%02x\n",
10358c2ecf20Sopenharmony_ci				   card->card_id);
10368c2ecf20Sopenharmony_ci			err = -ENODEV;
10378c2ecf20Sopenharmony_ci			goto out;
10388c2ecf20Sopenharmony_ci		}
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci		err = lbs_get_firmware(&card->spi->dev, card->card_id,
10418c2ecf20Sopenharmony_ci					&fw_table[0], &helper, &mainfw);
10428c2ecf20Sopenharmony_ci		if (err) {
10438c2ecf20Sopenharmony_ci			netdev_err(priv->dev, "failed to find firmware (%d)\n",
10448c2ecf20Sopenharmony_ci				   err);
10458c2ecf20Sopenharmony_ci			goto out;
10468c2ecf20Sopenharmony_ci		}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci		lbs_deb_spi("Initializing FW for Marvell WLAN 802.11 adapter "
10498c2ecf20Sopenharmony_ci				"(chip_id = 0x%04x, chip_rev = 0x%02x) "
10508c2ecf20Sopenharmony_ci				"attached to SPI bus_num %d, chip_select %d. "
10518c2ecf20Sopenharmony_ci				"spi->max_speed_hz=%d\n",
10528c2ecf20Sopenharmony_ci				card->card_id, card->card_rev,
10538c2ecf20Sopenharmony_ci				card->spi->master->bus_num,
10548c2ecf20Sopenharmony_ci				card->spi->chip_select,
10558c2ecf20Sopenharmony_ci				card->spi->max_speed_hz);
10568c2ecf20Sopenharmony_ci		err = if_spi_prog_helper_firmware(card, helper);
10578c2ecf20Sopenharmony_ci		if (err)
10588c2ecf20Sopenharmony_ci			goto out;
10598c2ecf20Sopenharmony_ci		err = if_spi_prog_main_firmware(card, mainfw);
10608c2ecf20Sopenharmony_ci		if (err)
10618c2ecf20Sopenharmony_ci			goto out;
10628c2ecf20Sopenharmony_ci		lbs_deb_spi("loaded FW for Marvell WLAN 802.11 adapter\n");
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	err = spu_set_interrupt_mode(card, 0, 1);
10668c2ecf20Sopenharmony_ci	if (err)
10678c2ecf20Sopenharmony_ci		goto out;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ciout:
10708c2ecf20Sopenharmony_ci	return err;
10718c2ecf20Sopenharmony_ci}
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_cistatic void if_spi_resume_worker(struct work_struct *work)
10748c2ecf20Sopenharmony_ci{
10758c2ecf20Sopenharmony_ci	struct if_spi_card *card;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	card = container_of(work, struct if_spi_card, resume_work);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	if (card->suspended) {
10808c2ecf20Sopenharmony_ci		if (card->pdata->setup)
10818c2ecf20Sopenharmony_ci			card->pdata->setup(card->spi);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci		/* Init card ... */
10848c2ecf20Sopenharmony_ci		if_spi_init_card(card);
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci		enable_irq(card->spi->irq);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci		/* And resume it ... */
10898c2ecf20Sopenharmony_ci		lbs_resume(card->priv);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci		card->suspended = 0;
10928c2ecf20Sopenharmony_ci	}
10938c2ecf20Sopenharmony_ci}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_cistatic int if_spi_probe(struct spi_device *spi)
10968c2ecf20Sopenharmony_ci{
10978c2ecf20Sopenharmony_ci	struct if_spi_card *card;
10988c2ecf20Sopenharmony_ci	struct lbs_private *priv = NULL;
10998c2ecf20Sopenharmony_ci	struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev);
11008c2ecf20Sopenharmony_ci	int err = 0;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	if (!pdata) {
11038c2ecf20Sopenharmony_ci		err = -EINVAL;
11048c2ecf20Sopenharmony_ci		goto out;
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	if (pdata->setup) {
11088c2ecf20Sopenharmony_ci		err = pdata->setup(spi);
11098c2ecf20Sopenharmony_ci		if (err)
11108c2ecf20Sopenharmony_ci			goto out;
11118c2ecf20Sopenharmony_ci	}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	/* Allocate card structure to represent this specific device */
11148c2ecf20Sopenharmony_ci	card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL);
11158c2ecf20Sopenharmony_ci	if (!card) {
11168c2ecf20Sopenharmony_ci		err = -ENOMEM;
11178c2ecf20Sopenharmony_ci		goto teardown;
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci	spi_set_drvdata(spi, card);
11208c2ecf20Sopenharmony_ci	card->pdata = pdata;
11218c2ecf20Sopenharmony_ci	card->spi = spi;
11228c2ecf20Sopenharmony_ci	card->prev_xfer_time = jiffies;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&card->cmd_packet_list);
11258c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&card->data_packet_list);
11268c2ecf20Sopenharmony_ci	spin_lock_init(&card->buffer_lock);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	/* Initialize the SPI Interface Unit */
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	/* Firmware load */
11318c2ecf20Sopenharmony_ci	err = if_spi_init_card(card);
11328c2ecf20Sopenharmony_ci	if (err)
11338c2ecf20Sopenharmony_ci		goto free_card;
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	/*
11368c2ecf20Sopenharmony_ci	 * Register our card with libertas.
11378c2ecf20Sopenharmony_ci	 * This will call alloc_etherdev.
11388c2ecf20Sopenharmony_ci	 */
11398c2ecf20Sopenharmony_ci	priv = lbs_add_card(card, &spi->dev);
11408c2ecf20Sopenharmony_ci	if (IS_ERR(priv)) {
11418c2ecf20Sopenharmony_ci		err = PTR_ERR(priv);
11428c2ecf20Sopenharmony_ci		goto free_card;
11438c2ecf20Sopenharmony_ci	}
11448c2ecf20Sopenharmony_ci	card->priv = priv;
11458c2ecf20Sopenharmony_ci	priv->setup_fw_on_resume = 1;
11468c2ecf20Sopenharmony_ci	priv->card = card;
11478c2ecf20Sopenharmony_ci	priv->hw_host_to_card = if_spi_host_to_card;
11488c2ecf20Sopenharmony_ci	priv->enter_deep_sleep = NULL;
11498c2ecf20Sopenharmony_ci	priv->exit_deep_sleep = NULL;
11508c2ecf20Sopenharmony_ci	priv->reset_deep_sleep_wakeup = NULL;
11518c2ecf20Sopenharmony_ci	priv->fw_ready = 1;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	/* Initialize interrupt handling stuff. */
11548c2ecf20Sopenharmony_ci	card->workqueue = alloc_workqueue("libertas_spi", WQ_MEM_RECLAIM, 0);
11558c2ecf20Sopenharmony_ci	if (!card->workqueue) {
11568c2ecf20Sopenharmony_ci		err = -ENOMEM;
11578c2ecf20Sopenharmony_ci		goto remove_card;
11588c2ecf20Sopenharmony_ci	}
11598c2ecf20Sopenharmony_ci	INIT_WORK(&card->packet_work, if_spi_host_to_card_worker);
11608c2ecf20Sopenharmony_ci	INIT_WORK(&card->resume_work, if_spi_resume_worker);
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	err = request_irq(spi->irq, if_spi_host_interrupt,
11638c2ecf20Sopenharmony_ci			IRQF_TRIGGER_FALLING, "libertas_spi", card);
11648c2ecf20Sopenharmony_ci	if (err) {
11658c2ecf20Sopenharmony_ci		pr_err("can't get host irq line-- request_irq failed\n");
11668c2ecf20Sopenharmony_ci		goto terminate_workqueue;
11678c2ecf20Sopenharmony_ci	}
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	/*
11708c2ecf20Sopenharmony_ci	 * Start the card.
11718c2ecf20Sopenharmony_ci	 * This will call register_netdev, and we'll start
11728c2ecf20Sopenharmony_ci	 * getting interrupts...
11738c2ecf20Sopenharmony_ci	 */
11748c2ecf20Sopenharmony_ci	err = lbs_start_card(priv);
11758c2ecf20Sopenharmony_ci	if (err)
11768c2ecf20Sopenharmony_ci		goto release_irq;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	lbs_deb_spi("Finished initializing WLAN module.\n");
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	/* successful exit */
11818c2ecf20Sopenharmony_ci	goto out;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_cirelease_irq:
11848c2ecf20Sopenharmony_ci	free_irq(spi->irq, card);
11858c2ecf20Sopenharmony_citerminate_workqueue:
11868c2ecf20Sopenharmony_ci	destroy_workqueue(card->workqueue);
11878c2ecf20Sopenharmony_ciremove_card:
11888c2ecf20Sopenharmony_ci	lbs_remove_card(priv); /* will call free_netdev */
11898c2ecf20Sopenharmony_cifree_card:
11908c2ecf20Sopenharmony_ci	free_if_spi_card(card);
11918c2ecf20Sopenharmony_citeardown:
11928c2ecf20Sopenharmony_ci	if (pdata->teardown)
11938c2ecf20Sopenharmony_ci		pdata->teardown(spi);
11948c2ecf20Sopenharmony_ciout:
11958c2ecf20Sopenharmony_ci	return err;
11968c2ecf20Sopenharmony_ci}
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_cistatic int libertas_spi_remove(struct spi_device *spi)
11998c2ecf20Sopenharmony_ci{
12008c2ecf20Sopenharmony_ci	struct if_spi_card *card = spi_get_drvdata(spi);
12018c2ecf20Sopenharmony_ci	struct lbs_private *priv = card->priv;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	lbs_deb_spi("libertas_spi_remove\n");
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	cancel_work_sync(&card->resume_work);
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	lbs_stop_card(priv);
12088c2ecf20Sopenharmony_ci	lbs_remove_card(priv); /* will call free_netdev */
12098c2ecf20Sopenharmony_ci
12108c2ecf20Sopenharmony_ci	free_irq(spi->irq, card);
12118c2ecf20Sopenharmony_ci	destroy_workqueue(card->workqueue);
12128c2ecf20Sopenharmony_ci	if (card->pdata->teardown)
12138c2ecf20Sopenharmony_ci		card->pdata->teardown(spi);
12148c2ecf20Sopenharmony_ci	free_if_spi_card(card);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	return 0;
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic int if_spi_suspend(struct device *dev)
12208c2ecf20Sopenharmony_ci{
12218c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
12228c2ecf20Sopenharmony_ci	struct if_spi_card *card = spi_get_drvdata(spi);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	if (!card->suspended) {
12258c2ecf20Sopenharmony_ci		lbs_suspend(card->priv);
12268c2ecf20Sopenharmony_ci		flush_workqueue(card->workqueue);
12278c2ecf20Sopenharmony_ci		disable_irq(spi->irq);
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci		if (card->pdata->teardown)
12308c2ecf20Sopenharmony_ci			card->pdata->teardown(spi);
12318c2ecf20Sopenharmony_ci		card->suspended = 1;
12328c2ecf20Sopenharmony_ci	}
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	return 0;
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_cistatic int if_spi_resume(struct device *dev)
12388c2ecf20Sopenharmony_ci{
12398c2ecf20Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
12408c2ecf20Sopenharmony_ci	struct if_spi_card *card = spi_get_drvdata(spi);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	/* Schedule delayed work */
12438c2ecf20Sopenharmony_ci	schedule_work(&card->resume_work);
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	return 0;
12468c2ecf20Sopenharmony_ci}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_cistatic const struct dev_pm_ops if_spi_pm_ops = {
12498c2ecf20Sopenharmony_ci	.suspend	= if_spi_suspend,
12508c2ecf20Sopenharmony_ci	.resume		= if_spi_resume,
12518c2ecf20Sopenharmony_ci};
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_cistatic struct spi_driver libertas_spi_driver = {
12548c2ecf20Sopenharmony_ci	.probe	= if_spi_probe,
12558c2ecf20Sopenharmony_ci	.remove = libertas_spi_remove,
12568c2ecf20Sopenharmony_ci	.driver = {
12578c2ecf20Sopenharmony_ci		.name	= "libertas_spi",
12588c2ecf20Sopenharmony_ci		.pm	= &if_spi_pm_ops,
12598c2ecf20Sopenharmony_ci	},
12608c2ecf20Sopenharmony_ci};
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci/*
12638c2ecf20Sopenharmony_ci * Module functions
12648c2ecf20Sopenharmony_ci */
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_cistatic int __init if_spi_init_module(void)
12678c2ecf20Sopenharmony_ci{
12688c2ecf20Sopenharmony_ci	int ret = 0;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	printk(KERN_INFO "libertas_spi: Libertas SPI driver\n");
12718c2ecf20Sopenharmony_ci	ret = spi_register_driver(&libertas_spi_driver);
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	return ret;
12748c2ecf20Sopenharmony_ci}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_cistatic void __exit if_spi_exit_module(void)
12778c2ecf20Sopenharmony_ci{
12788c2ecf20Sopenharmony_ci	spi_unregister_driver(&libertas_spi_driver);
12798c2ecf20Sopenharmony_ci}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_cimodule_init(if_spi_init_module);
12828c2ecf20Sopenharmony_cimodule_exit(if_spi_exit_module);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Libertas SPI WLAN Driver");
12858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andrey Yurovsky <andrey@cozybit.com>, "
12868c2ecf20Sopenharmony_ci	      "Colin McCabe <colin@cozybit.com>");
12878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
12888c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:libertas_spi");
1289