]>
Commit | Line | Data |
---|---|---|
247e9cff SA |
1 | /* |
2 | * Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved. | |
3 | * | |
4 | * This software is licensed under the terms of the GNU General Public | |
5 | * License version 2, as published by the Free Software Foundation, and | |
6 | * may be copied, distributed, and modified under those terms. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/kernel.h> | |
247e9cff SA |
17 | |
18 | #include <linux/mmc/core.h> | |
19 | #include <linux/mmc/card.h> | |
20 | #include <linux/mmc/sdio_func.h> | |
21 | #include <linux/mmc/sdio_ids.h> | |
22 | ||
23 | #include "gdm_sdio.h" | |
24 | #include "gdm_wimax.h" | |
25 | #include "sdio_boot.h" | |
26 | #include "hci.h" | |
27 | ||
28 | #define TYPE_A_HEADER_SIZE 4 | |
29 | #define TYPE_A_LOOKAHEAD_SIZE 16 | |
30 | ||
31 | #define MAX_NR_RX_BUF 4 | |
32 | ||
33 | #define SDU_TX_BUF_SIZE 2048 | |
ac1a3bfa | 34 | #define TX_BUF_SIZE 2048 |
247e9cff | 35 | #define TX_CHUNK_SIZE (2048 - TYPE_A_HEADER_SIZE) |
ac1a3bfa | 36 | #define RX_BUF_SIZE (25*1024) |
247e9cff | 37 | |
ac1a3bfa | 38 | #define TX_HZ 2000 |
247e9cff SA |
39 | #define TX_INTERVAL (1000000/TX_HZ) |
40 | ||
247e9cff SA |
41 | static struct sdio_tx *alloc_tx_struct(struct tx_cxt *tx) |
42 | { | |
129575f2 | 43 | struct sdio_tx *t = kzalloc(sizeof(*t), GFP_ATOMIC); |
247e9cff | 44 | |
129575f2 BC |
45 | if (!t) |
46 | return NULL; | |
247e9cff | 47 | |
247e9cff | 48 | t->buf = kmalloc(TX_BUF_SIZE, GFP_ATOMIC); |
129575f2 BC |
49 | if (!t->buf) { |
50 | kfree(t); | |
51 | return NULL; | |
52 | } | |
247e9cff SA |
53 | |
54 | t->tx_cxt = tx; | |
55 | ||
56 | return t; | |
247e9cff SA |
57 | } |
58 | ||
59 | static void free_tx_struct(struct sdio_tx *t) | |
60 | { | |
61 | if (t) { | |
62 | kfree(t->buf); | |
63 | kfree(t); | |
64 | } | |
65 | } | |
66 | ||
67 | static struct sdio_rx *alloc_rx_struct(struct rx_cxt *rx) | |
68 | { | |
129575f2 | 69 | struct sdio_rx *r = kzalloc(sizeof(*r), GFP_ATOMIC); |
247e9cff | 70 | |
129575f2 BC |
71 | if (r) |
72 | r->rx_cxt = rx; | |
247e9cff SA |
73 | |
74 | return r; | |
247e9cff SA |
75 | } |
76 | ||
77 | static void free_rx_struct(struct sdio_rx *r) | |
78 | { | |
79 | kfree(r); | |
80 | } | |
81 | ||
82 | /* Before this function is called, spin lock should be locked. */ | |
83 | static struct sdio_tx *get_tx_struct(struct tx_cxt *tx, int *no_spc) | |
84 | { | |
85 | struct sdio_tx *t; | |
86 | ||
87 | if (list_empty(&tx->free_list)) | |
88 | return NULL; | |
89 | ||
90 | t = list_entry(tx->free_list.prev, struct sdio_tx, list); | |
91 | list_del(&t->list); | |
92 | ||
93 | *no_spc = list_empty(&tx->free_list) ? 1 : 0; | |
94 | ||
95 | return t; | |
96 | } | |
97 | ||
98 | /* Before this function is called, spin lock should be locked. */ | |
99 | static void put_tx_struct(struct tx_cxt *tx, struct sdio_tx *t) | |
100 | { | |
101 | list_add_tail(&t->list, &tx->free_list); | |
102 | } | |
103 | ||
104 | /* Before this function is called, spin lock should be locked. */ | |
105 | static struct sdio_rx *get_rx_struct(struct rx_cxt *rx) | |
106 | { | |
107 | struct sdio_rx *r; | |
108 | ||
109 | if (list_empty(&rx->free_list)) | |
110 | return NULL; | |
111 | ||
112 | r = list_entry(rx->free_list.prev, struct sdio_rx, list); | |
113 | list_del(&r->list); | |
114 | ||
115 | return r; | |
116 | } | |
117 | ||
118 | /* Before this function is called, spin lock should be locked. */ | |
119 | static void put_rx_struct(struct rx_cxt *rx, struct sdio_rx *r) | |
120 | { | |
121 | list_add_tail(&r->list, &rx->free_list); | |
122 | } | |
123 | ||
1aa8ae70 MP |
124 | static void release_sdio(struct sdiowm_dev *sdev) |
125 | { | |
126 | struct tx_cxt *tx = &sdev->tx; | |
127 | struct rx_cxt *rx = &sdev->rx; | |
128 | struct sdio_tx *t, *t_next; | |
129 | struct sdio_rx *r, *r_next; | |
130 | ||
131 | kfree(tx->sdu_buf); | |
132 | ||
133 | list_for_each_entry_safe(t, t_next, &tx->free_list, list) { | |
134 | list_del(&t->list); | |
135 | free_tx_struct(t); | |
136 | } | |
137 | ||
138 | list_for_each_entry_safe(t, t_next, &tx->sdu_list, list) { | |
139 | list_del(&t->list); | |
140 | free_tx_struct(t); | |
141 | } | |
142 | ||
143 | list_for_each_entry_safe(t, t_next, &tx->hci_list, list) { | |
144 | list_del(&t->list); | |
145 | free_tx_struct(t); | |
146 | } | |
147 | ||
148 | kfree(rx->rx_buf); | |
149 | ||
150 | list_for_each_entry_safe(r, r_next, &rx->free_list, list) { | |
151 | list_del(&r->list); | |
152 | free_rx_struct(r); | |
153 | } | |
154 | ||
155 | list_for_each_entry_safe(r, r_next, &rx->req_list, list) { | |
156 | list_del(&r->list); | |
157 | free_rx_struct(r); | |
158 | } | |
159 | } | |
160 | ||
247e9cff SA |
161 | static int init_sdio(struct sdiowm_dev *sdev) |
162 | { | |
163 | int ret = 0, i; | |
ac1a3bfa MP |
164 | struct tx_cxt *tx = &sdev->tx; |
165 | struct rx_cxt *rx = &sdev->rx; | |
166 | struct sdio_tx *t; | |
167 | struct sdio_rx *r; | |
247e9cff SA |
168 | |
169 | INIT_LIST_HEAD(&tx->free_list); | |
170 | INIT_LIST_HEAD(&tx->sdu_list); | |
171 | INIT_LIST_HEAD(&tx->hci_list); | |
172 | ||
173 | spin_lock_init(&tx->lock); | |
174 | ||
175 | tx->sdu_buf = kmalloc(SDU_TX_BUF_SIZE, GFP_KERNEL); | |
78110bb8 | 176 | if (tx->sdu_buf == NULL) |
247e9cff | 177 | goto fail; |
247e9cff SA |
178 | |
179 | for (i = 0; i < MAX_NR_SDU_BUF; i++) { | |
180 | t = alloc_tx_struct(tx); | |
181 | if (t == NULL) { | |
182 | ret = -ENOMEM; | |
183 | goto fail; | |
184 | } | |
185 | list_add(&t->list, &tx->free_list); | |
186 | } | |
187 | ||
188 | INIT_LIST_HEAD(&rx->free_list); | |
189 | INIT_LIST_HEAD(&rx->req_list); | |
190 | ||
191 | spin_lock_init(&rx->lock); | |
192 | ||
193 | for (i = 0; i < MAX_NR_RX_BUF; i++) { | |
194 | r = alloc_rx_struct(rx); | |
195 | if (r == NULL) { | |
196 | ret = -ENOMEM; | |
197 | goto fail; | |
198 | } | |
199 | list_add(&r->list, &rx->free_list); | |
200 | } | |
201 | ||
202 | rx->rx_buf = kmalloc(RX_BUF_SIZE, GFP_KERNEL); | |
78110bb8 | 203 | if (rx->rx_buf == NULL) |
247e9cff | 204 | goto fail; |
247e9cff SA |
205 | |
206 | return 0; | |
207 | ||
208 | fail: | |
209 | release_sdio(sdev); | |
210 | return ret; | |
211 | } | |
212 | ||
247e9cff SA |
213 | static void send_sdio_pkt(struct sdio_func *func, u8 *data, int len) |
214 | { | |
215 | int n, blocks, ret, remain; | |
216 | ||
217 | sdio_claim_host(func); | |
218 | ||
219 | blocks = len / func->cur_blksize; | |
220 | n = blocks * func->cur_blksize; | |
221 | if (blocks) { | |
222 | ret = sdio_memcpy_toio(func, 0, data, n); | |
223 | if (ret < 0) { | |
224 | if (ret != -ENOMEDIUM) | |
c9a796d6 | 225 | dev_err(&func->dev, |
99beca13 | 226 | "gdmwms: error: ret = %d\n", ret); |
247e9cff SA |
227 | goto end_io; |
228 | } | |
229 | } | |
230 | ||
231 | remain = len - n; | |
232 | remain = (remain + 3) & ~3; | |
233 | ||
234 | if (remain) { | |
235 | ret = sdio_memcpy_toio(func, 0, data + n, remain); | |
236 | if (ret < 0) { | |
237 | if (ret != -ENOMEDIUM) | |
c9a796d6 | 238 | dev_err(&func->dev, |
99beca13 | 239 | "gdmwms: error: ret = %d\n", ret); |
247e9cff SA |
240 | goto end_io; |
241 | } | |
242 | } | |
243 | ||
244 | end_io: | |
245 | sdio_release_host(func); | |
246 | } | |
247 | ||
248 | static void send_sdu(struct sdio_func *func, struct tx_cxt *tx) | |
249 | { | |
250 | struct list_head *l, *next; | |
251 | struct hci_s *hci; | |
252 | struct sdio_tx *t; | |
253 | int pos, len, i, estlen, aggr_num = 0, aggr_len; | |
254 | u8 *buf; | |
255 | unsigned long flags; | |
256 | ||
257 | spin_lock_irqsave(&tx->lock, flags); | |
258 | ||
259 | pos = TYPE_A_HEADER_SIZE + HCI_HEADER_SIZE; | |
260 | list_for_each_entry(t, &tx->sdu_list, list) { | |
261 | estlen = ((t->len + 3) & ~3) + 4; | |
262 | if ((pos + estlen) > SDU_TX_BUF_SIZE) | |
263 | break; | |
264 | ||
265 | aggr_num++; | |
266 | memcpy(tx->sdu_buf + pos, t->buf, t->len); | |
267 | memset(tx->sdu_buf + pos + t->len, 0, estlen - t->len); | |
268 | pos += estlen; | |
269 | } | |
270 | aggr_len = pos; | |
271 | ||
272 | hci = (struct hci_s *)(tx->sdu_buf + TYPE_A_HEADER_SIZE); | |
a8a175d9 BC |
273 | hci->cmd_evt = cpu_to_be16(WIMAX_TX_SDU_AGGR); |
274 | hci->length = cpu_to_be16(aggr_len - TYPE_A_HEADER_SIZE - | |
275 | HCI_HEADER_SIZE); | |
247e9cff SA |
276 | |
277 | spin_unlock_irqrestore(&tx->lock, flags); | |
278 | ||
75533a03 | 279 | dev_dbg(&func->dev, "sdio_send: %*ph\n", aggr_len - TYPE_A_HEADER_SIZE, |
f5439c61 | 280 | tx->sdu_buf + TYPE_A_HEADER_SIZE); |
247e9cff SA |
281 | |
282 | for (pos = TYPE_A_HEADER_SIZE; pos < aggr_len; pos += TX_CHUNK_SIZE) { | |
283 | len = aggr_len - pos; | |
284 | len = len > TX_CHUNK_SIZE ? TX_CHUNK_SIZE : len; | |
285 | buf = tx->sdu_buf + pos - TYPE_A_HEADER_SIZE; | |
286 | ||
287 | buf[0] = len & 0xff; | |
288 | buf[1] = (len >> 8) & 0xff; | |
289 | buf[2] = (len >> 16) & 0xff; | |
290 | buf[3] = (pos + len) >= aggr_len ? 0 : 1; | |
291 | send_sdio_pkt(func, buf, len + TYPE_A_HEADER_SIZE); | |
292 | } | |
293 | ||
294 | spin_lock_irqsave(&tx->lock, flags); | |
295 | ||
296 | for (l = tx->sdu_list.next, i = 0; i < aggr_num; i++, l = next) { | |
297 | next = l->next; | |
298 | t = list_entry(l, struct sdio_tx, list); | |
299 | if (t->callback) | |
300 | t->callback(t->cb_data); | |
301 | ||
302 | list_del(l); | |
303 | put_tx_struct(t->tx_cxt, t); | |
304 | } | |
305 | ||
306 | do_gettimeofday(&tx->sdu_stamp); | |
307 | spin_unlock_irqrestore(&tx->lock, flags); | |
308 | } | |
309 | ||
a3709f7a DG |
310 | static void send_hci(struct sdio_func *func, struct tx_cxt *tx, |
311 | struct sdio_tx *t) | |
247e9cff SA |
312 | { |
313 | unsigned long flags; | |
314 | ||
75533a03 | 315 | dev_dbg(&func->dev, "sdio_send: %*ph\n", t->len - TYPE_A_HEADER_SIZE, |
f5439c61 MP |
316 | t->buf + TYPE_A_HEADER_SIZE); |
317 | ||
247e9cff SA |
318 | send_sdio_pkt(func, t->buf, t->len); |
319 | ||
320 | spin_lock_irqsave(&tx->lock, flags); | |
321 | if (t->callback) | |
322 | t->callback(t->cb_data); | |
323 | free_tx_struct(t); | |
324 | spin_unlock_irqrestore(&tx->lock, flags); | |
325 | } | |
326 | ||
327 | static void do_tx(struct work_struct *work) | |
328 | { | |
329 | struct sdiowm_dev *sdev = container_of(work, struct sdiowm_dev, ws); | |
330 | struct sdio_func *func = sdev->func; | |
331 | struct tx_cxt *tx = &sdev->tx; | |
332 | struct sdio_tx *t = NULL; | |
333 | struct timeval now, *before; | |
334 | int is_sdu = 0; | |
335 | long diff; | |
336 | unsigned long flags; | |
337 | ||
338 | spin_lock_irqsave(&tx->lock, flags); | |
339 | if (!tx->can_send) { | |
340 | spin_unlock_irqrestore(&tx->lock, flags); | |
341 | return; | |
342 | } | |
343 | ||
344 | if (!list_empty(&tx->hci_list)) { | |
345 | t = list_entry(tx->hci_list.next, struct sdio_tx, list); | |
346 | list_del(&t->list); | |
347 | is_sdu = 0; | |
348 | } else if (!tx->stop_sdu_tx && !list_empty(&tx->sdu_list)) { | |
349 | do_gettimeofday(&now); | |
350 | before = &tx->sdu_stamp; | |
351 | ||
352 | diff = (now.tv_sec - before->tv_sec) * 1000000 + | |
353 | (now.tv_usec - before->tv_usec); | |
354 | if (diff >= 0 && diff < TX_INTERVAL) { | |
355 | schedule_work(&sdev->ws); | |
356 | spin_unlock_irqrestore(&tx->lock, flags); | |
357 | return; | |
358 | } | |
359 | is_sdu = 1; | |
360 | } | |
361 | ||
362 | if (!is_sdu && t == NULL) { | |
363 | spin_unlock_irqrestore(&tx->lock, flags); | |
364 | return; | |
365 | } | |
366 | ||
367 | tx->can_send = 0; | |
368 | ||
369 | spin_unlock_irqrestore(&tx->lock, flags); | |
370 | ||
371 | if (is_sdu) | |
372 | send_sdu(func, tx); | |
373 | else | |
374 | send_hci(func, tx, t); | |
375 | } | |
376 | ||
377 | static int gdm_sdio_send(void *priv_dev, void *data, int len, | |
39c511f8 | 378 | void (*cb)(void *data), void *cb_data) |
247e9cff SA |
379 | { |
380 | struct sdiowm_dev *sdev = priv_dev; | |
381 | struct tx_cxt *tx = &sdev->tx; | |
382 | struct sdio_tx *t; | |
383 | u8 *pkt = data; | |
384 | int no_spc = 0; | |
385 | u16 cmd_evt; | |
386 | unsigned long flags; | |
387 | ||
0fbce84c BC |
388 | if (len > TX_BUF_SIZE - TYPE_A_HEADER_SIZE) |
389 | return -EINVAL; | |
247e9cff SA |
390 | |
391 | spin_lock_irqsave(&tx->lock, flags); | |
392 | ||
393 | cmd_evt = (pkt[0] << 8) | pkt[1]; | |
394 | if (cmd_evt == WIMAX_TX_SDU) { | |
395 | t = get_tx_struct(tx, &no_spc); | |
396 | if (t == NULL) { | |
397 | /* This case must not happen. */ | |
398 | spin_unlock_irqrestore(&tx->lock, flags); | |
399 | return -ENOSPC; | |
400 | } | |
401 | list_add_tail(&t->list, &tx->sdu_list); | |
402 | ||
403 | memcpy(t->buf, data, len); | |
404 | ||
405 | t->len = len; | |
406 | t->callback = cb; | |
407 | t->cb_data = cb_data; | |
408 | } else { | |
409 | t = alloc_tx_struct(tx); | |
410 | if (t == NULL) { | |
411 | spin_unlock_irqrestore(&tx->lock, flags); | |
412 | return -ENOMEM; | |
413 | } | |
414 | list_add_tail(&t->list, &tx->hci_list); | |
415 | ||
416 | t->buf[0] = len & 0xff; | |
417 | t->buf[1] = (len >> 8) & 0xff; | |
418 | t->buf[2] = (len >> 16) & 0xff; | |
419 | t->buf[3] = 2; | |
420 | memcpy(t->buf + TYPE_A_HEADER_SIZE, data, len); | |
421 | ||
422 | t->len = len + TYPE_A_HEADER_SIZE; | |
423 | t->callback = cb; | |
424 | t->cb_data = cb_data; | |
425 | } | |
426 | ||
427 | if (tx->can_send) | |
428 | schedule_work(&sdev->ws); | |
429 | ||
430 | spin_unlock_irqrestore(&tx->lock, flags); | |
431 | ||
432 | if (no_spc) | |
433 | return -ENOSPC; | |
434 | ||
435 | return 0; | |
436 | } | |
437 | ||
e7d374e7 | 438 | /* Handle the HCI, WIMAX_SDU_TX_FLOW. */ |
247e9cff SA |
439 | static int control_sdu_tx_flow(struct sdiowm_dev *sdev, u8 *hci_data, int len) |
440 | { | |
441 | struct tx_cxt *tx = &sdev->tx; | |
442 | u16 cmd_evt; | |
443 | unsigned long flags; | |
444 | ||
445 | spin_lock_irqsave(&tx->lock, flags); | |
446 | ||
447 | cmd_evt = (hci_data[0] << 8) | (hci_data[1]); | |
448 | if (cmd_evt != WIMAX_SDU_TX_FLOW) | |
449 | goto out; | |
450 | ||
451 | if (hci_data[4] == 0) { | |
8943a92f | 452 | dev_dbg(&sdev->func->dev, "WIMAX ==> STOP SDU TX\n"); |
247e9cff SA |
453 | tx->stop_sdu_tx = 1; |
454 | } else if (hci_data[4] == 1) { | |
8943a92f | 455 | dev_dbg(&sdev->func->dev, "WIMAX ==> START SDU TX\n"); |
247e9cff SA |
456 | tx->stop_sdu_tx = 0; |
457 | if (tx->can_send) | |
458 | schedule_work(&sdev->ws); | |
e7d374e7 | 459 | /* If free buffer for sdu tx doesn't exist, then tx queue |
247e9cff SA |
460 | * should not be woken. For this reason, don't pass the command, |
461 | * START_SDU_TX. | |
462 | */ | |
463 | if (list_empty(&tx->free_list)) | |
464 | len = 0; | |
465 | } | |
466 | ||
467 | out: | |
468 | spin_unlock_irqrestore(&tx->lock, flags); | |
469 | return len; | |
470 | } | |
471 | ||
472 | static void gdm_sdio_irq(struct sdio_func *func) | |
473 | { | |
474 | struct phy_dev *phy_dev = sdio_get_drvdata(func); | |
475 | struct sdiowm_dev *sdev = phy_dev->priv_dev; | |
476 | struct tx_cxt *tx = &sdev->tx; | |
477 | struct rx_cxt *rx = &sdev->rx; | |
478 | struct sdio_rx *r; | |
479 | unsigned long flags; | |
480 | u8 val, hdr[TYPE_A_LOOKAHEAD_SIZE], *buf; | |
481 | u32 len, blocks, n; | |
482 | int ret, remain; | |
483 | ||
484 | /* Check interrupt */ | |
485 | val = sdio_readb(func, 0x13, &ret); | |
486 | if (val & 0x01) | |
487 | sdio_writeb(func, 0x01, 0x13, &ret); /* clear interrupt */ | |
488 | else | |
489 | return; | |
490 | ||
491 | ret = sdio_memcpy_fromio(func, hdr, 0x0, TYPE_A_LOOKAHEAD_SIZE); | |
492 | if (ret) { | |
c9a796d6 YT |
493 | dev_err(&func->dev, |
494 | "Cannot read from function %d\n", func->num); | |
247e9cff SA |
495 | goto done; |
496 | } | |
497 | ||
498 | len = (hdr[2] << 16) | (hdr[1] << 8) | hdr[0]; | |
499 | if (len > (RX_BUF_SIZE - TYPE_A_HEADER_SIZE)) { | |
c9a796d6 | 500 | dev_err(&func->dev, "Too big Type-A size: %d\n", len); |
247e9cff SA |
501 | goto done; |
502 | } | |
503 | ||
504 | if (hdr[3] == 1) { /* Ack */ | |
247e9cff | 505 | u32 *ack_seq = (u32 *)&hdr[4]; |
39c511f8 | 506 | |
247e9cff SA |
507 | spin_lock_irqsave(&tx->lock, flags); |
508 | tx->can_send = 1; | |
509 | ||
510 | if (!list_empty(&tx->sdu_list) || !list_empty(&tx->hci_list)) | |
511 | schedule_work(&sdev->ws); | |
512 | spin_unlock_irqrestore(&tx->lock, flags); | |
8943a92f | 513 | dev_dbg(&func->dev, "Ack... %0x\n", ntohl(*ack_seq)); |
247e9cff SA |
514 | goto done; |
515 | } | |
516 | ||
517 | memcpy(rx->rx_buf, hdr + TYPE_A_HEADER_SIZE, | |
39c511f8 | 518 | TYPE_A_LOOKAHEAD_SIZE - TYPE_A_HEADER_SIZE); |
247e9cff SA |
519 | |
520 | buf = rx->rx_buf + TYPE_A_LOOKAHEAD_SIZE - TYPE_A_HEADER_SIZE; | |
521 | remain = len - TYPE_A_LOOKAHEAD_SIZE + TYPE_A_HEADER_SIZE; | |
522 | if (remain <= 0) | |
523 | goto end_io; | |
524 | ||
525 | blocks = remain / func->cur_blksize; | |
526 | ||
527 | if (blocks) { | |
528 | n = blocks * func->cur_blksize; | |
529 | ret = sdio_memcpy_fromio(func, buf, 0x0, n); | |
530 | if (ret) { | |
c9a796d6 YT |
531 | dev_err(&func->dev, |
532 | "Cannot read from function %d\n", func->num); | |
247e9cff SA |
533 | goto done; |
534 | } | |
535 | buf += n; | |
536 | remain -= n; | |
537 | } | |
538 | ||
539 | if (remain) { | |
540 | ret = sdio_memcpy_fromio(func, buf, 0x0, remain); | |
541 | if (ret) { | |
c9a796d6 YT |
542 | dev_err(&func->dev, |
543 | "Cannot read from function %d\n", func->num); | |
247e9cff SA |
544 | goto done; |
545 | } | |
546 | } | |
547 | ||
548 | end_io: | |
75533a03 | 549 | dev_dbg(&func->dev, "sdio_receive: %*ph\n", len, rx->rx_buf); |
f5439c61 | 550 | |
247e9cff SA |
551 | len = control_sdu_tx_flow(sdev, rx->rx_buf, len); |
552 | ||
553 | spin_lock_irqsave(&rx->lock, flags); | |
554 | ||
555 | if (!list_empty(&rx->req_list)) { | |
556 | r = list_entry(rx->req_list.next, struct sdio_rx, list); | |
557 | spin_unlock_irqrestore(&rx->lock, flags); | |
558 | if (r->callback) | |
559 | r->callback(r->cb_data, rx->rx_buf, len); | |
560 | spin_lock_irqsave(&rx->lock, flags); | |
561 | list_del(&r->list); | |
562 | put_rx_struct(rx, r); | |
563 | } | |
564 | ||
565 | spin_unlock_irqrestore(&rx->lock, flags); | |
566 | ||
567 | done: | |
568 | sdio_writeb(func, 0x00, 0x10, &ret); /* PCRRT */ | |
569 | if (!phy_dev->netdev) | |
54bc1ff1 | 570 | register_wimax_device(phy_dev, &func->dev); |
247e9cff SA |
571 | } |
572 | ||
573 | static int gdm_sdio_receive(void *priv_dev, | |
39c511f8 MP |
574 | void (*cb)(void *cb_data, void *data, int len), |
575 | void *cb_data) | |
247e9cff SA |
576 | { |
577 | struct sdiowm_dev *sdev = priv_dev; | |
578 | struct rx_cxt *rx = &sdev->rx; | |
579 | struct sdio_rx *r; | |
580 | unsigned long flags; | |
581 | ||
582 | spin_lock_irqsave(&rx->lock, flags); | |
583 | r = get_rx_struct(rx); | |
584 | if (r == NULL) { | |
585 | spin_unlock_irqrestore(&rx->lock, flags); | |
586 | return -ENOMEM; | |
587 | } | |
588 | ||
589 | r->callback = cb; | |
590 | r->cb_data = cb_data; | |
591 | ||
592 | list_add_tail(&r->list, &rx->req_list); | |
593 | spin_unlock_irqrestore(&rx->lock, flags); | |
594 | ||
595 | return 0; | |
596 | } | |
597 | ||
a3709f7a DG |
598 | static int sdio_wimax_probe(struct sdio_func *func, |
599 | const struct sdio_device_id *id) | |
247e9cff SA |
600 | { |
601 | int ret; | |
602 | struct phy_dev *phy_dev = NULL; | |
603 | struct sdiowm_dev *sdev = NULL; | |
604 | ||
c9a796d6 YT |
605 | dev_info(&func->dev, "Found GDM SDIO VID = 0x%04x PID = 0x%04x...\n", |
606 | func->vendor, func->device); | |
607 | dev_info(&func->dev, "GCT WiMax driver version %s\n", DRIVER_VERSION); | |
247e9cff SA |
608 | |
609 | sdio_claim_host(func); | |
610 | sdio_enable_func(func); | |
611 | sdio_claim_irq(func, gdm_sdio_irq); | |
612 | ||
613 | ret = sdio_boot(func); | |
614 | if (ret) | |
615 | return ret; | |
616 | ||
c2a1793d | 617 | phy_dev = kzalloc(sizeof(*phy_dev), GFP_KERNEL); |
247e9cff SA |
618 | if (phy_dev == NULL) { |
619 | ret = -ENOMEM; | |
620 | goto out; | |
621 | } | |
c2a1793d | 622 | sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); |
247e9cff SA |
623 | if (sdev == NULL) { |
624 | ret = -ENOMEM; | |
625 | goto out; | |
626 | } | |
627 | ||
247e9cff SA |
628 | phy_dev->priv_dev = (void *)sdev; |
629 | phy_dev->send_func = gdm_sdio_send; | |
630 | phy_dev->rcv_func = gdm_sdio_receive; | |
631 | ||
632 | ret = init_sdio(sdev); | |
f1efd9fe | 633 | if (ret < 0) |
247e9cff SA |
634 | goto out; |
635 | ||
636 | sdev->func = func; | |
637 | ||
638 | sdio_writeb(func, 1, 0x14, &ret); /* Enable interrupt */ | |
639 | sdio_release_host(func); | |
640 | ||
641 | INIT_WORK(&sdev->ws, do_tx); | |
642 | ||
643 | sdio_set_drvdata(func, phy_dev); | |
644 | out: | |
645 | if (ret) { | |
646 | kfree(phy_dev); | |
647 | kfree(sdev); | |
648 | } | |
649 | ||
650 | return ret; | |
651 | } | |
652 | ||
653 | static void sdio_wimax_remove(struct sdio_func *func) | |
654 | { | |
655 | struct phy_dev *phy_dev = sdio_get_drvdata(func); | |
656 | struct sdiowm_dev *sdev = phy_dev->priv_dev; | |
657 | ||
c0352cb7 | 658 | cancel_work_sync(&sdev->ws); |
247e9cff SA |
659 | if (phy_dev->netdev) |
660 | unregister_wimax_device(phy_dev); | |
661 | sdio_claim_host(func); | |
662 | sdio_release_irq(func); | |
663 | sdio_disable_func(func); | |
664 | sdio_release_host(func); | |
665 | release_sdio(sdev); | |
666 | ||
667 | kfree(sdev); | |
668 | kfree(phy_dev); | |
669 | } | |
670 | ||
671 | static const struct sdio_device_id sdio_wimax_ids[] = { | |
672 | { SDIO_DEVICE(0x0296, 0x5347) }, | |
673 | {0} | |
674 | }; | |
675 | ||
676 | MODULE_DEVICE_TABLE(sdio, sdio_wimax_ids); | |
677 | ||
678 | static struct sdio_driver sdio_wimax_driver = { | |
679 | .probe = sdio_wimax_probe, | |
680 | .remove = sdio_wimax_remove, | |
681 | .name = "sdio_wimax", | |
682 | .id_table = sdio_wimax_ids, | |
683 | }; | |
684 | ||
685 | static int __init sdio_gdm_wimax_init(void) | |
686 | { | |
687 | return sdio_register_driver(&sdio_wimax_driver); | |
688 | } | |
689 | ||
690 | static void __exit sdio_gdm_wimax_exit(void) | |
691 | { | |
692 | sdio_unregister_driver(&sdio_wimax_driver); | |
693 | } | |
694 | ||
695 | module_init(sdio_gdm_wimax_init); | |
696 | module_exit(sdio_gdm_wimax_exit); | |
697 | ||
698 | MODULE_VERSION(DRIVER_VERSION); | |
699 | MODULE_DESCRIPTION("GCT WiMax SDIO Device Driver"); | |
700 | MODULE_AUTHOR("Ethan Park"); | |
701 | MODULE_LICENSE("GPL"); |