aboutsummaryrefslogtreecommitdiff
path: root/drivers/ar6000/hif
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2009-09-23 14:27:26 +0200
committerLars-Peter Clausen <lars@metafoo.de>2009-12-27 19:41:58 +0100
commit4c9c79642d1c8353a339e5b662c0a735fdcae424 (patch)
treea1f37a14902c290776efb57898790a82ee115769 /drivers/ar6000/hif
parent22763c5cf3690a681551162c15d34d935308c8d7 (diff)
Add ar6000 wireless driver.
Diffstat (limited to 'drivers/ar6000/hif')
-rw-r--r--drivers/ar6000/hif/hif.c824
-rw-r--r--drivers/ar6000/hif/hif2.c768
-rw-r--r--drivers/ar6000/hif/hif_internal.h102
3 files changed, 1694 insertions, 0 deletions
diff --git a/drivers/ar6000/hif/hif.c b/drivers/ar6000/hif/hif.c
new file mode 100644
index 00000000000..d04486c35b0
--- /dev/null
+++ b/drivers/ar6000/hif/hif.c
@@ -0,0 +1,824 @@
+/*
+ * @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;
+}
diff --git a/drivers/ar6000/hif/hif2.c b/drivers/ar6000/hif/hif2.c
new file mode 100644
index 00000000000..386d96e668f
--- /dev/null
+++ b/drivers/ar6000/hif/hif2.c
@@ -0,0 +1,768 @@
+/*
+ * hif2.c - HIF layer re-implementation for the Linux SDIO stack
+ *
+ * Copyright (C) 2008, 2009 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner@openmoko.org>
+ * 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;
+ *
+ * Based on:
+ *
+ * @abstract: HIF layer reference implementation for Atheros SDIO stack
+ * @notice: Copyright (c) 2004-2006 Atheros Communications Inc.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_ids.h>
+
+#include "athdefs.h"
+#include "a_types.h"
+#include "hif.h"
+
+
+/* @@@ Hack - this wants cleaning up */
+
+#ifdef CONFIG_MACH_NEO1973_GTA02
+
+#include <mach/gta02-pm-wlan.h>
+
+#else /* CONFIG_MACH_NEO1973_GTA02 */
+
+#define gta02_wlan_query_rfkill_lock() 1
+#define gta02_wlan_set_rfkill_cb(cb, hif) ((void) cb)
+#define gta02_wlan_query_rfkill_unlock()
+#define gta02_wlan_clear_rfkill_cb()
+
+#endif /* !CONFIG_MACH_NEO1973_GTA02 */
+
+
+/*
+ * KNOWN BUGS:
+ *
+ * - HIF_DEVICE_IRQ_ASYNC_SYNC doesn't work yet (gets MMC errors)
+ * - latency can reach hundreds of ms, probably because of scheduling delays
+ * - packets go through about three queues before finally hitting the network
+ */
+
+/*
+ * Differences from Atheros' HIFs:
+ *
+ * - synchronous and asynchronous requests may get reordered with respect to
+ * each other, e.g., if HIFReadWrite returns for an asynchronous request and
+ * then HIFReadWrite is called for a synchronous request, the synchronous
+ * request may be executed before the asynchronous request.
+ *
+ * - request queue locking seems unnecessarily complex in the Atheros HIFs.
+ *
+ * - Atheros mask interrupts by calling sdio_claim_irq/sdio_release_irq, which
+ * can cause quite a bit of overhead. This HIF has its own light-weight
+ * interrupt masking.
+ *
+ * - Atheros call deviceInsertedHandler from a thread spawned off the probe or
+ * device insertion function. The original explanation for the Atheros SDIO
+ * stack said that this is done because a delay is needed to let the chip
+ * complete initialization. There is indeed a one second delay in the thread.
+ *
+ * The Atheros Linux SDIO HIF removes the delay and only retains the thread.
+ * Experimentally removing the thread didn't show any conflicts, so let's get
+ * rid of it for good.
+ *
+ * - The Atheros SDIO stack with Samuel's driver sets SDIO_CCCR_POWER in
+ * SDIO_POWER_EMPC. Atheros' Linux SDIO code apparently doesn't. We don't
+ * either, and this seems to work fine.
+ * @@@ Need to check this with Atheros.
+ */
+
+
+#define MBOXES 4
+
+#define HIF_MBOX_BLOCK_SIZE 128
+#define HIF_MBOX_BASE_ADDR 0x800
+#define HIF_MBOX_WIDTH 0x800
+#define HIF_MBOX_START_ADDR(mbox) \
+ (HIF_MBOX_BASE_ADDR+(mbox)*HIF_MBOX_WIDTH)
+
+
+struct hif_device {
+ void *htc_handle;
+ struct sdio_func *func;
+
+ /*
+ * @@@ our sweet little bit of bogosity - the mechanism that lets us
+ * use the SDIO stack from softirqs. This really wants to use skbs.
+ */
+ struct list_head queue;
+ spinlock_t queue_lock;
+ struct task_struct *io_task;
+ wait_queue_head_t wait;
+
+ /*
+ * activate_lock protects "active" and the activation/deactivation
+ * process itself.
+ *
+ * Relation to other locks: The SDIO function can be claimed while
+ * activate_lock is being held, but trying to acquire activate_lock
+ * while having ownership of the SDIO function could cause a deadlock.
+ */
+ int active;
+ struct mutex activate_lock;
+};
+
+struct hif_request {
+ struct list_head list;
+ struct sdio_func *func;
+ int (*read)(struct sdio_func *func,
+ void *dst, unsigned int addr, int count);
+ int (*write)(struct sdio_func *func,
+ unsigned int addr, void *src, int count);
+ void *buf;
+ unsigned long addr;
+ int len;
+ A_STATUS (*completion)(void *context, A_STATUS status);
+ void *context;
+};
+
+
+static HTC_CALLBACKS htcCallbacks;
+
+/*
+ * shutdown_lock prevents recursion through HIFShutDownDevice
+ */
+static DEFINE_MUTEX(shutdown_lock);
+
+
+/* ----- Request processing ------------------------------------------------ */
+
+
+static A_STATUS process_request(struct hif_request *req)
+{
+ int ret;
+ A_STATUS status;
+
+ dev_dbg(&req->func->dev, "process_request(req %p)\n", req);
+ sdio_claim_host(req->func);
+ if (req->read) {
+ ret = req->read(req->func, req->buf, req->addr, req->len);
+ } else {
+ ret = req->write(req->func, req->addr, req->buf, req->len);
+ }
+ sdio_release_host(req->func);
+ status = ret ? A_ERROR : A_OK;
+ if (req->completion)
+ req->completion(req->context, status);
+ kfree(req);
+ return status;
+}
+
+
+static void enqueue_request(struct hif_device *hif, struct hif_request *req)
+{
+ unsigned long flags;
+
+ dev_dbg(&req->func->dev, "enqueue_request(req %p)\n", req);
+ spin_lock_irqsave(&hif->queue_lock, flags);
+ list_add_tail(&req->list, &hif->queue);
+ spin_unlock_irqrestore(&hif->queue_lock, flags);
+ wake_up(&hif->wait);
+}
+
+
+static struct hif_request *dequeue_request(struct hif_device *hif)
+{
+ struct hif_request *req;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hif->queue_lock, flags);
+ if (list_empty(&hif->queue))
+ req = NULL;
+ else {
+ req = list_first_entry(&hif->queue,
+ struct hif_request, list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&hif->queue_lock, flags);
+ return req;
+}
+
+
+static void wait_queue_empty(struct hif_device *hif)
+{
+ unsigned long flags;
+ int empty;
+
+ while (1) {
+ spin_lock_irqsave(&hif->queue_lock, flags);
+ empty = list_empty(&hif->queue);
+ spin_unlock_irqrestore(&hif->queue_lock, flags);
+ if (empty)
+ break;
+ else
+ yield();
+ }
+}
+
+
+static int io(void *data)
+{
+ struct hif_device *hif = data;
+ struct sched_param param = { .sched_priority = 2 };
+ /* one priority level slower than ksdioirqd (which is at 1) */
+ DEFINE_WAIT(wait);
+ struct hif_request *req;
+
+ sched_setscheduler(current, SCHED_FIFO, &param);
+
+ while (1) {
+ while (1) {
+ /*
+ * Since we never use signals here, one might think
+ * that this ought to be TASK_UNINTERRUPTIBLE. However,
+ * such a task would increase the load average and,
+ * worse, it would trigger the softlockup check.
+ */
+ prepare_to_wait(&hif->wait, &wait, TASK_INTERRUPTIBLE);
+ if (kthread_should_stop()) {
+ finish_wait(&hif->wait, &wait);
+ return 0;
+ }
+ req = dequeue_request(hif);
+ if (req)
+ break;
+ schedule();
+ }
+ finish_wait(&hif->wait, &wait);
+
+ (void) process_request(req);
+ }
+ return 0;
+}
+
+
+A_STATUS HIFReadWrite(HIF_DEVICE *hif, A_UINT32 address, A_UCHAR *buffer,
+ A_UINT32 length, A_UINT32 request, void *context)
+{
+ struct device *dev = HIFGetOSDevice(hif);
+ struct hif_request *req;
+
+ dev_dbg(dev, "HIFReadWrite(device %p, address 0x%x, buffer %p, "
+ "length %d, request 0x%x, context %p)\n",
+ hif, address, buffer, length, request, context);
+
+ BUG_ON(!(request & (HIF_SYNCHRONOUS | HIF_ASYNCHRONOUS)));
+ BUG_ON(!(request & (HIF_BYTE_BASIS | HIF_BLOCK_BASIS)));
+ BUG_ON(!(request & (HIF_READ | HIF_WRITE)));
+ BUG_ON(!(request & HIF_EXTENDED_IO));
+
+ if (address >= HIF_MBOX_START_ADDR(0) &&
+ address < HIF_MBOX_START_ADDR(MBOXES+1)) {
+ BUG_ON(length > HIF_MBOX_WIDTH);
+ /* Adjust the address so that the last byte falls on the EOM
+ address. */
+ address += HIF_MBOX_WIDTH-length;
+ }
+
+ req = kzalloc(sizeof(*req), GFP_ATOMIC);
+ if (!req) {
+ if (request & HIF_ASYNCHRONOUS)
+ htcCallbacks.rwCompletionHandler(context, A_ERROR);
+ return A_ERROR;
+ }
+
+ req->func = hif->func;
+ req->addr = address;
+ req->buf = buffer;
+ req->len = length;
+
+ if (request & HIF_READ) {
+ if (request & HIF_FIXED_ADDRESS)
+ req->read = sdio_readsb;
+ else
+ req->read = sdio_memcpy_fromio;
+ } else {
+ if (request & HIF_FIXED_ADDRESS)
+ req->write = sdio_writesb;
+ else
+ req->write = sdio_memcpy_toio;
+ }
+
+ if (!(request & HIF_ASYNCHRONOUS))
+ return process_request(req);
+
+ req->completion = htcCallbacks.rwCompletionHandler;
+ req->context = context;
+ enqueue_request(hif, req);
+
+ return A_OK;
+}
+
+
+/* ----- Interrupt handling ------------------------------------------------ */
+
+/*
+ * Volatile ought to be good enough to make gcc do the right thing on S3C24xx.
+ * No need to use atomic or put barriers, keeping the code more readable.
+ *
+ * Warning: this story changes if going SMP/SMT.
+ */
+
+static volatile int masked = 1;
+static volatile int pending;
+static volatile int in_interrupt;
+
+
+static void ar6000_do_irq(struct sdio_func *func)
+{
+ HIF_DEVICE *hif = sdio_get_drvdata(func);
+ struct device *dev = HIFGetOSDevice(hif);
+ A_STATUS status;
+
+ dev_dbg(dev, "ar6000_do_irq -> %p\n", htcCallbacks.dsrHandler);
+
+ status = htcCallbacks.dsrHandler(hif->htc_handle);
+ BUG_ON(status != A_OK);
+}
+
+
+static void sdio_ar6000_irq(struct sdio_func *func)
+{
+ HIF_DEVICE *hif = sdio_get_drvdata(func);
+ struct device *dev = HIFGetOSDevice(hif);
+
+ dev_dbg(dev, "sdio_ar6000_irq\n");
+
+ in_interrupt = 1;
+ if (masked) {
+ in_interrupt = 0;
+ pending++;
+ return;
+ }
+ /*
+ * @@@ This is ugly. If we don't drop the lock, we'll deadlock when
+ * the handler tries to do SDIO. So there are four choices:
+ *
+ * 1) Break the call chain by calling the callback from a workqueue.
+ * Ugh.
+ * 2) Make process_request aware that we already have the lock.
+ * 3) Drop the lock. Which is ugly but should be safe as long as we're
+ * making sure the device doesn't go away.
+ * 4) Change the AR6k driver such that it only issues asynchronous
+ * quests when called from an interrupt.
+ *
+ * Solution 2) is probably the best for now. Will try it later.
+ */
+ sdio_release_host(func);
+ ar6000_do_irq(func);
+ sdio_claim_host(func);
+ in_interrupt = 0;
+}
+
+
+void HIFAckInterrupt(HIF_DEVICE *hif)
+{
+ struct device *dev = HIFGetOSDevice(hif);
+
+ dev_dbg(dev, "HIFAckInterrupt\n");
+ /* do nothing */
+}
+
+
+void HIFUnMaskInterrupt(HIF_DEVICE *hif)
+{
+ struct device *dev = HIFGetOSDevice(hif);
+
+ dev_dbg(dev, "HIFUnMaskInterrupt\n");
+ do {
+ masked = 1;
+ if (pending) {
+ pending = 0;
+ ar6000_do_irq(hif->func);
+ /* We may take an interrupt before unmasking and thus
+ get it pending. In this case, we just loop back. */
+ }
+ masked = 0;
+ }
+ while (pending);
+}
+
+
+void HIFMaskInterrupt(HIF_DEVICE *hif)
+{
+ struct device *dev = HIFGetOSDevice(hif);
+
+ dev_dbg(dev, "HIFMaskInterrupt\n");
+ /*
+ * Since sdio_ar6000_irq can also be called from a process context, we
+ * may conceivably end up racing with it. Thus, we need to wait until
+ * we can be sure that no concurrent interrupt processing is going on
+ * before we return.
+ *
+ * Note: this may be a bit on the paranoid side - the callers may
+ * actually be nice enough to disable scheduling. Check later.
+ */
+ masked = 1;
+ while (in_interrupt)
+ yield();
+}
+
+
+/* ----- HIF API glue functions -------------------------------------------- */
+
+
+struct device *HIFGetOSDevice(HIF_DEVICE *hif)
+{
+ return &hif->func->dev;
+}
+
+
+void HIFSetHandle(void *hif_handle, void *handle)
+{
+ HIF_DEVICE *hif = (HIF_DEVICE *) hif_handle;
+
+ hif->htc_handle = handle;
+}
+
+
+/* ----- Device configuration (HIF side) ----------------------------------- */
+
+
+A_STATUS HIFConfigureDevice(HIF_DEVICE *hif,
+ HIF_DEVICE_CONFIG_OPCODE opcode, void *config, A_UINT32 configLen)
+{
+ struct device *dev = HIFGetOSDevice(hif);
+ HIF_DEVICE_IRQ_PROCESSING_MODE *ipm_cfg = config;
+ A_UINT32 *mbs_cfg = config;
+ int i;
+
+ dev_dbg(dev, "HIFConfigureDevice\n");
+
+ switch (opcode) {
+ case HIF_DEVICE_GET_MBOX_BLOCK_SIZE:
+ for (i = 0; i != MBOXES; i++)
+ mbs_cfg[i] = HIF_MBOX_BLOCK_SIZE;
+ break;
+ case HIF_DEVICE_GET_MBOX_ADDR:
+ for (i = 0; i != MBOXES; i++)
+ mbs_cfg[i] = HIF_MBOX_START_ADDR(i);
+ break;
+ case HIF_DEVICE_GET_IRQ_PROC_MODE:
+ *ipm_cfg = HIF_DEVICE_IRQ_SYNC_ONLY;
+// *ipm_cfg = HIF_DEVICE_IRQ_ASYNC_SYNC;
+ break;
+ default:
+ return A_ERROR;
+ }
+ return A_OK;
+}
+
+
+/* ----- Device probe and removal (Linux side) ----------------------------- */
+
+
+static int ar6000_do_activate(struct hif_device *hif)
+{
+ struct sdio_func *func = hif->func;
+ struct device *dev = &func->dev;
+ int ret;
+
+ dev_dbg(dev, "ar6000_do_activate\n");
+
+ sdio_claim_host(func);
+ sdio_enable_func(func);
+
+ INIT_LIST_HEAD(&hif->queue);
+ init_waitqueue_head(&hif->wait);
+ spin_lock_init(&hif->queue_lock);
+
+ ret = sdio_set_block_size(func, HIF_MBOX_BLOCK_SIZE);
+ if (ret < 0) {
+ dev_err(dev, "sdio_set_block_size returns %d\n", ret);
+ goto out_enabled;
+ }
+ ret = sdio_claim_irq(func, sdio_ar6000_irq);
+ if (ret) {
+ dev_err(dev, "sdio_claim_irq returns %d\n", ret);
+ goto out_enabled;
+ }
+ /* Set SDIO_BUS_CD_DISABLE in SDIO_CCCR_IF ? */
+#if 0
+ sdio_f0_writeb(func, SDIO_CCCR_CAP_E4MI, SDIO_CCCR_CAPS, &ret);
+ if (ret) {
+ dev_err(dev, "sdio_f0_writeb(SDIO_CCCR_CAPS) returns %d\n",
+ ret);
+ goto out_got_irq;
+ }
+#else
+ if (0) /* avoid warning */
+ goto out_got_irq;
+#endif
+
+ sdio_release_host(func);
+
+ hif->io_task = kthread_run(io, hif, "ar6000_io");
+ ret = IS_ERR(hif->io_task);
+ if (ret) {
+ dev_err(dev, "kthread_run(ar6000_io): %d\n", ret);
+ goto out_func_ready;
+ }
+
+ ret = htcCallbacks.deviceInsertedHandler(hif);
+ if (ret == A_OK)
+ return 0;
+
+ dev_err(dev, "deviceInsertedHandler: %d\n", ret);
+
+ ret = kthread_stop(hif->io_task);
+ if (ret)
+ dev_err(dev, "kthread_stop (ar6000_io): %d\n", ret);
+
+out_func_ready:
+ sdio_claim_host(func);
+
+out_got_irq:
+ sdio_release_irq(func);
+
+out_enabled:
+ sdio_disable_func(func);
+ sdio_release_host(func);
+
+ return ret;
+}
+
+
+static void ar6000_do_deactivate(struct hif_device *hif)
+{
+ struct sdio_func *func = hif->func;
+ struct device *dev = &func->dev;
+ int ret;
+
+ dev_dbg(dev, "ar6000_do_deactivate\n");
+ if (!hif->active)
+ return;
+
+ if (mutex_trylock(&shutdown_lock)) {
+ /*
+ * Funny, Atheros' HIF does this call, but this just puts us in
+ * a recursion through HTCShutDown/HIFShutDown if unloading the
+ * module.
+ *
+ * However, we need it for suspend/resume. See the comment at
+ * HIFShutDown, below.
+ */
+ ret = htcCallbacks.deviceRemovedHandler(hif->htc_handle, A_OK);
+ if (ret != A_OK)
+ dev_err(dev, "deviceRemovedHandler: %d\n", ret);
+ mutex_unlock(&shutdown_lock);
+ }
+ wait_queue_empty(hif);
+ ret = kthread_stop(hif->io_task);
+ if (ret)
+ dev_err(dev, "kthread_stop (ar6000_io): %d\n", ret);
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+}
+
+
+static int ar6000_activate(struct hif_device *hif)
+{
+ int ret = 0;
+
+ dev_dbg(&hif->func->dev, "ar6000_activate\n");
+ mutex_lock(&hif->activate_lock);
+ if (!hif->active) {
+ ret = ar6000_do_activate(hif);
+ if (ret) {
+ printk(KERN_ERR "%s: Failed to activate %d\n",
+ __func__, ret);
+ goto out;
+ }
+ hif->active = 1;
+ }
+out:
+ mutex_unlock(&hif->activate_lock);
+ return ret;
+}
+
+
+static void ar6000_deactivate(struct hif_device *hif)
+{
+ dev_dbg(&hif->func->dev, "ar6000_deactivate\n");
+ mutex_lock(&hif->activate_lock);
+ if (hif->active) {
+ ar6000_do_deactivate(hif);
+ hif->active = 0;
+ }
+ mutex_unlock(&hif->activate_lock);
+}
+
+
+static int ar6000_rfkill_cb(void *data, int on)
+{
+ struct hif_device *hif = data;
+ struct sdio_func *func = hif->func;
+ struct device *dev = &func->dev;
+
+ dev_dbg(dev, "ar6000_rfkill_cb: on %d\n", on);
+ if (on)
+ return ar6000_activate(hif);
+ ar6000_deactivate(hif);
+ return 0;
+}
+
+
+static int sdio_ar6000_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct device *dev = &func->dev;
+ struct hif_device *hif;
+ int ret = 0;
+
+ dev_dbg(dev, "sdio_ar6000_probe\n");
+ BUG_ON(!htcCallbacks.deviceInsertedHandler);
+
+ hif = kzalloc(sizeof(*hif), GFP_KERNEL);
+ if (!hif)
+ return -ENOMEM;
+
+ sdio_set_drvdata(func, hif);
+ hif->func = func;
+ mutex_init(&hif->activate_lock);
+ hif->active = 0;
+
+ if (gta02_wlan_query_rfkill_lock())
+ ret = ar6000_activate(hif);
+ if (!ret) {
+ gta02_wlan_set_rfkill_cb(ar6000_rfkill_cb, hif);
+ return 0;
+ }
+ gta02_wlan_query_rfkill_unlock();
+ sdio_set_drvdata(func, NULL);
+ kfree(hif);
+ return ret;
+}
+
+
+static void sdio_ar6000_remove(struct sdio_func *func)
+{
+ struct device *dev = &func->dev;
+ HIF_DEVICE *hif = sdio_get_drvdata(func);
+
+ dev_dbg(dev, "sdio_ar6000_remove\n");
+ gta02_wlan_clear_rfkill_cb();
+ ar6000_deactivate(hif);
+ sdio_set_drvdata(func, NULL);
+ kfree(hif);
+}
+
+
+/* ----- Device registration/unregistration (called by HIF) ---------------- */
+
+
+#define ATHEROS_SDIO_DEVICE(id, offset) \
+ SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_##id | (offset))
+
+static const struct sdio_device_id sdio_ar6000_ids[] = {
+ { ATHEROS_SDIO_DEVICE(AR6002, 0) },
+ { ATHEROS_SDIO_DEVICE(AR6002, 0x1) },
+ { ATHEROS_SDIO_DEVICE(AR6001, 0x8) },
+ { ATHEROS_SDIO_DEVICE(AR6001, 0x9) },
+ { ATHEROS_SDIO_DEVICE(AR6001, 0xa) },
+ { ATHEROS_SDIO_DEVICE(AR6001, 0xb) },
+ { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(sdio, sdio_ar6000_ids);
+
+
+static struct sdio_driver sdio_ar6000_driver = {
+ .probe = sdio_ar6000_probe,
+ .remove = sdio_ar6000_remove,
+ .name = "sdio_ar6000",
+ .id_table = sdio_ar6000_ids,
+};
+
+
+int HIFInit(HTC_CALLBACKS *callbacks)
+{
+ int ret;
+
+ BUG_ON(!callbacks);
+
+ printk(KERN_DEBUG "HIFInit\n");
+ htcCallbacks = *callbacks;
+
+ ret = sdio_register_driver(&sdio_ar6000_driver);
+ if (ret) {
+ printk(KERN_ERR
+ "sdio_register_driver(sdio_ar6000_driver): %d\n", ret);
+ return A_ERROR;
+ }
+
+ return 0;
+}
+
+
+/*
+ * We have four possible call chains here:
+ *
+ * System shutdown/reboot:
+ *
+ * kernel_restart_prepare ...> device_shutdown ... > s3cmci_shutdown ->
+ * mmc_remove_host ..> sdio_bus_remove -> sdio_ar6000_remove ->
+ * ar6000_deactivate -> ar6000_do_deactivate ->
+ * deviceRemovedHandler (HTCTargetRemovedHandler) -> HIFShutDownDevice
+ *
+ * This is roughly the same sequence as suspend, described below.
+ *
+ * Module removal:
+ *
+ * sys_delete_module -> ar6000_cleanup_module -> HTCShutDown ->
+ * HIFShutDownDevice -> sdio_unregister_driver ...> sdio_bus_remove ->
+ * sdio_ar6000_remove -> ar6000_deactivate -> ar6000_do_deactivate
+ *
+ * In this case, HIFShutDownDevice must call sdio_unregister_driver to
+ * notify the driver about its removal. ar6000_do_deactivate must not call
+ * deviceRemovedHandler, because that would loop back into HIFShutDownDevice.
+ *
+ * Suspend:
+ *
+ * device_suspend ...> s3cmci_suspend ...> sdio_bus_remove ->
+ * sdio_ar6000_remove -> ar6000_deactivate -> ar6000_do_deactivate ->
+ * deviceRemovedHandler (HTCTargetRemovedHandler) -> HIFShutDownDevice
+ *
+ * We must call deviceRemovedHandler to inform the ar6k stack that the device
+ * has been removed. Since HTCTargetRemovedHandler calls back into
+ * HIFShutDownDevice, we must also prevent the call to
+ * sdio_unregister_driver, or we'd end up recursing into the SDIO stack,
+ * eventually deadlocking somewhere.
+ *
+ * rfkill:
+ *
+ * rfkill_state_store -> rfkill_toggle_radio -> gta02_wlan_toggle_radio ->
+ * ar6000_rfkill_cb -> ar6000_deactivate -> ar6000_do_deactivate ->
+ * deviceRemovedHandler (HTCTargetRemovedHandler) -> HIFShutDownDevice
+ *
+ * This is similar to suspend - only the entry point changes.
+ */
+
+void HIFShutDownDevice(HIF_DEVICE *hif)
+{
+ /* Beware, HTCShutDown calls us with hif == NULL ! */
+ if (mutex_trylock(&shutdown_lock)) {
+ sdio_unregister_driver(&sdio_ar6000_driver);
+ mutex_unlock(&shutdown_lock);
+ }
+}
diff --git a/drivers/ar6000/hif/hif_internal.h b/drivers/ar6000/hif/hif_internal.h
new file mode 100644
index 00000000000..d8fc1013234
--- /dev/null
+++ b/drivers/ar6000/hif/hif_internal.h
@@ -0,0 +1,102 @@
+/*
+ * @file: hif_internal.h
+ *
+ * @abstract: internal header file for hif layer
+ *
+ * @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 <linux/sdio/ctsystem.h>
+#include <linux/sdio/sdio_busdriver.h>
+#include <linux/sdio/_sdio_defs.h>
+#include <linux/sdio/sdio_lib.h>
+#include "a_config.h"
+#include "athdefs.h"
+#include "a_types.h"
+#include "a_osapi.h"
+#include "hif.h"
+
+#define MANUFACTURER_ID_AR6001_BASE 0x100
+#define MANUFACTURER_ID_AR6002_BASE 0x200
+#define FUNCTION_CLASS 0x0
+#define MANUFACTURER_CODE 0x271
+
+#define BUS_REQUEST_MAX_NUM 64
+
+#define SDIO_CLOCK_FREQUENCY_DEFAULT 25000000
+#define SDWLAN_ENABLE_DISABLE_TIMEOUT 20
+#define FLAGS_CARD_ENAB 0x02
+#define FLAGS_CARD_IRQ_UNMSK 0x04
+
+#define HIF_MBOX_BLOCK_SIZE 128
+#define HIF_MBOX_BASE_ADDR 0x800
+#define HIF_MBOX_WIDTH 0x800
+#define HIF_MBOX0_BLOCK_SIZE 1
+#define HIF_MBOX1_BLOCK_SIZE HIF_MBOX_BLOCK_SIZE
+#define HIF_MBOX2_BLOCK_SIZE HIF_MBOX_BLOCK_SIZE
+#define HIF_MBOX3_BLOCK_SIZE HIF_MBOX_BLOCK_SIZE
+
+#define HIF_MBOX_START_ADDR(mbox) \
+ HIF_MBOX_BASE_ADDR + mbox * HIF_MBOX_WIDTH
+
+#define HIF_MBOX_END_ADDR(mbox) \
+ HIF_MBOX_START_ADDR(mbox) + HIF_MBOX_WIDTH - 1
+
+struct hif_device {
+ SDDEVICE *handle;
+ void *htc_handle;
+ OSKERNEL_HELPER insert_helper;
+ BOOL helper_started;
+};
+
+typedef struct target_function_context {
+ SDFUNCTION function; /* function description of the bus driver */
+ OS_SEMAPHORE instanceSem; /* instance lock. Unused */
+ SDLIST instanceList; /* list of instances. Unused */
+} TARGET_FUNCTION_CONTEXT;
+
+typedef struct bus_request {
+ struct bus_request *next;
+ SDREQUEST *request;
+ void *context;
+} BUS_REQUEST;
+
+BOOL
+hifDeviceInserted(SDFUNCTION *function, SDDEVICE *device);
+
+void
+hifDeviceRemoved(SDFUNCTION *function, SDDEVICE *device);
+
+SDREQUEST *
+hifAllocateDeviceRequest(SDDEVICE *device);
+
+void
+hifFreeDeviceRequest(SDREQUEST *request);
+
+void
+hifRWCompletionHandler(SDREQUEST *request);
+
+void
+hifIRQHandler(void *context);
+
+HIF_DEVICE *
+addHifDevice(SDDEVICE *handle);
+
+HIF_DEVICE *
+getHifDevice(SDDEVICE *handle);
+
+void
+delHifDevice(SDDEVICE *handle);