18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci  Driver for the Marvell 8385 based compact flash WLAN cards.
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci  (C) 2007 by Holger Schurig <hs4233@mail.mn-solutions.de>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci*/
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
178c2ecf20Sopenharmony_ci#include <linux/firmware.h>
188c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <pcmcia/cistpl.h>
218c2ecf20Sopenharmony_ci#include <pcmcia/ds.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/io.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define DRV_NAME "libertas_cs"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "decl.h"
288c2ecf20Sopenharmony_ci#include "defs.h"
298c2ecf20Sopenharmony_ci#include "dev.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/********************************************************************/
338c2ecf20Sopenharmony_ci/* Module stuff                                                     */
348c2ecf20Sopenharmony_ci/********************************************************************/
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Holger Schurig <hs4233@mail.mn-solutions.de>");
378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards");
388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/********************************************************************/
438c2ecf20Sopenharmony_ci/* Data structures                                                  */
448c2ecf20Sopenharmony_ci/********************************************************************/
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct if_cs_card {
478c2ecf20Sopenharmony_ci	struct pcmcia_device *p_dev;
488c2ecf20Sopenharmony_ci	struct lbs_private *priv;
498c2ecf20Sopenharmony_ci	void __iomem *iobase;
508c2ecf20Sopenharmony_ci	bool align_regs;
518c2ecf20Sopenharmony_ci	u32 model;
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cienum {
568c2ecf20Sopenharmony_ci	MODEL_UNKNOWN = 0x00,
578c2ecf20Sopenharmony_ci	MODEL_8305 = 0x01,
588c2ecf20Sopenharmony_ci	MODEL_8381 = 0x02,
598c2ecf20Sopenharmony_ci	MODEL_8385 = 0x03
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic const struct lbs_fw_table fw_table[] = {
638c2ecf20Sopenharmony_ci	{ MODEL_8305, "libertas/cf8305.bin", NULL },
648c2ecf20Sopenharmony_ci	{ MODEL_8305, "libertas_cs_helper.fw", NULL },
658c2ecf20Sopenharmony_ci	{ MODEL_8381, "libertas/cf8381_helper.bin", "libertas/cf8381.bin" },
668c2ecf20Sopenharmony_ci	{ MODEL_8381, "libertas_cs_helper.fw", "libertas_cs.fw" },
678c2ecf20Sopenharmony_ci	{ MODEL_8385, "libertas/cf8385_helper.bin", "libertas/cf8385.bin" },
688c2ecf20Sopenharmony_ci	{ MODEL_8385, "libertas_cs_helper.fw", "libertas_cs.fw" },
698c2ecf20Sopenharmony_ci	{ 0, NULL, NULL }
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/cf8305.bin");
728c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/cf8381_helper.bin");
738c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/cf8381.bin");
748c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/cf8385_helper.bin");
758c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas/cf8385.bin");
768c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas_cs_helper.fw");
778c2ecf20Sopenharmony_ciMODULE_FIRMWARE("libertas_cs.fw");
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/********************************************************************/
818c2ecf20Sopenharmony_ci/* Hardware access                                                  */
828c2ecf20Sopenharmony_ci/********************************************************************/
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* This define enables wrapper functions which allow you
858c2ecf20Sopenharmony_ci   to dump all register accesses. You normally won't this,
868c2ecf20Sopenharmony_ci   except for development */
878c2ecf20Sopenharmony_ci/* #define DEBUG_IO */
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#ifdef DEBUG_IO
908c2ecf20Sopenharmony_cistatic int debug_output = 0;
918c2ecf20Sopenharmony_ci#else
928c2ecf20Sopenharmony_ci/* This way the compiler optimizes the printk's away */
938c2ecf20Sopenharmony_ci#define debug_output 0
948c2ecf20Sopenharmony_ci#endif
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	unsigned int val = ioread8(card->iobase + reg);
998c2ecf20Sopenharmony_ci	if (debug_output)
1008c2ecf20Sopenharmony_ci		printk(KERN_INFO "inb %08x<%02x\n", reg, val);
1018c2ecf20Sopenharmony_ci	return val;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_cistatic inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	unsigned int val = ioread16(card->iobase + reg);
1068c2ecf20Sopenharmony_ci	if (debug_output)
1078c2ecf20Sopenharmony_ci		printk(KERN_INFO "inw %08x<%04x\n", reg, val);
1088c2ecf20Sopenharmony_ci	return val;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_cistatic inline void if_cs_read16_rep(
1118c2ecf20Sopenharmony_ci	struct if_cs_card *card,
1128c2ecf20Sopenharmony_ci	uint reg,
1138c2ecf20Sopenharmony_ci	void *buf,
1148c2ecf20Sopenharmony_ci	unsigned long count)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	if (debug_output)
1178c2ecf20Sopenharmony_ci		printk(KERN_INFO "insw %08x<(0x%lx words)\n",
1188c2ecf20Sopenharmony_ci			reg, count);
1198c2ecf20Sopenharmony_ci	ioread16_rep(card->iobase + reg, buf, count);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	if (debug_output)
1258c2ecf20Sopenharmony_ci		printk(KERN_INFO "outb %08x>%02x\n", reg, val);
1268c2ecf20Sopenharmony_ci	iowrite8(val, card->iobase + reg);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	if (debug_output)
1328c2ecf20Sopenharmony_ci		printk(KERN_INFO "outw %08x>%04x\n", reg, val);
1338c2ecf20Sopenharmony_ci	iowrite16(val, card->iobase + reg);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic inline void if_cs_write16_rep(
1378c2ecf20Sopenharmony_ci	struct if_cs_card *card,
1388c2ecf20Sopenharmony_ci	uint reg,
1398c2ecf20Sopenharmony_ci	const void *buf,
1408c2ecf20Sopenharmony_ci	unsigned long count)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	if (debug_output)
1438c2ecf20Sopenharmony_ci		printk(KERN_INFO "outsw %08x>(0x%lx words)\n",
1448c2ecf20Sopenharmony_ci			reg, count);
1458c2ecf20Sopenharmony_ci	iowrite16_rep(card->iobase + reg, buf, count);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/*
1508c2ecf20Sopenharmony_ci * I know that polling/delaying is frowned upon. However, this procedure
1518c2ecf20Sopenharmony_ci * with polling is needed while downloading the firmware. At this stage,
1528c2ecf20Sopenharmony_ci * the hardware does unfortunately not create any interrupts.
1538c2ecf20Sopenharmony_ci *
1548c2ecf20Sopenharmony_ci * Fortunately, this function is never used once the firmware is in
1558c2ecf20Sopenharmony_ci * the card. :-)
1568c2ecf20Sopenharmony_ci *
1578c2ecf20Sopenharmony_ci * As a reference, see the "Firmware Specification v5.1", page 18
1588c2ecf20Sopenharmony_ci * and 19. I did not follow their suggested timing to the word,
1598c2ecf20Sopenharmony_ci * but this works nice & fast anyway.
1608c2ecf20Sopenharmony_ci */
1618c2ecf20Sopenharmony_cistatic int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 reg)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	int i;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	for (i = 0; i < 100000; i++) {
1668c2ecf20Sopenharmony_ci		u8 val = if_cs_read8(card, addr);
1678c2ecf20Sopenharmony_ci		if (val == reg)
1688c2ecf20Sopenharmony_ci			return 0;
1698c2ecf20Sopenharmony_ci		udelay(5);
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci	return -ETIME;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/*
1778c2ecf20Sopenharmony_ci * First the bitmasks for the host/card interrupt/status registers:
1788c2ecf20Sopenharmony_ci */
1798c2ecf20Sopenharmony_ci#define IF_CS_BIT_TX			0x0001
1808c2ecf20Sopenharmony_ci#define IF_CS_BIT_RX			0x0002
1818c2ecf20Sopenharmony_ci#define IF_CS_BIT_COMMAND		0x0004
1828c2ecf20Sopenharmony_ci#define IF_CS_BIT_RESP			0x0008
1838c2ecf20Sopenharmony_ci#define IF_CS_BIT_EVENT			0x0010
1848c2ecf20Sopenharmony_ci#define	IF_CS_BIT_MASK			0x001f
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/*
1898c2ecf20Sopenharmony_ci * It's not really clear to me what the host status register is for. It
1908c2ecf20Sopenharmony_ci * needs to be set almost in union with "host int cause". The following
1918c2ecf20Sopenharmony_ci * bits from above are used:
1928c2ecf20Sopenharmony_ci *
1938c2ecf20Sopenharmony_ci *   IF_CS_BIT_TX         driver downloaded a data packet
1948c2ecf20Sopenharmony_ci *   IF_CS_BIT_RX         driver got a data packet
1958c2ecf20Sopenharmony_ci *   IF_CS_BIT_COMMAND    driver downloaded a command
1968c2ecf20Sopenharmony_ci *   IF_CS_BIT_RESP       not used (has some meaning with powerdown)
1978c2ecf20Sopenharmony_ci *   IF_CS_BIT_EVENT      driver read a host event
1988c2ecf20Sopenharmony_ci */
1998c2ecf20Sopenharmony_ci#define IF_CS_HOST_STATUS		0x00000000
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/*
2028c2ecf20Sopenharmony_ci * With the host int cause register can the host (that is, Linux) cause
2038c2ecf20Sopenharmony_ci * an interrupt in the firmware, to tell the firmware about those events:
2048c2ecf20Sopenharmony_ci *
2058c2ecf20Sopenharmony_ci *   IF_CS_BIT_TX         a data packet has been downloaded
2068c2ecf20Sopenharmony_ci *   IF_CS_BIT_RX         a received data packet has retrieved
2078c2ecf20Sopenharmony_ci *   IF_CS_BIT_COMMAND    a firmware block or a command has been downloaded
2088c2ecf20Sopenharmony_ci *   IF_CS_BIT_RESP       not used (has some meaning with powerdown)
2098c2ecf20Sopenharmony_ci *   IF_CS_BIT_EVENT      a host event (link lost etc) has been retrieved
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_ci#define IF_CS_HOST_INT_CAUSE		0x00000002
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/*
2148c2ecf20Sopenharmony_ci * The host int mask register is used to enable/disable interrupt.  However,
2158c2ecf20Sopenharmony_ci * I have the suspicion that disabled interrupts are lost.
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_ci#define IF_CS_HOST_INT_MASK		0x00000004
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/*
2208c2ecf20Sopenharmony_ci * Used to send or receive data packets:
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_ci#define IF_CS_WRITE			0x00000016
2238c2ecf20Sopenharmony_ci#define IF_CS_WRITE_LEN			0x00000014
2248c2ecf20Sopenharmony_ci#define IF_CS_READ			0x00000010
2258c2ecf20Sopenharmony_ci#define IF_CS_READ_LEN			0x00000024
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/*
2288c2ecf20Sopenharmony_ci * Used to send commands (and to send firmware block) and to
2298c2ecf20Sopenharmony_ci * receive command responses:
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_ci#define IF_CS_CMD			0x0000001A
2328c2ecf20Sopenharmony_ci#define IF_CS_CMD_LEN			0x00000018
2338c2ecf20Sopenharmony_ci#define IF_CS_RESP			0x00000012
2348c2ecf20Sopenharmony_ci#define IF_CS_RESP_LEN			0x00000030
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/*
2378c2ecf20Sopenharmony_ci * The card status registers shows what the card/firmware actually
2388c2ecf20Sopenharmony_ci * accepts:
2398c2ecf20Sopenharmony_ci *
2408c2ecf20Sopenharmony_ci *   IF_CS_BIT_TX        you may send a data packet
2418c2ecf20Sopenharmony_ci *   IF_CS_BIT_RX        you may retrieve a data packet
2428c2ecf20Sopenharmony_ci *   IF_CS_BIT_COMMAND   you may send a command
2438c2ecf20Sopenharmony_ci *   IF_CS_BIT_RESP      you may retrieve a command response
2448c2ecf20Sopenharmony_ci *   IF_CS_BIT_EVENT     the card has a event for use (link lost, snr low etc)
2458c2ecf20Sopenharmony_ci *
2468c2ecf20Sopenharmony_ci * When reading this register several times, you will get back the same
2478c2ecf20Sopenharmony_ci * results --- with one exception: the IF_CS_BIT_EVENT clear itself
2488c2ecf20Sopenharmony_ci * automatically.
2498c2ecf20Sopenharmony_ci *
2508c2ecf20Sopenharmony_ci * Not that we don't rely on BIT_RX,_BIT_RESP or BIT_EVENT because
2518c2ecf20Sopenharmony_ci * we handle this via the card int cause register.
2528c2ecf20Sopenharmony_ci */
2538c2ecf20Sopenharmony_ci#define IF_CS_CARD_STATUS		0x00000020
2548c2ecf20Sopenharmony_ci#define IF_CS_CARD_STATUS_MASK		0x7f00
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci/*
2578c2ecf20Sopenharmony_ci * The card int cause register is used by the card/firmware to notify us
2588c2ecf20Sopenharmony_ci * about the following events:
2598c2ecf20Sopenharmony_ci *
2608c2ecf20Sopenharmony_ci *   IF_CS_BIT_TX        a data packet has successfully been sentx
2618c2ecf20Sopenharmony_ci *   IF_CS_BIT_RX        a data packet has been received and can be retrieved
2628c2ecf20Sopenharmony_ci *   IF_CS_BIT_COMMAND   not used
2638c2ecf20Sopenharmony_ci *   IF_CS_BIT_RESP      the firmware has a command response for us
2648c2ecf20Sopenharmony_ci *   IF_CS_BIT_EVENT     the card has a event for use (link lost, snr low etc)
2658c2ecf20Sopenharmony_ci */
2668c2ecf20Sopenharmony_ci#define IF_CS_CARD_INT_CAUSE		0x00000022
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/*
2698c2ecf20Sopenharmony_ci * This is used to for handshaking with the card's bootloader/helper image
2708c2ecf20Sopenharmony_ci * to synchronize downloading of firmware blocks.
2718c2ecf20Sopenharmony_ci */
2728c2ecf20Sopenharmony_ci#define IF_CS_SQ_READ_LOW		0x00000028
2738c2ecf20Sopenharmony_ci#define IF_CS_SQ_HELPER_OK		0x10
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/*
2768c2ecf20Sopenharmony_ci * The scratch register tells us ...
2778c2ecf20Sopenharmony_ci *
2788c2ecf20Sopenharmony_ci * IF_CS_SCRATCH_BOOT_OK     the bootloader runs
2798c2ecf20Sopenharmony_ci * IF_CS_SCRATCH_HELPER_OK   the helper firmware already runs
2808c2ecf20Sopenharmony_ci */
2818c2ecf20Sopenharmony_ci#define IF_CS_SCRATCH			0x0000003F
2828c2ecf20Sopenharmony_ci#define IF_CS_SCRATCH_BOOT_OK		0x00
2838c2ecf20Sopenharmony_ci#define IF_CS_SCRATCH_HELPER_OK		0x5a
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci/*
2868c2ecf20Sopenharmony_ci * Used to detect ancient chips:
2878c2ecf20Sopenharmony_ci */
2888c2ecf20Sopenharmony_ci#define IF_CS_PRODUCT_ID		0x0000001C
2898c2ecf20Sopenharmony_ci#define IF_CS_CF8385_B1_REV		0x12
2908c2ecf20Sopenharmony_ci#define IF_CS_CF8381_B3_REV		0x04
2918c2ecf20Sopenharmony_ci#define IF_CS_CF8305_B1_REV		0x03
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci/*
2948c2ecf20Sopenharmony_ci * Used to detect other cards than CF8385 since their revisions of silicon
2958c2ecf20Sopenharmony_ci * doesn't match those from CF8385, eg. CF8381 B3 works with this driver.
2968c2ecf20Sopenharmony_ci */
2978c2ecf20Sopenharmony_ci#define CF8305_MANFID		0x02db
2988c2ecf20Sopenharmony_ci#define CF8305_CARDID		0x8103
2998c2ecf20Sopenharmony_ci#define CF8381_MANFID		0x02db
3008c2ecf20Sopenharmony_ci#define CF8381_CARDID		0x6064
3018c2ecf20Sopenharmony_ci#define CF8385_MANFID		0x02df
3028c2ecf20Sopenharmony_ci#define CF8385_CARDID		0x8103
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci/*
3058c2ecf20Sopenharmony_ci * FIXME: just use the 'driver_info' field of 'struct pcmcia_device_id' when
3068c2ecf20Sopenharmony_ci * that gets fixed.  Currently there's no way to access it from the probe hook.
3078c2ecf20Sopenharmony_ci */
3088c2ecf20Sopenharmony_cistatic inline u32 get_model(u16 manf_id, u16 card_id)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	/* NOTE: keep in sync with if_cs_ids */
3118c2ecf20Sopenharmony_ci	if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID)
3128c2ecf20Sopenharmony_ci		return MODEL_8305;
3138c2ecf20Sopenharmony_ci	else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID)
3148c2ecf20Sopenharmony_ci		return MODEL_8381;
3158c2ecf20Sopenharmony_ci	else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID)
3168c2ecf20Sopenharmony_ci		return MODEL_8385;
3178c2ecf20Sopenharmony_ci	return MODEL_UNKNOWN;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci/********************************************************************/
3218c2ecf20Sopenharmony_ci/* I/O and interrupt handling                                       */
3228c2ecf20Sopenharmony_ci/********************************************************************/
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic inline void if_cs_enable_ints(struct if_cs_card *card)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_INT_MASK, 0);
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic inline void if_cs_disable_ints(struct if_cs_card *card)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_INT_MASK, IF_CS_BIT_MASK);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/*
3358c2ecf20Sopenharmony_ci * Called from if_cs_host_to_card to send a command to the hardware
3368c2ecf20Sopenharmony_ci */
3378c2ecf20Sopenharmony_cistatic int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	struct if_cs_card *card = (struct if_cs_card *)priv->card;
3408c2ecf20Sopenharmony_ci	int ret = -1;
3418c2ecf20Sopenharmony_ci	int loops = 0;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if_cs_disable_ints(card);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* Is hardware ready? */
3468c2ecf20Sopenharmony_ci	while (1) {
3478c2ecf20Sopenharmony_ci		u16 status = if_cs_read16(card, IF_CS_CARD_STATUS);
3488c2ecf20Sopenharmony_ci		if (status & IF_CS_BIT_COMMAND)
3498c2ecf20Sopenharmony_ci			break;
3508c2ecf20Sopenharmony_ci		if (++loops > 100) {
3518c2ecf20Sopenharmony_ci			netdev_err(priv->dev, "card not ready for commands\n");
3528c2ecf20Sopenharmony_ci			goto done;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci		mdelay(1);
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if_cs_write16(card, IF_CS_CMD_LEN, nb);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if_cs_write16_rep(card, IF_CS_CMD, buf, nb / 2);
3608c2ecf20Sopenharmony_ci	/* Are we supposed to transfer an odd amount of bytes? */
3618c2ecf20Sopenharmony_ci	if (nb & 1)
3628c2ecf20Sopenharmony_ci		if_cs_write8(card, IF_CS_CMD, buf[nb-1]);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	/* "Assert the download over interrupt command in the Host
3658c2ecf20Sopenharmony_ci	 * status register" */
3668c2ecf20Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* "Assert the download over interrupt command in the Card
3698c2ecf20Sopenharmony_ci	 * interrupt case register" */
3708c2ecf20Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND);
3718c2ecf20Sopenharmony_ci	ret = 0;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cidone:
3748c2ecf20Sopenharmony_ci	if_cs_enable_ints(card);
3758c2ecf20Sopenharmony_ci	return ret;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci/*
3798c2ecf20Sopenharmony_ci * Called from if_cs_host_to_card to send a data to the hardware
3808c2ecf20Sopenharmony_ci */
3818c2ecf20Sopenharmony_cistatic void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct if_cs_card *card = (struct if_cs_card *)priv->card;
3848c2ecf20Sopenharmony_ci	u16 status;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	if_cs_disable_ints(card);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	status = if_cs_read16(card, IF_CS_CARD_STATUS);
3898c2ecf20Sopenharmony_ci	BUG_ON((status & IF_CS_BIT_TX) == 0);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if_cs_write16(card, IF_CS_WRITE_LEN, nb);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	/* write even number of bytes, then odd byte if necessary */
3948c2ecf20Sopenharmony_ci	if_cs_write16_rep(card, IF_CS_WRITE, buf, nb / 2);
3958c2ecf20Sopenharmony_ci	if (nb & 1)
3968c2ecf20Sopenharmony_ci		if_cs_write8(card, IF_CS_WRITE, buf[nb-1]);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_TX);
3998c2ecf20Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_TX);
4008c2ecf20Sopenharmony_ci	if_cs_enable_ints(card);
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci/*
4048c2ecf20Sopenharmony_ci * Get the command result out of the card.
4058c2ecf20Sopenharmony_ci */
4068c2ecf20Sopenharmony_cistatic int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	unsigned long flags;
4098c2ecf20Sopenharmony_ci	int ret = -1;
4108c2ecf20Sopenharmony_ci	u16 status;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* is hardware ready? */
4138c2ecf20Sopenharmony_ci	status = if_cs_read16(priv->card, IF_CS_CARD_STATUS);
4148c2ecf20Sopenharmony_ci	if ((status & IF_CS_BIT_RESP) == 0) {
4158c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "no cmd response in card\n");
4168c2ecf20Sopenharmony_ci		*len = 0;
4178c2ecf20Sopenharmony_ci		goto out;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	*len = if_cs_read16(priv->card, IF_CS_RESP_LEN);
4218c2ecf20Sopenharmony_ci	if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) {
4228c2ecf20Sopenharmony_ci		netdev_err(priv->dev,
4238c2ecf20Sopenharmony_ci			   "card cmd buffer has invalid # of bytes (%d)\n",
4248c2ecf20Sopenharmony_ci			   *len);
4258c2ecf20Sopenharmony_ci		goto out;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/* read even number of bytes, then odd byte if necessary */
4298c2ecf20Sopenharmony_ci	if_cs_read16_rep(priv->card, IF_CS_RESP, data, *len/sizeof(u16));
4308c2ecf20Sopenharmony_ci	if (*len & 1)
4318c2ecf20Sopenharmony_ci		data[*len-1] = if_cs_read8(priv->card, IF_CS_RESP);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	/* This is a workaround for a firmware that reports too much
4348c2ecf20Sopenharmony_ci	 * bytes */
4358c2ecf20Sopenharmony_ci	*len -= 8;
4368c2ecf20Sopenharmony_ci	ret = 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* Clear this flag again */
4398c2ecf20Sopenharmony_ci	spin_lock_irqsave(&priv->driver_lock, flags);
4408c2ecf20Sopenharmony_ci	priv->dnld_sent = DNLD_RES_RECEIVED;
4418c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&priv->driver_lock, flags);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ciout:
4448c2ecf20Sopenharmony_ci	return ret;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic struct sk_buff *if_cs_receive_data(struct lbs_private *priv)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct sk_buff *skb = NULL;
4508c2ecf20Sopenharmony_ci	u16 len;
4518c2ecf20Sopenharmony_ci	u8 *data;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	len = if_cs_read16(priv->card, IF_CS_READ_LEN);
4548c2ecf20Sopenharmony_ci	if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
4558c2ecf20Sopenharmony_ci		netdev_err(priv->dev,
4568c2ecf20Sopenharmony_ci			   "card data buffer has invalid # of bytes (%d)\n",
4578c2ecf20Sopenharmony_ci			   len);
4588c2ecf20Sopenharmony_ci		priv->dev->stats.rx_dropped++;
4598c2ecf20Sopenharmony_ci		goto dat_err;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2);
4638c2ecf20Sopenharmony_ci	if (!skb)
4648c2ecf20Sopenharmony_ci		goto out;
4658c2ecf20Sopenharmony_ci	skb_put(skb, len);
4668c2ecf20Sopenharmony_ci	skb_reserve(skb, 2);/* 16 byte align */
4678c2ecf20Sopenharmony_ci	data = skb->data;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/* read even number of bytes, then odd byte if necessary */
4708c2ecf20Sopenharmony_ci	if_cs_read16_rep(priv->card, IF_CS_READ, data, len/sizeof(u16));
4718c2ecf20Sopenharmony_ci	if (len & 1)
4728c2ecf20Sopenharmony_ci		data[len-1] = if_cs_read8(priv->card, IF_CS_READ);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_cidat_err:
4758c2ecf20Sopenharmony_ci	if_cs_write16(priv->card, IF_CS_HOST_STATUS, IF_CS_BIT_RX);
4768c2ecf20Sopenharmony_ci	if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_RX);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ciout:
4798c2ecf20Sopenharmony_ci	return skb;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic irqreturn_t if_cs_interrupt(int irq, void *data)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct if_cs_card *card = data;
4858c2ecf20Sopenharmony_ci	struct lbs_private *priv = card->priv;
4868c2ecf20Sopenharmony_ci	u16 cause;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/* Ask card interrupt cause register if there is something for us */
4898c2ecf20Sopenharmony_ci	cause = if_cs_read16(card, IF_CS_CARD_INT_CAUSE);
4908c2ecf20Sopenharmony_ci	lbs_deb_cs("cause 0x%04x\n", cause);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (cause == 0) {
4938c2ecf20Sopenharmony_ci		/* Not for us */
4948c2ecf20Sopenharmony_ci		return IRQ_NONE;
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (cause == 0xffff) {
4988c2ecf20Sopenharmony_ci		/* Read in junk, the card has probably been removed */
4998c2ecf20Sopenharmony_ci		card->priv->surpriseremoved = 1;
5008c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
5018c2ecf20Sopenharmony_ci	}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (cause & IF_CS_BIT_RX) {
5048c2ecf20Sopenharmony_ci		struct sk_buff *skb;
5058c2ecf20Sopenharmony_ci		lbs_deb_cs("rx packet\n");
5068c2ecf20Sopenharmony_ci		skb = if_cs_receive_data(priv);
5078c2ecf20Sopenharmony_ci		if (skb)
5088c2ecf20Sopenharmony_ci			lbs_process_rxed_packet(priv, skb);
5098c2ecf20Sopenharmony_ci	}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if (cause & IF_CS_BIT_TX) {
5128c2ecf20Sopenharmony_ci		lbs_deb_cs("tx done\n");
5138c2ecf20Sopenharmony_ci		lbs_host_to_card_done(priv);
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	if (cause & IF_CS_BIT_RESP) {
5178c2ecf20Sopenharmony_ci		unsigned long flags;
5188c2ecf20Sopenharmony_ci		u8 i;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		lbs_deb_cs("cmd resp\n");
5218c2ecf20Sopenharmony_ci		spin_lock_irqsave(&priv->driver_lock, flags);
5228c2ecf20Sopenharmony_ci		i = (priv->resp_idx == 0) ? 1 : 0;
5238c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->driver_lock, flags);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		BUG_ON(priv->resp_len[i]);
5268c2ecf20Sopenharmony_ci		if_cs_receive_cmdres(priv, priv->resp_buf[i],
5278c2ecf20Sopenharmony_ci			&priv->resp_len[i]);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		spin_lock_irqsave(&priv->driver_lock, flags);
5308c2ecf20Sopenharmony_ci		lbs_notify_command_response(priv, i);
5318c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&priv->driver_lock, flags);
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	if (cause & IF_CS_BIT_EVENT) {
5358c2ecf20Sopenharmony_ci		u16 status = if_cs_read16(priv->card, IF_CS_CARD_STATUS);
5368c2ecf20Sopenharmony_ci		if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE,
5378c2ecf20Sopenharmony_ci			IF_CS_BIT_EVENT);
5388c2ecf20Sopenharmony_ci		lbs_queue_event(priv, (status & IF_CS_CARD_STATUS_MASK) >> 8);
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	/* Clear interrupt cause */
5428c2ecf20Sopenharmony_ci	if_cs_write16(card, IF_CS_CARD_INT_CAUSE, cause & IF_CS_BIT_MASK);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
5458c2ecf20Sopenharmony_ci}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci/********************************************************************/
5518c2ecf20Sopenharmony_ci/* Firmware                                                         */
5528c2ecf20Sopenharmony_ci/********************************************************************/
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci/*
5558c2ecf20Sopenharmony_ci * Tries to program the helper firmware.
5568c2ecf20Sopenharmony_ci *
5578c2ecf20Sopenharmony_ci * Return 0 on success
5588c2ecf20Sopenharmony_ci */
5598c2ecf20Sopenharmony_cistatic int if_cs_prog_helper(struct if_cs_card *card, const struct firmware *fw)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	int ret = 0;
5628c2ecf20Sopenharmony_ci	int sent = 0;
5638c2ecf20Sopenharmony_ci	u8  scratch;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	/*
5668c2ecf20Sopenharmony_ci	 * This is the only place where an unaligned register access happens on
5678c2ecf20Sopenharmony_ci	 * the CF8305 card, therefore for the sake of speed of the driver, we do
5688c2ecf20Sopenharmony_ci	 * the alignment correction here.
5698c2ecf20Sopenharmony_ci	 */
5708c2ecf20Sopenharmony_ci	if (card->align_regs)
5718c2ecf20Sopenharmony_ci		scratch = if_cs_read16(card, IF_CS_SCRATCH) >> 8;
5728c2ecf20Sopenharmony_ci	else
5738c2ecf20Sopenharmony_ci		scratch = if_cs_read8(card, IF_CS_SCRATCH);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	/* "If the value is 0x5a, the firmware is already
5768c2ecf20Sopenharmony_ci	 * downloaded successfully"
5778c2ecf20Sopenharmony_ci	 */
5788c2ecf20Sopenharmony_ci	if (scratch == IF_CS_SCRATCH_HELPER_OK)
5798c2ecf20Sopenharmony_ci		goto done;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	/* "If the value is != 00, it is invalid value of register */
5828c2ecf20Sopenharmony_ci	if (scratch != IF_CS_SCRATCH_BOOT_OK) {
5838c2ecf20Sopenharmony_ci		ret = -ENODEV;
5848c2ecf20Sopenharmony_ci		goto done;
5858c2ecf20Sopenharmony_ci	}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	lbs_deb_cs("helper size %td\n", fw->size);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	/* "Set the 5 bytes of the helper image to 0" */
5908c2ecf20Sopenharmony_ci	/* Not needed, this contains an ARM branch instruction */
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	for (;;) {
5938c2ecf20Sopenharmony_ci		/* "the number of bytes to send is 256" */
5948c2ecf20Sopenharmony_ci		int count = 256;
5958c2ecf20Sopenharmony_ci		int remain = fw->size - sent;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci		if (remain < count)
5988c2ecf20Sopenharmony_ci			count = remain;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		/*
6018c2ecf20Sopenharmony_ci		 * "write the number of bytes to be sent to the I/O Command
6028c2ecf20Sopenharmony_ci		 * write length register"
6038c2ecf20Sopenharmony_ci		 */
6048c2ecf20Sopenharmony_ci		if_cs_write16(card, IF_CS_CMD_LEN, count);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci		/* "write this to I/O Command port register as 16 bit writes */
6078c2ecf20Sopenharmony_ci		if (count)
6088c2ecf20Sopenharmony_ci			if_cs_write16_rep(card, IF_CS_CMD,
6098c2ecf20Sopenharmony_ci				&fw->data[sent],
6108c2ecf20Sopenharmony_ci				count >> 1);
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci		/*
6138c2ecf20Sopenharmony_ci		 * "Assert the download over interrupt command in the Host
6148c2ecf20Sopenharmony_ci		 * status register"
6158c2ecf20Sopenharmony_ci		 */
6168c2ecf20Sopenharmony_ci		if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		/*
6198c2ecf20Sopenharmony_ci		 * "Assert the download over interrupt command in the Card
6208c2ecf20Sopenharmony_ci		 * interrupt case register"
6218c2ecf20Sopenharmony_ci		 */
6228c2ecf20Sopenharmony_ci		if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci		/*
6258c2ecf20Sopenharmony_ci		 * "The host polls the Card Status register ... for 50 ms before
6268c2ecf20Sopenharmony_ci		 * declaring a failure"
6278c2ecf20Sopenharmony_ci		 */
6288c2ecf20Sopenharmony_ci		ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS,
6298c2ecf20Sopenharmony_ci			IF_CS_BIT_COMMAND);
6308c2ecf20Sopenharmony_ci		if (ret < 0) {
6318c2ecf20Sopenharmony_ci			pr_err("can't download helper at 0x%x, ret %d\n",
6328c2ecf20Sopenharmony_ci			       sent, ret);
6338c2ecf20Sopenharmony_ci			goto done;
6348c2ecf20Sopenharmony_ci		}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci		if (count == 0)
6378c2ecf20Sopenharmony_ci			break;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		sent += count;
6408c2ecf20Sopenharmony_ci	}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cidone:
6438c2ecf20Sopenharmony_ci	return ret;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic int if_cs_prog_real(struct if_cs_card *card, const struct firmware *fw)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	int ret = 0;
6508c2ecf20Sopenharmony_ci	int retry = 0;
6518c2ecf20Sopenharmony_ci	int len = 0;
6528c2ecf20Sopenharmony_ci	int sent;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	lbs_deb_cs("fw size %td\n", fw->size);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW,
6578c2ecf20Sopenharmony_ci		IF_CS_SQ_HELPER_OK);
6588c2ecf20Sopenharmony_ci	if (ret < 0) {
6598c2ecf20Sopenharmony_ci		pr_err("helper firmware doesn't answer\n");
6608c2ecf20Sopenharmony_ci		goto done;
6618c2ecf20Sopenharmony_ci	}
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	for (sent = 0; sent < fw->size; sent += len) {
6648c2ecf20Sopenharmony_ci		len = if_cs_read16(card, IF_CS_SQ_READ_LOW);
6658c2ecf20Sopenharmony_ci		if (len & 1) {
6668c2ecf20Sopenharmony_ci			retry++;
6678c2ecf20Sopenharmony_ci			pr_info("odd, need to retry this firmware block\n");
6688c2ecf20Sopenharmony_ci		} else {
6698c2ecf20Sopenharmony_ci			retry = 0;
6708c2ecf20Sopenharmony_ci		}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		if (retry > 20) {
6738c2ecf20Sopenharmony_ci			pr_err("could not download firmware\n");
6748c2ecf20Sopenharmony_ci			ret = -ENODEV;
6758c2ecf20Sopenharmony_ci			goto done;
6768c2ecf20Sopenharmony_ci		}
6778c2ecf20Sopenharmony_ci		if (retry) {
6788c2ecf20Sopenharmony_ci			sent -= len;
6798c2ecf20Sopenharmony_ci		}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci		if_cs_write16(card, IF_CS_CMD_LEN, len);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci		if_cs_write16_rep(card, IF_CS_CMD,
6858c2ecf20Sopenharmony_ci			&fw->data[sent],
6868c2ecf20Sopenharmony_ci			(len+1) >> 1);
6878c2ecf20Sopenharmony_ci		if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND);
6888c2ecf20Sopenharmony_ci		if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS,
6918c2ecf20Sopenharmony_ci			IF_CS_BIT_COMMAND);
6928c2ecf20Sopenharmony_ci		if (ret < 0) {
6938c2ecf20Sopenharmony_ci			pr_err("can't download firmware at 0x%x\n", sent);
6948c2ecf20Sopenharmony_ci			goto done;
6958c2ecf20Sopenharmony_ci		}
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a);
6998c2ecf20Sopenharmony_ci	if (ret < 0)
7008c2ecf20Sopenharmony_ci		pr_err("firmware download failed\n");
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cidone:
7038c2ecf20Sopenharmony_ci	return ret;
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic void if_cs_prog_firmware(struct lbs_private *priv, int ret,
7078c2ecf20Sopenharmony_ci				 const struct firmware *helper,
7088c2ecf20Sopenharmony_ci				 const struct firmware *mainfw)
7098c2ecf20Sopenharmony_ci{
7108c2ecf20Sopenharmony_ci	struct if_cs_card *card = priv->card;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	if (ret) {
7138c2ecf20Sopenharmony_ci		pr_err("failed to find firmware (%d)\n", ret);
7148c2ecf20Sopenharmony_ci		return;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	/* Load the firmware */
7188c2ecf20Sopenharmony_ci	ret = if_cs_prog_helper(card, helper);
7198c2ecf20Sopenharmony_ci	if (ret == 0 && (card->model != MODEL_8305))
7208c2ecf20Sopenharmony_ci		ret = if_cs_prog_real(card, mainfw);
7218c2ecf20Sopenharmony_ci	if (ret)
7228c2ecf20Sopenharmony_ci		return;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	/* Now actually get the IRQ */
7258c2ecf20Sopenharmony_ci	ret = request_irq(card->p_dev->irq, if_cs_interrupt,
7268c2ecf20Sopenharmony_ci		IRQF_SHARED, DRV_NAME, card);
7278c2ecf20Sopenharmony_ci	if (ret) {
7288c2ecf20Sopenharmony_ci		pr_err("error in request_irq\n");
7298c2ecf20Sopenharmony_ci		return;
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	/*
7338c2ecf20Sopenharmony_ci	 * Clear any interrupt cause that happened while sending
7348c2ecf20Sopenharmony_ci	 * firmware/initializing card
7358c2ecf20Sopenharmony_ci	 */
7368c2ecf20Sopenharmony_ci	if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK);
7378c2ecf20Sopenharmony_ci	if_cs_enable_ints(card);
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	/* And finally bring the card up */
7408c2ecf20Sopenharmony_ci	priv->fw_ready = 1;
7418c2ecf20Sopenharmony_ci	if (lbs_start_card(priv) != 0) {
7428c2ecf20Sopenharmony_ci		pr_err("could not activate card\n");
7438c2ecf20Sopenharmony_ci		free_irq(card->p_dev->irq, card);
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci/********************************************************************/
7498c2ecf20Sopenharmony_ci/* Callback functions for libertas.ko                               */
7508c2ecf20Sopenharmony_ci/********************************************************************/
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci/* Send commands or data packets to the card */
7538c2ecf20Sopenharmony_cistatic int if_cs_host_to_card(struct lbs_private *priv,
7548c2ecf20Sopenharmony_ci	u8 type,
7558c2ecf20Sopenharmony_ci	u8 *buf,
7568c2ecf20Sopenharmony_ci	u16 nb)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	int ret = -1;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	switch (type) {
7618c2ecf20Sopenharmony_ci	case MVMS_DAT:
7628c2ecf20Sopenharmony_ci		priv->dnld_sent = DNLD_DATA_SENT;
7638c2ecf20Sopenharmony_ci		if_cs_send_data(priv, buf, nb);
7648c2ecf20Sopenharmony_ci		ret = 0;
7658c2ecf20Sopenharmony_ci		break;
7668c2ecf20Sopenharmony_ci	case MVMS_CMD:
7678c2ecf20Sopenharmony_ci		priv->dnld_sent = DNLD_CMD_SENT;
7688c2ecf20Sopenharmony_ci		ret = if_cs_send_cmd(priv, buf, nb);
7698c2ecf20Sopenharmony_ci		break;
7708c2ecf20Sopenharmony_ci	default:
7718c2ecf20Sopenharmony_ci		netdev_err(priv->dev, "%s: unsupported type %d\n",
7728c2ecf20Sopenharmony_ci			   __func__, type);
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	return ret;
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic void if_cs_release(struct pcmcia_device *p_dev)
7808c2ecf20Sopenharmony_ci{
7818c2ecf20Sopenharmony_ci	struct if_cs_card *card = p_dev->priv;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	free_irq(p_dev->irq, card);
7848c2ecf20Sopenharmony_ci	pcmcia_disable_device(p_dev);
7858c2ecf20Sopenharmony_ci	if (card->iobase)
7868c2ecf20Sopenharmony_ci		ioport_unmap(card->iobase);
7878c2ecf20Sopenharmony_ci}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_cistatic int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data)
7918c2ecf20Sopenharmony_ci{
7928c2ecf20Sopenharmony_ci	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
7938c2ecf20Sopenharmony_ci	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if (p_dev->resource[1]->end) {
7968c2ecf20Sopenharmony_ci		pr_err("wrong CIS (check number of IO windows)\n");
7978c2ecf20Sopenharmony_ci		return -ENODEV;
7988c2ecf20Sopenharmony_ci	}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	/* This reserves IO space but doesn't actually enable it */
8018c2ecf20Sopenharmony_ci	return pcmcia_request_io(p_dev);
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cistatic int if_cs_probe(struct pcmcia_device *p_dev)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
8078c2ecf20Sopenharmony_ci	unsigned int prod_id;
8088c2ecf20Sopenharmony_ci	struct lbs_private *priv;
8098c2ecf20Sopenharmony_ci	struct if_cs_card *card;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL);
8128c2ecf20Sopenharmony_ci	if (!card)
8138c2ecf20Sopenharmony_ci		goto out;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	card->p_dev = p_dev;
8168c2ecf20Sopenharmony_ci	p_dev->priv = card;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) {
8218c2ecf20Sopenharmony_ci		pr_err("error in pcmcia_loop_config\n");
8228c2ecf20Sopenharmony_ci		goto out1;
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	/*
8268c2ecf20Sopenharmony_ci	 * Allocate an interrupt line.  Note that this does not assign
8278c2ecf20Sopenharmony_ci	 * a handler to the interrupt, unless the 'Handler' member of
8288c2ecf20Sopenharmony_ci	 * the irq structure is initialized.
8298c2ecf20Sopenharmony_ci	 */
8308c2ecf20Sopenharmony_ci	if (!p_dev->irq)
8318c2ecf20Sopenharmony_ci		goto out1;
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	/* Initialize io access */
8348c2ecf20Sopenharmony_ci	card->iobase = ioport_map(p_dev->resource[0]->start,
8358c2ecf20Sopenharmony_ci				resource_size(p_dev->resource[0]));
8368c2ecf20Sopenharmony_ci	if (!card->iobase) {
8378c2ecf20Sopenharmony_ci		pr_err("error in ioport_map\n");
8388c2ecf20Sopenharmony_ci		ret = -EIO;
8398c2ecf20Sopenharmony_ci		goto out1;
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	ret = pcmcia_enable_device(p_dev);
8438c2ecf20Sopenharmony_ci	if (ret) {
8448c2ecf20Sopenharmony_ci		pr_err("error in pcmcia_enable_device\n");
8458c2ecf20Sopenharmony_ci		goto out2;
8468c2ecf20Sopenharmony_ci	}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	/* Finally, report what we've done */
8498c2ecf20Sopenharmony_ci	lbs_deb_cs("irq %d, io %pR", p_dev->irq, p_dev->resource[0]);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	/*
8528c2ecf20Sopenharmony_ci	 * Most of the libertas cards can do unaligned register access, but some
8538c2ecf20Sopenharmony_ci	 * weird ones cannot. That's especially true for the CF8305 card.
8548c2ecf20Sopenharmony_ci	 */
8558c2ecf20Sopenharmony_ci	card->align_regs = false;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	card->model = get_model(p_dev->manf_id, p_dev->card_id);
8588c2ecf20Sopenharmony_ci	if (card->model == MODEL_UNKNOWN) {
8598c2ecf20Sopenharmony_ci		pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n",
8608c2ecf20Sopenharmony_ci		       p_dev->manf_id, p_dev->card_id);
8618c2ecf20Sopenharmony_ci		ret = -ENODEV;
8628c2ecf20Sopenharmony_ci		goto out2;
8638c2ecf20Sopenharmony_ci	}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	/* Check if we have a current silicon */
8668c2ecf20Sopenharmony_ci	prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID);
8678c2ecf20Sopenharmony_ci	if (card->model == MODEL_8305) {
8688c2ecf20Sopenharmony_ci		card->align_regs = true;
8698c2ecf20Sopenharmony_ci		if (prod_id < IF_CS_CF8305_B1_REV) {
8708c2ecf20Sopenharmony_ci			pr_err("8305 rev B0 and older are not supported\n");
8718c2ecf20Sopenharmony_ci			ret = -ENODEV;
8728c2ecf20Sopenharmony_ci			goto out2;
8738c2ecf20Sopenharmony_ci		}
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) {
8778c2ecf20Sopenharmony_ci		pr_err("8381 rev B2 and older are not supported\n");
8788c2ecf20Sopenharmony_ci		ret = -ENODEV;
8798c2ecf20Sopenharmony_ci		goto out2;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) {
8838c2ecf20Sopenharmony_ci		pr_err("8385 rev B0 and older are not supported\n");
8848c2ecf20Sopenharmony_ci		ret = -ENODEV;
8858c2ecf20Sopenharmony_ci		goto out2;
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	/* Make this card known to the libertas driver */
8898c2ecf20Sopenharmony_ci	priv = lbs_add_card(card, &p_dev->dev);
8908c2ecf20Sopenharmony_ci	if (IS_ERR(priv)) {
8918c2ecf20Sopenharmony_ci		ret = PTR_ERR(priv);
8928c2ecf20Sopenharmony_ci		goto out2;
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	/* Set up fields in lbs_private */
8968c2ecf20Sopenharmony_ci	card->priv = priv;
8978c2ecf20Sopenharmony_ci	priv->card = card;
8988c2ecf20Sopenharmony_ci	priv->hw_host_to_card = if_cs_host_to_card;
8998c2ecf20Sopenharmony_ci	priv->enter_deep_sleep = NULL;
9008c2ecf20Sopenharmony_ci	priv->exit_deep_sleep = NULL;
9018c2ecf20Sopenharmony_ci	priv->reset_deep_sleep_wakeup = NULL;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	/* Get firmware */
9048c2ecf20Sopenharmony_ci	ret = lbs_get_firmware_async(priv, &p_dev->dev, card->model, fw_table,
9058c2ecf20Sopenharmony_ci				     if_cs_prog_firmware);
9068c2ecf20Sopenharmony_ci	if (ret) {
9078c2ecf20Sopenharmony_ci		pr_err("failed to find firmware (%d)\n", ret);
9088c2ecf20Sopenharmony_ci		goto out3;
9098c2ecf20Sopenharmony_ci	}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	goto out;
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ciout3:
9148c2ecf20Sopenharmony_ci	lbs_remove_card(priv);
9158c2ecf20Sopenharmony_ciout2:
9168c2ecf20Sopenharmony_ci	ioport_unmap(card->iobase);
9178c2ecf20Sopenharmony_ciout1:
9188c2ecf20Sopenharmony_ci	pcmcia_disable_device(p_dev);
9198c2ecf20Sopenharmony_ciout:
9208c2ecf20Sopenharmony_ci	return ret;
9218c2ecf20Sopenharmony_ci}
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_cistatic void if_cs_detach(struct pcmcia_device *p_dev)
9258c2ecf20Sopenharmony_ci{
9268c2ecf20Sopenharmony_ci	struct if_cs_card *card = p_dev->priv;
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	lbs_stop_card(card->priv);
9298c2ecf20Sopenharmony_ci	lbs_remove_card(card->priv);
9308c2ecf20Sopenharmony_ci	if_cs_disable_ints(card);
9318c2ecf20Sopenharmony_ci	if_cs_release(p_dev);
9328c2ecf20Sopenharmony_ci	kfree(card);
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci/********************************************************************/
9388c2ecf20Sopenharmony_ci/* Module initialization                                            */
9398c2ecf20Sopenharmony_ci/********************************************************************/
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_cistatic const struct pcmcia_device_id if_cs_ids[] = {
9428c2ecf20Sopenharmony_ci	PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID),
9438c2ecf20Sopenharmony_ci	PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID),
9448c2ecf20Sopenharmony_ci	PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID),
9458c2ecf20Sopenharmony_ci	/* NOTE: keep in sync with get_model() */
9468c2ecf20Sopenharmony_ci	PCMCIA_DEVICE_NULL,
9478c2ecf20Sopenharmony_ci};
9488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pcmcia, if_cs_ids);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_cistatic struct pcmcia_driver lbs_driver = {
9518c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
9528c2ecf20Sopenharmony_ci	.name		= DRV_NAME,
9538c2ecf20Sopenharmony_ci	.probe		= if_cs_probe,
9548c2ecf20Sopenharmony_ci	.remove		= if_cs_detach,
9558c2ecf20Sopenharmony_ci	.id_table       = if_cs_ids,
9568c2ecf20Sopenharmony_ci};
9578c2ecf20Sopenharmony_cimodule_pcmcia_driver(lbs_driver);
958