162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci  Driver for the Marvell 8385 based compact flash WLAN cards.
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci  (C) 2007 by Holger Schurig <hs4233@mail.mn-solutions.de>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci*/
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/moduleparam.h>
1762306a36Sopenharmony_ci#include <linux/firmware.h>
1862306a36Sopenharmony_ci#include <linux/netdevice.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <pcmcia/cistpl.h>
2162306a36Sopenharmony_ci#include <pcmcia/ds.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/io.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define DRV_NAME "libertas_cs"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include "decl.h"
2862306a36Sopenharmony_ci#include "defs.h"
2962306a36Sopenharmony_ci#include "dev.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/********************************************************************/
3362306a36Sopenharmony_ci/* Module stuff                                                     */
3462306a36Sopenharmony_ci/********************************************************************/
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ciMODULE_AUTHOR("Holger Schurig <hs4233@mail.mn-solutions.de>");
3762306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards");
3862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/********************************************************************/
4362306a36Sopenharmony_ci/* Data structures                                                  */
4462306a36Sopenharmony_ci/********************************************************************/
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistruct if_cs_card {
4762306a36Sopenharmony_ci	struct pcmcia_device *p_dev;
4862306a36Sopenharmony_ci	struct lbs_private *priv;
4962306a36Sopenharmony_ci	void __iomem *iobase;
5062306a36Sopenharmony_ci	bool align_regs;
5162306a36Sopenharmony_ci	u32 model;
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cienum {
5662306a36Sopenharmony_ci	MODEL_UNKNOWN = 0x00,
5762306a36Sopenharmony_ci	MODEL_8305 = 0x01,
5862306a36Sopenharmony_ci	MODEL_8381 = 0x02,
5962306a36Sopenharmony_ci	MODEL_8385 = 0x03
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const struct lbs_fw_table fw_table[] = {
6362306a36Sopenharmony_ci	{ MODEL_8305, "libertas/cf8305.bin", NULL },
6462306a36Sopenharmony_ci	{ MODEL_8305, "libertas_cs_helper.fw", NULL },
6562306a36Sopenharmony_ci	{ MODEL_8381, "libertas/cf8381_helper.bin", "libertas/cf8381.bin" },
6662306a36Sopenharmony_ci	{ MODEL_8381, "libertas_cs_helper.fw", "libertas_cs.fw" },
6762306a36Sopenharmony_ci	{ MODEL_8385, "libertas/cf8385_helper.bin", "libertas/cf8385.bin" },
6862306a36Sopenharmony_ci	{ MODEL_8385, "libertas_cs_helper.fw", "libertas_cs.fw" },
6962306a36Sopenharmony_ci	{ 0, NULL, NULL }
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/cf8305.bin");
7262306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/cf8381_helper.bin");
7362306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/cf8381.bin");
7462306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/cf8385_helper.bin");
7562306a36Sopenharmony_ciMODULE_FIRMWARE("libertas/cf8385.bin");
7662306a36Sopenharmony_ciMODULE_FIRMWARE("libertas_cs_helper.fw");
7762306a36Sopenharmony_ciMODULE_FIRMWARE("libertas_cs.fw");
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/********************************************************************/
8162306a36Sopenharmony_ci/* Hardware access                                                  */
8262306a36Sopenharmony_ci/********************************************************************/
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/* This define enables wrapper functions which allow you
8562306a36Sopenharmony_ci   to dump all register accesses. You normally won't this,
8662306a36Sopenharmony_ci   except for development */
8762306a36Sopenharmony_ci/* #define DEBUG_IO */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#ifdef DEBUG_IO
9062306a36Sopenharmony_cistatic int debug_output = 0;
9162306a36Sopenharmony_ci#else
9262306a36Sopenharmony_ci/* This way the compiler optimizes the printk's away */
9362306a36Sopenharmony_ci#define debug_output 0
9462306a36Sopenharmony_ci#endif
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	unsigned int val = ioread8(card->iobase + reg);
9962306a36Sopenharmony_ci	if (debug_output)
10062306a36Sopenharmony_ci		printk(KERN_INFO "inb %08x<%02x\n", reg, val);
10162306a36Sopenharmony_ci	return val;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_cistatic inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	unsigned int val = ioread16(card->iobase + reg);
10662306a36Sopenharmony_ci	if (debug_output)
10762306a36Sopenharmony_ci		printk(KERN_INFO "inw %08x<%04x\n", reg, val);
10862306a36Sopenharmony_ci	return val;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_cistatic inline void if_cs_read16_rep(
11162306a36Sopenharmony_ci	struct if_cs_card *card,
11262306a36Sopenharmony_ci	uint reg,
11362306a36Sopenharmony_ci	void *buf,
11462306a36Sopenharmony_ci	unsigned long count)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	if (debug_output)
11762306a36Sopenharmony_ci		printk(KERN_INFO "insw %08x<(0x%lx words)\n",
11862306a36Sopenharmony_ci			reg, count);
11962306a36Sopenharmony_ci	ioread16_rep(card->iobase + reg, buf, count);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	if (debug_output)
12562306a36Sopenharmony_ci		printk(KERN_INFO "outb %08x>%02x\n", reg, val);
12662306a36Sopenharmony_ci	iowrite8(val, card->iobase + reg);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	if (debug_output)
13262306a36Sopenharmony_ci		printk(KERN_INFO "outw %08x>%04x\n", reg, val);
13362306a36Sopenharmony_ci	iowrite16(val, card->iobase + reg);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic inline void if_cs_write16_rep(
13762306a36Sopenharmony_ci	struct if_cs_card *card,
13862306a36Sopenharmony_ci	uint reg,
13962306a36Sopenharmony_ci	const void *buf,
14062306a36Sopenharmony_ci	unsigned long count)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	if (debug_output)
14362306a36Sopenharmony_ci		printk(KERN_INFO "outsw %08x>(0x%lx words)\n",
14462306a36Sopenharmony_ci			reg, count);
14562306a36Sopenharmony_ci	iowrite16_rep(card->iobase + reg, buf, count);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci * I know that polling/delaying is frowned upon. However, this procedure
15162306a36Sopenharmony_ci * with polling is needed while downloading the firmware. At this stage,
15262306a36Sopenharmony_ci * the hardware does unfortunately not create any interrupts.
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * Fortunately, this function is never used once the firmware is in
15562306a36Sopenharmony_ci * the card. :-)
15662306a36Sopenharmony_ci *
15762306a36Sopenharmony_ci * As a reference, see the "Firmware Specification v5.1", page 18
15862306a36Sopenharmony_ci * and 19. I did not follow their suggested timing to the word,
15962306a36Sopenharmony_ci * but this works nice & fast anyway.
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistatic int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 reg)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	int i;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	for (i = 0; i < 100000; i++) {
16662306a36Sopenharmony_ci		u8 val = if_cs_read8(card, addr);
16762306a36Sopenharmony_ci		if (val == reg)
16862306a36Sopenharmony_ci			return 0;
16962306a36Sopenharmony_ci		udelay(5);
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	return -ETIME;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/*
17762306a36Sopenharmony_ci * First the bitmasks for the host/card interrupt/status registers:
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_ci#define IF_CS_BIT_TX			0x0001
18062306a36Sopenharmony_ci#define IF_CS_BIT_RX			0x0002
18162306a36Sopenharmony_ci#define IF_CS_BIT_COMMAND		0x0004
18262306a36Sopenharmony_ci#define IF_CS_BIT_RESP			0x0008
18362306a36Sopenharmony_ci#define IF_CS_BIT_EVENT			0x0010
18462306a36Sopenharmony_ci#define	IF_CS_BIT_MASK			0x001f
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * It's not really clear to me what the host status register is for. It
19062306a36Sopenharmony_ci * needs to be set almost in union with "host int cause". The following
19162306a36Sopenharmony_ci * bits from above are used:
19262306a36Sopenharmony_ci *
19362306a36Sopenharmony_ci *   IF_CS_BIT_TX         driver downloaded a data packet
19462306a36Sopenharmony_ci *   IF_CS_BIT_RX         driver got a data packet
19562306a36Sopenharmony_ci *   IF_CS_BIT_COMMAND    driver downloaded a command
19662306a36Sopenharmony_ci *   IF_CS_BIT_RESP       not used (has some meaning with powerdown)
19762306a36Sopenharmony_ci *   IF_CS_BIT_EVENT      driver read a host event
19862306a36Sopenharmony_ci */
19962306a36Sopenharmony_ci#define IF_CS_HOST_STATUS		0x00000000
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/*
20262306a36Sopenharmony_ci * With the host int cause register can the host (that is, Linux) cause
20362306a36Sopenharmony_ci * an interrupt in the firmware, to tell the firmware about those events:
20462306a36Sopenharmony_ci *
20562306a36Sopenharmony_ci *   IF_CS_BIT_TX         a data packet has been downloaded
20662306a36Sopenharmony_ci *   IF_CS_BIT_RX         a received data packet has retrieved
20762306a36Sopenharmony_ci *   IF_CS_BIT_COMMAND    a firmware block or a command has been downloaded
20862306a36Sopenharmony_ci *   IF_CS_BIT_RESP       not used (has some meaning with powerdown)
20962306a36Sopenharmony_ci *   IF_CS_BIT_EVENT      a host event (link lost etc) has been retrieved
21062306a36Sopenharmony_ci */
21162306a36Sopenharmony_ci#define IF_CS_HOST_INT_CAUSE		0x00000002
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/*
21462306a36Sopenharmony_ci * The host int mask register is used to enable/disable interrupt.  However,
21562306a36Sopenharmony_ci * I have the suspicion that disabled interrupts are lost.
21662306a36Sopenharmony_ci */
21762306a36Sopenharmony_ci#define IF_CS_HOST_INT_MASK		0x00000004
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/*
22062306a36Sopenharmony_ci * Used to send or receive data packets:
22162306a36Sopenharmony_ci */
22262306a36Sopenharmony_ci#define IF_CS_WRITE			0x00000016
22362306a36Sopenharmony_ci#define IF_CS_WRITE_LEN			0x00000014
22462306a36Sopenharmony_ci#define IF_CS_READ			0x00000010
22562306a36Sopenharmony_ci#define IF_CS_READ_LEN			0x00000024
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/*
22862306a36Sopenharmony_ci * Used to send commands (and to send firmware block) and to
22962306a36Sopenharmony_ci * receive command responses:
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_ci#define IF_CS_CMD			0x0000001A
23262306a36Sopenharmony_ci#define IF_CS_CMD_LEN			0x00000018
23362306a36Sopenharmony_ci#define IF_CS_RESP			0x00000012
23462306a36Sopenharmony_ci#define IF_CS_RESP_LEN			0x00000030
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/*
23762306a36Sopenharmony_ci * The card status registers shows what the card/firmware actually
23862306a36Sopenharmony_ci * accepts:
23962306a36Sopenharmony_ci *
24062306a36Sopenharmony_ci *   IF_CS_BIT_TX        you may send a data packet
24162306a36Sopenharmony_ci *   IF_CS_BIT_RX        you may retrieve a data packet
24262306a36Sopenharmony_ci *   IF_CS_BIT_COMMAND   you may send a command
24362306a36Sopenharmony_ci *   IF_CS_BIT_RESP      you may retrieve a command response
24462306a36Sopenharmony_ci *   IF_CS_BIT_EVENT     the card has a event for use (link lost, snr low etc)
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * When reading this register several times, you will get back the same
24762306a36Sopenharmony_ci * results --- with one exception: the IF_CS_BIT_EVENT clear itself
24862306a36Sopenharmony_ci * automatically.
24962306a36Sopenharmony_ci *
25062306a36Sopenharmony_ci * Not that we don't rely on BIT_RX,_BIT_RESP or BIT_EVENT because
25162306a36Sopenharmony_ci * we handle this via the card int cause register.
25262306a36Sopenharmony_ci */
25362306a36Sopenharmony_ci#define IF_CS_CARD_STATUS		0x00000020
25462306a36Sopenharmony_ci#define IF_CS_CARD_STATUS_MASK		0x7f00
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/*
25762306a36Sopenharmony_ci * The card int cause register is used by the card/firmware to notify us
25862306a36Sopenharmony_ci * about the following events:
25962306a36Sopenharmony_ci *
26062306a36Sopenharmony_ci *   IF_CS_BIT_TX        a data packet has successfully been sentx
26162306a36Sopenharmony_ci *   IF_CS_BIT_RX        a data packet has been received and can be retrieved
26262306a36Sopenharmony_ci *   IF_CS_BIT_COMMAND   not used
26362306a36Sopenharmony_ci *   IF_CS_BIT_RESP      the firmware has a command response for us
26462306a36Sopenharmony_ci *   IF_CS_BIT_EVENT     the card has a event for use (link lost, snr low etc)
26562306a36Sopenharmony_ci */
26662306a36Sopenharmony_ci#define IF_CS_CARD_INT_CAUSE		0x00000022
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/*
26962306a36Sopenharmony_ci * This is used to for handshaking with the card's bootloader/helper image
27062306a36Sopenharmony_ci * to synchronize downloading of firmware blocks.
27162306a36Sopenharmony_ci */
27262306a36Sopenharmony_ci#define IF_CS_SQ_READ_LOW		0x00000028
27362306a36Sopenharmony_ci#define IF_CS_SQ_HELPER_OK		0x10
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/*
27662306a36Sopenharmony_ci * The scratch register tells us ...
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * IF_CS_SCRATCH_BOOT_OK     the bootloader runs
27962306a36Sopenharmony_ci * IF_CS_SCRATCH_HELPER_OK   the helper firmware already runs
28062306a36Sopenharmony_ci */
28162306a36Sopenharmony_ci#define IF_CS_SCRATCH			0x0000003F
28262306a36Sopenharmony_ci#define IF_CS_SCRATCH_BOOT_OK		0x00
28362306a36Sopenharmony_ci#define IF_CS_SCRATCH_HELPER_OK		0x5a
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/*
28662306a36Sopenharmony_ci * Used to detect ancient chips:
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_ci#define IF_CS_PRODUCT_ID		0x0000001C
28962306a36Sopenharmony_ci#define IF_CS_CF8385_B1_REV		0x12
29062306a36Sopenharmony_ci#define IF_CS_CF8381_B3_REV		0x04
29162306a36Sopenharmony_ci#define IF_CS_CF8305_B1_REV		0x03
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci/*
29462306a36Sopenharmony_ci * Used to detect other cards than CF8385 since their revisions of silicon
29562306a36Sopenharmony_ci * doesn't match those from CF8385, eg. CF8381 B3 works with this driver.
29662306a36Sopenharmony_ci */
29762306a36Sopenharmony_ci#define CF8305_MANFID		0x02db
29862306a36Sopenharmony_ci#define CF8305_CARDID		0x8103
29962306a36Sopenharmony_ci#define CF8381_MANFID		0x02db
30062306a36Sopenharmony_ci#define CF8381_CARDID		0x6064
30162306a36Sopenharmony_ci#define CF8385_MANFID		0x02df
30262306a36Sopenharmony_ci#define CF8385_CARDID		0x8103
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci/*
30562306a36Sopenharmony_ci * FIXME: just use the 'driver_info' field of 'struct pcmcia_device_id' when
30662306a36Sopenharmony_ci * that gets fixed.  Currently there's no way to access it from the probe hook.
30762306a36Sopenharmony_ci */
30862306a36Sopenharmony_cistatic inline u32 get_model(u16 manf_id, u16 card_id)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	/* NOTE: keep in sync with if_cs_ids */
31162306a36Sopenharmony_ci	if (manf_id == CF8305_MANFID && card_id == CF8305_CARDID)
31262306a36Sopenharmony_ci		return MODEL_8305;
31362306a36Sopenharmony_ci	else if (manf_id == CF8381_MANFID && card_id == CF8381_CARDID)
31462306a36Sopenharmony_ci		return MODEL_8381;
31562306a36Sopenharmony_ci	else if (manf_id == CF8385_MANFID && card_id == CF8385_CARDID)
31662306a36Sopenharmony_ci		return MODEL_8385;
31762306a36Sopenharmony_ci	return MODEL_UNKNOWN;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci/********************************************************************/
32162306a36Sopenharmony_ci/* I/O and interrupt handling                                       */
32262306a36Sopenharmony_ci/********************************************************************/
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cistatic inline void if_cs_enable_ints(struct if_cs_card *card)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_INT_MASK, 0);
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic inline void if_cs_disable_ints(struct if_cs_card *card)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_INT_MASK, IF_CS_BIT_MASK);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci/*
33562306a36Sopenharmony_ci * Called from if_cs_host_to_card to send a command to the hardware
33662306a36Sopenharmony_ci */
33762306a36Sopenharmony_cistatic int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct if_cs_card *card = (struct if_cs_card *)priv->card;
34062306a36Sopenharmony_ci	int ret = -1;
34162306a36Sopenharmony_ci	int loops = 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if_cs_disable_ints(card);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* Is hardware ready? */
34662306a36Sopenharmony_ci	while (1) {
34762306a36Sopenharmony_ci		u16 status = if_cs_read16(card, IF_CS_CARD_STATUS);
34862306a36Sopenharmony_ci		if (status & IF_CS_BIT_COMMAND)
34962306a36Sopenharmony_ci			break;
35062306a36Sopenharmony_ci		if (++loops > 100) {
35162306a36Sopenharmony_ci			netdev_err(priv->dev, "card not ready for commands\n");
35262306a36Sopenharmony_ci			goto done;
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci		mdelay(1);
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if_cs_write16(card, IF_CS_CMD_LEN, nb);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if_cs_write16_rep(card, IF_CS_CMD, buf, nb / 2);
36062306a36Sopenharmony_ci	/* Are we supposed to transfer an odd amount of bytes? */
36162306a36Sopenharmony_ci	if (nb & 1)
36262306a36Sopenharmony_ci		if_cs_write8(card, IF_CS_CMD, buf[nb-1]);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* "Assert the download over interrupt command in the Host
36562306a36Sopenharmony_ci	 * status register" */
36662306a36Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* "Assert the download over interrupt command in the Card
36962306a36Sopenharmony_ci	 * interrupt case register" */
37062306a36Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND);
37162306a36Sopenharmony_ci	ret = 0;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cidone:
37462306a36Sopenharmony_ci	if_cs_enable_ints(card);
37562306a36Sopenharmony_ci	return ret;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/*
37962306a36Sopenharmony_ci * Called from if_cs_host_to_card to send a data to the hardware
38062306a36Sopenharmony_ci */
38162306a36Sopenharmony_cistatic void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct if_cs_card *card = (struct if_cs_card *)priv->card;
38462306a36Sopenharmony_ci	u16 status;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if_cs_disable_ints(card);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	status = if_cs_read16(card, IF_CS_CARD_STATUS);
38962306a36Sopenharmony_ci	BUG_ON((status & IF_CS_BIT_TX) == 0);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if_cs_write16(card, IF_CS_WRITE_LEN, nb);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* write even number of bytes, then odd byte if necessary */
39462306a36Sopenharmony_ci	if_cs_write16_rep(card, IF_CS_WRITE, buf, nb / 2);
39562306a36Sopenharmony_ci	if (nb & 1)
39662306a36Sopenharmony_ci		if_cs_write8(card, IF_CS_WRITE, buf[nb-1]);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_STATUS, IF_CS_BIT_TX);
39962306a36Sopenharmony_ci	if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_TX);
40062306a36Sopenharmony_ci	if_cs_enable_ints(card);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci/*
40462306a36Sopenharmony_ci * Get the command result out of the card.
40562306a36Sopenharmony_ci */
40662306a36Sopenharmony_cistatic int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	unsigned long flags;
40962306a36Sopenharmony_ci	int ret = -1;
41062306a36Sopenharmony_ci	u16 status;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* is hardware ready? */
41362306a36Sopenharmony_ci	status = if_cs_read16(priv->card, IF_CS_CARD_STATUS);
41462306a36Sopenharmony_ci	if ((status & IF_CS_BIT_RESP) == 0) {
41562306a36Sopenharmony_ci		netdev_err(priv->dev, "no cmd response in card\n");
41662306a36Sopenharmony_ci		*len = 0;
41762306a36Sopenharmony_ci		goto out;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	*len = if_cs_read16(priv->card, IF_CS_RESP_LEN);
42162306a36Sopenharmony_ci	if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) {
42262306a36Sopenharmony_ci		netdev_err(priv->dev,
42362306a36Sopenharmony_ci			   "card cmd buffer has invalid # of bytes (%d)\n",
42462306a36Sopenharmony_ci			   *len);
42562306a36Sopenharmony_ci		goto out;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/* read even number of bytes, then odd byte if necessary */
42962306a36Sopenharmony_ci	if_cs_read16_rep(priv->card, IF_CS_RESP, data, *len/sizeof(u16));
43062306a36Sopenharmony_ci	if (*len & 1)
43162306a36Sopenharmony_ci		data[*len-1] = if_cs_read8(priv->card, IF_CS_RESP);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	/* This is a workaround for a firmware that reports too much
43462306a36Sopenharmony_ci	 * bytes */
43562306a36Sopenharmony_ci	*len -= 8;
43662306a36Sopenharmony_ci	ret = 0;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	/* Clear this flag again */
43962306a36Sopenharmony_ci	spin_lock_irqsave(&priv->driver_lock, flags);
44062306a36Sopenharmony_ci	priv->dnld_sent = DNLD_RES_RECEIVED;
44162306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->driver_lock, flags);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ciout:
44462306a36Sopenharmony_ci	return ret;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic struct sk_buff *if_cs_receive_data(struct lbs_private *priv)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
45062306a36Sopenharmony_ci	u16 len;
45162306a36Sopenharmony_ci	u8 *data;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	len = if_cs_read16(priv->card, IF_CS_READ_LEN);
45462306a36Sopenharmony_ci	if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
45562306a36Sopenharmony_ci		netdev_err(priv->dev,
45662306a36Sopenharmony_ci			   "card data buffer has invalid # of bytes (%d)\n",
45762306a36Sopenharmony_ci			   len);
45862306a36Sopenharmony_ci		priv->dev->stats.rx_dropped++;
45962306a36Sopenharmony_ci		goto dat_err;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2);
46362306a36Sopenharmony_ci	if (!skb)
46462306a36Sopenharmony_ci		goto out;
46562306a36Sopenharmony_ci	skb_put(skb, len);
46662306a36Sopenharmony_ci	skb_reserve(skb, 2);/* 16 byte align */
46762306a36Sopenharmony_ci	data = skb->data;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	/* read even number of bytes, then odd byte if necessary */
47062306a36Sopenharmony_ci	if_cs_read16_rep(priv->card, IF_CS_READ, data, len/sizeof(u16));
47162306a36Sopenharmony_ci	if (len & 1)
47262306a36Sopenharmony_ci		data[len-1] = if_cs_read8(priv->card, IF_CS_READ);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cidat_err:
47562306a36Sopenharmony_ci	if_cs_write16(priv->card, IF_CS_HOST_STATUS, IF_CS_BIT_RX);
47662306a36Sopenharmony_ci	if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_RX);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ciout:
47962306a36Sopenharmony_ci	return skb;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic irqreturn_t if_cs_interrupt(int irq, void *data)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct if_cs_card *card = data;
48562306a36Sopenharmony_ci	struct lbs_private *priv = card->priv;
48662306a36Sopenharmony_ci	u16 cause;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* Ask card interrupt cause register if there is something for us */
48962306a36Sopenharmony_ci	cause = if_cs_read16(card, IF_CS_CARD_INT_CAUSE);
49062306a36Sopenharmony_ci	lbs_deb_cs("cause 0x%04x\n", cause);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (cause == 0) {
49362306a36Sopenharmony_ci		/* Not for us */
49462306a36Sopenharmony_ci		return IRQ_NONE;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	if (cause == 0xffff) {
49862306a36Sopenharmony_ci		/* Read in junk, the card has probably been removed */
49962306a36Sopenharmony_ci		card->priv->surpriseremoved = 1;
50062306a36Sopenharmony_ci		return IRQ_HANDLED;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (cause & IF_CS_BIT_RX) {
50462306a36Sopenharmony_ci		struct sk_buff *skb;
50562306a36Sopenharmony_ci		lbs_deb_cs("rx packet\n");
50662306a36Sopenharmony_ci		skb = if_cs_receive_data(priv);
50762306a36Sopenharmony_ci		if (skb)
50862306a36Sopenharmony_ci			lbs_process_rxed_packet(priv, skb);
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (cause & IF_CS_BIT_TX) {
51262306a36Sopenharmony_ci		lbs_deb_cs("tx done\n");
51362306a36Sopenharmony_ci		lbs_host_to_card_done(priv);
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (cause & IF_CS_BIT_RESP) {
51762306a36Sopenharmony_ci		unsigned long flags;
51862306a36Sopenharmony_ci		u8 i;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		lbs_deb_cs("cmd resp\n");
52162306a36Sopenharmony_ci		spin_lock_irqsave(&priv->driver_lock, flags);
52262306a36Sopenharmony_ci		i = (priv->resp_idx == 0) ? 1 : 0;
52362306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->driver_lock, flags);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci		BUG_ON(priv->resp_len[i]);
52662306a36Sopenharmony_ci		if_cs_receive_cmdres(priv, priv->resp_buf[i],
52762306a36Sopenharmony_ci			&priv->resp_len[i]);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci		spin_lock_irqsave(&priv->driver_lock, flags);
53062306a36Sopenharmony_ci		lbs_notify_command_response(priv, i);
53162306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->driver_lock, flags);
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (cause & IF_CS_BIT_EVENT) {
53562306a36Sopenharmony_ci		u16 status = if_cs_read16(priv->card, IF_CS_CARD_STATUS);
53662306a36Sopenharmony_ci		if_cs_write16(priv->card, IF_CS_HOST_INT_CAUSE,
53762306a36Sopenharmony_ci			IF_CS_BIT_EVENT);
53862306a36Sopenharmony_ci		lbs_queue_event(priv, (status & IF_CS_CARD_STATUS_MASK) >> 8);
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	/* Clear interrupt cause */
54262306a36Sopenharmony_ci	if_cs_write16(card, IF_CS_CARD_INT_CAUSE, cause & IF_CS_BIT_MASK);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	return IRQ_HANDLED;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/********************************************************************/
55162306a36Sopenharmony_ci/* Firmware                                                         */
55262306a36Sopenharmony_ci/********************************************************************/
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci/*
55562306a36Sopenharmony_ci * Tries to program the helper firmware.
55662306a36Sopenharmony_ci *
55762306a36Sopenharmony_ci * Return 0 on success
55862306a36Sopenharmony_ci */
55962306a36Sopenharmony_cistatic int if_cs_prog_helper(struct if_cs_card *card, const struct firmware *fw)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	int ret = 0;
56262306a36Sopenharmony_ci	int sent = 0;
56362306a36Sopenharmony_ci	u8  scratch;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	/*
56662306a36Sopenharmony_ci	 * This is the only place where an unaligned register access happens on
56762306a36Sopenharmony_ci	 * the CF8305 card, therefore for the sake of speed of the driver, we do
56862306a36Sopenharmony_ci	 * the alignment correction here.
56962306a36Sopenharmony_ci	 */
57062306a36Sopenharmony_ci	if (card->align_regs)
57162306a36Sopenharmony_ci		scratch = if_cs_read16(card, IF_CS_SCRATCH) >> 8;
57262306a36Sopenharmony_ci	else
57362306a36Sopenharmony_ci		scratch = if_cs_read8(card, IF_CS_SCRATCH);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	/* "If the value is 0x5a, the firmware is already
57662306a36Sopenharmony_ci	 * downloaded successfully"
57762306a36Sopenharmony_ci	 */
57862306a36Sopenharmony_ci	if (scratch == IF_CS_SCRATCH_HELPER_OK)
57962306a36Sopenharmony_ci		goto done;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	/* "If the value is != 00, it is invalid value of register */
58262306a36Sopenharmony_ci	if (scratch != IF_CS_SCRATCH_BOOT_OK) {
58362306a36Sopenharmony_ci		ret = -ENODEV;
58462306a36Sopenharmony_ci		goto done;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	lbs_deb_cs("helper size %td\n", fw->size);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* "Set the 5 bytes of the helper image to 0" */
59062306a36Sopenharmony_ci	/* Not needed, this contains an ARM branch instruction */
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	for (;;) {
59362306a36Sopenharmony_ci		/* "the number of bytes to send is 256" */
59462306a36Sopenharmony_ci		int count = 256;
59562306a36Sopenharmony_ci		int remain = fw->size - sent;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		if (remain < count)
59862306a36Sopenharmony_ci			count = remain;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		/*
60162306a36Sopenharmony_ci		 * "write the number of bytes to be sent to the I/O Command
60262306a36Sopenharmony_ci		 * write length register"
60362306a36Sopenharmony_ci		 */
60462306a36Sopenharmony_ci		if_cs_write16(card, IF_CS_CMD_LEN, count);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		/* "write this to I/O Command port register as 16 bit writes */
60762306a36Sopenharmony_ci		if (count)
60862306a36Sopenharmony_ci			if_cs_write16_rep(card, IF_CS_CMD,
60962306a36Sopenharmony_ci				&fw->data[sent],
61062306a36Sopenharmony_ci				count >> 1);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		/*
61362306a36Sopenharmony_ci		 * "Assert the download over interrupt command in the Host
61462306a36Sopenharmony_ci		 * status register"
61562306a36Sopenharmony_ci		 */
61662306a36Sopenharmony_ci		if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		/*
61962306a36Sopenharmony_ci		 * "Assert the download over interrupt command in the Card
62062306a36Sopenharmony_ci		 * interrupt case register"
62162306a36Sopenharmony_ci		 */
62262306a36Sopenharmony_ci		if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		/*
62562306a36Sopenharmony_ci		 * "The host polls the Card Status register ... for 50 ms before
62662306a36Sopenharmony_ci		 * declaring a failure"
62762306a36Sopenharmony_ci		 */
62862306a36Sopenharmony_ci		ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS,
62962306a36Sopenharmony_ci			IF_CS_BIT_COMMAND);
63062306a36Sopenharmony_ci		if (ret < 0) {
63162306a36Sopenharmony_ci			pr_err("can't download helper at 0x%x, ret %d\n",
63262306a36Sopenharmony_ci			       sent, ret);
63362306a36Sopenharmony_ci			goto done;
63462306a36Sopenharmony_ci		}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci		if (count == 0)
63762306a36Sopenharmony_ci			break;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci		sent += count;
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cidone:
64362306a36Sopenharmony_ci	return ret;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_cistatic int if_cs_prog_real(struct if_cs_card *card, const struct firmware *fw)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	int ret = 0;
65062306a36Sopenharmony_ci	int retry = 0;
65162306a36Sopenharmony_ci	int len = 0;
65262306a36Sopenharmony_ci	int sent;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	lbs_deb_cs("fw size %td\n", fw->size);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	ret = if_cs_poll_while_fw_download(card, IF_CS_SQ_READ_LOW,
65762306a36Sopenharmony_ci		IF_CS_SQ_HELPER_OK);
65862306a36Sopenharmony_ci	if (ret < 0) {
65962306a36Sopenharmony_ci		pr_err("helper firmware doesn't answer\n");
66062306a36Sopenharmony_ci		goto done;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	for (sent = 0; sent < fw->size; sent += len) {
66462306a36Sopenharmony_ci		len = if_cs_read16(card, IF_CS_SQ_READ_LOW);
66562306a36Sopenharmony_ci		if (len & 1) {
66662306a36Sopenharmony_ci			retry++;
66762306a36Sopenharmony_ci			pr_info("odd, need to retry this firmware block\n");
66862306a36Sopenharmony_ci		} else {
66962306a36Sopenharmony_ci			retry = 0;
67062306a36Sopenharmony_ci		}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		if (retry > 20) {
67362306a36Sopenharmony_ci			pr_err("could not download firmware\n");
67462306a36Sopenharmony_ci			ret = -ENODEV;
67562306a36Sopenharmony_ci			goto done;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci		if (retry) {
67862306a36Sopenharmony_ci			sent -= len;
67962306a36Sopenharmony_ci		}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci		if_cs_write16(card, IF_CS_CMD_LEN, len);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		if_cs_write16_rep(card, IF_CS_CMD,
68562306a36Sopenharmony_ci			&fw->data[sent],
68662306a36Sopenharmony_ci			(len+1) >> 1);
68762306a36Sopenharmony_ci		if_cs_write8(card, IF_CS_HOST_STATUS, IF_CS_BIT_COMMAND);
68862306a36Sopenharmony_ci		if_cs_write16(card, IF_CS_HOST_INT_CAUSE, IF_CS_BIT_COMMAND);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		ret = if_cs_poll_while_fw_download(card, IF_CS_CARD_STATUS,
69162306a36Sopenharmony_ci			IF_CS_BIT_COMMAND);
69262306a36Sopenharmony_ci		if (ret < 0) {
69362306a36Sopenharmony_ci			pr_err("can't download firmware at 0x%x\n", sent);
69462306a36Sopenharmony_ci			goto done;
69562306a36Sopenharmony_ci		}
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a);
69962306a36Sopenharmony_ci	if (ret < 0)
70062306a36Sopenharmony_ci		pr_err("firmware download failed\n");
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cidone:
70362306a36Sopenharmony_ci	return ret;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic void if_cs_prog_firmware(struct lbs_private *priv, int ret,
70762306a36Sopenharmony_ci				 const struct firmware *helper,
70862306a36Sopenharmony_ci				 const struct firmware *mainfw)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct if_cs_card *card = priv->card;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (ret) {
71362306a36Sopenharmony_ci		pr_err("failed to find firmware (%d)\n", ret);
71462306a36Sopenharmony_ci		return;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	/* Load the firmware */
71862306a36Sopenharmony_ci	ret = if_cs_prog_helper(card, helper);
71962306a36Sopenharmony_ci	if (ret == 0 && (card->model != MODEL_8305))
72062306a36Sopenharmony_ci		ret = if_cs_prog_real(card, mainfw);
72162306a36Sopenharmony_ci	if (ret)
72262306a36Sopenharmony_ci		return;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	/* Now actually get the IRQ */
72562306a36Sopenharmony_ci	ret = request_irq(card->p_dev->irq, if_cs_interrupt,
72662306a36Sopenharmony_ci		IRQF_SHARED, DRV_NAME, card);
72762306a36Sopenharmony_ci	if (ret) {
72862306a36Sopenharmony_ci		pr_err("error in request_irq\n");
72962306a36Sopenharmony_ci		return;
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	/*
73362306a36Sopenharmony_ci	 * Clear any interrupt cause that happened while sending
73462306a36Sopenharmony_ci	 * firmware/initializing card
73562306a36Sopenharmony_ci	 */
73662306a36Sopenharmony_ci	if_cs_write16(card, IF_CS_CARD_INT_CAUSE, IF_CS_BIT_MASK);
73762306a36Sopenharmony_ci	if_cs_enable_ints(card);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/* And finally bring the card up */
74062306a36Sopenharmony_ci	priv->fw_ready = 1;
74162306a36Sopenharmony_ci	if (lbs_start_card(priv) != 0) {
74262306a36Sopenharmony_ci		pr_err("could not activate card\n");
74362306a36Sopenharmony_ci		free_irq(card->p_dev->irq, card);
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci/********************************************************************/
74962306a36Sopenharmony_ci/* Callback functions for libertas.ko                               */
75062306a36Sopenharmony_ci/********************************************************************/
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci/* Send commands or data packets to the card */
75362306a36Sopenharmony_cistatic int if_cs_host_to_card(struct lbs_private *priv,
75462306a36Sopenharmony_ci	u8 type,
75562306a36Sopenharmony_ci	u8 *buf,
75662306a36Sopenharmony_ci	u16 nb)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	int ret = -1;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	switch (type) {
76162306a36Sopenharmony_ci	case MVMS_DAT:
76262306a36Sopenharmony_ci		priv->dnld_sent = DNLD_DATA_SENT;
76362306a36Sopenharmony_ci		if_cs_send_data(priv, buf, nb);
76462306a36Sopenharmony_ci		ret = 0;
76562306a36Sopenharmony_ci		break;
76662306a36Sopenharmony_ci	case MVMS_CMD:
76762306a36Sopenharmony_ci		priv->dnld_sent = DNLD_CMD_SENT;
76862306a36Sopenharmony_ci		ret = if_cs_send_cmd(priv, buf, nb);
76962306a36Sopenharmony_ci		break;
77062306a36Sopenharmony_ci	default:
77162306a36Sopenharmony_ci		netdev_err(priv->dev, "%s: unsupported type %d\n",
77262306a36Sopenharmony_ci			   __func__, type);
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	return ret;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_cistatic void if_cs_release(struct pcmcia_device *p_dev)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct if_cs_card *card = p_dev->priv;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	free_irq(p_dev->irq, card);
78462306a36Sopenharmony_ci	pcmcia_disable_device(p_dev);
78562306a36Sopenharmony_ci	if (card->iobase)
78662306a36Sopenharmony_ci		ioport_unmap(card->iobase);
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic int if_cs_ioprobe(struct pcmcia_device *p_dev, void *priv_data)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
79362306a36Sopenharmony_ci	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (p_dev->resource[1]->end) {
79662306a36Sopenharmony_ci		pr_err("wrong CIS (check number of IO windows)\n");
79762306a36Sopenharmony_ci		return -ENODEV;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	/* This reserves IO space but doesn't actually enable it */
80162306a36Sopenharmony_ci	return pcmcia_request_io(p_dev);
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic int if_cs_probe(struct pcmcia_device *p_dev)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	int ret = -ENOMEM;
80762306a36Sopenharmony_ci	unsigned int prod_id;
80862306a36Sopenharmony_ci	struct lbs_private *priv;
80962306a36Sopenharmony_ci	struct if_cs_card *card;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL);
81262306a36Sopenharmony_ci	if (!card)
81362306a36Sopenharmony_ci		goto out;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	card->p_dev = p_dev;
81662306a36Sopenharmony_ci	p_dev->priv = card;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	p_dev->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	if (pcmcia_loop_config(p_dev, if_cs_ioprobe, NULL)) {
82162306a36Sopenharmony_ci		pr_err("error in pcmcia_loop_config\n");
82262306a36Sopenharmony_ci		goto out1;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/*
82662306a36Sopenharmony_ci	 * Allocate an interrupt line.  Note that this does not assign
82762306a36Sopenharmony_ci	 * a handler to the interrupt, unless the 'Handler' member of
82862306a36Sopenharmony_ci	 * the irq structure is initialized.
82962306a36Sopenharmony_ci	 */
83062306a36Sopenharmony_ci	if (!p_dev->irq)
83162306a36Sopenharmony_ci		goto out1;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/* Initialize io access */
83462306a36Sopenharmony_ci	card->iobase = ioport_map(p_dev->resource[0]->start,
83562306a36Sopenharmony_ci				resource_size(p_dev->resource[0]));
83662306a36Sopenharmony_ci	if (!card->iobase) {
83762306a36Sopenharmony_ci		pr_err("error in ioport_map\n");
83862306a36Sopenharmony_ci		ret = -EIO;
83962306a36Sopenharmony_ci		goto out1;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	ret = pcmcia_enable_device(p_dev);
84362306a36Sopenharmony_ci	if (ret) {
84462306a36Sopenharmony_ci		pr_err("error in pcmcia_enable_device\n");
84562306a36Sopenharmony_ci		goto out2;
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	/* Finally, report what we've done */
84962306a36Sopenharmony_ci	lbs_deb_cs("irq %d, io %pR", p_dev->irq, p_dev->resource[0]);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	/*
85262306a36Sopenharmony_ci	 * Most of the libertas cards can do unaligned register access, but some
85362306a36Sopenharmony_ci	 * weird ones cannot. That's especially true for the CF8305 card.
85462306a36Sopenharmony_ci	 */
85562306a36Sopenharmony_ci	card->align_regs = false;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	card->model = get_model(p_dev->manf_id, p_dev->card_id);
85862306a36Sopenharmony_ci	if (card->model == MODEL_UNKNOWN) {
85962306a36Sopenharmony_ci		pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n",
86062306a36Sopenharmony_ci		       p_dev->manf_id, p_dev->card_id);
86162306a36Sopenharmony_ci		ret = -ENODEV;
86262306a36Sopenharmony_ci		goto out2;
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	/* Check if we have a current silicon */
86662306a36Sopenharmony_ci	prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID);
86762306a36Sopenharmony_ci	if (card->model == MODEL_8305) {
86862306a36Sopenharmony_ci		card->align_regs = true;
86962306a36Sopenharmony_ci		if (prod_id < IF_CS_CF8305_B1_REV) {
87062306a36Sopenharmony_ci			pr_err("8305 rev B0 and older are not supported\n");
87162306a36Sopenharmony_ci			ret = -ENODEV;
87262306a36Sopenharmony_ci			goto out2;
87362306a36Sopenharmony_ci		}
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	if ((card->model == MODEL_8381) && prod_id < IF_CS_CF8381_B3_REV) {
87762306a36Sopenharmony_ci		pr_err("8381 rev B2 and older are not supported\n");
87862306a36Sopenharmony_ci		ret = -ENODEV;
87962306a36Sopenharmony_ci		goto out2;
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if ((card->model == MODEL_8385) && prod_id < IF_CS_CF8385_B1_REV) {
88362306a36Sopenharmony_ci		pr_err("8385 rev B0 and older are not supported\n");
88462306a36Sopenharmony_ci		ret = -ENODEV;
88562306a36Sopenharmony_ci		goto out2;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	/* Make this card known to the libertas driver */
88962306a36Sopenharmony_ci	priv = lbs_add_card(card, &p_dev->dev);
89062306a36Sopenharmony_ci	if (IS_ERR(priv)) {
89162306a36Sopenharmony_ci		ret = PTR_ERR(priv);
89262306a36Sopenharmony_ci		goto out2;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	/* Set up fields in lbs_private */
89662306a36Sopenharmony_ci	card->priv = priv;
89762306a36Sopenharmony_ci	priv->card = card;
89862306a36Sopenharmony_ci	priv->hw_host_to_card = if_cs_host_to_card;
89962306a36Sopenharmony_ci	priv->enter_deep_sleep = NULL;
90062306a36Sopenharmony_ci	priv->exit_deep_sleep = NULL;
90162306a36Sopenharmony_ci	priv->reset_deep_sleep_wakeup = NULL;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	/* Get firmware */
90462306a36Sopenharmony_ci	ret = lbs_get_firmware_async(priv, &p_dev->dev, card->model, fw_table,
90562306a36Sopenharmony_ci				     if_cs_prog_firmware);
90662306a36Sopenharmony_ci	if (ret) {
90762306a36Sopenharmony_ci		pr_err("failed to find firmware (%d)\n", ret);
90862306a36Sopenharmony_ci		goto out3;
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	goto out;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ciout3:
91462306a36Sopenharmony_ci	lbs_remove_card(priv);
91562306a36Sopenharmony_ciout2:
91662306a36Sopenharmony_ci	ioport_unmap(card->iobase);
91762306a36Sopenharmony_ciout1:
91862306a36Sopenharmony_ci	pcmcia_disable_device(p_dev);
91962306a36Sopenharmony_ciout:
92062306a36Sopenharmony_ci	return ret;
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic void if_cs_detach(struct pcmcia_device *p_dev)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	struct if_cs_card *card = p_dev->priv;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	lbs_stop_card(card->priv);
92962306a36Sopenharmony_ci	lbs_remove_card(card->priv);
93062306a36Sopenharmony_ci	if_cs_disable_ints(card);
93162306a36Sopenharmony_ci	if_cs_release(p_dev);
93262306a36Sopenharmony_ci	kfree(card);
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci/********************************************************************/
93862306a36Sopenharmony_ci/* Module initialization                                            */
93962306a36Sopenharmony_ci/********************************************************************/
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic const struct pcmcia_device_id if_cs_ids[] = {
94262306a36Sopenharmony_ci	PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID),
94362306a36Sopenharmony_ci	PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID),
94462306a36Sopenharmony_ci	PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID),
94562306a36Sopenharmony_ci	/* NOTE: keep in sync with get_model() */
94662306a36Sopenharmony_ci	PCMCIA_DEVICE_NULL,
94762306a36Sopenharmony_ci};
94862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pcmcia, if_cs_ids);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic struct pcmcia_driver lbs_driver = {
95162306a36Sopenharmony_ci	.owner		= THIS_MODULE,
95262306a36Sopenharmony_ci	.name		= DRV_NAME,
95362306a36Sopenharmony_ci	.probe		= if_cs_probe,
95462306a36Sopenharmony_ci	.remove		= if_cs_detach,
95562306a36Sopenharmony_ci	.id_table       = if_cs_ids,
95662306a36Sopenharmony_ci};
95762306a36Sopenharmony_cimodule_pcmcia_driver(lbs_driver);
958