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