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 
ks7010_to_func(struct ks_wlan_private * priv)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) */
ks7010_sdio_readb(struct ks_wlan_private * priv,u32 address,u8 * byte)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) */
ks7010_sdio_read(struct ks_wlan_private * priv,u32 address,u8 * buffer,unsigned int length)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) */
ks7010_sdio_writeb(struct ks_wlan_private * priv,u32 address,u8 byte)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) */
ks7010_sdio_write(struct ks_wlan_private * priv,u32 address,u8 * buffer,unsigned int length)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 
ks_wlan_hw_sleep_doze_request(struct ks_wlan_private * priv)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 
ks_wlan_hw_sleep_wakeup_request(struct ks_wlan_private * priv)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 
ks_wlan_hw_wakeup_request(struct ks_wlan_private * priv)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 
_ks_wlan_hw_power_save(struct ks_wlan_private * priv)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 
ks_wlan_hw_power_save(struct ks_wlan_private * priv)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 
enqueue_txdev(struct ks_wlan_private * priv,unsigned char * p,unsigned long size,void (* complete_handler)(struct ks_wlan_private * priv,struct sk_buff * skb),struct sk_buff * skb)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 */
write_to_device(struct ks_wlan_private * priv,u8 * buffer,unsigned long size)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 
tx_device_task(struct ks_wlan_private * priv)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 
ks_wlan_hw_tx(struct ks_wlan_private * priv,void * p,unsigned long size,void (* complete_handler)(struct ks_wlan_private * priv,struct sk_buff * skb),struct sk_buff * skb)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 {
3830fce6660SNishka Dasgupta 	int result;
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 
rx_event_task(struct tasklet_struct * t)40822f73079SAllen Pais static void rx_event_task(struct tasklet_struct *t)
40913a9930dSWolfram Sang {
41022f73079SAllen Pais 	struct ks_wlan_private *priv = from_tasklet(priv, t, rx_bh_task);
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 
ks_wlan_hw_rx(struct ks_wlan_private * priv,size_t size)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
449a5e7d27eSPayal Kshirsagar 		ret = ks7010_sdio_writeb(priv, READ_STATUS_REG,
450a5e7d27eSPayal Kshirsagar 					 REG_STATUS_IDLE);
451f7172487STobin C. Harding 		if (ret)
452156f2703SSergio Paracuellos 			netdev_err(priv->net_dev, "write READ_STATUS_REG\n");
45353638cefSsayli karnik 
45413b05e46STobin C. Harding 		/* length check fail */
45513b05e46STobin C. Harding 		return;
45613a9930dSWolfram Sang 	}
45713a9930dSWolfram Sang 
45813a9930dSWolfram Sang 	hdr = (struct hostif_hdr *)&rx_buffer->data[0];
45913a9930dSWolfram Sang 	rx_buffer->size = le16_to_cpu(hdr->size) + sizeof(hdr->size);
460d9d1ffd4SCezary Gapinski 	event = le16_to_cpu(hdr->event);
46113a9930dSWolfram Sang 	inc_rxqtail(priv);
46213a9930dSWolfram Sang 
463a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_writeb(priv, READ_STATUS_REG, REG_STATUS_IDLE);
464f7172487STobin C. Harding 	if (ret)
465156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "write READ_STATUS_REG\n");
46613a9930dSWolfram Sang 
4677570d757SSergio Paracuellos 	if (atomic_read(&priv->psstatus.confirm_wait) && is_hif_conf(event)) {
4685259b329SSergio Paracuellos 		netdev_dbg(priv->net_dev, "IS_HIF_CONF true !!\n");
46913a9930dSWolfram Sang 		atomic_dec(&priv->psstatus.confirm_wait);
47013a9930dSWolfram Sang 	}
47113a9930dSWolfram Sang 
472321dabdcSTobin C. Harding 	tasklet_schedule(&priv->rx_bh_task);
47313a9930dSWolfram Sang }
47413a9930dSWolfram Sang 
ks7010_rw_function(struct work_struct * work)47513a9930dSWolfram Sang static void ks7010_rw_function(struct work_struct *work)
47613a9930dSWolfram Sang {
47707511629SSergio Paracuellos 	struct ks_wlan_private *priv = container_of(work,
47807511629SSergio Paracuellos 						    struct ks_wlan_private,
47907511629SSergio Paracuellos 						    rw_dwork.work);
48007511629SSergio Paracuellos 	struct sdio_func *func = ks7010_to_func(priv);
481a51333d1SSergio Paracuellos 	u8 byte;
4821770ae9dSTobin C. Harding 	int ret;
48313a9930dSWolfram Sang 
484e61e73d7STobin C. Harding 	/* wait after DOZE */
4856adc30b1SSergio Paracuellos 	if (time_after(priv->last_doze + msecs_to_jiffies(30), jiffies)) {
4865259b329SSergio Paracuellos 		netdev_dbg(priv->net_dev, "wait after DOZE\n");
48718bd6dd1STobin C. Harding 		queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
48813a9930dSWolfram Sang 		return;
48913a9930dSWolfram Sang 	}
49013a9930dSWolfram Sang 
491e61e73d7STobin C. Harding 	/* wait after WAKEUP */
4926adc30b1SSergio Paracuellos 	while (time_after(priv->last_wakeup + msecs_to_jiffies(30), jiffies)) {
4935259b329SSergio Paracuellos 		netdev_dbg(priv->net_dev, "wait after WAKEUP\n");
49407511629SSergio Paracuellos 		dev_info(&func->dev, "wake: %lu %lu\n",
4956adc30b1SSergio Paracuellos 			 priv->last_wakeup + msecs_to_jiffies(30), jiffies);
49613a9930dSWolfram Sang 		msleep(30);
49713a9930dSWolfram Sang 	}
49813a9930dSWolfram Sang 
49907511629SSergio Paracuellos 	sdio_claim_host(func);
50013a9930dSWolfram Sang 
50113a9930dSWolfram Sang 	/* power save wakeup */
50213a9930dSWolfram Sang 	if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
503e6bc5053SSergio Paracuellos 		if (txq_has_space(priv)) {
50413a9930dSWolfram Sang 			ks_wlan_hw_wakeup_request(priv);
50518bd6dd1STobin C. Harding 			queue_delayed_work(priv->wq, &priv->rw_dwork, 1);
50613a9930dSWolfram Sang 		}
5070dd30a74STobin C. Harding 		goto release_host;
50813a9930dSWolfram Sang 	}
50913a9930dSWolfram Sang 
51013a9930dSWolfram Sang 	/* sleep mode doze */
51113a9930dSWolfram Sang 	if (atomic_read(&priv->sleepstatus.doze_request) == 1) {
51213a9930dSWolfram Sang 		ks_wlan_hw_sleep_doze_request(priv);
5130dd30a74STobin C. Harding 		goto release_host;
51413a9930dSWolfram Sang 	}
51513a9930dSWolfram Sang 	/* sleep mode wakeup */
51613a9930dSWolfram Sang 	if (atomic_read(&priv->sleepstatus.wakeup_request) == 1) {
51713a9930dSWolfram Sang 		ks_wlan_hw_sleep_wakeup_request(priv);
5180dd30a74STobin C. Harding 		goto release_host;
51913a9930dSWolfram Sang 	}
52013a9930dSWolfram Sang 
52113a9930dSWolfram Sang 	/* read (WriteStatus/ReadDataSize FN1:00_0014) */
522a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_readb(priv, WSTATUS_RSIZE_REG, &byte);
5231770ae9dSTobin C. Harding 	if (ret) {
524156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "read WSTATUS_RSIZE_REG psstatus=%d\n",
525cdf6ecc5SWolfram Sang 			   atomic_read(&priv->psstatus.status));
5260dd30a74STobin C. Harding 		goto release_host;
52713a9930dSWolfram Sang 	}
52813a9930dSWolfram Sang 
529f1e79f4bSTobin C. Harding 	if (byte & RSIZE_MASK) {	/* Read schedule */
53089467e74SSergio Paracuellos 		ks_wlan_hw_rx(priv, (size_t)((byte & RSIZE_MASK) << 4));
53113a9930dSWolfram Sang 	}
532f1e79f4bSTobin C. Harding 	if ((byte & WSTATUS_MASK))
5335141e9c6STobin C. Harding 		tx_device_task(priv);
53453638cefSsayli karnik 
53513a9930dSWolfram Sang 	_ks_wlan_hw_power_save(priv);
53613a9930dSWolfram Sang 
5370dd30a74STobin C. Harding release_host:
53807511629SSergio Paracuellos 	sdio_release_host(func);
53913a9930dSWolfram Sang }
54013a9930dSWolfram Sang 
ks_sdio_interrupt(struct sdio_func * func)54113a9930dSWolfram Sang static void ks_sdio_interrupt(struct sdio_func *func)
54213a9930dSWolfram Sang {
543f7172487STobin C. Harding 	int ret;
54413a9930dSWolfram Sang 	struct ks_sdio_card *card;
545feedcf1aSWolfram Sang 	struct ks_wlan_private *priv;
54629699193SSergio Paracuellos 	u8 status, rsize, byte;
54713a9930dSWolfram Sang 
54813a9930dSWolfram Sang 	card = sdio_get_drvdata(func);
54913a9930dSWolfram Sang 	priv = card->priv;
55013a9930dSWolfram Sang 
551638a75b6STobin C. Harding 	if (priv->dev_state < DEVICE_STATE_BOOT)
552638a75b6STobin C. Harding 		goto queue_delayed_work;
553638a75b6STobin C. Harding 
554a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_readb(priv, INT_PENDING_REG, &status);
555f7172487STobin C. Harding 	if (ret) {
556156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "read INT_PENDING_REG\n");
557f283dd69STobin C. Harding 		goto queue_delayed_work;
55813a9930dSWolfram Sang 	}
55913a9930dSWolfram Sang 
56013a9930dSWolfram Sang 	/* schedule task for interrupt status */
56113a9930dSWolfram Sang 	/* bit7 -> Write General Communication B register */
56213a9930dSWolfram Sang 	/* read (General Communication B register) */
56313a9930dSWolfram Sang 	/* bit5 -> Write Status Idle */
56413a9930dSWolfram Sang 	/* bit2 -> Read Status Busy  */
565ddd10774SXiangyang Zhang 	if (status & INT_GCR_B ||
566ddd10774SXiangyang Zhang 	    atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
567a704a1bcSSergio Paracuellos 		ret = ks7010_sdio_readb(priv, GCR_B_REG, &byte);
568f7172487STobin C. Harding 		if (ret) {
569156f2703SSergio Paracuellos 			netdev_err(priv->net_dev, "read GCR_B_REG\n");
570f283dd69STobin C. Harding 			goto queue_delayed_work;
57113a9930dSWolfram Sang 		}
572f1e79f4bSTobin C. Harding 		if (byte == GCR_B_ACTIVE) {
573638a75b6STobin C. Harding 			if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
574638a75b6STobin C. Harding 				atomic_set(&priv->psstatus.status, PS_WAKEUP);
57513a9930dSWolfram Sang 				priv->wakeup_count = 0;
57613a9930dSWolfram Sang 			}
57713a9930dSWolfram Sang 			complete(&priv->psstatus.wakeup_wait);
57813a9930dSWolfram Sang 		}
57913a9930dSWolfram Sang 	}
58013a9930dSWolfram Sang 
58113a9930dSWolfram Sang 	do {
58213a9930dSWolfram Sang 		/* read (WriteStatus/ReadDataSize FN1:00_0014) */
583a704a1bcSSergio Paracuellos 		ret = ks7010_sdio_readb(priv, WSTATUS_RSIZE_REG, &byte);
584f7172487STobin C. Harding 		if (ret) {
585156f2703SSergio Paracuellos 			netdev_err(priv->net_dev, "read WSTATUS_RSIZE_REG\n");
586f283dd69STobin C. Harding 			goto queue_delayed_work;
58713a9930dSWolfram Sang 		}
588f1e79f4bSTobin C. Harding 		rsize = byte & RSIZE_MASK;
5895141e9c6STobin C. Harding 		if (rsize != 0)		/* Read schedule */
59089467e74SSergio Paracuellos 			ks_wlan_hw_rx(priv, (size_t)(rsize << 4));
5915141e9c6STobin C. Harding 
592f1e79f4bSTobin C. Harding 		if (byte & WSTATUS_MASK) {
59313a9930dSWolfram Sang 			if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
594e6bc5053SSergio Paracuellos 				if (txq_has_space(priv)) {
59513a9930dSWolfram Sang 					ks_wlan_hw_wakeup_request(priv);
5967dd51ea1SSergio Paracuellos 					queue_delayed_work(priv->wq,
5977dd51ea1SSergio Paracuellos 							   &priv->rw_dwork, 1);
59813a9930dSWolfram Sang 					return;
59913a9930dSWolfram Sang 				}
600cdf6ecc5SWolfram Sang 			} else {
6015141e9c6STobin C. Harding 				tx_device_task(priv);
60213a9930dSWolfram Sang 			}
60313a9930dSWolfram Sang 		}
60413a9930dSWolfram Sang 	} while (rsize);
60513a9930dSWolfram Sang 
606f283dd69STobin C. Harding queue_delayed_work:
60718bd6dd1STobin C. Harding 	queue_delayed_work(priv->wq, &priv->rw_dwork, 0);
60813a9930dSWolfram Sang }
60913a9930dSWolfram Sang 
trx_device_init(struct ks_wlan_private * priv)610feedcf1aSWolfram Sang static int trx_device_init(struct ks_wlan_private *priv)
61113a9930dSWolfram Sang {
61220358d13SNick Rosbrook 	priv->tx_dev.qhead = 0;
61320358d13SNick Rosbrook 	priv->tx_dev.qtail = 0;
61413a9930dSWolfram Sang 
61520358d13SNick Rosbrook 	priv->rx_dev.qhead = 0;
61620358d13SNick Rosbrook 	priv->rx_dev.qtail = 0;
61713a9930dSWolfram Sang 
61813a9930dSWolfram Sang 	spin_lock_init(&priv->tx_dev.tx_dev_lock);
61913a9930dSWolfram Sang 	spin_lock_init(&priv->rx_dev.rx_dev_lock);
62013a9930dSWolfram Sang 
62122f73079SAllen Pais 	tasklet_setup(&priv->rx_bh_task, rx_event_task);
62213a9930dSWolfram Sang 
62313a9930dSWolfram Sang 	return 0;
62413a9930dSWolfram Sang }
62513a9930dSWolfram Sang 
trx_device_exit(struct ks_wlan_private * priv)626feedcf1aSWolfram Sang static void trx_device_exit(struct ks_wlan_private *priv)
62713a9930dSWolfram Sang {
62813a9930dSWolfram Sang 	struct tx_device_buffer *sp;
62913a9930dSWolfram Sang 
63013a9930dSWolfram Sang 	/* tx buffer clear */
631e6bc5053SSergio Paracuellos 	while (txq_has_space(priv)) {
63213a9930dSWolfram Sang 		sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead];
633e61e73d7STobin C. Harding 		kfree(sp->sendp);
634c7e65f4dSsayli karnik 		if (sp->complete_handler)	/* TX Complete */
635055da4f9STobin C. Harding 			(*sp->complete_handler)(priv, sp->skb);
63613a9930dSWolfram Sang 		inc_txqhead(priv);
63713a9930dSWolfram Sang 	}
63813a9930dSWolfram Sang 
639321dabdcSTobin C. Harding 	tasklet_kill(&priv->rx_bh_task);
64013a9930dSWolfram Sang }
641cdf6ecc5SWolfram Sang 
ks7010_sdio_update_index(struct ks_wlan_private * priv,u32 index)642feedcf1aSWolfram Sang static int ks7010_sdio_update_index(struct ks_wlan_private *priv, u32 index)
64313a9930dSWolfram Sang {
6441770ae9dSTobin C. Harding 	int ret;
64513a9930dSWolfram Sang 	unsigned char *data_buf;
64613a9930dSWolfram Sang 
64747bda74aSJi-Hun Kim 	data_buf = kmemdup(&index, sizeof(u32), GFP_KERNEL);
648aa6ca807STobin C. Harding 	if (!data_buf)
649aa6ca807STobin C. Harding 		return -ENOMEM;
65013a9930dSWolfram Sang 
651a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_write(priv, WRITE_INDEX_REG, data_buf, sizeof(index));
6521770ae9dSTobin C. Harding 	if (ret)
653f283dd69STobin C. Harding 		goto err_free_data_buf;
65413a9930dSWolfram Sang 
655a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_write(priv, READ_INDEX_REG, data_buf, sizeof(index));
6561770ae9dSTobin C. Harding 	if (ret)
657f283dd69STobin C. Harding 		goto err_free_data_buf;
658aa6ca807STobin C. Harding 
659aa6ca807STobin C. Harding 	return 0;
660aa6ca807STobin C. Harding 
661f283dd69STobin C. Harding err_free_data_buf:
662cdf6ecc5SWolfram Sang 	kfree(data_buf);
663aa6ca807STobin C. Harding 
6641770ae9dSTobin C. Harding 	return ret;
66513a9930dSWolfram Sang }
66613a9930dSWolfram Sang 
66713a9930dSWolfram Sang #define ROM_BUFF_SIZE (64 * 1024)
ks7010_sdio_data_compare(struct ks_wlan_private * priv,u32 address,u8 * data,unsigned int size)668feedcf1aSWolfram Sang static int ks7010_sdio_data_compare(struct ks_wlan_private *priv, u32 address,
669a7360b18SSergio Paracuellos 				    u8 *data, unsigned int size)
67013a9930dSWolfram Sang {
6711770ae9dSTobin C. Harding 	int ret;
672a7360b18SSergio Paracuellos 	u8 *read_buf;
673eeed92c0SMarkus Elfring 
67413a9930dSWolfram Sang 	read_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL);
675aa6ca807STobin C. Harding 	if (!read_buf)
676aa6ca807STobin C. Harding 		return -ENOMEM;
67713a9930dSWolfram Sang 
6781770ae9dSTobin C. Harding 	ret = ks7010_sdio_read(priv, address, read_buf, size);
6791770ae9dSTobin C. Harding 	if (ret)
680f283dd69STobin C. Harding 		goto err_free_read_buf;
681aa6ca807STobin C. Harding 
68258af272bSTobin C. Harding 	if (memcmp(data, read_buf, size) != 0) {
68358af272bSTobin C. Harding 		ret = -EIO;
6845259b329SSergio Paracuellos 		netdev_err(priv->net_dev, "data compare error (%d)\n", ret);
685f283dd69STobin C. Harding 		goto err_free_read_buf;
68613a9930dSWolfram Sang 	}
687aa6ca807STobin C. Harding 
688aa6ca807STobin C. Harding 	return 0;
689aa6ca807STobin C. Harding 
690f283dd69STobin C. Harding err_free_read_buf:
691cdf6ecc5SWolfram Sang 	kfree(read_buf);
692aa6ca807STobin C. Harding 
6931770ae9dSTobin C. Harding 	return ret;
69413a9930dSWolfram Sang }
695cdf6ecc5SWolfram Sang 
ks7010_copy_firmware(struct ks_wlan_private * priv,const struct firmware * fw_entry)69667082c22SSergio Paracuellos static int ks7010_copy_firmware(struct ks_wlan_private *priv,
69767082c22SSergio Paracuellos 				const struct firmware *fw_entry)
69813a9930dSWolfram Sang {
699881f76b9STobin C. Harding 	unsigned int length;
70067082c22SSergio Paracuellos 	unsigned int size;
70167082c22SSergio Paracuellos 	unsigned int offset;
70267082c22SSergio Paracuellos 	unsigned int n = 0;
703a7360b18SSergio Paracuellos 	u8 *rom_buf;
70467082c22SSergio Paracuellos 	int ret;
70513a9930dSWolfram Sang 
70613a9930dSWolfram Sang 	rom_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL);
707369e1b69SSudip Mukherjee 	if (!rom_buf)
708aa6ca807STobin C. Harding 		return -ENOMEM;
70913a9930dSWolfram Sang 
71013a9930dSWolfram Sang 	length = fw_entry->size;
71113a9930dSWolfram Sang 
71213a9930dSWolfram Sang 	do {
71313a9930dSWolfram Sang 		if (length >= ROM_BUFF_SIZE) {
71413a9930dSWolfram Sang 			size = ROM_BUFF_SIZE;
71513a9930dSWolfram Sang 			length = length - ROM_BUFF_SIZE;
716cdf6ecc5SWolfram Sang 		} else {
71713a9930dSWolfram Sang 			size = length;
71813a9930dSWolfram Sang 			length = 0;
71913a9930dSWolfram Sang 		}
720cdf6ecc5SWolfram Sang 		if (size == 0)
721cdf6ecc5SWolfram Sang 			break;
722bd6dad98SSergio Paracuellos 
72313a9930dSWolfram Sang 		memcpy(rom_buf, fw_entry->data + n, size);
724e61e73d7STobin C. Harding 
72513a9930dSWolfram Sang 		offset = n;
726bd6dad98SSergio Paracuellos 		ret = ks7010_sdio_update_index(priv,
727bd6dad98SSergio Paracuellos 					       KS7010_IRAM_ADDRESS + offset);
7281770ae9dSTobin C. Harding 		if (ret)
72967082c22SSergio Paracuellos 			goto free_rom_buf;
73013a9930dSWolfram Sang 
7311770ae9dSTobin C. Harding 		ret = ks7010_sdio_write(priv, DATA_WINDOW, rom_buf, size);
7321770ae9dSTobin C. Harding 		if (ret)
73367082c22SSergio Paracuellos 			goto free_rom_buf;
73413a9930dSWolfram Sang 
735bd6dad98SSergio Paracuellos 		ret = ks7010_sdio_data_compare(priv,
736bd6dad98SSergio Paracuellos 					       DATA_WINDOW, rom_buf, size);
7371770ae9dSTobin C. Harding 		if (ret)
73867082c22SSergio Paracuellos 			goto free_rom_buf;
739aa6ca807STobin C. Harding 
74013a9930dSWolfram Sang 		n += size;
74113a9930dSWolfram Sang 
74213a9930dSWolfram Sang 	} while (size);
74313a9930dSWolfram Sang 
744a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_writeb(priv, GCR_A_REG, GCR_A_REMAP);
74567082c22SSergio Paracuellos 
74667082c22SSergio Paracuellos free_rom_buf:
74767082c22SSergio Paracuellos 	kfree(rom_buf);
74867082c22SSergio Paracuellos 	return ret;
74967082c22SSergio Paracuellos }
75067082c22SSergio Paracuellos 
ks7010_upload_firmware(struct ks_sdio_card * card)75167082c22SSergio Paracuellos static int ks7010_upload_firmware(struct ks_sdio_card *card)
75267082c22SSergio Paracuellos {
75367082c22SSergio Paracuellos 	struct ks_wlan_private *priv = card->priv;
75407511629SSergio Paracuellos 	struct sdio_func *func = ks7010_to_func(priv);
75567082c22SSergio Paracuellos 	unsigned int n;
75662a37b72SSergio Paracuellos 	u8 byte = 0;
75767082c22SSergio Paracuellos 	int ret;
75867082c22SSergio Paracuellos 	const struct firmware *fw_entry = NULL;
75967082c22SSergio Paracuellos 
76007511629SSergio Paracuellos 	sdio_claim_host(func);
76167082c22SSergio Paracuellos 
76267082c22SSergio Paracuellos 	/* Firmware running ? */
763a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_readb(priv, GCR_A_REG, &byte);
76467082c22SSergio Paracuellos 	if (ret)
76567082c22SSergio Paracuellos 		goto release_host;
76667082c22SSergio Paracuellos 	if (byte == GCR_A_RUN) {
76767082c22SSergio Paracuellos 		netdev_dbg(priv->net_dev, "MAC firmware running ...\n");
76867082c22SSergio Paracuellos 		ret = -EBUSY;
76967082c22SSergio Paracuellos 		goto release_host;
77067082c22SSergio Paracuellos 	}
77167082c22SSergio Paracuellos 
77267082c22SSergio Paracuellos 	ret = request_firmware(&fw_entry, ROM_FILE,
77307511629SSergio Paracuellos 			       &func->dev);
77467082c22SSergio Paracuellos 	if (ret)
77567082c22SSergio Paracuellos 		goto release_host;
77667082c22SSergio Paracuellos 
77767082c22SSergio Paracuellos 	ret = ks7010_copy_firmware(priv, fw_entry);
7781770ae9dSTobin C. Harding 	if (ret)
779aa6ca807STobin C. Harding 		goto release_firmware;
780aa6ca807STobin C. Harding 
78113a9930dSWolfram Sang 	/* Firmware running check */
78213a9930dSWolfram Sang 	for (n = 0; n < 50; ++n) {
78395751f19SJia-Ju Bai 		usleep_range(10000, 11000);	/* wait_ms(10); */
784a704a1bcSSergio Paracuellos 		ret = ks7010_sdio_readb(priv, GCR_A_REG, &byte);
7851770ae9dSTobin C. Harding 		if (ret)
786aa6ca807STobin C. Harding 			goto release_firmware;
787aa6ca807STobin C. Harding 
788f1e79f4bSTobin C. Harding 		if (byte == GCR_A_RUN)
789cdf6ecc5SWolfram Sang 			break;
79013a9930dSWolfram Sang 	}
79113a9930dSWolfram Sang 	if ((50) <= n) {
7925259b329SSergio Paracuellos 		netdev_err(priv->net_dev, "firmware can't start\n");
7931770ae9dSTobin C. Harding 		ret = -EIO;
794aa6ca807STobin C. Harding 		goto release_firmware;
79513a9930dSWolfram Sang 	}
79613a9930dSWolfram Sang 
7971770ae9dSTobin C. Harding 	ret = 0;
79813a9930dSWolfram Sang 
799aa6ca807STobin C. Harding  release_firmware:
80013a9930dSWolfram Sang 	release_firmware(fw_entry);
80167082c22SSergio Paracuellos  release_host:
80207511629SSergio Paracuellos 	sdio_release_host(func);
803aa6ca807STobin C. Harding 
8041770ae9dSTobin C. Harding 	return ret;
80513a9930dSWolfram Sang }
80613a9930dSWolfram Sang 
ks7010_sme_enqueue_events(struct ks_wlan_private * priv)8076e6156f6SSergio Paracuellos static void ks7010_sme_enqueue_events(struct ks_wlan_private *priv)
80813a9930dSWolfram Sang {
809f0d5a975SSergio Paracuellos 	static const u16 init_events[] = {
810f0d5a975SSergio Paracuellos 		SME_GET_EEPROM_CKSUM, SME_STOP_REQUEST,
811f0d5a975SSergio Paracuellos 		SME_RTS_THRESHOLD_REQUEST, SME_FRAGMENTATION_THRESHOLD_REQUEST,
812f0d5a975SSergio Paracuellos 		SME_WEP_INDEX_REQUEST, SME_WEP_KEY1_REQUEST,
813f0d5a975SSergio Paracuellos 		SME_WEP_KEY2_REQUEST, SME_WEP_KEY3_REQUEST,
814f0d5a975SSergio Paracuellos 		SME_WEP_KEY4_REQUEST, SME_WEP_FLAG_REQUEST,
815f0d5a975SSergio Paracuellos 		SME_RSN_ENABLED_REQUEST, SME_MODE_SET_REQUEST,
816f0d5a975SSergio Paracuellos 		SME_START_REQUEST
817f0d5a975SSergio Paracuellos 	};
818f0d5a975SSergio Paracuellos 	int ev;
81913a9930dSWolfram Sang 
820f0d5a975SSergio Paracuellos 	for (ev = 0; ev < ARRAY_SIZE(init_events); ev++)
821f0d5a975SSergio Paracuellos 		hostif_sme_enqueue(priv, init_events[ev]);
8226e6156f6SSergio Paracuellos }
8236e6156f6SSergio Paracuellos 
ks7010_card_init(struct ks_wlan_private * priv)8246e6156f6SSergio Paracuellos static void ks7010_card_init(struct ks_wlan_private *priv)
8256e6156f6SSergio Paracuellos {
8266e6156f6SSergio Paracuellos 	init_completion(&priv->confirm_wait);
8276e6156f6SSergio Paracuellos 
8286e6156f6SSergio Paracuellos 	/* get mac address & firmware version */
8296e6156f6SSergio Paracuellos 	hostif_sme_enqueue(priv, SME_START);
8306e6156f6SSergio Paracuellos 
8316e6156f6SSergio Paracuellos 	if (!wait_for_completion_interruptible_timeout
8326e6156f6SSergio Paracuellos 	    (&priv->confirm_wait, 5 * HZ)) {
8336e6156f6SSergio Paracuellos 		netdev_dbg(priv->net_dev, "wait time out!! SME_START\n");
8346e6156f6SSergio Paracuellos 	}
8356e6156f6SSergio Paracuellos 
8366e6156f6SSergio Paracuellos 	if (priv->mac_address_valid && priv->version_size != 0)
8376e6156f6SSergio Paracuellos 		priv->dev_state = DEVICE_STATE_PREINIT;
8386e6156f6SSergio Paracuellos 
8396e6156f6SSergio Paracuellos 	ks7010_sme_enqueue_events(priv);
84013a9930dSWolfram Sang 
841cdf6ecc5SWolfram Sang 	if (!wait_for_completion_interruptible_timeout
842cdf6ecc5SWolfram Sang 	    (&priv->confirm_wait, 5 * HZ)) {
8435259b329SSergio Paracuellos 		netdev_dbg(priv->net_dev, "wait time out!! wireless parameter set\n");
84413a9930dSWolfram Sang 	}
84513a9930dSWolfram Sang 
84613a9930dSWolfram Sang 	if (priv->dev_state >= DEVICE_STATE_PREINIT) {
8475259b329SSergio Paracuellos 		netdev_dbg(priv->net_dev, "DEVICE READY!!\n");
84813a9930dSWolfram Sang 		priv->dev_state = DEVICE_STATE_READY;
84913a9930dSWolfram Sang 	}
85013a9930dSWolfram Sang }
85113a9930dSWolfram Sang 
ks7010_init_defaults(struct ks_wlan_private * priv)8526ee9169bSWolfram Sang static void ks7010_init_defaults(struct ks_wlan_private *priv)
8536ee9169bSWolfram Sang {
8546ee9169bSWolfram Sang 	priv->reg.tx_rate = TX_RATE_AUTO;
8556ee9169bSWolfram Sang 	priv->reg.preamble = LONG_PREAMBLE;
8568fb8e05cSTobin C. Harding 	priv->reg.power_mgmt = POWER_MGMT_ACTIVE;
8576ee9169bSWolfram Sang 	priv->reg.scan_type = ACTIVE_SCAN;
8586ee9169bSWolfram Sang 	priv->reg.beacon_lost_count = 20;
8596ee9169bSWolfram Sang 	priv->reg.rts = 2347UL;
8606ee9169bSWolfram Sang 	priv->reg.fragment = 2346UL;
8616ee9169bSWolfram Sang 	priv->reg.phy_type = D_11BG_COMPATIBLE_MODE;
8626ee9169bSWolfram Sang 	priv->reg.cts_mode = CTS_MODE_FALSE;
8636ee9169bSWolfram Sang 	priv->reg.rate_set.body[11] = TX_RATE_54M;
8646ee9169bSWolfram Sang 	priv->reg.rate_set.body[10] = TX_RATE_48M;
8656ee9169bSWolfram Sang 	priv->reg.rate_set.body[9] = TX_RATE_36M;
8666ee9169bSWolfram Sang 	priv->reg.rate_set.body[8] = TX_RATE_18M;
8676ee9169bSWolfram Sang 	priv->reg.rate_set.body[7] = TX_RATE_9M;
8686ee9169bSWolfram Sang 	priv->reg.rate_set.body[6] = TX_RATE_24M | BASIC_RATE;
8696ee9169bSWolfram Sang 	priv->reg.rate_set.body[5] = TX_RATE_12M | BASIC_RATE;
8706ee9169bSWolfram Sang 	priv->reg.rate_set.body[4] = TX_RATE_6M | BASIC_RATE;
8716ee9169bSWolfram Sang 	priv->reg.rate_set.body[3] = TX_RATE_11M | BASIC_RATE;
8726ee9169bSWolfram Sang 	priv->reg.rate_set.body[2] = TX_RATE_5M | BASIC_RATE;
8736ee9169bSWolfram Sang 	priv->reg.rate_set.body[1] = TX_RATE_2M | BASIC_RATE;
8746ee9169bSWolfram Sang 	priv->reg.rate_set.body[0] = TX_RATE_1M | BASIC_RATE;
8756ee9169bSWolfram Sang 	priv->reg.tx_rate = TX_RATE_FULL_AUTO;
8766ee9169bSWolfram Sang 	priv->reg.rate_set.size = 12;
8776ee9169bSWolfram Sang }
8786ee9169bSWolfram Sang 
ks7010_sdio_setup_irqs(struct sdio_func * func)8798d4ab8f7SSergio Paracuellos static int ks7010_sdio_setup_irqs(struct sdio_func *func)
8808d4ab8f7SSergio Paracuellos {
8818d4ab8f7SSergio Paracuellos 	int ret;
8828d4ab8f7SSergio Paracuellos 
8838d4ab8f7SSergio Paracuellos 	/* interrupt disable */
884a704a1bcSSergio Paracuellos 	sdio_writeb(func, 0, INT_ENABLE_REG, &ret);
8858d4ab8f7SSergio Paracuellos 	if (ret)
8868d4ab8f7SSergio Paracuellos 		goto irq_error;
8878d4ab8f7SSergio Paracuellos 
888a704a1bcSSergio Paracuellos 	sdio_writeb(func, 0xff, INT_PENDING_REG, &ret);
8898d4ab8f7SSergio Paracuellos 	if (ret)
8908d4ab8f7SSergio Paracuellos 		goto irq_error;
8918d4ab8f7SSergio Paracuellos 
8928d4ab8f7SSergio Paracuellos 	/* setup interrupt handler */
8938d4ab8f7SSergio Paracuellos 	ret = sdio_claim_irq(func, ks_sdio_interrupt);
8948d4ab8f7SSergio Paracuellos 
8958d4ab8f7SSergio Paracuellos irq_error:
8968d4ab8f7SSergio Paracuellos 	return ret;
8978d4ab8f7SSergio Paracuellos }
8988d4ab8f7SSergio Paracuellos 
ks7010_sdio_init_irqs(struct sdio_func * func,struct ks_wlan_private * priv)8990e80e31aSSergio Paracuellos static void ks7010_sdio_init_irqs(struct sdio_func *func,
9000e80e31aSSergio Paracuellos 				  struct ks_wlan_private *priv)
9010e80e31aSSergio Paracuellos {
90258f8128eSSergio Paracuellos 	u8 byte;
9030e80e31aSSergio Paracuellos 	int ret;
9040e80e31aSSergio Paracuellos 
9050e80e31aSSergio Paracuellos 	/*
9060e80e31aSSergio Paracuellos 	 * interrupt setting
9070e80e31aSSergio Paracuellos 	 * clear Interrupt status write
9080e80e31aSSergio Paracuellos 	 * (ARMtoSD_InterruptPending FN1:00_0024)
9090e80e31aSSergio Paracuellos 	 */
9100e80e31aSSergio Paracuellos 	sdio_claim_host(func);
911a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_writeb(priv, INT_PENDING_REG, 0xff);
9120e80e31aSSergio Paracuellos 	sdio_release_host(func);
9130e80e31aSSergio Paracuellos 	if (ret)
914156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "write INT_PENDING_REG\n");
9150e80e31aSSergio Paracuellos 
9160e80e31aSSergio Paracuellos 	/* enable ks7010sdio interrupt */
9170e80e31aSSergio Paracuellos 	byte = (INT_GCR_B | INT_READ_STATUS | INT_WRITE_STATUS);
9180e80e31aSSergio Paracuellos 	sdio_claim_host(func);
919a704a1bcSSergio Paracuellos 	ret = ks7010_sdio_writeb(priv, INT_ENABLE_REG, byte);
9200e80e31aSSergio Paracuellos 	sdio_release_host(func);
9210e80e31aSSergio Paracuellos 	if (ret)
922156f2703SSergio Paracuellos 		netdev_err(priv->net_dev, "write INT_ENABLE_REG\n");
9230e80e31aSSergio Paracuellos }
9240e80e31aSSergio Paracuellos 
ks7010_private_init(struct ks_wlan_private * priv,struct ks_sdio_card * card,struct net_device * netdev)9255df835e8SSergio Paracuellos static void ks7010_private_init(struct ks_wlan_private *priv,
9265df835e8SSergio Paracuellos 				struct ks_sdio_card *card,
9275df835e8SSergio Paracuellos 				struct net_device *netdev)
9285df835e8SSergio Paracuellos {
9295df835e8SSergio Paracuellos 	/* private memory initialize */
93007511629SSergio Paracuellos 	priv->if_hw = card;
9315df835e8SSergio Paracuellos 
9325df835e8SSergio Paracuellos 	priv->dev_state = DEVICE_STATE_PREBOOT;
9335df835e8SSergio Paracuellos 	priv->net_dev = netdev;
9345df835e8SSergio Paracuellos 	priv->firmware_version[0] = '\0';
9355df835e8SSergio Paracuellos 	priv->version_size = 0;
9365df835e8SSergio Paracuellos 	priv->last_doze = jiffies;
9375df835e8SSergio Paracuellos 	priv->last_wakeup = jiffies;
9385df835e8SSergio Paracuellos 	memset(&priv->nstats, 0, sizeof(priv->nstats));
9395df835e8SSergio Paracuellos 	memset(&priv->wstats, 0, sizeof(priv->wstats));
9405df835e8SSergio Paracuellos 
9415df835e8SSergio Paracuellos 	/* sleep mode */
94256315e55SChristophe JAILLET 	atomic_set(&priv->sleepstatus.status, 0);
9435df835e8SSergio Paracuellos 	atomic_set(&priv->sleepstatus.doze_request, 0);
9445df835e8SSergio Paracuellos 	atomic_set(&priv->sleepstatus.wakeup_request, 0);
9455df835e8SSergio Paracuellos 
9465df835e8SSergio Paracuellos 	trx_device_init(priv);
9475df835e8SSergio Paracuellos 	hostif_init(priv);
9485df835e8SSergio Paracuellos 	ks_wlan_net_start(netdev);
9495df835e8SSergio Paracuellos 	ks7010_init_defaults(priv);
9505df835e8SSergio Paracuellos }
9515df835e8SSergio Paracuellos 
ks7010_sdio_probe(struct sdio_func * func,const struct sdio_device_id * device)952c4730a92SWolfram Sang static int ks7010_sdio_probe(struct sdio_func *func,
953cdf6ecc5SWolfram Sang 			     const struct sdio_device_id *device)
95413a9930dSWolfram Sang {
9552c3f8945SSergio Paracuellos 	struct ks_wlan_private *priv = NULL;
9562c3f8945SSergio Paracuellos 	struct net_device *netdev = NULL;
95713a9930dSWolfram Sang 	struct ks_sdio_card *card;
9582801d7a2SWolfram Sang 	int ret;
95913a9930dSWolfram Sang 
9602d738bd2SSandhya Bankar 	card = kzalloc(sizeof(*card), GFP_KERNEL);
96113a9930dSWolfram Sang 	if (!card)
96213a9930dSWolfram Sang 		return -ENOMEM;
96313a9930dSWolfram Sang 
96413a9930dSWolfram Sang 	card->func = func;
96513a9930dSWolfram Sang 
96613a9930dSWolfram Sang 	sdio_claim_host(func);
96713a9930dSWolfram Sang 
96813a9930dSWolfram Sang 	ret = sdio_set_block_size(func, KS7010_IO_BLOCK_SIZE);
96931d7b1b1SSergio Paracuellos 	if (ret)
97031d7b1b1SSergio Paracuellos 		goto err_free_card;
97131d7b1b1SSergio Paracuellos 
9725259b329SSergio Paracuellos 	dev_dbg(&card->func->dev, "multi_block=%d sdio_set_block_size()=%d %d\n",
973cdf6ecc5SWolfram Sang 		func->card->cccr.multi_block, func->cur_blksize, ret);
97413a9930dSWolfram Sang 
97513a9930dSWolfram Sang 	ret = sdio_enable_func(func);
97613a9930dSWolfram Sang 	if (ret)
977f283dd69STobin C. Harding 		goto err_free_card;
97813a9930dSWolfram Sang 
9798d4ab8f7SSergio Paracuellos 	ret = ks7010_sdio_setup_irqs(func);
98013a9930dSWolfram Sang 	if (ret)
981f283dd69STobin C. Harding 		goto err_disable_func;
98213a9930dSWolfram Sang 
98313a9930dSWolfram Sang 	sdio_release_host(func);
98413a9930dSWolfram Sang 
98513a9930dSWolfram Sang 	sdio_set_drvdata(func, card);
98613a9930dSWolfram Sang 
9875259b329SSergio Paracuellos 	dev_dbg(&card->func->dev, "class = 0x%X, vendor = 0x%X, device = 0x%X\n",
9887c1c4361STobin C. Harding 		func->class, func->vendor, func->device);
98913a9930dSWolfram Sang 
99013a9930dSWolfram Sang 	/* private memory allocate */
99113a9930dSWolfram Sang 	netdev = alloc_etherdev(sizeof(*priv));
992c7e65f4dSsayli karnik 	if (!netdev) {
993156f2703SSergio Paracuellos 		dev_err(&card->func->dev, "Unable to alloc new net device\n");
994f283dd69STobin C. Harding 		goto err_release_irq;
99513a9930dSWolfram Sang 	}
99606176b87SSergio Paracuellos 
99706176b87SSergio Paracuellos 	ret = dev_alloc_name(netdev, "wlan%d");
99806176b87SSergio Paracuellos 	if (ret < 0) {
999156f2703SSergio Paracuellos 		dev_err(&card->func->dev, "Couldn't get name!\n");
1000f283dd69STobin C. Harding 		goto err_free_netdev;
100113a9930dSWolfram Sang 	}
100213a9930dSWolfram Sang 
100313a9930dSWolfram Sang 	priv = netdev_priv(netdev);
100413a9930dSWolfram Sang 
100513a9930dSWolfram Sang 	card->priv = priv;
1006e4844d6fSSergio Paracuellos 	SET_NETDEV_DEV(netdev, &card->func->dev);
100713a9930dSWolfram Sang 
10085df835e8SSergio Paracuellos 	ks7010_private_init(priv, card, netdev);
100913a9930dSWolfram Sang 
1010ed246b9eSTobin C. Harding 	ret = ks7010_upload_firmware(card);
101113a9930dSWolfram Sang 	if (ret) {
10125259b329SSergio Paracuellos 		netdev_err(priv->net_dev,
1013156f2703SSergio Paracuellos 			   "firmware load failed !! ret = %d\n", ret);
1014cf10e78eSTobin C. Harding 		goto err_free_netdev;
101513a9930dSWolfram Sang 	}
101613a9930dSWolfram Sang 
10170e80e31aSSergio Paracuellos 	ks7010_sdio_init_irqs(func, priv);
101853638cefSsayli karnik 
101913a9930dSWolfram Sang 	priv->dev_state = DEVICE_STATE_BOOT;
102013a9930dSWolfram Sang 
10214487cf88SSergio Paracuellos 	priv->wq = alloc_workqueue("wq", WQ_MEM_RECLAIM, 1);
102218bd6dd1STobin C. Harding 	if (!priv->wq) {
10235259b329SSergio Paracuellos 		netdev_err(priv->net_dev, "create_workqueue failed !!\n");
1024cf10e78eSTobin C. Harding 		goto err_free_netdev;
102513a9930dSWolfram Sang 	}
102613a9930dSWolfram Sang 
102718bd6dd1STobin C. Harding 	INIT_DELAYED_WORK(&priv->rw_dwork, ks7010_rw_function);
1028e8593a8aSWolfram Sang 	ks7010_card_init(priv);
102913a9930dSWolfram Sang 
10303fb54d75SWolfram Sang 	ret = register_netdev(priv->net_dev);
10313fb54d75SWolfram Sang 	if (ret)
1032d1e7550aSQinglang Miao 		goto err_destroy_wq;
10333fb54d75SWolfram Sang 
103413a9930dSWolfram Sang 	return 0;
103513a9930dSWolfram Sang 
1036d1e7550aSQinglang Miao  err_destroy_wq:
1037d1e7550aSQinglang Miao 	destroy_workqueue(priv->wq);
1038f283dd69STobin C. Harding  err_free_netdev:
10399962d86dSGustavo A. R. Silva 	free_netdev(netdev);
1040f283dd69STobin C. Harding  err_release_irq:
104113a9930dSWolfram Sang 	sdio_claim_host(func);
104213a9930dSWolfram Sang 	sdio_release_irq(func);
1043f283dd69STobin C. Harding  err_disable_func:
104413a9930dSWolfram Sang 	sdio_disable_func(func);
1045f283dd69STobin C. Harding  err_free_card:
104613a9930dSWolfram Sang 	sdio_release_host(func);
104713a9930dSWolfram Sang 	sdio_set_drvdata(func, NULL);
104813a9930dSWolfram Sang 	kfree(card);
10492801d7a2SWolfram Sang 
105013a9930dSWolfram Sang 	return -ENODEV;
105113a9930dSWolfram Sang }
105213a9930dSWolfram Sang 
10532ab6fd59STobin C. Harding /* send stop request to MAC */
send_stop_request(struct sdio_func * func)10542ab6fd59STobin C. Harding static int send_stop_request(struct sdio_func *func)
10552ab6fd59STobin C. Harding {
10569f2ae0a4SQuytelda Kahja 	struct hostif_stop_request *pp;
10572ab6fd59STobin C. Harding 	struct ks_sdio_card *card;
10582ab6fd59STobin C. Harding 	size_t size;
10592ab6fd59STobin C. Harding 
10602ab6fd59STobin C. Harding 	card = sdio_get_drvdata(func);
10612ab6fd59STobin C. Harding 
10622ab6fd59STobin C. Harding 	pp = kzalloc(hif_align_size(sizeof(*pp)), GFP_KERNEL);
1063cea78d61SSergio Paracuellos 	if (!pp)
10642ab6fd59STobin C. Harding 		return -ENOMEM;
10652ab6fd59STobin C. Harding 
10662ab6fd59STobin C. Harding 	size = sizeof(*pp) - sizeof(pp->header.size);
1067b2d187ccSSergio Paracuellos 	pp->header.size = cpu_to_le16(size);
1068b2d187ccSSergio Paracuellos 	pp->header.event = cpu_to_le16(HIF_STOP_REQ);
10692ab6fd59STobin C. Harding 
10702ab6fd59STobin C. Harding 	sdio_claim_host(func);
107149705f9aSSergio Paracuellos 	write_to_device(card->priv, (u8 *)pp, hif_align_size(sizeof(*pp)));
10722ab6fd59STobin C. Harding 	sdio_release_host(func);
10732ab6fd59STobin C. Harding 
10742ab6fd59STobin C. Harding 	kfree(pp);
10752ab6fd59STobin C. Harding 	return 0;
10762ab6fd59STobin C. Harding }
10772ab6fd59STobin C. Harding 
ks7010_sdio_remove(struct sdio_func * func)1078c4730a92SWolfram Sang static void ks7010_sdio_remove(struct sdio_func *func)
107913a9930dSWolfram Sang {
108013a9930dSWolfram Sang 	int ret;
108113a9930dSWolfram Sang 	struct ks_sdio_card *card;
108213a9930dSWolfram Sang 	struct ks_wlan_private *priv;
1083697f9f7fSMuraru Mihaela 
108413a9930dSWolfram Sang 	card = sdio_get_drvdata(func);
108513a9930dSWolfram Sang 
1086c7e65f4dSsayli karnik 	if (!card)
108713a9930dSWolfram Sang 		return;
108813a9930dSWolfram Sang 
108913a9930dSWolfram Sang 	priv = card->priv;
10900cc053ddSSergio Paracuellos 	if (!priv)
10910cc053ddSSergio Paracuellos 		goto err_free_card;
1092803394d0SColin Ian King 
10930cc053ddSSergio Paracuellos 	ks_wlan_net_stop(priv->net_dev);
109413a9930dSWolfram Sang 
109513a9930dSWolfram Sang 	/* interrupt disable */
109613a9930dSWolfram Sang 	sdio_claim_host(func);
1097a704a1bcSSergio Paracuellos 	sdio_writeb(func, 0, INT_ENABLE_REG, &ret);
1098a704a1bcSSergio Paracuellos 	sdio_writeb(func, 0xff, INT_PENDING_REG, &ret);
109913a9930dSWolfram Sang 	sdio_release_host(func);
110013a9930dSWolfram Sang 
11012ab6fd59STobin C. Harding 	ret = send_stop_request(func);
11022ab6fd59STobin C. Harding 	if (ret)	/* memory allocation failure */
11030cc053ddSSergio Paracuellos 		goto err_free_card;
1104697f9f7fSMuraru Mihaela 
1105*f89019d4SMinghao Chi (CGEL ZTE) 	if (priv->wq)
110618bd6dd1STobin C. Harding 		destroy_workqueue(priv->wq);
110713a9930dSWolfram Sang 
110813a9930dSWolfram Sang 	hostif_exit(priv);
110913a9930dSWolfram Sang 
11100cc053ddSSergio Paracuellos 	unregister_netdev(priv->net_dev);
111113a9930dSWolfram Sang 
111213a9930dSWolfram Sang 	trx_device_exit(priv);
111313a9930dSWolfram Sang 	free_netdev(priv->net_dev);
111413a9930dSWolfram Sang 	card->priv = NULL;
111513a9930dSWolfram Sang 
111613a9930dSWolfram Sang 	sdio_claim_host(func);
111713a9930dSWolfram Sang 	sdio_release_irq(func);
111813a9930dSWolfram Sang 	sdio_disable_func(func);
111913a9930dSWolfram Sang 	sdio_release_host(func);
11200cc053ddSSergio Paracuellos err_free_card:
112113a9930dSWolfram Sang 	sdio_set_drvdata(func, NULL);
112213a9930dSWolfram Sang 	kfree(card);
112313a9930dSWolfram Sang }
112413a9930dSWolfram Sang 
112578c74a5fSSergio Paracuellos static const struct sdio_device_id ks7010_sdio_ids[] = {
112678c74a5fSSergio Paracuellos 	{SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_A, SDIO_DEVICE_ID_KS_7010)},
112778c74a5fSSergio Paracuellos 	{SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_B, SDIO_DEVICE_ID_KS_7010)},
112878c74a5fSSergio Paracuellos 	{ /* all zero */ }
112978c74a5fSSergio Paracuellos };
113078c74a5fSSergio Paracuellos MODULE_DEVICE_TABLE(sdio, ks7010_sdio_ids);
113178c74a5fSSergio Paracuellos 
11324c0d46d2SWolfram Sang static struct sdio_driver ks7010_sdio_driver = {
11334c0d46d2SWolfram Sang 	.name = "ks7010_sdio",
11344c0d46d2SWolfram Sang 	.id_table = ks7010_sdio_ids,
11354c0d46d2SWolfram Sang 	.probe = ks7010_sdio_probe,
11364c0d46d2SWolfram Sang 	.remove = ks7010_sdio_remove,
11374c0d46d2SWolfram Sang };
11384c0d46d2SWolfram Sang 
11396b0cb0b0SWolfram Sang module_driver(ks7010_sdio_driver, sdio_register_driver, sdio_unregister_driver);
1140e1240140SWolfram Sang MODULE_AUTHOR("Sang Engineering, Qi-Hardware, KeyStream");
1141e1240140SWolfram Sang MODULE_DESCRIPTION("Driver for KeyStream KS7010 based SDIO cards");
1142e1240140SWolfram Sang MODULE_LICENSE("GPL v2");
1143e1240140SWolfram Sang MODULE_FIRMWARE(ROM_FILE);
1144