blob: d912da6ab0d1afd29e7c6833e2d671c728899c92 [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 */
Kalle Valo2e1cb232011-10-05 12:23:49 +030016#include "hif.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030017
Kalle Valod6a434d2012-01-17 20:09:36 +020018#include <linux/export.h>
19
Kalle Valobdcd8172011-07-18 00:22:30 +030020#include "core.h"
21#include "target.h"
22#include "hif-ops.h"
Kalle Valobdcd8172011-07-18 00:22:30 +030023#include "debug.h"
24
25#define MAILBOX_FOR_BLOCK_SIZE 1
26
27#define ATH6KL_TIME_QUANTUM 10 /* in ms */
28
Kalle Valo8e8ddb22011-10-05 12:23:33 +030029static int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req,
30 bool from_dma)
Kalle Valobdcd8172011-07-18 00:22:30 +030031{
32 u8 *buf;
33 int i;
34
35 buf = req->virt_dma_buf;
36
37 for (i = 0; i < req->scat_entries; i++) {
38
39 if (from_dma)
40 memcpy(req->scat_list[i].buf, buf,
41 req->scat_list[i].len);
42 else
43 memcpy(buf, req->scat_list[i].buf,
44 req->scat_list[i].len);
45
46 buf += req->scat_list[i].len;
47 }
48
49 return 0;
50}
51
Kalle Valo8e8ddb22011-10-05 12:23:33 +030052int ath6kl_hif_rw_comp_handler(void *context, int status)
Kalle Valobdcd8172011-07-18 00:22:30 +030053{
54 struct htc_packet *packet = context;
55
Kalle Valo83973e02011-10-13 15:21:53 +030056 ath6kl_dbg(ATH6KL_DBG_HIF, "hif rw completion pkt 0x%p status %d\n",
Kalle Valobdcd8172011-07-18 00:22:30 +030057 packet, status);
58
59 packet->status = status;
60 packet->completion(packet->context, packet);
61
62 return 0;
63}
Kalle Valod6a434d2012-01-17 20:09:36 +020064EXPORT_SYMBOL(ath6kl_hif_rw_comp_handler);
65
Kalle Valo6250aac2011-10-30 21:16:41 +020066#define REG_DUMP_COUNT_AR6003 60
67#define REGISTER_DUMP_LEN_MAX 60
68
69static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar)
70{
71 __le32 regdump_val[REGISTER_DUMP_LEN_MAX];
72 u32 i, address, regdump_addr = 0;
73 int ret;
74
75 if (ar->target_type != TARGET_TYPE_AR6003)
76 return;
77
78 /* the reg dump pointer is copied to the host interest area */
79 address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state));
80 address = TARG_VTOP(ar->target_type, address);
81
82 /* read RAM location through diagnostic window */
83 ret = ath6kl_diag_read32(ar, address, &regdump_addr);
84
85 if (ret || !regdump_addr) {
86 ath6kl_warn("failed to get ptr to register dump area: %d\n",
87 ret);
88 return;
89 }
90
91 ath6kl_dbg(ATH6KL_DBG_IRQ, "register dump data address 0x%x\n",
92 regdump_addr);
93 regdump_addr = TARG_VTOP(ar->target_type, regdump_addr);
94
95 /* fetch register dump data */
96 ret = ath6kl_diag_read(ar, regdump_addr, (u8 *)&regdump_val[0],
97 REG_DUMP_COUNT_AR6003 * (sizeof(u32)));
98 if (ret) {
99 ath6kl_warn("failed to get register dump: %d\n", ret);
100 return;
101 }
102
103 ath6kl_info("crash dump:\n");
104 ath6kl_info("hw 0x%x fw %s\n", ar->wiphy->hw_version,
105 ar->wiphy->fw_version);
106
107 BUILD_BUG_ON(REG_DUMP_COUNT_AR6003 % 4);
108
109 for (i = 0; i < REG_DUMP_COUNT_AR6003 / 4; i++) {
110 ath6kl_info("%d: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n",
111 4 * i,
112 le32_to_cpu(regdump_val[i]),
113 le32_to_cpu(regdump_val[i + 1]),
114 le32_to_cpu(regdump_val[i + 2]),
115 le32_to_cpu(regdump_val[i + 3]));
116 }
117
118}
Kalle Valobdcd8172011-07-18 00:22:30 +0300119
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300120static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +0300121{
122 u32 dummy;
Kalle Valo6250aac2011-10-30 21:16:41 +0200123 int ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300124
Kalle Valo6250aac2011-10-30 21:16:41 +0200125 ath6kl_warn("firmware crashed\n");
Kalle Valobdcd8172011-07-18 00:22:30 +0300126
127 /*
128 * read counter to clear the interrupt, the debug error interrupt is
129 * counter 0.
130 */
Kalle Valo6250aac2011-10-30 21:16:41 +0200131 ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
Kalle Valobdcd8172011-07-18 00:22:30 +0300132 (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
Kalle Valo6250aac2011-10-30 21:16:41 +0200133 if (ret)
134 ath6kl_warn("Failed to clear debug interrupt: %d\n", ret);
Kalle Valobdcd8172011-07-18 00:22:30 +0300135
Kalle Valo6250aac2011-10-30 21:16:41 +0200136 ath6kl_hif_dump_fw_crash(dev->ar);
137
138 return ret;
Kalle Valobdcd8172011-07-18 00:22:30 +0300139}
140
141/* mailbox recv message polling */
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300142int ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
Kalle Valobdcd8172011-07-18 00:22:30 +0300143 int timeout)
144{
145 struct ath6kl_irq_proc_registers *rg;
146 int status = 0, i;
147 u8 htc_mbox = 1 << HTC_MAILBOX;
148
149 for (i = timeout / ATH6KL_TIME_QUANTUM; i > 0; i--) {
150 /* this is the standard HIF way, load the reg table */
151 status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
152 (u8 *) &dev->irq_proc_reg,
153 sizeof(dev->irq_proc_reg),
154 HIF_RD_SYNC_BYTE_INC);
155
156 if (status) {
157 ath6kl_err("failed to read reg table\n");
158 return status;
159 }
160
161 /* check for MBOX data and valid lookahead */
162 if (dev->irq_proc_reg.host_int_status & htc_mbox) {
163 if (dev->irq_proc_reg.rx_lkahd_valid &
164 htc_mbox) {
165 /*
166 * Mailbox has a message and the look ahead
167 * is valid.
168 */
169 rg = &dev->irq_proc_reg;
170 *lk_ahd =
171 le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
172 break;
173 }
174 }
175
176 /* delay a little */
177 mdelay(ATH6KL_TIME_QUANTUM);
Kalle Valo83973e02011-10-13 15:21:53 +0300178 ath6kl_dbg(ATH6KL_DBG_HIF, "hif retry mbox poll try %d\n", i);
Kalle Valobdcd8172011-07-18 00:22:30 +0300179 }
180
181 if (i == 0) {
182 ath6kl_err("timeout waiting for recv message\n");
183 status = -ETIME;
184 /* check if the target asserted */
185 if (dev->irq_proc_reg.counter_int_status &
186 ATH6KL_TARGET_DEBUG_INTR_MASK)
187 /*
188 * Target failure handler will be called in case of
189 * an assert.
190 */
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300191 ath6kl_hif_proc_dbg_intr(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300192 }
193
194 return status;
195}
196
197/*
198 * Disable packet reception (used in case the host runs out of buffers)
199 * using the interrupt enable registers through the host I/F
200 */
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300201int ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx)
Kalle Valobdcd8172011-07-18 00:22:30 +0300202{
203 struct ath6kl_irq_enable_reg regs;
204 int status = 0;
205
Kalle Valo83973e02011-10-13 15:21:53 +0300206 ath6kl_dbg(ATH6KL_DBG_HIF, "hif rx %s\n",
207 enable_rx ? "enable" : "disable");
208
Kalle Valobdcd8172011-07-18 00:22:30 +0300209 /* take the lock to protect interrupt enable shadows */
210 spin_lock_bh(&dev->lock);
211
212 if (enable_rx)
213 dev->irq_en_reg.int_status_en |=
214 SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
215 else
216 dev->irq_en_reg.int_status_en &=
217 ~SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
218
219 memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
220
221 spin_unlock_bh(&dev->lock);
222
223 status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
224 &regs.int_status_en,
225 sizeof(struct ath6kl_irq_enable_reg),
226 HIF_WR_SYNC_BYTE_INC);
227
228 return status;
229}
230
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300231int ath6kl_hif_submit_scat_req(struct ath6kl_device *dev,
Kalle Valobdcd8172011-07-18 00:22:30 +0300232 struct hif_scatter_req *scat_req, bool read)
233{
234 int status = 0;
235
236 if (read) {
237 scat_req->req = HIF_RD_SYNC_BLOCK_FIX;
238 scat_req->addr = dev->ar->mbox_info.htc_addr;
239 } else {
240 scat_req->req = HIF_WR_ASYNC_BLOCK_INC;
241
242 scat_req->addr =
243 (scat_req->len > HIF_MBOX_WIDTH) ?
244 dev->ar->mbox_info.htc_ext_addr :
245 dev->ar->mbox_info.htc_addr;
246 }
247
Kalle Valo83973e02011-10-13 15:21:53 +0300248 ath6kl_dbg(ATH6KL_DBG_HIF,
249 "hif submit scatter request entries %d len %d mbox 0x%x %s %s\n",
Kalle Valobdcd8172011-07-18 00:22:30 +0300250 scat_req->scat_entries, scat_req->len,
251 scat_req->addr, !read ? "async" : "sync",
252 (read) ? "rd" : "wr");
253
Vasanthakumar Thiagarajan23b78402011-07-18 14:23:25 +0530254 if (!read && scat_req->virt_scat) {
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300255 status = ath6kl_hif_cp_scat_dma_buf(scat_req, false);
Vasanthakumar Thiagarajan23b78402011-07-18 14:23:25 +0530256 if (status) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300257 scat_req->status = status;
Vasanthakumar Thiagarajane041c7f2011-07-16 20:29:09 +0530258 scat_req->complete(dev->ar->htc_target, scat_req);
Kalle Valobdcd8172011-07-18 00:22:30 +0300259 return 0;
260 }
Kalle Valobdcd8172011-07-18 00:22:30 +0300261 }
262
Vasanthakumar Thiagarajan348a8fb2011-07-16 20:29:17 +0530263 status = ath6kl_hif_scat_req_rw(dev->ar, scat_req);
Kalle Valobdcd8172011-07-18 00:22:30 +0300264
265 if (read) {
266 /* in sync mode, we can touch the scatter request */
267 scat_req->status = status;
Vasanthakumar Thiagarajan4a005c32011-07-16 20:29:15 +0530268 if (!status && scat_req->virt_scat)
Kalle Valobdcd8172011-07-18 00:22:30 +0300269 scat_req->status =
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300270 ath6kl_hif_cp_scat_dma_buf(scat_req, true);
Kalle Valobdcd8172011-07-18 00:22:30 +0300271 }
272
273 return status;
274}
275
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300276static int ath6kl_hif_proc_counter_intr(struct ath6kl_device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +0300277{
278 u8 counter_int_status;
279
280 ath6kl_dbg(ATH6KL_DBG_IRQ, "counter interrupt\n");
281
282 counter_int_status = dev->irq_proc_reg.counter_int_status &
283 dev->irq_en_reg.cntr_int_status_en;
284
285 ath6kl_dbg(ATH6KL_DBG_IRQ,
286 "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
287 counter_int_status);
288
289 /*
290 * NOTE: other modules like GMBOX may use the counter interrupt for
291 * credit flow control on other counters, we only need to check for
292 * the debug assertion counter interrupt.
293 */
294 if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK)
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300295 return ath6kl_hif_proc_dbg_intr(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300296
297 return 0;
298}
299
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300300static int ath6kl_hif_proc_err_intr(struct ath6kl_device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +0300301{
302 int status;
303 u8 error_int_status;
304 u8 reg_buf[4];
305
306 ath6kl_dbg(ATH6KL_DBG_IRQ, "error interrupt\n");
307
308 error_int_status = dev->irq_proc_reg.error_int_status & 0x0F;
309 if (!error_int_status) {
310 WARN_ON(1);
311 return -EIO;
312 }
313
314 ath6kl_dbg(ATH6KL_DBG_IRQ,
315 "valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n",
316 error_int_status);
317
318 if (MS(ERROR_INT_STATUS_WAKEUP, error_int_status))
319 ath6kl_dbg(ATH6KL_DBG_IRQ, "error : wakeup\n");
320
321 if (MS(ERROR_INT_STATUS_RX_UNDERFLOW, error_int_status))
322 ath6kl_err("rx underflow\n");
323
324 if (MS(ERROR_INT_STATUS_TX_OVERFLOW, error_int_status))
325 ath6kl_err("tx overflow\n");
326
327 /* Clear the interrupt */
328 dev->irq_proc_reg.error_int_status &= ~error_int_status;
329
330 /* set W1C value to clear the interrupt, this hits the register first */
331 reg_buf[0] = error_int_status;
332 reg_buf[1] = 0;
333 reg_buf[2] = 0;
334 reg_buf[3] = 0;
335
336 status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS,
337 reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
338
339 if (status)
340 WARN_ON(1);
341
342 return status;
343}
344
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300345static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +0300346{
347 int status;
348 u8 cpu_int_status;
349 u8 reg_buf[4];
350
351 ath6kl_dbg(ATH6KL_DBG_IRQ, "cpu interrupt\n");
352
353 cpu_int_status = dev->irq_proc_reg.cpu_int_status &
354 dev->irq_en_reg.cpu_int_status_en;
355 if (!cpu_int_status) {
356 WARN_ON(1);
357 return -EIO;
358 }
359
360 ath6kl_dbg(ATH6KL_DBG_IRQ,
361 "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
362 cpu_int_status);
363
364 /* Clear the interrupt */
365 dev->irq_proc_reg.cpu_int_status &= ~cpu_int_status;
366
367 /*
368 * Set up the register transfer buffer to hit the register 4 times ,
369 * this is done to make the access 4-byte aligned to mitigate issues
370 * with host bus interconnects that restrict bus transfer lengths to
371 * be a multiple of 4-bytes.
372 */
373
374 /* set W1C value to clear the interrupt, this hits the register first */
375 reg_buf[0] = cpu_int_status;
376 /* the remaining are set to zero which have no-effect */
377 reg_buf[1] = 0;
378 reg_buf[2] = 0;
379 reg_buf[3] = 0;
380
381 status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS,
382 reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
383
384 if (status)
385 WARN_ON(1);
386
387 return status;
388}
389
390/* process pending interrupts synchronously */
391static int proc_pending_irqs(struct ath6kl_device *dev, bool *done)
392{
393 struct ath6kl_irq_proc_registers *rg;
394 int status = 0;
395 u8 host_int_status = 0;
396 u32 lk_ahd = 0;
397 u8 htc_mbox = 1 << HTC_MAILBOX;
398
399 ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev);
400
401 /*
402 * NOTE: HIF implementation guarantees that the context of this
403 * call allows us to perform SYNCHRONOUS I/O, that is we can block,
404 * sleep or call any API that can block or switch thread/task
405 * contexts. This is a fully schedulable context.
406 */
407
408 /*
409 * Process pending intr only when int_status_en is clear, it may
410 * result in unnecessary bus transaction otherwise. Target may be
411 * unresponsive at the time.
412 */
413 if (dev->irq_en_reg.int_status_en) {
414 /*
415 * Read the first 28 bytes of the HTC register table. This
416 * will yield us the value of different int status
417 * registers and the lookahead registers.
418 *
419 * length = sizeof(int_status) + sizeof(cpu_int_status)
420 * + sizeof(error_int_status) +
421 * sizeof(counter_int_status) +
422 * sizeof(mbox_frame) + sizeof(rx_lkahd_valid)
423 * + sizeof(hole) + sizeof(rx_lkahd) +
424 * sizeof(int_status_en) +
425 * sizeof(cpu_int_status_en) +
426 * sizeof(err_int_status_en) +
427 * sizeof(cntr_int_status_en);
428 */
429 status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
430 (u8 *) &dev->irq_proc_reg,
431 sizeof(dev->irq_proc_reg),
432 HIF_RD_SYNC_BYTE_INC);
433 if (status)
434 goto out;
435
Kalle Valo5afa5aa2012-01-17 20:09:19 +0200436 ath6kl_dump_registers(dev, &dev->irq_proc_reg,
437 &dev->irq_en_reg);
Kalle Valobdcd8172011-07-18 00:22:30 +0300438
439 /* Update only those registers that are enabled */
440 host_int_status = dev->irq_proc_reg.host_int_status &
441 dev->irq_en_reg.int_status_en;
442
443 /* Look at mbox status */
444 if (host_int_status & htc_mbox) {
445 /*
446 * Mask out pending mbox value, we use "lookAhead as
447 * the real flag for mbox processing.
448 */
449 host_int_status &= ~htc_mbox;
450 if (dev->irq_proc_reg.rx_lkahd_valid &
451 htc_mbox) {
452 rg = &dev->irq_proc_reg;
453 lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
454 if (!lk_ahd)
455 ath6kl_err("lookAhead is zero!\n");
456 }
457 }
458 }
459
460 if (!host_int_status && !lk_ahd) {
461 *done = true;
462 goto out;
463 }
464
465 if (lk_ahd) {
466 int fetched = 0;
467
468 ath6kl_dbg(ATH6KL_DBG_IRQ,
469 "pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd);
470 /*
471 * Mailbox Interrupt, the HTC layer may issue async
472 * requests to empty the mailbox. When emptying the recv
473 * mailbox we use the async handler above called from the
474 * completion routine of the callers read request. This can
475 * improve performance by reducing context switching when
476 * we rapidly pull packets.
477 */
Kalle Vaload226ec2011-08-10 09:49:12 +0300478 status = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt,
Vasanthakumar Thiagarajan4533d902011-10-03 17:26:27 +0530479 lk_ahd, &fetched);
Kalle Valobdcd8172011-07-18 00:22:30 +0300480 if (status)
481 goto out;
482
483 if (!fetched)
484 /*
485 * HTC could not pull any messages out due to lack
486 * of resources.
487 */
Vasanthakumar Thiagarajanfcb82052011-07-18 14:23:31 +0530488 dev->htc_cnxt->chk_irq_status_cnt = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300489 }
490
491 /* now handle the rest of them */
492 ath6kl_dbg(ATH6KL_DBG_IRQ,
493 "valid interrupt source(s) for other interrupts: 0x%x\n",
494 host_int_status);
495
496 if (MS(HOST_INT_STATUS_CPU, host_int_status)) {
497 /* CPU Interrupt */
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300498 status = ath6kl_hif_proc_cpu_intr(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300499 if (status)
500 goto out;
501 }
502
503 if (MS(HOST_INT_STATUS_ERROR, host_int_status)) {
504 /* Error Interrupt */
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300505 status = ath6kl_hif_proc_err_intr(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300506 if (status)
507 goto out;
508 }
509
510 if (MS(HOST_INT_STATUS_COUNTER, host_int_status))
511 /* Counter Interrupt */
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300512 status = ath6kl_hif_proc_counter_intr(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300513
514out:
515 /*
516 * An optimization to bypass reading the IRQ status registers
517 * unecessarily which can re-wake the target, if upper layers
518 * determine that we are in a low-throughput mode, we can rely on
519 * taking another interrupt rather than re-checking the status
520 * registers which can re-wake the target.
521 *
522 * NOTE : for host interfaces that makes use of detecting pending
523 * mbox messages at hif can not use this optimization due to
524 * possible side effects, SPI requires the host to drain all
525 * messages from the mailbox before exiting the ISR routine.
526 */
527
528 ath6kl_dbg(ATH6KL_DBG_IRQ,
529 "bypassing irq status re-check, forcing done\n");
530
Vasanthakumar Thiagarajanfcb82052011-07-18 14:23:31 +0530531 if (!dev->htc_cnxt->chk_irq_status_cnt)
Vasanthakumar Thiagarajan7520ceb2011-07-18 14:23:30 +0530532 *done = true;
Kalle Valobdcd8172011-07-18 00:22:30 +0300533
534 ath6kl_dbg(ATH6KL_DBG_IRQ,
535 "proc_pending_irqs: (done:%d, status=%d\n", *done, status);
536
537 return status;
538}
539
540/* interrupt handler, kicks off all interrupt processing */
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300541int ath6kl_hif_intr_bh_handler(struct ath6kl *ar)
Kalle Valobdcd8172011-07-18 00:22:30 +0300542{
543 struct ath6kl_device *dev = ar->htc_target->dev;
Kalle Valod60e8ab2011-10-27 18:48:52 +0300544 unsigned long timeout;
Kalle Valobdcd8172011-07-18 00:22:30 +0300545 int status = 0;
546 bool done = false;
547
548 /*
549 * Reset counter used to flag a re-scan of IRQ status registers on
550 * the target.
551 */
Vasanthakumar Thiagarajanfcb82052011-07-18 14:23:31 +0530552 dev->htc_cnxt->chk_irq_status_cnt = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300553
554 /*
555 * IRQ processing is synchronous, interrupt status registers can be
556 * re-read.
557 */
Kalle Valod60e8ab2011-10-27 18:48:52 +0300558 timeout = jiffies + msecs_to_jiffies(ATH6KL_HIF_COMMUNICATION_TIMEOUT);
559 while (time_before(jiffies, timeout) && !done) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300560 status = proc_pending_irqs(dev, &done);
561 if (status)
562 break;
563 }
564
565 return status;
566}
Kalle Valod6a434d2012-01-17 20:09:36 +0200567EXPORT_SYMBOL(ath6kl_hif_intr_bh_handler);
Kalle Valobdcd8172011-07-18 00:22:30 +0300568
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300569static int ath6kl_hif_enable_intrs(struct ath6kl_device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +0300570{
571 struct ath6kl_irq_enable_reg regs;
572 int status;
573
574 spin_lock_bh(&dev->lock);
575
576 /* Enable all but ATH6KL CPU interrupts */
577 dev->irq_en_reg.int_status_en =
578 SM(INT_STATUS_ENABLE_ERROR, 0x01) |
579 SM(INT_STATUS_ENABLE_CPU, 0x01) |
580 SM(INT_STATUS_ENABLE_COUNTER, 0x01);
581
582 /*
583 * NOTE: There are some cases where HIF can do detection of
584 * pending mbox messages which is disabled now.
585 */
586 dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
587
588 /* Set up the CPU Interrupt status Register */
589 dev->irq_en_reg.cpu_int_status_en = 0;
590
591 /* Set up the Error Interrupt status Register */
592 dev->irq_en_reg.err_int_status_en =
593 SM(ERROR_STATUS_ENABLE_RX_UNDERFLOW, 0x01) |
594 SM(ERROR_STATUS_ENABLE_TX_OVERFLOW, 0x1);
595
596 /*
597 * Enable Counter interrupt status register to get fatal errors for
598 * debugging.
599 */
600 dev->irq_en_reg.cntr_int_status_en = SM(COUNTER_INT_STATUS_ENABLE_BIT,
601 ATH6KL_TARGET_DEBUG_INTR_MASK);
602 memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
603
604 spin_unlock_bh(&dev->lock);
605
606 status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
607 &regs.int_status_en, sizeof(regs),
608 HIF_WR_SYNC_BYTE_INC);
609
610 if (status)
611 ath6kl_err("failed to update interrupt ctl reg err: %d\n",
612 status);
613
614 return status;
615}
616
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300617int ath6kl_hif_disable_intrs(struct ath6kl_device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +0300618{
619 struct ath6kl_irq_enable_reg regs;
620
621 spin_lock_bh(&dev->lock);
622 /* Disable all interrupts */
623 dev->irq_en_reg.int_status_en = 0;
624 dev->irq_en_reg.cpu_int_status_en = 0;
625 dev->irq_en_reg.err_int_status_en = 0;
626 dev->irq_en_reg.cntr_int_status_en = 0;
627 memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
628 spin_unlock_bh(&dev->lock);
629
630 return hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
631 &regs.int_status_en, sizeof(regs),
632 HIF_WR_SYNC_BYTE_INC);
633}
634
635/* enable device interrupts */
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300636int ath6kl_hif_unmask_intrs(struct ath6kl_device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +0300637{
638 int status = 0;
639
640 /*
641 * Make sure interrupt are disabled before unmasking at the HIF
642 * layer. The rationale here is that between device insertion
643 * (where we clear the interrupts the first time) and when HTC
644 * is finally ready to handle interrupts, other software can perform
645 * target "soft" resets. The ATH6KL interrupt enables reset back to an
646 * "enabled" state when this happens.
647 */
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300648 ath6kl_hif_disable_intrs(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300649
650 /* unmask the host controller interrupts */
651 ath6kl_hif_irq_enable(dev->ar);
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300652 status = ath6kl_hif_enable_intrs(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300653
654 return status;
655}
656
657/* disable all device interrupts */
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300658int ath6kl_hif_mask_intrs(struct ath6kl_device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +0300659{
660 /*
661 * Mask the interrupt at the HIF layer to avoid any stray interrupt
662 * taken while we zero out our shadow registers in
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300663 * ath6kl_hif_disable_intrs().
Kalle Valobdcd8172011-07-18 00:22:30 +0300664 */
665 ath6kl_hif_irq_disable(dev->ar);
666
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300667 return ath6kl_hif_disable_intrs(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300668}
669
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300670int ath6kl_hif_setup(struct ath6kl_device *dev)
Kalle Valobdcd8172011-07-18 00:22:30 +0300671{
672 int status = 0;
Kalle Valobdcd8172011-07-18 00:22:30 +0300673
Kalle Valobdcd8172011-07-18 00:22:30 +0300674 spin_lock_init(&dev->lock);
675
Kalle Valobdcd8172011-07-18 00:22:30 +0300676 /*
677 * NOTE: we actually get the block size of a mailbox other than 0,
678 * for SDIO the block size on mailbox 0 is artificially set to 1.
679 * So we use the block size that is set for the other 3 mailboxes.
680 */
Vasanthakumar Thiagarajan5be88242011-07-18 14:23:28 +0530681 dev->htc_cnxt->block_sz = dev->ar->mbox_info.block_size;
Kalle Valobdcd8172011-07-18 00:22:30 +0300682
683 /* must be a power of 2 */
Vasanthakumar Thiagarajan5be88242011-07-18 14:23:28 +0530684 if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) {
Kalle Valobdcd8172011-07-18 00:22:30 +0300685 WARN_ON(1);
Kalle Valob4be8952011-10-05 12:23:25 +0300686 status = -EINVAL;
Kalle Valobdcd8172011-07-18 00:22:30 +0300687 goto fail_setup;
688 }
689
690 /* assemble mask, used for padding to a block */
Vasanthakumar Thiagarajan5be88242011-07-18 14:23:28 +0530691 dev->htc_cnxt->block_mask = dev->htc_cnxt->block_sz - 1;
Kalle Valobdcd8172011-07-18 00:22:30 +0300692
Kalle Valo83973e02011-10-13 15:21:53 +0300693 ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n",
Vasanthakumar Thiagarajan5be88242011-07-18 14:23:28 +0530694 dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr);
Kalle Valobdcd8172011-07-18 00:22:30 +0300695
Kalle Valo8e8ddb22011-10-05 12:23:33 +0300696 status = ath6kl_hif_disable_intrs(dev);
Kalle Valobdcd8172011-07-18 00:22:30 +0300697
698fail_setup:
699 return status;
700
701}