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