www.pudn.com > sdio-2.6.18-full.rar > sdio_bus_events.c
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @file: sdio_bus_events.c @abstract: OS independent bus driver support #notes: this file contains various event handlers and helpers @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. * * Portions o this code were developed with information supplied from the * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: * * The following conditions apply to the release of the SD simplified specification (“Simplified * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified * Specification may require a license from the SD Card Association or other third parties. * Disclaimers: * The information contained in the Simplified Specification is presented only as a standard * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for * any damages, any infringements of patents or other right of the SD Card Association or any third * parties, which may result from its use. No license is granted by implication, estoppel or otherwise * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall * be construed as an obligation by the SD Card Association to disclose or distribute any technical * information, know-how or other confidential information to any third party. * * * The initial developers of the original code are Seung Yi and Paul Lever * * sdio@atheros.com * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ #define MODULE_NAME SDBUSDRIVER #include#include #include #include "_busdriver.h" #include #include static SDIO_STATUS ScanSlotForCard(PSDHCD pHcd, PBOOL pCardPresent); static void GetPendingIrqComplete(PSDREQUEST pReq); static void ProcessPendingIrqs(PSDHCD pHcd, UINT8 IntPendingMsk); /* * DeviceDetach - tell core a device was removed from a slot */ SDIO_STATUS DeviceDetach(PSDHCD pHcd) { SDCONFIG_SDIO_INT_CTRL_DATA irqData; ZERO_OBJECT(irqData); DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: DeviceDetach\n")); /* tell any function drivers we are gone */ RemoveHcdFunctions(pHcd); /* delete the devices associated with this HCD */ DeleteDevices(pHcd); /* check and see if there are any IRQs that were left enabled */ if (pHcd->IrqsEnabled) { irqData.SlotIRQEnable = FALSE; /* turn off IRQ detection in HCD */ _IssueConfig(pHcd,SDCONFIG_SDIO_INT_CTRL,(PVOID)&irqData, sizeof(irqData)); } /* reset hcd state */ ResetHcdState(pHcd); DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: DeviceDetach\n")); return SDIO_STATUS_SUCCESS; } /* * DeviceAttach - tell core a device was inserted into a slot */ SDIO_STATUS DeviceAttach(PSDHCD pHcd) { SDIO_STATUS status = SDIO_STATUS_SUCCESS; PSDDEVICE pDevice = NULL; UINT ii; if (IS_CARD_PRESENT(pHcd)) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: DeviceAttach called on occupied slot!\n")); return SDIO_STATUS_ERROR; } DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: DeviceAttach bdctxt:0x%X \n", (UINT32)pBusContext)); if (IS_HCD_RAW(pHcd)) { DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: RAW HCD (%s) device attach \n",pHcd->pName)); /* this is a raw HCD */ memset(&pHcd->CardProperties,0,sizeof(pHcd->CardProperties)); pHcd->CardProperties.Flags = CARD_RAW; pHcd->CardProperties.IOFnCount = 0; /* for raw HCD, set up minimum parameters * since we cannot determine these values using any standard, use values * reported by the HCD */ /* the operational rate is just the max clock rate reported */ pHcd->CardProperties.OperBusClock = pHcd->MaxClockRate; /* the max bytes per data transfer is just the max bytes per block */ pHcd->CardProperties.OperBlockLenLimit = pHcd->MaxBytesPerBlock; /* if the raw HCD uses blocks to transfer, report the operational size * from the HCD max value */ pHcd->CardProperties.OperBlockCountLimit = pHcd->MaxBlocksPerTrans; /* set the slot preferred voltage */ pHcd->CardProperties.CardVoltage = pHcd->SlotVoltagePreferred; } else { /* initialize this card and get card properties */ if (!SDIO_SUCCESS((status = SDInitializeCard(pHcd)))) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: DeviceAttach, failed to initialize card, %d\n", status)); return status; } } #ifndef CT_CONFIG_NO_SDMMC /* check for SD or MMC, this must be done first as the query may involve * de-selecting the card */ do { if (!(pHcd->CardProperties.Flags & (CARD_MMC | CARD_SD | CARD_RAW))) { /* none of these were discovered */ break; } pDevice = AllocateDevice(pHcd); if (NULL == pDevice) { break; } if (pHcd->CardProperties.Flags & CARD_RAW) { /* set function number to 1 for IRQ processing */ SDDEVICE_SET_SDIO_FUNCNO(pDevice,1); } else { /* get the ID info for the SD/MMC Card */ if (!SDIO_SUCCESS((status = SDQuerySDMMCInfo(pDevice)))) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: DeviceAttach, query SDMMC Info failed \n")); FreeDevice(pDevice); break; } } AddDeviceToList(pDevice); /* look for a function driver to handle this card */ ProbeForFunction(pDevice, pHcd); } while (FALSE); #endif /* create a device for each I/O function */ for(ii= 1; ii <= pHcd->CardProperties.IOFnCount; ii++) { pDevice = AllocateDevice(pHcd); if (NULL == pDevice) { break; } /* set the function number */ SDDEVICE_SET_SDIO_FUNCNO(pDevice,ii); /* get the ID info for each I/O function */ if (!SDIO_SUCCESS((status = SDQuerySDIOInfo(pDevice)))) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: DeviceAttach, could not query SDIO Info, funcNo:%d status:%d \n", ii, status)); FreeDevice(pDevice); /* keep loading other functions */ continue; } AddDeviceToList(pDevice); /* look for a function driver to handle this card */ ProbeForFunction(pDevice, pHcd); } DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: DeviceAttach \n")); return status; } static INLINE void CompleteRequestCheckCancel(PSDHCD pHcd, PSDREQUEST pReqToComplete) { BOOL cancel = FALSE; PSDFUNCTION pFunc = NULL; /* handle cancel of current request */ if (pReqToComplete->Flags & SDREQ_FLAGS_CANCELED) { DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - _SDIO_HandleHcdEvent: cancelling req 0X%X\n", (UINT)pReqToComplete)); cancel = TRUE; pReqToComplete->Status = SDIO_STATUS_CANCELED; pFunc = pReqToComplete->pFunction; DBG_ASSERT(pFunc != NULL); } DoRequestCompletion(pReqToComplete, pHcd); if (cancel) { SignalSet(&pFunc->CleanupReqSig); } } /*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @function: Indicate to the SDIO bus driver (core) of an event in the host controller driver. @function name: SDIO_HandleHcdEvent @prototype: SDIO_STATUS SDIO_HandleHcdEvent(PSDHCD pHcd, HCD_EVENT Event) @category: HD_Reference @input: pHcd - the host controller structure that was registered HCD_EVENT - event code @output: none @return: SDIO_STATUS @notes: The host controller driver can indicate asynchronous events by calling this function with an appropriate event code. Refer to the HDK help manual for more information on the event types @example: Example of indicating a card insertion event: SDIO_HandleHcdEvent(&Hcd, EVENT_HCD_ATTACH); +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ SDIO_STATUS _SDIO_HandleHcdEvent(PSDHCD pHcd, HCD_EVENT Event) { PSDREQUEST pReq; PSDREQUEST pReqToComplete = NULL; PSDREQUEST pNextReq = NULL; SDIO_STATUS status; CT_DECLARE_IRQ_SYNC_CONTEXT(); DBG_PRINT(SDIODBG_HCD_EVENTS, ("SDIO Bus Driver: _SDIO_HandleHcdEvent, event type 0x%X, HCD:0x%X\n", Event, (UINT)pHcd)); if (Event == EVENT_HCD_TRANSFER_DONE) { pReq = GET_CURRENT_REQUEST(pHcd); if (NULL == pReq) { DBG_ASSERT(FALSE); return SDIO_STATUS_ERROR; } status = _AcquireHcdLock(pHcd); if (SDIO_SUCCESS(status)) { /* null out the current request */ SET_CURRENT_REQUEST(pHcd, NULL); status = _ReleaseHcdLock(pHcd); } else { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: SDIO_HandleHcdEvent Failed to acquire HCD lock \n")); return SDIO_STATUS_ERROR; } /* note: the queue is still marked busy to prevent other threads/tasks from starting * new requests while we are handling completion , some completed requests are * marked as barrier requests which must be handled atomically */ status = pReq->Status; DBG_PRINT(SDIODBG_REQUESTS, ("+SDIO Bus Driver: Handling Transfer Done (CMD:%d, Status:%d) from HCD:0x%08X \n", pReq->Command, status, (INT)pHcd)); /* check SPI mode conversion */ if (IS_HCD_BUS_MODE_SPI(pHcd) && SDIO_SUCCESS(status)) { if (!(pReq->Flags & SDREQ_FLAGS_RESP_SKIP_SPI_FILT) && !(pReq->Flags & SDREQ_FLAGS_PSEUDO) && (GET_SDREQ_RESP_TYPE(pReq->Flags) != SDREQ_FLAGS_NO_RESP)) { ConvertSPI_Response(pReq, NULL); } } DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Completing Request:0x%08X \n",(INT)pReq)); if (!SDIO_SUCCESS(status) && (status != SDIO_STATUS_CANCELED) && !(pReq->Flags & SDREQ_FLAGS_CANCELED) && (pReq->RetryCount > 0)) { /* retry the request if it failed, was NOT cancelled and the retry count * is greater than zero */ pReq->RetryCount--; pReqToComplete = NULL; /* clear SPI converted flag */ pReq->Flags &= ~SDREQ_FLAGS_RESP_SPI_CONVERTED; pNextReq = pReq; } else { /* complete the request */ if (pReq->Flags & SDREQ_FLAGS_BARRIER) { /* a barrier request must be completed before the next bus request is * started */ CompleteRequestCheckCancel(pHcd, pReq); if (!ForceAllRequestsAsync()) { if (CHECK_API_VERSION_COMPAT(pHcd,2,6)) { /* the request was completed, decrement recursion count */ status = _AcquireHcdLock(pHcd); if (!SDIO_SUCCESS(status)) { return status; } pHcd->Recursion--; DBG_ASSERT(pHcd->Recursion >= 0); status = _ReleaseHcdLock(pHcd); } else { /* reset bit */ AtomicTest_Clear(&pHcd->HcdFlags, HCD_REQUEST_CALL_BIT); } } pReqToComplete = NULL; } else { /* complete this after the next request has * been started */ pReqToComplete = pReq; } } /* acquire the hcd lock to look at the queues */ status = _AcquireHcdLock(pHcd); if (SDIO_SUCCESS(status)) { if (pReqToComplete != NULL) { /* queue the request that was completed */ QueueRequest(&pHcd->CompletedRequestQueue, pReqToComplete); } if (NULL == pNextReq) { /* check the queue for the next request */ DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Checking queue.. \n")); /* check to see if the HCD was already working on one. This occurs if * the current request being completed was a barrier request and the * barrier completion routine submitted a new request to the head of the * queue */ if (GET_CURRENT_REQUEST(pHcd) == NULL) { pNextReq = DequeueRequest(&pHcd->RequestQueue); if (NULL == pNextReq) { /* nothing in the queue, mark it not busy */ MarkQueueNotBusy(&pHcd->RequestQueue); DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Queue idle \n")); } else { DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Next request in queue: 0x%X \n", (INT)pNextReq)); } } else { DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Busy Queue from barrier request \n")); } } if (pNextReq != NULL) { /* a new request will be submitted to the HCD below, * check recursion while we have the lock */ if (CHECK_API_VERSION_COMPAT(pHcd,2,6)) { CHECK_HCD_RECURSE(pHcd,pNextReq); } } status = _ReleaseHcdLock(pHcd); } else { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: SDIO_HandleHcdEvent Failed to acquire HCD lock \n")); return SDIO_STATUS_ERROR; } /* check for the next request to issue */ if (pNextReq != NULL) { DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Starting Next Request: 0x%X \n", (INT)pNextReq)); SET_CURRENT_REQUEST(pHcd,pNextReq); status = CallHcdRequest(pHcd); /* check and see if the HCD completed the request in the callback */ if (status != SDIO_STATUS_PENDING) { /* recurse and process the request */ _SDIO_HandleHcdEvent(pHcd, EVENT_HCD_TRANSFER_DONE); } } /* now empty the completed request queue * - this guarantees in-order completion even during recursion */ status = _AcquireHcdLock(pHcd); if (SDIO_SUCCESS(status)) { while (1) { pReqToComplete = DequeueRequest(&pHcd->CompletedRequestQueue); status = _ReleaseHcdLock(pHcd); if (pReqToComplete != NULL) { CompleteRequestCheckCancel(pHcd, pReqToComplete); if (!CHECK_API_VERSION_COMPAT(pHcd,2,6)) { if (!ForceAllRequestsAsync()) { /* reset bit */ AtomicTest_Clear(&pHcd->HcdFlags, HCD_REQUEST_CALL_BIT); } } /* re-acquire lock */ status = _AcquireHcdLock(pHcd); if (!SDIO_SUCCESS(status)) { return SDIO_STATUS_ERROR; } if (CHECK_API_VERSION_COMPAT(pHcd,2,6)) { if (!ForceAllRequestsAsync()) { /* while we have the lock, decrement recursion count each time * we complete a request */ pHcd->Recursion--; DBG_ASSERT(pHcd->Recursion >= 0); } } } else { /* we're done */ break; } } } else { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: SDIO_HandleHcdEvent Failed to acquire HCD lock \n")); return SDIO_STATUS_ERROR; } DBG_PRINT(SDIODBG_REQUESTS, ("-SDIO Bus Driver: Transfer Done Handled \n")); return SDIO_STATUS_SUCCESS; } switch(Event) { case EVENT_HCD_ATTACH: case EVENT_HCD_DETACH: /* card detect helper does the actual attach detach */ return PostCardDetectEvent(pBusContext,Event,pHcd); case EVENT_HCD_SDIO_IRQ_PENDING: return DeviceInterrupt(pHcd); default: DBG_PRINT(SDDBG_ERROR, ("-SDIO Bus Driver: SDIO_HandleHcdEvent, invalid event type 0x%X, HCD:0x%X\n", Event, (UINT)pHcd)); return SDIO_STATUS_INVALID_PARAMETER; } } /* card detect helper function */ THREAD_RETURN CardDetectHelperFunction(POSKERNEL_HELPER pHelper) { SDIO_STATUS status; HCD_EVENT_MESSAGE message; INT length; DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - CardDetectHelperFunction starting up: 0x%X \n", (INT)pHelper)); while (1) { /* wait for wake up event */ status = SD_WAIT_FOR_WAKEUP(pHelper); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver - Card Detect Helper Semaphore Pend Error:%d \n", status)); break; } if (SD_IS_HELPER_SHUTTING_DOWN(pHelper)) { /* cleanup message queue on shutdown */ while (1) { length = sizeof(message); /* get a message */ status = SDLIB_GetMessage(pBusContext->pCardDetectMsgQueue, &message, &length); if (!SDIO_SUCCESS(status)) { break; } if (message.pHcd != NULL) { /* decrement HCD reference count */ OS_DecHcdReference(message.pHcd); } } break; } while (1) { length = sizeof(message); /* get a message */ status = SDLIB_GetMessage(pBusContext->pCardDetectMsgQueue, &message, &length); if (!SDIO_SUCCESS(status)) { break; } switch (message.Event) { case EVENT_HCD_ATTACH: DeviceAttach(message.pHcd); break; case EVENT_HCD_DETACH: DeviceDetach(message.pHcd); break; #ifndef CT_CONFIG_NO_CARD_POLLING case EVENT_HCD_CD_POLLING: /* run detector */ RunCardDetect(); break; #endif default: DBG_ASSERT(FALSE); break; } if (message.pHcd != NULL) { /* message was processed, decrement reference count */ OS_DecHcdReference(message.pHcd); } } } DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - Card Detect Helper Exiting.. \n")); return 0; } #ifndef CT_CONFIG_NO_CARD_POLLING /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ RunCardDetect - run card detect on host controller slots that require polling Input: Output: Return: Notes: This function is called from the card detect timer thread ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ void RunCardDetect(void) { BOOL CDPollingRequired = FALSE; PSDLIST pListItem; PSDHCD pHcd; BOOL cardPresent; DBG_PRINT(SDIODBG_CD_TIMER, ("+SDIO Bus Driver: RunCardDetect\n")); /* protect the HCD list */ if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pBusContext->HcdListSem))) { DBG_ASSERT(FALSE); return; /* wait interrupted */ } /* while we are running the detector we are blocking HCD removal*/ SDITERATE_OVER_LIST(&pBusContext->HcdList, pListItem) { pHcd = CONTAINING_STRUCT(pListItem, SDHCD, SDList); /* does the HCD require polling ? */ if (pHcd->Attributes & SDHCD_ATTRIB_SLOT_POLLING) { DBG_PRINT(SDIODBG_CD_TIMER, ("SDIO Bus Driver: Found HCD requiring polling \n")); /* set flag to queue the timer */ CDPollingRequired = TRUE; if (IS_CARD_PRESENT(pHcd)) { /* there is a device in the slot */ cardPresent = TRUE; if (SDIO_SUCCESS(ScanSlotForCard(pHcd,&cardPresent))) { if (!cardPresent) { DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver CD Polling.. Card Removal Detected\n")); DeviceDetach(pHcd); } } } else { cardPresent = FALSE; if (SDIO_SUCCESS(ScanSlotForCard(pHcd,&cardPresent))) { if (cardPresent) { DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver CD Polling.. Card Detected\n")); DeviceAttach(pHcd); } } } } DBG_PRINT(SDIODBG_CD_TIMER, ("SDIO Bus Driver: moving to next hcd:0x%X \n", (INT)pListItem->pNext)); } /* check if we need to queue the timer */ if (CDPollingRequired && !pBusContext->CDTimerQueued) { pBusContext->CDTimerQueued = TRUE; DBG_PRINT(SDIODBG_CD_TIMER, ("SDIO Bus Driver: Queuing Card detect timer \n")); if (!SDIO_SUCCESS( QueueTimer(SDIOBUS_CD_TIMER_ID, pBusContext->CDPollingInterval))) { DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: failed to queue CD timer \n")); pBusContext->CDTimerQueued = FALSE; } } /* release HCD list lock */ SemaphorePost(&pBusContext->HcdListSem); DBG_PRINT(SDIODBG_CD_TIMER, ("-SDIO Bus Driver: RunCardDetect\n")); } #endif #ifndef CT_CONFIG_NO_CARD_POLLING /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ScanSlotForCard - scan slot for a card Input: pHcd - the hcd Output: pCardPresent - card present flag (set/cleared on return) Return: Notes: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ static SDIO_STATUS ScanSlotForCard(PSDHCD pHcd,PBOOL pCardPresent) { SDIO_STATUS status = SDIO_STATUS_SUCCESS; UINT8 temp; DBG_PRINT(SDIODBG_CD_TIMER, ("+SDIO Bus Driver: ScanSlotForCard\n")); do { if (!IS_CARD_PRESENT(pHcd)) { #ifdef DEBUG INT dbgLvl; dbgLvl = DBG_GET_DEBUG_LEVEL(); DBG_SET_DEBUG_LEVEL(SDDBG_WARN); #endif status = CardInitSetup(pHcd); #ifdef DEBUG DBG_SET_DEBUG_LEVEL(dbgLvl); #endif if (!SDIO_SUCCESS(status)) { break; } /* issue go-idle */ if (IS_HCD_BUS_MODE_SPI(pHcd)) { _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_RESP_R1,NULL); } else { _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_NO_RESP,NULL); } /* try SDIO */ status = TestPresence(pHcd,CARD_SDIO,NULL); if (SDIO_SUCCESS(status)) { *pCardPresent = TRUE; break; } #ifndef CT_CONFIG_NO_SDMMC /* issue go-idle */ if (IS_HCD_BUS_MODE_SPI(pHcd)) { _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_RESP_R1,NULL); } else { _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_NO_RESP,NULL); } /* try SD */ status = TestPresence(pHcd,CARD_SD,NULL); if (SDIO_SUCCESS(status)) { *pCardPresent = TRUE; break; } /* issue go-idle */ if (IS_HCD_BUS_MODE_SPI(pHcd)) { _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_RESP_R1,NULL); } else { _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_NO_RESP,NULL); } /* try MMC */ status = TestPresence(pHcd,CARD_MMC,NULL); if (SDIO_SUCCESS(status)) { *pCardPresent = TRUE; break; } #endif } else { if (pHcd->CardProperties.Flags & CARD_SDIO) { #ifdef DUMP_INT_PENDING temp = 0; /* handy debug prints to check interrupt status and print pending register */ status = Cmd52ReadByteCommon(pHcd->pPseudoDev, SDIO_INT_ENABLE_REG, &temp); if (SDIO_SUCCESS(status) && (temp != 0)) { DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: INT Enable Reg: 0x%2.2X\n", temp)); status = Cmd52ReadByteCommon(pHcd->pPseudoDev, SDIO_INT_PENDING_REG, &temp); if (SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: INT Pend Reg: 0x%2.2X\n", temp)); } } #endif /* for SDIO cards, read the revision register */ status = Cmd52ReadByteCommon(pHcd->pPseudoDev, CCCR_SDIO_REVISION_REG, &temp); } else if (pHcd->CardProperties.Flags & (CARD_SD | CARD_MMC)) { #ifndef CT_CONFIG_NO_SDMMC /* for SD/MMC cards, issue SEND_STATUS */ if (IS_HCD_BUS_MODE_SPI(pHcd)) { /* SPI uses the SPI R2 response */ status = _IssueSimpleBusRequest(pHcd, CMD13, 0, SDREQ_FLAGS_RESP_R2, NULL); } else { status = _IssueSimpleBusRequest(pHcd, CMD13, (pHcd->CardProperties.RCA << 16), SDREQ_FLAGS_RESP_R1,NULL); } #endif } else { DBG_ASSERT(FALSE); } if (!SDIO_SUCCESS(status)) { /* card is gone */ *pCardPresent = FALSE; } } } while (FALSE); if (status == SDIO_STATUS_BUS_RESP_TIMEOUT) { status = SDIO_STATUS_SUCCESS; } DBG_PRINT(SDIODBG_CD_TIMER, ("-SDIO Bus Driver: ScanSlotForCard status:%d\n", status)); return status; } #endif /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ DeviceInterrupt - handle device interrupt Input: pHcd - host controller Output: Return: Notes: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ SDIO_STATUS DeviceInterrupt(PSDHCD pHcd) { SDIO_STATUS status = SDIO_STATUS_SUCCESS; SDIO_STATUS status2; PSDREQUEST pReq = NULL; CT_DECLARE_IRQ_SYNC_CONTEXT(); DBG_PRINT(SDIODBG_FUNC_IRQ, ("+SDIO Bus Driver: DeviceInterrupt\n")); if (!IS_CARD_PRESENT(pHcd)) { DBG_PRINT(SDDBG_ERROR, ("-SDIO Bus Driver: Device interrupt asserted on empty slot!\n")); return SDIO_STATUS_ERROR; } do { /* for RAW HCDs or HCDs flagged for single-function IRQ optimization */ if (IS_HCD_RAW(pHcd) || (pHcd->HcdFlags & (1 << HCD_IRQ_NO_PEND_CHECK))) { status = _AcquireHcdLock(pHcd); if (!SDIO_SUCCESS(status)) { return status; } if (pHcd->IrqProcState != SDHCD_IDLE) { DBG_PRINT(SDDBG_ERROR, ("-SDIO Bus Driver: Already processing interrupts! (state = %d) \n", pHcd->IrqProcState)); status = SDIO_STATUS_ERROR; status2 = _ReleaseHcdLock(pHcd); } else { DBG_PRINT(SDIODBG_FUNC_IRQ, ("SDIO Bus Driver : Device Interrupt \n")); /* mark that we are processing */ pHcd->IrqProcState = SDHCD_IRQ_PENDING; status2 = _ReleaseHcdLock(pHcd); /* process Irqs for raw hcds or HCDs with the single function optimization */ /* force processing of function 1 interrupt */ ProcessPendingIrqs(pHcd, (1 << 1)); } DBG_PRINT(SDIODBG_FUNC_IRQ, ("-SDIO Bus Driver: DeviceInterrupt: %d\n", status)); /* done with RAW irqs */ return status; } /* pre-allocate a request to get the pending bits, we have to do this outside the * hcd lock acquisition */ pReq = AllocateRequest(); if (NULL == pReq) { status = SDIO_STATUS_NO_RESOURCES; break; } status = _AcquireHcdLock(pHcd); if (!SDIO_SUCCESS(status)) { break; } if (pHcd->IrqProcState != SDHCD_IDLE) { DBG_PRINT(SDDBG_ERROR, ("-SDIO Bus Driver: Already processing interrupts! (state = %d) \n", pHcd->IrqProcState)); status = SDIO_STATUS_ERROR; } else { /* mark that we are processing */ pHcd->IrqProcState = SDHCD_IRQ_PENDING; /* build argument to read IRQ pending register */ SDIO_SET_CMD52_READ_ARG(pReq->Argument,0,SDIO_INT_PENDING_REG); pReq->Command = CMD52; pReq->Flags = SDREQ_FLAGS_TRANS_ASYNC | SDREQ_FLAGS_RESP_SDIO_R5; pReq->pCompleteContext = (PVOID)pHcd; pReq->pCompletion = GetPendingIrqComplete; pReq->RetryCount = SDBUS_MAX_RETRY; } status2 = _ReleaseHcdLock(pHcd); if (!SDIO_SUCCESS(status2)) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: lock release error: %d\n", status2)); } } while (FALSE); if (SDIO_SUCCESS(status)) { DBG_ASSERT(pReq != NULL); IssueRequestToHCD(pHcd,pReq); status = SDIO_STATUS_PENDING; } else { if (pReq != NULL) { FreeRequest(pReq); } } DBG_PRINT(SDIODBG_FUNC_IRQ, ("-SDIO Bus Driver: DeviceInterrupt: %d\n", status)); return status; } /* SDIO IRQ helper */ THREAD_RETURN SDIOIrqHelperFunction(POSKERNEL_HELPER pHelper) { PSDHCD pHcd; SDIO_STATUS status; PSDLIST pListItem; PSDDEVICE pDevice; UINT8 funcMask; PSDDEVICE pDeviceIRQ[7]; UINT deviceIrqCount = 0; UINT ii; DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - SDIOIrqHelperFunction starting up \n")); pHcd = (PSDHCD)pHelper->pContext; DBG_ASSERT(pHcd != NULL); while (1) { /* wait for wake up event */ status = SD_WAIT_FOR_WAKEUP(pHelper); if (!SDIO_SUCCESS(status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver - SDIOIrqHelperFunction Pend Error:%d \n", status)); break; } if (SD_IS_HELPER_SHUTTING_DOWN(pHelper)) { break; } DBG_PRINT(SDIODBG_FUNC_IRQ, ("SDIO Bus Driver - Pending IRQs:0x%X \n", pHcd->PendingHelperIrqs)); /* take the device list lock as we iterate through the list, this blocks * device removals */ status = SemaphorePendInterruptable(&pBusContext->DeviceListSem); if (!SDIO_SUCCESS(status)) { break; } /* walk through the device list matching HCD and interrupting function */ SDITERATE_OVER_LIST(&pBusContext->DeviceList, pListItem) { pDevice = CONTAINING_STRUCT(pListItem, SDDEVICE, SDList); /* check if device belongs to the HCD */ if (pDevice->pHcd != pHcd){ /* not on this hcd */ continue; } funcMask = 1 << SDDEVICE_GET_SDIO_FUNCNO(pDevice); /* check device function against the pending mask */ if (!(funcMask & pHcd->PendingHelperIrqs)) { /* this one is not scheduled for the helper */ continue; } /* clear bit */ pHcd->PendingHelperIrqs &= ~funcMask; /* check for sync IRQ and call handler */ if (pDevice->pIrqFunction != NULL) { DBG_PRINT(SDIODBG_FUNC_IRQ, ("SDIO Bus Driver: Calling IRQ Handler. Fn:%d\n", SDDEVICE_GET_SDIO_FUNCNO(pDevice))); /* save the device so we can process it without holding any locks */ pDeviceIRQ[deviceIrqCount++] = pDevice; } else { /* this is actually okay if the device is removing, the callback * is NULLed out */ DBG_PRINT(SDIODBG_FUNC_IRQ, ("SDIO Bus Driver: No IRQ handler Fn:%d\n", SDDEVICE_GET_SDIO_FUNCNO(pDevice))); } } /* should have handled all these */ DBG_ASSERT(pHcd->PendingHelperIrqs == 0); pHcd->PendingHelperIrqs = 0; SemaphorePost(&pBusContext->DeviceListSem); for (ii = 0; ii < deviceIrqCount; ii++) { /* now call the function */ SDDEVICE_CALL_IRQ_HANDLER(pDeviceIRQ[ii]); } deviceIrqCount = 0; } DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - SDIOIrqHelperFunction Exiting.. \n")); return 0; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ GetPendingIrqComplete - completion routine for getting pending IRQs Input: pRequest - completed request Output: Return: Notes: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ static void GetPendingIrqComplete(PSDREQUEST pReq) { UINT8 intPendingMsk; PSDHCD pHcd; do { pHcd = (PSDHCD)pReq->pCompleteContext; DBG_ASSERT(pHcd != NULL); if (!SDIO_SUCCESS(pReq->Status)) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get Interrupt pending register Err:%d\n", pReq->Status)); break; } if (SD_R5_GET_RESP_FLAGS(pReq->Response) & SD_R5_ERRORS) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: CMD52 resp error: 0x%X \n", SD_R5_GET_RESP_FLAGS(pReq->Response))); break; } /* extract the pending mask */ intPendingMsk = SD_R5_GET_READ_DATA(pReq->Response) & SDIO_INT_PEND_MASK; /* process them */ ProcessPendingIrqs(pHcd, intPendingMsk); } while (FALSE); FreeRequest(pReq); DBG_PRINT(SDIODBG_FUNC_IRQ, ("-SDIO Bus Driver: GetPendingIrqComplete \n")); } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ProcessPendingIrqs - processing pending Irqs Input: pHcd - host controller Input: IntPendingMsk - pending irq bit mask Output: Return: Notes: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ static void ProcessPendingIrqs(PSDHCD pHcd, UINT8 IntPendingMsk) { PSDLIST pListItem; PSDDEVICE pDevice; UINT8 funcMask; SDIO_STATUS status = SDIO_STATUS_SUCCESS; CT_DECLARE_IRQ_SYNC_CONTEXT(); DBG_PRINT(SDIODBG_FUNC_IRQ, ("+SDIO Bus Driver: ProcessPendingIrqs \n")); do { /* acquire lock to protect configuration and irq enables */ status = _AcquireHcdLock(pHcd); if (!SDIO_SUCCESS(status)) { break; } /* sanity check */ if ((IntPendingMsk & pHcd->IrqsEnabled) != IntPendingMsk) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: IRQs asserting when not enabled : curr:0x%X , card reports: 0x%X\n", pHcd->IrqsEnabled, IntPendingMsk)); /* remove the pending IRQs that are not enabled */ IntPendingMsk &= pHcd->IrqsEnabled; /* fall through */ } if (!IntPendingMsk) { DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: No interrupts on HCD:0x%X \n", (INT)pHcd)); pHcd->IrqProcState = SDHCD_IDLE; if (pHcd->IrqsEnabled) { /* only re-arm if there are IRQs enabled */ _IssueConfig(pHcd,SDCONFIG_SDIO_REARM_INT,NULL,0); } status = _ReleaseHcdLock(pHcd); break; } /* reset helper IRQ bits */ pHcd->PendingHelperIrqs = 0; /* save pending IRQ acks */ pHcd->PendingIrqAcks = IntPendingMsk; status = _ReleaseHcdLock(pHcd); DBG_PRINT(SDIODBG_FUNC_IRQ, ("SDIO Bus Driver: INTs Pending - 0x%2.2X \n", IntPendingMsk)); /* take the device list lock as we iterate through the list, this blocks * device removals */ status = SemaphorePendInterruptable(&pBusContext->DeviceListSem); if (!SDIO_SUCCESS(status)) { break; } /* walk through the device list matching HCD and interrupting function */ SDITERATE_OVER_LIST(&pBusContext->DeviceList, pListItem) { pDevice = CONTAINING_STRUCT(pListItem, SDDEVICE, SDList); /* check if device belongs to the HCD */ if (pDevice->pHcd != pHcd){ /* not on this hcd */ continue; } funcMask = 1 << SDDEVICE_GET_SDIO_FUNCNO(pDevice); /* check device function against the pending mask */ if (!(funcMask & IntPendingMsk)) { /* this one is not interrupting */ continue; } /* check for async IRQ and call handler */ if (pDevice->pIrqAsyncFunction != NULL) { DBG_PRINT(SDIODBG_FUNC_IRQ, ("SDIO Bus Driver: Calling Async IRQ Handler. Fn:%d\n", SDDEVICE_GET_SDIO_FUNCNO(pDevice))); SDDEVICE_CALL_IRQ_ASYNC_HANDLER(pDevice); } else { /* this one needs the helper */ pHcd->PendingHelperIrqs |= funcMask; DBG_PRINT(SDIODBG_FUNC_IRQ, ("SDIO Bus Driver: No Async IRQ, Pending Helper Fn:%d\n", SDDEVICE_GET_SDIO_FUNCNO(pDevice))); } } /* release HCD list lock */ SemaphorePost(&pBusContext->DeviceListSem); /* check for helper IRQs */ if (pHcd->PendingHelperIrqs) { pHcd->IrqProcState = SDHCD_IRQ_HELPER; DBG_PRINT(SDIODBG_FUNC_IRQ, ("SDIO Bus Driver: Waking IRQ Helper \n")); if (!SDIO_SUCCESS(SD_WAKE_OS_HELPER(&pHcd->SDIOIrqHelper))) { DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: failed to wake helper! \n")); } } } while (FALSE); DBG_PRINT(SDIODBG_FUNC_IRQ, ("-SDIO Bus Driver: ProcessPendingIrqs \n")); } SDIO_STATUS TryNoIrqPendingCheck(PSDDEVICE pDevice) { if (pDevice->pHcd->CardProperties.IOFnCount > 1) { /* not supported on multi-function cards */ DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: IRQ Pending Check cannot be bypassed, (Funcs:%d)\n", pDevice->pHcd->CardProperties.IOFnCount)); return SDIO_STATUS_UNSUPPORTED; } DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: pending IRQ check bypassed \n")); /* set flag to optimize this */ AtomicTest_Set(&pDevice->pHcd->HcdFlags, HCD_IRQ_NO_PEND_CHECK); return SDIO_STATUS_SUCCESS; } /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ SDIO_NotifyTimerTriggered - notification handler that a timer expired Input: TimerID - ID of timer that expired Output: Return: Notes: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ void SDIO_NotifyTimerTriggered(INT TimerID) { switch (TimerID) { case SDIOBUS_CD_TIMER_ID: pBusContext->CDTimerQueued = FALSE; /* post an HCD polling event to the helper thread */ PostCardDetectEvent(pBusContext, EVENT_HCD_CD_POLLING, NULL); break; default: DBG_ASSERT(FALSE); } }