| Index: chromeos/drivers/ath6kl/htc2/AR6000/ar6k_gmbox.c
|
| diff --git a/chromeos/drivers/ath6kl/htc2/AR6000/ar6k_gmbox.c b/chromeos/drivers/ath6kl/htc2/AR6000/ar6k_gmbox.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f1408082a80946be194271d946feabf1d0f98f56
|
| --- /dev/null
|
| +++ b/chromeos/drivers/ath6kl/htc2/AR6000/ar6k_gmbox.c
|
| @@ -0,0 +1,752 @@
|
| +//------------------------------------------------------------------------------
|
| +// <copyright file="ar6k_gmbox.c" company="Atheros">
|
| +// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
|
| +//
|
| +// This program is free software; you can redistribute it and/or modify
|
| +// it under the terms of the GNU General Public License version 2 as
|
| +// published by the Free Software Foundation;
|
| +//
|
| +// Software distributed under the License is distributed on an "AS
|
| +// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
| +// implied. See the License for the specific language governing
|
| +// rights and limitations under the License.
|
| +//
|
| +//
|
| +//------------------------------------------------------------------------------
|
| +//==============================================================================
|
| +// Generic MBOX API implementation
|
| +//
|
| +// Author(s): ="Atheros"
|
| +//==============================================================================
|
| +#include "a_config.h"
|
| +#include "athdefs.h"
|
| +#include "a_types.h"
|
| +#include "a_osapi.h"
|
| +#include "../htc_debug.h"
|
| +#include "hif.h"
|
| +#include "htc_packet.h"
|
| +#include "ar6k.h"
|
| +#include "hw/mbox_host_reg.h"
|
| +#include "gmboxif.h"
|
| +
|
| +/*
|
| + * This file provides management functions and a toolbox for GMBOX protocol modules.
|
| + * Only one protocol module can be installed at a time. The determination of which protocol
|
| + * module is installed is determined at compile time.
|
| + *
|
| + */
|
| +#ifdef ATH_AR6K_ENABLE_GMBOX
|
| + /* GMBOX definitions */
|
| +#define GMBOX_INT_STATUS_ENABLE_REG 0x488
|
| +#define GMBOX_INT_STATUS_RX_DATA (1 << 0)
|
| +#define GMBOX_INT_STATUS_TX_OVERFLOW (1 << 1)
|
| +#define GMBOX_INT_STATUS_RX_OVERFLOW (1 << 2)
|
| +
|
| +#define GMBOX_LOOKAHEAD_MUX_REG 0x498
|
| +#define GMBOX_LA_MUX_OVERRIDE_2_3 (1 << 0)
|
| +
|
| +#define AR6K_GMBOX_CREDIT_DEC_ADDRESS (COUNT_DEC_ADDRESS + 4 * AR6K_GMBOX_CREDIT_COUNTER)
|
| +#define AR6K_GMBOX_CREDIT_SIZE_ADDRESS (COUNT_ADDRESS + AR6K_GMBOX_CREDIT_SIZE_COUNTER)
|
| +
|
| +
|
| + /* external APIs for allocating and freeing internal I/O packets to handle ASYNC I/O */
|
| +extern void AR6KFreeIOPacket(AR6K_DEVICE *pDev, HTC_PACKET *pPacket);
|
| +extern HTC_PACKET *AR6KAllocIOPacket(AR6K_DEVICE *pDev);
|
| +
|
| +
|
| +/* callback when our fetch to enable/disable completes */
|
| +static void DevGMboxIRQActionAsyncHandler(void *Context, HTC_PACKET *pPacket)
|
| +{
|
| + AR6K_DEVICE *pDev = (AR6K_DEVICE *)Context;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevGMboxIRQActionAsyncHandler: (dev: 0x%X)\n", (A_UINT32)pDev));
|
| +
|
| + if (A_FAILED(pPacket->Status)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
|
| + ("IRQAction Operation (%d) failed! status:%d \n", pPacket->PktInfo.AsRx.HTCRxFlags,pPacket->Status));
|
| + }
|
| + /* free this IO packet */
|
| + AR6KFreeIOPacket(pDev,pPacket);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevGMboxIRQActionAsyncHandler \n"));
|
| +}
|
| +
|
| +static A_STATUS DevGMboxCounterEnableDisable(AR6K_DEVICE *pDev, GMBOX_IRQ_ACTION_TYPE IrqAction, A_BOOL AsyncMode)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + AR6K_IRQ_ENABLE_REGISTERS regs;
|
| + HTC_PACKET *pIOPacket = NULL;
|
| +
|
| + LOCK_AR6K(pDev);
|
| +
|
| + if (GMBOX_CREDIT_IRQ_ENABLE == IrqAction) {
|
| + pDev->GMboxInfo.CreditCountIRQEnabled = TRUE;
|
| + pDev->IrqEnableRegisters.counter_int_status_enable |=
|
| + COUNTER_INT_STATUS_ENABLE_BIT_SET(1 << AR6K_GMBOX_CREDIT_COUNTER);
|
| + pDev->IrqEnableRegisters.int_status_enable |= INT_STATUS_ENABLE_COUNTER_SET(0x01);
|
| + } else {
|
| + pDev->GMboxInfo.CreditCountIRQEnabled = FALSE;
|
| + pDev->IrqEnableRegisters.counter_int_status_enable &=
|
| + ~(COUNTER_INT_STATUS_ENABLE_BIT_SET(1 << AR6K_GMBOX_CREDIT_COUNTER));
|
| + }
|
| + /* copy into our temp area */
|
| + A_MEMCPY(®s,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE);
|
| +
|
| + UNLOCK_AR6K(pDev);
|
| +
|
| + do {
|
| +
|
| + if (AsyncMode) {
|
| +
|
| + pIOPacket = AR6KAllocIOPacket(pDev);
|
| +
|
| + if (NULL == pIOPacket) {
|
| + status = A_NO_MEMORY;
|
| + A_ASSERT(FALSE);
|
| + break;
|
| + }
|
| +
|
| + /* copy values to write to our async I/O buffer */
|
| + A_MEMCPY(pIOPacket->pBuffer,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE);
|
| +
|
| + /* stick in our completion routine when the I/O operation completes */
|
| + pIOPacket->Completion = DevGMboxIRQActionAsyncHandler;
|
| + pIOPacket->pContext = pDev;
|
| + pIOPacket->PktInfo.AsRx.HTCRxFlags = IrqAction;
|
| + /* write it out asynchronously */
|
| + HIFReadWrite(pDev->HIFDevice,
|
| + INT_STATUS_ENABLE_ADDRESS,
|
| + pIOPacket->pBuffer,
|
| + AR6K_IRQ_ENABLE_REGS_SIZE,
|
| + HIF_WR_ASYNC_BYTE_INC,
|
| + pIOPacket);
|
| +
|
| + pIOPacket = NULL;
|
| + break;
|
| + }
|
| +
|
| + /* if we get here we are doing it synchronously */
|
| + status = HIFReadWrite(pDev->HIFDevice,
|
| + INT_STATUS_ENABLE_ADDRESS,
|
| + ®s.int_status_enable,
|
| + AR6K_IRQ_ENABLE_REGS_SIZE,
|
| + HIF_WR_SYNC_BYTE_INC,
|
| + NULL);
|
| + } while (FALSE);
|
| +
|
| + if (A_FAILED(status)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
|
| + (" IRQAction Operation (%d) failed! status:%d \n", IrqAction, status));
|
| + } else {
|
| + if (!AsyncMode) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
|
| + (" IRQAction Operation (%d) success \n", IrqAction));
|
| + }
|
| + }
|
| +
|
| + if (pIOPacket != NULL) {
|
| + AR6KFreeIOPacket(pDev,pIOPacket);
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +
|
| +A_STATUS DevGMboxIRQAction(AR6K_DEVICE *pDev, GMBOX_IRQ_ACTION_TYPE IrqAction, A_BOOL AsyncMode)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + HTC_PACKET *pIOPacket = NULL;
|
| + A_UINT8 GMboxIntControl[4];
|
| +
|
| + if (GMBOX_CREDIT_IRQ_ENABLE == IrqAction) {
|
| + return DevGMboxCounterEnableDisable(pDev, GMBOX_CREDIT_IRQ_ENABLE, AsyncMode);
|
| + } else if(GMBOX_CREDIT_IRQ_DISABLE == IrqAction) {
|
| + return DevGMboxCounterEnableDisable(pDev, GMBOX_CREDIT_IRQ_DISABLE, AsyncMode);
|
| + }
|
| +
|
| + if (GMBOX_DISABLE_ALL == IrqAction) {
|
| + /* disable credit IRQ, those are on a different set of registers */
|
| + DevGMboxCounterEnableDisable(pDev, GMBOX_CREDIT_IRQ_DISABLE, AsyncMode);
|
| + }
|
| +
|
| + /* take the lock to protect interrupt enable shadows */
|
| + LOCK_AR6K(pDev);
|
| +
|
| + switch (IrqAction) {
|
| +
|
| + case GMBOX_DISABLE_ALL:
|
| + pDev->GMboxControlRegisters.int_status_enable = 0;
|
| + break;
|
| + case GMBOX_ERRORS_IRQ_ENABLE:
|
| + pDev->GMboxControlRegisters.int_status_enable |= GMBOX_INT_STATUS_TX_OVERFLOW |
|
| + GMBOX_INT_STATUS_RX_OVERFLOW;
|
| + break;
|
| + case GMBOX_RECV_IRQ_ENABLE:
|
| + pDev->GMboxControlRegisters.int_status_enable |= GMBOX_INT_STATUS_RX_DATA;
|
| + break;
|
| + case GMBOX_RECV_IRQ_DISABLE:
|
| + pDev->GMboxControlRegisters.int_status_enable &= ~GMBOX_INT_STATUS_RX_DATA;
|
| + break;
|
| + case GMBOX_ACTION_NONE:
|
| + default:
|
| + A_ASSERT(FALSE);
|
| + break;
|
| + }
|
| +
|
| + GMboxIntControl[0] = pDev->GMboxControlRegisters.int_status_enable;
|
| + GMboxIntControl[1] = GMboxIntControl[0];
|
| + GMboxIntControl[2] = GMboxIntControl[0];
|
| + GMboxIntControl[3] = GMboxIntControl[0];
|
| +
|
| + UNLOCK_AR6K(pDev);
|
| +
|
| + do {
|
| +
|
| + if (AsyncMode) {
|
| +
|
| + pIOPacket = AR6KAllocIOPacket(pDev);
|
| +
|
| + if (NULL == pIOPacket) {
|
| + status = A_NO_MEMORY;
|
| + A_ASSERT(FALSE);
|
| + break;
|
| + }
|
| +
|
| + /* copy values to write to our async I/O buffer */
|
| + A_MEMCPY(pIOPacket->pBuffer,GMboxIntControl,sizeof(GMboxIntControl));
|
| +
|
| + /* stick in our completion routine when the I/O operation completes */
|
| + pIOPacket->Completion = DevGMboxIRQActionAsyncHandler;
|
| + pIOPacket->pContext = pDev;
|
| + pIOPacket->PktInfo.AsRx.HTCRxFlags = IrqAction;
|
| + /* write it out asynchronously */
|
| + HIFReadWrite(pDev->HIFDevice,
|
| + GMBOX_INT_STATUS_ENABLE_REG,
|
| + pIOPacket->pBuffer,
|
| + sizeof(GMboxIntControl),
|
| + HIF_WR_ASYNC_BYTE_FIX,
|
| + pIOPacket);
|
| + pIOPacket = NULL;
|
| + break;
|
| + }
|
| +
|
| + /* if we get here we are doing it synchronously */
|
| +
|
| + status = HIFReadWrite(pDev->HIFDevice,
|
| + GMBOX_INT_STATUS_ENABLE_REG,
|
| + GMboxIntControl,
|
| + sizeof(GMboxIntControl),
|
| + HIF_WR_SYNC_BYTE_FIX,
|
| + NULL);
|
| +
|
| + } while (FALSE);
|
| +
|
| + if (A_FAILED(status)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
|
| + (" IRQAction Operation (%d) failed! status:%d \n", IrqAction, status));
|
| + } else {
|
| + if (!AsyncMode) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,
|
| + (" IRQAction Operation (%d) success \n", IrqAction));
|
| + }
|
| + }
|
| +
|
| + if (pIOPacket != NULL) {
|
| + AR6KFreeIOPacket(pDev,pIOPacket);
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +void DevCleanupGMbox(AR6K_DEVICE *pDev)
|
| +{
|
| + if (pDev->GMboxEnabled) {
|
| + pDev->GMboxEnabled = FALSE;
|
| + GMboxProtocolUninstall(pDev);
|
| + }
|
| +}
|
| +
|
| +A_STATUS DevSetupGMbox(AR6K_DEVICE *pDev)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + A_UINT8 muxControl[4];
|
| +
|
| + do {
|
| +
|
| + if (0 == pDev->MailBoxInfo.GMboxAddress) {
|
| + break;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY,(" GMBOX Advertised: Address:0x%X , size:%d \n",
|
| + pDev->MailBoxInfo.GMboxAddress, pDev->MailBoxInfo.GMboxSize));
|
| +
|
| + status = DevGMboxIRQAction(pDev, GMBOX_DISABLE_ALL, PROC_IO_SYNC);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + /* write to mailbox look ahead mux control register, we want the
|
| + * GMBOX lookaheads to appear on lookaheads 2 and 3
|
| + * the register is 1-byte wide so we need to hit it 4 times to align the operation
|
| + * to 4-bytes */
|
| + muxControl[0] = GMBOX_LA_MUX_OVERRIDE_2_3;
|
| + muxControl[1] = GMBOX_LA_MUX_OVERRIDE_2_3;
|
| + muxControl[2] = GMBOX_LA_MUX_OVERRIDE_2_3;
|
| + muxControl[3] = GMBOX_LA_MUX_OVERRIDE_2_3;
|
| +
|
| + status = HIFReadWrite(pDev->HIFDevice,
|
| + GMBOX_LOOKAHEAD_MUX_REG,
|
| + muxControl,
|
| + sizeof(muxControl),
|
| + HIF_WR_SYNC_BYTE_FIX, /* hit this register 4 times */
|
| + NULL);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + status = GMboxProtocolInstall(pDev);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + pDev->GMboxEnabled = TRUE;
|
| +
|
| + } while (FALSE);
|
| +
|
| + return status;
|
| +}
|
| +
|
| +A_STATUS DevCheckGMboxInterrupts(AR6K_DEVICE *pDev)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + A_UINT8 counter_int_status;
|
| + int credits;
|
| + A_UINT8 host_int_status2;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("+DevCheckGMboxInterrupts \n"));
|
| +
|
| + /* the caller guarantees that this is a context that allows for blocking I/O */
|
| +
|
| + do {
|
| +
|
| + host_int_status2 = pDev->IrqProcRegisters.host_int_status2 &
|
| + pDev->GMboxControlRegisters.int_status_enable;
|
| +
|
| + if (host_int_status2 & GMBOX_INT_STATUS_TX_OVERFLOW) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("GMBOX : TX Overflow \n"));
|
| + status = A_ECOMM;
|
| + }
|
| +
|
| + if (host_int_status2 & GMBOX_INT_STATUS_RX_OVERFLOW) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("GMBOX : RX Overflow \n"));
|
| + status = A_ECOMM;
|
| + }
|
| +
|
| + if (A_FAILED(status)) {
|
| + if (pDev->GMboxInfo.pTargetFailureCallback != NULL) {
|
| + pDev->GMboxInfo.pTargetFailureCallback(pDev->GMboxInfo.pProtocolContext, status);
|
| + }
|
| + break;
|
| + }
|
| +
|
| + if (host_int_status2 & GMBOX_INT_STATUS_RX_DATA) {
|
| + if (pDev->IrqProcRegisters.gmbox_rx_avail > 0) {
|
| + A_ASSERT(pDev->GMboxInfo.pMessagePendingCallBack != NULL);
|
| + status = pDev->GMboxInfo.pMessagePendingCallBack(
|
| + pDev->GMboxInfo.pProtocolContext,
|
| + (A_UINT8 *)&pDev->IrqProcRegisters.rx_gmbox_lookahead_alias[0],
|
| + pDev->IrqProcRegisters.gmbox_rx_avail);
|
| + }
|
| + }
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + counter_int_status = pDev->IrqProcRegisters.counter_int_status &
|
| + pDev->IrqEnableRegisters.counter_int_status_enable;
|
| +
|
| + /* check if credit interrupt is pending */
|
| + if (counter_int_status & (COUNTER_INT_STATUS_ENABLE_BIT_SET(1 << AR6K_GMBOX_CREDIT_COUNTER))) {
|
| +
|
| + /* do synchronous read */
|
| + status = DevGMboxReadCreditCounter(pDev, PROC_IO_SYNC, &credits);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + A_ASSERT(pDev->GMboxInfo.pCreditsPendingCallback != NULL);
|
| + status = pDev->GMboxInfo.pCreditsPendingCallback(pDev->GMboxInfo.pProtocolContext,
|
| + credits,
|
| + pDev->GMboxInfo.CreditCountIRQEnabled);
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("-DevCheckGMboxInterrupts (%d) \n",status));
|
| +
|
| + return status;
|
| +}
|
| +
|
| +
|
| +A_STATUS DevGMboxWrite(AR6K_DEVICE *pDev, HTC_PACKET *pPacket, A_UINT32 WriteLength)
|
| +{
|
| + A_UINT32 paddedLength;
|
| + A_BOOL sync = (pPacket->Completion == NULL) ? TRUE : FALSE;
|
| + A_STATUS status;
|
| + A_UINT32 address;
|
| +
|
| + /* adjust the length to be a multiple of block size if appropriate */
|
| + paddedLength = DEV_CALC_SEND_PADDED_LEN(pDev, WriteLength);
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
|
| + ("DevGMboxWrite, Padded Length: %d Mbox:0x%X (mode:%s)\n",
|
| + WriteLength,
|
| + pDev->MailBoxInfo.GMboxAddress,
|
| + sync ? "SYNC" : "ASYNC"));
|
| +
|
| + /* last byte of packet has to hit the EOM marker */
|
| + address = pDev->MailBoxInfo.GMboxAddress + pDev->MailBoxInfo.GMboxSize - paddedLength;
|
| +
|
| + status = HIFReadWrite(pDev->HIFDevice,
|
| + address,
|
| + pPacket->pBuffer,
|
| + paddedLength, /* the padded length */
|
| + sync ? HIF_WR_SYNC_BLOCK_INC : HIF_WR_ASYNC_BLOCK_INC,
|
| + sync ? NULL : pPacket); /* pass the packet as the context to the HIF request */
|
| +
|
| + if (sync) {
|
| + pPacket->Status = status;
|
| + } else {
|
| + if (status == A_PENDING) {
|
| + status = A_OK;
|
| + }
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +A_STATUS DevGMboxRead(AR6K_DEVICE *pDev, HTC_PACKET *pPacket, A_UINT32 ReadLength)
|
| +{
|
| +
|
| + A_UINT32 paddedLength;
|
| + A_STATUS status;
|
| + A_BOOL sync = (pPacket->Completion == NULL) ? TRUE : FALSE;
|
| +
|
| + /* adjust the length to be a multiple of block size if appropriate */
|
| + paddedLength = DEV_CALC_RECV_PADDED_LEN(pDev, ReadLength);
|
| +
|
| + if (paddedLength > pPacket->BufferLength) {
|
| + A_ASSERT(FALSE);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
|
| + ("DevGMboxRead, Not enough space for padlen:%d recvlen:%d bufferlen:%d \n",
|
| + paddedLength,ReadLength,pPacket->BufferLength));
|
| + if (pPacket->Completion != NULL) {
|
| + COMPLETE_HTC_PACKET(pPacket,A_EINVAL);
|
| + return A_OK;
|
| + }
|
| + return A_EINVAL;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_RECV,
|
| + ("DevGMboxRead (0x%X : hdr:0x%X) Padded Length: %d Mbox:0x%X (mode:%s)\n",
|
| + (A_UINT32)pPacket, pPacket->PktInfo.AsRx.ExpectedHdr,
|
| + paddedLength,
|
| + pDev->MailBoxInfo.GMboxAddress,
|
| + sync ? "SYNC" : "ASYNC"));
|
| +
|
| + status = HIFReadWrite(pDev->HIFDevice,
|
| + pDev->MailBoxInfo.GMboxAddress,
|
| + pPacket->pBuffer,
|
| + paddedLength,
|
| + sync ? HIF_RD_SYNC_BLOCK_FIX : HIF_RD_ASYNC_BLOCK_FIX,
|
| + sync ? NULL : pPacket); /* pass the packet as the context to the HIF request */
|
| +
|
| + if (sync) {
|
| + pPacket->Status = status;
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +
|
| +static int ProcessCreditCounterReadBuffer(A_UINT8 *pBuffer, int Length)
|
| +{
|
| + int credits = 0;
|
| +
|
| + /* theory of how this works:
|
| + * We read the credit decrement register multiple times on a byte-wide basis.
|
| + * The number of times (32) aligns the I/O operation to be a multiple of 4 bytes and provides a
|
| + * reasonable chance to acquire "all" pending credits in a single I/O operation.
|
| + *
|
| + * Once we obtain the filled buffer, we can walk through it looking for credit decrement transitions.
|
| + * Each non-zero byte represents a single credit decrement (which is a credit given back to the host)
|
| + * For example if the target provides 3 credits and added 4 more during the 32-byte read operation the following
|
| + * pattern "could" appear:
|
| + *
|
| + * 0x3 0x2 0x1 0x0 0x0 0x0 0x0 0x0 0x1 0x0 0x1 0x0 0x1 0x0 0x1 0x0 ......rest zeros
|
| + * <---------> <----------------------------->
|
| + * \_ credits aleady there \_ target adding 4 more credits
|
| + *
|
| + * The total available credits would be 7, since there are 7 non-zero bytes in the buffer.
|
| + *
|
| + * */
|
| +
|
| + if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_RECV)) {
|
| + DebugDumpBytes(pBuffer, Length, "GMBOX Credit read buffer");
|
| + }
|
| +
|
| + while (Length) {
|
| + if (*pBuffer != 0) {
|
| + credits++;
|
| + }
|
| + Length--;
|
| + pBuffer++;
|
| + }
|
| +
|
| + return credits;
|
| +}
|
| +
|
| +
|
| +/* callback when our fetch to enable/disable completes */
|
| +static void DevGMboxReadCreditsAsyncHandler(void *Context, HTC_PACKET *pPacket)
|
| +{
|
| + AR6K_DEVICE *pDev = (AR6K_DEVICE *)Context;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevGMboxReadCreditsAsyncHandler: (dev: 0x%X)\n", (A_UINT32)pDev));
|
| +
|
| + if (A_FAILED(pPacket->Status)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
|
| + ("Read Credit Operation failed! status:%d \n", pPacket->Status));
|
| + } else {
|
| + int credits = 0;
|
| + credits = ProcessCreditCounterReadBuffer(pPacket->pBuffer, AR6K_REG_IO_BUFFER_SIZE);
|
| + pDev->GMboxInfo.pCreditsPendingCallback(pDev->GMboxInfo.pProtocolContext,
|
| + credits,
|
| + pDev->GMboxInfo.CreditCountIRQEnabled);
|
| +
|
| +
|
| + }
|
| + /* free this IO packet */
|
| + AR6KFreeIOPacket(pDev,pPacket);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevGMboxReadCreditsAsyncHandler \n"));
|
| +}
|
| +
|
| +A_STATUS DevGMboxReadCreditCounter(AR6K_DEVICE *pDev, A_BOOL AsyncMode, int *pCredits)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + HTC_PACKET *pIOPacket = NULL;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+DevGMboxReadCreditCounter (%s) \n", AsyncMode ? "ASYNC" : "SYNC"));
|
| +
|
| + do {
|
| +
|
| + pIOPacket = AR6KAllocIOPacket(pDev);
|
| +
|
| + if (NULL == pIOPacket) {
|
| + status = A_NO_MEMORY;
|
| + A_ASSERT(FALSE);
|
| + break;
|
| + }
|
| +
|
| + A_MEMZERO(pIOPacket->pBuffer,AR6K_REG_IO_BUFFER_SIZE);
|
| +
|
| + if (AsyncMode) {
|
| + /* stick in our completion routine when the I/O operation completes */
|
| + pIOPacket->Completion = DevGMboxReadCreditsAsyncHandler;
|
| + pIOPacket->pContext = pDev;
|
| + /* read registers asynchronously */
|
| + HIFReadWrite(pDev->HIFDevice,
|
| + AR6K_GMBOX_CREDIT_DEC_ADDRESS,
|
| + pIOPacket->pBuffer,
|
| + AR6K_REG_IO_BUFFER_SIZE, /* hit the register multiple times */
|
| + HIF_RD_ASYNC_BYTE_FIX,
|
| + pIOPacket);
|
| + pIOPacket = NULL;
|
| + break;
|
| + }
|
| +
|
| + pIOPacket->Completion = NULL;
|
| + /* if we get here we are doing it synchronously */
|
| + status = HIFReadWrite(pDev->HIFDevice,
|
| + AR6K_GMBOX_CREDIT_DEC_ADDRESS,
|
| + pIOPacket->pBuffer,
|
| + AR6K_REG_IO_BUFFER_SIZE,
|
| + HIF_RD_SYNC_BYTE_FIX,
|
| + NULL);
|
| + } while (FALSE);
|
| +
|
| + if (A_FAILED(status)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
|
| + (" DevGMboxReadCreditCounter failed! status:%d \n", status));
|
| + }
|
| +
|
| + if (pIOPacket != NULL) {
|
| + if (A_SUCCESS(status)) {
|
| + /* sync mode processing */
|
| + *pCredits = ProcessCreditCounterReadBuffer(pIOPacket->pBuffer, AR6K_REG_IO_BUFFER_SIZE);
|
| + }
|
| + AR6KFreeIOPacket(pDev,pIOPacket);
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-DevGMboxReadCreditCounter (%s) (%d) \n",
|
| + AsyncMode ? "ASYNC" : "SYNC", status));
|
| +
|
| + return status;
|
| +}
|
| +
|
| +A_STATUS DevGMboxReadCreditSize(AR6K_DEVICE *pDev, int *pCreditSize)
|
| +{
|
| + A_STATUS status;
|
| + A_UINT8 buffer[4];
|
| +
|
| + status = HIFReadWrite(pDev->HIFDevice,
|
| + AR6K_GMBOX_CREDIT_SIZE_ADDRESS,
|
| + buffer,
|
| + sizeof(buffer),
|
| + HIF_RD_SYNC_BYTE_FIX, /* hit the register 4 times to align the I/O */
|
| + NULL);
|
| +
|
| + if (A_SUCCESS(status)) {
|
| + if (buffer[0] == 0) {
|
| + *pCreditSize = 256;
|
| + } else {
|
| + *pCreditSize = buffer[0];
|
| + }
|
| +
|
| + }
|
| +
|
| + return status;
|
| +}
|
| +
|
| +void DevNotifyGMboxTargetFailure(AR6K_DEVICE *pDev)
|
| +{
|
| + /* Target ASSERTED!!! */
|
| + if (pDev->GMboxInfo.pTargetFailureCallback != NULL) {
|
| + pDev->GMboxInfo.pTargetFailureCallback(pDev->GMboxInfo.pProtocolContext, A_HARDWARE);
|
| + }
|
| +}
|
| +
|
| +A_STATUS DevGMboxRecvLookAheadPeek(AR6K_DEVICE *pDev, A_UINT8 *pLookAheadBuffer, int *pLookAheadBytes)
|
| +{
|
| +
|
| + A_STATUS status = A_OK;
|
| + AR6K_IRQ_PROC_REGISTERS procRegs;
|
| + int maxCopy;
|
| +
|
| + do {
|
| + /* on entry the caller provides the length of the lookahead buffer */
|
| + if (*pLookAheadBytes > sizeof(procRegs.rx_gmbox_lookahead_alias)) {
|
| + A_ASSERT(FALSE);
|
| + status = A_EINVAL;
|
| + break;
|
| + }
|
| +
|
| + maxCopy = *pLookAheadBytes;
|
| + *pLookAheadBytes = 0;
|
| + /* load the register table from the device */
|
| + status = HIFReadWrite(pDev->HIFDevice,
|
| + HOST_INT_STATUS_ADDRESS,
|
| + (A_UINT8 *)&procRegs,
|
| + AR6K_IRQ_PROC_REGS_SIZE,
|
| + HIF_RD_SYNC_BYTE_INC,
|
| + NULL);
|
| +
|
| + if (A_FAILED(status)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
|
| + ("DevGMboxRecvLookAheadPeek : Failed to read register table (%d) \n",status));
|
| + break;
|
| + }
|
| +
|
| + if (procRegs.gmbox_rx_avail > 0) {
|
| + int bytes = procRegs.gmbox_rx_avail > maxCopy ? maxCopy : procRegs.gmbox_rx_avail;
|
| + A_MEMCPY(pLookAheadBuffer,&procRegs.rx_gmbox_lookahead_alias[0],bytes);
|
| + *pLookAheadBytes = bytes;
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| + return status;
|
| +}
|
| +
|
| +A_STATUS DevGMboxSetTargetInterrupt(AR6K_DEVICE *pDev, int Signal, int AckTimeoutMS)
|
| +{
|
| + A_STATUS status = A_OK;
|
| + int i;
|
| + A_UINT8 buffer[4];
|
| +
|
| + A_MEMZERO(buffer, sizeof(buffer));
|
| +
|
| + do {
|
| +
|
| + if (Signal >= MBOX_SIG_HCI_BRIDGE_MAX) {
|
| + status = A_EINVAL;
|
| + break;
|
| + }
|
| +
|
| + /* set the last buffer to do the actual signal trigger */
|
| + buffer[3] = (1 << Signal);
|
| +
|
| + status = HIFReadWrite(pDev->HIFDevice,
|
| + INT_WLAN_ADDRESS,
|
| + buffer,
|
| + sizeof(buffer),
|
| + HIF_WR_SYNC_BYTE_FIX, /* hit the register 4 times to align the I/O */
|
| + NULL);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| +
|
| + if (A_SUCCESS(status)) {
|
| + /* now read back the register to see if the bit cleared */
|
| + while (AckTimeoutMS) {
|
| + status = HIFReadWrite(pDev->HIFDevice,
|
| + INT_WLAN_ADDRESS,
|
| + buffer,
|
| + sizeof(buffer),
|
| + HIF_RD_SYNC_BYTE_FIX,
|
| + NULL);
|
| +
|
| + if (A_FAILED(status)) {
|
| + break;
|
| + }
|
| +
|
| + for (i = 0; i < sizeof(buffer); i++) {
|
| + if (buffer[i] & (1 << Signal)) {
|
| + /* bit is still set */
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (i >= sizeof(buffer)) {
|
| + /* done */
|
| + break;
|
| + }
|
| +
|
| + AckTimeoutMS--;
|
| + A_MDELAY(1);
|
| + }
|
| +
|
| + if (0 == AckTimeoutMS) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
|
| + ("DevGMboxSetTargetInterrupt : Ack Timed-out (sig:%d) \n",Signal));
|
| + status = A_ERROR;
|
| + }
|
| + }
|
| +
|
| + return status;
|
| +
|
| +}
|
| +
|
| +#endif //ATH_AR6K_ENABLE_GMBOX
|
| +
|
| +
|
| +
|
| +
|
|
|