xref: /openbmc/linux/drivers/net/wireless/ath/ath6kl/hif.c (revision 241b128b)
1 /*
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 #include "hif.h"
17 
18 #include <linux/export.h>
19 
20 #include "core.h"
21 #include "target.h"
22 #include "hif-ops.h"
23 #include "debug.h"
24 
25 #define MAILBOX_FOR_BLOCK_SIZE          1
26 
27 #define ATH6KL_TIME_QUANTUM	10  /* in ms */
28 
29 static int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req,
30 				      bool from_dma)
31 {
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 
52 int ath6kl_hif_rw_comp_handler(void *context, int status)
53 {
54 	struct htc_packet *packet = context;
55 
56 	ath6kl_dbg(ATH6KL_DBG_HIF, "hif rw completion pkt 0x%p status %d\n",
57 		   packet, status);
58 
59 	packet->status = status;
60 	packet->completion(packet->context, packet);
61 
62 	return 0;
63 }
64 EXPORT_SYMBOL(ath6kl_hif_rw_comp_handler);
65 
66 #define REG_DUMP_COUNT_AR6003   60
67 #define REGISTER_DUMP_LEN_MAX   60
68 
69 static 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 }
119 
120 static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
121 {
122 	u32 dummy;
123 	int ret;
124 
125 	ath6kl_warn("firmware crashed\n");
126 
127 	/*
128 	 * read counter to clear the interrupt, the debug error interrupt is
129 	 * counter 0.
130 	 */
131 	ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
132 				     (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
133 	if (ret)
134 		ath6kl_warn("Failed to clear debug interrupt: %d\n", ret);
135 
136 	ath6kl_hif_dump_fw_crash(dev->ar);
137 
138 	return ret;
139 }
140 
141 /* mailbox recv message polling */
142 int ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
143 			      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);
178 		ath6kl_dbg(ATH6KL_DBG_HIF, "hif retry mbox poll try %d\n", i);
179 	}
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 			 */
191 			ath6kl_hif_proc_dbg_intr(dev);
192 	}
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  */
201 int ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx)
202 {
203 	struct ath6kl_irq_enable_reg regs;
204 	int status = 0;
205 
206 	ath6kl_dbg(ATH6KL_DBG_HIF, "hif rx %s\n",
207 		   enable_rx ? "enable" : "disable");
208 
209 	/* 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 
231 int ath6kl_hif_submit_scat_req(struct ath6kl_device *dev,
232 			      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 
248 	ath6kl_dbg(ATH6KL_DBG_HIF,
249 		   "hif submit scatter request entries %d len %d mbox 0x%x %s %s\n",
250 		   scat_req->scat_entries, scat_req->len,
251 		   scat_req->addr, !read ? "async" : "sync",
252 		   (read) ? "rd" : "wr");
253 
254 	if (!read && scat_req->virt_scat) {
255 		status = ath6kl_hif_cp_scat_dma_buf(scat_req, false);
256 		if (status) {
257 			scat_req->status = status;
258 			scat_req->complete(dev->ar->htc_target, scat_req);
259 			return 0;
260 		}
261 	}
262 
263 	status = ath6kl_hif_scat_req_rw(dev->ar, scat_req);
264 
265 	if (read) {
266 		/* in sync mode, we can touch the scatter request */
267 		scat_req->status = status;
268 		if (!status && scat_req->virt_scat)
269 			scat_req->status =
270 				ath6kl_hif_cp_scat_dma_buf(scat_req, true);
271 	}
272 
273 	return status;
274 }
275 
276 static int ath6kl_hif_proc_counter_intr(struct ath6kl_device *dev)
277 {
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)
295 		return ath6kl_hif_proc_dbg_intr(dev);
296 
297 	return 0;
298 }
299 
300 static int ath6kl_hif_proc_err_intr(struct ath6kl_device *dev)
301 {
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 
345 static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev)
346 {
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 */
391 static 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 
436 		ath6kl_dump_registers(dev, &dev->irq_proc_reg,
437 				      &dev->irq_en_reg);
438 
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 		 */
478 		status = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt,
479 							  lk_ahd, &fetched);
480 		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 			 */
488 			dev->htc_cnxt->chk_irq_status_cnt = 0;
489 	}
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 */
498 		status = ath6kl_hif_proc_cpu_intr(dev);
499 		if (status)
500 			goto out;
501 	}
502 
503 	if (MS(HOST_INT_STATUS_ERROR, host_int_status)) {
504 		/* Error Interrupt */
505 		status = ath6kl_hif_proc_err_intr(dev);
506 		if (status)
507 			goto out;
508 	}
509 
510 	if (MS(HOST_INT_STATUS_COUNTER, host_int_status))
511 		/* Counter Interrupt */
512 		status = ath6kl_hif_proc_counter_intr(dev);
513 
514 out:
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 
531 	if (!dev->htc_cnxt->chk_irq_status_cnt)
532 		*done = true;
533 
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 */
541 int ath6kl_hif_intr_bh_handler(struct ath6kl *ar)
542 {
543 	struct ath6kl_device *dev = ar->htc_target->dev;
544 	unsigned long timeout;
545 	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 	 */
552 	dev->htc_cnxt->chk_irq_status_cnt = 0;
553 
554 	/*
555 	 * IRQ processing is synchronous, interrupt status registers can be
556 	 * re-read.
557 	 */
558 	timeout = jiffies + msecs_to_jiffies(ATH6KL_HIF_COMMUNICATION_TIMEOUT);
559 	while (time_before(jiffies, timeout) && !done) {
560 		status = proc_pending_irqs(dev, &done);
561 		if (status)
562 			break;
563 	}
564 
565 	return status;
566 }
567 EXPORT_SYMBOL(ath6kl_hif_intr_bh_handler);
568 
569 static int ath6kl_hif_enable_intrs(struct ath6kl_device *dev)
570 {
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 
617 int ath6kl_hif_disable_intrs(struct ath6kl_device *dev)
618 {
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 */
636 int ath6kl_hif_unmask_intrs(struct ath6kl_device *dev)
637 {
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 	 */
648 	ath6kl_hif_disable_intrs(dev);
649 
650 	/* unmask the host controller interrupts */
651 	ath6kl_hif_irq_enable(dev->ar);
652 	status = ath6kl_hif_enable_intrs(dev);
653 
654 	return status;
655 }
656 
657 /* disable all device interrupts */
658 int ath6kl_hif_mask_intrs(struct ath6kl_device *dev)
659 {
660 	/*
661 	 * Mask the interrupt at the HIF layer to avoid any stray interrupt
662 	 * taken while we zero out our shadow registers in
663 	 * ath6kl_hif_disable_intrs().
664 	 */
665 	ath6kl_hif_irq_disable(dev->ar);
666 
667 	return ath6kl_hif_disable_intrs(dev);
668 }
669 
670 int ath6kl_hif_setup(struct ath6kl_device *dev)
671 {
672 	int status = 0;
673 
674 	spin_lock_init(&dev->lock);
675 
676 	/*
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 	 */
681 	dev->htc_cnxt->block_sz = dev->ar->mbox_info.block_size;
682 
683 	/* must be a power of 2 */
684 	if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) {
685 		WARN_ON(1);
686 		status = -EINVAL;
687 		goto fail_setup;
688 	}
689 
690 	/* assemble mask, used for padding to a block */
691 	dev->htc_cnxt->block_mask = dev->htc_cnxt->block_sz - 1;
692 
693 	ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n",
694 		   dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr);
695 
696 	/* usb doesn't support enabling interrupts */
697 	/* FIXME: remove check once USB support is implemented */
698 	if (dev->ar->hif_type == ATH6KL_HIF_TYPE_USB)
699 		return 0;
700 
701 	status = ath6kl_hif_disable_intrs(dev);
702 
703 fail_setup:
704 	return status;
705 
706 }
707