]>
Commit | Line | Data |
---|---|---|
13a9930d WS |
1 | /* |
2 | * Driver for KeyStream, KS7010 based SDIO cards. | |
3 | * | |
13a9930d WS |
4 | * Copyright (C) 2006-2008 KeyStream Corp. |
5 | * Copyright (C) 2009 Renesas Technology Corp. | |
c5d9a030 | 6 | * Copyright (C) 2016 Sang Engineering, Wolfram Sang |
13a9930d WS |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify | |
c5d9a030 WS |
9 | * it under the terms of the GNU General Public License version 2 as |
10 | * published by the Free Software Foundation. | |
13a9930d WS |
11 | */ |
12 | ||
1c013a5c | 13 | #include <linux/firmware.h> |
13a9930d WS |
14 | #include <linux/mmc/card.h> |
15 | #include <linux/mmc/sdio_func.h> | |
1c013a5c | 16 | #include <linux/workqueue.h> |
041c4d75 | 17 | #include <linux/atomic.h> |
13a9930d WS |
18 | |
19 | #include "ks_wlan.h" | |
20 | #include "ks_wlan_ioctl.h" | |
13a9930d | 21 | #include "ks_hostif.h" |
13a9930d WS |
22 | #include "ks7010_sdio.h" |
23 | ||
24 | #define KS7010_FUNC_NUM 1 | |
25 | #define KS7010_IO_BLOCK_SIZE 512 | |
26 | #define KS7010_MAX_CLOCK 25000000 | |
27 | ||
f9b5bd05 | 28 | static const struct sdio_device_id ks7010_sdio_ids[] = { |
cdf6ecc5 WS |
29 | {SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_A, SDIO_DEVICE_ID_KS_7010)}, |
30 | {SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_B, SDIO_DEVICE_ID_KS_7010)}, | |
13a9930d WS |
31 | { /* all zero */ } |
32 | }; | |
f9b5bd05 | 33 | MODULE_DEVICE_TABLE(sdio, ks7010_sdio_ids); |
13a9930d | 34 | |
13a9930d | 35 | #define inc_txqhead(priv) \ |
4fdaa0d7 | 36 | (priv->tx_dev.qhead = (priv->tx_dev.qhead + 1) % TX_DEVICE_BUFF_SIZE) |
13a9930d | 37 | #define inc_txqtail(priv) \ |
4fdaa0d7 | 38 | (priv->tx_dev.qtail = (priv->tx_dev.qtail + 1) % TX_DEVICE_BUFF_SIZE) |
13a9930d | 39 | #define cnt_txqbody(priv) \ |
4fdaa0d7 | 40 | (((priv->tx_dev.qtail + TX_DEVICE_BUFF_SIZE) - (priv->tx_dev.qhead)) % TX_DEVICE_BUFF_SIZE) |
13a9930d WS |
41 | |
42 | #define inc_rxqhead(priv) \ | |
4fdaa0d7 | 43 | (priv->rx_dev.qhead = (priv->rx_dev.qhead + 1) % RX_DEVICE_BUFF_SIZE) |
13a9930d | 44 | #define inc_rxqtail(priv) \ |
4fdaa0d7 | 45 | (priv->rx_dev.qtail = (priv->rx_dev.qtail + 1) % RX_DEVICE_BUFF_SIZE) |
13a9930d | 46 | #define cnt_rxqbody(priv) \ |
4fdaa0d7 | 47 | (((priv->rx_dev.qtail + RX_DEVICE_BUFF_SIZE) - (priv->rx_dev.qhead)) % RX_DEVICE_BUFF_SIZE) |
13a9930d | 48 | |
4c0d46d2 WS |
49 | static int ks7010_sdio_read(struct ks_wlan_private *priv, unsigned int address, |
50 | unsigned char *buffer, int length) | |
51 | { | |
52 | struct ks_sdio_card *card; | |
1770ae9d | 53 | int ret; |
4c0d46d2 | 54 | |
18bd6dd1 | 55 | card = priv->ks_sdio_card; |
4c0d46d2 WS |
56 | |
57 | if (length == 1) /* CMD52 */ | |
1770ae9d | 58 | *buffer = sdio_readb(card->func, address, &ret); |
4c0d46d2 | 59 | else /* CMD53 multi-block transfer */ |
1770ae9d | 60 | ret = sdio_memcpy_fromio(card->func, buffer, address, length); |
4c0d46d2 | 61 | |
1770ae9d TH |
62 | if (ret) { |
63 | DPRINTK(1, "sdio error=%d size=%d\n", ret, length); | |
64 | return ret; | |
65 | } | |
4c0d46d2 | 66 | |
1770ae9d | 67 | return 0; |
4c0d46d2 WS |
68 | } |
69 | ||
70 | static int ks7010_sdio_write(struct ks_wlan_private *priv, unsigned int address, | |
71 | unsigned char *buffer, int length) | |
72 | { | |
73 | struct ks_sdio_card *card; | |
1770ae9d | 74 | int ret; |
4c0d46d2 | 75 | |
18bd6dd1 | 76 | card = priv->ks_sdio_card; |
4c0d46d2 WS |
77 | |
78 | if (length == 1) /* CMD52 */ | |
1770ae9d | 79 | sdio_writeb(card->func, *buffer, address, &ret); |
4c0d46d2 | 80 | else /* CMD53 */ |
1770ae9d | 81 | ret = sdio_memcpy_toio(card->func, address, buffer, length); |
4c0d46d2 | 82 | |
1770ae9d TH |
83 | if (ret) { |
84 | DPRINTK(1, "sdio error=%d size=%d\n", ret, length); | |
85 | return ret; | |
86 | } | |
4c0d46d2 | 87 | |
1770ae9d | 88 | return 0; |
4c0d46d2 WS |
89 | } |
90 | ||
4433459a | 91 | static void ks_wlan_hw_sleep_doze_request(struct ks_wlan_private *priv) |
13a9930d WS |
92 | { |
93 | unsigned char rw_data; | |
1770ae9d | 94 | int ret; |
13a9930d WS |
95 | |
96 | DPRINTK(4, "\n"); | |
97 | ||
98 | /* clear request */ | |
cdf6ecc5 | 99 | atomic_set(&priv->sleepstatus.doze_request, 0); |
13a9930d | 100 | |
cdf6ecc5 | 101 | if (atomic_read(&priv->sleepstatus.status) == 0) { |
13a9930d | 102 | rw_data = GCR_B_DOZE; |
1770ae9d TH |
103 | ret = ks7010_sdio_write(priv, GCR_B, &rw_data, sizeof(rw_data)); |
104 | if (ret) { | |
13a9930d | 105 | DPRINTK(1, " error : GCR_B=%02X\n", rw_data); |
f283dd69 | 106 | goto set_sleep_mode; |
13a9930d WS |
107 | } |
108 | DPRINTK(4, "PMG SET!! : GCR_B=%02X\n", rw_data); | |
cdf6ecc5 | 109 | DPRINTK(3, "sleep_mode=SLP_SLEEP\n"); |
13a9930d | 110 | atomic_set(&priv->sleepstatus.status, 1); |
cdf6ecc5 WS |
111 | priv->last_doze = jiffies; |
112 | } else { | |
113 | DPRINTK(1, "sleep_mode=%d\n", priv->sleep_mode); | |
13a9930d WS |
114 | } |
115 | ||
f283dd69 | 116 | set_sleep_mode: |
13a9930d | 117 | priv->sleep_mode = atomic_read(&priv->sleepstatus.status); |
13a9930d WS |
118 | } |
119 | ||
4433459a | 120 | static void ks_wlan_hw_sleep_wakeup_request(struct ks_wlan_private *priv) |
13a9930d WS |
121 | { |
122 | unsigned char rw_data; | |
1770ae9d | 123 | int ret; |
13a9930d WS |
124 | |
125 | DPRINTK(4, "\n"); | |
126 | ||
127 | /* clear request */ | |
cdf6ecc5 | 128 | atomic_set(&priv->sleepstatus.wakeup_request, 0); |
13a9930d | 129 | |
cdf6ecc5 | 130 | if (atomic_read(&priv->sleepstatus.status) == 1) { |
13a9930d | 131 | rw_data = WAKEUP_REQ; |
1770ae9d TH |
132 | ret = ks7010_sdio_write(priv, WAKEUP, &rw_data, sizeof(rw_data)); |
133 | if (ret) { | |
13a9930d | 134 | DPRINTK(1, " error : WAKEUP=%02X\n", rw_data); |
f283dd69 | 135 | goto set_sleep_mode; |
13a9930d WS |
136 | } |
137 | DPRINTK(4, "wake up : WAKEUP=%02X\n", rw_data); | |
138 | atomic_set(&priv->sleepstatus.status, 0); | |
cdf6ecc5 | 139 | priv->last_wakeup = jiffies; |
13a9930d | 140 | ++priv->wakeup_count; |
cdf6ecc5 WS |
141 | } else { |
142 | DPRINTK(1, "sleep_mode=%d\n", priv->sleep_mode); | |
13a9930d WS |
143 | } |
144 | ||
f283dd69 | 145 | set_sleep_mode: |
13a9930d | 146 | priv->sleep_mode = atomic_read(&priv->sleepstatus.status); |
13a9930d WS |
147 | } |
148 | ||
feedcf1a | 149 | void ks_wlan_hw_wakeup_request(struct ks_wlan_private *priv) |
13a9930d WS |
150 | { |
151 | unsigned char rw_data; | |
1770ae9d | 152 | int ret; |
13a9930d WS |
153 | |
154 | DPRINTK(4, "\n"); | |
cdf6ecc5 | 155 | if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { |
13a9930d | 156 | rw_data = WAKEUP_REQ; |
1770ae9d TH |
157 | ret = ks7010_sdio_write(priv, WAKEUP, &rw_data, sizeof(rw_data)); |
158 | if (ret) | |
13a9930d | 159 | DPRINTK(1, " error : WAKEUP=%02X\n", rw_data); |
53638cef | 160 | |
13a9930d | 161 | DPRINTK(4, "wake up : WAKEUP=%02X\n", rw_data); |
cdf6ecc5 | 162 | priv->last_wakeup = jiffies; |
13a9930d | 163 | ++priv->wakeup_count; |
cdf6ecc5 WS |
164 | } else { |
165 | DPRINTK(1, "psstatus=%d\n", | |
166 | atomic_read(&priv->psstatus.status)); | |
13a9930d WS |
167 | } |
168 | } | |
169 | ||
fa740a9e | 170 | static void _ks_wlan_hw_power_save(struct ks_wlan_private *priv) |
13a9930d | 171 | { |
13a9930d | 172 | unsigned char rw_data; |
f7172487 | 173 | int ret; |
13a9930d | 174 | |
cdf6ecc5 | 175 | if (priv->reg.powermgt == POWMGT_ACTIVE_MODE) |
fa740a9e | 176 | return; |
13a9930d | 177 | |
482c03c7 TH |
178 | if (priv->reg.operation_mode != MODE_INFRASTRUCTURE) |
179 | return; | |
180 | ||
181 | if ((priv->connect_status & CONNECT_STATUS_MASK) != CONNECT_STATUS) | |
fa740a9e | 182 | return; |
d5f1db31 TH |
183 | |
184 | if (priv->dev_state != DEVICE_STATE_SLEEP) | |
fa740a9e | 185 | return; |
d5f1db31 | 186 | |
3188bc09 | 187 | if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) |
fa740a9e | 188 | return; |
3188bc09 TH |
189 | |
190 | DPRINTK(5, "\npsstatus.status=%d\npsstatus.confirm_wait=%d\npsstatus.snooze_guard=%d\ncnt_txqbody=%d\n", | |
191 | atomic_read(&priv->psstatus.status), | |
192 | atomic_read(&priv->psstatus.confirm_wait), | |
193 | atomic_read(&priv->psstatus.snooze_guard), | |
194 | cnt_txqbody(priv)); | |
195 | ||
fa740a9e TH |
196 | if (atomic_read(&priv->psstatus.confirm_wait) || |
197 | atomic_read(&priv->psstatus.snooze_guard) || | |
198 | cnt_txqbody(priv)) { | |
18bd6dd1 | 199 | queue_delayed_work(priv->wq, &priv->rw_dwork, 0); |
fa740a9e TH |
200 | return; |
201 | } | |
202 | ||
203 | ret = ks7010_sdio_read(priv, INT_PENDING, &rw_data, sizeof(rw_data)); | |
204 | if (ret) { | |
205 | DPRINTK(1, " error : INT_PENDING=%02X\n", rw_data); | |
f8641485 | 206 | goto queue_delayed_work; |
fa740a9e | 207 | } |
f8641485 TH |
208 | if (rw_data) |
209 | goto queue_delayed_work; | |
fa740a9e | 210 | |
f8641485 TH |
211 | rw_data = GCR_B_DOZE; |
212 | ret = ks7010_sdio_write(priv, GCR_B, &rw_data, sizeof(rw_data)); | |
213 | if (ret) { | |
214 | DPRINTK(1, " error : GCR_B=%02X\n", rw_data); | |
215 | goto queue_delayed_work; | |
13a9930d | 216 | } |
f8641485 TH |
217 | DPRINTK(4, "PMG SET!! : GCR_B=%02X\n", rw_data); |
218 | atomic_set(&priv->psstatus.status, PS_SNOOZE); | |
219 | DPRINTK(3, "psstatus.status=PS_SNOOZE\n"); | |
13a9930d | 220 | |
fa740a9e | 221 | return; |
f8641485 TH |
222 | |
223 | queue_delayed_work: | |
18bd6dd1 | 224 | queue_delayed_work(priv->wq, &priv->rw_dwork, 1); |
13a9930d WS |
225 | } |
226 | ||
feedcf1a | 227 | int ks_wlan_hw_power_save(struct ks_wlan_private *priv) |
13a9930d | 228 | { |
18bd6dd1 | 229 | queue_delayed_work(priv->wq, &priv->rw_dwork, 1); |
13a9930d WS |
230 | return 0; |
231 | } | |
232 | ||
cdf6ecc5 WS |
233 | static int enqueue_txdev(struct ks_wlan_private *priv, unsigned char *p, |
234 | unsigned long size, | |
055da4f9 TH |
235 | void (*complete_handler)(struct ks_wlan_private *priv, |
236 | struct sk_buff *skb), | |
237 | struct sk_buff *skb) | |
13a9930d WS |
238 | { |
239 | struct tx_device_buffer *sp; | |
1770ae9d | 240 | int ret; |
13a9930d WS |
241 | |
242 | if (priv->dev_state < DEVICE_STATE_BOOT) { | |
1770ae9d | 243 | ret = -EPERM; |
aa6ca807 | 244 | goto err_complete; |
13a9930d WS |
245 | } |
246 | ||
cdf6ecc5 | 247 | if ((TX_DEVICE_BUFF_SIZE - 1) <= cnt_txqbody(priv)) { |
cdf6ecc5 | 248 | DPRINTK(1, "tx buffer overflow\n"); |
1770ae9d | 249 | ret = -EOVERFLOW; |
aa6ca807 | 250 | goto err_complete; |
13a9930d WS |
251 | } |
252 | ||
253 | sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qtail]; | |
254 | sp->sendp = p; | |
255 | sp->size = size; | |
256 | sp->complete_handler = complete_handler; | |
055da4f9 | 257 | sp->skb = skb; |
13a9930d WS |
258 | inc_txqtail(priv); |
259 | ||
260 | return 0; | |
aa6ca807 TH |
261 | |
262 | err_complete: | |
263 | kfree(p); | |
264 | if (complete_handler) | |
055da4f9 | 265 | (*complete_handler)(priv, skb); |
aa6ca807 | 266 | |
1770ae9d | 267 | return ret; |
13a9930d WS |
268 | } |
269 | ||
270 | /* write data */ | |
cdf6ecc5 WS |
271 | static int write_to_device(struct ks_wlan_private *priv, unsigned char *buffer, |
272 | unsigned long size) | |
13a9930d | 273 | { |
13a9930d WS |
274 | unsigned char rw_data; |
275 | struct hostif_hdr *hdr; | |
1770ae9d | 276 | int ret; |
697f9f7f | 277 | |
13a9930d | 278 | hdr = (struct hostif_hdr *)buffer; |
13a9930d | 279 | |
cdf6ecc5 WS |
280 | DPRINTK(4, "size=%d\n", hdr->size); |
281 | if (hdr->event < HIF_DATA_REQ || HIF_REQ_MAX < hdr->event) { | |
282 | DPRINTK(1, "unknown event=%04X\n", hdr->event); | |
13a9930d WS |
283 | return 0; |
284 | } | |
285 | ||
1770ae9d TH |
286 | ret = ks7010_sdio_write(priv, DATA_WINDOW, buffer, size); |
287 | if (ret) { | |
288 | DPRINTK(1, " write error : retval=%d\n", ret); | |
289 | return ret; | |
13a9930d WS |
290 | } |
291 | ||
bfee6a23 | 292 | rw_data = REG_STATUS_BUSY; |
1770ae9d TH |
293 | ret = ks7010_sdio_write(priv, WRITE_STATUS, &rw_data, sizeof(rw_data)); |
294 | if (ret) { | |
13a9930d | 295 | DPRINTK(1, " error : WRITE_STATUS=%02X\n", rw_data); |
1770ae9d | 296 | return ret; |
13a9930d WS |
297 | } |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
5141e9c6 | 302 | static void tx_device_task(struct ks_wlan_private *priv) |
13a9930d | 303 | { |
cdf6ecc5 | 304 | struct tx_device_buffer *sp; |
03b02449 | 305 | int ret; |
13a9930d WS |
306 | |
307 | DPRINTK(4, "\n"); | |
638a75b6 TH |
308 | if (cnt_txqbody(priv) <= 0 || |
309 | atomic_read(&priv->psstatus.status) == PS_SNOOZE) | |
310 | return; | |
13a9930d | 311 | |
638a75b6 TH |
312 | sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead]; |
313 | if (priv->dev_state >= DEVICE_STATE_BOOT) { | |
314 | ret = write_to_device(priv, sp->sendp, sp->size); | |
315 | if (ret) { | |
316 | DPRINTK(1, "write_to_device error !!(%d)\n", ret); | |
18bd6dd1 | 317 | queue_delayed_work(priv->wq, &priv->rw_dwork, 1); |
638a75b6 | 318 | return; |
13a9930d WS |
319 | } |
320 | } | |
638a75b6 TH |
321 | kfree(sp->sendp); |
322 | if (sp->complete_handler) /* TX Complete */ | |
055da4f9 | 323 | (*sp->complete_handler)(priv, sp->skb); |
638a75b6 TH |
324 | inc_txqhead(priv); |
325 | ||
18bd6dd1 TH |
326 | if (cnt_txqbody(priv) > 0) |
327 | queue_delayed_work(priv->wq, &priv->rw_dwork, 0); | |
13a9930d WS |
328 | } |
329 | ||
cdf6ecc5 | 330 | int ks_wlan_hw_tx(struct ks_wlan_private *priv, void *p, unsigned long size, |
055da4f9 TH |
331 | void (*complete_handler)(struct ks_wlan_private *priv, |
332 | struct sk_buff *skb), | |
333 | struct sk_buff *skb) | |
13a9930d | 334 | { |
cdf6ecc5 | 335 | int result = 0; |
13a9930d | 336 | struct hostif_hdr *hdr; |
697f9f7f | 337 | |
13a9930d WS |
338 | hdr = (struct hostif_hdr *)p; |
339 | ||
cdf6ecc5 WS |
340 | if (hdr->event < HIF_DATA_REQ || HIF_REQ_MAX < hdr->event) { |
341 | DPRINTK(1, "unknown event=%04X\n", hdr->event); | |
13a9930d WS |
342 | return 0; |
343 | } | |
344 | ||
345 | /* add event to hostt buffer */ | |
346 | priv->hostt.buff[priv->hostt.qtail] = hdr->event; | |
cdf6ecc5 | 347 | priv->hostt.qtail = (priv->hostt.qtail + 1) % SME_EVENT_BUFF_SIZE; |
13a9930d | 348 | |
cdf6ecc5 | 349 | DPRINTK(4, "event=%04X\n", hdr->event); |
13a9930d | 350 | spin_lock(&priv->tx_dev.tx_dev_lock); |
055da4f9 | 351 | result = enqueue_txdev(priv, p, size, complete_handler, skb); |
13a9930d WS |
352 | spin_unlock(&priv->tx_dev.tx_dev_lock); |
353 | ||
18bd6dd1 TH |
354 | if (cnt_txqbody(priv) > 0) |
355 | queue_delayed_work(priv->wq, &priv->rw_dwork, 0); | |
356 | ||
13a9930d WS |
357 | return result; |
358 | } | |
359 | ||
360 | static void rx_event_task(unsigned long dev) | |
361 | { | |
cdf6ecc5 WS |
362 | struct ks_wlan_private *priv = (struct ks_wlan_private *)dev; |
363 | struct rx_device_buffer *rp; | |
13a9930d | 364 | |
cdf6ecc5 | 365 | DPRINTK(4, "\n"); |
13a9930d | 366 | |
cdf6ecc5 | 367 | if (cnt_rxqbody(priv) > 0 && priv->dev_state >= DEVICE_STATE_BOOT) { |
13a9930d WS |
368 | rp = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qhead]; |
369 | hostif_receive(priv, rp->data, rp->size); | |
370 | inc_rxqhead(priv); | |
371 | ||
53638cef | 372 | if (cnt_rxqbody(priv) > 0) |
321dabdc | 373 | tasklet_schedule(&priv->rx_bh_task); |
13a9930d | 374 | } |
13a9930d WS |
375 | } |
376 | ||
5141e9c6 | 377 | static void ks_wlan_hw_rx(struct ks_wlan_private *priv, uint16_t size) |
13a9930d | 378 | { |
f7172487 | 379 | int ret; |
13a9930d WS |
380 | struct rx_device_buffer *rx_buffer; |
381 | struct hostif_hdr *hdr; | |
cdf6ecc5 WS |
382 | unsigned char read_status; |
383 | unsigned short event = 0; | |
13a9930d | 384 | |
cdf6ecc5 | 385 | DPRINTK(4, "\n"); |
13a9930d WS |
386 | |
387 | /* receive data */ | |
cdf6ecc5 | 388 | if (cnt_rxqbody(priv) >= (RX_DEVICE_BUFF_SIZE - 1)) { |
71a476e4 | 389 | DPRINTK(1, "rx buffer overflow\n"); |
13b05e46 | 390 | return; |
13a9930d WS |
391 | } |
392 | rx_buffer = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qtail]; | |
393 | ||
f7172487 TH |
394 | ret = ks7010_sdio_read(priv, DATA_WINDOW, &rx_buffer->data[0], |
395 | hif_align_size(size)); | |
396 | if (ret) | |
13b05e46 | 397 | return; |
13a9930d WS |
398 | |
399 | /* length check */ | |
cdf6ecc5 | 400 | if (size > 2046 || size == 0) { |
3215bb1a WS |
401 | #ifdef KS_WLAN_DEBUG |
402 | if (KS_WLAN_DEBUG > 5) | |
cdf6ecc5 WS |
403 | print_hex_dump_bytes("INVALID DATA dump: ", |
404 | DUMP_PREFIX_OFFSET, | |
3215bb1a WS |
405 | rx_buffer->data, 32); |
406 | #endif | |
13a9930d | 407 | /* rx_status update */ |
bfee6a23 | 408 | read_status = REG_STATUS_IDLE; |
f7172487 TH |
409 | ret = ks7010_sdio_write(priv, READ_STATUS, &read_status, |
410 | sizeof(read_status)); | |
411 | if (ret) | |
13a9930d | 412 | DPRINTK(1, " error : READ_STATUS=%02X\n", read_status); |
53638cef | 413 | |
13b05e46 TH |
414 | /* length check fail */ |
415 | return; | |
13a9930d WS |
416 | } |
417 | ||
418 | hdr = (struct hostif_hdr *)&rx_buffer->data[0]; | |
419 | rx_buffer->size = le16_to_cpu(hdr->size) + sizeof(hdr->size); | |
420 | event = hdr->event; | |
421 | inc_rxqtail(priv); | |
422 | ||
423 | /* read status update */ | |
bfee6a23 | 424 | read_status = REG_STATUS_IDLE; |
f7172487 TH |
425 | ret = ks7010_sdio_write(priv, READ_STATUS, &read_status, |
426 | sizeof(read_status)); | |
427 | if (ret) | |
13a9930d | 428 | DPRINTK(1, " error : READ_STATUS=%02X\n", read_status); |
53638cef | 429 | |
13a9930d WS |
430 | DPRINTK(4, "READ_STATUS=%02X\n", read_status); |
431 | ||
cdf6ecc5 WS |
432 | if (atomic_read(&priv->psstatus.confirm_wait)) { |
433 | if (IS_HIF_CONF(event)) { | |
13a9930d WS |
434 | DPRINTK(4, "IS_HIF_CONF true !!\n"); |
435 | atomic_dec(&priv->psstatus.confirm_wait); | |
436 | } | |
437 | } | |
438 | ||
321dabdc | 439 | tasklet_schedule(&priv->rx_bh_task); |
13a9930d WS |
440 | } |
441 | ||
442 | static void ks7010_rw_function(struct work_struct *work) | |
443 | { | |
13a9930d WS |
444 | struct ks_wlan_private *priv; |
445 | unsigned char rw_data; | |
1770ae9d | 446 | int ret; |
13a9930d | 447 | |
18bd6dd1 | 448 | priv = container_of(work, struct ks_wlan_private, rw_dwork.work); |
13a9930d | 449 | |
cdf6ecc5 | 450 | DPRINTK(4, "\n"); |
13a9930d | 451 | |
e61e73d7 | 452 | /* wait after DOZE */ |
cdf6ecc5 | 453 | if (time_after(priv->last_doze + ((30 * HZ) / 1000), jiffies)) { |
c8abeaf8 | 454 | DPRINTK(4, "wait after DOZE\n"); |
18bd6dd1 | 455 | queue_delayed_work(priv->wq, &priv->rw_dwork, 1); |
13a9930d WS |
456 | return; |
457 | } | |
458 | ||
e61e73d7 | 459 | /* wait after WAKEUP */ |
cdf6ecc5 | 460 | while (time_after(priv->last_wakeup + ((30 * HZ) / 1000), jiffies)) { |
c8abeaf8 | 461 | DPRINTK(4, "wait after WAKEUP\n"); |
18bd6dd1 | 462 | dev_info(&priv->ks_sdio_card->func->dev, |
9887b5e5 SG |
463 | "wake: %lu %lu\n", |
464 | priv->last_wakeup + (30 * HZ) / 1000, | |
465 | jiffies); | |
13a9930d WS |
466 | msleep(30); |
467 | } | |
468 | ||
18bd6dd1 | 469 | sdio_claim_host(priv->ks_sdio_card->func); |
13a9930d WS |
470 | |
471 | /* power save wakeup */ | |
cdf6ecc5 WS |
472 | if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { |
473 | if (cnt_txqbody(priv) > 0) { | |
13a9930d | 474 | ks_wlan_hw_wakeup_request(priv); |
18bd6dd1 | 475 | queue_delayed_work(priv->wq, &priv->rw_dwork, 1); |
13a9930d | 476 | } |
f283dd69 | 477 | goto err_release_host; |
13a9930d WS |
478 | } |
479 | ||
480 | /* sleep mode doze */ | |
cdf6ecc5 | 481 | if (atomic_read(&priv->sleepstatus.doze_request) == 1) { |
13a9930d | 482 | ks_wlan_hw_sleep_doze_request(priv); |
f283dd69 | 483 | goto err_release_host; |
13a9930d WS |
484 | } |
485 | /* sleep mode wakeup */ | |
cdf6ecc5 | 486 | if (atomic_read(&priv->sleepstatus.wakeup_request) == 1) { |
13a9930d | 487 | ks_wlan_hw_sleep_wakeup_request(priv); |
f283dd69 | 488 | goto err_release_host; |
13a9930d WS |
489 | } |
490 | ||
491 | /* read (WriteStatus/ReadDataSize FN1:00_0014) */ | |
1770ae9d TH |
492 | ret = ks7010_sdio_read(priv, WSTATUS_RSIZE, &rw_data, sizeof(rw_data)); |
493 | if (ret) { | |
cdf6ecc5 WS |
494 | DPRINTK(1, " error : WSTATUS_RSIZE=%02X psstatus=%d\n", rw_data, |
495 | atomic_read(&priv->psstatus.status)); | |
f283dd69 | 496 | goto err_release_host; |
13a9930d WS |
497 | } |
498 | DPRINTK(4, "WSTATUS_RSIZE=%02X\n", rw_data); | |
499 | ||
cdf6ecc5 | 500 | if (rw_data & RSIZE_MASK) { /* Read schedule */ |
5141e9c6 | 501 | ks_wlan_hw_rx(priv, (uint16_t)((rw_data & RSIZE_MASK) << 4)); |
13a9930d | 502 | } |
53638cef | 503 | if ((rw_data & WSTATUS_MASK)) |
5141e9c6 | 504 | tx_device_task(priv); |
53638cef | 505 | |
13a9930d WS |
506 | _ks_wlan_hw_power_save(priv); |
507 | ||
f283dd69 | 508 | err_release_host: |
18bd6dd1 | 509 | sdio_release_host(priv->ks_sdio_card->func); |
13a9930d WS |
510 | } |
511 | ||
13a9930d WS |
512 | static void ks_sdio_interrupt(struct sdio_func *func) |
513 | { | |
f7172487 | 514 | int ret; |
13a9930d | 515 | struct ks_sdio_card *card; |
feedcf1a | 516 | struct ks_wlan_private *priv; |
13a9930d WS |
517 | unsigned char status, rsize, rw_data; |
518 | ||
519 | card = sdio_get_drvdata(func); | |
520 | priv = card->priv; | |
521 | DPRINTK(4, "\n"); | |
522 | ||
638a75b6 TH |
523 | if (priv->dev_state < DEVICE_STATE_BOOT) |
524 | goto queue_delayed_work; | |
525 | ||
526 | ret = ks7010_sdio_read(priv, INT_PENDING, &status, sizeof(status)); | |
527 | if (ret) { | |
528 | DPRINTK(1, "read INT_PENDING Failed!!(%d)\n", ret); | |
529 | goto queue_delayed_work; | |
530 | } | |
531 | DPRINTK(4, "INT_PENDING=%02X\n", rw_data); | |
532 | ||
533 | /* schedule task for interrupt status */ | |
534 | /* bit7 -> Write General Communication B register */ | |
535 | /* read (General Communication B register) */ | |
536 | /* bit5 -> Write Status Idle */ | |
537 | /* bit2 -> Read Status Busy */ | |
538 | if (status & INT_GCR_B || | |
539 | atomic_read(&priv->psstatus.status) == PS_SNOOZE) { | |
540 | ret = ks7010_sdio_read(priv, GCR_B, &rw_data, | |
541 | sizeof(rw_data)); | |
f7172487 | 542 | if (ret) { |
638a75b6 | 543 | DPRINTK(1, " error : GCR_B=%02X\n", rw_data); |
f283dd69 | 544 | goto queue_delayed_work; |
13a9930d | 545 | } |
638a75b6 TH |
546 | if (rw_data == GCR_B_ACTIVE) { |
547 | if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { | |
548 | atomic_set(&priv->psstatus.status, PS_WAKEUP); | |
549 | priv->wakeup_count = 0; | |
13a9930d | 550 | } |
638a75b6 | 551 | complete(&priv->psstatus.wakeup_wait); |
13a9930d | 552 | } |
638a75b6 | 553 | } |
13a9930d | 554 | |
638a75b6 TH |
555 | do { |
556 | /* read (WriteStatus/ReadDataSize FN1:00_0014) */ | |
557 | ret = ks7010_sdio_read(priv, WSTATUS_RSIZE, &rw_data, | |
558 | sizeof(rw_data)); | |
559 | if (ret) { | |
560 | DPRINTK(1, " error : WSTATUS_RSIZE=%02X\n", rw_data); | |
561 | goto queue_delayed_work; | |
562 | } | |
563 | DPRINTK(4, "WSTATUS_RSIZE=%02X\n", rw_data); | |
564 | rsize = rw_data & RSIZE_MASK; | |
565 | if (rsize != 0) /* Read schedule */ | |
566 | ks_wlan_hw_rx(priv, (uint16_t)(rsize << 4)); | |
567 | ||
568 | if (rw_data & WSTATUS_MASK) { | |
569 | if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { | |
570 | if (cnt_txqbody(priv)) { | |
571 | ks_wlan_hw_wakeup_request(priv); | |
18bd6dd1 | 572 | queue_delayed_work(priv->wq, &priv->rw_dwork, 1); |
638a75b6 | 573 | return; |
cdf6ecc5 | 574 | } |
638a75b6 TH |
575 | } else { |
576 | tx_device_task(priv); | |
13a9930d | 577 | } |
638a75b6 TH |
578 | } |
579 | } while (rsize); | |
13a9930d | 580 | |
f283dd69 | 581 | queue_delayed_work: |
18bd6dd1 | 582 | queue_delayed_work(priv->wq, &priv->rw_dwork, 0); |
13a9930d WS |
583 | } |
584 | ||
cdf6ecc5 | 585 | static int trx_device_init(struct ks_wlan_private *priv) |
13a9930d | 586 | { |
20358d13 NR |
587 | priv->tx_dev.qhead = 0; |
588 | priv->tx_dev.qtail = 0; | |
13a9930d | 589 | |
20358d13 NR |
590 | priv->rx_dev.qhead = 0; |
591 | priv->rx_dev.qtail = 0; | |
13a9930d | 592 | |
13a9930d WS |
593 | spin_lock_init(&priv->tx_dev.tx_dev_lock); |
594 | spin_lock_init(&priv->rx_dev.rx_dev_lock); | |
595 | ||
321dabdc | 596 | tasklet_init(&priv->rx_bh_task, rx_event_task, (unsigned long)priv); |
13a9930d WS |
597 | |
598 | return 0; | |
599 | } | |
600 | ||
cdf6ecc5 | 601 | static void trx_device_exit(struct ks_wlan_private *priv) |
13a9930d | 602 | { |
cdf6ecc5 | 603 | struct tx_device_buffer *sp; |
13a9930d WS |
604 | |
605 | /* tx buffer clear */ | |
cdf6ecc5 | 606 | while (cnt_txqbody(priv) > 0) { |
13a9930d | 607 | sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead]; |
e61e73d7 | 608 | kfree(sp->sendp); |
c7e65f4d | 609 | if (sp->complete_handler) /* TX Complete */ |
055da4f9 | 610 | (*sp->complete_handler)(priv, sp->skb); |
13a9930d WS |
611 | inc_txqhead(priv); |
612 | } | |
613 | ||
321dabdc | 614 | tasklet_kill(&priv->rx_bh_task); |
13a9930d | 615 | } |
cdf6ecc5 | 616 | |
feedcf1a | 617 | static int ks7010_sdio_update_index(struct ks_wlan_private *priv, u32 index) |
13a9930d | 618 | { |
1770ae9d | 619 | int ret; |
13a9930d | 620 | unsigned char *data_buf; |
13a9930d WS |
621 | |
622 | data_buf = kmalloc(sizeof(u32), GFP_KERNEL); | |
aa6ca807 TH |
623 | if (!data_buf) |
624 | return -ENOMEM; | |
13a9930d WS |
625 | |
626 | memcpy(data_buf, &index, sizeof(index)); | |
1770ae9d TH |
627 | ret = ks7010_sdio_write(priv, WRITE_INDEX, data_buf, sizeof(index)); |
628 | if (ret) | |
f283dd69 | 629 | goto err_free_data_buf; |
13a9930d | 630 | |
1770ae9d TH |
631 | ret = ks7010_sdio_write(priv, READ_INDEX, data_buf, sizeof(index)); |
632 | if (ret) | |
f283dd69 | 633 | goto err_free_data_buf; |
aa6ca807 TH |
634 | |
635 | return 0; | |
636 | ||
f283dd69 | 637 | err_free_data_buf: |
58043f25 | 638 | kfree(data_buf); |
aa6ca807 | 639 | |
1770ae9d | 640 | return ret; |
13a9930d WS |
641 | } |
642 | ||
0966c755 | 643 | #define ROM_BUFF_SIZE (64 * 1024) |
feedcf1a | 644 | static int ks7010_sdio_data_compare(struct ks_wlan_private *priv, u32 address, |
13a9930d WS |
645 | unsigned char *data, unsigned int size) |
646 | { | |
1770ae9d | 647 | int ret; |
13a9930d | 648 | unsigned char *read_buf; |
eeed92c0 | 649 | |
13a9930d | 650 | read_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL); |
aa6ca807 TH |
651 | if (!read_buf) |
652 | return -ENOMEM; | |
653 | ||
1770ae9d TH |
654 | ret = ks7010_sdio_read(priv, address, read_buf, size); |
655 | if (ret) | |
f283dd69 | 656 | goto err_free_read_buf; |
13a9930d | 657 | |
58af272b TH |
658 | if (memcmp(data, read_buf, size) != 0) { |
659 | ret = -EIO; | |
1770ae9d | 660 | DPRINTK(0, "data compare error (%d)\n", ret); |
f283dd69 | 661 | goto err_free_read_buf; |
13a9930d | 662 | } |
aa6ca807 TH |
663 | |
664 | return 0; | |
665 | ||
f283dd69 | 666 | err_free_read_buf: |
58043f25 | 667 | kfree(read_buf); |
aa6ca807 | 668 | |
1770ae9d | 669 | return ret; |
13a9930d | 670 | } |
cdf6ecc5 | 671 | |
ed246b9e | 672 | static int ks7010_upload_firmware(struct ks_sdio_card *card) |
13a9930d | 673 | { |
ed246b9e | 674 | struct ks_wlan_private *priv = card->priv; |
cdf6ecc5 WS |
675 | unsigned int size, offset, n = 0; |
676 | unsigned char *rom_buf; | |
677 | unsigned char rw_data = 0; | |
1770ae9d | 678 | int ret; |
881f76b9 | 679 | unsigned int length; |
13a9930d | 680 | const struct firmware *fw_entry = NULL; |
13a9930d | 681 | |
13a9930d | 682 | rom_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL); |
369e1b69 | 683 | if (!rom_buf) |
aa6ca807 | 684 | return -ENOMEM; |
13a9930d WS |
685 | |
686 | sdio_claim_host(card->func); | |
687 | ||
688 | /* Firmware running ? */ | |
1770ae9d | 689 | ret = ks7010_sdio_read(priv, GCR_A, &rw_data, sizeof(rw_data)); |
cdf6ecc5 WS |
690 | if (rw_data == GCR_A_RUN) { |
691 | DPRINTK(0, "MAC firmware running ...\n"); | |
aa6ca807 | 692 | goto release_host_and_free; |
13a9930d WS |
693 | } |
694 | ||
1770ae9d | 695 | ret = request_firmware(&fw_entry, ROM_FILE, |
18bd6dd1 | 696 | &priv->ks_sdio_card->func->dev); |
1770ae9d | 697 | if (ret) |
aa6ca807 | 698 | goto release_host_and_free; |
6ee9169b | 699 | |
13a9930d | 700 | length = fw_entry->size; |
13a9930d | 701 | |
13a9930d | 702 | n = 0; |
cdf6ecc5 WS |
703 | do { |
704 | if (length >= ROM_BUFF_SIZE) { | |
13a9930d WS |
705 | size = ROM_BUFF_SIZE; |
706 | length = length - ROM_BUFF_SIZE; | |
cdf6ecc5 WS |
707 | } else { |
708 | size = length; | |
709 | length = 0; | |
13a9930d | 710 | } |
cdf6ecc5 WS |
711 | DPRINTK(4, "size = %d\n", size); |
712 | if (size == 0) | |
713 | break; | |
714 | memcpy(rom_buf, fw_entry->data + n, size); | |
e61e73d7 | 715 | |
13a9930d | 716 | offset = n; |
1770ae9d TH |
717 | ret = ks7010_sdio_update_index(priv, KS7010_IRAM_ADDRESS + offset); |
718 | if (ret) | |
aa6ca807 | 719 | goto release_firmware; |
13a9930d | 720 | |
1770ae9d TH |
721 | ret = ks7010_sdio_write(priv, DATA_WINDOW, rom_buf, size); |
722 | if (ret) | |
aa6ca807 | 723 | goto release_firmware; |
13a9930d | 724 | |
1770ae9d TH |
725 | ret = ks7010_sdio_data_compare(priv, DATA_WINDOW, rom_buf, size); |
726 | if (ret) | |
aa6ca807 TH |
727 | goto release_firmware; |
728 | ||
13a9930d WS |
729 | n += size; |
730 | ||
cdf6ecc5 | 731 | } while (size); |
13a9930d | 732 | |
13a9930d | 733 | rw_data = GCR_A_REMAP; |
1770ae9d TH |
734 | ret = ks7010_sdio_write(priv, GCR_A, &rw_data, sizeof(rw_data)); |
735 | if (ret) | |
aa6ca807 TH |
736 | goto release_firmware; |
737 | ||
cdf6ecc5 | 738 | DPRINTK(4, " REMAP Request : GCR_A=%02X\n", rw_data); |
13a9930d WS |
739 | |
740 | /* Firmware running check */ | |
741 | for (n = 0; n < 50; ++n) { | |
cdf6ecc5 | 742 | mdelay(10); /* wait_ms(10); */ |
1770ae9d TH |
743 | ret = ks7010_sdio_read(priv, GCR_A, &rw_data, sizeof(rw_data)); |
744 | if (ret) | |
aa6ca807 TH |
745 | goto release_firmware; |
746 | ||
cdf6ecc5 WS |
747 | if (rw_data == GCR_A_RUN) |
748 | break; | |
13a9930d | 749 | } |
cdf6ecc5 | 750 | DPRINTK(4, "firmware wakeup (%d)!!!!\n", n); |
13a9930d WS |
751 | if ((50) <= n) { |
752 | DPRINTK(1, "firmware can't start\n"); | |
1770ae9d | 753 | ret = -EIO; |
aa6ca807 | 754 | goto release_firmware; |
13a9930d WS |
755 | } |
756 | ||
1770ae9d | 757 | ret = 0; |
13a9930d | 758 | |
aa6ca807 | 759 | release_firmware: |
13a9930d | 760 | release_firmware(fw_entry); |
aa6ca807 | 761 | release_host_and_free: |
13a9930d | 762 | sdio_release_host(card->func); |
58043f25 | 763 | kfree(rom_buf); |
aa6ca807 | 764 | |
1770ae9d | 765 | return ret; |
13a9930d WS |
766 | } |
767 | ||
e8593a8a | 768 | static void ks7010_card_init(struct ks_wlan_private *priv) |
13a9930d | 769 | { |
cdf6ecc5 | 770 | DPRINTK(5, "\ncard_init_task()\n"); |
13a9930d | 771 | |
13a9930d WS |
772 | init_completion(&priv->confirm_wait); |
773 | ||
cdf6ecc5 | 774 | DPRINTK(5, "init_completion()\n"); |
13a9930d WS |
775 | |
776 | /* get mac address & firmware version */ | |
777 | hostif_sme_enqueue(priv, SME_START); | |
778 | ||
cdf6ecc5 | 779 | DPRINTK(5, "hostif_sme_enqueu()\n"); |
13a9930d | 780 | |
cdf6ecc5 WS |
781 | if (!wait_for_completion_interruptible_timeout |
782 | (&priv->confirm_wait, 5 * HZ)) { | |
783 | DPRINTK(1, "wait time out!! SME_START\n"); | |
13a9930d WS |
784 | } |
785 | ||
310e916f | 786 | if (priv->mac_address_valid && priv->version_size != 0) |
13a9930d | 787 | priv->dev_state = DEVICE_STATE_PREINIT; |
53638cef | 788 | |
13a9930d WS |
789 | hostif_sme_enqueue(priv, SME_GET_EEPROM_CKSUM); |
790 | ||
791 | /* load initial wireless parameter */ | |
792 | hostif_sme_enqueue(priv, SME_STOP_REQUEST); | |
793 | ||
794 | hostif_sme_enqueue(priv, SME_RTS_THRESHOLD_REQUEST); | |
795 | hostif_sme_enqueue(priv, SME_FRAGMENTATION_THRESHOLD_REQUEST); | |
796 | ||
797 | hostif_sme_enqueue(priv, SME_WEP_INDEX_REQUEST); | |
798 | hostif_sme_enqueue(priv, SME_WEP_KEY1_REQUEST); | |
799 | hostif_sme_enqueue(priv, SME_WEP_KEY2_REQUEST); | |
800 | hostif_sme_enqueue(priv, SME_WEP_KEY3_REQUEST); | |
801 | hostif_sme_enqueue(priv, SME_WEP_KEY4_REQUEST); | |
802 | ||
803 | hostif_sme_enqueue(priv, SME_WEP_FLAG_REQUEST); | |
804 | hostif_sme_enqueue(priv, SME_RSN_ENABLED_REQUEST); | |
805 | hostif_sme_enqueue(priv, SME_MODE_SET_REQUEST); | |
806 | hostif_sme_enqueue(priv, SME_START_REQUEST); | |
807 | ||
cdf6ecc5 WS |
808 | if (!wait_for_completion_interruptible_timeout |
809 | (&priv->confirm_wait, 5 * HZ)) { | |
810 | DPRINTK(1, "wait time out!! wireless parameter set\n"); | |
13a9930d WS |
811 | } |
812 | ||
cdf6ecc5 | 813 | if (priv->dev_state >= DEVICE_STATE_PREINIT) { |
13a9930d WS |
814 | DPRINTK(1, "DEVICE READY!!\n"); |
815 | priv->dev_state = DEVICE_STATE_READY; | |
cdf6ecc5 WS |
816 | } else { |
817 | DPRINTK(1, "dev_state=%d\n", priv->dev_state); | |
13a9930d WS |
818 | } |
819 | } | |
820 | ||
6ee9169b WS |
821 | static void ks7010_init_defaults(struct ks_wlan_private *priv) |
822 | { | |
823 | priv->reg.tx_rate = TX_RATE_AUTO; | |
824 | priv->reg.preamble = LONG_PREAMBLE; | |
825 | priv->reg.powermgt = POWMGT_ACTIVE_MODE; | |
826 | priv->reg.scan_type = ACTIVE_SCAN; | |
827 | priv->reg.beacon_lost_count = 20; | |
828 | priv->reg.rts = 2347UL; | |
829 | priv->reg.fragment = 2346UL; | |
830 | priv->reg.phy_type = D_11BG_COMPATIBLE_MODE; | |
831 | priv->reg.cts_mode = CTS_MODE_FALSE; | |
832 | priv->reg.rate_set.body[11] = TX_RATE_54M; | |
833 | priv->reg.rate_set.body[10] = TX_RATE_48M; | |
834 | priv->reg.rate_set.body[9] = TX_RATE_36M; | |
835 | priv->reg.rate_set.body[8] = TX_RATE_18M; | |
836 | priv->reg.rate_set.body[7] = TX_RATE_9M; | |
837 | priv->reg.rate_set.body[6] = TX_RATE_24M | BASIC_RATE; | |
838 | priv->reg.rate_set.body[5] = TX_RATE_12M | BASIC_RATE; | |
839 | priv->reg.rate_set.body[4] = TX_RATE_6M | BASIC_RATE; | |
840 | priv->reg.rate_set.body[3] = TX_RATE_11M | BASIC_RATE; | |
841 | priv->reg.rate_set.body[2] = TX_RATE_5M | BASIC_RATE; | |
842 | priv->reg.rate_set.body[1] = TX_RATE_2M | BASIC_RATE; | |
843 | priv->reg.rate_set.body[0] = TX_RATE_1M | BASIC_RATE; | |
844 | priv->reg.tx_rate = TX_RATE_FULL_AUTO; | |
845 | priv->reg.rate_set.size = 12; | |
846 | } | |
847 | ||
c4730a92 | 848 | static int ks7010_sdio_probe(struct sdio_func *func, |
cdf6ecc5 | 849 | const struct sdio_device_id *device) |
13a9930d | 850 | { |
feedcf1a | 851 | struct ks_wlan_private *priv; |
13a9930d WS |
852 | struct ks_sdio_card *card; |
853 | struct net_device *netdev; | |
854 | unsigned char rw_data; | |
2801d7a2 | 855 | int ret; |
13a9930d | 856 | |
c4730a92 | 857 | DPRINTK(5, "ks7010_sdio_probe()\n"); |
13a9930d WS |
858 | |
859 | priv = NULL; | |
cdf6ecc5 | 860 | netdev = NULL; |
13a9930d | 861 | |
2d738bd2 | 862 | card = kzalloc(sizeof(*card), GFP_KERNEL); |
13a9930d WS |
863 | if (!card) |
864 | return -ENOMEM; | |
865 | ||
cdf6ecc5 | 866 | card->func = func; |
13a9930d | 867 | |
13a9930d WS |
868 | sdio_claim_host(func); |
869 | ||
13a9930d | 870 | ret = sdio_set_block_size(func, KS7010_IO_BLOCK_SIZE); |
cdf6ecc5 WS |
871 | DPRINTK(5, "multi_block=%d sdio_set_block_size()=%d %d\n", |
872 | func->card->cccr.multi_block, func->cur_blksize, ret); | |
13a9930d | 873 | |
13a9930d WS |
874 | ret = sdio_enable_func(func); |
875 | DPRINTK(5, "sdio_enable_func() %d\n", ret); | |
876 | if (ret) | |
f283dd69 | 877 | goto err_free_card; |
13a9930d WS |
878 | |
879 | /* interrupt disable */ | |
cdf6ecc5 | 880 | sdio_writeb(func, 0, INT_ENABLE, &ret); |
13a9930d | 881 | if (ret) |
f283dd69 | 882 | goto err_free_card; |
cdf6ecc5 | 883 | sdio_writeb(func, 0xff, INT_PENDING, &ret); |
13a9930d | 884 | if (ret) |
f283dd69 | 885 | goto err_disable_func; |
13a9930d WS |
886 | |
887 | /* setup interrupt handler */ | |
888 | ret = sdio_claim_irq(func, ks_sdio_interrupt); | |
889 | if (ret) | |
f283dd69 | 890 | goto err_disable_func; |
13a9930d WS |
891 | |
892 | sdio_release_host(func); | |
893 | ||
894 | sdio_set_drvdata(func, card); | |
895 | ||
896 | DPRINTK(5, "class = 0x%X, vendor = 0x%X, " | |
cdf6ecc5 | 897 | "device = 0x%X\n", func->class, func->vendor, func->device); |
13a9930d WS |
898 | |
899 | /* private memory allocate */ | |
900 | netdev = alloc_etherdev(sizeof(*priv)); | |
c7e65f4d | 901 | if (!netdev) { |
9887b5e5 | 902 | dev_err(&card->func->dev, "ks7010 : Unable to alloc new net device\n"); |
f283dd69 | 903 | goto err_release_irq; |
13a9930d | 904 | } |
6634cff1 | 905 | if (dev_alloc_name(netdev, "wlan%d") < 0) { |
9887b5e5 SG |
906 | dev_err(&card->func->dev, |
907 | "ks7010 : Couldn't get name!\n"); | |
f283dd69 | 908 | goto err_free_netdev; |
13a9930d WS |
909 | } |
910 | ||
911 | priv = netdev_priv(netdev); | |
912 | ||
913 | card->priv = priv; | |
914 | SET_NETDEV_DEV(netdev, &card->func->dev); /* for create sysfs symlinks */ | |
915 | ||
916 | /* private memory initialize */ | |
18bd6dd1 | 917 | priv->ks_sdio_card = card; |
53638cef | 918 | |
13a9930d WS |
919 | priv->dev_state = DEVICE_STATE_PREBOOT; |
920 | priv->net_dev = netdev; | |
921 | priv->firmware_version[0] = '\0'; | |
922 | priv->version_size = 0; | |
e61e73d7 | 923 | priv->last_doze = jiffies; |
13a9930d WS |
924 | priv->last_wakeup = jiffies; |
925 | memset(&priv->nstats, 0, sizeof(priv->nstats)); | |
926 | memset(&priv->wstats, 0, sizeof(priv->wstats)); | |
927 | ||
928 | /* sleep mode */ | |
cdf6ecc5 WS |
929 | atomic_set(&priv->sleepstatus.doze_request, 0); |
930 | atomic_set(&priv->sleepstatus.wakeup_request, 0); | |
931 | atomic_set(&priv->sleepstatus.wakeup_request, 0); | |
13a9930d WS |
932 | |
933 | trx_device_init(priv); | |
934 | hostif_init(priv); | |
cdf6ecc5 | 935 | ks_wlan_net_start(netdev); |
13a9930d | 936 | |
6ee9169b | 937 | ks7010_init_defaults(priv); |
13a9930d | 938 | |
ed246b9e | 939 | ret = ks7010_upload_firmware(card); |
cdf6ecc5 | 940 | if (ret) { |
9887b5e5 SG |
941 | dev_err(&card->func->dev, |
942 | "ks7010: firmware load failed !! return code = %d\n", | |
943 | ret); | |
cf10e78e | 944 | goto err_free_netdev; |
13a9930d WS |
945 | } |
946 | ||
947 | /* interrupt setting */ | |
948 | /* clear Interrupt status write (ARMtoSD_InterruptPending FN1:00_0024) */ | |
949 | rw_data = 0xff; | |
950 | sdio_claim_host(func); | |
951 | ret = ks7010_sdio_write(priv, INT_PENDING, &rw_data, sizeof(rw_data)); | |
952 | sdio_release_host(func); | |
53638cef | 953 | if (ret) |
13a9930d | 954 | DPRINTK(1, " error : INT_PENDING=%02X\n", rw_data); |
53638cef | 955 | |
13a9930d WS |
956 | DPRINTK(4, " clear Interrupt : INT_PENDING=%02X\n", rw_data); |
957 | ||
13a9930d | 958 | /* enable ks7010sdio interrupt (INT_GCR_B|INT_READ_STATUS|INT_WRITE_STATUS) */ |
cdf6ecc5 | 959 | rw_data = (INT_GCR_B | INT_READ_STATUS | INT_WRITE_STATUS); |
13a9930d WS |
960 | sdio_claim_host(func); |
961 | ret = ks7010_sdio_write(priv, INT_ENABLE, &rw_data, sizeof(rw_data)); | |
962 | sdio_release_host(func); | |
53638cef | 963 | if (ret) |
f283dd69 | 964 | DPRINTK(1, " err : INT_ENABLE=%02X\n", rw_data); |
53638cef | 965 | |
13a9930d WS |
966 | DPRINTK(4, " enable Interrupt : INT_ENABLE=%02X\n", rw_data); |
967 | priv->dev_state = DEVICE_STATE_BOOT; | |
968 | ||
18bd6dd1 TH |
969 | priv->wq = create_workqueue("wq"); |
970 | if (!priv->wq) { | |
13a9930d | 971 | DPRINTK(1, "create_workqueue failed !!\n"); |
cf10e78e | 972 | goto err_free_netdev; |
13a9930d WS |
973 | } |
974 | ||
18bd6dd1 | 975 | INIT_DELAYED_WORK(&priv->rw_dwork, ks7010_rw_function); |
e8593a8a | 976 | ks7010_card_init(priv); |
13a9930d | 977 | |
3fb54d75 WS |
978 | ret = register_netdev(priv->net_dev); |
979 | if (ret) | |
cf10e78e | 980 | goto err_free_netdev; |
3fb54d75 | 981 | |
13a9930d WS |
982 | return 0; |
983 | ||
f283dd69 | 984 | err_free_netdev: |
13a9930d WS |
985 | free_netdev(priv->net_dev); |
986 | card->priv = NULL; | |
f283dd69 | 987 | err_release_irq: |
13a9930d WS |
988 | sdio_claim_host(func); |
989 | sdio_release_irq(func); | |
f283dd69 | 990 | err_disable_func: |
13a9930d | 991 | sdio_disable_func(func); |
f283dd69 | 992 | err_free_card: |
13a9930d WS |
993 | sdio_release_host(func); |
994 | sdio_set_drvdata(func, NULL); | |
995 | kfree(card); | |
2801d7a2 | 996 | |
13a9930d WS |
997 | return -ENODEV; |
998 | } | |
999 | ||
2ab6fd59 TH |
1000 | /* send stop request to MAC */ |
1001 | static int send_stop_request(struct sdio_func *func) | |
1002 | { | |
1003 | struct hostif_stop_request_t *pp; | |
1004 | struct ks_sdio_card *card; | |
1005 | size_t size; | |
1006 | ||
1007 | card = sdio_get_drvdata(func); | |
1008 | ||
1009 | pp = kzalloc(hif_align_size(sizeof(*pp)), GFP_KERNEL); | |
1010 | if (!pp) { | |
1011 | DPRINTK(3, "allocate memory failed..\n"); | |
1012 | return -ENOMEM; | |
1013 | } | |
1014 | ||
1015 | size = sizeof(*pp) - sizeof(pp->header.size); | |
1016 | pp->header.size = cpu_to_le16((uint16_t)size); | |
1017 | pp->header.event = cpu_to_le16((uint16_t)HIF_STOP_REQ); | |
1018 | ||
1019 | sdio_claim_host(func); | |
1020 | write_to_device(card->priv, (unsigned char *)pp, | |
1021 | hif_align_size(sizeof(*pp))); | |
1022 | sdio_release_host(func); | |
1023 | ||
1024 | kfree(pp); | |
1025 | return 0; | |
1026 | } | |
1027 | ||
1028 | ||
c4730a92 | 1029 | static void ks7010_sdio_remove(struct sdio_func *func) |
13a9930d WS |
1030 | { |
1031 | int ret; | |
1032 | struct ks_sdio_card *card; | |
1033 | struct ks_wlan_private *priv; | |
697f9f7f | 1034 | |
c4730a92 | 1035 | DPRINTK(1, "ks7010_sdio_remove()\n"); |
13a9930d WS |
1036 | |
1037 | card = sdio_get_drvdata(func); | |
1038 | ||
c7e65f4d | 1039 | if (!card) |
13a9930d WS |
1040 | return; |
1041 | ||
1042 | DPRINTK(1, "priv = card->priv\n"); | |
1043 | priv = card->priv; | |
cdf6ecc5 | 1044 | if (priv) { |
803394d0 CIK |
1045 | struct net_device *netdev = priv->net_dev; |
1046 | ||
13a9930d WS |
1047 | ks_wlan_net_stop(netdev); |
1048 | DPRINTK(1, "ks_wlan_net_stop\n"); | |
1049 | ||
1050 | /* interrupt disable */ | |
1051 | sdio_claim_host(func); | |
cdf6ecc5 WS |
1052 | sdio_writeb(func, 0, INT_ENABLE, &ret); |
1053 | sdio_writeb(func, 0xff, INT_PENDING, &ret); | |
13a9930d WS |
1054 | sdio_release_host(func); |
1055 | DPRINTK(1, "interrupt disable\n"); | |
1056 | ||
2ab6fd59 TH |
1057 | ret = send_stop_request(func); |
1058 | if (ret) /* memory allocation failure */ | |
1059 | return; | |
697f9f7f | 1060 | |
13a9930d WS |
1061 | DPRINTK(1, "STOP Req\n"); |
1062 | ||
18bd6dd1 TH |
1063 | if (priv->wq) { |
1064 | flush_workqueue(priv->wq); | |
1065 | destroy_workqueue(priv->wq); | |
13a9930d | 1066 | } |
18bd6dd1 | 1067 | DPRINTK(1, "destroy_workqueue(priv->wq);\n"); |
13a9930d | 1068 | |
13a9930d WS |
1069 | hostif_exit(priv); |
1070 | DPRINTK(1, "hostif_exit\n"); | |
1071 | ||
3fb54d75 | 1072 | unregister_netdev(netdev); |
13a9930d WS |
1073 | |
1074 | trx_device_exit(priv); | |
13a9930d WS |
1075 | free_netdev(priv->net_dev); |
1076 | card->priv = NULL; | |
1077 | } | |
1078 | ||
1079 | sdio_claim_host(func); | |
1080 | sdio_release_irq(func); | |
1081 | DPRINTK(1, "sdio_release_irq()\n"); | |
1082 | sdio_disable_func(func); | |
1083 | DPRINTK(1, "sdio_disable_func()\n"); | |
1084 | sdio_release_host(func); | |
1085 | ||
1086 | sdio_set_drvdata(func, NULL); | |
1087 | ||
1088 | kfree(card); | |
1089 | DPRINTK(1, "kfree()\n"); | |
1090 | ||
cdf6ecc5 | 1091 | DPRINTK(5, " Bye !!\n"); |
13a9930d WS |
1092 | } |
1093 | ||
4c0d46d2 WS |
1094 | static struct sdio_driver ks7010_sdio_driver = { |
1095 | .name = "ks7010_sdio", | |
1096 | .id_table = ks7010_sdio_ids, | |
1097 | .probe = ks7010_sdio_probe, | |
1098 | .remove = ks7010_sdio_remove, | |
1099 | }; | |
1100 | ||
6b0cb0b0 | 1101 | module_driver(ks7010_sdio_driver, sdio_register_driver, sdio_unregister_driver); |
e1240140 WS |
1102 | MODULE_AUTHOR("Sang Engineering, Qi-Hardware, KeyStream"); |
1103 | MODULE_DESCRIPTION("Driver for KeyStream KS7010 based SDIO cards"); | |
1104 | MODULE_LICENSE("GPL v2"); | |
1105 | MODULE_FIRMWARE(ROM_FILE); |