mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-27 22:43:19 +02:00
fb189822fc
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@15918 3c298f89-4303-0410-b956-a3cf2f4a3e73
825 lines
27 KiB
C
825 lines
27 KiB
C
/*
|
|
* @file: hif.c
|
|
*
|
|
* @abstract: HIF layer reference implementation for Atheros SDIO stack
|
|
*
|
|
* @notice: Copyright (c) 2004-2006 Atheros Communications Inc.
|
|
*
|
|
*
|
|
* 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.
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
#include "hif_internal.h"
|
|
|
|
/* ------ Static Variables ------ */
|
|
|
|
/* ------ Global Variable Declarations ------- */
|
|
SD_PNP_INFO Ids[] = {
|
|
{
|
|
.SDIO_ManufacturerID = MANUFACTURER_ID_AR6001_BASE | 0xB,
|
|
.SDIO_ManufacturerCode = MANUFACTURER_CODE,
|
|
.SDIO_FunctionClass = FUNCTION_CLASS,
|
|
.SDIO_FunctionNo = 1
|
|
},
|
|
{
|
|
.SDIO_ManufacturerID = MANUFACTURER_ID_AR6001_BASE | 0xA,
|
|
.SDIO_ManufacturerCode = MANUFACTURER_CODE,
|
|
.SDIO_FunctionClass = FUNCTION_CLASS,
|
|
.SDIO_FunctionNo = 1
|
|
},
|
|
{
|
|
.SDIO_ManufacturerID = MANUFACTURER_ID_AR6001_BASE | 0x9,
|
|
.SDIO_ManufacturerCode = MANUFACTURER_CODE,
|
|
.SDIO_FunctionClass = FUNCTION_CLASS,
|
|
.SDIO_FunctionNo = 1
|
|
},
|
|
{
|
|
.SDIO_ManufacturerID = MANUFACTURER_ID_AR6001_BASE | 0x8,
|
|
.SDIO_ManufacturerCode = MANUFACTURER_CODE,
|
|
.SDIO_FunctionClass = FUNCTION_CLASS,
|
|
.SDIO_FunctionNo = 1
|
|
},
|
|
{
|
|
.SDIO_ManufacturerID = MANUFACTURER_ID_AR6002_BASE | 0x0,
|
|
.SDIO_ManufacturerCode = MANUFACTURER_CODE,
|
|
.SDIO_FunctionClass = FUNCTION_CLASS,
|
|
.SDIO_FunctionNo = 1
|
|
},
|
|
{
|
|
.SDIO_ManufacturerID = MANUFACTURER_ID_AR6002_BASE | 0x1,
|
|
.SDIO_ManufacturerCode = MANUFACTURER_CODE,
|
|
.SDIO_FunctionClass = FUNCTION_CLASS,
|
|
.SDIO_FunctionNo = 1
|
|
},
|
|
{
|
|
} //list is null termintaed
|
|
};
|
|
|
|
TARGET_FUNCTION_CONTEXT FunctionContext = {
|
|
.function.Version = CT_SDIO_STACK_VERSION_CODE,
|
|
.function.pName = "sdio_wlan",
|
|
.function.MaxDevices = 1,
|
|
.function.NumDevices = 0,
|
|
.function.pIds = Ids,
|
|
.function.pProbe = hifDeviceInserted,
|
|
.function.pRemove = hifDeviceRemoved,
|
|
.function.pSuspend = NULL,
|
|
.function.pResume = NULL,
|
|
.function.pWake = NULL,
|
|
.function.pContext = &FunctionContext,
|
|
};
|
|
|
|
HIF_DEVICE hifDevice[HIF_MAX_DEVICES];
|
|
HTC_CALLBACKS htcCallbacks;
|
|
BUS_REQUEST busRequest[BUS_REQUEST_MAX_NUM];
|
|
static BUS_REQUEST *s_busRequestFreeQueue = NULL;
|
|
OS_CRITICALSECTION lock;
|
|
extern A_UINT32 onebitmode;
|
|
extern A_UINT32 busspeedlow;
|
|
|
|
#ifdef DEBUG
|
|
extern A_UINT32 debughif;
|
|
#define ATH_DEBUG_ERROR 1
|
|
#define ATH_DEBUG_WARN 2
|
|
#define ATH_DEBUG_TRACE 3
|
|
#define _AR_DEBUG_PRINTX_ARG(arg...) arg
|
|
#define AR_DEBUG_PRINTF(lvl, args)\
|
|
{if (lvl <= debughif)\
|
|
A_PRINTF(KERN_ALERT _AR_DEBUG_PRINTX_ARG args);\
|
|
}
|
|
#else
|
|
#define AR_DEBUG_PRINTF(lvl, args)
|
|
#endif
|
|
|
|
static BUS_REQUEST *hifAllocateBusRequest(void);
|
|
static void hifFreeBusRequest(BUS_REQUEST *busrequest);
|
|
static THREAD_RETURN insert_helper_func(POSKERNEL_HELPER pHelper);
|
|
static void ResetAllCards(void);
|
|
|
|
/* ------ Functions ------ */
|
|
int HIFInit(HTC_CALLBACKS *callbacks)
|
|
{
|
|
SDIO_STATUS status;
|
|
DBG_ASSERT(callbacks != NULL);
|
|
|
|
/* Store the callback and event handlers */
|
|
htcCallbacks.deviceInsertedHandler = callbacks->deviceInsertedHandler;
|
|
htcCallbacks.deviceRemovedHandler = callbacks->deviceRemovedHandler;
|
|
htcCallbacks.deviceSuspendHandler = callbacks->deviceSuspendHandler;
|
|
htcCallbacks.deviceResumeHandler = callbacks->deviceResumeHandler;
|
|
htcCallbacks.deviceWakeupHandler = callbacks->deviceWakeupHandler;
|
|
htcCallbacks.rwCompletionHandler = callbacks->rwCompletionHandler;
|
|
htcCallbacks.dsrHandler = callbacks->dsrHandler;
|
|
|
|
CriticalSectionInit(&lock);
|
|
|
|
/* Register with bus driver core */
|
|
status = SDIO_RegisterFunction(&FunctionContext.function);
|
|
DBG_ASSERT(SDIO_SUCCESS(status));
|
|
|
|
return(0);
|
|
}
|
|
|
|
A_STATUS
|
|
HIFReadWrite(HIF_DEVICE *device,
|
|
A_UINT32 address,
|
|
A_UCHAR *buffer,
|
|
A_UINT32 length,
|
|
A_UINT32 request,
|
|
void *context)
|
|
{
|
|
A_UINT8 rw;
|
|
A_UINT8 mode;
|
|
A_UINT8 funcNo;
|
|
A_UINT8 opcode;
|
|
A_UINT16 count;
|
|
SDREQUEST *sdrequest;
|
|
SDIO_STATUS sdiostatus;
|
|
BUS_REQUEST *busrequest;
|
|
A_STATUS status = A_OK;
|
|
|
|
DBG_ASSERT(device != NULL);
|
|
DBG_ASSERT(device->handle != NULL);
|
|
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device: %p\n", device));
|
|
|
|
do {
|
|
busrequest = hifAllocateBusRequest();
|
|
if (busrequest == NULL) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("HIF Unable to allocate bus request\n"));
|
|
status = A_NO_RESOURCE;
|
|
break;
|
|
}
|
|
|
|
sdrequest = busrequest->request;
|
|
busrequest->context = context;
|
|
|
|
sdrequest->pDataBuffer = buffer;
|
|
if (request & HIF_SYNCHRONOUS) {
|
|
sdrequest->Flags = SDREQ_FLAGS_RESP_SDIO_R5 | SDREQ_FLAGS_DATA_TRANS;
|
|
sdrequest->pCompleteContext = NULL;
|
|
sdrequest->pCompletion = NULL;
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Execution mode: Synchronous\n"));
|
|
} else if (request & HIF_ASYNCHRONOUS) {
|
|
sdrequest->Flags = SDREQ_FLAGS_RESP_SDIO_R5 | SDREQ_FLAGS_DATA_TRANS |
|
|
SDREQ_FLAGS_TRANS_ASYNC;
|
|
sdrequest->pCompleteContext = busrequest;
|
|
sdrequest->pCompletion = hifRWCompletionHandler;
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Execution mode: Asynchronous\n"));
|
|
} else {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Invalid execution mode: 0x%08x\n", request));
|
|
status = A_EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (request & HIF_EXTENDED_IO) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Command type: CMD53\n"));
|
|
sdrequest->Command = CMD53;
|
|
} else {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Invalid command type: 0x%08x\n", request));
|
|
status = A_EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (request & HIF_BLOCK_BASIS) {
|
|
mode = CMD53_BLOCK_BASIS;
|
|
sdrequest->BlockLen = HIF_MBOX_BLOCK_SIZE;
|
|
sdrequest->BlockCount = length / HIF_MBOX_BLOCK_SIZE;
|
|
count = sdrequest->BlockCount;
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
|
|
("Block mode (BlockLen: %d, BlockCount: %d)\n",
|
|
sdrequest->BlockLen, sdrequest->BlockCount));
|
|
} else if (request & HIF_BYTE_BASIS) {
|
|
mode = CMD53_BYTE_BASIS;
|
|
sdrequest->BlockLen = length;
|
|
sdrequest->BlockCount = 1;
|
|
count = sdrequest->BlockLen;
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
|
|
("Byte mode (BlockLen: %d, BlockCount: %d)\n",
|
|
sdrequest->BlockLen, sdrequest->BlockCount));
|
|
} else {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Invalid data mode: 0x%08x\n", request));
|
|
status = A_EINVAL;
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
/* useful for checking register accesses */
|
|
if (length & 0x3) {
|
|
A_PRINTF(KERN_ALERT"HIF (%s) is not a multiple of 4 bytes, addr:0x%X, len:%d\n",
|
|
request & HIF_WRITE ? "write":"read", address, length);
|
|
}
|
|
#endif
|
|
|
|
if ((address >= HIF_MBOX_START_ADDR(0)) &&
|
|
(address <= HIF_MBOX_END_ADDR(3)))
|
|
{
|
|
|
|
DBG_ASSERT(length <= HIF_MBOX_WIDTH);
|
|
|
|
/*
|
|
* Mailbox write. Adjust the address so that the last byte
|
|
* falls on the EOM address.
|
|
*/
|
|
address += (HIF_MBOX_WIDTH - length);
|
|
}
|
|
|
|
|
|
|
|
if (request & HIF_WRITE) {
|
|
rw = CMD53_WRITE;
|
|
sdrequest->Flags |= SDREQ_FLAGS_DATA_WRITE;
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Direction: Write\n"));
|
|
} else if (request & HIF_READ) {
|
|
rw = CMD53_READ;
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Direction: Read\n"));
|
|
} else {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Invalid direction: 0x%08x\n", request));
|
|
status = A_EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (request & HIF_FIXED_ADDRESS) {
|
|
opcode = CMD53_FIXED_ADDRESS;
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Address mode: Fixed\n"));
|
|
} else if (request & HIF_INCREMENTAL_ADDRESS) {
|
|
opcode = CMD53_INCR_ADDRESS;
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Address mode: Incremental\n"));
|
|
} else {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Invalid address mode: 0x%08x\n", request));
|
|
status = A_EINVAL;
|
|
break;
|
|
}
|
|
|
|
funcNo = SDDEVICE_GET_SDIO_FUNCNO(device->handle);
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Function number: %d\n", funcNo));
|
|
SDIO_SET_CMD53_ARG(sdrequest->Argument, rw, funcNo,
|
|
mode, opcode, address, count);
|
|
|
|
/* Send the command out */
|
|
sdiostatus = SDDEVICE_CALL_REQUEST_FUNC(device->handle, sdrequest);
|
|
|
|
if (!SDIO_SUCCESS(sdiostatus)) {
|
|
status = A_ERROR;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
if (A_FAILED(status) || (request & HIF_SYNCHRONOUS)) {
|
|
if (busrequest != NULL) {
|
|
hifFreeBusRequest(busrequest);
|
|
}
|
|
}
|
|
|
|
if (A_FAILED(status) && (request & HIF_ASYNCHRONOUS)) {
|
|
/* call back async handler on failure */
|
|
htcCallbacks.rwCompletionHandler(context, status);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
A_STATUS
|
|
HIFConfigureDevice(HIF_DEVICE *device, HIF_DEVICE_CONFIG_OPCODE opcode,
|
|
void *config, A_UINT32 configLen)
|
|
{
|
|
A_UINT32 count;
|
|
|
|
switch(opcode) {
|
|
case HIF_DEVICE_GET_MBOX_BLOCK_SIZE:
|
|
((A_UINT32 *)config)[0] = HIF_MBOX0_BLOCK_SIZE;
|
|
((A_UINT32 *)config)[1] = HIF_MBOX1_BLOCK_SIZE;
|
|
((A_UINT32 *)config)[2] = HIF_MBOX2_BLOCK_SIZE;
|
|
((A_UINT32 *)config)[3] = HIF_MBOX3_BLOCK_SIZE;
|
|
break;
|
|
|
|
case HIF_DEVICE_GET_MBOX_ADDR:
|
|
for (count = 0; count < 4; count ++) {
|
|
((A_UINT32 *)config)[count] = HIF_MBOX_START_ADDR(count);
|
|
}
|
|
break;
|
|
case HIF_DEVICE_GET_IRQ_PROC_MODE:
|
|
/* the SDIO stack allows the interrupts to be processed either way, ASYNC or SYNC */
|
|
*((HIF_DEVICE_IRQ_PROCESSING_MODE *)config) = HIF_DEVICE_IRQ_ASYNC_SYNC;
|
|
break;
|
|
default:
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
|
|
("Unsupported configuration opcode: %d\n", opcode));
|
|
return A_ERROR;
|
|
}
|
|
|
|
return A_OK;
|
|
}
|
|
|
|
void
|
|
HIFShutDownDevice(HIF_DEVICE *device)
|
|
{
|
|
A_UINT8 data;
|
|
A_UINT32 count;
|
|
SDIO_STATUS status;
|
|
SDCONFIG_BUS_MODE_DATA busSettings;
|
|
SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData;
|
|
|
|
if (device != NULL) {
|
|
DBG_ASSERT(device->handle != NULL);
|
|
|
|
/* Remove the allocated current if any */
|
|
status = SDLIB_IssueConfig(device->handle,
|
|
SDCONFIG_FUNC_FREE_SLOT_CURRENT, NULL, 0);
|
|
DBG_ASSERT(SDIO_SUCCESS(status));
|
|
|
|
/* Disable the card */
|
|
fData.EnableFlags = SDCONFIG_DISABLE_FUNC;
|
|
fData.TimeOut = 1;
|
|
status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_ENABLE_DISABLE,
|
|
&fData, sizeof(fData));
|
|
DBG_ASSERT(SDIO_SUCCESS(status));
|
|
|
|
/* Perform a soft I/O reset */
|
|
data = SDIO_IO_RESET;
|
|
status = SDLIB_IssueCMD52(device->handle, 0, SDIO_IO_ABORT_REG,
|
|
&data, 1, 1);
|
|
DBG_ASSERT(SDIO_SUCCESS(status));
|
|
|
|
/*
|
|
* WAR - Codetelligence driver does not seem to shutdown correctly in 1
|
|
* bit mode. By default it configures the HC in the 4 bit. Its later in
|
|
* our driver that we switch to 1 bit mode. If we try to shutdown, the
|
|
* driver hangs so we revert to 4 bit mode, to be transparent to the
|
|
* underlying bus driver.
|
|
*/
|
|
if (onebitmode) {
|
|
ZERO_OBJECT(busSettings);
|
|
busSettings.BusModeFlags = SDDEVICE_GET_BUSMODE_FLAGS(device->handle);
|
|
SDCONFIG_SET_BUS_WIDTH(busSettings.BusModeFlags,
|
|
SDCONFIG_BUS_WIDTH_4_BIT);
|
|
|
|
/* Issue config request to change the bus width to 4 bit */
|
|
status = SDLIB_IssueConfig(device->handle, SDCONFIG_BUS_MODE_CTRL,
|
|
&busSettings,
|
|
sizeof(SDCONFIG_BUS_MODE_DATA));
|
|
DBG_ASSERT(SDIO_SUCCESS(status));
|
|
}
|
|
|
|
/* Free the bus requests */
|
|
for (count = 0; count < BUS_REQUEST_MAX_NUM; count ++) {
|
|
SDDeviceFreeRequest(device->handle, busRequest[count].request);
|
|
}
|
|
/* Clean up the queue */
|
|
s_busRequestFreeQueue = NULL;
|
|
} else {
|
|
/* since we are unloading the driver anyways, reset all cards in case the SDIO card
|
|
* is externally powered and we are unloading the SDIO stack. This avoids the problem when
|
|
* the SDIO stack is reloaded and attempts are made to re-enumerate a card that is already
|
|
* enumerated */
|
|
ResetAllCards();
|
|
/* Unregister with bus driver core */
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
|
|
("Unregistering with the bus driver\n"));
|
|
status = SDIO_UnregisterFunction(&FunctionContext.function);
|
|
DBG_ASSERT(SDIO_SUCCESS(status));
|
|
}
|
|
}
|
|
|
|
void
|
|
hifRWCompletionHandler(SDREQUEST *request)
|
|
{
|
|
A_STATUS status;
|
|
void *context;
|
|
BUS_REQUEST *busrequest;
|
|
|
|
if (SDIO_SUCCESS(request->Status)) {
|
|
status = A_OK;
|
|
} else {
|
|
status = A_ERROR;
|
|
}
|
|
|
|
DBG_ASSERT(status == A_OK);
|
|
busrequest = (BUS_REQUEST *) request->pCompleteContext;
|
|
context = (void *) busrequest->context;
|
|
/* free the request before calling the callback, in case the
|
|
* callback submits another request, this guarantees that
|
|
* there is at least 1 free request available everytime the callback
|
|
* is invoked */
|
|
hifFreeBusRequest(busrequest);
|
|
htcCallbacks.rwCompletionHandler(context, status);
|
|
}
|
|
|
|
void
|
|
hifIRQHandler(void *context)
|
|
{
|
|
A_STATUS status;
|
|
HIF_DEVICE *device;
|
|
|
|
device = (HIF_DEVICE *)context;
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device: %p\n", device));
|
|
status = htcCallbacks.dsrHandler(device->htc_handle);
|
|
DBG_ASSERT(status == A_OK);
|
|
}
|
|
|
|
BOOL
|
|
hifDeviceInserted(SDFUNCTION *function, SDDEVICE *handle)
|
|
{
|
|
BOOL enabled;
|
|
A_UINT8 data;
|
|
A_UINT32 count;
|
|
HIF_DEVICE *device;
|
|
SDIO_STATUS status;
|
|
A_UINT16 maxBlocks;
|
|
A_UINT16 maxBlockSize;
|
|
SDCONFIG_BUS_MODE_DATA busSettings;
|
|
SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData;
|
|
TARGET_FUNCTION_CONTEXT *functionContext;
|
|
SDCONFIG_FUNC_SLOT_CURRENT_DATA slotCurrent;
|
|
SD_BUSCLOCK_RATE currentBusClock;
|
|
|
|
DBG_ASSERT(function != NULL);
|
|
DBG_ASSERT(handle != NULL);
|
|
|
|
device = addHifDevice(handle);
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device: %p\n", device));
|
|
functionContext = (TARGET_FUNCTION_CONTEXT *)function->pContext;
|
|
|
|
/*
|
|
* Issue commands to get the manufacturer ID and stuff and compare it
|
|
* against the rev Id derived from the ID registered during the
|
|
* initialization process. Report the device only in the case there
|
|
* is a match. In the case od SDIO, the bus driver has already queried
|
|
* these details so we just need to use their data structures to get the
|
|
* relevant values. Infact, the driver has already matched it against
|
|
* the Ids that we registered with it so we dont need to the step here.
|
|
*/
|
|
|
|
/* Configure the SDIO Bus Width */
|
|
if (onebitmode) {
|
|
data = SDIO_BUS_WIDTH_1_BIT;
|
|
status = SDLIB_IssueCMD52(handle, 0, SDIO_BUS_IF_REG, &data, 1, 1);
|
|
if (!SDIO_SUCCESS(status)) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Unable to set the bus width to 1 bit\n"));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Get current bus flags */
|
|
ZERO_OBJECT(busSettings);
|
|
|
|
busSettings.BusModeFlags = SDDEVICE_GET_BUSMODE_FLAGS(handle);
|
|
if (onebitmode) {
|
|
SDCONFIG_SET_BUS_WIDTH(busSettings.BusModeFlags,
|
|
SDCONFIG_BUS_WIDTH_1_BIT);
|
|
}
|
|
|
|
/* get the current operating clock, the bus driver sets us up based
|
|
* on what our CIS reports and what the host controller can handle
|
|
* we can use this to determine whether we want to drop our clock rate
|
|
* down */
|
|
currentBusClock = SDDEVICE_GET_OPER_CLOCK(handle);
|
|
busSettings.ClockRate = currentBusClock;
|
|
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
|
|
("HIF currently running at: %d \n",currentBusClock));
|
|
|
|
/* see if HIF wants to run at a lower clock speed, we may already be
|
|
* at that lower clock speed */
|
|
if (currentBusClock > (SDIO_CLOCK_FREQUENCY_DEFAULT >> busspeedlow)) {
|
|
busSettings.ClockRate = SDIO_CLOCK_FREQUENCY_DEFAULT >> busspeedlow;
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_WARN,
|
|
("HIF overriding clock to %d \n",busSettings.ClockRate));
|
|
}
|
|
|
|
/* Issue config request to override clock rate */
|
|
status = SDLIB_IssueConfig(handle, SDCONFIG_FUNC_CHANGE_BUS_MODE, &busSettings,
|
|
sizeof(SDCONFIG_BUS_MODE_DATA));
|
|
if (!SDIO_SUCCESS(status)) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Unable to configure the host clock\n"));
|
|
return FALSE;
|
|
} else {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
|
|
("Configured clock: %d, Maximum clock: %d\n",
|
|
busSettings.ActualClockRate,
|
|
SDDEVICE_GET_MAX_CLOCK(handle)));
|
|
}
|
|
|
|
/*
|
|
* Check if the target supports block mode. This result of this check
|
|
* can be used to implement the HIFReadWrite API.
|
|
*/
|
|
if (SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(handle)) {
|
|
/* Limit block size to operational block limit or card function
|
|
capability */
|
|
maxBlockSize = min(SDDEVICE_GET_OPER_BLOCK_LEN(handle),
|
|
SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(handle));
|
|
|
|
/* check if the card support multi-block transfers */
|
|
if (!(SDDEVICE_GET_SDIOCARD_CAPS(handle) & SDIO_CAPS_MULTI_BLOCK)) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Byte basis only\n"));
|
|
|
|
/* Limit block size to max byte basis */
|
|
maxBlockSize = min(maxBlockSize,
|
|
(A_UINT16)SDIO_MAX_LENGTH_BYTE_BASIS);
|
|
maxBlocks = 1;
|
|
} else {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Multi-block capable\n"));
|
|
maxBlocks = SDDEVICE_GET_OPER_BLOCKS(handle);
|
|
status = SDLIB_SetFunctionBlockSize(handle, HIF_MBOX_BLOCK_SIZE);
|
|
if (!SDIO_SUCCESS(status)) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Failed to set block size. Err:%d\n", status));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
|
|
("Bytes Per Block: %d bytes, Block Count:%d \n",
|
|
maxBlockSize, maxBlocks));
|
|
} else {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Function does not support Block Mode!\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
/* Allocate the slot current */
|
|
status = SDLIB_GetDefaultOpCurrent(handle, &slotCurrent.SlotCurrent);
|
|
if (SDIO_SUCCESS(status)) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Allocating Slot current: %d mA\n",
|
|
slotCurrent.SlotCurrent));
|
|
status = SDLIB_IssueConfig(handle, SDCONFIG_FUNC_ALLOC_SLOT_CURRENT,
|
|
&slotCurrent, sizeof(slotCurrent));
|
|
if (!SDIO_SUCCESS(status)) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Failed to allocate slot current %d\n", status));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Enable the dragon function */
|
|
count = 0;
|
|
enabled = FALSE;
|
|
fData.TimeOut = 1;
|
|
fData.EnableFlags = SDCONFIG_ENABLE_FUNC;
|
|
while ((count++ < SDWLAN_ENABLE_DISABLE_TIMEOUT) && !enabled)
|
|
{
|
|
/* Enable dragon */
|
|
status = SDLIB_IssueConfig(handle, SDCONFIG_FUNC_ENABLE_DISABLE,
|
|
&fData, sizeof(fData));
|
|
if (!SDIO_SUCCESS(status)) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
|
|
("Attempting to enable the card again\n"));
|
|
continue;
|
|
}
|
|
|
|
/* Mark the status as enabled */
|
|
enabled = TRUE;
|
|
}
|
|
|
|
/* Check if we were succesful in enabling the target */
|
|
if (!enabled) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
|
|
("Failed to communicate with the target\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
/* Allocate the bus requests to be used later */
|
|
A_MEMZERO(busRequest, sizeof(busRequest));
|
|
for (count = 0; count < BUS_REQUEST_MAX_NUM; count ++) {
|
|
if ((busRequest[count].request = SDDeviceAllocRequest(handle)) == NULL){
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_ERROR, ("Unable to allocate memory\n"));
|
|
/* TODO: Free the memory that has already been allocated */
|
|
return FALSE;
|
|
}
|
|
hifFreeBusRequest(&busRequest[count]);
|
|
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
|
|
("0x%08x = busRequest[%d].request = 0x%08x\n",
|
|
(unsigned int) &busRequest[count], count,
|
|
(unsigned int) busRequest[count].request));
|
|
}
|
|
|
|
/* Schedule a worker to handle device inserted, this is a temporary workaround
|
|
* to fix a deadlock if the device fails to intialize in the insertion handler
|
|
* The failure causes the instance to shutdown the HIF layer and unregister the
|
|
* function driver within the busdriver probe context which can deadlock
|
|
*
|
|
* NOTE: we cannot use the default work queue because that would block
|
|
* SD bus request processing for all synchronous I/O. We must use a kernel
|
|
* thread that is creating using the helper library.
|
|
* */
|
|
|
|
if (SDIO_SUCCESS(SDLIB_OSCreateHelper(&device->insert_helper,
|
|
insert_helper_func,
|
|
device))) {
|
|
device->helper_started = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static THREAD_RETURN insert_helper_func(POSKERNEL_HELPER pHelper)
|
|
{
|
|
|
|
/*
|
|
* Adding a wait of around a second before we issue the very first
|
|
* command to dragon. During the process of loading/unloading the
|
|
* driver repeatedly it was observed that we get a data timeout
|
|
* while accessing function 1 registers in the chip. The theory at
|
|
* this point is that some initialization delay in dragon is
|
|
* causing the SDIO state in dragon core to be not ready even after
|
|
* the ready bit indicates that function 1 is ready. Accomodating
|
|
* for this behavior by adding some delay in the driver before it
|
|
* issues the first command after switching on dragon. Need to
|
|
* investigate this a bit more - TODO
|
|
*/
|
|
|
|
A_MDELAY(1000);
|
|
/* Inform HTC */
|
|
if ((htcCallbacks.deviceInsertedHandler(SD_GET_OS_HELPER_CONTEXT(pHelper))) != A_OK) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device rejected\n"));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
HIFAckInterrupt(HIF_DEVICE *device)
|
|
{
|
|
SDIO_STATUS status;
|
|
DBG_ASSERT(device != NULL);
|
|
DBG_ASSERT(device->handle != NULL);
|
|
|
|
/* Acknowledge our function IRQ */
|
|
status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_ACK_IRQ,
|
|
NULL, 0);
|
|
DBG_ASSERT(SDIO_SUCCESS(status));
|
|
}
|
|
|
|
void
|
|
HIFUnMaskInterrupt(HIF_DEVICE *device)
|
|
{
|
|
SDIO_STATUS status;
|
|
|
|
DBG_ASSERT(device != NULL);
|
|
DBG_ASSERT(device->handle != NULL);
|
|
|
|
/* Register the IRQ Handler */
|
|
SDDEVICE_SET_IRQ_HANDLER(device->handle, hifIRQHandler, device);
|
|
|
|
/* Unmask our function IRQ */
|
|
status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_UNMASK_IRQ,
|
|
NULL, 0);
|
|
DBG_ASSERT(SDIO_SUCCESS(status));
|
|
}
|
|
|
|
void HIFMaskInterrupt(HIF_DEVICE *device)
|
|
{
|
|
SDIO_STATUS status;
|
|
DBG_ASSERT(device != NULL);
|
|
DBG_ASSERT(device->handle != NULL);
|
|
|
|
/* Mask our function IRQ */
|
|
status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_MASK_IRQ,
|
|
NULL, 0);
|
|
DBG_ASSERT(SDIO_SUCCESS(status));
|
|
|
|
/* Unregister the IRQ Handler */
|
|
SDDEVICE_SET_IRQ_HANDLER(device->handle, NULL, NULL);
|
|
}
|
|
|
|
static BUS_REQUEST *hifAllocateBusRequest(void)
|
|
{
|
|
BUS_REQUEST *busrequest;
|
|
|
|
/* Acquire lock */
|
|
CriticalSectionAcquire(&lock);
|
|
|
|
/* Remove first in list */
|
|
if((busrequest = s_busRequestFreeQueue) != NULL)
|
|
{
|
|
s_busRequestFreeQueue = busrequest->next;
|
|
}
|
|
|
|
/* Release lock */
|
|
CriticalSectionRelease(&lock);
|
|
|
|
return busrequest;
|
|
}
|
|
|
|
static void
|
|
hifFreeBusRequest(BUS_REQUEST *busrequest)
|
|
{
|
|
DBG_ASSERT(busrequest != NULL);
|
|
|
|
/* Acquire lock */
|
|
CriticalSectionAcquire(&lock);
|
|
|
|
/* Insert first in list */
|
|
busrequest->next = s_busRequestFreeQueue;
|
|
s_busRequestFreeQueue = busrequest;
|
|
|
|
/* Release lock */
|
|
CriticalSectionRelease(&lock);
|
|
}
|
|
|
|
void
|
|
hifDeviceRemoved(SDFUNCTION *function, SDDEVICE *handle)
|
|
{
|
|
A_STATUS status;
|
|
HIF_DEVICE *device;
|
|
DBG_ASSERT(function != NULL);
|
|
DBG_ASSERT(handle != NULL);
|
|
|
|
device = getHifDevice(handle);
|
|
status = htcCallbacks.deviceRemovedHandler(device->htc_handle, A_OK);
|
|
|
|
/* cleanup the helper thread */
|
|
if (device->helper_started) {
|
|
SDLIB_OSDeleteHelper(&device->insert_helper);
|
|
device->helper_started = FALSE;
|
|
}
|
|
|
|
delHifDevice(handle);
|
|
DBG_ASSERT(status == A_OK);
|
|
}
|
|
|
|
HIF_DEVICE *
|
|
addHifDevice(SDDEVICE *handle)
|
|
{
|
|
DBG_ASSERT(handle != NULL);
|
|
hifDevice[0].handle = handle;
|
|
return &hifDevice[0];
|
|
}
|
|
|
|
HIF_DEVICE *
|
|
getHifDevice(SDDEVICE *handle)
|
|
{
|
|
DBG_ASSERT(handle != NULL);
|
|
return &hifDevice[0];
|
|
}
|
|
|
|
void
|
|
delHifDevice(SDDEVICE *handle)
|
|
{
|
|
DBG_ASSERT(handle != NULL);
|
|
hifDevice[0].handle = NULL;
|
|
}
|
|
|
|
struct device*
|
|
HIFGetOSDevice(HIF_DEVICE *device)
|
|
{
|
|
return &device->handle->Device->dev;
|
|
}
|
|
|
|
static void ResetAllCards(void)
|
|
{
|
|
UINT8 data;
|
|
SDIO_STATUS status;
|
|
int i;
|
|
|
|
data = SDIO_IO_RESET;
|
|
|
|
/* set the I/O CARD reset bit:
|
|
* NOTE: we are exploiting a "feature" of the SDIO core that resets the core when you
|
|
* set the RES bit in the SDIO_IO_ABORT register. This bit however "normally" resets the
|
|
* I/O functions leaving the SDIO core in the same state (as per SDIO spec).
|
|
* In this design, this reset can be used to reset the SDIO core itself */
|
|
for (i = 0; i < HIF_MAX_DEVICES; i++) {
|
|
if (hifDevice[i].handle != NULL) {
|
|
AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
|
|
("Issuing I/O Card reset for instance: %d \n",i));
|
|
/* set the I/O Card reset bit */
|
|
status = SDLIB_IssueCMD52(hifDevice[i].handle,
|
|
0, /* function 0 space */
|
|
SDIO_IO_ABORT_REG,
|
|
&data,
|
|
1, /* 1 byte */
|
|
TRUE); /* write */
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void HIFSetHandle(void *hif_handle, void *handle)
|
|
{
|
|
HIF_DEVICE *device = (HIF_DEVICE *) hif_handle;
|
|
|
|
device->htc_handle = handle;
|
|
|
|
return;
|
|
}
|