1a0a954b1SSergio Paracuellos // SPDX-License-Identifier: GPL-2.0
213a9930dSWolfram Sang /*
313a9930dSWolfram Sang  *   Driver for KeyStream, KS7010 based SDIO cards.
413a9930dSWolfram Sang  *
513a9930dSWolfram Sang  *   Copyright (C) 2006-2008 KeyStream Corp.
613a9930dSWolfram Sang  *   Copyright (C) 2009 Renesas Technology Corp.
7c5d9a030SWolfram Sang  *   Copyright (C) 2016 Sang Engineering, Wolfram Sang
813a9930dSWolfram Sang  */
913a9930dSWolfram Sang 
10fa3fd846SSergio Paracuellos #include <linux/atomic.h>
111c013a5cSWolfram Sang #include <linux/firmware.h>
12fa3fd846SSergio Paracuellos #include <linux/jiffies.h>
1313a9930dSWolfram Sang #include <linux/mmc/card.h>
1413a9930dSWolfram Sang #include <linux/mmc/sdio_func.h>
15fa3fd846SSergio Paracuellos #include <linux/module.h>
161c013a5cSWolfram Sang #include <linux/workqueue.h>
1713a9930dSWolfram Sang #include "ks_wlan.h"
1813a9930dSWolfram Sang #include "ks_hostif.h"
1913a9930dSWolfram Sang 
202c54ee54SSergio Paracuellos #define ROM_FILE "ks7010sd.rom"
2148ecb225SSergio Paracuellos 
2248ecb225SSergio Paracuellos /*  SDIO KeyStream vendor and device */
2348ecb225SSergio Paracuellos #define SDIO_VENDOR_ID_KS_CODE_A	0x005b
2448ecb225SSergio Paracuellos #define SDIO_VENDOR_ID_KS_CODE_B	0x0023
2548ecb225SSergio Paracuellos 
2648ecb225SSergio Paracuellos /* Older sources suggest earlier versions were named 7910 or 79xx */
2748ecb225SSergio Paracuellos #define SDIO_DEVICE_ID_KS_7010		0x7910
2848ecb225SSergio Paracuellos 
2948ecb225SSergio Paracuellos /* Read/Write Status Register */
30a704a1bcSSergio Paracuellos #define READ_STATUS_REG		0x000000
31a704a1bcSSergio Paracuellos #define WRITE_STATUS_REG	0x00000C
3248ecb225SSergio Paracuellos enum reg_status_type {
3348ecb225SSergio Paracuellos 	REG_STATUS_BUSY,
3448ecb225SSergio Paracuellos 	REG_STATUS_IDLE
3548ecb225SSergio Paracuellos };
3648ecb225SSergio Paracuellos 
3748ecb225SSergio Paracuellos /* Read Index Register */
38a704a1bcSSergio Paracuellos #define READ_INDEX_REG		0x000004
3948ecb225SSergio Paracuellos 
4048ecb225SSergio Paracuellos /* Read Data Size Register */
41a704a1bcSSergio Paracuellos #define READ_DATA_SIZE_REG	0x000008
4248ecb225SSergio Paracuellos 
4348ecb225SSergio Paracuellos /* Write Index Register */
44a704a1bcSSergio Paracuellos #define WRITE_INDEX_REG		0x000010
4548ecb225SSergio Paracuellos 
46004e43c2SSergio Paracuellos /*
47004e43c2SSergio Paracuellos  * Write Status/Read Data Size Register
4848ecb225SSergio Paracuellos  * for network packet (less than 2048 bytes data)
4948ecb225SSergio Paracuellos  */
50a704a1bcSSergio Paracuellos #define WSTATUS_RSIZE_REG	0x000014
51004e43c2SSergio Paracuellos 
52004e43c2SSergio Paracuellos /* Write Status Register value */
53004e43c2SSergio Paracuellos #define WSTATUS_MASK		0x80
54004e43c2SSergio Paracuellos 
55004e43c2SSergio Paracuellos /* Read Data Size Register value [10:4] */
56004e43c2SSergio Paracuellos #define RSIZE_MASK		0x7F
5748ecb225SSergio Paracuellos 
5848ecb225SSergio Paracuellos /* ARM to SD interrupt Enable */
59a704a1bcSSergio Paracuellos #define INT_ENABLE_REG		0x000020
6048ecb225SSergio Paracuellos /* ARM to SD interrupt Pending */
61a704a1bcSSergio Paracuellos #define INT_PENDING_REG		0x000024
6248ecb225SSergio Paracuellos 
6348ecb225SSergio Paracuellos #define INT_GCR_B              BIT(7)
6448ecb225SSergio Paracuellos #define INT_GCR_A              BIT(6)
6548ecb225SSergio Paracuellos #define INT_WRITE_STATUS       BIT(5)
6648ecb225SSergio Paracuellos #define INT_WRITE_INDEX        BIT(4)
6748ecb225SSergio Paracuellos #define INT_WRITE_SIZE         BIT(3)
6848ecb225SSergio Paracuellos #define INT_READ_STATUS        BIT(2)
6948ecb225SSergio Paracuellos #define INT_READ_INDEX         BIT(1)
7048ecb225SSergio Paracuellos #define INT_READ_SIZE          BIT(0)
7148ecb225SSergio Paracuellos 
7248ecb225SSergio Paracuellos /* General Communication Register A */
73a704a1bcSSergio Paracuellos #define GCR_A_REG		0x000028
7448ecb225SSergio Paracuellos enum gen_com_reg_a {
7548ecb225SSergio Paracuellos 	GCR_A_INIT,
7648ecb225SSergio Paracuellos 	GCR_A_REMAP,
7748ecb225SSergio Paracuellos 	GCR_A_RUN
7848ecb225SSergio Paracuellos };
7948ecb225SSergio Paracuellos 
8048ecb225SSergio Paracuellos /* General Communication Register B */
81a704a1bcSSergio Paracuellos #define GCR_B_REG		0x00002C
8248ecb225SSergio Paracuellos enum gen_com_reg_b {
8348ecb225SSergio Paracuellos 	GCR_B_ACTIVE,
8448ecb225SSergio Paracuellos 	GCR_B_DOZE
8548ecb225SSergio Paracuellos };
8648ecb225SSergio Paracuellos 
8748ecb225SSergio Paracuellos /* Wakeup Register */
88a704a1bcSSergio Paracuellos #define WAKEUP_REG		0x008018
8948ecb225SSergio Paracuellos #define WAKEUP_REQ		0x5a
9048ecb225SSergio Paracuellos 
9148ecb225SSergio Paracuellos /* AHB Data Window  0x010000-0x01FFFF */
9248ecb225SSergio Paracuellos #define DATA_WINDOW		0x010000
9348ecb225SSergio Paracuellos #define WINDOW_SIZE		(64 * 1024)
9448ecb225SSergio Paracuellos 
9548ecb225SSergio Paracuellos #define KS7010_IRAM_ADDRESS	0x06000000
9648ecb225SSergio Paracuellos 
9713a9930dSWolfram Sang #define KS7010_IO_BLOCK_SIZE 512
9813a9930dSWolfram Sang 
9907511629SSergio Paracuellos /**
10007511629SSergio Paracuellos  * struct ks_sdio_card - SDIO device data.
10107511629SSergio Paracuellos  *
10207511629SSergio Paracuellos  * Structure is used as the &struct sdio_func private data.
10307511629SSergio Paracuellos  *
10407511629SSergio Paracuellos  * @func: Pointer to the SDIO function device.
10507511629SSergio Paracuellos  * @priv: Pointer to the &struct net_device private data.
10607511629SSergio Paracuellos  */
10707511629SSergio Paracuellos struct ks_sdio_card {
10807511629SSergio Paracuellos 	struct sdio_func *func;
10907511629SSergio Paracuellos 	struct ks_wlan_private *priv;
11007511629SSergio Paracuellos };
11107511629SSergio Paracuellos 
11207511629SSergio Paracuellos static struct sdio_func *ks7010_to_func(struct ks_wlan_private *priv)
11307511629SSergio Paracuellos {
11407511629SSergio Paracuellos 	struct ks_sdio_card *ks_sdio = priv->if_hw;
11507511629SSergio Paracuellos 
11607511629SSergio Paracuellos 	return ks_sdio->func;
11707511629SSergio Paracuellos }
11807511629SSergio Paracuellos 
119f1e79f4bSTobin C. Harding /* Read single byte from device address into byte (CMD52) */
120e0ba53a4SSergio Paracuellos static int ks7010_sdio_readb(struct ks_wlan_private *priv,
121e0ba53a4SSergio Paracuellos 			     u32 address, u8 *byte)
122f1e79f4bSTobin C. Harding {
12307511629SSergio Paracuellos 	struct sdio_func *func = ks7010_to_func(priv);
124f1e79f4bSTobin C. Harding 	int ret;
125f1e79f4bSTobin C. Harding 
126f1e79f4bSTobin C. Harding 	*byte = sdio_readb(func, address, &ret);
127f1e79f4bSTobin C. Harding 
128f1e79f4bSTobin C. Harding 	return ret;
129f1e79f4bSTobin C. Harding }
130f1e79f4bSTobin C. Harding 
131f1e79f4bSTobin C. Harding /* Read length bytes from device address into buffer (CMD53) */
132e0ba53a4SSergio Paracuellos static int ks7010_sdio_read(struct ks_wlan_private *priv, u32 address,
133e0ba53a4SSergio Paracuellos 			    u8 *buffer, unsigned int length)
1344c0d46d2SWolfram Sang {
13507511629SSergio Paracuellos 	struct sdio_func *func = ks7010_to_func(priv);
136f1e79f4bSTobin C. Harding 
137f1e79f4bSTobin C. Harding 	return sdio_memcpy_fromio(func, buffer, address, length);
138f1e79f4bSTobin C. Harding }
139f1e79f4bSTobin C. Harding 
140f1e79f4bSTobin C. Harding /* Write single byte to device address (CMD52) */
141f1e79f4bSTobin C. Harding static int ks7010_sdio_writeb(struct ks_wlan_private *priv,
142e0ba53a4SSergio Paracuellos 			      u32 address, u8 byte)
143f1e79f4bSTobin C. Harding {
14407511629SSergio Paracuellos 	struct sdio_func *func = ks7010_to_func(priv);
1451770ae9dSTobin C. Harding 	int ret;
1464c0d46d2SWolfram Sang 
147f1e79f4bSTobin C. Harding 	sdio_writeb(func, byte, address, &ret);
1484c0d46d2SWolfram Sang 
1491770ae9dSTobin C. Harding 	return ret;
1501770ae9dSTobin C. Harding }
1514c0d46d2SWolfram Sang 
152f1e79f4bSTobin C. Harding /* Write length bytes to device address from buffer (CMD53) */
153e0ba53a4SSergio Paracuellos static int ks7010_sdio_write(struct ks_wlan_private *priv, u32 address,
154e0ba53a4SSergio Paracuellos 			     u8 *buffer, unsigned int length)
1554c0d46d2SWolfram Sang {
15607511629SSergio Paracuellos 	struct sdio_func *func = ks7010_to_func(priv);
1574c0d46d2SWolfram Sang 
158f1e79f4bSTobin C. Harding 	return sdio_memcpy_toio(func, address, buffer, length);
1594c0d46d2SWolfram Sang }
1604c0d46d2SWolfram Sang 
1614433459aSSergio Paracuellos static void ks_wlan_hw_sleep_doze_request(struct ks_wlan_private *priv)
16213a9930dSWolfram Sang {
1631770ae9dSTobin C. Harding 	int ret;
16413a9930dSWolfram Sang 
16513a9930dSWolfram Sang 	/* clear request */
16613a9930dSWolfram Sang 	atomic_set(&priv->sleepstatus.doze_request, 0);
16713a9930dSWolfram Sang 
16813a9930dSWolfram Sang 	if (atomic_read(&priv->sleepstatus.status) == 0) {
169a704a1bcSSergio Paracuellos 		ret = ks7010_sdio_writeb(priv, GCR_B_REG, GCR_B_DOZE);
1701770ae9dSTobin C. Harding 		if (ret) {
171156f2703SSergio Paracuellos 			netdev_err(priv->net_dev, "write GCR_B_REG\n");
172f283dd69STobin C. Harding 			goto set_sleep_mode;
17313a9930dSWolfram Sang 		}
17413a9930dSWolfram Sang 		atomic_set(&priv->sleepstatus.status, 1);
17513a9930dSWolfram Sang 		priv->last_doze = jiffies;
17613a9930dSWolfram Sang 	}
17713a9930dSWolfram Sang 
178f283dd69STobin C. Harding set_sleep_mode:
17913a9930dSWolfram Sang 	priv->sleep_mode = atomic_read(&priv->sleepstatus.status);
18013a9930dSWolfram Sang }
18113a9930dSWolfram Sang 
1824433459aSSergio Paracuellos static void ks_wlan_hw_sleep_wakeup_request(struct ks_wlan_private *priv)
18313a9930dSWolfram Sang {
1841770ae9dSTobin C. Harding 	int ret;
18513a9930dSWolfram Sang 
18613a9930dSWolfram Sang 	/* clear request */
18713a9930dSWolfram Sang 	atomic_set(&priv->sleepstatus.wakeup_request, 0);
18813a9930dSWolfram Sang 
18913a9930dSWolfram Sang 	if (atomic_read(&priv->sleepstatus.status) == 1) {
190a704a1bcSSergio Paracuellos 		ret = ks7010_sdio_writeb(priv, WAKEUP_REG, WAKEUP_REQ);
1911770ae9dSTobin C. Harding 		if (ret) {
192156f2703SSergio Paracuellos 			netdev_err(priv->net_dev, "write WAKEUP_REG\n");
193f283dd69STobin C. Harding 			goto set_sleep_mode;
19413a9930dSWolfram Sang 		}
19513a9930dSWolfram Sang 		atomic_set(&priv->sleepstatus.status, 0);
19613a9930dSWolfram Sang 		priv->last_wakeup = jiffies;
19713a9930dSWolfram Sang 		++priv->wakeup_count;
19813a9930dSWolfram Sang 	}
19913a9930dSWolfram Sang 
200f283dd69STobin C. Harding set_sleep_mode:
20113a9930dSWolfram Sang 	priv->sleep_mode = atomic_read(&priv->sleepstatus.status);
20213a9930dSWolfram Sang }
20313a9930dSWolfram Sang 
204feedcf1aSWolfram Sang void ks_wlan_hw_wakeup_request(struct ks_wlan_private *priv)
20513a9930dSWolfram Sang {
2061770ae9dSTobin C. Harding 	int ret;
20713a9930dSWolfram Sang 
20813a9930dSWolfram Sang 	if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
209a704a1bcSSergio Paracuellos 		ret = ks7010_sdio_writeb(priv, WAKEUP_REG, WAKEUP_REQ);
2101770ae9dSTobin C. Harding 		if (ret)
211156f2703SSergio Paracuellos 			netdev_err(priv->net_dev, "write WAKEUP_REG\n");
21253638cefSsayli karnik 
21313a9930dSWolfram Sang 		priv->last_wakeup = jiffies;
21413a9930dSWolfram Sang 		++priv->wakeup_count;
21513a9930dSWolfram Sang 	}
21613a9930dSWolfram Sang }
21713a9930dSWolfram Sang 
218fa740a9eSTobin C. Harding static void _ks_wlan_hw_power_save(struct ks_wlan_private *priv)
21913a9930dSWolfram Sang {
2209d418fa8SSergio Paracuellos 	u8 byte;
221f7172487STobin C. Harding 	int ret;
22213a9930dSWolfram Sang 
2238fb8e05cSTobin C. Harding 	if (priv->reg.power_mgmt == POWER_MGMT_ACTIVE)
224fa740a9eSTobin C. Harding 		return;
22513a9930dSWolfram Sang 
226482c03c7STobin C. Harding 	if (priv->reg.operation_mode != MODE_INFRASTRUCTURE)
227482c03c7STobin C. Harding 		return;
228482c03c7STobin C. Harding 
2290e24eb8aSTobin C. Harding 	if (!is_connect_status(priv->connect_status))
230fa740a9eSTobin C. Harding 		return;
231d5f1db31STobin C. Harding 
232d5f1db31STobin C. Harding 	if (priv->dev_state != DEVICE_STATE_SLEEP)
233fa740a9eSTobin C. Harding 		return;
234d5f1db31STobin C. Harding 
2353188bc09STobin C. Harding 	if (atomic_read(&priv->psstatus.status) == PS_SNOOZE)
236fa740a9eSTobin C. Harding 		return;
2373188bc09STobin C. Harding 
238832ec535SSergio Paracuellos 	netdev_dbg(priv->net_dev,
239832ec535SSergio Paracuellos 		   "STATUS:\n"
240832ec535SSergio Paracuellos 		   "- psstatus.status = %d\n"
241832ec535SSergio Paracuellos 		   "- psstatus.confirm_wait = %d\n"
242832ec535SSergio Paracuellos 		   "- psstatus.snooze_guard = %d\n"
243832ec535SSergio Paracuellos 		   "- txq_count = %d\n",
24413a9930dSWolfram Sang 		   atomic_read(&priv->psstatus.status),
24513a9930dSWolfram Sang 		   atomic_read(&priv->psstatus.confirm_wait),
24613a9930dSWolfram Sang 		   atomic_read(&priv->psstatus.snooze_guard),
247e6bc5053SSergio Paracuellos 		   txq_count(priv));
24813a9930dSWolfram Sang 
249fa740a9eSTobin C. Harding 	if (atomic_read(&priv->psstatus.confirm_wait) ||
250fa740a9eSTobin C. Harding 	    atomic_read(&priv->psstatus.snooze_guard) ||
251e6bc5053SSergio Paracuellos 	    txq_has_space(priv)) {
25218bd6dd1STobin C. Harding 		queue_delayed_work(priv->wq, &priv->rw_dwork, 0);
253fa740a9eSTobin C. Harding 		return;
254fa740a9eSTobin C. Harding 	}
255fa740a9eSTobin C. Harding 
256a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_readb(priv, INT_PENDING_REG, &byte);
257f7172487STobin C. Harding 	if (ret) {
258156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "read INT_PENDING_REG\n");
259f8641485STobin C. Harding 		goto queue_delayed_work;
26013a9930dSWolfram Sang 	}
261f1e79f4bSTobin C. Harding 	if (byte)
262f8641485STobin C. Harding 		goto queue_delayed_work;
263fa740a9eSTobin C. Harding 
264a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_writeb(priv, GCR_B_REG, GCR_B_DOZE);
265f7172487STobin C. Harding 	if (ret) {
266156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "write GCR_B_REG\n");
267f8641485STobin C. Harding 		goto queue_delayed_work;
26813a9930dSWolfram Sang 	}
2699f9d7030STobin C. Harding 	atomic_set(&priv->psstatus.status, PS_SNOOZE);
27013a9930dSWolfram Sang 
271fa740a9eSTobin C. Harding 	return;
272f8641485STobin C. Harding 
273f8641485STobin C. Harding queue_delayed_work:
27418bd6dd1STobin C. Harding 	queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
27513a9930dSWolfram Sang }
27613a9930dSWolfram Sang 
277feedcf1aSWolfram Sang int ks_wlan_hw_power_save(struct ks_wlan_private *priv)
27813a9930dSWolfram Sang {
27918bd6dd1STobin C. Harding 	queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
28013a9930dSWolfram Sang 	return 0;
28113a9930dSWolfram Sang }
28213a9930dSWolfram Sang 
283cdf6ecc5SWolfram Sang static int enqueue_txdev(struct ks_wlan_private *priv, unsigned char *p,
284cdf6ecc5SWolfram Sang 			 unsigned long size,
285055da4f9STobin C. Harding 			 void (*complete_handler)(struct ks_wlan_private *priv,
286055da4f9STobin C. Harding 						  struct sk_buff *skb),
287055da4f9STobin C. Harding 			 struct sk_buff *skb)
28813a9930dSWolfram Sang {
28913a9930dSWolfram Sang 	struct tx_device_buffer *sp;
2901770ae9dSTobin C. Harding 	int ret;
29113a9930dSWolfram Sang 
29213a9930dSWolfram Sang 	if (priv->dev_state < DEVICE_STATE_BOOT) {
2931770ae9dSTobin C. Harding 		ret = -EPERM;
294aa6ca807STobin C. Harding 		goto err_complete;
29513a9930dSWolfram Sang 	}
29613a9930dSWolfram Sang 
297e6bc5053SSergio Paracuellos 	if ((TX_DEVICE_BUFF_SIZE - 1) <= txq_count(priv)) {
2985259b329SSergio Paracuellos 		netdev_err(priv->net_dev, "tx buffer overflow\n");
2991770ae9dSTobin C. Harding 		ret = -EOVERFLOW;
300aa6ca807STobin C. Harding 		goto err_complete;
30113a9930dSWolfram Sang 	}
30213a9930dSWolfram Sang 
30313a9930dSWolfram Sang 	sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qtail];
30413a9930dSWolfram Sang 	sp->sendp = p;
30513a9930dSWolfram Sang 	sp->size = size;
30613a9930dSWolfram Sang 	sp->complete_handler = complete_handler;
307055da4f9STobin C. Harding 	sp->skb = skb;
30813a9930dSWolfram Sang 	inc_txqtail(priv);
30913a9930dSWolfram Sang 
31013a9930dSWolfram Sang 	return 0;
311aa6ca807STobin C. Harding 
312aa6ca807STobin C. Harding err_complete:
313aa6ca807STobin C. Harding 	kfree(p);
314aa6ca807STobin C. Harding 	if (complete_handler)
315055da4f9STobin C. Harding 		(*complete_handler)(priv, skb);
316aa6ca807STobin C. Harding 
3171770ae9dSTobin C. Harding 	return ret;
31813a9930dSWolfram Sang }
31913a9930dSWolfram Sang 
32013a9930dSWolfram Sang /* write data */
32149705f9aSSergio Paracuellos static int write_to_device(struct ks_wlan_private *priv, u8 *buffer,
322cdf6ecc5SWolfram Sang 			   unsigned long size)
32313a9930dSWolfram Sang {
32413a9930dSWolfram Sang 	struct hostif_hdr *hdr;
3251770ae9dSTobin C. Harding 	int ret;
326697f9f7fSMuraru Mihaela 
32713a9930dSWolfram Sang 	hdr = (struct hostif_hdr *)buffer;
32813a9930dSWolfram Sang 
329d9d1ffd4SCezary Gapinski 	if (le16_to_cpu(hdr->event) < HIF_DATA_REQ ||
330d9d1ffd4SCezary Gapinski 	    le16_to_cpu(hdr->event) > HIF_REQ_MAX) {
3315259b329SSergio Paracuellos 		netdev_err(priv->net_dev, "unknown event=%04X\n", hdr->event);
33213a9930dSWolfram Sang 		return 0;
33313a9930dSWolfram Sang 	}
33413a9930dSWolfram Sang 
3351770ae9dSTobin C. Harding 	ret = ks7010_sdio_write(priv, DATA_WINDOW, buffer, size);
3361770ae9dSTobin C. Harding 	if (ret) {
337156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "write DATA_WINDOW\n");
3381770ae9dSTobin C. Harding 		return ret;
33913a9930dSWolfram Sang 	}
34013a9930dSWolfram Sang 
341a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_writeb(priv, WRITE_STATUS_REG, REG_STATUS_BUSY);
3421770ae9dSTobin C. Harding 	if (ret) {
343156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "write WRITE_STATUS_REG\n");
3441770ae9dSTobin C. Harding 		return ret;
34513a9930dSWolfram Sang 	}
34613a9930dSWolfram Sang 
34713a9930dSWolfram Sang 	return 0;
34813a9930dSWolfram Sang }
34913a9930dSWolfram Sang 
3505141e9c6STobin C. Harding static void tx_device_task(struct ks_wlan_private *priv)
35113a9930dSWolfram Sang {
35213a9930dSWolfram Sang 	struct tx_device_buffer *sp;
35303b02449STobin C. Harding 	int ret;
35413a9930dSWolfram Sang 
355e6bc5053SSergio Paracuellos 	if (!txq_has_space(priv) ||
356638a75b6STobin C. Harding 	    atomic_read(&priv->psstatus.status) == PS_SNOOZE)
357638a75b6STobin C. Harding 		return;
358638a75b6STobin C. Harding 
35913a9930dSWolfram Sang 	sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead];
36013a9930dSWolfram Sang 	if (priv->dev_state >= DEVICE_STATE_BOOT) {
36103b02449STobin C. Harding 		ret = write_to_device(priv, sp->sendp, sp->size);
36203b02449STobin C. Harding 		if (ret) {
3637dd51ea1SSergio Paracuellos 			netdev_err(priv->net_dev,
3647dd51ea1SSergio Paracuellos 				   "write_to_device error !!(%d)\n", ret);
36518bd6dd1STobin C. Harding 			queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
36613a9930dSWolfram Sang 			return;
36713a9930dSWolfram Sang 		}
36813a9930dSWolfram Sang 	}
369638a75b6STobin C. Harding 	kfree(sp->sendp);
370c7e65f4dSsayli karnik 	if (sp->complete_handler)	/* TX Complete */
371055da4f9STobin C. Harding 		(*sp->complete_handler)(priv, sp->skb);
37213a9930dSWolfram Sang 	inc_txqhead(priv);
37313a9930dSWolfram Sang 
374e6bc5053SSergio Paracuellos 	if (txq_has_space(priv))
37518bd6dd1STobin C. Harding 		queue_delayed_work(priv->wq, &priv->rw_dwork, 0);
37613a9930dSWolfram Sang }
37713a9930dSWolfram Sang 
378feedcf1aSWolfram Sang int ks_wlan_hw_tx(struct ks_wlan_private *priv, void *p, unsigned long size,
379055da4f9STobin C. Harding 		  void (*complete_handler)(struct ks_wlan_private *priv,
380055da4f9STobin C. Harding 					   struct sk_buff *skb),
381055da4f9STobin C. Harding 		  struct sk_buff *skb)
38213a9930dSWolfram Sang {
38313a9930dSWolfram Sang 	int result = 0;
38413a9930dSWolfram Sang 	struct hostif_hdr *hdr;
385697f9f7fSMuraru Mihaela 
38613a9930dSWolfram Sang 	hdr = (struct hostif_hdr *)p;
38713a9930dSWolfram Sang 
388d9d1ffd4SCezary Gapinski 	if (le16_to_cpu(hdr->event) < HIF_DATA_REQ ||
389d9d1ffd4SCezary Gapinski 	    le16_to_cpu(hdr->event) > HIF_REQ_MAX) {
3905259b329SSergio Paracuellos 		netdev_err(priv->net_dev, "unknown event=%04X\n", hdr->event);
39113a9930dSWolfram Sang 		return 0;
39213a9930dSWolfram Sang 	}
39313a9930dSWolfram Sang 
39413a9930dSWolfram Sang 	/* add event to hostt buffer */
395d9d1ffd4SCezary Gapinski 	priv->hostt.buff[priv->hostt.qtail] = le16_to_cpu(hdr->event);
39613a9930dSWolfram Sang 	priv->hostt.qtail = (priv->hostt.qtail + 1) % SME_EVENT_BUFF_SIZE;
39713a9930dSWolfram Sang 
39813a9930dSWolfram Sang 	spin_lock(&priv->tx_dev.tx_dev_lock);
399055da4f9STobin C. Harding 	result = enqueue_txdev(priv, p, size, complete_handler, skb);
40013a9930dSWolfram Sang 	spin_unlock(&priv->tx_dev.tx_dev_lock);
40113a9930dSWolfram Sang 
402e6bc5053SSergio Paracuellos 	if (txq_has_space(priv))
40318bd6dd1STobin C. Harding 		queue_delayed_work(priv->wq, &priv->rw_dwork, 0);
40418bd6dd1STobin C. Harding 
40513a9930dSWolfram Sang 	return result;
40613a9930dSWolfram Sang }
40713a9930dSWolfram Sang 
40813a9930dSWolfram Sang static void rx_event_task(unsigned long dev)
40913a9930dSWolfram Sang {
410feedcf1aSWolfram Sang 	struct ks_wlan_private *priv = (struct ks_wlan_private *)dev;
41113a9930dSWolfram Sang 	struct rx_device_buffer *rp;
41213a9930dSWolfram Sang 
413e6bc5053SSergio Paracuellos 	if (rxq_has_space(priv) && priv->dev_state >= DEVICE_STATE_BOOT) {
41413a9930dSWolfram Sang 		rp = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qhead];
41513a9930dSWolfram Sang 		hostif_receive(priv, rp->data, rp->size);
41613a9930dSWolfram Sang 		inc_rxqhead(priv);
41713a9930dSWolfram Sang 
418e6bc5053SSergio Paracuellos 		if (rxq_has_space(priv))
419321dabdcSTobin C. Harding 			tasklet_schedule(&priv->rx_bh_task);
42013a9930dSWolfram Sang 	}
42113a9930dSWolfram Sang }
42213a9930dSWolfram Sang 
42389467e74SSergio Paracuellos static void ks_wlan_hw_rx(struct ks_wlan_private *priv, size_t size)
42413a9930dSWolfram Sang {
425f7172487STobin C. Harding 	int ret;
42613a9930dSWolfram Sang 	struct rx_device_buffer *rx_buffer;
42713a9930dSWolfram Sang 	struct hostif_hdr *hdr;
4283c6b7591SSergio Paracuellos 	u16 event = 0;
42913a9930dSWolfram Sang 
43013a9930dSWolfram Sang 	/* receive data */
431e6bc5053SSergio Paracuellos 	if (rxq_count(priv) >= (RX_DEVICE_BUFF_SIZE - 1)) {
4325259b329SSergio Paracuellos 		netdev_err(priv->net_dev, "rx buffer overflow\n");
43313b05e46STobin C. Harding 		return;
43413a9930dSWolfram Sang 	}
43513a9930dSWolfram Sang 	rx_buffer = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qtail];
43613a9930dSWolfram Sang 
437f7172487STobin C. Harding 	ret = ks7010_sdio_read(priv, DATA_WINDOW, &rx_buffer->data[0],
438cdf6ecc5SWolfram Sang 			       hif_align_size(size));
439f7172487STobin C. Harding 	if (ret)
44013b05e46STobin C. Harding 		return;
44113a9930dSWolfram Sang 
44213a9930dSWolfram Sang 	/* length check */
44313a9930dSWolfram Sang 	if (size > 2046 || size == 0) {
44431ce0d86SSergio Paracuellos #ifdef DEBUG
445cdf6ecc5SWolfram Sang 		print_hex_dump_bytes("INVALID DATA dump: ",
446cdf6ecc5SWolfram Sang 				     DUMP_PREFIX_OFFSET,
4473215bb1aSWolfram Sang 				     rx_buffer->data, 32);
4483215bb1aSWolfram Sang #endif
449a704a1bcSSergio Paracuellos 		ret = ks7010_sdio_writeb(priv, READ_STATUS_REG, REG_STATUS_IDLE);
450f7172487STobin C. Harding 		if (ret)
451156f2703SSergio Paracuellos 			netdev_err(priv->net_dev, "write READ_STATUS_REG\n");
45253638cefSsayli karnik 
45313b05e46STobin C. Harding 		/* length check fail */
45413b05e46STobin C. Harding 		return;
45513a9930dSWolfram Sang 	}
45613a9930dSWolfram Sang 
45713a9930dSWolfram Sang 	hdr = (struct hostif_hdr *)&rx_buffer->data[0];
45813a9930dSWolfram Sang 	rx_buffer->size = le16_to_cpu(hdr->size) + sizeof(hdr->size);
459d9d1ffd4SCezary Gapinski 	event = le16_to_cpu(hdr->event);
46013a9930dSWolfram Sang 	inc_rxqtail(priv);
46113a9930dSWolfram Sang 
462a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_writeb(priv, READ_STATUS_REG, REG_STATUS_IDLE);
463f7172487STobin C. Harding 	if (ret)
464156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "write READ_STATUS_REG\n");
46513a9930dSWolfram Sang 
4667570d757SSergio Paracuellos 	if (atomic_read(&priv->psstatus.confirm_wait) && is_hif_conf(event)) {
4675259b329SSergio Paracuellos 		netdev_dbg(priv->net_dev, "IS_HIF_CONF true !!\n");
46813a9930dSWolfram Sang 		atomic_dec(&priv->psstatus.confirm_wait);
46913a9930dSWolfram Sang 	}
47013a9930dSWolfram Sang 
471321dabdcSTobin C. Harding 	tasklet_schedule(&priv->rx_bh_task);
47213a9930dSWolfram Sang }
47313a9930dSWolfram Sang 
47413a9930dSWolfram Sang static void ks7010_rw_function(struct work_struct *work)
47513a9930dSWolfram Sang {
47607511629SSergio Paracuellos 	struct ks_wlan_private *priv = container_of(work,
47707511629SSergio Paracuellos 						    struct ks_wlan_private,
47807511629SSergio Paracuellos 						    rw_dwork.work);
47907511629SSergio Paracuellos 	struct sdio_func *func = ks7010_to_func(priv);
480a51333d1SSergio Paracuellos 	u8 byte;
4811770ae9dSTobin C. Harding 	int ret;
48213a9930dSWolfram Sang 
483e61e73d7STobin C. Harding 	/* wait after DOZE */
4846adc30b1SSergio Paracuellos 	if (time_after(priv->last_doze + msecs_to_jiffies(30), jiffies)) {
4855259b329SSergio Paracuellos 		netdev_dbg(priv->net_dev, "wait after DOZE\n");
48618bd6dd1STobin C. Harding 		queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
48713a9930dSWolfram Sang 		return;
48813a9930dSWolfram Sang 	}
48913a9930dSWolfram Sang 
490e61e73d7STobin C. Harding 	/* wait after WAKEUP */
4916adc30b1SSergio Paracuellos 	while (time_after(priv->last_wakeup + msecs_to_jiffies(30), jiffies)) {
4925259b329SSergio Paracuellos 		netdev_dbg(priv->net_dev, "wait after WAKEUP\n");
49307511629SSergio Paracuellos 		dev_info(&func->dev, "wake: %lu %lu\n",
4946adc30b1SSergio Paracuellos 			 priv->last_wakeup + msecs_to_jiffies(30), jiffies);
49513a9930dSWolfram Sang 		msleep(30);
49613a9930dSWolfram Sang 	}
49713a9930dSWolfram Sang 
49807511629SSergio Paracuellos 	sdio_claim_host(func);
49913a9930dSWolfram Sang 
50013a9930dSWolfram Sang 	/* power save wakeup */
50113a9930dSWolfram Sang 	if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
502e6bc5053SSergio Paracuellos 		if (txq_has_space(priv)) {
50313a9930dSWolfram Sang 			ks_wlan_hw_wakeup_request(priv);
50418bd6dd1STobin C. Harding 			queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
50513a9930dSWolfram Sang 		}
5060dd30a74STobin C. Harding 		goto release_host;
50713a9930dSWolfram Sang 	}
50813a9930dSWolfram Sang 
50913a9930dSWolfram Sang 	/* sleep mode doze */
51013a9930dSWolfram Sang 	if (atomic_read(&priv->sleepstatus.doze_request) == 1) {
51113a9930dSWolfram Sang 		ks_wlan_hw_sleep_doze_request(priv);
5120dd30a74STobin C. Harding 		goto release_host;
51313a9930dSWolfram Sang 	}
51413a9930dSWolfram Sang 	/* sleep mode wakeup */
51513a9930dSWolfram Sang 	if (atomic_read(&priv->sleepstatus.wakeup_request) == 1) {
51613a9930dSWolfram Sang 		ks_wlan_hw_sleep_wakeup_request(priv);
5170dd30a74STobin C. Harding 		goto release_host;
51813a9930dSWolfram Sang 	}
51913a9930dSWolfram Sang 
52013a9930dSWolfram Sang 	/* read (WriteStatus/ReadDataSize FN1:00_0014) */
521a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_readb(priv, WSTATUS_RSIZE_REG, &byte);
5221770ae9dSTobin C. Harding 	if (ret) {
523156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "read WSTATUS_RSIZE_REG psstatus=%d\n",
524cdf6ecc5SWolfram Sang 			   atomic_read(&priv->psstatus.status));
5250dd30a74STobin C. Harding 		goto release_host;
52613a9930dSWolfram Sang 	}
52713a9930dSWolfram Sang 
528f1e79f4bSTobin C. Harding 	if (byte & RSIZE_MASK) {	/* Read schedule */
52989467e74SSergio Paracuellos 		ks_wlan_hw_rx(priv, (size_t)((byte & RSIZE_MASK) << 4));
53013a9930dSWolfram Sang 	}
531f1e79f4bSTobin C. Harding 	if ((byte & WSTATUS_MASK))
5325141e9c6STobin C. Harding 		tx_device_task(priv);
53353638cefSsayli karnik 
53413a9930dSWolfram Sang 	_ks_wlan_hw_power_save(priv);
53513a9930dSWolfram Sang 
5360dd30a74STobin C. Harding release_host:
53707511629SSergio Paracuellos 	sdio_release_host(func);
53813a9930dSWolfram Sang }
53913a9930dSWolfram Sang 
54013a9930dSWolfram Sang static void ks_sdio_interrupt(struct sdio_func *func)
54113a9930dSWolfram Sang {
542f7172487STobin C. Harding 	int ret;
54313a9930dSWolfram Sang 	struct ks_sdio_card *card;
544feedcf1aSWolfram Sang 	struct ks_wlan_private *priv;
54529699193SSergio Paracuellos 	u8 status, rsize, byte;
54613a9930dSWolfram Sang 
54713a9930dSWolfram Sang 	card = sdio_get_drvdata(func);
54813a9930dSWolfram Sang 	priv = card->priv;
54913a9930dSWolfram Sang 
550638a75b6STobin C. Harding 	if (priv->dev_state < DEVICE_STATE_BOOT)
551638a75b6STobin C. Harding 		goto queue_delayed_work;
552638a75b6STobin C. Harding 
553a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_readb(priv, INT_PENDING_REG, &status);
554f7172487STobin C. Harding 	if (ret) {
555156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "read INT_PENDING_REG\n");
556f283dd69STobin C. Harding 		goto queue_delayed_work;
55713a9930dSWolfram Sang 	}
55813a9930dSWolfram Sang 
55913a9930dSWolfram Sang 	/* schedule task for interrupt status */
56013a9930dSWolfram Sang 	/* bit7 -> Write General Communication B register */
56113a9930dSWolfram Sang 	/* read (General Communication B register) */
56213a9930dSWolfram Sang 	/* bit5 -> Write Status Idle */
56313a9930dSWolfram Sang 	/* bit2 -> Read Status Busy  */
564ddd10774SXiangyang Zhang 	if (status & INT_GCR_B ||
565ddd10774SXiangyang Zhang 	    atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
566a704a1bcSSergio Paracuellos 		ret = ks7010_sdio_readb(priv, GCR_B_REG, &byte);
567f7172487STobin C. Harding 		if (ret) {
568156f2703SSergio Paracuellos 			netdev_err(priv->net_dev, "read GCR_B_REG\n");
569f283dd69STobin C. Harding 			goto queue_delayed_work;
57013a9930dSWolfram Sang 		}
571f1e79f4bSTobin C. Harding 		if (byte == GCR_B_ACTIVE) {
572638a75b6STobin C. Harding 			if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
573638a75b6STobin C. Harding 				atomic_set(&priv->psstatus.status, PS_WAKEUP);
57413a9930dSWolfram Sang 				priv->wakeup_count = 0;
57513a9930dSWolfram Sang 			}
57613a9930dSWolfram Sang 			complete(&priv->psstatus.wakeup_wait);
57713a9930dSWolfram Sang 		}
57813a9930dSWolfram Sang 	}
57913a9930dSWolfram Sang 
58013a9930dSWolfram Sang 	do {
58113a9930dSWolfram Sang 		/* read (WriteStatus/ReadDataSize FN1:00_0014) */
582a704a1bcSSergio Paracuellos 		ret = ks7010_sdio_readb(priv, WSTATUS_RSIZE_REG, &byte);
583f7172487STobin C. Harding 		if (ret) {
584156f2703SSergio Paracuellos 			netdev_err(priv->net_dev, "read WSTATUS_RSIZE_REG\n");
585f283dd69STobin C. Harding 			goto queue_delayed_work;
58613a9930dSWolfram Sang 		}
587f1e79f4bSTobin C. Harding 		rsize = byte & RSIZE_MASK;
5885141e9c6STobin C. Harding 		if (rsize != 0)		/* Read schedule */
58989467e74SSergio Paracuellos 			ks_wlan_hw_rx(priv, (size_t)(rsize << 4));
5905141e9c6STobin C. Harding 
591f1e79f4bSTobin C. Harding 		if (byte & WSTATUS_MASK) {
59213a9930dSWolfram Sang 			if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
593e6bc5053SSergio Paracuellos 				if (txq_has_space(priv)) {
59413a9930dSWolfram Sang 					ks_wlan_hw_wakeup_request(priv);
5957dd51ea1SSergio Paracuellos 					queue_delayed_work(priv->wq,
5967dd51ea1SSergio Paracuellos 							   &priv->rw_dwork, 1);
59713a9930dSWolfram Sang 					return;
59813a9930dSWolfram Sang 				}
599cdf6ecc5SWolfram Sang 			} else {
6005141e9c6STobin C. Harding 				tx_device_task(priv);
60113a9930dSWolfram Sang 			}
60213a9930dSWolfram Sang 		}
60313a9930dSWolfram Sang 	} while (rsize);
60413a9930dSWolfram Sang 
605f283dd69STobin C. Harding queue_delayed_work:
60618bd6dd1STobin C. Harding 	queue_delayed_work(priv->wq, &priv->rw_dwork, 0);
60713a9930dSWolfram Sang }
60813a9930dSWolfram Sang 
609feedcf1aSWolfram Sang static int trx_device_init(struct ks_wlan_private *priv)
61013a9930dSWolfram Sang {
61120358d13SNick Rosbrook 	priv->tx_dev.qhead = 0;
61220358d13SNick Rosbrook 	priv->tx_dev.qtail = 0;
61313a9930dSWolfram Sang 
61420358d13SNick Rosbrook 	priv->rx_dev.qhead = 0;
61520358d13SNick Rosbrook 	priv->rx_dev.qtail = 0;
61613a9930dSWolfram Sang 
61713a9930dSWolfram Sang 	spin_lock_init(&priv->tx_dev.tx_dev_lock);
61813a9930dSWolfram Sang 	spin_lock_init(&priv->rx_dev.rx_dev_lock);
61913a9930dSWolfram Sang 
620321dabdcSTobin C. Harding 	tasklet_init(&priv->rx_bh_task, rx_event_task, (unsigned long)priv);
62113a9930dSWolfram Sang 
62213a9930dSWolfram Sang 	return 0;
62313a9930dSWolfram Sang }
62413a9930dSWolfram Sang 
625feedcf1aSWolfram Sang static void trx_device_exit(struct ks_wlan_private *priv)
62613a9930dSWolfram Sang {
62713a9930dSWolfram Sang 	struct tx_device_buffer *sp;
62813a9930dSWolfram Sang 
62913a9930dSWolfram Sang 	/* tx buffer clear */
630e6bc5053SSergio Paracuellos 	while (txq_has_space(priv)) {
63113a9930dSWolfram Sang 		sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead];
632e61e73d7STobin C. Harding 		kfree(sp->sendp);
633c7e65f4dSsayli karnik 		if (sp->complete_handler)	/* TX Complete */
634055da4f9STobin C. Harding 			(*sp->complete_handler)(priv, sp->skb);
63513a9930dSWolfram Sang 		inc_txqhead(priv);
63613a9930dSWolfram Sang 	}
63713a9930dSWolfram Sang 
638321dabdcSTobin C. Harding 	tasklet_kill(&priv->rx_bh_task);
63913a9930dSWolfram Sang }
640cdf6ecc5SWolfram Sang 
641feedcf1aSWolfram Sang static int ks7010_sdio_update_index(struct ks_wlan_private *priv, u32 index)
64213a9930dSWolfram Sang {
6431770ae9dSTobin C. Harding 	int ret;
64413a9930dSWolfram Sang 	unsigned char *data_buf;
64513a9930dSWolfram Sang 
64647bda74aSJi-Hun Kim 	data_buf = kmemdup(&index, sizeof(u32), GFP_KERNEL);
647aa6ca807STobin C. Harding 	if (!data_buf)
648aa6ca807STobin C. Harding 		return -ENOMEM;
64913a9930dSWolfram Sang 
650a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_write(priv, WRITE_INDEX_REG, data_buf, sizeof(index));
6511770ae9dSTobin C. Harding 	if (ret)
652f283dd69STobin C. Harding 		goto err_free_data_buf;
65313a9930dSWolfram Sang 
654a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_write(priv, READ_INDEX_REG, data_buf, sizeof(index));
6551770ae9dSTobin C. Harding 	if (ret)
656f283dd69STobin C. Harding 		goto err_free_data_buf;
657aa6ca807STobin C. Harding 
658aa6ca807STobin C. Harding 	return 0;
659aa6ca807STobin C. Harding 
660f283dd69STobin C. Harding err_free_data_buf:
661cdf6ecc5SWolfram Sang 	kfree(data_buf);
662aa6ca807STobin C. Harding 
6631770ae9dSTobin C. Harding 	return ret;
66413a9930dSWolfram Sang }
66513a9930dSWolfram Sang 
66613a9930dSWolfram Sang #define ROM_BUFF_SIZE (64 * 1024)
667feedcf1aSWolfram Sang static int ks7010_sdio_data_compare(struct ks_wlan_private *priv, u32 address,
668a7360b18SSergio Paracuellos 				    u8 *data, unsigned int size)
66913a9930dSWolfram Sang {
6701770ae9dSTobin C. Harding 	int ret;
671a7360b18SSergio Paracuellos 	u8 *read_buf;
672eeed92c0SMarkus Elfring 
67313a9930dSWolfram Sang 	read_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL);
674aa6ca807STobin C. Harding 	if (!read_buf)
675aa6ca807STobin C. Harding 		return -ENOMEM;
67613a9930dSWolfram Sang 
6771770ae9dSTobin C. Harding 	ret = ks7010_sdio_read(priv, address, read_buf, size);
6781770ae9dSTobin C. Harding 	if (ret)
679f283dd69STobin C. Harding 		goto err_free_read_buf;
680aa6ca807STobin C. Harding 
68158af272bSTobin C. Harding 	if (memcmp(data, read_buf, size) != 0) {
68258af272bSTobin C. Harding 		ret = -EIO;
6835259b329SSergio Paracuellos 		netdev_err(priv->net_dev, "data compare error (%d)\n", ret);
684f283dd69STobin C. Harding 		goto err_free_read_buf;
68513a9930dSWolfram Sang 	}
686aa6ca807STobin C. Harding 
687aa6ca807STobin C. Harding 	return 0;
688aa6ca807STobin C. Harding 
689f283dd69STobin C. Harding err_free_read_buf:
690cdf6ecc5SWolfram Sang 	kfree(read_buf);
691aa6ca807STobin C. Harding 
6921770ae9dSTobin C. Harding 	return ret;
69313a9930dSWolfram Sang }
694cdf6ecc5SWolfram Sang 
69567082c22SSergio Paracuellos static int ks7010_copy_firmware(struct ks_wlan_private *priv,
69667082c22SSergio Paracuellos 				const struct firmware *fw_entry)
69713a9930dSWolfram Sang {
698881f76b9STobin C. Harding 	unsigned int length;
69967082c22SSergio Paracuellos 	unsigned int size;
70067082c22SSergio Paracuellos 	unsigned int offset;
70167082c22SSergio Paracuellos 	unsigned int n = 0;
702a7360b18SSergio Paracuellos 	u8 *rom_buf;
70367082c22SSergio Paracuellos 	int ret;
70413a9930dSWolfram Sang 
70513a9930dSWolfram Sang 	rom_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL);
706369e1b69SSudip Mukherjee 	if (!rom_buf)
707aa6ca807STobin C. Harding 		return -ENOMEM;
70813a9930dSWolfram Sang 
70913a9930dSWolfram Sang 	length = fw_entry->size;
71013a9930dSWolfram Sang 
71113a9930dSWolfram Sang 	do {
71213a9930dSWolfram Sang 		if (length >= ROM_BUFF_SIZE) {
71313a9930dSWolfram Sang 			size = ROM_BUFF_SIZE;
71413a9930dSWolfram Sang 			length = length - ROM_BUFF_SIZE;
715cdf6ecc5SWolfram Sang 		} else {
71613a9930dSWolfram Sang 			size = length;
71713a9930dSWolfram Sang 			length = 0;
71813a9930dSWolfram Sang 		}
719cdf6ecc5SWolfram Sang 		if (size == 0)
720cdf6ecc5SWolfram Sang 			break;
721bd6dad98SSergio Paracuellos 
72213a9930dSWolfram Sang 		memcpy(rom_buf, fw_entry->data + n, size);
723e61e73d7STobin C. Harding 
72413a9930dSWolfram Sang 		offset = n;
725bd6dad98SSergio Paracuellos 		ret = ks7010_sdio_update_index(priv,
726bd6dad98SSergio Paracuellos 					       KS7010_IRAM_ADDRESS + offset);
7271770ae9dSTobin C. Harding 		if (ret)
72867082c22SSergio Paracuellos 			goto free_rom_buf;
72913a9930dSWolfram Sang 
7301770ae9dSTobin C. Harding 		ret = ks7010_sdio_write(priv, DATA_WINDOW, rom_buf, size);
7311770ae9dSTobin C. Harding 		if (ret)
73267082c22SSergio Paracuellos 			goto free_rom_buf;
73313a9930dSWolfram Sang 
734bd6dad98SSergio Paracuellos 		ret = ks7010_sdio_data_compare(priv,
735bd6dad98SSergio Paracuellos 					       DATA_WINDOW, rom_buf, size);
7361770ae9dSTobin C. Harding 		if (ret)
73767082c22SSergio Paracuellos 			goto free_rom_buf;
738aa6ca807STobin C. Harding 
73913a9930dSWolfram Sang 		n += size;
74013a9930dSWolfram Sang 
74113a9930dSWolfram Sang 	} while (size);
74213a9930dSWolfram Sang 
743a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_writeb(priv, GCR_A_REG, GCR_A_REMAP);
74467082c22SSergio Paracuellos 
74567082c22SSergio Paracuellos free_rom_buf:
74667082c22SSergio Paracuellos 	kfree(rom_buf);
74767082c22SSergio Paracuellos 	return ret;
74867082c22SSergio Paracuellos }
74967082c22SSergio Paracuellos 
75067082c22SSergio Paracuellos static int ks7010_upload_firmware(struct ks_sdio_card *card)
75167082c22SSergio Paracuellos {
75267082c22SSergio Paracuellos 	struct ks_wlan_private *priv = card->priv;
75307511629SSergio Paracuellos 	struct sdio_func *func = ks7010_to_func(priv);
75467082c22SSergio Paracuellos 	unsigned int n;
75562a37b72SSergio Paracuellos 	u8 byte = 0;
75667082c22SSergio Paracuellos 	int ret;
75767082c22SSergio Paracuellos 	const struct firmware *fw_entry = NULL;
75867082c22SSergio Paracuellos 
75907511629SSergio Paracuellos 	sdio_claim_host(func);
76067082c22SSergio Paracuellos 
76167082c22SSergio Paracuellos 	/* Firmware running ? */
762a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_readb(priv, GCR_A_REG, &byte);
76367082c22SSergio Paracuellos 	if (ret)
76467082c22SSergio Paracuellos 		goto release_host;
76567082c22SSergio Paracuellos 	if (byte == GCR_A_RUN) {
76667082c22SSergio Paracuellos 		netdev_dbg(priv->net_dev, "MAC firmware running ...\n");
76767082c22SSergio Paracuellos 		ret = -EBUSY;
76867082c22SSergio Paracuellos 		goto release_host;
76967082c22SSergio Paracuellos 	}
77067082c22SSergio Paracuellos 
77167082c22SSergio Paracuellos 	ret = request_firmware(&fw_entry, ROM_FILE,
77207511629SSergio Paracuellos 			       &func->dev);
77367082c22SSergio Paracuellos 	if (ret)
77467082c22SSergio Paracuellos 		goto release_host;
77567082c22SSergio Paracuellos 
77667082c22SSergio Paracuellos 	ret = ks7010_copy_firmware(priv, fw_entry);
7771770ae9dSTobin C. Harding 	if (ret)
778aa6ca807STobin C. Harding 		goto release_firmware;
779aa6ca807STobin C. Harding 
78013a9930dSWolfram Sang 	/* Firmware running check */
78113a9930dSWolfram Sang 	for (n = 0; n < 50; ++n) {
78295751f19SJia-Ju Bai 		usleep_range(10000, 11000);	/* wait_ms(10); */
783a704a1bcSSergio Paracuellos 		ret = ks7010_sdio_readb(priv, GCR_A_REG, &byte);
7841770ae9dSTobin C. Harding 		if (ret)
785aa6ca807STobin C. Harding 			goto release_firmware;
786aa6ca807STobin C. Harding 
787f1e79f4bSTobin C. Harding 		if (byte == GCR_A_RUN)
788cdf6ecc5SWolfram Sang 			break;
78913a9930dSWolfram Sang 	}
79013a9930dSWolfram Sang 	if ((50) <= n) {
7915259b329SSergio Paracuellos 		netdev_err(priv->net_dev, "firmware can't start\n");
7921770ae9dSTobin C. Harding 		ret = -EIO;
793aa6ca807STobin C. Harding 		goto release_firmware;
79413a9930dSWolfram Sang 	}
79513a9930dSWolfram Sang 
7961770ae9dSTobin C. Harding 	ret = 0;
79713a9930dSWolfram Sang 
798aa6ca807STobin C. Harding  release_firmware:
79913a9930dSWolfram Sang 	release_firmware(fw_entry);
80067082c22SSergio Paracuellos  release_host:
80107511629SSergio Paracuellos 	sdio_release_host(func);
802aa6ca807STobin C. Harding 
8031770ae9dSTobin C. Harding 	return ret;
80413a9930dSWolfram Sang }
80513a9930dSWolfram Sang 
8066e6156f6SSergio Paracuellos static void ks7010_sme_enqueue_events(struct ks_wlan_private *priv)
80713a9930dSWolfram Sang {
808f0d5a975SSergio Paracuellos 	static const u16 init_events[] = {
809f0d5a975SSergio Paracuellos 		SME_GET_EEPROM_CKSUM, SME_STOP_REQUEST,
810f0d5a975SSergio Paracuellos 		SME_RTS_THRESHOLD_REQUEST, SME_FRAGMENTATION_THRESHOLD_REQUEST,
811f0d5a975SSergio Paracuellos 		SME_WEP_INDEX_REQUEST, SME_WEP_KEY1_REQUEST,
812f0d5a975SSergio Paracuellos 		SME_WEP_KEY2_REQUEST, SME_WEP_KEY3_REQUEST,
813f0d5a975SSergio Paracuellos 		SME_WEP_KEY4_REQUEST, SME_WEP_FLAG_REQUEST,
814f0d5a975SSergio Paracuellos 		SME_RSN_ENABLED_REQUEST, SME_MODE_SET_REQUEST,
815f0d5a975SSergio Paracuellos 		SME_START_REQUEST
816f0d5a975SSergio Paracuellos 	};
817f0d5a975SSergio Paracuellos 	int ev;
81813a9930dSWolfram Sang 
819f0d5a975SSergio Paracuellos 	for (ev = 0; ev < ARRAY_SIZE(init_events); ev++)
820f0d5a975SSergio Paracuellos 		hostif_sme_enqueue(priv, init_events[ev]);
8216e6156f6SSergio Paracuellos }
8226e6156f6SSergio Paracuellos 
8236e6156f6SSergio Paracuellos static void ks7010_card_init(struct ks_wlan_private *priv)
8246e6156f6SSergio Paracuellos {
8256e6156f6SSergio Paracuellos 	init_completion(&priv->confirm_wait);
8266e6156f6SSergio Paracuellos 
8276e6156f6SSergio Paracuellos 	/* get mac address & firmware version */
8286e6156f6SSergio Paracuellos 	hostif_sme_enqueue(priv, SME_START);
8296e6156f6SSergio Paracuellos 
8306e6156f6SSergio Paracuellos 	if (!wait_for_completion_interruptible_timeout
8316e6156f6SSergio Paracuellos 	    (&priv->confirm_wait, 5 * HZ)) {
8326e6156f6SSergio Paracuellos 		netdev_dbg(priv->net_dev, "wait time out!! SME_START\n");
8336e6156f6SSergio Paracuellos 	}
8346e6156f6SSergio Paracuellos 
8356e6156f6SSergio Paracuellos 	if (priv->mac_address_valid && priv->version_size != 0)
8366e6156f6SSergio Paracuellos 		priv->dev_state = DEVICE_STATE_PREINIT;
8376e6156f6SSergio Paracuellos 
8386e6156f6SSergio Paracuellos 	ks7010_sme_enqueue_events(priv);
83913a9930dSWolfram Sang 
840cdf6ecc5SWolfram Sang 	if (!wait_for_completion_interruptible_timeout
841cdf6ecc5SWolfram Sang 	    (&priv->confirm_wait, 5 * HZ)) {
8425259b329SSergio Paracuellos 		netdev_dbg(priv->net_dev, "wait time out!! wireless parameter set\n");
84313a9930dSWolfram Sang 	}
84413a9930dSWolfram Sang 
84513a9930dSWolfram Sang 	if (priv->dev_state >= DEVICE_STATE_PREINIT) {
8465259b329SSergio Paracuellos 		netdev_dbg(priv->net_dev, "DEVICE READY!!\n");
84713a9930dSWolfram Sang 		priv->dev_state = DEVICE_STATE_READY;
84813a9930dSWolfram Sang 	}
84913a9930dSWolfram Sang }
85013a9930dSWolfram Sang 
8516ee9169bSWolfram Sang static void ks7010_init_defaults(struct ks_wlan_private *priv)
8526ee9169bSWolfram Sang {
8536ee9169bSWolfram Sang 	priv->reg.tx_rate = TX_RATE_AUTO;
8546ee9169bSWolfram Sang 	priv->reg.preamble = LONG_PREAMBLE;
8558fb8e05cSTobin C. Harding 	priv->reg.power_mgmt = POWER_MGMT_ACTIVE;
8566ee9169bSWolfram Sang 	priv->reg.scan_type = ACTIVE_SCAN;
8576ee9169bSWolfram Sang 	priv->reg.beacon_lost_count = 20;
8586ee9169bSWolfram Sang 	priv->reg.rts = 2347UL;
8596ee9169bSWolfram Sang 	priv->reg.fragment = 2346UL;
8606ee9169bSWolfram Sang 	priv->reg.phy_type = D_11BG_COMPATIBLE_MODE;
8616ee9169bSWolfram Sang 	priv->reg.cts_mode = CTS_MODE_FALSE;
8626ee9169bSWolfram Sang 	priv->reg.rate_set.body[11] = TX_RATE_54M;
8636ee9169bSWolfram Sang 	priv->reg.rate_set.body[10] = TX_RATE_48M;
8646ee9169bSWolfram Sang 	priv->reg.rate_set.body[9] = TX_RATE_36M;
8656ee9169bSWolfram Sang 	priv->reg.rate_set.body[8] = TX_RATE_18M;
8666ee9169bSWolfram Sang 	priv->reg.rate_set.body[7] = TX_RATE_9M;
8676ee9169bSWolfram Sang 	priv->reg.rate_set.body[6] = TX_RATE_24M | BASIC_RATE;
8686ee9169bSWolfram Sang 	priv->reg.rate_set.body[5] = TX_RATE_12M | BASIC_RATE;
8696ee9169bSWolfram Sang 	priv->reg.rate_set.body[4] = TX_RATE_6M | BASIC_RATE;
8706ee9169bSWolfram Sang 	priv->reg.rate_set.body[3] = TX_RATE_11M | BASIC_RATE;
8716ee9169bSWolfram Sang 	priv->reg.rate_set.body[2] = TX_RATE_5M | BASIC_RATE;
8726ee9169bSWolfram Sang 	priv->reg.rate_set.body[1] = TX_RATE_2M | BASIC_RATE;
8736ee9169bSWolfram Sang 	priv->reg.rate_set.body[0] = TX_RATE_1M | BASIC_RATE;
8746ee9169bSWolfram Sang 	priv->reg.tx_rate = TX_RATE_FULL_AUTO;
8756ee9169bSWolfram Sang 	priv->reg.rate_set.size = 12;
8766ee9169bSWolfram Sang }
8776ee9169bSWolfram Sang 
8788d4ab8f7SSergio Paracuellos static int ks7010_sdio_setup_irqs(struct sdio_func *func)
8798d4ab8f7SSergio Paracuellos {
8808d4ab8f7SSergio Paracuellos 	int ret;
8818d4ab8f7SSergio Paracuellos 
8828d4ab8f7SSergio Paracuellos 	/* interrupt disable */
883a704a1bcSSergio Paracuellos 	sdio_writeb(func, 0, INT_ENABLE_REG, &ret);
8848d4ab8f7SSergio Paracuellos 	if (ret)
8858d4ab8f7SSergio Paracuellos 		goto irq_error;
8868d4ab8f7SSergio Paracuellos 
887a704a1bcSSergio Paracuellos 	sdio_writeb(func, 0xff, INT_PENDING_REG, &ret);
8888d4ab8f7SSergio Paracuellos 	if (ret)
8898d4ab8f7SSergio Paracuellos 		goto irq_error;
8908d4ab8f7SSergio Paracuellos 
8918d4ab8f7SSergio Paracuellos 	/* setup interrupt handler */
8928d4ab8f7SSergio Paracuellos 	ret = sdio_claim_irq(func, ks_sdio_interrupt);
8938d4ab8f7SSergio Paracuellos 
8948d4ab8f7SSergio Paracuellos irq_error:
8958d4ab8f7SSergio Paracuellos 	return ret;
8968d4ab8f7SSergio Paracuellos }
8978d4ab8f7SSergio Paracuellos 
8980e80e31aSSergio Paracuellos static void ks7010_sdio_init_irqs(struct sdio_func *func,
8990e80e31aSSergio Paracuellos 				  struct ks_wlan_private *priv)
9000e80e31aSSergio Paracuellos {
90158f8128eSSergio Paracuellos 	u8 byte;
9020e80e31aSSergio Paracuellos 	int ret;
9030e80e31aSSergio Paracuellos 
9040e80e31aSSergio Paracuellos 	/*
9050e80e31aSSergio Paracuellos 	 * interrupt setting
9060e80e31aSSergio Paracuellos 	 * clear Interrupt status write
9070e80e31aSSergio Paracuellos 	 * (ARMtoSD_InterruptPending FN1:00_0024)
9080e80e31aSSergio Paracuellos 	 */
9090e80e31aSSergio Paracuellos 	sdio_claim_host(func);
910a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_writeb(priv, INT_PENDING_REG, 0xff);
9110e80e31aSSergio Paracuellos 	sdio_release_host(func);
9120e80e31aSSergio Paracuellos 	if (ret)
913156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "write INT_PENDING_REG\n");
9140e80e31aSSergio Paracuellos 
9150e80e31aSSergio Paracuellos 	/* enable ks7010sdio interrupt */
9160e80e31aSSergio Paracuellos 	byte = (INT_GCR_B | INT_READ_STATUS | INT_WRITE_STATUS);
9170e80e31aSSergio Paracuellos 	sdio_claim_host(func);
918a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_writeb(priv, INT_ENABLE_REG, byte);
9190e80e31aSSergio Paracuellos 	sdio_release_host(func);
9200e80e31aSSergio Paracuellos 	if (ret)
921156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "write INT_ENABLE_REG\n");
9220e80e31aSSergio Paracuellos }
9230e80e31aSSergio Paracuellos 
9245df835e8SSergio Paracuellos static void ks7010_private_init(struct ks_wlan_private *priv,
9255df835e8SSergio Paracuellos 				struct ks_sdio_card *card,
9265df835e8SSergio Paracuellos 				struct net_device *netdev)
9275df835e8SSergio Paracuellos {
9285df835e8SSergio Paracuellos 	/* private memory initialize */
92907511629SSergio Paracuellos 	priv->if_hw = card;
9305df835e8SSergio Paracuellos 
9315df835e8SSergio Paracuellos 	priv->dev_state = DEVICE_STATE_PREBOOT;
9325df835e8SSergio Paracuellos 	priv->net_dev = netdev;
9335df835e8SSergio Paracuellos 	priv->firmware_version[0] = '\0';
9345df835e8SSergio Paracuellos 	priv->version_size = 0;
9355df835e8SSergio Paracuellos 	priv->last_doze = jiffies;
9365df835e8SSergio Paracuellos 	priv->last_wakeup = jiffies;
9375df835e8SSergio Paracuellos 	memset(&priv->nstats, 0, sizeof(priv->nstats));
9385df835e8SSergio Paracuellos 	memset(&priv->wstats, 0, sizeof(priv->wstats));
9395df835e8SSergio Paracuellos 
9405df835e8SSergio Paracuellos 	/* sleep mode */
9415df835e8SSergio Paracuellos 	atomic_set(&priv->sleepstatus.doze_request, 0);
9425df835e8SSergio Paracuellos 	atomic_set(&priv->sleepstatus.wakeup_request, 0);
9435df835e8SSergio Paracuellos 	atomic_set(&priv->sleepstatus.wakeup_request, 0);
9445df835e8SSergio Paracuellos 
9455df835e8SSergio Paracuellos 	trx_device_init(priv);
9465df835e8SSergio Paracuellos 	hostif_init(priv);
9475df835e8SSergio Paracuellos 	ks_wlan_net_start(netdev);
9485df835e8SSergio Paracuellos 	ks7010_init_defaults(priv);
9495df835e8SSergio Paracuellos }
9505df835e8SSergio Paracuellos 
951c4730a92SWolfram Sang static int ks7010_sdio_probe(struct sdio_func *func,
952cdf6ecc5SWolfram Sang 			     const struct sdio_device_id *device)
95313a9930dSWolfram Sang {
9542c3f8945SSergio Paracuellos 	struct ks_wlan_private *priv = NULL;
9552c3f8945SSergio Paracuellos 	struct net_device *netdev = NULL;
95613a9930dSWolfram Sang 	struct ks_sdio_card *card;
9572801d7a2SWolfram Sang 	int ret;
95813a9930dSWolfram Sang 
9592d738bd2SSandhya Bankar 	card = kzalloc(sizeof(*card), GFP_KERNEL);
96013a9930dSWolfram Sang 	if (!card)
96113a9930dSWolfram Sang 		return -ENOMEM;
96213a9930dSWolfram Sang 
96313a9930dSWolfram Sang 	card->func = func;
96413a9930dSWolfram Sang 
96513a9930dSWolfram Sang 	sdio_claim_host(func);
96613a9930dSWolfram Sang 
96713a9930dSWolfram Sang 	ret = sdio_set_block_size(func, KS7010_IO_BLOCK_SIZE);
96831d7b1b1SSergio Paracuellos 	if (ret)
96931d7b1b1SSergio Paracuellos 		goto err_free_card;
97031d7b1b1SSergio Paracuellos 
9715259b329SSergio Paracuellos 	dev_dbg(&card->func->dev, "multi_block=%d sdio_set_block_size()=%d %d\n",
972cdf6ecc5SWolfram Sang 		func->card->cccr.multi_block, func->cur_blksize, ret);
97313a9930dSWolfram Sang 
97413a9930dSWolfram Sang 	ret = sdio_enable_func(func);
97513a9930dSWolfram Sang 	if (ret)
976f283dd69STobin C. Harding 		goto err_free_card;
97713a9930dSWolfram Sang 
9788d4ab8f7SSergio Paracuellos 	ret = ks7010_sdio_setup_irqs(func);
97913a9930dSWolfram Sang 	if (ret)
980f283dd69STobin C. Harding 		goto err_disable_func;
98113a9930dSWolfram Sang 
98213a9930dSWolfram Sang 	sdio_release_host(func);
98313a9930dSWolfram Sang 
98413a9930dSWolfram Sang 	sdio_set_drvdata(func, card);
98513a9930dSWolfram Sang 
9865259b329SSergio Paracuellos 	dev_dbg(&card->func->dev, "class = 0x%X, vendor = 0x%X, device = 0x%X\n",
9877c1c4361STobin C. Harding 		func->class, func->vendor, func->device);
98813a9930dSWolfram Sang 
98913a9930dSWolfram Sang 	/* private memory allocate */
99013a9930dSWolfram Sang 	netdev = alloc_etherdev(sizeof(*priv));
991c7e65f4dSsayli karnik 	if (!netdev) {
992156f2703SSergio Paracuellos 		dev_err(&card->func->dev, "Unable to alloc new net device\n");
993f283dd69STobin C. Harding 		goto err_release_irq;
99413a9930dSWolfram Sang 	}
99506176b87SSergio Paracuellos 
99606176b87SSergio Paracuellos 	ret = dev_alloc_name(netdev, "wlan%d");
99706176b87SSergio Paracuellos 	if (ret < 0) {
998156f2703SSergio Paracuellos 		dev_err(&card->func->dev, "Couldn't get name!\n");
999f283dd69STobin C. Harding 		goto err_free_netdev;
100013a9930dSWolfram Sang 	}
100113a9930dSWolfram Sang 
100213a9930dSWolfram Sang 	priv = netdev_priv(netdev);
100313a9930dSWolfram Sang 
100413a9930dSWolfram Sang 	card->priv = priv;
1005e4844d6fSSergio Paracuellos 	SET_NETDEV_DEV(netdev, &card->func->dev);
100613a9930dSWolfram Sang 
10075df835e8SSergio Paracuellos 	ks7010_private_init(priv, card, netdev);
100813a9930dSWolfram Sang 
1009ed246b9eSTobin C. Harding 	ret = ks7010_upload_firmware(card);
101013a9930dSWolfram Sang 	if (ret) {
10115259b329SSergio Paracuellos 		netdev_err(priv->net_dev,
1012156f2703SSergio Paracuellos 			   "firmware load failed !! ret = %d\n", ret);
1013cf10e78eSTobin C. Harding 		goto err_free_netdev;
101413a9930dSWolfram Sang 	}
101513a9930dSWolfram Sang 
10160e80e31aSSergio Paracuellos 	ks7010_sdio_init_irqs(func, priv);
101753638cefSsayli karnik 
101813a9930dSWolfram Sang 	priv->dev_state = DEVICE_STATE_BOOT;
101913a9930dSWolfram Sang 
10204487cf88SSergio Paracuellos 	priv->wq = alloc_workqueue("wq", WQ_MEM_RECLAIM, 1);
102118bd6dd1STobin C. Harding 	if (!priv->wq) {
10225259b329SSergio Paracuellos 		netdev_err(priv->net_dev, "create_workqueue failed !!\n");
1023cf10e78eSTobin C. Harding 		goto err_free_netdev;
102413a9930dSWolfram Sang 	}
102513a9930dSWolfram Sang 
102618bd6dd1STobin C. Harding 	INIT_DELAYED_WORK(&priv->rw_dwork, ks7010_rw_function);
1027e8593a8aSWolfram Sang 	ks7010_card_init(priv);
102813a9930dSWolfram Sang 
10293fb54d75SWolfram Sang 	ret = register_netdev(priv->net_dev);
10303fb54d75SWolfram Sang 	if (ret)
1031cf10e78eSTobin C. Harding 		goto err_free_netdev;
10323fb54d75SWolfram Sang 
103313a9930dSWolfram Sang 	return 0;
103413a9930dSWolfram Sang 
1035f283dd69STobin C. Harding  err_free_netdev:
10369962d86dSGustavo A. R. Silva 	free_netdev(netdev);
1037f283dd69STobin C. Harding  err_release_irq:
103813a9930dSWolfram Sang 	sdio_claim_host(func);
103913a9930dSWolfram Sang 	sdio_release_irq(func);
1040f283dd69STobin C. Harding  err_disable_func:
104113a9930dSWolfram Sang 	sdio_disable_func(func);
1042f283dd69STobin C. Harding  err_free_card:
104313a9930dSWolfram Sang 	sdio_release_host(func);
104413a9930dSWolfram Sang 	sdio_set_drvdata(func, NULL);
104513a9930dSWolfram Sang 	kfree(card);
10462801d7a2SWolfram Sang 
104713a9930dSWolfram Sang 	return -ENODEV;
104813a9930dSWolfram Sang }
104913a9930dSWolfram Sang 
10502ab6fd59STobin C. Harding /* send stop request to MAC */
10512ab6fd59STobin C. Harding static int send_stop_request(struct sdio_func *func)
10522ab6fd59STobin C. Harding {
10539f2ae0a4SQuytelda Kahja 	struct hostif_stop_request *pp;
10542ab6fd59STobin C. Harding 	struct ks_sdio_card *card;
10552ab6fd59STobin C. Harding 	size_t size;
10562ab6fd59STobin C. Harding 
10572ab6fd59STobin C. Harding 	card = sdio_get_drvdata(func);
10582ab6fd59STobin C. Harding 
10592ab6fd59STobin C. Harding 	pp = kzalloc(hif_align_size(sizeof(*pp)), GFP_KERNEL);
1060cea78d61SSergio Paracuellos 	if (!pp)
10612ab6fd59STobin C. Harding 		return -ENOMEM;
10622ab6fd59STobin C. Harding 
10632ab6fd59STobin C. Harding 	size = sizeof(*pp) - sizeof(pp->header.size);
1064b2d187ccSSergio Paracuellos 	pp->header.size = cpu_to_le16(size);
1065b2d187ccSSergio Paracuellos 	pp->header.event = cpu_to_le16(HIF_STOP_REQ);
10662ab6fd59STobin C. Harding 
10672ab6fd59STobin C. Harding 	sdio_claim_host(func);
106849705f9aSSergio Paracuellos 	write_to_device(card->priv, (u8 *)pp, hif_align_size(sizeof(*pp)));
10692ab6fd59STobin C. Harding 	sdio_release_host(func);
10702ab6fd59STobin C. Harding 
10712ab6fd59STobin C. Harding 	kfree(pp);
10722ab6fd59STobin C. Harding 	return 0;
10732ab6fd59STobin C. Harding }
10742ab6fd59STobin C. Harding 
1075c4730a92SWolfram Sang static void ks7010_sdio_remove(struct sdio_func *func)
107613a9930dSWolfram Sang {
107713a9930dSWolfram Sang 	int ret;
107813a9930dSWolfram Sang 	struct ks_sdio_card *card;
107913a9930dSWolfram Sang 	struct ks_wlan_private *priv;
1080697f9f7fSMuraru Mihaela 
108113a9930dSWolfram Sang 	card = sdio_get_drvdata(func);
108213a9930dSWolfram Sang 
1083c7e65f4dSsayli karnik 	if (!card)
108413a9930dSWolfram Sang 		return;
108513a9930dSWolfram Sang 
108613a9930dSWolfram Sang 	priv = card->priv;
10870cc053ddSSergio Paracuellos 	if (!priv)
10880cc053ddSSergio Paracuellos 		goto err_free_card;
1089803394d0SColin Ian King 
10900cc053ddSSergio Paracuellos 	ks_wlan_net_stop(priv->net_dev);
109113a9930dSWolfram Sang 
109213a9930dSWolfram Sang 	/* interrupt disable */
109313a9930dSWolfram Sang 	sdio_claim_host(func);
1094a704a1bcSSergio Paracuellos 	sdio_writeb(func, 0, INT_ENABLE_REG, &ret);
1095a704a1bcSSergio Paracuellos 	sdio_writeb(func, 0xff, INT_PENDING_REG, &ret);
109613a9930dSWolfram Sang 	sdio_release_host(func);
109713a9930dSWolfram Sang 
10982ab6fd59STobin C. Harding 	ret = send_stop_request(func);
10992ab6fd59STobin C. Harding 	if (ret)	/* memory allocation failure */
11000cc053ddSSergio Paracuellos 		goto err_free_card;
1101697f9f7fSMuraru Mihaela 
110218bd6dd1STobin C. Harding 	if (priv->wq) {
110318bd6dd1STobin C. Harding 		flush_workqueue(priv->wq);
110418bd6dd1STobin C. Harding 		destroy_workqueue(priv->wq);
110513a9930dSWolfram Sang 	}
110613a9930dSWolfram Sang 
110713a9930dSWolfram Sang 	hostif_exit(priv);
110813a9930dSWolfram Sang 
11090cc053ddSSergio Paracuellos 	unregister_netdev(priv->net_dev);
111013a9930dSWolfram Sang 
111113a9930dSWolfram Sang 	trx_device_exit(priv);
111213a9930dSWolfram Sang 	free_netdev(priv->net_dev);
111313a9930dSWolfram Sang 	card->priv = NULL;
111413a9930dSWolfram Sang 
111513a9930dSWolfram Sang 	sdio_claim_host(func);
111613a9930dSWolfram Sang 	sdio_release_irq(func);
111713a9930dSWolfram Sang 	sdio_disable_func(func);
111813a9930dSWolfram Sang 	sdio_release_host(func);
11190cc053ddSSergio Paracuellos err_free_card:
112013a9930dSWolfram Sang 	sdio_set_drvdata(func, NULL);
112113a9930dSWolfram Sang 	kfree(card);
112213a9930dSWolfram Sang }
112313a9930dSWolfram Sang 
112478c74a5fSSergio Paracuellos static const struct sdio_device_id ks7010_sdio_ids[] = {
112578c74a5fSSergio Paracuellos 	{SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_A, SDIO_DEVICE_ID_KS_7010)},
112678c74a5fSSergio Paracuellos 	{SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_B, SDIO_DEVICE_ID_KS_7010)},
112778c74a5fSSergio Paracuellos 	{ /* all zero */ }
112878c74a5fSSergio Paracuellos };
112978c74a5fSSergio Paracuellos MODULE_DEVICE_TABLE(sdio, ks7010_sdio_ids);
113078c74a5fSSergio Paracuellos 
11314c0d46d2SWolfram Sang static struct sdio_driver ks7010_sdio_driver = {
11324c0d46d2SWolfram Sang 	.name = "ks7010_sdio",
11334c0d46d2SWolfram Sang 	.id_table = ks7010_sdio_ids,
11344c0d46d2SWolfram Sang 	.probe = ks7010_sdio_probe,
11354c0d46d2SWolfram Sang 	.remove = ks7010_sdio_remove,
11364c0d46d2SWolfram Sang };
11374c0d46d2SWolfram Sang 
11386b0cb0b0SWolfram Sang module_driver(ks7010_sdio_driver, sdio_register_driver, sdio_unregister_driver);
1139e1240140SWolfram Sang MODULE_AUTHOR("Sang Engineering, Qi-Hardware, KeyStream");
1140e1240140SWolfram Sang MODULE_DESCRIPTION("Driver for KeyStream KS7010 based SDIO cards");
1141e1240140SWolfram Sang MODULE_LICENSE("GPL v2");
1142e1240140SWolfram Sang MODULE_FIRMWARE(ROM_FILE);
1143