113a9930dSWolfram Sang /*
213a9930dSWolfram Sang  *   Driver for KeyStream, KS7010 based SDIO cards.
313a9930dSWolfram Sang  *
413a9930dSWolfram Sang  *   Copyright (C) 2006-2008 KeyStream Corp.
513a9930dSWolfram Sang  *   Copyright (C) 2009 Renesas Technology Corp.
6c5d9a030SWolfram Sang  *   Copyright (C) 2016 Sang Engineering, Wolfram Sang
713a9930dSWolfram Sang  *
813a9930dSWolfram Sang  *   This program is free software; you can redistribute it and/or modify
9c5d9a030SWolfram Sang  *   it under the terms of the GNU General Public License version 2 as
10c5d9a030SWolfram Sang  *   published by the Free Software Foundation.
1113a9930dSWolfram Sang  */
1213a9930dSWolfram Sang 
131c013a5cSWolfram Sang #include <linux/firmware.h>
1413a9930dSWolfram Sang #include <linux/mmc/card.h>
1513a9930dSWolfram Sang #include <linux/mmc/sdio_func.h>
161c013a5cSWolfram Sang #include <linux/workqueue.h>
17041c4d75Ssayli karnik #include <linux/atomic.h>
1813a9930dSWolfram Sang 
1913a9930dSWolfram Sang #include "ks_wlan.h"
2013a9930dSWolfram Sang #include "ks_wlan_ioctl.h"
2113a9930dSWolfram Sang #include "ks_hostif.h"
2213a9930dSWolfram Sang #include "ks7010_sdio.h"
2313a9930dSWolfram Sang 
2413a9930dSWolfram Sang #define KS7010_FUNC_NUM 1
2513a9930dSWolfram Sang #define KS7010_IO_BLOCK_SIZE 512
2613a9930dSWolfram Sang #define KS7010_MAX_CLOCK 25000000
2713a9930dSWolfram Sang 
28f9b5bd05SWolfram Sang static const struct sdio_device_id ks7010_sdio_ids[] = {
2913a9930dSWolfram Sang 	{SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_A, SDIO_DEVICE_ID_KS_7010)},
3013a9930dSWolfram Sang 	{SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_B, SDIO_DEVICE_ID_KS_7010)},
3113a9930dSWolfram Sang 	{ /* all zero */ }
3213a9930dSWolfram Sang };
33f9b5bd05SWolfram Sang MODULE_DEVICE_TABLE(sdio, ks7010_sdio_ids);
3413a9930dSWolfram Sang 
3513a9930dSWolfram Sang /* macro */
3613a9930dSWolfram Sang 
3713a9930dSWolfram Sang #define inc_txqhead(priv) \
3813a9930dSWolfram Sang 	(priv->tx_dev.qhead = (priv->tx_dev.qhead + 1) % TX_DEVICE_BUFF_SIZE)
3913a9930dSWolfram Sang #define inc_txqtail(priv) \
4013a9930dSWolfram Sang 	(priv->tx_dev.qtail = (priv->tx_dev.qtail + 1) % TX_DEVICE_BUFF_SIZE)
4113a9930dSWolfram Sang #define cnt_txqbody(priv) \
4213a9930dSWolfram Sang 	(((priv->tx_dev.qtail + TX_DEVICE_BUFF_SIZE) - (priv->tx_dev.qhead)) % TX_DEVICE_BUFF_SIZE)
4313a9930dSWolfram Sang 
4413a9930dSWolfram Sang #define inc_rxqhead(priv) \
4513a9930dSWolfram Sang 	(priv->rx_dev.qhead = (priv->rx_dev.qhead + 1) % RX_DEVICE_BUFF_SIZE)
4613a9930dSWolfram Sang #define inc_rxqtail(priv) \
4713a9930dSWolfram Sang 	(priv->rx_dev.qtail = (priv->rx_dev.qtail + 1) % RX_DEVICE_BUFF_SIZE)
4813a9930dSWolfram Sang #define cnt_rxqbody(priv) \
4913a9930dSWolfram Sang 	(((priv->rx_dev.qtail + RX_DEVICE_BUFF_SIZE) - (priv->rx_dev.qhead)) % RX_DEVICE_BUFF_SIZE)
5013a9930dSWolfram Sang 
514c0d46d2SWolfram Sang static int ks7010_sdio_read(struct ks_wlan_private *priv, unsigned int address,
524c0d46d2SWolfram Sang 			    unsigned char *buffer, int length)
534c0d46d2SWolfram Sang {
544c0d46d2SWolfram Sang 	struct ks_sdio_card *card;
551770ae9dSTobin C. Harding 	int ret;
564c0d46d2SWolfram Sang 
574c0d46d2SWolfram Sang 	card = priv->ks_wlan_hw.sdio_card;
584c0d46d2SWolfram Sang 
594c0d46d2SWolfram Sang 	if (length == 1)	/* CMD52 */
601770ae9dSTobin C. Harding 		*buffer = sdio_readb(card->func, address, &ret);
614c0d46d2SWolfram Sang 	else	/* CMD53 multi-block transfer */
621770ae9dSTobin C. Harding 		ret = sdio_memcpy_fromio(card->func, buffer, address, length);
634c0d46d2SWolfram Sang 
641770ae9dSTobin C. Harding 	if (ret) {
651770ae9dSTobin C. Harding 		DPRINTK(1, "sdio error=%d size=%d\n", ret, length);
661770ae9dSTobin C. Harding 		return ret;
671770ae9dSTobin C. Harding 	}
684c0d46d2SWolfram Sang 
691770ae9dSTobin C. Harding 	return 0;
704c0d46d2SWolfram Sang }
714c0d46d2SWolfram Sang 
724c0d46d2SWolfram Sang static int ks7010_sdio_write(struct ks_wlan_private *priv, unsigned int address,
734c0d46d2SWolfram Sang 			     unsigned char *buffer, int length)
744c0d46d2SWolfram Sang {
754c0d46d2SWolfram Sang 	struct ks_sdio_card *card;
761770ae9dSTobin C. Harding 	int ret;
774c0d46d2SWolfram Sang 
784c0d46d2SWolfram Sang 	card = priv->ks_wlan_hw.sdio_card;
794c0d46d2SWolfram Sang 
804c0d46d2SWolfram Sang 	if (length == 1)	/* CMD52 */
811770ae9dSTobin C. Harding 		sdio_writeb(card->func, *buffer, address, &ret);
824c0d46d2SWolfram Sang 	else	/* CMD53 */
831770ae9dSTobin C. Harding 		ret = sdio_memcpy_toio(card->func, address, buffer, length);
844c0d46d2SWolfram Sang 
851770ae9dSTobin C. Harding 	if (ret) {
861770ae9dSTobin C. Harding 		DPRINTK(1, "sdio error=%d size=%d\n", ret, length);
871770ae9dSTobin C. Harding 		return ret;
881770ae9dSTobin C. Harding 	}
894c0d46d2SWolfram Sang 
901770ae9dSTobin C. Harding 	return 0;
914c0d46d2SWolfram Sang }
924c0d46d2SWolfram Sang 
934433459aSSergio Paracuellos static void ks_wlan_hw_sleep_doze_request(struct ks_wlan_private *priv)
9413a9930dSWolfram Sang {
9513a9930dSWolfram Sang 	unsigned char rw_data;
961770ae9dSTobin C. Harding 	int ret;
9713a9930dSWolfram Sang 
9813a9930dSWolfram Sang 	DPRINTK(4, "\n");
9913a9930dSWolfram Sang 
10013a9930dSWolfram Sang 	/* clear request */
10113a9930dSWolfram Sang 	atomic_set(&priv->sleepstatus.doze_request, 0);
10213a9930dSWolfram Sang 
10313a9930dSWolfram Sang 	if (atomic_read(&priv->sleepstatus.status) == 0) {
10413a9930dSWolfram Sang 		rw_data = GCR_B_DOZE;
1051770ae9dSTobin C. Harding 		ret = ks7010_sdio_write(priv, GCR_B, &rw_data, sizeof(rw_data));
1061770ae9dSTobin C. Harding 		if (ret) {
10713a9930dSWolfram Sang 			DPRINTK(1, " error : GCR_B=%02X\n", rw_data);
108f283dd69STobin C. Harding 			goto set_sleep_mode;
10913a9930dSWolfram Sang 		}
11013a9930dSWolfram Sang 		DPRINTK(4, "PMG SET!! : GCR_B=%02X\n", rw_data);
11113a9930dSWolfram Sang 		DPRINTK(3, "sleep_mode=SLP_SLEEP\n");
11213a9930dSWolfram Sang 		atomic_set(&priv->sleepstatus.status, 1);
11313a9930dSWolfram Sang 		priv->last_doze = jiffies;
114cdf6ecc5SWolfram Sang 	} else {
11513a9930dSWolfram Sang 		DPRINTK(1, "sleep_mode=%d\n", priv->sleep_mode);
11613a9930dSWolfram Sang 	}
11713a9930dSWolfram Sang 
118f283dd69STobin C. Harding set_sleep_mode:
11913a9930dSWolfram Sang 	priv->sleep_mode = atomic_read(&priv->sleepstatus.status);
12013a9930dSWolfram Sang }
12113a9930dSWolfram Sang 
1224433459aSSergio Paracuellos static void ks_wlan_hw_sleep_wakeup_request(struct ks_wlan_private *priv)
12313a9930dSWolfram Sang {
12413a9930dSWolfram Sang 	unsigned char rw_data;
1251770ae9dSTobin C. Harding 	int ret;
12613a9930dSWolfram Sang 
12713a9930dSWolfram Sang 	DPRINTK(4, "\n");
12813a9930dSWolfram Sang 
12913a9930dSWolfram Sang 	/* clear request */
13013a9930dSWolfram Sang 	atomic_set(&priv->sleepstatus.wakeup_request, 0);
13113a9930dSWolfram Sang 
13213a9930dSWolfram Sang 	if (atomic_read(&priv->sleepstatus.status) == 1) {
13313a9930dSWolfram Sang 		rw_data = WAKEUP_REQ;
1341770ae9dSTobin C. Harding 		ret = ks7010_sdio_write(priv, WAKEUP, &rw_data, sizeof(rw_data));
1351770ae9dSTobin C. Harding 		if (ret) {
13613a9930dSWolfram Sang 			DPRINTK(1, " error : WAKEUP=%02X\n", rw_data);
137f283dd69STobin C. Harding 			goto set_sleep_mode;
13813a9930dSWolfram Sang 		}
13913a9930dSWolfram Sang 		DPRINTK(4, "wake up : WAKEUP=%02X\n", rw_data);
14013a9930dSWolfram Sang 		atomic_set(&priv->sleepstatus.status, 0);
14113a9930dSWolfram Sang 		priv->last_wakeup = jiffies;
14213a9930dSWolfram Sang 		++priv->wakeup_count;
143cdf6ecc5SWolfram Sang 	} else {
14413a9930dSWolfram Sang 		DPRINTK(1, "sleep_mode=%d\n", priv->sleep_mode);
14513a9930dSWolfram Sang 	}
14613a9930dSWolfram Sang 
147f283dd69STobin C. Harding set_sleep_mode:
14813a9930dSWolfram Sang 	priv->sleep_mode = atomic_read(&priv->sleepstatus.status);
14913a9930dSWolfram Sang }
15013a9930dSWolfram Sang 
151feedcf1aSWolfram Sang void ks_wlan_hw_wakeup_request(struct ks_wlan_private *priv)
15213a9930dSWolfram Sang {
15313a9930dSWolfram Sang 	unsigned char rw_data;
1541770ae9dSTobin C. Harding 	int ret;
15513a9930dSWolfram Sang 
15613a9930dSWolfram Sang 	DPRINTK(4, "\n");
15713a9930dSWolfram Sang 	if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
15813a9930dSWolfram Sang 		rw_data = WAKEUP_REQ;
1591770ae9dSTobin C. Harding 		ret = ks7010_sdio_write(priv, WAKEUP, &rw_data, sizeof(rw_data));
1601770ae9dSTobin C. Harding 		if (ret)
16113a9930dSWolfram Sang 			DPRINTK(1, " error : WAKEUP=%02X\n", rw_data);
16253638cefSsayli karnik 
16313a9930dSWolfram Sang 		DPRINTK(4, "wake up : WAKEUP=%02X\n", rw_data);
16413a9930dSWolfram Sang 		priv->last_wakeup = jiffies;
16513a9930dSWolfram Sang 		++priv->wakeup_count;
166cdf6ecc5SWolfram Sang 	} else {
167cdf6ecc5SWolfram Sang 		DPRINTK(1, "psstatus=%d\n",
168cdf6ecc5SWolfram Sang 			atomic_read(&priv->psstatus.status));
16913a9930dSWolfram Sang 	}
17013a9930dSWolfram Sang }
17113a9930dSWolfram Sang 
1724433459aSSergio Paracuellos static int _ks_wlan_hw_power_save(struct ks_wlan_private *priv)
17313a9930dSWolfram Sang {
17413a9930dSWolfram Sang 	unsigned char rw_data;
175f7172487STobin C. Harding 	int ret;
17613a9930dSWolfram Sang 
17713a9930dSWolfram Sang 	if (priv->reg.powermgt == POWMGT_ACTIVE_MODE)
1787d359a84Ssayli karnik 		return 0;
17913a9930dSWolfram Sang 
180d5f1db31STobin C. Harding 	if (priv->reg.operation_mode != MODE_INFRASTRUCTURE ||
181d5f1db31STobin C. Harding 	    (priv->connect_status & CONNECT_STATUS_MASK) != CONNECT_STATUS)
182d5f1db31STobin C. Harding 		return 0;
183d5f1db31STobin C. Harding 
184d5f1db31STobin C. Harding 	if (priv->dev_state != DEVICE_STATE_SLEEP)
185d5f1db31STobin C. Harding 		return 0;
186d5f1db31STobin C. Harding 
1873188bc09STobin C. Harding 	if (atomic_read(&priv->psstatus.status) == PS_SNOOZE)
1883188bc09STobin C. Harding 		return 0;
1893188bc09STobin C. Harding 
190cdf6ecc5SWolfram Sang 	DPRINTK(5, "\npsstatus.status=%d\npsstatus.confirm_wait=%d\npsstatus.snooze_guard=%d\ncnt_txqbody=%d\n",
19113a9930dSWolfram Sang 		atomic_read(&priv->psstatus.status),
19213a9930dSWolfram Sang 		atomic_read(&priv->psstatus.confirm_wait),
19313a9930dSWolfram Sang 		atomic_read(&priv->psstatus.snooze_guard),
19413a9930dSWolfram Sang 		cnt_txqbody(priv));
19513a9930dSWolfram Sang 
196cf57e659STobin C. Harding 	if (!atomic_read(&priv->psstatus.confirm_wait) &&
197cf57e659STobin C. Harding 	    !atomic_read(&priv->psstatus.snooze_guard) &&
198cf57e659STobin C. Harding 	    !cnt_txqbody(priv)) {
199f7172487STobin C. Harding 		ret = ks7010_sdio_read(priv, INT_PENDING, &rw_data,
200cdf6ecc5SWolfram Sang 				       sizeof(rw_data));
201f7172487STobin C. Harding 		if (ret) {
202dad5980eSTobin C. Harding 			DPRINTK(1, " error : INT_PENDING=%02X\n", rw_data);
203cdf6ecc5SWolfram Sang 			queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
204cdf6ecc5SWolfram Sang 					   &priv->ks_wlan_hw.rw_wq, 1);
2053188bc09STobin C. Harding 			return 0;
20613a9930dSWolfram Sang 		}
20713a9930dSWolfram Sang 		if (!rw_data) {
20813a9930dSWolfram Sang 			rw_data = GCR_B_DOZE;
209f7172487STobin C. Harding 			ret = ks7010_sdio_write(priv, GCR_B, &rw_data,
210cdf6ecc5SWolfram Sang 						sizeof(rw_data));
211f7172487STobin C. Harding 			if (ret) {
212dad5980eSTobin C. Harding 				DPRINTK(1, " error : GCR_B=%02X\n", rw_data);
213dad5980eSTobin C. Harding 				queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
214cdf6ecc5SWolfram Sang 						   &priv->ks_wlan_hw.rw_wq, 1);
2153188bc09STobin C. Harding 				return 0;
21613a9930dSWolfram Sang 			}
217dad5980eSTobin C. Harding 			DPRINTK(4, "PMG SET!! : GCR_B=%02X\n", rw_data);
2189f9d7030STobin C. Harding 			atomic_set(&priv->psstatus.status, PS_SNOOZE);
219dad5980eSTobin C. Harding 			DPRINTK(3, "psstatus.status=PS_SNOOZE\n");
220cdf6ecc5SWolfram Sang 		} else {
221cdf6ecc5SWolfram Sang 			queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
222cdf6ecc5SWolfram Sang 					   &priv->ks_wlan_hw.rw_wq, 1);
22313a9930dSWolfram Sang 		}
224cdf6ecc5SWolfram Sang 	} else {
2259f9d7030STobin C. Harding 		queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
226dad5980eSTobin C. Harding 				   &priv->ks_wlan_hw.rw_wq, 0);
22713a9930dSWolfram Sang 	}
22813a9930dSWolfram Sang 
2297d359a84Ssayli karnik 	return 0;
23013a9930dSWolfram Sang }
23113a9930dSWolfram Sang 
232feedcf1aSWolfram Sang int ks_wlan_hw_power_save(struct ks_wlan_private *priv)
23313a9930dSWolfram Sang {
234cdf6ecc5SWolfram Sang 	queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
235cdf6ecc5SWolfram Sang 			   &priv->ks_wlan_hw.rw_wq, 1);
23613a9930dSWolfram Sang 	return 0;
23713a9930dSWolfram Sang }
23813a9930dSWolfram Sang 
239cdf6ecc5SWolfram Sang static int enqueue_txdev(struct ks_wlan_private *priv, unsigned char *p,
240cdf6ecc5SWolfram Sang 			 unsigned long size,
24113a9930dSWolfram Sang 			 void (*complete_handler)(void *arg1, void *arg2),
24213a9930dSWolfram Sang 			 void *arg1, void *arg2)
24313a9930dSWolfram Sang {
24413a9930dSWolfram Sang 	struct tx_device_buffer *sp;
2451770ae9dSTobin C. Harding 	int ret;
24613a9930dSWolfram Sang 
24713a9930dSWolfram Sang 	if (priv->dev_state < DEVICE_STATE_BOOT) {
2481770ae9dSTobin C. Harding 		ret = -EPERM;
249aa6ca807STobin C. Harding 		goto err_complete;
25013a9930dSWolfram Sang 	}
25113a9930dSWolfram Sang 
25213a9930dSWolfram Sang 	if ((TX_DEVICE_BUFF_SIZE - 1) <= cnt_txqbody(priv)) {
25313a9930dSWolfram Sang 		/* in case of buffer overflow */
25413a9930dSWolfram Sang 		DPRINTK(1, "tx buffer overflow\n");
2551770ae9dSTobin C. Harding 		ret = -EOVERFLOW;
256aa6ca807STobin C. Harding 		goto err_complete;
25713a9930dSWolfram Sang 	}
25813a9930dSWolfram Sang 
25913a9930dSWolfram Sang 	sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qtail];
26013a9930dSWolfram Sang 	sp->sendp = p;
26113a9930dSWolfram Sang 	sp->size = size;
26213a9930dSWolfram Sang 	sp->complete_handler = complete_handler;
26313a9930dSWolfram Sang 	sp->arg1 = arg1;
26413a9930dSWolfram Sang 	sp->arg2 = arg2;
26513a9930dSWolfram Sang 	inc_txqtail(priv);
26613a9930dSWolfram Sang 
26713a9930dSWolfram Sang 	return 0;
268aa6ca807STobin C. Harding 
269aa6ca807STobin C. Harding err_complete:
270aa6ca807STobin C. Harding 	kfree(p);
271aa6ca807STobin C. Harding 	if (complete_handler)
272aa6ca807STobin C. Harding 		(*complete_handler) (arg1, arg2);
273aa6ca807STobin C. Harding 
2741770ae9dSTobin C. Harding 	return ret;
27513a9930dSWolfram Sang }
27613a9930dSWolfram Sang 
27713a9930dSWolfram Sang /* write data */
278cdf6ecc5SWolfram Sang static int write_to_device(struct ks_wlan_private *priv, unsigned char *buffer,
279cdf6ecc5SWolfram Sang 			   unsigned long size)
28013a9930dSWolfram Sang {
28113a9930dSWolfram Sang 	unsigned char rw_data;
28213a9930dSWolfram Sang 	struct hostif_hdr *hdr;
2831770ae9dSTobin C. Harding 	int ret;
284697f9f7fSMuraru Mihaela 
28513a9930dSWolfram Sang 	hdr = (struct hostif_hdr *)buffer;
28613a9930dSWolfram Sang 
28713a9930dSWolfram Sang 	DPRINTK(4, "size=%d\n", hdr->size);
28813a9930dSWolfram Sang 	if (hdr->event < HIF_DATA_REQ || HIF_REQ_MAX < hdr->event) {
28913a9930dSWolfram Sang 		DPRINTK(1, "unknown event=%04X\n", hdr->event);
29013a9930dSWolfram Sang 		return 0;
29113a9930dSWolfram Sang 	}
29213a9930dSWolfram Sang 
2931770ae9dSTobin C. Harding 	ret = ks7010_sdio_write(priv, DATA_WINDOW, buffer, size);
2941770ae9dSTobin C. Harding 	if (ret) {
2951770ae9dSTobin C. Harding 		DPRINTK(1, " write error : retval=%d\n", ret);
2961770ae9dSTobin C. Harding 		return ret;
29713a9930dSWolfram Sang 	}
29813a9930dSWolfram Sang 
29913a9930dSWolfram Sang 	rw_data = WRITE_STATUS_BUSY;
3001770ae9dSTobin C. Harding 	ret = ks7010_sdio_write(priv, WRITE_STATUS, &rw_data, sizeof(rw_data));
3011770ae9dSTobin C. Harding 	if (ret) {
30213a9930dSWolfram Sang 		DPRINTK(1, " error : WRITE_STATUS=%02X\n", rw_data);
3031770ae9dSTobin C. Harding 		return ret;
30413a9930dSWolfram Sang 	}
30513a9930dSWolfram Sang 
30613a9930dSWolfram Sang 	return 0;
30713a9930dSWolfram Sang }
30813a9930dSWolfram Sang 
3095141e9c6STobin C. Harding static void tx_device_task(struct ks_wlan_private *priv)
31013a9930dSWolfram Sang {
31113a9930dSWolfram Sang 	struct tx_device_buffer *sp;
31203b02449STobin C. Harding 	int ret;
31313a9930dSWolfram Sang 
31413a9930dSWolfram Sang 	DPRINTK(4, "\n");
315638a75b6STobin C. Harding 	if (cnt_txqbody(priv) <= 0 ||
316638a75b6STobin C. Harding 	    atomic_read(&priv->psstatus.status) == PS_SNOOZE)
317638a75b6STobin C. Harding 		return;
318638a75b6STobin C. Harding 
31913a9930dSWolfram Sang 	sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead];
32013a9930dSWolfram Sang 	if (priv->dev_state >= DEVICE_STATE_BOOT) {
32103b02449STobin C. Harding 		ret = write_to_device(priv, sp->sendp, sp->size);
32203b02449STobin C. Harding 		if (ret) {
32303b02449STobin C. Harding 			DPRINTK(1, "write_to_device error !!(%d)\n", ret);
32403b02449STobin C. Harding 			queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
325cdf6ecc5SWolfram Sang 					   &priv->ks_wlan_hw.rw_wq, 1);
32613a9930dSWolfram Sang 			return;
32713a9930dSWolfram Sang 		}
32813a9930dSWolfram Sang 	}
329638a75b6STobin C. Harding 	kfree(sp->sendp);
330c7e65f4dSsayli karnik 	if (sp->complete_handler)	/* TX Complete */
33113a9930dSWolfram Sang 		(*sp->complete_handler) (sp->arg1, sp->arg2);
33213a9930dSWolfram Sang 	inc_txqhead(priv);
33313a9930dSWolfram Sang 
33413a9930dSWolfram Sang 	if (cnt_txqbody(priv) > 0) {
335cdf6ecc5SWolfram Sang 		queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
336cdf6ecc5SWolfram Sang 				   &priv->ks_wlan_hw.rw_wq, 0);
33713a9930dSWolfram Sang 	}
33813a9930dSWolfram Sang }
33913a9930dSWolfram Sang 
340feedcf1aSWolfram Sang int ks_wlan_hw_tx(struct ks_wlan_private *priv, void *p, unsigned long size,
34113a9930dSWolfram Sang 		  void (*complete_handler)(void *arg1, void *arg2),
34213a9930dSWolfram Sang 		  void *arg1, void *arg2)
34313a9930dSWolfram Sang {
34413a9930dSWolfram Sang 	int result = 0;
34513a9930dSWolfram Sang 	struct hostif_hdr *hdr;
346697f9f7fSMuraru Mihaela 
34713a9930dSWolfram Sang 	hdr = (struct hostif_hdr *)p;
34813a9930dSWolfram Sang 
34913a9930dSWolfram Sang 	if (hdr->event < HIF_DATA_REQ || HIF_REQ_MAX < hdr->event) {
35013a9930dSWolfram Sang 		DPRINTK(1, "unknown event=%04X\n", hdr->event);
35113a9930dSWolfram Sang 		return 0;
35213a9930dSWolfram Sang 	}
35313a9930dSWolfram Sang 
35413a9930dSWolfram Sang 	/* add event to hostt buffer */
35513a9930dSWolfram Sang 	priv->hostt.buff[priv->hostt.qtail] = hdr->event;
35613a9930dSWolfram Sang 	priv->hostt.qtail = (priv->hostt.qtail + 1) % SME_EVENT_BUFF_SIZE;
35713a9930dSWolfram Sang 
35813a9930dSWolfram Sang 	DPRINTK(4, "event=%04X\n", hdr->event);
35913a9930dSWolfram Sang 	spin_lock(&priv->tx_dev.tx_dev_lock);
36013a9930dSWolfram Sang 	result = enqueue_txdev(priv, p, size, complete_handler, arg1, arg2);
36113a9930dSWolfram Sang 	spin_unlock(&priv->tx_dev.tx_dev_lock);
36213a9930dSWolfram Sang 
36313a9930dSWolfram Sang 	if (cnt_txqbody(priv) > 0) {
364cdf6ecc5SWolfram Sang 		queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
365cdf6ecc5SWolfram Sang 				   &priv->ks_wlan_hw.rw_wq, 0);
36613a9930dSWolfram Sang 	}
36713a9930dSWolfram Sang 	return result;
36813a9930dSWolfram Sang }
36913a9930dSWolfram Sang 
37013a9930dSWolfram Sang static void rx_event_task(unsigned long dev)
37113a9930dSWolfram Sang {
372feedcf1aSWolfram Sang 	struct ks_wlan_private *priv = (struct ks_wlan_private *)dev;
37313a9930dSWolfram Sang 	struct rx_device_buffer *rp;
37413a9930dSWolfram Sang 
37513a9930dSWolfram Sang 	DPRINTK(4, "\n");
37613a9930dSWolfram Sang 
37713a9930dSWolfram Sang 	if (cnt_rxqbody(priv) > 0 && priv->dev_state >= DEVICE_STATE_BOOT) {
37813a9930dSWolfram Sang 		rp = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qhead];
37913a9930dSWolfram Sang 		hostif_receive(priv, rp->data, rp->size);
38013a9930dSWolfram Sang 		inc_rxqhead(priv);
38113a9930dSWolfram Sang 
38253638cefSsayli karnik 		if (cnt_rxqbody(priv) > 0)
38313a9930dSWolfram Sang 			tasklet_schedule(&priv->ks_wlan_hw.rx_bh_task);
38413a9930dSWolfram Sang 	}
38513a9930dSWolfram Sang }
38613a9930dSWolfram Sang 
3875141e9c6STobin C. Harding static void ks_wlan_hw_rx(struct ks_wlan_private *priv, uint16_t size)
38813a9930dSWolfram Sang {
389f7172487STobin C. Harding 	int ret;
39013a9930dSWolfram Sang 	struct rx_device_buffer *rx_buffer;
39113a9930dSWolfram Sang 	struct hostif_hdr *hdr;
39213a9930dSWolfram Sang 	unsigned char read_status;
39313a9930dSWolfram Sang 	unsigned short event = 0;
39413a9930dSWolfram Sang 
39513a9930dSWolfram Sang 	DPRINTK(4, "\n");
39613a9930dSWolfram Sang 
39713a9930dSWolfram Sang 	/* receive data */
39813a9930dSWolfram Sang 	if (cnt_rxqbody(priv) >= (RX_DEVICE_BUFF_SIZE - 1)) {
39913a9930dSWolfram Sang 		/* in case of buffer overflow */
40013a9930dSWolfram Sang 		DPRINTK(1, "rx buffer overflow\n");
40113b05e46STobin C. Harding 		return;
40213a9930dSWolfram Sang 	}
40313a9930dSWolfram Sang 	rx_buffer = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qtail];
40413a9930dSWolfram Sang 
405f7172487STobin C. Harding 	ret = ks7010_sdio_read(priv, DATA_WINDOW, &rx_buffer->data[0],
406cdf6ecc5SWolfram Sang 			       hif_align_size(size));
407f7172487STobin C. Harding 	if (ret)
40813b05e46STobin C. Harding 		return;
40913a9930dSWolfram Sang 
41013a9930dSWolfram Sang 	/* length check */
41113a9930dSWolfram Sang 	if (size > 2046 || size == 0) {
4123215bb1aSWolfram Sang #ifdef KS_WLAN_DEBUG
4133215bb1aSWolfram Sang 		if (KS_WLAN_DEBUG > 5)
414cdf6ecc5SWolfram Sang 			print_hex_dump_bytes("INVALID DATA dump: ",
415cdf6ecc5SWolfram Sang 					     DUMP_PREFIX_OFFSET,
4163215bb1aSWolfram Sang 					     rx_buffer->data, 32);
4173215bb1aSWolfram Sang #endif
41813a9930dSWolfram Sang 		/* rx_status update */
41913a9930dSWolfram Sang 		read_status = READ_STATUS_IDLE;
420f7172487STobin C. Harding 		ret = ks7010_sdio_write(priv, READ_STATUS, &read_status,
421cdf6ecc5SWolfram Sang 					sizeof(read_status));
422f7172487STobin C. Harding 		if (ret)
42313a9930dSWolfram Sang 			DPRINTK(1, " error : READ_STATUS=%02X\n", read_status);
42453638cefSsayli karnik 
42513b05e46STobin C. Harding 		/* length check fail */
42613b05e46STobin C. Harding 		return;
42713a9930dSWolfram Sang 	}
42813a9930dSWolfram Sang 
42913a9930dSWolfram Sang 	hdr = (struct hostif_hdr *)&rx_buffer->data[0];
43013a9930dSWolfram Sang 	rx_buffer->size = le16_to_cpu(hdr->size) + sizeof(hdr->size);
43113a9930dSWolfram Sang 	event = hdr->event;
43213a9930dSWolfram Sang 	inc_rxqtail(priv);
43313a9930dSWolfram Sang 
43413a9930dSWolfram Sang 	/* read status update */
43513a9930dSWolfram Sang 	read_status = READ_STATUS_IDLE;
436f7172487STobin C. Harding 	ret = ks7010_sdio_write(priv, READ_STATUS, &read_status,
437cdf6ecc5SWolfram Sang 				sizeof(read_status));
438f7172487STobin C. Harding 	if (ret)
43913a9930dSWolfram Sang 		DPRINTK(1, " error : READ_STATUS=%02X\n", read_status);
44053638cefSsayli karnik 
44113a9930dSWolfram Sang 	DPRINTK(4, "READ_STATUS=%02X\n", read_status);
44213a9930dSWolfram Sang 
44313a9930dSWolfram Sang 	if (atomic_read(&priv->psstatus.confirm_wait)) {
44413a9930dSWolfram Sang 		if (IS_HIF_CONF(event)) {
44513a9930dSWolfram Sang 			DPRINTK(4, "IS_HIF_CONF true !!\n");
44613a9930dSWolfram Sang 			atomic_dec(&priv->psstatus.confirm_wait);
44713a9930dSWolfram Sang 		}
44813a9930dSWolfram Sang 	}
44913a9930dSWolfram Sang 
45013a9930dSWolfram Sang 	/* rx_event_task((void *)priv); */
45113a9930dSWolfram Sang 	tasklet_schedule(&priv->ks_wlan_hw.rx_bh_task);
45213a9930dSWolfram Sang }
45313a9930dSWolfram Sang 
45413a9930dSWolfram Sang static void ks7010_rw_function(struct work_struct *work)
45513a9930dSWolfram Sang {
45613a9930dSWolfram Sang 	struct hw_info_t *hw;
45713a9930dSWolfram Sang 	struct ks_wlan_private *priv;
45813a9930dSWolfram Sang 	unsigned char rw_data;
4591770ae9dSTobin C. Harding 	int ret;
46013a9930dSWolfram Sang 
46113a9930dSWolfram Sang 	hw = container_of(work, struct hw_info_t, rw_wq.work);
46213a9930dSWolfram Sang 	priv = container_of(hw, struct ks_wlan_private, ks_wlan_hw);
46313a9930dSWolfram Sang 
46413a9930dSWolfram Sang 	DPRINTK(4, "\n");
46513a9930dSWolfram Sang 
46613a9930dSWolfram Sang 	/* wiat after DOZE */
46713a9930dSWolfram Sang 	if (time_after(priv->last_doze + ((30 * HZ) / 1000), jiffies)) {
46813a9930dSWolfram Sang 		DPRINTK(4, "wait after DOZE\n");
469cdf6ecc5SWolfram Sang 		queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
470cdf6ecc5SWolfram Sang 				   &priv->ks_wlan_hw.rw_wq, 1);
47113a9930dSWolfram Sang 		return;
47213a9930dSWolfram Sang 	}
47313a9930dSWolfram Sang 
47413a9930dSWolfram Sang 	/* wiat after WAKEUP */
47513a9930dSWolfram Sang 	while (time_after(priv->last_wakeup + ((30 * HZ) / 1000), jiffies)) {
47613a9930dSWolfram Sang 		DPRINTK(4, "wait after WAKEUP\n");
4779887b5e5SSabitha George 		dev_info(&priv->ks_wlan_hw.sdio_card->func->dev,
4789887b5e5SSabitha George 			 "wake: %lu %lu\n",
4799887b5e5SSabitha George 			 priv->last_wakeup + (30 * HZ) / 1000,
480cdf6ecc5SWolfram Sang 				jiffies);
48113a9930dSWolfram Sang 		msleep(30);
48213a9930dSWolfram Sang 	}
48313a9930dSWolfram Sang 
48413a9930dSWolfram Sang 	sdio_claim_host(priv->ks_wlan_hw.sdio_card->func);
48513a9930dSWolfram Sang 
48613a9930dSWolfram Sang 	/* power save wakeup */
48713a9930dSWolfram Sang 	if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
48813a9930dSWolfram Sang 		if (cnt_txqbody(priv) > 0) {
48913a9930dSWolfram Sang 			ks_wlan_hw_wakeup_request(priv);
490cdf6ecc5SWolfram Sang 			queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
491cdf6ecc5SWolfram Sang 					   &priv->ks_wlan_hw.rw_wq, 1);
49213a9930dSWolfram Sang 		}
493f283dd69STobin C. Harding 		goto err_release_host;
49413a9930dSWolfram Sang 	}
49513a9930dSWolfram Sang 
49613a9930dSWolfram Sang 	/* sleep mode doze */
49713a9930dSWolfram Sang 	if (atomic_read(&priv->sleepstatus.doze_request) == 1) {
49813a9930dSWolfram Sang 		ks_wlan_hw_sleep_doze_request(priv);
499f283dd69STobin C. Harding 		goto err_release_host;
50013a9930dSWolfram Sang 	}
50113a9930dSWolfram Sang 	/* sleep mode wakeup */
50213a9930dSWolfram Sang 	if (atomic_read(&priv->sleepstatus.wakeup_request) == 1) {
50313a9930dSWolfram Sang 		ks_wlan_hw_sleep_wakeup_request(priv);
504f283dd69STobin C. Harding 		goto err_release_host;
50513a9930dSWolfram Sang 	}
50613a9930dSWolfram Sang 
50713a9930dSWolfram Sang 	/* read (WriteStatus/ReadDataSize FN1:00_0014) */
5081770ae9dSTobin C. Harding 	ret = ks7010_sdio_read(priv, WSTATUS_RSIZE, &rw_data, sizeof(rw_data));
5091770ae9dSTobin C. Harding 	if (ret) {
510cdf6ecc5SWolfram Sang 		DPRINTK(1, " error : WSTATUS_RSIZE=%02X psstatus=%d\n", rw_data,
511cdf6ecc5SWolfram Sang 			atomic_read(&priv->psstatus.status));
512f283dd69STobin C. Harding 		goto err_release_host;
51313a9930dSWolfram Sang 	}
51413a9930dSWolfram Sang 	DPRINTK(4, "WSTATUS_RSIZE=%02X\n", rw_data);
51513a9930dSWolfram Sang 
51613a9930dSWolfram Sang 	if (rw_data & RSIZE_MASK) {	/* Read schedule */
5175141e9c6STobin C. Harding 		ks_wlan_hw_rx(priv, (uint16_t)((rw_data & RSIZE_MASK) << 4));
51813a9930dSWolfram Sang 	}
51953638cefSsayli karnik 	if ((rw_data & WSTATUS_MASK))
5205141e9c6STobin C. Harding 		tx_device_task(priv);
52153638cefSsayli karnik 
52213a9930dSWolfram Sang 	_ks_wlan_hw_power_save(priv);
52313a9930dSWolfram Sang 
524f283dd69STobin C. Harding err_release_host:
52513a9930dSWolfram Sang 	sdio_release_host(priv->ks_wlan_hw.sdio_card->func);
52613a9930dSWolfram Sang }
52713a9930dSWolfram Sang 
52813a9930dSWolfram Sang static void ks_sdio_interrupt(struct sdio_func *func)
52913a9930dSWolfram Sang {
530f7172487STobin C. Harding 	int ret;
53113a9930dSWolfram Sang 	struct ks_sdio_card *card;
532feedcf1aSWolfram Sang 	struct ks_wlan_private *priv;
53313a9930dSWolfram Sang 	unsigned char status, rsize, rw_data;
53413a9930dSWolfram Sang 
53513a9930dSWolfram Sang 	card = sdio_get_drvdata(func);
53613a9930dSWolfram Sang 	priv = card->priv;
53713a9930dSWolfram Sang 	DPRINTK(4, "\n");
53813a9930dSWolfram Sang 
539638a75b6STobin C. Harding 	if (priv->dev_state < DEVICE_STATE_BOOT)
540638a75b6STobin C. Harding 		goto queue_delayed_work;
541638a75b6STobin C. Harding 
542638a75b6STobin C. Harding 	ret = ks7010_sdio_read(priv, INT_PENDING, &status, sizeof(status));
543f7172487STobin C. Harding 	if (ret) {
544f7172487STobin C. Harding 		DPRINTK(1, "read INT_PENDING Failed!!(%d)\n", ret);
545f283dd69STobin C. Harding 		goto queue_delayed_work;
54613a9930dSWolfram Sang 	}
54713a9930dSWolfram Sang 	DPRINTK(4, "INT_PENDING=%02X\n", rw_data);
54813a9930dSWolfram Sang 
54913a9930dSWolfram Sang 	/* schedule task for interrupt status */
55013a9930dSWolfram Sang 	/* bit7 -> Write General Communication B register */
55113a9930dSWolfram Sang 	/* read (General Communication B register) */
55213a9930dSWolfram Sang 	/* bit5 -> Write Status Idle */
55313a9930dSWolfram Sang 	/* bit2 -> Read Status Busy  */
554ddd10774SXiangyang Zhang 	if (status & INT_GCR_B ||
555ddd10774SXiangyang Zhang 	    atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
556f7172487STobin C. Harding 		ret = ks7010_sdio_read(priv, GCR_B, &rw_data,
557cdf6ecc5SWolfram Sang 				       sizeof(rw_data));
558f7172487STobin C. Harding 		if (ret) {
55913a9930dSWolfram Sang 			DPRINTK(1, " error : GCR_B=%02X\n", rw_data);
560f283dd69STobin C. Harding 			goto queue_delayed_work;
56113a9930dSWolfram Sang 		}
56213a9930dSWolfram Sang 		if (rw_data == GCR_B_ACTIVE) {
563638a75b6STobin C. Harding 			if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
564638a75b6STobin C. Harding 				atomic_set(&priv->psstatus.status, PS_WAKEUP);
56513a9930dSWolfram Sang 				priv->wakeup_count = 0;
56613a9930dSWolfram Sang 			}
56713a9930dSWolfram Sang 			complete(&priv->psstatus.wakeup_wait);
56813a9930dSWolfram Sang 		}
56913a9930dSWolfram Sang 	}
57013a9930dSWolfram Sang 
57113a9930dSWolfram Sang 	do {
57213a9930dSWolfram Sang 		/* read (WriteStatus/ReadDataSize FN1:00_0014) */
573f7172487STobin C. Harding 		ret = ks7010_sdio_read(priv, WSTATUS_RSIZE, &rw_data,
574cdf6ecc5SWolfram Sang 				       sizeof(rw_data));
575f7172487STobin C. Harding 		if (ret) {
576638a75b6STobin C. Harding 			DPRINTK(1, " error : WSTATUS_RSIZE=%02X\n", rw_data);
577f283dd69STobin C. Harding 			goto queue_delayed_work;
57813a9930dSWolfram Sang 		}
57913a9930dSWolfram Sang 		DPRINTK(4, "WSTATUS_RSIZE=%02X\n", rw_data);
58013a9930dSWolfram Sang 		rsize = rw_data & RSIZE_MASK;
5815141e9c6STobin C. Harding 		if (rsize != 0) 	/* Read schedule */
5825141e9c6STobin C. Harding 			ks_wlan_hw_rx(priv, (uint16_t)(rsize << 4));
5835141e9c6STobin C. Harding 
58413a9930dSWolfram Sang 		if (rw_data & WSTATUS_MASK) {
58513a9930dSWolfram Sang 			if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) {
58613a9930dSWolfram Sang 				if (cnt_txqbody(priv)) {
58713a9930dSWolfram Sang 					ks_wlan_hw_wakeup_request(priv);
588638a75b6STobin C. Harding 					queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
589638a75b6STobin C. Harding 							   &priv->ks_wlan_hw.rw_wq, 1);
59013a9930dSWolfram Sang 					return;
59113a9930dSWolfram Sang 				}
592cdf6ecc5SWolfram Sang 			} else {
5935141e9c6STobin C. Harding 				tx_device_task(priv);
59413a9930dSWolfram Sang 			}
59513a9930dSWolfram Sang 		}
59613a9930dSWolfram Sang 	} while (rsize);
59713a9930dSWolfram Sang 
598f283dd69STobin C. Harding queue_delayed_work:
599cdf6ecc5SWolfram Sang 	queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,
600cdf6ecc5SWolfram Sang 			   &priv->ks_wlan_hw.rw_wq, 0);
60113a9930dSWolfram Sang }
60213a9930dSWolfram Sang 
603feedcf1aSWolfram Sang static int trx_device_init(struct ks_wlan_private *priv)
60413a9930dSWolfram Sang {
60513a9930dSWolfram Sang 	/* initialize values (tx) */
60620358d13SNick Rosbrook 	priv->tx_dev.qhead = 0;
60720358d13SNick Rosbrook 	priv->tx_dev.qtail = 0;
60813a9930dSWolfram Sang 
60913a9930dSWolfram Sang 	/* initialize values (rx) */
61020358d13SNick Rosbrook 	priv->rx_dev.qhead = 0;
61120358d13SNick Rosbrook 	priv->rx_dev.qtail = 0;
61213a9930dSWolfram Sang 
61313a9930dSWolfram Sang 	/* initialize spinLock (tx,rx) */
61413a9930dSWolfram Sang 	spin_lock_init(&priv->tx_dev.tx_dev_lock);
61513a9930dSWolfram Sang 	spin_lock_init(&priv->rx_dev.rx_dev_lock);
61613a9930dSWolfram Sang 
617cdf6ecc5SWolfram Sang 	tasklet_init(&priv->ks_wlan_hw.rx_bh_task, rx_event_task,
618cdf6ecc5SWolfram Sang 		     (unsigned long)priv);
61913a9930dSWolfram Sang 
62013a9930dSWolfram Sang 	return 0;
62113a9930dSWolfram Sang }
62213a9930dSWolfram Sang 
623feedcf1aSWolfram Sang static void trx_device_exit(struct ks_wlan_private *priv)
62413a9930dSWolfram Sang {
62513a9930dSWolfram Sang 	struct tx_device_buffer *sp;
62613a9930dSWolfram Sang 
62713a9930dSWolfram Sang 	/* tx buffer clear */
62813a9930dSWolfram Sang 	while (cnt_txqbody(priv) > 0) {
62913a9930dSWolfram Sang 		sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead];
63013a9930dSWolfram Sang 		kfree(sp->sendp);	/* allocated memory free */
631c7e65f4dSsayli karnik 		if (sp->complete_handler)	/* TX Complete */
63213a9930dSWolfram Sang 			(*sp->complete_handler) (sp->arg1, sp->arg2);
63313a9930dSWolfram Sang 		inc_txqhead(priv);
63413a9930dSWolfram Sang 	}
63513a9930dSWolfram Sang 
63613a9930dSWolfram Sang 	tasklet_kill(&priv->ks_wlan_hw.rx_bh_task);
63713a9930dSWolfram Sang }
638cdf6ecc5SWolfram Sang 
639feedcf1aSWolfram Sang static int ks7010_sdio_update_index(struct ks_wlan_private *priv, u32 index)
64013a9930dSWolfram Sang {
6411770ae9dSTobin C. Harding 	int ret;
64213a9930dSWolfram Sang 	unsigned char *data_buf;
64313a9930dSWolfram Sang 
64413a9930dSWolfram Sang 	data_buf = kmalloc(sizeof(u32), GFP_KERNEL);
645aa6ca807STobin C. Harding 	if (!data_buf)
646aa6ca807STobin C. Harding 		return -ENOMEM;
64713a9930dSWolfram Sang 
64813a9930dSWolfram Sang 	memcpy(data_buf, &index, sizeof(index));
6491770ae9dSTobin C. Harding 	ret = ks7010_sdio_write(priv, WRITE_INDEX, data_buf, sizeof(index));
6501770ae9dSTobin C. Harding 	if (ret)
651f283dd69STobin C. Harding 		goto err_free_data_buf;
65213a9930dSWolfram Sang 
6531770ae9dSTobin C. Harding 	ret = ks7010_sdio_write(priv, READ_INDEX, data_buf, sizeof(index));
6541770ae9dSTobin C. Harding 	if (ret)
655f283dd69STobin C. Harding 		goto err_free_data_buf;
656aa6ca807STobin C. Harding 
657aa6ca807STobin C. Harding 	return 0;
658aa6ca807STobin C. Harding 
659f283dd69STobin C. Harding err_free_data_buf:
660cdf6ecc5SWolfram Sang 	kfree(data_buf);
661aa6ca807STobin C. Harding 
6621770ae9dSTobin C. Harding 	return ret;
66313a9930dSWolfram Sang }
66413a9930dSWolfram Sang 
66513a9930dSWolfram Sang #define ROM_BUFF_SIZE (64 * 1024)
666feedcf1aSWolfram Sang static int ks7010_sdio_data_compare(struct ks_wlan_private *priv, u32 address,
66713a9930dSWolfram Sang 				    unsigned char *data, unsigned int size)
66813a9930dSWolfram Sang {
6691770ae9dSTobin C. Harding 	int ret;
67013a9930dSWolfram Sang 	unsigned char *read_buf;
671eeed92c0SMarkus Elfring 
67213a9930dSWolfram Sang 	read_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL);
673aa6ca807STobin C. Harding 	if (!read_buf)
674aa6ca807STobin C. Harding 		return -ENOMEM;
67513a9930dSWolfram Sang 
6761770ae9dSTobin C. Harding 	ret = ks7010_sdio_read(priv, address, read_buf, size);
6771770ae9dSTobin C. Harding 	if (ret)
678f283dd69STobin C. Harding 		goto err_free_read_buf;
679aa6ca807STobin C. Harding 
68058af272bSTobin C. Harding 	if (memcmp(data, read_buf, size) != 0) {
68158af272bSTobin C. Harding 		ret = -EIO;
6821770ae9dSTobin C. Harding 		DPRINTK(0, "data compare error (%d)\n", ret);
683f283dd69STobin C. Harding 		goto err_free_read_buf;
68413a9930dSWolfram Sang 	}
685aa6ca807STobin C. Harding 
686aa6ca807STobin C. Harding 	return 0;
687aa6ca807STobin C. Harding 
688f283dd69STobin C. Harding err_free_read_buf:
689cdf6ecc5SWolfram Sang 	kfree(read_buf);
690aa6ca807STobin C. Harding 
6911770ae9dSTobin C. Harding 	return ret;
69213a9930dSWolfram Sang }
693cdf6ecc5SWolfram Sang 
694ed246b9eSTobin C. Harding static int ks7010_upload_firmware(struct ks_sdio_card *card)
69513a9930dSWolfram Sang {
696ed246b9eSTobin C. Harding 	struct ks_wlan_private *priv = card->priv;
69713a9930dSWolfram Sang 	unsigned int size, offset, n = 0;
69813a9930dSWolfram Sang 	unsigned char *rom_buf;
69913a9930dSWolfram Sang 	unsigned char rw_data = 0;
7001770ae9dSTobin C. Harding 	int ret;
701881f76b9STobin C. Harding 	unsigned int length;
70213a9930dSWolfram Sang 	const struct firmware *fw_entry = NULL;
70313a9930dSWolfram Sang 
70413a9930dSWolfram Sang 	/* buffer allocate */
70513a9930dSWolfram Sang 	rom_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL);
706369e1b69SSudip Mukherjee 	if (!rom_buf)
707aa6ca807STobin C. Harding 		return -ENOMEM;
70813a9930dSWolfram Sang 
70913a9930dSWolfram Sang 	sdio_claim_host(card->func);
71013a9930dSWolfram Sang 
71113a9930dSWolfram Sang 	/* Firmware running ? */
7121770ae9dSTobin C. Harding 	ret = ks7010_sdio_read(priv, GCR_A, &rw_data, sizeof(rw_data));
71313a9930dSWolfram Sang 	if (rw_data == GCR_A_RUN) {
71413a9930dSWolfram Sang 		DPRINTK(0, "MAC firmware running ...\n");
715aa6ca807STobin C. Harding 		goto release_host_and_free;
71613a9930dSWolfram Sang 	}
71713a9930dSWolfram Sang 
7181770ae9dSTobin C. Harding 	ret = request_firmware(&fw_entry, ROM_FILE,
7191770ae9dSTobin C. Harding 			       &priv->ks_wlan_hw.sdio_card->func->dev);
7201770ae9dSTobin C. Harding 	if (ret)
721aa6ca807STobin C. Harding 		goto release_host_and_free;
7226ee9169bSWolfram Sang 
72313a9930dSWolfram Sang 	length = fw_entry->size;
72413a9930dSWolfram Sang 
72513a9930dSWolfram Sang 	/* Load Program */
72613a9930dSWolfram Sang 	n = 0;
72713a9930dSWolfram Sang 	do {
72813a9930dSWolfram Sang 		if (length >= ROM_BUFF_SIZE) {
72913a9930dSWolfram Sang 			size = ROM_BUFF_SIZE;
73013a9930dSWolfram Sang 			length = length - ROM_BUFF_SIZE;
731cdf6ecc5SWolfram Sang 		} else {
73213a9930dSWolfram Sang 			size = length;
73313a9930dSWolfram Sang 			length = 0;
73413a9930dSWolfram Sang 		}
73513a9930dSWolfram Sang 		DPRINTK(4, "size = %d\n", size);
736cdf6ecc5SWolfram Sang 		if (size == 0)
737cdf6ecc5SWolfram Sang 			break;
73813a9930dSWolfram Sang 		memcpy(rom_buf, fw_entry->data + n, size);
73913a9930dSWolfram Sang 		/* Update write index */
74013a9930dSWolfram Sang 		offset = n;
7411770ae9dSTobin C. Harding 		ret = ks7010_sdio_update_index(priv, KS7010_IRAM_ADDRESS + offset);
7421770ae9dSTobin C. Harding 		if (ret)
743aa6ca807STobin C. Harding 			goto release_firmware;
74413a9930dSWolfram Sang 
74513a9930dSWolfram Sang 		/* Write data */
7461770ae9dSTobin C. Harding 		ret = ks7010_sdio_write(priv, DATA_WINDOW, rom_buf, size);
7471770ae9dSTobin C. Harding 		if (ret)
748aa6ca807STobin C. Harding 			goto release_firmware;
74913a9930dSWolfram Sang 
75013a9930dSWolfram Sang 		/* compare */
7511770ae9dSTobin C. Harding 		ret = ks7010_sdio_data_compare(priv, DATA_WINDOW, rom_buf, size);
7521770ae9dSTobin C. Harding 		if (ret)
753aa6ca807STobin C. Harding 			goto release_firmware;
754aa6ca807STobin C. Harding 
75513a9930dSWolfram Sang 		n += size;
75613a9930dSWolfram Sang 
75713a9930dSWolfram Sang 	} while (size);
75813a9930dSWolfram Sang 
75913a9930dSWolfram Sang 	/* Remap request */
76013a9930dSWolfram Sang 	rw_data = GCR_A_REMAP;
7611770ae9dSTobin C. Harding 	ret = ks7010_sdio_write(priv, GCR_A, &rw_data, sizeof(rw_data));
7621770ae9dSTobin C. Harding 	if (ret)
763aa6ca807STobin C. Harding 		goto release_firmware;
764aa6ca807STobin C. Harding 
76513a9930dSWolfram Sang 	DPRINTK(4, " REMAP Request : GCR_A=%02X\n", rw_data);
76613a9930dSWolfram Sang 
76713a9930dSWolfram Sang 	/* Firmware running check */
76813a9930dSWolfram Sang 	for (n = 0; n < 50; ++n) {
76913a9930dSWolfram Sang 		mdelay(10);	/* wait_ms(10); */
7701770ae9dSTobin C. Harding 		ret = ks7010_sdio_read(priv, GCR_A, &rw_data, sizeof(rw_data));
7711770ae9dSTobin C. Harding 		if (ret)
772aa6ca807STobin C. Harding 			goto release_firmware;
773aa6ca807STobin C. Harding 
774cdf6ecc5SWolfram Sang 		if (rw_data == GCR_A_RUN)
775cdf6ecc5SWolfram Sang 			break;
77613a9930dSWolfram Sang 	}
77713a9930dSWolfram Sang 	DPRINTK(4, "firmware wakeup (%d)!!!!\n", n);
77813a9930dSWolfram Sang 	if ((50) <= n) {
77913a9930dSWolfram Sang 		DPRINTK(1, "firmware can't start\n");
7801770ae9dSTobin C. Harding 		ret = -EIO;
781aa6ca807STobin C. Harding 		goto release_firmware;
78213a9930dSWolfram Sang 	}
78313a9930dSWolfram Sang 
7841770ae9dSTobin C. Harding 	ret = 0;
78513a9930dSWolfram Sang 
786aa6ca807STobin C. Harding  release_firmware:
78713a9930dSWolfram Sang 	release_firmware(fw_entry);
788aa6ca807STobin C. Harding  release_host_and_free:
78913a9930dSWolfram Sang 	sdio_release_host(card->func);
79013a9930dSWolfram Sang 	kfree(rom_buf);
791aa6ca807STobin C. Harding 
7921770ae9dSTobin C. Harding 	return ret;
79313a9930dSWolfram Sang }
79413a9930dSWolfram Sang 
795e8593a8aSWolfram Sang static void ks7010_card_init(struct ks_wlan_private *priv)
79613a9930dSWolfram Sang {
79713a9930dSWolfram Sang 	DPRINTK(5, "\ncard_init_task()\n");
79813a9930dSWolfram Sang 
79913a9930dSWolfram Sang 	/* init_waitqueue_head(&priv->confirm_wait); */
80013a9930dSWolfram Sang 	init_completion(&priv->confirm_wait);
80113a9930dSWolfram Sang 
80213a9930dSWolfram Sang 	DPRINTK(5, "init_completion()\n");
80313a9930dSWolfram Sang 
80413a9930dSWolfram Sang 	/* get mac address & firmware version */
80513a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_START);
80613a9930dSWolfram Sang 
80713a9930dSWolfram Sang 	DPRINTK(5, "hostif_sme_enqueu()\n");
80813a9930dSWolfram Sang 
809cdf6ecc5SWolfram Sang 	if (!wait_for_completion_interruptible_timeout
810cdf6ecc5SWolfram Sang 	    (&priv->confirm_wait, 5 * HZ)) {
81113a9930dSWolfram Sang 		DPRINTK(1, "wait time out!! SME_START\n");
81213a9930dSWolfram Sang 	}
81313a9930dSWolfram Sang 
814310e916fSTobin C. Harding 	if (priv->mac_address_valid && priv->version_size != 0)
81513a9930dSWolfram Sang 		priv->dev_state = DEVICE_STATE_PREINIT;
81653638cefSsayli karnik 
81713a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_GET_EEPROM_CKSUM);
81813a9930dSWolfram Sang 
81913a9930dSWolfram Sang 	/* load initial wireless parameter */
82013a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_STOP_REQUEST);
82113a9930dSWolfram Sang 
82213a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_RTS_THRESHOLD_REQUEST);
82313a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_FRAGMENTATION_THRESHOLD_REQUEST);
82413a9930dSWolfram Sang 
82513a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_WEP_INDEX_REQUEST);
82613a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_WEP_KEY1_REQUEST);
82713a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_WEP_KEY2_REQUEST);
82813a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_WEP_KEY3_REQUEST);
82913a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_WEP_KEY4_REQUEST);
83013a9930dSWolfram Sang 
83113a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_WEP_FLAG_REQUEST);
83213a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_RSN_ENABLED_REQUEST);
83313a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_MODE_SET_REQUEST);
83413a9930dSWolfram Sang 	hostif_sme_enqueue(priv, SME_START_REQUEST);
83513a9930dSWolfram Sang 
836cdf6ecc5SWolfram Sang 	if (!wait_for_completion_interruptible_timeout
837cdf6ecc5SWolfram Sang 	    (&priv->confirm_wait, 5 * HZ)) {
83813a9930dSWolfram Sang 		DPRINTK(1, "wait time out!! wireless parameter set\n");
83913a9930dSWolfram Sang 	}
84013a9930dSWolfram Sang 
84113a9930dSWolfram Sang 	if (priv->dev_state >= DEVICE_STATE_PREINIT) {
84213a9930dSWolfram Sang 		DPRINTK(1, "DEVICE READY!!\n");
84313a9930dSWolfram Sang 		priv->dev_state = DEVICE_STATE_READY;
844cdf6ecc5SWolfram Sang 	} else {
84513a9930dSWolfram Sang 		DPRINTK(1, "dev_state=%d\n", priv->dev_state);
84613a9930dSWolfram Sang 	}
84713a9930dSWolfram Sang }
84813a9930dSWolfram Sang 
8496ee9169bSWolfram Sang static void ks7010_init_defaults(struct ks_wlan_private *priv)
8506ee9169bSWolfram Sang {
8516ee9169bSWolfram Sang 	priv->reg.tx_rate = TX_RATE_AUTO;
8526ee9169bSWolfram Sang 	priv->reg.preamble = LONG_PREAMBLE;
8536ee9169bSWolfram Sang 	priv->reg.powermgt = POWMGT_ACTIVE_MODE;
8546ee9169bSWolfram Sang 	priv->reg.scan_type = ACTIVE_SCAN;
8556ee9169bSWolfram Sang 	priv->reg.beacon_lost_count = 20;
8566ee9169bSWolfram Sang 	priv->reg.rts = 2347UL;
8576ee9169bSWolfram Sang 	priv->reg.fragment = 2346UL;
8586ee9169bSWolfram Sang 	priv->reg.phy_type = D_11BG_COMPATIBLE_MODE;
8596ee9169bSWolfram Sang 	priv->reg.cts_mode = CTS_MODE_FALSE;
8606ee9169bSWolfram Sang 	priv->reg.rate_set.body[11] = TX_RATE_54M;
8616ee9169bSWolfram Sang 	priv->reg.rate_set.body[10] = TX_RATE_48M;
8626ee9169bSWolfram Sang 	priv->reg.rate_set.body[9] = TX_RATE_36M;
8636ee9169bSWolfram Sang 	priv->reg.rate_set.body[8] = TX_RATE_18M;
8646ee9169bSWolfram Sang 	priv->reg.rate_set.body[7] = TX_RATE_9M;
8656ee9169bSWolfram Sang 	priv->reg.rate_set.body[6] = TX_RATE_24M | BASIC_RATE;
8666ee9169bSWolfram Sang 	priv->reg.rate_set.body[5] = TX_RATE_12M | BASIC_RATE;
8676ee9169bSWolfram Sang 	priv->reg.rate_set.body[4] = TX_RATE_6M | BASIC_RATE;
8686ee9169bSWolfram Sang 	priv->reg.rate_set.body[3] = TX_RATE_11M | BASIC_RATE;
8696ee9169bSWolfram Sang 	priv->reg.rate_set.body[2] = TX_RATE_5M | BASIC_RATE;
8706ee9169bSWolfram Sang 	priv->reg.rate_set.body[1] = TX_RATE_2M | BASIC_RATE;
8716ee9169bSWolfram Sang 	priv->reg.rate_set.body[0] = TX_RATE_1M | BASIC_RATE;
8726ee9169bSWolfram Sang 	priv->reg.tx_rate = TX_RATE_FULL_AUTO;
8736ee9169bSWolfram Sang 	priv->reg.rate_set.size = 12;
8746ee9169bSWolfram Sang }
8756ee9169bSWolfram Sang 
876c4730a92SWolfram Sang static int ks7010_sdio_probe(struct sdio_func *func,
877cdf6ecc5SWolfram Sang 			     const struct sdio_device_id *device)
87813a9930dSWolfram Sang {
879feedcf1aSWolfram Sang 	struct ks_wlan_private *priv;
88013a9930dSWolfram Sang 	struct ks_sdio_card *card;
88113a9930dSWolfram Sang 	struct net_device *netdev;
88213a9930dSWolfram Sang 	unsigned char rw_data;
8832801d7a2SWolfram Sang 	int ret;
88413a9930dSWolfram Sang 
885c4730a92SWolfram Sang 	DPRINTK(5, "ks7010_sdio_probe()\n");
88613a9930dSWolfram Sang 
88713a9930dSWolfram Sang 	priv = NULL;
88813a9930dSWolfram Sang 	netdev = NULL;
88913a9930dSWolfram Sang 
890f3f2d351SNick Rosbrook 	/* initialize ks_sdio_card */
8912d738bd2SSandhya Bankar 	card = kzalloc(sizeof(*card), GFP_KERNEL);
89213a9930dSWolfram Sang 	if (!card)
89313a9930dSWolfram Sang 		return -ENOMEM;
89413a9930dSWolfram Sang 
89513a9930dSWolfram Sang 	card->func = func;
89613a9930dSWolfram Sang 	spin_lock_init(&card->lock);
89713a9930dSWolfram Sang 
89813a9930dSWolfram Sang 	/*** Initialize  SDIO ***/
89913a9930dSWolfram Sang 	sdio_claim_host(func);
90013a9930dSWolfram Sang 
90113a9930dSWolfram Sang 	/* bus setting  */
90213a9930dSWolfram Sang 	/* Issue config request to override clock rate */
90313a9930dSWolfram Sang 
90413a9930dSWolfram Sang 	/* function blocksize set */
90513a9930dSWolfram Sang 	ret = sdio_set_block_size(func, KS7010_IO_BLOCK_SIZE);
906cdf6ecc5SWolfram Sang 	DPRINTK(5, "multi_block=%d sdio_set_block_size()=%d %d\n",
907cdf6ecc5SWolfram Sang 		func->card->cccr.multi_block, func->cur_blksize, ret);
90813a9930dSWolfram Sang 
90913a9930dSWolfram Sang 	/* Allocate the slot current */
91013a9930dSWolfram Sang 
91113a9930dSWolfram Sang 	/* function enable */
91213a9930dSWolfram Sang 	ret = sdio_enable_func(func);
91313a9930dSWolfram Sang 	DPRINTK(5, "sdio_enable_func() %d\n", ret);
91413a9930dSWolfram Sang 	if (ret)
915f283dd69STobin C. Harding 		goto err_free_card;
91613a9930dSWolfram Sang 
91713a9930dSWolfram Sang 	/* interrupt disable */
91813a9930dSWolfram Sang 	sdio_writeb(func, 0, INT_ENABLE, &ret);
91913a9930dSWolfram Sang 	if (ret)
920f283dd69STobin C. Harding 		goto err_free_card;
92113a9930dSWolfram Sang 	sdio_writeb(func, 0xff, INT_PENDING, &ret);
92213a9930dSWolfram Sang 	if (ret)
923f283dd69STobin C. Harding 		goto err_disable_func;
92413a9930dSWolfram Sang 
92513a9930dSWolfram Sang 	/* setup interrupt handler */
92613a9930dSWolfram Sang 	ret = sdio_claim_irq(func, ks_sdio_interrupt);
92713a9930dSWolfram Sang 	if (ret)
928f283dd69STobin C. Harding 		goto err_disable_func;
92913a9930dSWolfram Sang 
93013a9930dSWolfram Sang 	sdio_release_host(func);
93113a9930dSWolfram Sang 
93213a9930dSWolfram Sang 	sdio_set_drvdata(func, card);
93313a9930dSWolfram Sang 
93413a9930dSWolfram Sang 	DPRINTK(5, "class = 0x%X, vendor = 0x%X, "
935cdf6ecc5SWolfram Sang 		"device = 0x%X\n", func->class, func->vendor, func->device);
93613a9930dSWolfram Sang 
93713a9930dSWolfram Sang 	/* private memory allocate */
93813a9930dSWolfram Sang 	netdev = alloc_etherdev(sizeof(*priv));
939c7e65f4dSsayli karnik 	if (!netdev) {
9409887b5e5SSabitha George 		dev_err(&card->func->dev, "ks7010 : Unable to alloc new net device\n");
941f283dd69STobin C. Harding 		goto err_release_irq;
94213a9930dSWolfram Sang 	}
9436634cff1SWolfram Sang 	if (dev_alloc_name(netdev, "wlan%d") < 0) {
9449887b5e5SSabitha George 		dev_err(&card->func->dev,
9459887b5e5SSabitha George 			"ks7010 :  Couldn't get name!\n");
946f283dd69STobin C. Harding 		goto err_free_netdev;
94713a9930dSWolfram Sang 	}
94813a9930dSWolfram Sang 
94913a9930dSWolfram Sang 	priv = netdev_priv(netdev);
95013a9930dSWolfram Sang 
95113a9930dSWolfram Sang 	card->priv = priv;
95213a9930dSWolfram Sang 	SET_NETDEV_DEV(netdev, &card->func->dev);	/* for create sysfs symlinks */
95313a9930dSWolfram Sang 
95413a9930dSWolfram Sang 	/* private memory initialize */
95513a9930dSWolfram Sang 	priv->ks_wlan_hw.sdio_card = card;
95613a9930dSWolfram Sang 	init_completion(&priv->ks_wlan_hw.ks7010_sdio_wait);
95713a9930dSWolfram Sang 	priv->ks_wlan_hw.read_buf = NULL;
95813a9930dSWolfram Sang 	priv->ks_wlan_hw.read_buf = kmalloc(RX_DATA_SIZE, GFP_KERNEL);
95953638cefSsayli karnik 	if (!priv->ks_wlan_hw.read_buf)
960f283dd69STobin C. Harding 		goto err_free_netdev;
96153638cefSsayli karnik 
96213a9930dSWolfram Sang 	priv->dev_state = DEVICE_STATE_PREBOOT;
96313a9930dSWolfram Sang 	priv->net_dev = netdev;
96413a9930dSWolfram Sang 	priv->firmware_version[0] = '\0';
96513a9930dSWolfram Sang 	priv->version_size = 0;
96613a9930dSWolfram Sang 	priv->last_doze = jiffies;	/* set current jiffies */
96713a9930dSWolfram Sang 	priv->last_wakeup = jiffies;
96813a9930dSWolfram Sang 	memset(&priv->nstats, 0, sizeof(priv->nstats));
96913a9930dSWolfram Sang 	memset(&priv->wstats, 0, sizeof(priv->wstats));
97013a9930dSWolfram Sang 
97113a9930dSWolfram Sang 	/* sleep mode */
97213a9930dSWolfram Sang 	atomic_set(&priv->sleepstatus.doze_request, 0);
97313a9930dSWolfram Sang 	atomic_set(&priv->sleepstatus.wakeup_request, 0);
97413a9930dSWolfram Sang 	atomic_set(&priv->sleepstatus.wakeup_request, 0);
97513a9930dSWolfram Sang 
97613a9930dSWolfram Sang 	trx_device_init(priv);
97713a9930dSWolfram Sang 	hostif_init(priv);
97813a9930dSWolfram Sang 	ks_wlan_net_start(netdev);
97913a9930dSWolfram Sang 
9806ee9169bSWolfram Sang 	ks7010_init_defaults(priv);
98113a9930dSWolfram Sang 
982ed246b9eSTobin C. Harding 	ret = ks7010_upload_firmware(card);
98313a9930dSWolfram Sang 	if (ret) {
9849887b5e5SSabitha George 		dev_err(&card->func->dev,
9859887b5e5SSabitha George 			"ks7010: firmware load failed !! return code = %d\n",
986cdf6ecc5SWolfram Sang 			 ret);
987f283dd69STobin C. Harding 		goto err_free_read_buf;
98813a9930dSWolfram Sang 	}
98913a9930dSWolfram Sang 
99013a9930dSWolfram Sang 	/* interrupt setting */
99113a9930dSWolfram Sang 	/* clear Interrupt status write (ARMtoSD_InterruptPending FN1:00_0024) */
99213a9930dSWolfram Sang 	rw_data = 0xff;
99313a9930dSWolfram Sang 	sdio_claim_host(func);
99413a9930dSWolfram Sang 	ret = ks7010_sdio_write(priv, INT_PENDING, &rw_data, sizeof(rw_data));
99513a9930dSWolfram Sang 	sdio_release_host(func);
99653638cefSsayli karnik 	if (ret)
99713a9930dSWolfram Sang 		DPRINTK(1, " error : INT_PENDING=%02X\n", rw_data);
99853638cefSsayli karnik 
99913a9930dSWolfram Sang 	DPRINTK(4, " clear Interrupt : INT_PENDING=%02X\n", rw_data);
100013a9930dSWolfram Sang 
100113a9930dSWolfram Sang 	/* enable ks7010sdio interrupt (INT_GCR_B|INT_READ_STATUS|INT_WRITE_STATUS) */
100213a9930dSWolfram Sang 	rw_data = (INT_GCR_B | INT_READ_STATUS | INT_WRITE_STATUS);
100313a9930dSWolfram Sang 	sdio_claim_host(func);
100413a9930dSWolfram Sang 	ret = ks7010_sdio_write(priv, INT_ENABLE, &rw_data, sizeof(rw_data));
100513a9930dSWolfram Sang 	sdio_release_host(func);
100653638cefSsayli karnik 	if (ret)
1007f283dd69STobin C. Harding 		DPRINTK(1, " err : INT_ENABLE=%02X\n", rw_data);
100853638cefSsayli karnik 
100913a9930dSWolfram Sang 	DPRINTK(4, " enable Interrupt : INT_ENABLE=%02X\n", rw_data);
101013a9930dSWolfram Sang 	priv->dev_state = DEVICE_STATE_BOOT;
101113a9930dSWolfram Sang 
101213a9930dSWolfram Sang 	priv->ks_wlan_hw.ks7010sdio_wq = create_workqueue("ks7010sdio_wq");
101313a9930dSWolfram Sang 	if (!priv->ks_wlan_hw.ks7010sdio_wq) {
101413a9930dSWolfram Sang 		DPRINTK(1, "create_workqueue failed !!\n");
1015f283dd69STobin C. Harding 		goto err_free_read_buf;
101613a9930dSWolfram Sang 	}
101713a9930dSWolfram Sang 
101813a9930dSWolfram Sang 	INIT_DELAYED_WORK(&priv->ks_wlan_hw.rw_wq, ks7010_rw_function);
1019e8593a8aSWolfram Sang 	ks7010_card_init(priv);
102013a9930dSWolfram Sang 
10213fb54d75SWolfram Sang 	ret = register_netdev(priv->net_dev);
10223fb54d75SWolfram Sang 	if (ret)
1023f283dd69STobin C. Harding 		goto err_free_read_buf;
10243fb54d75SWolfram Sang 
102513a9930dSWolfram Sang 	return 0;
102613a9930dSWolfram Sang 
1027f283dd69STobin C. Harding  err_free_read_buf:
102813a9930dSWolfram Sang 	kfree(priv->ks_wlan_hw.read_buf);
102913a9930dSWolfram Sang 	priv->ks_wlan_hw.read_buf = NULL;
1030f283dd69STobin C. Harding  err_free_netdev:
103113a9930dSWolfram Sang 	free_netdev(priv->net_dev);
103213a9930dSWolfram Sang 	card->priv = NULL;
1033f283dd69STobin C. Harding  err_release_irq:
103413a9930dSWolfram Sang 	sdio_claim_host(func);
103513a9930dSWolfram Sang 	sdio_release_irq(func);
1036f283dd69STobin C. Harding  err_disable_func:
103713a9930dSWolfram Sang 	sdio_disable_func(func);
1038f283dd69STobin C. Harding  err_free_card:
103913a9930dSWolfram Sang 	sdio_release_host(func);
104013a9930dSWolfram Sang 	sdio_set_drvdata(func, NULL);
104113a9930dSWolfram Sang 	kfree(card);
10422801d7a2SWolfram Sang 
104313a9930dSWolfram Sang 	return -ENODEV;
104413a9930dSWolfram Sang }
104513a9930dSWolfram Sang 
1046c4730a92SWolfram Sang static void ks7010_sdio_remove(struct sdio_func *func)
104713a9930dSWolfram Sang {
104813a9930dSWolfram Sang 	int ret;
104913a9930dSWolfram Sang 	struct ks_sdio_card *card;
105013a9930dSWolfram Sang 	struct ks_wlan_private *priv;
1051697f9f7fSMuraru Mihaela 
1052c4730a92SWolfram Sang 	DPRINTK(1, "ks7010_sdio_remove()\n");
105313a9930dSWolfram Sang 
105413a9930dSWolfram Sang 	card = sdio_get_drvdata(func);
105513a9930dSWolfram Sang 
1056c7e65f4dSsayli karnik 	if (!card)
105713a9930dSWolfram Sang 		return;
105813a9930dSWolfram Sang 
105913a9930dSWolfram Sang 	DPRINTK(1, "priv = card->priv\n");
106013a9930dSWolfram Sang 	priv = card->priv;
106113a9930dSWolfram Sang 	if (priv) {
1062803394d0SColin Ian King 		struct net_device *netdev = priv->net_dev;
1063803394d0SColin Ian King 
106413a9930dSWolfram Sang 		ks_wlan_net_stop(netdev);
106513a9930dSWolfram Sang 		DPRINTK(1, "ks_wlan_net_stop\n");
106613a9930dSWolfram Sang 
106713a9930dSWolfram Sang 		/* interrupt disable */
106813a9930dSWolfram Sang 		sdio_claim_host(func);
106913a9930dSWolfram Sang 		sdio_writeb(func, 0, INT_ENABLE, &ret);
107013a9930dSWolfram Sang 		sdio_writeb(func, 0xff, INT_PENDING, &ret);
107113a9930dSWolfram Sang 		sdio_release_host(func);
107213a9930dSWolfram Sang 		DPRINTK(1, "interrupt disable\n");
107313a9930dSWolfram Sang 
107413a9930dSWolfram Sang 		/* send stop request to MAC */
107513a9930dSWolfram Sang 		{
107613a9930dSWolfram Sang 			struct hostif_stop_request_t *pp;
1077697f9f7fSMuraru Mihaela 
1078cbb351ccSSandhya Bankar 			pp = kzalloc(hif_align_size(sizeof(*pp)), GFP_KERNEL);
1079c7e65f4dSsayli karnik 			if (!pp) {
108013a9930dSWolfram Sang 				DPRINTK(3, "allocate memory failed..\n");
108113a9930dSWolfram Sang 				return;	/* to do goto ni suru */
108213a9930dSWolfram Sang 			}
1083cdf6ecc5SWolfram Sang 			pp->header.size =
1084cdf6ecc5SWolfram Sang 			    cpu_to_le16((uint16_t)
1085cdf6ecc5SWolfram Sang 					(sizeof(*pp) -
1086cdf6ecc5SWolfram Sang 					 sizeof(pp->header.size)));
108713a9930dSWolfram Sang 			pp->header.event = cpu_to_le16((uint16_t)HIF_STOP_REQ);
108813a9930dSWolfram Sang 
108913a9930dSWolfram Sang 			sdio_claim_host(func);
1090cdf6ecc5SWolfram Sang 			write_to_device(priv, (unsigned char *)pp,
1091cdf6ecc5SWolfram Sang 					hif_align_size(sizeof(*pp)));
109213a9930dSWolfram Sang 			sdio_release_host(func);
109313a9930dSWolfram Sang 			kfree(pp);
109413a9930dSWolfram Sang 		}
109513a9930dSWolfram Sang 		DPRINTK(1, "STOP Req\n");
109613a9930dSWolfram Sang 
109713a9930dSWolfram Sang 		if (priv->ks_wlan_hw.ks7010sdio_wq) {
109813a9930dSWolfram Sang 			flush_workqueue(priv->ks_wlan_hw.ks7010sdio_wq);
109913a9930dSWolfram Sang 			destroy_workqueue(priv->ks_wlan_hw.ks7010sdio_wq);
110013a9930dSWolfram Sang 		}
1101cdf6ecc5SWolfram Sang 		DPRINTK(1,
1102cdf6ecc5SWolfram Sang 			"destroy_workqueue(priv->ks_wlan_hw.ks7010sdio_wq);\n");
110313a9930dSWolfram Sang 
110413a9930dSWolfram Sang 		hostif_exit(priv);
110513a9930dSWolfram Sang 		DPRINTK(1, "hostif_exit\n");
110613a9930dSWolfram Sang 
110713a9930dSWolfram Sang 		unregister_netdev(netdev);
110813a9930dSWolfram Sang 
110913a9930dSWolfram Sang 		trx_device_exit(priv);
111013a9930dSWolfram Sang 		kfree(priv->ks_wlan_hw.read_buf);
111113a9930dSWolfram Sang 		free_netdev(priv->net_dev);
111213a9930dSWolfram Sang 		card->priv = NULL;
111313a9930dSWolfram Sang 	}
111413a9930dSWolfram Sang 
111513a9930dSWolfram Sang 	sdio_claim_host(func);
111613a9930dSWolfram Sang 	sdio_release_irq(func);
111713a9930dSWolfram Sang 	DPRINTK(1, "sdio_release_irq()\n");
111813a9930dSWolfram Sang 	sdio_disable_func(func);
111913a9930dSWolfram Sang 	DPRINTK(1, "sdio_disable_func()\n");
112013a9930dSWolfram Sang 	sdio_release_host(func);
112113a9930dSWolfram Sang 
112213a9930dSWolfram Sang 	sdio_set_drvdata(func, NULL);
112313a9930dSWolfram Sang 
112413a9930dSWolfram Sang 	kfree(card);
112513a9930dSWolfram Sang 	DPRINTK(1, "kfree()\n");
112613a9930dSWolfram Sang 
112713a9930dSWolfram Sang 	DPRINTK(5, " Bye !!\n");
112813a9930dSWolfram Sang }
112913a9930dSWolfram Sang 
11304c0d46d2SWolfram Sang static struct sdio_driver ks7010_sdio_driver = {
11314c0d46d2SWolfram Sang 	.name = "ks7010_sdio",
11324c0d46d2SWolfram Sang 	.id_table = ks7010_sdio_ids,
11334c0d46d2SWolfram Sang 	.probe = ks7010_sdio_probe,
11344c0d46d2SWolfram Sang 	.remove = ks7010_sdio_remove,
11354c0d46d2SWolfram Sang };
11364c0d46d2SWolfram Sang 
11376b0cb0b0SWolfram Sang module_driver(ks7010_sdio_driver, sdio_register_driver, sdio_unregister_driver);
1138e1240140SWolfram Sang MODULE_AUTHOR("Sang Engineering, Qi-Hardware, KeyStream");
1139e1240140SWolfram Sang MODULE_DESCRIPTION("Driver for KeyStream KS7010 based SDIO cards");
1140e1240140SWolfram Sang MODULE_LICENSE("GPL v2");
1141e1240140SWolfram Sang MODULE_FIRMWARE(ROM_FILE);
1142