blob: 1bcaaec579c5079f764e373aa27d21dd79b8e4e9 [file] [log] [blame]
Kalle Valobdcd8172011-07-18 00:22:30 +03001/*
2 * Copyright (c) 2007-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 "core.h"
18#include "target.h"
19#include "hif-ops.h"
20#include "htc_hif.h"
21#include "debug.h"
22
23#define MAILBOX_FOR_BLOCK_SIZE 1
24
25#define ATH6KL_TIME_QUANTUM 10 /* in ms */
26
27static void ath6kl_add_io_pkt(struct ath6kl_device *dev,
28 struct htc_packet *packet)
29{
30 spin_lock_bh(&dev->lock);
31 list_add_tail(&packet->list, &dev->reg_io);
32 spin_unlock_bh(&dev->lock);
33}
34
35static struct htc_packet *ath6kl_get_io_pkt(struct ath6kl_device *dev)
36{
37 struct htc_packet *packet = NULL;
38
39 spin_lock_bh(&dev->lock);
40 if (!list_empty(&dev->reg_io)) {
41 packet = list_first_entry(&dev->reg_io,
42 struct htc_packet, list);
43 list_del(&packet->list);
44 }
45 spin_unlock_bh(&dev->lock);
46
47 return packet;
48}
49
50static int ath6kldev_cp_scat_dma_buf(struct hif_scatter_req *req, bool from_dma)
51{
52 u8 *buf;
53 int i;
54
55 buf = req->virt_dma_buf;
56
57 for (i = 0; i < req->scat_entries; i++) {
58
59 if (from_dma)
60 memcpy(req->scat_list[i].buf, buf,
61 req->scat_list[i].len);
62 else
63 memcpy(buf, req->scat_list[i].buf,
64 req->scat_list[i].len);
65
66 buf += req->scat_list[i].len;
67 }
68
69 return 0;
70}
71
72int ath6kldev_rw_comp_handler(void *context, int status)
73{
74 struct htc_packet *packet = context;
75
76 ath6kl_dbg(ATH6KL_DBG_HTC_RECV,
77 "ath6kldev_rw_comp_handler (pkt:0x%p , status: %d\n",
78 packet, status);
79
80 packet->status = status;
81 packet->completion(packet->context, packet);
82
83 return 0;
84}
85
86static int ath6kldev_proc_dbg_intr(struct ath6kl_device *dev)
87{
88 u32 dummy;
89 int status;
90
91 ath6kl_err("target debug interrupt\n");
92
93 ath6kl_target_failure(dev->ar);
94
95 /*
96 * read counter to clear the interrupt, the debug error interrupt is
97 * counter 0.
98 */
99 status = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
100 (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
101 if (status)
102 WARN_ON(1);
103
104 return status;
105}
106
107/* mailbox recv message polling */
108int ath6kldev_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
109 int timeout)
110{
111 struct ath6kl_irq_proc_registers *rg;
112 int status = 0, i;
113 u8 htc_mbox = 1 << HTC_MAILBOX;
114
115 for (i = timeout / ATH6KL_TIME_QUANTUM; i > 0; i--) {
116 /* this is the standard HIF way, load the reg table */
117 status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
118 (u8 *) &dev->irq_proc_reg,
119 sizeof(dev->irq_proc_reg),
120 HIF_RD_SYNC_BYTE_INC);
121
122 if (status) {
123 ath6kl_err("failed to read reg table\n");
124 return status;
125 }
126
127 /* check for MBOX data and valid lookahead */
128 if (dev->irq_proc_reg.host_int_status & htc_mbox) {
129 if (dev->irq_proc_reg.rx_lkahd_valid &
130 htc_mbox) {
131 /*
132 * Mailbox has a message and the look ahead
133 * is valid.
134 */
135 rg = &dev->irq_proc_reg;
136 *lk_ahd =
137 le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
138 break;
139 }
140 }
141
142 /* delay a little */
143 mdelay(ATH6KL_TIME_QUANTUM);
144 ath6kl_dbg(ATH6KL_DBG_HTC_RECV, "retry mbox poll : %d\n", i);
145 }
146
147 if (i == 0) {
148 ath6kl_err("timeout waiting for recv message\n");
149 status = -ETIME;
150 /* check if the target asserted */
151 if (dev->irq_proc_reg.counter_int_status &
152 ATH6KL_TARGET_DEBUG_INTR_MASK)
153 /*
154 * Target failure handler will be called in case of
155 * an assert.
156 */
157 ath6kldev_proc_dbg_intr(dev);
158 }
159
160 return status;
161}
162
163/*
164 * Disable packet reception (used in case the host runs out of buffers)
165 * using the interrupt enable registers through the host I/F
166 */
167int ath6kldev_rx_control(struct ath6kl_device *dev, bool enable_rx)
168{
169 struct ath6kl_irq_enable_reg regs;
170 int status = 0;
171
172 /* take the lock to protect interrupt enable shadows */
173 spin_lock_bh(&dev->lock);
174
175 if (enable_rx)
176 dev->irq_en_reg.int_status_en |=
177 SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
178 else
179 dev->irq_en_reg.int_status_en &=
180 ~SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
181
182 memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
183
184 spin_unlock_bh(&dev->lock);
185
186 status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
187 &regs.int_status_en,
188 sizeof(struct ath6kl_irq_enable_reg),
189 HIF_WR_SYNC_BYTE_INC);
190
191 return status;
192}
193
194static void ath6kldev_rw_async_handler(struct htc_target *target,
195 struct htc_packet *packet)
196{
197 struct ath6kl_device *dev = target->dev;
198 struct hif_scatter_req *req = packet->pkt_cntxt;
199
200 req->status = packet->status;
201
202 ath6kl_add_io_pkt(dev, packet);
203
204 req->complete(req);
205}
206
207static int ath6kldev_rw_scatter(struct ath6kl *ar, struct hif_scatter_req *req)
208{
209 struct ath6kl_device *dev = ar->htc_target->dev;
210 struct htc_packet *packet = NULL;
211 int status = 0;
212 u32 request = req->req;
213 u8 *virt_dma_buf;
214
215 if (!req->len)
216 return 0;
217
218 if (request & HIF_ASYNCHRONOUS) {
219 /* use an I/O packet to carry this request */
220 packet = ath6kl_get_io_pkt(dev);
221 if (!packet) {
222 status = -ENOMEM;
223 goto out;
224 }
225
226 packet->pkt_cntxt = req;
227 packet->completion = ath6kldev_rw_async_handler;
228 packet->context = ar->htc_target;
229 }
230
231 virt_dma_buf = req->virt_dma_buf;
232
233 if (request & HIF_ASYNCHRONOUS)
234 status = hif_write_async(dev->ar, req->addr, virt_dma_buf,
235 req->len, request, packet);
236 else
237 status = hif_read_write_sync(dev->ar, req->addr, virt_dma_buf,
238 req->len, request);
239
240out:
241 if (status)
242 if (request & HIF_ASYNCHRONOUS) {
243 if (packet != NULL)
244 ath6kl_add_io_pkt(dev, packet);
245 req->status = status;
246 req->complete(req);
247 status = 0;
248 }
249
250 return status;
251}
252
253int ath6kldev_submit_scat_req(struct ath6kl_device *dev,
254 struct hif_scatter_req *scat_req, bool read)
255{
256 int status = 0;
257
258 if (read) {
259 scat_req->req = HIF_RD_SYNC_BLOCK_FIX;
260 scat_req->addr = dev->ar->mbox_info.htc_addr;
261 } else {
262 scat_req->req = HIF_WR_ASYNC_BLOCK_INC;
263
264 scat_req->addr =
265 (scat_req->len > HIF_MBOX_WIDTH) ?
266 dev->ar->mbox_info.htc_ext_addr :
267 dev->ar->mbox_info.htc_addr;
268 }
269
270 ath6kl_dbg((ATH6KL_DBG_HTC_RECV | ATH6KL_DBG_HTC_SEND),
271 "ath6kldev_submit_scat_req, entries: %d, total len: %d mbox:0x%X (mode: %s : %s)\n",
272 scat_req->scat_entries, scat_req->len,
273 scat_req->addr, !read ? "async" : "sync",
274 (read) ? "rd" : "wr");
275
276 if (!read && dev->virt_scat)
277 status = ath6kldev_cp_scat_dma_buf(scat_req, false);
278
279 if (status) {
280 if (!read) {
281 scat_req->status = status;
282 scat_req->complete(scat_req);
283 return 0;
284 }
285 return status;
286 }
287
288 status = dev->hif_scat_info.rw_scat_func(dev->ar, scat_req);
289
290 if (read) {
291 /* in sync mode, we can touch the scatter request */
292 scat_req->status = status;
293 if (!status && dev->virt_scat)
294 scat_req->status =
295 ath6kldev_cp_scat_dma_buf(scat_req, true);
296 }
297
298 return status;
299}
300
301/*
302 * function to set up virtual scatter support if HIF
303 * layer has not implemented the interface.
304 */
305static int ath6kldev_setup_virt_scat_sup(struct ath6kl_device *dev)
306{
307 struct hif_scatter_req *scat_req;
308 int buf_sz, scat_req_sz, scat_list_sz;
309 int i, status = 0;
310 u8 *virt_dma_buf;
311
312 buf_sz = 2 * L1_CACHE_BYTES + ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
313
314 scat_list_sz = (ATH6KL_SCATTER_ENTRIES_PER_REQ - 1) *
315 sizeof(struct hif_scatter_item);
316 scat_req_sz = sizeof(*scat_req) + scat_list_sz;
317
318 for (i = 0; i < ATH6KL_SCATTER_REQS; i++) {
319 scat_req = kzalloc(scat_req_sz, GFP_KERNEL);
320
321 if (!scat_req) {
322 status = -ENOMEM;
323 break;
324 }
325
326 virt_dma_buf = kzalloc(buf_sz, GFP_KERNEL);
327 if (!virt_dma_buf) {
328 kfree(scat_req);
329 status = -ENOMEM;
330 break;
331 }
332
333 scat_req->virt_dma_buf =
334 (u8 *)L1_CACHE_ALIGN((unsigned long)virt_dma_buf);
335
336 /* we emulate a DMA bounce interface */
337 hif_scatter_req_add(dev->ar, scat_req);
338 }
339
340 if (status)
341 ath6kl_hif_cleanup_scatter(dev->ar);
342 else {
343 dev->hif_scat_info.rw_scat_func = ath6kldev_rw_scatter;
344 dev->hif_scat_info.max_scat_entries =
345 ATH6KL_SCATTER_ENTRIES_PER_REQ;
346 dev->hif_scat_info.max_xfer_szper_scatreq =
347 ATH6KL_MAX_TRANSFER_SIZE_PER_SCATTER;
348 dev->virt_scat = true;
349 }
350
351 return status;
352}
353
354int ath6kldev_setup_msg_bndl(struct ath6kl_device *dev, int max_msg_per_trans)
355{
356 int status;
357
358 status = ath6kl_hif_enable_scatter(dev->ar, &dev->hif_scat_info);
359
360 if (status) {
361 ath6kl_warn("hif does not support scatter requests (%d)\n",
362 status);
363
364 /* we can try to use a virtual DMA scatter mechanism */
365 status = ath6kldev_setup_virt_scat_sup(dev);
366 }
367
368 if (!status)
369 ath6kl_dbg(ATH6KL_DBG_ANY, "max scatter items:%d: maxlen:%d\n",
370 dev->hif_scat_info.max_scat_entries,
371 dev->hif_scat_info.max_xfer_szper_scatreq);
372
373 return status;
374}
375
376static int ath6kldev_proc_counter_intr(struct ath6kl_device *dev)
377{
378 u8 counter_int_status;
379
380 ath6kl_dbg(ATH6KL_DBG_IRQ, "counter interrupt\n");
381
382 counter_int_status = dev->irq_proc_reg.counter_int_status &
383 dev->irq_en_reg.cntr_int_status_en;
384
385 ath6kl_dbg(ATH6KL_DBG_IRQ,
386 "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
387 counter_int_status);
388
389 /*
390 * NOTE: other modules like GMBOX may use the counter interrupt for
391 * credit flow control on other counters, we only need to check for
392 * the debug assertion counter interrupt.
393 */
394 if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK)
395 return ath6kldev_proc_dbg_intr(dev);
396
397 return 0;
398}
399
400static int ath6kldev_proc_err_intr(struct ath6kl_device *dev)
401{
402 int status;
403 u8 error_int_status;
404 u8 reg_buf[4];
405
406 ath6kl_dbg(ATH6KL_DBG_IRQ, "error interrupt\n");
407
408 error_int_status = dev->irq_proc_reg.error_int_status & 0x0F;
409 if (!error_int_status) {
410 WARN_ON(1);
411 return -EIO;
412 }
413
414 ath6kl_dbg(ATH6KL_DBG_IRQ,
415 "valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n",
416 error_int_status);
417
418 if (MS(ERROR_INT_STATUS_WAKEUP, error_int_status))
419 ath6kl_dbg(ATH6KL_DBG_IRQ, "error : wakeup\n");
420
421 if (MS(ERROR_INT_STATUS_RX_UNDERFLOW, error_int_status))
422 ath6kl_err("rx underflow\n");
423
424 if (MS(ERROR_INT_STATUS_TX_OVERFLOW, error_int_status))
425 ath6kl_err("tx overflow\n");
426
427 /* Clear the interrupt */
428 dev->irq_proc_reg.error_int_status &= ~error_int_status;
429
430 /* set W1C value to clear the interrupt, this hits the register first */
431 reg_buf[0] = error_int_status;
432 reg_buf[1] = 0;
433 reg_buf[2] = 0;
434 reg_buf[3] = 0;
435
436 status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS,
437 reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
438
439 if (status)
440 WARN_ON(1);
441
442 return status;
443}
444
445static int ath6kldev_proc_cpu_intr(struct ath6kl_device *dev)
446{
447 int status;
448 u8 cpu_int_status;
449 u8 reg_buf[4];
450
451 ath6kl_dbg(ATH6KL_DBG_IRQ, "cpu interrupt\n");
452
453 cpu_int_status = dev->irq_proc_reg.cpu_int_status &
454 dev->irq_en_reg.cpu_int_status_en;
455 if (!cpu_int_status) {
456 WARN_ON(1);
457 return -EIO;
458 }
459
460 ath6kl_dbg(ATH6KL_DBG_IRQ,
461 "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
462 cpu_int_status);
463
464 /* Clear the interrupt */
465 dev->irq_proc_reg.cpu_int_status &= ~cpu_int_status;
466
467 /*
468 * Set up the register transfer buffer to hit the register 4 times ,
469 * this is done to make the access 4-byte aligned to mitigate issues
470 * with host bus interconnects that restrict bus transfer lengths to
471 * be a multiple of 4-bytes.
472 */
473
474 /* set W1C value to clear the interrupt, this hits the register first */
475 reg_buf[0] = cpu_int_status;
476 /* the remaining are set to zero which have no-effect */
477 reg_buf[1] = 0;
478 reg_buf[2] = 0;
479 reg_buf[3] = 0;
480
481 status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS,
482 reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
483
484 if (status)
485 WARN_ON(1);
486
487 return status;
488}
489
490/* process pending interrupts synchronously */
491static int proc_pending_irqs(struct ath6kl_device *dev, bool *done)
492{
493 struct ath6kl_irq_proc_registers *rg;
494 int status = 0;
495 u8 host_int_status = 0;
496 u32 lk_ahd = 0;
497 u8 htc_mbox = 1 << HTC_MAILBOX;
498
499 ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev);
500
501 /*
502 * NOTE: HIF implementation guarantees that the context of this
503 * call allows us to perform SYNCHRONOUS I/O, that is we can block,
504 * sleep or call any API that can block or switch thread/task
505 * contexts. This is a fully schedulable context.
506 */
507
508 /*
509 * Process pending intr only when int_status_en is clear, it may
510 * result in unnecessary bus transaction otherwise. Target may be
511 * unresponsive at the time.
512 */
513 if (dev->irq_en_reg.int_status_en) {
514 /*
515 * Read the first 28 bytes of the HTC register table. This
516 * will yield us the value of different int status
517 * registers and the lookahead registers.
518 *
519 * length = sizeof(int_status) + sizeof(cpu_int_status)
520 * + sizeof(error_int_status) +
521 * sizeof(counter_int_status) +
522 * sizeof(mbox_frame) + sizeof(rx_lkahd_valid)
523 * + sizeof(hole) + sizeof(rx_lkahd) +
524 * sizeof(int_status_en) +
525 * sizeof(cpu_int_status_en) +
526 * sizeof(err_int_status_en) +
527 * sizeof(cntr_int_status_en);
528 */
529 status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
530 (u8 *) &dev->irq_proc_reg,
531 sizeof(dev->irq_proc_reg),
532 HIF_RD_SYNC_BYTE_INC);
533 if (status)
534 goto out;
535
536 if (AR_DBG_LVL_CHECK(ATH6KL_DBG_IRQ))
537 ath6kl_dump_registers(dev, &dev->irq_proc_reg,
538 &dev->irq_en_reg);
539
540 /* Update only those registers that are enabled */
541 host_int_status = dev->irq_proc_reg.host_int_status &
542 dev->irq_en_reg.int_status_en;
543
544 /* Look at mbox status */
545 if (host_int_status & htc_mbox) {
546 /*
547 * Mask out pending mbox value, we use "lookAhead as
548 * the real flag for mbox processing.
549 */
550 host_int_status &= ~htc_mbox;
551 if (dev->irq_proc_reg.rx_lkahd_valid &
552 htc_mbox) {
553 rg = &dev->irq_proc_reg;
554 lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
555 if (!lk_ahd)
556 ath6kl_err("lookAhead is zero!\n");
557 }
558 }
559 }
560
561 if (!host_int_status && !lk_ahd) {
562 *done = true;
563 goto out;
564 }
565
566 if (lk_ahd) {
567 int fetched = 0;
568
569 ath6kl_dbg(ATH6KL_DBG_IRQ,
570 "pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd);
571 /*
572 * Mailbox Interrupt, the HTC layer may issue async
573 * requests to empty the mailbox. When emptying the recv
574 * mailbox we use the async handler above called from the
575 * completion routine of the callers read request. This can
576 * improve performance by reducing context switching when
577 * we rapidly pull packets.
578 */
579 status = dev->msg_pending(dev->htc_cnxt, &lk_ahd, &fetched);
580 if (status)
581 goto out;
582
583 if (!fetched)
584 /*
585 * HTC could not pull any messages out due to lack
586 * of resources.
587 */
588 dev->chk_irq_status_cnt = 0;
589 }
590
591 /* now handle the rest of them */
592 ath6kl_dbg(ATH6KL_DBG_IRQ,
593 "valid interrupt source(s) for other interrupts: 0x%x\n",
594 host_int_status);
595
596 if (MS(HOST_INT_STATUS_CPU, host_int_status)) {
597 /* CPU Interrupt */
598 status = ath6kldev_proc_cpu_intr(dev);
599 if (status)
600 goto out;
601 }
602
603 if (MS(HOST_INT_STATUS_ERROR, host_int_status)) {
604 /* Error Interrupt */
605 status = ath6kldev_proc_err_intr(dev);
606 if (status)
607 goto out;
608 }
609
610 if (MS(HOST_INT_STATUS_COUNTER, host_int_status))
611 /* Counter Interrupt */
612 status = ath6kldev_proc_counter_intr(dev);
613
614out:
615 /*
616 * An optimization to bypass reading the IRQ status registers
617 * unecessarily which can re-wake the target, if upper layers
618 * determine that we are in a low-throughput mode, we can rely on
619 * taking another interrupt rather than re-checking the status
620 * registers which can re-wake the target.
621 *
622 * NOTE : for host interfaces that makes use of detecting pending
623 * mbox messages at hif can not use this optimization due to
624 * possible side effects, SPI requires the host to drain all
625 * messages from the mailbox before exiting the ISR routine.
626 */
627
628 ath6kl_dbg(ATH6KL_DBG_IRQ,
629 "bypassing irq status re-check, forcing done\n");
630
631 *done = true;
632
633 ath6kl_dbg(ATH6KL_DBG_IRQ,
634 "proc_pending_irqs: (done:%d, status=%d\n", *done, status);
635
636 return status;
637}
638
639/* interrupt handler, kicks off all interrupt processing */
640int ath6kldev_intr_bh_handler(struct ath6kl *ar)
641{
642 struct ath6kl_device *dev = ar->htc_target->dev;
643 int status = 0;
644 bool done = false;
645
646 /*
647 * Reset counter used to flag a re-scan of IRQ status registers on
648 * the target.
649 */
650 dev->chk_irq_status_cnt = 0;
651
652 /*
653 * IRQ processing is synchronous, interrupt status registers can be
654 * re-read.
655 */
656 while (!done) {
657 status = proc_pending_irqs(dev, &done);
658 if (status)
659 break;
660 }
661
662 return status;
663}
664
665static int ath6kldev_enable_intrs(struct ath6kl_device *dev)
666{
667 struct ath6kl_irq_enable_reg regs;
668 int status;
669
670 spin_lock_bh(&dev->lock);
671
672 /* Enable all but ATH6KL CPU interrupts */
673 dev->irq_en_reg.int_status_en =
674 SM(INT_STATUS_ENABLE_ERROR, 0x01) |
675 SM(INT_STATUS_ENABLE_CPU, 0x01) |
676 SM(INT_STATUS_ENABLE_COUNTER, 0x01);
677
678 /*
679 * NOTE: There are some cases where HIF can do detection of
680 * pending mbox messages which is disabled now.
681 */
682 dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
683
684 /* Set up the CPU Interrupt status Register */
685 dev->irq_en_reg.cpu_int_status_en = 0;
686
687 /* Set up the Error Interrupt status Register */
688 dev->irq_en_reg.err_int_status_en =
689 SM(ERROR_STATUS_ENABLE_RX_UNDERFLOW, 0x01) |
690 SM(ERROR_STATUS_ENABLE_TX_OVERFLOW, 0x1);
691
692 /*
693 * Enable Counter interrupt status register to get fatal errors for
694 * debugging.
695 */
696 dev->irq_en_reg.cntr_int_status_en = SM(COUNTER_INT_STATUS_ENABLE_BIT,
697 ATH6KL_TARGET_DEBUG_INTR_MASK);
698 memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
699
700 spin_unlock_bh(&dev->lock);
701
702 status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
703 &regs.int_status_en, sizeof(regs),
704 HIF_WR_SYNC_BYTE_INC);
705
706 if (status)
707 ath6kl_err("failed to update interrupt ctl reg err: %d\n",
708 status);
709
710 return status;
711}
712
713int ath6kldev_disable_intrs(struct ath6kl_device *dev)
714{
715 struct ath6kl_irq_enable_reg regs;
716
717 spin_lock_bh(&dev->lock);
718 /* Disable all interrupts */
719 dev->irq_en_reg.int_status_en = 0;
720 dev->irq_en_reg.cpu_int_status_en = 0;
721 dev->irq_en_reg.err_int_status_en = 0;
722 dev->irq_en_reg.cntr_int_status_en = 0;
723 memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
724 spin_unlock_bh(&dev->lock);
725
726 return hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
727 &regs.int_status_en, sizeof(regs),
728 HIF_WR_SYNC_BYTE_INC);
729}
730
731/* enable device interrupts */
732int ath6kldev_unmask_intrs(struct ath6kl_device *dev)
733{
734 int status = 0;
735
736 /*
737 * Make sure interrupt are disabled before unmasking at the HIF
738 * layer. The rationale here is that between device insertion
739 * (where we clear the interrupts the first time) and when HTC
740 * is finally ready to handle interrupts, other software can perform
741 * target "soft" resets. The ATH6KL interrupt enables reset back to an
742 * "enabled" state when this happens.
743 */
744 ath6kldev_disable_intrs(dev);
745
746 /* unmask the host controller interrupts */
747 ath6kl_hif_irq_enable(dev->ar);
748 status = ath6kldev_enable_intrs(dev);
749
750 return status;
751}
752
753/* disable all device interrupts */
754int ath6kldev_mask_intrs(struct ath6kl_device *dev)
755{
756 /*
757 * Mask the interrupt at the HIF layer to avoid any stray interrupt
758 * taken while we zero out our shadow registers in
759 * ath6kldev_disable_intrs().
760 */
761 ath6kl_hif_irq_disable(dev->ar);
762
763 return ath6kldev_disable_intrs(dev);
764}
765
766int ath6kldev_setup(struct ath6kl_device *dev)
767{
768 int status = 0;
769 int i;
770 struct htc_packet *packet;
771
772 /* initialize our free list of IO packets */
773 INIT_LIST_HEAD(&dev->reg_io);
774 spin_lock_init(&dev->lock);
775
776 /* carve up register I/O packets (these are for ASYNC register I/O ) */
777 for (i = 0; i < ATH6KL_MAX_REG_IO_BUFFERS; i++) {
778 packet = &dev->reg_io_buf[i].packet;
779 set_htc_rxpkt_info(packet, dev, dev->reg_io_buf[i].buf,
780 ATH6KL_REG_IO_BUFFER_SIZE, 0);
781 ath6kl_add_io_pkt(dev, packet);
782 }
783
784 /*
785 * NOTE: we actually get the block size of a mailbox other than 0,
786 * for SDIO the block size on mailbox 0 is artificially set to 1.
787 * So we use the block size that is set for the other 3 mailboxes.
788 */
789 dev->block_sz = dev->ar->mbox_info.block_size;
790
791 /* must be a power of 2 */
792 if ((dev->block_sz & (dev->block_sz - 1)) != 0) {
793 WARN_ON(1);
794 goto fail_setup;
795 }
796
797 /* assemble mask, used for padding to a block */
798 dev->block_mask = dev->block_sz - 1;
799
800 ath6kl_dbg(ATH6KL_DBG_TRC, "block size: %d, mbox addr:0x%X\n",
801 dev->block_sz, dev->ar->mbox_info.htc_addr);
802
803 ath6kl_dbg(ATH6KL_DBG_TRC,
804 "hif interrupt processing is sync only\n");
805
806 status = ath6kldev_disable_intrs(dev);
807
808fail_setup:
809 return status;
810
811}