diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/sdio.c')
-rw-r--r-- | drivers/net/wireless/ath/ath6kl/sdio.c | 853 |
1 files changed, 853 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c new file mode 100644 index 000000000000..b38732aaf41a --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/sdio.c | |||
@@ -0,0 +1,853 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2004-2011 Atheros Communications Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/mmc/card.h> | ||
18 | #include <linux/mmc/mmc.h> | ||
19 | #include <linux/mmc/host.h> | ||
20 | #include <linux/mmc/sdio_func.h> | ||
21 | #include <linux/mmc/sdio_ids.h> | ||
22 | #include <linux/mmc/sdio.h> | ||
23 | #include <linux/mmc/sd.h> | ||
24 | #include "htc_hif.h" | ||
25 | #include "hif-ops.h" | ||
26 | #include "target.h" | ||
27 | #include "debug.h" | ||
28 | |||
29 | struct ath6kl_sdio { | ||
30 | struct sdio_func *func; | ||
31 | |||
32 | spinlock_t lock; | ||
33 | |||
34 | /* free list */ | ||
35 | struct list_head bus_req_freeq; | ||
36 | |||
37 | /* available bus requests */ | ||
38 | struct bus_request bus_req[BUS_REQUEST_MAX_NUM]; | ||
39 | |||
40 | struct ath6kl *ar; | ||
41 | u8 *dma_buffer; | ||
42 | |||
43 | /* scatter request list head */ | ||
44 | struct list_head scat_req; | ||
45 | |||
46 | spinlock_t scat_lock; | ||
47 | bool is_disabled; | ||
48 | atomic_t irq_handling; | ||
49 | const struct sdio_device_id *id; | ||
50 | struct work_struct wr_async_work; | ||
51 | struct list_head wr_asyncq; | ||
52 | spinlock_t wr_async_lock; | ||
53 | }; | ||
54 | |||
55 | #define CMD53_ARG_READ 0 | ||
56 | #define CMD53_ARG_WRITE 1 | ||
57 | #define CMD53_ARG_BLOCK_BASIS 1 | ||
58 | #define CMD53_ARG_FIXED_ADDRESS 0 | ||
59 | #define CMD53_ARG_INCR_ADDRESS 1 | ||
60 | |||
61 | static inline struct ath6kl_sdio *ath6kl_sdio_priv(struct ath6kl *ar) | ||
62 | { | ||
63 | return ar->hif_priv; | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * Macro to check if DMA buffer is WORD-aligned and DMA-able. | ||
68 | * Most host controllers assume the buffer is DMA'able and will | ||
69 | * bug-check otherwise (i.e. buffers on the stack). virt_addr_valid | ||
70 | * check fails on stack memory. | ||
71 | */ | ||
72 | static inline bool buf_needs_bounce(u8 *buf) | ||
73 | { | ||
74 | return ((unsigned long) buf & 0x3) || !virt_addr_valid(buf); | ||
75 | } | ||
76 | |||
77 | static void ath6kl_sdio_set_mbox_info(struct ath6kl *ar) | ||
78 | { | ||
79 | struct ath6kl_mbox_info *mbox_info = &ar->mbox_info; | ||
80 | |||
81 | /* EP1 has an extended range */ | ||
82 | mbox_info->htc_addr = HIF_MBOX_BASE_ADDR; | ||
83 | mbox_info->htc_ext_addr = HIF_MBOX0_EXT_BASE_ADDR; | ||
84 | mbox_info->htc_ext_sz = HIF_MBOX0_EXT_WIDTH; | ||
85 | mbox_info->block_size = HIF_MBOX_BLOCK_SIZE; | ||
86 | mbox_info->gmbox_addr = HIF_GMBOX_BASE_ADDR; | ||
87 | mbox_info->gmbox_sz = HIF_GMBOX_WIDTH; | ||
88 | } | ||
89 | |||
90 | static inline void ath6kl_sdio_set_cmd53_arg(u32 *arg, u8 rw, u8 func, | ||
91 | u8 mode, u8 opcode, u32 addr, | ||
92 | u16 blksz) | ||
93 | { | ||
94 | *arg = (((rw & 1) << 31) | | ||
95 | ((func & 0x7) << 28) | | ||
96 | ((mode & 1) << 27) | | ||
97 | ((opcode & 1) << 26) | | ||
98 | ((addr & 0x1FFFF) << 9) | | ||
99 | (blksz & 0x1FF)); | ||
100 | } | ||
101 | |||
102 | static inline void ath6kl_sdio_set_cmd52_arg(u32 *arg, u8 write, u8 raw, | ||
103 | unsigned int address, | ||
104 | unsigned char val) | ||
105 | { | ||
106 | const u8 func = 0; | ||
107 | |||
108 | *arg = ((write & 1) << 31) | | ||
109 | ((func & 0x7) << 28) | | ||
110 | ((raw & 1) << 27) | | ||
111 | (1 << 26) | | ||
112 | ((address & 0x1FFFF) << 9) | | ||
113 | (1 << 8) | | ||
114 | (val & 0xFF); | ||
115 | } | ||
116 | |||
117 | static int ath6kl_sdio_func0_cmd52_wr_byte(struct mmc_card *card, | ||
118 | unsigned int address, | ||
119 | unsigned char byte) | ||
120 | { | ||
121 | struct mmc_command io_cmd; | ||
122 | |||
123 | memset(&io_cmd, 0, sizeof(io_cmd)); | ||
124 | ath6kl_sdio_set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte); | ||
125 | io_cmd.opcode = SD_IO_RW_DIRECT; | ||
126 | io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC; | ||
127 | |||
128 | return mmc_wait_for_cmd(card->host, &io_cmd, 0); | ||
129 | } | ||
130 | |||
131 | static struct bus_request *ath6kl_sdio_alloc_busreq(struct ath6kl_sdio *ar_sdio) | ||
132 | { | ||
133 | struct bus_request *bus_req; | ||
134 | unsigned long flag; | ||
135 | |||
136 | spin_lock_irqsave(&ar_sdio->lock, flag); | ||
137 | |||
138 | if (list_empty(&ar_sdio->bus_req_freeq)) { | ||
139 | spin_unlock_irqrestore(&ar_sdio->lock, flag); | ||
140 | return NULL; | ||
141 | } | ||
142 | |||
143 | bus_req = list_first_entry(&ar_sdio->bus_req_freeq, | ||
144 | struct bus_request, list); | ||
145 | list_del(&bus_req->list); | ||
146 | |||
147 | spin_unlock_irqrestore(&ar_sdio->lock, flag); | ||
148 | ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req); | ||
149 | |||
150 | return bus_req; | ||
151 | } | ||
152 | |||
153 | static void ath6kl_sdio_free_bus_req(struct ath6kl_sdio *ar_sdio, | ||
154 | struct bus_request *bus_req) | ||
155 | { | ||
156 | unsigned long flag; | ||
157 | |||
158 | ath6kl_dbg(ATH6KL_DBG_TRC, "%s: bus request 0x%p\n", __func__, bus_req); | ||
159 | |||
160 | spin_lock_irqsave(&ar_sdio->lock, flag); | ||
161 | list_add_tail(&bus_req->list, &ar_sdio->bus_req_freeq); | ||
162 | spin_unlock_irqrestore(&ar_sdio->lock, flag); | ||
163 | } | ||
164 | |||
165 | static void ath6kl_sdio_setup_scat_data(struct hif_scatter_req *scat_req, | ||
166 | struct hif_scatter_req_priv *s_req_priv, | ||
167 | struct mmc_data *data) | ||
168 | { | ||
169 | struct scatterlist *sg; | ||
170 | int i; | ||
171 | |||
172 | data->blksz = HIF_MBOX_BLOCK_SIZE; | ||
173 | data->blocks = scat_req->len / HIF_MBOX_BLOCK_SIZE; | ||
174 | |||
175 | ath6kl_dbg(ATH6KL_DBG_SCATTER, | ||
176 | "hif-scatter: (%s) addr: 0x%X, (block len: %d, block count: %d) , (tot:%d,sg:%d)\n", | ||
177 | (scat_req->req & HIF_WRITE) ? "WR" : "RD", scat_req->addr, | ||
178 | data->blksz, data->blocks, scat_req->len, | ||
179 | scat_req->scat_entries); | ||
180 | |||
181 | data->flags = (scat_req->req & HIF_WRITE) ? MMC_DATA_WRITE : | ||
182 | MMC_DATA_READ; | ||
183 | |||
184 | /* fill SG entries */ | ||
185 | sg = s_req_priv->sgentries; | ||
186 | sg_init_table(sg, scat_req->scat_entries); | ||
187 | |||
188 | /* assemble SG list */ | ||
189 | for (i = 0; i < scat_req->scat_entries; i++, sg++) { | ||
190 | if ((unsigned long)scat_req->scat_list[i].buf & 0x3) | ||
191 | /* | ||
192 | * Some scatter engines can handle unaligned | ||
193 | * buffers, print this as informational only. | ||
194 | */ | ||
195 | ath6kl_dbg(ATH6KL_DBG_SCATTER, | ||
196 | "(%s) scatter buffer is unaligned 0x%p\n", | ||
197 | scat_req->req & HIF_WRITE ? "WR" : "RD", | ||
198 | scat_req->scat_list[i].buf); | ||
199 | |||
200 | ath6kl_dbg(ATH6KL_DBG_SCATTER, "%d: addr:0x%p, len:%d\n", | ||
201 | i, scat_req->scat_list[i].buf, | ||
202 | scat_req->scat_list[i].len); | ||
203 | |||
204 | sg_set_buf(sg, scat_req->scat_list[i].buf, | ||
205 | scat_req->scat_list[i].len); | ||
206 | } | ||
207 | |||
208 | /* set scatter-gather table for request */ | ||
209 | data->sg = s_req_priv->sgentries; | ||
210 | data->sg_len = scat_req->scat_entries; | ||
211 | } | ||
212 | |||
213 | static int ath6kl_sdio_scat_rw(struct ath6kl_sdio *ar_sdio, | ||
214 | struct bus_request *req) | ||
215 | { | ||
216 | struct mmc_request mmc_req; | ||
217 | struct mmc_command cmd; | ||
218 | struct mmc_data data; | ||
219 | struct hif_scatter_req *scat_req; | ||
220 | u8 opcode, rw; | ||
221 | int status; | ||
222 | |||
223 | scat_req = req->scat_req; | ||
224 | |||
225 | memset(&mmc_req, 0, sizeof(struct mmc_request)); | ||
226 | memset(&cmd, 0, sizeof(struct mmc_command)); | ||
227 | memset(&data, 0, sizeof(struct mmc_data)); | ||
228 | |||
229 | ath6kl_sdio_setup_scat_data(scat_req, scat_req->req_priv, &data); | ||
230 | |||
231 | opcode = (scat_req->req & HIF_FIXED_ADDRESS) ? | ||
232 | CMD53_ARG_FIXED_ADDRESS : CMD53_ARG_INCR_ADDRESS; | ||
233 | |||
234 | rw = (scat_req->req & HIF_WRITE) ? CMD53_ARG_WRITE : CMD53_ARG_READ; | ||
235 | |||
236 | /* Fixup the address so that the last byte will fall on MBOX EOM */ | ||
237 | if (scat_req->req & HIF_WRITE) { | ||
238 | if (scat_req->addr == HIF_MBOX_BASE_ADDR) | ||
239 | scat_req->addr += HIF_MBOX_WIDTH - scat_req->len; | ||
240 | else | ||
241 | /* Uses extended address range */ | ||
242 | scat_req->addr += HIF_MBOX0_EXT_WIDTH - scat_req->len; | ||
243 | } | ||
244 | |||
245 | /* set command argument */ | ||
246 | ath6kl_sdio_set_cmd53_arg(&cmd.arg, rw, ar_sdio->func->num, | ||
247 | CMD53_ARG_BLOCK_BASIS, opcode, scat_req->addr, | ||
248 | data.blocks); | ||
249 | |||
250 | cmd.opcode = SD_IO_RW_EXTENDED; | ||
251 | cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; | ||
252 | |||
253 | mmc_req.cmd = &cmd; | ||
254 | mmc_req.data = &data; | ||
255 | |||
256 | mmc_set_data_timeout(&data, ar_sdio->func->card); | ||
257 | /* synchronous call to process request */ | ||
258 | mmc_wait_for_req(ar_sdio->func->card->host, &mmc_req); | ||
259 | |||
260 | status = cmd.error ? cmd.error : data.error; | ||
261 | scat_req->status = status; | ||
262 | |||
263 | if (scat_req->status) | ||
264 | ath6kl_err("Scatter write request failed:%d\n", | ||
265 | scat_req->status); | ||
266 | |||
267 | if (scat_req->req & HIF_ASYNCHRONOUS) | ||
268 | scat_req->complete(scat_req); | ||
269 | |||
270 | return status; | ||
271 | } | ||
272 | |||
273 | |||
274 | /* callback to issue a read-write scatter request */ | ||
275 | static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar, | ||
276 | struct hif_scatter_req *scat_req) | ||
277 | { | ||
278 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | ||
279 | struct hif_scatter_req_priv *req_priv = scat_req->req_priv; | ||
280 | u32 request = scat_req->req; | ||
281 | int status = 0; | ||
282 | unsigned long flags; | ||
283 | |||
284 | if (!scat_req->len) | ||
285 | return -EINVAL; | ||
286 | |||
287 | ath6kl_dbg(ATH6KL_DBG_SCATTER, | ||
288 | "hif-scatter: total len: %d scatter entries: %d\n", | ||
289 | scat_req->len, scat_req->scat_entries); | ||
290 | |||
291 | if (request & HIF_SYNCHRONOUS) { | ||
292 | sdio_claim_host(ar_sdio->func); | ||
293 | status = ath6kl_sdio_scat_rw(ar_sdio, req_priv->busrequest); | ||
294 | sdio_release_host(ar_sdio->func); | ||
295 | } else { | ||
296 | spin_lock_irqsave(&ar_sdio->wr_async_lock, flags); | ||
297 | list_add_tail(&req_priv->busrequest->list, &ar_sdio->wr_asyncq); | ||
298 | spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags); | ||
299 | queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work); | ||
300 | } | ||
301 | |||
302 | return status; | ||
303 | } | ||
304 | |||
305 | /* clean up scatter support */ | ||
306 | static void ath6kl_sdio_cleanup_scat_resource(struct ath6kl_sdio *ar_sdio) | ||
307 | { | ||
308 | struct hif_scatter_req *s_req, *tmp_req; | ||
309 | unsigned long flag; | ||
310 | |||
311 | /* empty the free list */ | ||
312 | spin_lock_irqsave(&ar_sdio->scat_lock, flag); | ||
313 | list_for_each_entry_safe(s_req, tmp_req, &ar_sdio->scat_req, list) { | ||
314 | list_del(&s_req->list); | ||
315 | spin_unlock_irqrestore(&ar_sdio->scat_lock, flag); | ||
316 | |||
317 | if (s_req->req_priv && s_req->req_priv->busrequest) | ||
318 | ath6kl_sdio_free_bus_req(ar_sdio, | ||
319 | s_req->req_priv->busrequest); | ||
320 | kfree(s_req->virt_dma_buf); | ||
321 | kfree(s_req->req_priv); | ||
322 | kfree(s_req); | ||
323 | |||
324 | spin_lock_irqsave(&ar_sdio->scat_lock, flag); | ||
325 | } | ||
326 | spin_unlock_irqrestore(&ar_sdio->scat_lock, flag); | ||
327 | } | ||
328 | |||
329 | /* setup of HIF scatter resources */ | ||
330 | static int ath6kl_sdio_setup_scat_resource(struct ath6kl_sdio *ar_sdio, | ||
331 | struct hif_dev_scat_sup_info *pinfo) | ||
332 | { | ||
333 | struct hif_scatter_req *s_req; | ||
334 | struct bus_request *bus_req; | ||
335 | int i, scat_req_sz, scat_list_sz; | ||
336 | |||
337 | /* check if host supports scatter and it meets our requirements */ | ||
338 | if (ar_sdio->func->card->host->max_segs < MAX_SCATTER_ENTRIES_PER_REQ) { | ||
339 | ath6kl_err("hif-scatter: host only supports scatter of : %d entries, need: %d\n", | ||
340 | ar_sdio->func->card->host->max_segs, | ||
341 | MAX_SCATTER_ENTRIES_PER_REQ); | ||
342 | return -EINVAL; | ||
343 | } | ||
344 | |||
345 | ath6kl_dbg(ATH6KL_DBG_ANY, | ||
346 | "hif-scatter enabled: max scatter req : %d entries: %d\n", | ||
347 | MAX_SCATTER_REQUESTS, MAX_SCATTER_ENTRIES_PER_REQ); | ||
348 | |||
349 | scat_list_sz = (MAX_SCATTER_ENTRIES_PER_REQ - 1) * | ||
350 | sizeof(struct hif_scatter_item); | ||
351 | scat_req_sz = sizeof(*s_req) + scat_list_sz; | ||
352 | |||
353 | for (i = 0; i < MAX_SCATTER_REQUESTS; i++) { | ||
354 | /* allocate the scatter request */ | ||
355 | s_req = kzalloc(scat_req_sz, GFP_KERNEL); | ||
356 | if (!s_req) | ||
357 | goto fail_setup_scat; | ||
358 | |||
359 | /* allocate the private request blob */ | ||
360 | s_req->req_priv = kzalloc(sizeof(*s_req->req_priv), GFP_KERNEL); | ||
361 | |||
362 | if (!s_req->req_priv) { | ||
363 | kfree(s_req); | ||
364 | goto fail_setup_scat; | ||
365 | } | ||
366 | |||
367 | /* allocate a bus request for this scatter request */ | ||
368 | bus_req = ath6kl_sdio_alloc_busreq(ar_sdio); | ||
369 | if (!bus_req) { | ||
370 | kfree(s_req->req_priv); | ||
371 | kfree(s_req); | ||
372 | goto fail_setup_scat; | ||
373 | } | ||
374 | |||
375 | /* assign the scatter request to this bus request */ | ||
376 | bus_req->scat_req = s_req; | ||
377 | s_req->req_priv->busrequest = bus_req; | ||
378 | /* add it to the scatter pool */ | ||
379 | hif_scatter_req_add(ar_sdio->ar, s_req); | ||
380 | } | ||
381 | |||
382 | /* set scatter function pointers */ | ||
383 | pinfo->rw_scat_func = ath6kl_sdio_async_rw_scatter; | ||
384 | pinfo->max_scat_entries = MAX_SCATTER_ENTRIES_PER_REQ; | ||
385 | pinfo->max_xfer_szper_scatreq = MAX_SCATTER_REQ_TRANSFER_SIZE; | ||
386 | |||
387 | return 0; | ||
388 | |||
389 | fail_setup_scat: | ||
390 | ath6kl_err("hif-scatter: failed to alloc scatter resources !\n"); | ||
391 | ath6kl_sdio_cleanup_scat_resource(ar_sdio); | ||
392 | |||
393 | return -ENOMEM; | ||
394 | } | ||
395 | |||
396 | static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf, | ||
397 | u32 len, u32 request) | ||
398 | { | ||
399 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | ||
400 | u8 *tbuf = NULL; | ||
401 | int ret; | ||
402 | bool bounced = false; | ||
403 | |||
404 | if (request & HIF_BLOCK_BASIS) | ||
405 | len = round_down(len, HIF_MBOX_BLOCK_SIZE); | ||
406 | |||
407 | if (buf_needs_bounce(buf)) { | ||
408 | if (!ar_sdio->dma_buffer) | ||
409 | return -ENOMEM; | ||
410 | tbuf = ar_sdio->dma_buffer; | ||
411 | memcpy(tbuf, buf, len); | ||
412 | bounced = true; | ||
413 | } else | ||
414 | tbuf = buf; | ||
415 | |||
416 | sdio_claim_host(ar_sdio->func); | ||
417 | if (request & HIF_WRITE) { | ||
418 | if (addr >= HIF_MBOX_BASE_ADDR && | ||
419 | addr <= HIF_MBOX_END_ADDR) | ||
420 | addr += (HIF_MBOX_WIDTH - len); | ||
421 | |||
422 | if (addr == HIF_MBOX0_EXT_BASE_ADDR) | ||
423 | addr += HIF_MBOX0_EXT_WIDTH - len; | ||
424 | |||
425 | if (request & HIF_FIXED_ADDRESS) | ||
426 | ret = sdio_writesb(ar_sdio->func, addr, tbuf, len); | ||
427 | else | ||
428 | ret = sdio_memcpy_toio(ar_sdio->func, addr, tbuf, len); | ||
429 | } else { | ||
430 | if (request & HIF_FIXED_ADDRESS) | ||
431 | ret = sdio_readsb(ar_sdio->func, tbuf, addr, len); | ||
432 | else | ||
433 | ret = sdio_memcpy_fromio(ar_sdio->func, tbuf, | ||
434 | addr, len); | ||
435 | if (bounced) | ||
436 | memcpy(buf, tbuf, len); | ||
437 | } | ||
438 | sdio_release_host(ar_sdio->func); | ||
439 | |||
440 | return ret; | ||
441 | } | ||
442 | |||
443 | static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio, | ||
444 | struct bus_request *req) | ||
445 | { | ||
446 | if (req->scat_req) | ||
447 | ath6kl_sdio_scat_rw(ar_sdio, req); | ||
448 | else { | ||
449 | void *context; | ||
450 | int status; | ||
451 | |||
452 | status = ath6kl_sdio_read_write_sync(ar_sdio->ar, req->address, | ||
453 | req->buffer, req->length, | ||
454 | req->request); | ||
455 | context = req->packet; | ||
456 | ath6kl_sdio_free_bus_req(ar_sdio, req); | ||
457 | ath6kldev_rw_comp_handler(context, status); | ||
458 | } | ||
459 | } | ||
460 | |||
461 | static void ath6kl_sdio_write_async_work(struct work_struct *work) | ||
462 | { | ||
463 | struct ath6kl_sdio *ar_sdio; | ||
464 | unsigned long flags; | ||
465 | struct bus_request *req, *tmp_req; | ||
466 | |||
467 | ar_sdio = container_of(work, struct ath6kl_sdio, wr_async_work); | ||
468 | sdio_claim_host(ar_sdio->func); | ||
469 | |||
470 | spin_lock_irqsave(&ar_sdio->wr_async_lock, flags); | ||
471 | list_for_each_entry_safe(req, tmp_req, &ar_sdio->wr_asyncq, list) { | ||
472 | list_del(&req->list); | ||
473 | spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags); | ||
474 | __ath6kl_sdio_write_async(ar_sdio, req); | ||
475 | spin_lock_irqsave(&ar_sdio->wr_async_lock, flags); | ||
476 | } | ||
477 | spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags); | ||
478 | |||
479 | sdio_release_host(ar_sdio->func); | ||
480 | } | ||
481 | |||
482 | static void ath6kl_sdio_irq_handler(struct sdio_func *func) | ||
483 | { | ||
484 | int status; | ||
485 | struct ath6kl_sdio *ar_sdio; | ||
486 | |||
487 | ar_sdio = sdio_get_drvdata(func); | ||
488 | atomic_set(&ar_sdio->irq_handling, 1); | ||
489 | |||
490 | /* | ||
491 | * Release the host during interrups so we can pick it back up when | ||
492 | * we process commands. | ||
493 | */ | ||
494 | sdio_release_host(ar_sdio->func); | ||
495 | |||
496 | status = ath6kldev_intr_bh_handler(ar_sdio->ar); | ||
497 | sdio_claim_host(ar_sdio->func); | ||
498 | atomic_set(&ar_sdio->irq_handling, 0); | ||
499 | WARN_ON(status && status != -ECANCELED); | ||
500 | } | ||
501 | |||
502 | static int ath6kl_sdio_power_on(struct ath6kl_sdio *ar_sdio) | ||
503 | { | ||
504 | struct sdio_func *func = ar_sdio->func; | ||
505 | int ret = 0; | ||
506 | |||
507 | if (!ar_sdio->is_disabled) | ||
508 | return 0; | ||
509 | |||
510 | sdio_claim_host(func); | ||
511 | |||
512 | ret = sdio_enable_func(func); | ||
513 | if (ret) { | ||
514 | ath6kl_err("Unable to enable sdio func: %d)\n", ret); | ||
515 | sdio_release_host(func); | ||
516 | return ret; | ||
517 | } | ||
518 | |||
519 | sdio_release_host(func); | ||
520 | |||
521 | /* | ||
522 | * Wait for hardware to initialise. It should take a lot less than | ||
523 | * 10 ms but let's be conservative here. | ||
524 | */ | ||
525 | msleep(10); | ||
526 | |||
527 | ar_sdio->is_disabled = false; | ||
528 | |||
529 | return ret; | ||
530 | } | ||
531 | |||
532 | static int ath6kl_sdio_power_off(struct ath6kl_sdio *ar_sdio) | ||
533 | { | ||
534 | int ret; | ||
535 | |||
536 | if (ar_sdio->is_disabled) | ||
537 | return 0; | ||
538 | |||
539 | /* Disable the card */ | ||
540 | sdio_claim_host(ar_sdio->func); | ||
541 | ret = sdio_disable_func(ar_sdio->func); | ||
542 | sdio_release_host(ar_sdio->func); | ||
543 | |||
544 | if (ret) | ||
545 | return ret; | ||
546 | |||
547 | ar_sdio->is_disabled = true; | ||
548 | |||
549 | return ret; | ||
550 | } | ||
551 | |||
552 | static int ath6kl_sdio_write_async(struct ath6kl *ar, u32 address, u8 *buffer, | ||
553 | u32 length, u32 request, | ||
554 | struct htc_packet *packet) | ||
555 | { | ||
556 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | ||
557 | struct bus_request *bus_req; | ||
558 | unsigned long flags; | ||
559 | |||
560 | bus_req = ath6kl_sdio_alloc_busreq(ar_sdio); | ||
561 | |||
562 | if (!bus_req) | ||
563 | return -ENOMEM; | ||
564 | |||
565 | bus_req->address = address; | ||
566 | bus_req->buffer = buffer; | ||
567 | bus_req->length = length; | ||
568 | bus_req->request = request; | ||
569 | bus_req->packet = packet; | ||
570 | |||
571 | spin_lock_irqsave(&ar_sdio->wr_async_lock, flags); | ||
572 | list_add_tail(&bus_req->list, &ar_sdio->wr_asyncq); | ||
573 | spin_unlock_irqrestore(&ar_sdio->wr_async_lock, flags); | ||
574 | queue_work(ar->ath6kl_wq, &ar_sdio->wr_async_work); | ||
575 | |||
576 | return 0; | ||
577 | } | ||
578 | |||
579 | static void ath6kl_sdio_irq_enable(struct ath6kl *ar) | ||
580 | { | ||
581 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | ||
582 | int ret; | ||
583 | |||
584 | sdio_claim_host(ar_sdio->func); | ||
585 | |||
586 | /* Register the isr */ | ||
587 | ret = sdio_claim_irq(ar_sdio->func, ath6kl_sdio_irq_handler); | ||
588 | if (ret) | ||
589 | ath6kl_err("Failed to claim sdio irq: %d\n", ret); | ||
590 | |||
591 | sdio_release_host(ar_sdio->func); | ||
592 | } | ||
593 | |||
594 | static void ath6kl_sdio_irq_disable(struct ath6kl *ar) | ||
595 | { | ||
596 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | ||
597 | int ret; | ||
598 | |||
599 | sdio_claim_host(ar_sdio->func); | ||
600 | |||
601 | /* Mask our function IRQ */ | ||
602 | while (atomic_read(&ar_sdio->irq_handling)) { | ||
603 | sdio_release_host(ar_sdio->func); | ||
604 | schedule_timeout(HZ / 10); | ||
605 | sdio_claim_host(ar_sdio->func); | ||
606 | } | ||
607 | |||
608 | ret = sdio_release_irq(ar_sdio->func); | ||
609 | if (ret) | ||
610 | ath6kl_err("Failed to release sdio irq: %d\n", ret); | ||
611 | |||
612 | sdio_release_host(ar_sdio->func); | ||
613 | } | ||
614 | |||
615 | static struct hif_scatter_req *ath6kl_sdio_scatter_req_get(struct ath6kl *ar) | ||
616 | { | ||
617 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | ||
618 | struct hif_scatter_req *node = NULL; | ||
619 | unsigned long flag; | ||
620 | |||
621 | spin_lock_irqsave(&ar_sdio->scat_lock, flag); | ||
622 | |||
623 | if (!list_empty(&ar_sdio->scat_req)) { | ||
624 | node = list_first_entry(&ar_sdio->scat_req, | ||
625 | struct hif_scatter_req, list); | ||
626 | list_del(&node->list); | ||
627 | } | ||
628 | |||
629 | spin_unlock_irqrestore(&ar_sdio->scat_lock, flag); | ||
630 | |||
631 | return node; | ||
632 | } | ||
633 | |||
634 | static void ath6kl_sdio_scatter_req_add(struct ath6kl *ar, | ||
635 | struct hif_scatter_req *s_req) | ||
636 | { | ||
637 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | ||
638 | unsigned long flag; | ||
639 | |||
640 | spin_lock_irqsave(&ar_sdio->scat_lock, flag); | ||
641 | |||
642 | list_add_tail(&s_req->list, &ar_sdio->scat_req); | ||
643 | |||
644 | spin_unlock_irqrestore(&ar_sdio->scat_lock, flag); | ||
645 | |||
646 | } | ||
647 | |||
648 | static int ath6kl_sdio_enable_scatter(struct ath6kl *ar, | ||
649 | struct hif_dev_scat_sup_info *info) | ||
650 | { | ||
651 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | ||
652 | int ret; | ||
653 | |||
654 | ret = ath6kl_sdio_setup_scat_resource(ar_sdio, info); | ||
655 | |||
656 | return ret; | ||
657 | } | ||
658 | |||
659 | static void ath6kl_sdio_cleanup_scatter(struct ath6kl *ar) | ||
660 | { | ||
661 | struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); | ||
662 | |||
663 | ath6kl_sdio_cleanup_scat_resource(ar_sdio); | ||
664 | } | ||
665 | |||
666 | static const struct ath6kl_hif_ops ath6kl_sdio_ops = { | ||
667 | .read_write_sync = ath6kl_sdio_read_write_sync, | ||
668 | .write_async = ath6kl_sdio_write_async, | ||
669 | .irq_enable = ath6kl_sdio_irq_enable, | ||
670 | .irq_disable = ath6kl_sdio_irq_disable, | ||
671 | .scatter_req_get = ath6kl_sdio_scatter_req_get, | ||
672 | .scatter_req_add = ath6kl_sdio_scatter_req_add, | ||
673 | .enable_scatter = ath6kl_sdio_enable_scatter, | ||
674 | .cleanup_scatter = ath6kl_sdio_cleanup_scatter, | ||
675 | }; | ||
676 | |||
677 | static int ath6kl_sdio_probe(struct sdio_func *func, | ||
678 | const struct sdio_device_id *id) | ||
679 | { | ||
680 | int ret; | ||
681 | struct ath6kl_sdio *ar_sdio; | ||
682 | struct ath6kl *ar; | ||
683 | int count; | ||
684 | |||
685 | ath6kl_dbg(ATH6KL_DBG_TRC, | ||
686 | "%s: func: 0x%X, vendor id: 0x%X, dev id: 0x%X, block size: 0x%X/0x%X\n", | ||
687 | __func__, func->num, func->vendor, | ||
688 | func->device, func->max_blksize, func->cur_blksize); | ||
689 | |||
690 | ar_sdio = kzalloc(sizeof(struct ath6kl_sdio), GFP_KERNEL); | ||
691 | if (!ar_sdio) | ||
692 | return -ENOMEM; | ||
693 | |||
694 | ar_sdio->dma_buffer = kzalloc(HIF_DMA_BUFFER_SIZE, GFP_KERNEL); | ||
695 | if (!ar_sdio->dma_buffer) { | ||
696 | ret = -ENOMEM; | ||
697 | goto err_hif; | ||
698 | } | ||
699 | |||
700 | ar_sdio->func = func; | ||
701 | sdio_set_drvdata(func, ar_sdio); | ||
702 | |||
703 | ar_sdio->id = id; | ||
704 | ar_sdio->is_disabled = true; | ||
705 | |||
706 | spin_lock_init(&ar_sdio->lock); | ||
707 | spin_lock_init(&ar_sdio->scat_lock); | ||
708 | spin_lock_init(&ar_sdio->wr_async_lock); | ||
709 | |||
710 | INIT_LIST_HEAD(&ar_sdio->scat_req); | ||
711 | INIT_LIST_HEAD(&ar_sdio->bus_req_freeq); | ||
712 | INIT_LIST_HEAD(&ar_sdio->wr_asyncq); | ||
713 | |||
714 | INIT_WORK(&ar_sdio->wr_async_work, ath6kl_sdio_write_async_work); | ||
715 | |||
716 | for (count = 0; count < BUS_REQUEST_MAX_NUM; count++) | ||
717 | ath6kl_sdio_free_bus_req(ar_sdio, &ar_sdio->bus_req[count]); | ||
718 | |||
719 | ar = ath6kl_core_alloc(&ar_sdio->func->dev); | ||
720 | if (!ar) { | ||
721 | ath6kl_err("Failed to alloc ath6kl core\n"); | ||
722 | ret = -ENOMEM; | ||
723 | goto err_dma; | ||
724 | } | ||
725 | |||
726 | ar_sdio->ar = ar; | ||
727 | ar->hif_priv = ar_sdio; | ||
728 | ar->hif_ops = &ath6kl_sdio_ops; | ||
729 | |||
730 | ath6kl_sdio_set_mbox_info(ar); | ||
731 | |||
732 | sdio_claim_host(func); | ||
733 | |||
734 | if ((ar_sdio->id->device & MANUFACTURER_ID_ATH6KL_BASE_MASK) >= | ||
735 | MANUFACTURER_ID_AR6003_BASE) { | ||
736 | /* enable 4-bit ASYNC interrupt on AR6003 or later */ | ||
737 | ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card, | ||
738 | CCCR_SDIO_IRQ_MODE_REG, | ||
739 | SDIO_IRQ_MODE_ASYNC_4BIT_IRQ); | ||
740 | if (ret) { | ||
741 | ath6kl_err("Failed to enable 4-bit async irq mode %d\n", | ||
742 | ret); | ||
743 | sdio_release_host(func); | ||
744 | goto err_dma; | ||
745 | } | ||
746 | |||
747 | ath6kl_dbg(ATH6KL_DBG_TRC, "4-bit async irq mode enabled\n"); | ||
748 | } | ||
749 | |||
750 | /* give us some time to enable, in ms */ | ||
751 | func->enable_timeout = 100; | ||
752 | |||
753 | sdio_release_host(func); | ||
754 | |||
755 | ret = ath6kl_sdio_power_on(ar_sdio); | ||
756 | if (ret) | ||
757 | goto err_dma; | ||
758 | |||
759 | sdio_claim_host(func); | ||
760 | |||
761 | ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE); | ||
762 | if (ret) { | ||
763 | ath6kl_err("Set sdio block size %d failed: %d)\n", | ||
764 | HIF_MBOX_BLOCK_SIZE, ret); | ||
765 | sdio_release_host(func); | ||
766 | goto err_off; | ||
767 | } | ||
768 | |||
769 | sdio_release_host(func); | ||
770 | |||
771 | ret = ath6kl_core_init(ar); | ||
772 | if (ret) { | ||
773 | ath6kl_err("Failed to init ath6kl core\n"); | ||
774 | goto err_off; | ||
775 | } | ||
776 | |||
777 | return ret; | ||
778 | |||
779 | err_off: | ||
780 | ath6kl_sdio_power_off(ar_sdio); | ||
781 | err_dma: | ||
782 | kfree(ar_sdio->dma_buffer); | ||
783 | err_hif: | ||
784 | kfree(ar_sdio); | ||
785 | |||
786 | return ret; | ||
787 | } | ||
788 | |||
789 | static void ath6kl_sdio_remove(struct sdio_func *func) | ||
790 | { | ||
791 | struct ath6kl_sdio *ar_sdio; | ||
792 | |||
793 | ar_sdio = sdio_get_drvdata(func); | ||
794 | |||
795 | ath6kl_stop_txrx(ar_sdio->ar); | ||
796 | cancel_work_sync(&ar_sdio->wr_async_work); | ||
797 | |||
798 | ath6kl_unavail_ev(ar_sdio->ar); | ||
799 | |||
800 | ath6kl_sdio_power_off(ar_sdio); | ||
801 | |||
802 | kfree(ar_sdio->dma_buffer); | ||
803 | kfree(ar_sdio); | ||
804 | } | ||
805 | |||
806 | static const struct sdio_device_id ath6kl_sdio_devices[] = { | ||
807 | {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))}, | ||
808 | {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))}, | ||
809 | {}, | ||
810 | }; | ||
811 | |||
812 | MODULE_DEVICE_TABLE(sdio, ath6kl_sdio_devices); | ||
813 | |||
814 | static struct sdio_driver ath6kl_sdio_driver = { | ||
815 | .name = "ath6kl_sdio", | ||
816 | .id_table = ath6kl_sdio_devices, | ||
817 | .probe = ath6kl_sdio_probe, | ||
818 | .remove = ath6kl_sdio_remove, | ||
819 | }; | ||
820 | |||
821 | static int __init ath6kl_sdio_init(void) | ||
822 | { | ||
823 | int ret; | ||
824 | |||
825 | ret = sdio_register_driver(&ath6kl_sdio_driver); | ||
826 | if (ret) | ||
827 | ath6kl_err("sdio driver registration failed: %d\n", ret); | ||
828 | |||
829 | return ret; | ||
830 | } | ||
831 | |||
832 | static void __exit ath6kl_sdio_exit(void) | ||
833 | { | ||
834 | sdio_unregister_driver(&ath6kl_sdio_driver); | ||
835 | } | ||
836 | |||
837 | module_init(ath6kl_sdio_init); | ||
838 | module_exit(ath6kl_sdio_exit); | ||
839 | |||
840 | MODULE_AUTHOR("Atheros Communications, Inc."); | ||
841 | MODULE_DESCRIPTION("Driver support for Atheros AR600x SDIO devices"); | ||
842 | MODULE_LICENSE("Dual BSD/GPL"); | ||
843 | |||
844 | MODULE_FIRMWARE(AR6003_REV2_OTP_FILE); | ||
845 | MODULE_FIRMWARE(AR6003_REV2_FIRMWARE_FILE); | ||
846 | MODULE_FIRMWARE(AR6003_REV2_PATCH_FILE); | ||
847 | MODULE_FIRMWARE(AR6003_REV2_BOARD_DATA_FILE); | ||
848 | MODULE_FIRMWARE(AR6003_REV2_DEFAULT_BOARD_DATA_FILE); | ||
849 | MODULE_FIRMWARE(AR6003_REV3_OTP_FILE); | ||
850 | MODULE_FIRMWARE(AR6003_REV3_FIRMWARE_FILE); | ||
851 | MODULE_FIRMWARE(AR6003_REV3_PATCH_FILE); | ||
852 | MODULE_FIRMWARE(AR6003_REV3_BOARD_DATA_FILE); | ||
853 | MODULE_FIRMWARE(AR6003_REV3_DEFAULT_BOARD_DATA_FILE); | ||