1 //------------------------------------------------------------------------------
2 // <copyright file="ar6k_gmbox.c" company="Atheros">
3 // Copyright (c) 2007-2010 Atheros Corporation. All rights reserved.
6 // Permission to use, copy, modify, and/or distribute this software for any
7 // purpose with or without fee is hereby granted, provided that the above
8 // copyright notice and this permission notice appear in all copies.
10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 //------------------------------------------------------------------------------
20 //==============================================================================
21 // Generic MBOX API implementation
23 // Author(s): ="Atheros"
24 //==============================================================================
28 #include "../htc_debug.h"
30 #include "htc_packet.h"
32 #include "hw/mbox_host_reg.h"
36 * This file provides management functions and a toolbox for GMBOX protocol modules.
37 * Only one protocol module can be installed at a time. The determination of which protocol
38 * module is installed is determined at compile time.
41 #ifdef ATH_AR6K_ENABLE_GMBOX
42 /* GMBOX definitions */
43 #define GMBOX_INT_STATUS_ENABLE_REG 0x488
44 #define GMBOX_INT_STATUS_RX_DATA (1 << 0)
45 #define GMBOX_INT_STATUS_TX_OVERFLOW (1 << 1)
46 #define GMBOX_INT_STATUS_RX_OVERFLOW (1 << 2)
48 #define GMBOX_LOOKAHEAD_MUX_REG 0x498
49 #define GMBOX_LA_MUX_OVERRIDE_2_3 (1 << 0)
51 #define AR6K_GMBOX_CREDIT_DEC_ADDRESS (COUNT_DEC_ADDRESS + 4 * AR6K_GMBOX_CREDIT_COUNTER)
52 #define AR6K_GMBOX_CREDIT_SIZE_ADDRESS (COUNT_ADDRESS + AR6K_GMBOX_CREDIT_SIZE_COUNTER)
55 /* external APIs for allocating and freeing internal I/O packets to handle ASYNC I/O */
56 extern void AR6KFreeIOPacket(struct ar6k_device *pDev, struct htc_packet *pPacket);
57 extern struct htc_packet *AR6KAllocIOPacket(struct ar6k_device *pDev);
60 /* callback when our fetch to enable/disable completes */
61 static void DevGMboxIRQActionAsyncHandler(void *Context, struct htc_packet *pPacket)
63 struct ar6k_device *pDev = (struct ar6k_device *)Context;
65 AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevGMboxIRQActionAsyncHandler: (dev: 0x%lX)\n", (unsigned long)pDev));
67 if (pPacket->Status) {
68 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
69 ("IRQAction Operation (%d) failed! status:%d \n", pPacket->PktInfo.AsRx.HTCRxFlags,pPacket->Status));
71 /* free this IO packet */
72 AR6KFreeIOPacket(pDev,pPacket);
73 AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevGMboxIRQActionAsyncHandler \n"));
76 static int DevGMboxCounterEnableDisable(struct ar6k_device *pDev, GMBOX_IRQ_ACTION_TYPE IrqAction, bool AsyncMode)
79 struct ar6k_irq_enable_registers regs;
80 struct htc_packet *pIOPacket = NULL;
84 if (GMBOX_CREDIT_IRQ_ENABLE == IrqAction) {
85 pDev->GMboxInfo.CreditCountIRQEnabled = true;
86 pDev->IrqEnableRegisters.counter_int_status_enable |=
87 COUNTER_INT_STATUS_ENABLE_BIT_SET(1 << AR6K_GMBOX_CREDIT_COUNTER);
88 pDev->IrqEnableRegisters.int_status_enable |= INT_STATUS_ENABLE_COUNTER_SET(0x01);
90 pDev->GMboxInfo.CreditCountIRQEnabled = false;
91 pDev->IrqEnableRegisters.counter_int_status_enable &=
92 ~(COUNTER_INT_STATUS_ENABLE_BIT_SET(1 << AR6K_GMBOX_CREDIT_COUNTER));
94 /* copy into our temp area */
95 memcpy(®s,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE);
103 pIOPacket = AR6KAllocIOPacket(pDev);
105 if (NULL == pIOPacket) {
106 status = A_NO_MEMORY;
111 /* copy values to write to our async I/O buffer */
112 memcpy(pIOPacket->pBuffer,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE);
114 /* stick in our completion routine when the I/O operation completes */
115 pIOPacket->Completion = DevGMboxIRQActionAsyncHandler;
116 pIOPacket->pContext = pDev;
117 pIOPacket->PktInfo.AsRx.HTCRxFlags = IrqAction;
118 /* write it out asynchronously */
119 HIFReadWrite(pDev->HIFDevice,
120 INT_STATUS_ENABLE_ADDRESS,
122 AR6K_IRQ_ENABLE_REGS_SIZE,
123 HIF_WR_ASYNC_BYTE_INC,
130 /* if we get here we are doing it synchronously */
131 status = HIFReadWrite(pDev->HIFDevice,
132 INT_STATUS_ENABLE_ADDRESS,
133 ®s.int_status_enable,
134 AR6K_IRQ_ENABLE_REGS_SIZE,
135 HIF_WR_SYNC_BYTE_INC,
140 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
141 (" IRQAction Operation (%d) failed! status:%d \n", IrqAction, status));
144 AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
145 (" IRQAction Operation (%d) success \n", IrqAction));
149 if (pIOPacket != NULL) {
150 AR6KFreeIOPacket(pDev,pIOPacket);
157 int DevGMboxIRQAction(struct ar6k_device *pDev, GMBOX_IRQ_ACTION_TYPE IrqAction, bool AsyncMode)
160 struct htc_packet *pIOPacket = NULL;
161 u8 GMboxIntControl[4];
163 if (GMBOX_CREDIT_IRQ_ENABLE == IrqAction) {
164 return DevGMboxCounterEnableDisable(pDev, GMBOX_CREDIT_IRQ_ENABLE, AsyncMode);
165 } else if(GMBOX_CREDIT_IRQ_DISABLE == IrqAction) {
166 return DevGMboxCounterEnableDisable(pDev, GMBOX_CREDIT_IRQ_DISABLE, AsyncMode);
169 if (GMBOX_DISABLE_ALL == IrqAction) {
170 /* disable credit IRQ, those are on a different set of registers */
171 DevGMboxCounterEnableDisable(pDev, GMBOX_CREDIT_IRQ_DISABLE, AsyncMode);
174 /* take the lock to protect interrupt enable shadows */
179 case GMBOX_DISABLE_ALL:
180 pDev->GMboxControlRegisters.int_status_enable = 0;
182 case GMBOX_ERRORS_IRQ_ENABLE:
183 pDev->GMboxControlRegisters.int_status_enable |= GMBOX_INT_STATUS_TX_OVERFLOW |
184 GMBOX_INT_STATUS_RX_OVERFLOW;
186 case GMBOX_RECV_IRQ_ENABLE:
187 pDev->GMboxControlRegisters.int_status_enable |= GMBOX_INT_STATUS_RX_DATA;
189 case GMBOX_RECV_IRQ_DISABLE:
190 pDev->GMboxControlRegisters.int_status_enable &= ~GMBOX_INT_STATUS_RX_DATA;
192 case GMBOX_ACTION_NONE:
198 GMboxIntControl[0] = pDev->GMboxControlRegisters.int_status_enable;
199 GMboxIntControl[1] = GMboxIntControl[0];
200 GMboxIntControl[2] = GMboxIntControl[0];
201 GMboxIntControl[3] = GMboxIntControl[0];
209 pIOPacket = AR6KAllocIOPacket(pDev);
211 if (NULL == pIOPacket) {
212 status = A_NO_MEMORY;
217 /* copy values to write to our async I/O buffer */
218 memcpy(pIOPacket->pBuffer,GMboxIntControl,sizeof(GMboxIntControl));
220 /* stick in our completion routine when the I/O operation completes */
221 pIOPacket->Completion = DevGMboxIRQActionAsyncHandler;
222 pIOPacket->pContext = pDev;
223 pIOPacket->PktInfo.AsRx.HTCRxFlags = IrqAction;
224 /* write it out asynchronously */
225 HIFReadWrite(pDev->HIFDevice,
226 GMBOX_INT_STATUS_ENABLE_REG,
228 sizeof(GMboxIntControl),
229 HIF_WR_ASYNC_BYTE_FIX,
235 /* if we get here we are doing it synchronously */
237 status = HIFReadWrite(pDev->HIFDevice,
238 GMBOX_INT_STATUS_ENABLE_REG,
240 sizeof(GMboxIntControl),
241 HIF_WR_SYNC_BYTE_FIX,
247 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
248 (" IRQAction Operation (%d) failed! status:%d \n", IrqAction, status));
251 AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
252 (" IRQAction Operation (%d) success \n", IrqAction));
256 if (pIOPacket != NULL) {
257 AR6KFreeIOPacket(pDev,pIOPacket);
263 void DevCleanupGMbox(struct ar6k_device *pDev)
265 if (pDev->GMboxEnabled) {
266 pDev->GMboxEnabled = false;
267 GMboxProtocolUninstall(pDev);
271 int DevSetupGMbox(struct ar6k_device *pDev)
278 if (0 == pDev->MailBoxInfo.GMboxAddress) {
282 AR_DEBUG_PRINTF(ATH_DEBUG_ANY,(" GMBOX Advertised: Address:0x%X , size:%d \n",
283 pDev->MailBoxInfo.GMboxAddress, pDev->MailBoxInfo.GMboxSize));
285 status = DevGMboxIRQAction(pDev, GMBOX_DISABLE_ALL, PROC_IO_SYNC);
291 /* write to mailbox look ahead mux control register, we want the
292 * GMBOX lookaheads to appear on lookaheads 2 and 3
293 * the register is 1-byte wide so we need to hit it 4 times to align the operation
295 muxControl[0] = GMBOX_LA_MUX_OVERRIDE_2_3;
296 muxControl[1] = GMBOX_LA_MUX_OVERRIDE_2_3;
297 muxControl[2] = GMBOX_LA_MUX_OVERRIDE_2_3;
298 muxControl[3] = GMBOX_LA_MUX_OVERRIDE_2_3;
300 status = HIFReadWrite(pDev->HIFDevice,
301 GMBOX_LOOKAHEAD_MUX_REG,
304 HIF_WR_SYNC_BYTE_FIX, /* hit this register 4 times */
311 status = GMboxProtocolInstall(pDev);
317 pDev->GMboxEnabled = true;
324 int DevCheckGMboxInterrupts(struct ar6k_device *pDev)
327 u8 counter_int_status;
331 AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("+DevCheckGMboxInterrupts \n"));
333 /* the caller guarantees that this is a context that allows for blocking I/O */
337 host_int_status2 = pDev->IrqProcRegisters.host_int_status2 &
338 pDev->GMboxControlRegisters.int_status_enable;
340 if (host_int_status2 & GMBOX_INT_STATUS_TX_OVERFLOW) {
341 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("GMBOX : TX Overflow \n"));
345 if (host_int_status2 & GMBOX_INT_STATUS_RX_OVERFLOW) {
346 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("GMBOX : RX Overflow \n"));
351 if (pDev->GMboxInfo.pTargetFailureCallback != NULL) {
352 pDev->GMboxInfo.pTargetFailureCallback(pDev->GMboxInfo.pProtocolContext, status);
357 if (host_int_status2 & GMBOX_INT_STATUS_RX_DATA) {
358 if (pDev->IrqProcRegisters.gmbox_rx_avail > 0) {
359 A_ASSERT(pDev->GMboxInfo.pMessagePendingCallBack != NULL);
360 status = pDev->GMboxInfo.pMessagePendingCallBack(
361 pDev->GMboxInfo.pProtocolContext,
362 (u8 *)&pDev->IrqProcRegisters.rx_gmbox_lookahead_alias[0],
363 pDev->IrqProcRegisters.gmbox_rx_avail);
371 counter_int_status = pDev->IrqProcRegisters.counter_int_status &
372 pDev->IrqEnableRegisters.counter_int_status_enable;
374 /* check if credit interrupt is pending */
375 if (counter_int_status & (COUNTER_INT_STATUS_ENABLE_BIT_SET(1 << AR6K_GMBOX_CREDIT_COUNTER))) {
377 /* do synchronous read */
378 status = DevGMboxReadCreditCounter(pDev, PROC_IO_SYNC, &credits);
384 A_ASSERT(pDev->GMboxInfo.pCreditsPendingCallback != NULL);
385 status = pDev->GMboxInfo.pCreditsPendingCallback(pDev->GMboxInfo.pProtocolContext,
387 pDev->GMboxInfo.CreditCountIRQEnabled);
392 AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("-DevCheckGMboxInterrupts (%d) \n",status));
398 int DevGMboxWrite(struct ar6k_device *pDev, struct htc_packet *pPacket, u32 WriteLength)
401 bool sync = (pPacket->Completion == NULL) ? true : false;
405 /* adjust the length to be a multiple of block size if appropriate */
406 paddedLength = DEV_CALC_SEND_PADDED_LEN(pDev, WriteLength);
408 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
409 ("DevGMboxWrite, Padded Length: %d Mbox:0x%X (mode:%s)\n",
411 pDev->MailBoxInfo.GMboxAddress,
412 sync ? "SYNC" : "ASYNC"));
414 /* last byte of packet has to hit the EOM marker */
415 address = pDev->MailBoxInfo.GMboxAddress + pDev->MailBoxInfo.GMboxSize - paddedLength;
417 status = HIFReadWrite(pDev->HIFDevice,
420 paddedLength, /* the padded length */
421 sync ? HIF_WR_SYNC_BLOCK_INC : HIF_WR_ASYNC_BLOCK_INC,
422 sync ? NULL : pPacket); /* pass the packet as the context to the HIF request */
425 pPacket->Status = status;
427 if (status == A_PENDING) {
435 int DevGMboxRead(struct ar6k_device *pDev, struct htc_packet *pPacket, u32 ReadLength)
440 bool sync = (pPacket->Completion == NULL) ? true : false;
442 /* adjust the length to be a multiple of block size if appropriate */
443 paddedLength = DEV_CALC_RECV_PADDED_LEN(pDev, ReadLength);
445 if (paddedLength > pPacket->BufferLength) {
447 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
448 ("DevGMboxRead, Not enough space for padlen:%d recvlen:%d bufferlen:%d \n",
449 paddedLength,ReadLength,pPacket->BufferLength));
450 if (pPacket->Completion != NULL) {
451 COMPLETE_HTC_PACKET(pPacket,A_EINVAL);
457 AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
458 ("DevGMboxRead (0x%lX : hdr:0x%X) Padded Length: %d Mbox:0x%X (mode:%s)\n",
459 (unsigned long)pPacket, pPacket->PktInfo.AsRx.ExpectedHdr,
461 pDev->MailBoxInfo.GMboxAddress,
462 sync ? "SYNC" : "ASYNC"));
464 status = HIFReadWrite(pDev->HIFDevice,
465 pDev->MailBoxInfo.GMboxAddress,
468 sync ? HIF_RD_SYNC_BLOCK_FIX : HIF_RD_ASYNC_BLOCK_FIX,
469 sync ? NULL : pPacket); /* pass the packet as the context to the HIF request */
472 pPacket->Status = status;
479 static int ProcessCreditCounterReadBuffer(u8 *pBuffer, int Length)
483 /* theory of how this works:
484 * We read the credit decrement register multiple times on a byte-wide basis.
485 * The number of times (32) aligns the I/O operation to be a multiple of 4 bytes and provides a
486 * reasonable chance to acquire "all" pending credits in a single I/O operation.
488 * Once we obtain the filled buffer, we can walk through it looking for credit decrement transitions.
489 * Each non-zero byte represents a single credit decrement (which is a credit given back to the host)
490 * For example if the target provides 3 credits and added 4 more during the 32-byte read operation the following
491 * pattern "could" appear:
493 * 0x3 0x2 0x1 0x0 0x0 0x0 0x0 0x0 0x1 0x0 0x1 0x0 0x1 0x0 0x1 0x0 ......rest zeros
494 * <---------> <----------------------------->
495 * \_ credits aleady there \_ target adding 4 more credits
497 * The total available credits would be 7, since there are 7 non-zero bytes in the buffer.
501 if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
502 DebugDumpBytes(pBuffer, Length, "GMBOX Credit read buffer");
517 /* callback when our fetch to enable/disable completes */
518 static void DevGMboxReadCreditsAsyncHandler(void *Context, struct htc_packet *pPacket)
520 struct ar6k_device *pDev = (struct ar6k_device *)Context;
522 AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevGMboxReadCreditsAsyncHandler: (dev: 0x%lX)\n", (unsigned long)pDev));
524 if (pPacket->Status) {
525 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
526 ("Read Credit Operation failed! status:%d \n", pPacket->Status));
529 credits = ProcessCreditCounterReadBuffer(pPacket->pBuffer, AR6K_REG_IO_BUFFER_SIZE);
530 pDev->GMboxInfo.pCreditsPendingCallback(pDev->GMboxInfo.pProtocolContext,
532 pDev->GMboxInfo.CreditCountIRQEnabled);
536 /* free this IO packet */
537 AR6KFreeIOPacket(pDev,pPacket);
538 AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevGMboxReadCreditsAsyncHandler \n"));
541 int DevGMboxReadCreditCounter(struct ar6k_device *pDev, bool AsyncMode, int *pCredits)
544 struct htc_packet *pIOPacket = NULL;
546 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+DevGMboxReadCreditCounter (%s) \n", AsyncMode ? "ASYNC" : "SYNC"));
550 pIOPacket = AR6KAllocIOPacket(pDev);
552 if (NULL == pIOPacket) {
553 status = A_NO_MEMORY;
558 A_MEMZERO(pIOPacket->pBuffer,AR6K_REG_IO_BUFFER_SIZE);
561 /* stick in our completion routine when the I/O operation completes */
562 pIOPacket->Completion = DevGMboxReadCreditsAsyncHandler;
563 pIOPacket->pContext = pDev;
564 /* read registers asynchronously */
565 HIFReadWrite(pDev->HIFDevice,
566 AR6K_GMBOX_CREDIT_DEC_ADDRESS,
568 AR6K_REG_IO_BUFFER_SIZE, /* hit the register multiple times */
569 HIF_RD_ASYNC_BYTE_FIX,
575 pIOPacket->Completion = NULL;
576 /* if we get here we are doing it synchronously */
577 status = HIFReadWrite(pDev->HIFDevice,
578 AR6K_GMBOX_CREDIT_DEC_ADDRESS,
580 AR6K_REG_IO_BUFFER_SIZE,
581 HIF_RD_SYNC_BYTE_FIX,
586 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
587 (" DevGMboxReadCreditCounter failed! status:%d \n", status));
590 if (pIOPacket != NULL) {
592 /* sync mode processing */
593 *pCredits = ProcessCreditCounterReadBuffer(pIOPacket->pBuffer, AR6K_REG_IO_BUFFER_SIZE);
595 AR6KFreeIOPacket(pDev,pIOPacket);
598 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-DevGMboxReadCreditCounter (%s) (%d) \n",
599 AsyncMode ? "ASYNC" : "SYNC", status));
604 int DevGMboxReadCreditSize(struct ar6k_device *pDev, int *pCreditSize)
609 status = HIFReadWrite(pDev->HIFDevice,
610 AR6K_GMBOX_CREDIT_SIZE_ADDRESS,
613 HIF_RD_SYNC_BYTE_FIX, /* hit the register 4 times to align the I/O */
617 if (buffer[0] == 0) {
620 *pCreditSize = buffer[0];
628 void DevNotifyGMboxTargetFailure(struct ar6k_device *pDev)
630 /* Target ASSERTED!!! */
631 if (pDev->GMboxInfo.pTargetFailureCallback != NULL) {
632 pDev->GMboxInfo.pTargetFailureCallback(pDev->GMboxInfo.pProtocolContext, A_HARDWARE);
636 int DevGMboxRecvLookAheadPeek(struct ar6k_device *pDev, u8 *pLookAheadBuffer, int *pLookAheadBytes)
640 struct ar6k_irq_proc_registers procRegs;
644 /* on entry the caller provides the length of the lookahead buffer */
645 if (*pLookAheadBytes > sizeof(procRegs.rx_gmbox_lookahead_alias)) {
651 maxCopy = *pLookAheadBytes;
652 *pLookAheadBytes = 0;
653 /* load the register table from the device */
654 status = HIFReadWrite(pDev->HIFDevice,
655 HOST_INT_STATUS_ADDRESS,
657 AR6K_IRQ_PROC_REGS_SIZE,
658 HIF_RD_SYNC_BYTE_INC,
662 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
663 ("DevGMboxRecvLookAheadPeek : Failed to read register table (%d) \n",status));
667 if (procRegs.gmbox_rx_avail > 0) {
668 int bytes = procRegs.gmbox_rx_avail > maxCopy ? maxCopy : procRegs.gmbox_rx_avail;
669 memcpy(pLookAheadBuffer,&procRegs.rx_gmbox_lookahead_alias[0],bytes);
670 *pLookAheadBytes = bytes;
678 int DevGMboxSetTargetInterrupt(struct ar6k_device *pDev, int Signal, int AckTimeoutMS)
684 A_MEMZERO(buffer, sizeof(buffer));
688 if (Signal >= MBOX_SIG_HCI_BRIDGE_MAX) {
693 /* set the last buffer to do the actual signal trigger */
694 buffer[3] = (1 << Signal);
696 status = HIFReadWrite(pDev->HIFDevice,
700 HIF_WR_SYNC_BYTE_FIX, /* hit the register 4 times to align the I/O */
711 /* now read back the register to see if the bit cleared */
712 while (AckTimeoutMS) {
713 status = HIFReadWrite(pDev->HIFDevice,
717 HIF_RD_SYNC_BYTE_FIX,
724 for (i = 0; i < sizeof(buffer); i++) {
725 if (buffer[i] & (1 << Signal)) {
726 /* bit is still set */
731 if (i >= sizeof(buffer)) {
740 if (0 == AckTimeoutMS) {
741 AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
742 ("DevGMboxSetTargetInterrupt : Ack Timed-out (sig:%d) \n",Signal));
751 #endif //ATH_AR6K_ENABLE_GMBOX