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