From e7ab2e913536616b9f17e3323a7f2b83e808a198 Mon Sep 17 00:00:00 2001 From: mokopatches Date: Wed, 19 Nov 2008 17:03:22 +0000 Subject: atheros_2_0_sdio_stack.patch --- arch/arm/Kconfig | 2 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/sdio/Kconfig | 17 + drivers/sdio/Makefile | 4 + drivers/sdio/stack/Makefile | 1 + drivers/sdio/stack/busdriver/Makefile | 2 + drivers/sdio/stack/busdriver/_busdriver.h | 466 ++++ drivers/sdio/stack/busdriver/sdio_bus.c | 2120 +++++++++++++++ drivers/sdio/stack/busdriver/sdio_bus_events.c | 1040 +++++++ drivers/sdio/stack/busdriver/sdio_bus_misc.c | 3122 ++++++++++++++++++++++ drivers/sdio/stack/busdriver/sdio_bus_os.c | 832 ++++++ drivers/sdio/stack/busdriver/sdio_function.c | 715 +++++ drivers/sdio/stack/lib/Makefile | 2 + drivers/sdio/stack/lib/_sdio_lib.h | 50 + drivers/sdio/stack/lib/sdio_lib_c.c | 908 +++++++ drivers/sdio/stack/lib/sdio_lib_os.c | 251 ++ drivers/sdio/stack/platform/Makefile | 2 + drivers/sdio/stack/platform/sdioplatformdriver.c | 300 +++ include/linux/sdio/_sdio_defs.h | 638 +++++ include/linux/sdio/ctsystem.h | 115 + include/linux/sdio/ctsystem_linux.h | 983 +++++++ include/linux/sdio/mmc_defs.h | 103 + include/linux/sdio/sdio_busdriver.h | 1435 ++++++++++ include/linux/sdio/sdio_hcd_defs.h | 219 ++ include/linux/sdio/sdio_lib.h | 270 ++ include/linux/sdio/sdlist.h | 141 + 27 files changed, 13741 insertions(+) create mode 100644 drivers/sdio/Kconfig create mode 100644 drivers/sdio/Makefile create mode 100644 drivers/sdio/stack/Makefile create mode 100644 drivers/sdio/stack/busdriver/Makefile create mode 100644 drivers/sdio/stack/busdriver/_busdriver.h create mode 100644 drivers/sdio/stack/busdriver/sdio_bus.c create mode 100644 drivers/sdio/stack/busdriver/sdio_bus_events.c create mode 100644 drivers/sdio/stack/busdriver/sdio_bus_misc.c create mode 100644 drivers/sdio/stack/busdriver/sdio_bus_os.c create mode 100644 drivers/sdio/stack/busdriver/sdio_function.c create mode 100644 drivers/sdio/stack/lib/Makefile create mode 100644 drivers/sdio/stack/lib/_sdio_lib.h create mode 100644 drivers/sdio/stack/lib/sdio_lib_c.c create mode 100644 drivers/sdio/stack/lib/sdio_lib_os.c create mode 100644 drivers/sdio/stack/platform/Makefile create mode 100644 drivers/sdio/stack/platform/sdioplatformdriver.c create mode 100644 include/linux/sdio/_sdio_defs.h create mode 100644 include/linux/sdio/ctsystem.h create mode 100644 include/linux/sdio/ctsystem_linux.h create mode 100644 include/linux/sdio/mmc_defs.h create mode 100644 include/linux/sdio/sdio_busdriver.h create mode 100644 include/linux/sdio/sdio_hcd_defs.h create mode 100644 include/linux/sdio/sdio_lib.h create mode 100644 include/linux/sdio/sdlist.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index df026340a50..eb42a1e6fbc 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1269,6 +1269,8 @@ source "drivers/usb/Kconfig" source "drivers/uwb/Kconfig" +source "drivers/sdio/Kconfig" + source "drivers/mmc/Kconfig" source "drivers/memstick/Kconfig" diff --git a/drivers/Kconfig b/drivers/Kconfig index 2f557f570ad..1265b93a5bb 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -82,6 +82,8 @@ source "drivers/usb/Kconfig" source "drivers/uwb/Kconfig" +source "drivers/sdio/Kconfig" + source "drivers/mmc/Kconfig" source "drivers/memstick/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index fceb71a741c..ca7b4c243ac 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-y += idle/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ +obj-$(CONFIG_SDIO) += sdio/ obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ diff --git a/drivers/sdio/Kconfig b/drivers/sdio/Kconfig new file mode 100644 index 00000000000..14bf5e36a35 --- /dev/null +++ b/drivers/sdio/Kconfig @@ -0,0 +1,17 @@ +# +# SDIO driver and host controller support +# + +menu "SDIO support" + +config SDIO + tristate "SDIO support" + default m + ---help--- + good luck. + +source "drivers/sdio/hcd/Kconfig" + +source "drivers/sdio/function/Kconfig" + +endmenu diff --git a/drivers/sdio/Makefile b/drivers/sdio/Makefile new file mode 100644 index 00000000000..f56aa0f7650 --- /dev/null +++ b/drivers/sdio/Makefile @@ -0,0 +1,4 @@ +#Makefile for SDIO stack +obj-$(CONFIG_SDIO) += stack/ +obj-$(CONFIG_SDIO) += hcd/ +obj-$(CONFIG_SDIO) += function/ diff --git a/drivers/sdio/stack/Makefile b/drivers/sdio/stack/Makefile new file mode 100644 index 00000000000..ff0e24d0176 --- /dev/null +++ b/drivers/sdio/stack/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SDIO) += busdriver/ lib/ \ No newline at end of file diff --git a/drivers/sdio/stack/busdriver/Makefile b/drivers/sdio/stack/busdriver/Makefile new file mode 100644 index 00000000000..1130e2d9139 --- /dev/null +++ b/drivers/sdio/stack/busdriver/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SDIO) += sdio_busdriver.o +sdio_busdriver-objs := sdio_bus.o sdio_function.o sdio_bus_misc.o sdio_bus_events.o sdio_bus_os.o diff --git a/drivers/sdio/stack/busdriver/_busdriver.h b/drivers/sdio/stack/busdriver/_busdriver.h new file mode 100644 index 00000000000..a85aed11692 --- /dev/null +++ b/drivers/sdio/stack/busdriver/_busdriver.h @@ -0,0 +1,466 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: _busdriver.h + +@abstract: internal include file for busdriver + +@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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef ___BUSDRIVER_H___ +#define ___BUSDRIVER_H___ +#include + +#define SDIODBG_FUNC_IRQ (SDDBG_TRACE + 1) +#define SDIODBG_REQUESTS (SDDBG_TRACE + 2) +#define SDIODBG_CD_TIMER (SDDBG_TRACE + 3) +#define SDIODBG_HCD_EVENTS (SDDBG_TRACE + 4) + +#define SDIOBUS_CD_TIMER_ID 0 + +#define SDBUS_MAX_RETRY 3 + +/* Notes on list linkages: + * list heads are held in BDCONTEXT + * HcdList - SDHCD + * one per registered host controller + * Next - links of all HCDs + * DeviceList SDDEVICE + * one per inserted device + * Next - links of all devices + * DeviceListNext - links of all devices on a function + * pFunction - ptr to Function supportting this device + * pHcd - ptr to HCD with supporting this device + * FunctionList SDFUNCTION + * one per register function driver + * Next - links of all functions + * DeviceList - list of devices being support by this function + * uses DeviceListNext in SDDEVICE to link + * + * +*/ + +#define SDMMC_DEFAULT_CMD_RETRIES 1 +#define SDMMC_DEFAULT_CARD_READY_RETRIES 200 +#define OCR_READY_CHECK_DELAY_MS 10 +#define SDMMC_POWER_SETTLE_DELAY 400 /* in milliseconds */ +#define SDBUS_DEFAULT_REQ_LIST_SIZE 16 +#define SDBUS_DEFAULT_REQ_SIG_SIZE 8 +#define CARD_DETECT_PAUSE 100 +#define SDBUS_DEFAULT_CD_POLLING_INTERVAL 1000 /* in milliseconds */ +#define MAX_CARD_DETECT_MSGS 16 +#define SDMMC_DEFAULT_BYTES_PER_BLOCK 2048 +#define SDMMC_DEFAULT_BLOCKS_PER_TRANS 512 +#define SDMMC_CMD13_POLLING_MULTIPLIER 1000 /* per block multiplier */ +#define MAX_HCD_REQ_RECURSION 5 +#define MAX_HCD_RECURSION_RUNAWAY 100 + + /* internal signalling item */ +typedef struct _SIGNAL_ITEM{ + SDLIST SDList; /* list link*/ + OS_SIGNAL Signal; /* signal */ +}SIGNAL_ITEM, *PSIGNAL_ITEM; + +typedef struct _HCD_EVENT_MESSAGE { + HCD_EVENT Event; /* the event */ + PSDHCD pHcd; /* hcd that generated the event */ +}HCD_EVENT_MESSAGE, *PHCD_EVENT_MESSAGE; + +/* internal data for bus driver */ +typedef struct _BDCONTEXT { + + /* list of SD requests and signalling semaphores and a semaphore to protect it */ + SDLIST RequestList; + SDLIST SignalList; + OS_CRITICALSECTION RequestListCritSection; + /* list of host controller bus drivers, sempahore to protect it */ + SDLIST HcdList; + OS_SEMAPHORE HcdListSem; + /* list of inserted devices, semaphore to protect it */ + SDLIST DeviceList; + OS_SEMAPHORE DeviceListSem; + /* list of function drivers, semaphore to protect it */ + SDLIST FunctionList; + OS_SEMAPHORE FunctionListSem; + INT RequestListSize; /* default request list */ + INT SignalSemListSize; /* default signalling semaphore size */ + INT CurrentRequestAllocations; /*current count of allocated requests */ + INT CurrentSignalAllocations; /* current count of signal allocations */ + INT MaxRequestAllocations; /* max number of allocated requests to keep around*/ + INT MaxSignalAllocations; /* max number of signal allocations to keep around*/ + INT RequestRetries; /* cmd retries */ + INT CardReadyPollingRetry; /* card ready polling retry count */ + INT PowerSettleDelay; /* power settle delay */ + INT CMD13PollingMultiplier; /* CMD13 (GET STATUS) multiplier */ + SD_BUSCLOCK_RATE DefaultOperClock; /* default operation clock */ + SD_BUSMODE_FLAGS DefaultBusMode; /* default bus mode */ + UINT16 DefaultOperBlockLen; /* default operational block length per block */ + UINT16 DefaultOperBlockCount; /* default operational block count per transaction */ + UINT32 CDPollingInterval; /* card insert/removal polling interval */ + UINT8 InitMask; /* bus driver init mask */ +#define BD_TIMER_INIT 0x01 +#define HELPER_INIT 0x02 +#define RESOURCE_INIT 0x04 + BOOL CDTimerQueued; /* card detect timer queued */ + OSKERNEL_HELPER CardDetectHelper; /* card detect helper */ + PSDMESSAGE_QUEUE pCardDetectMsgQueue; /* card detect message queue */ + ULONG HcdInUseField; /* bit field of in use HCD numbers*/ + UINT32 ConfigFlags; /* bus driver configuration flags */ +#define BD_CONFIG_SDREQ_FORCE_ALL_ASYNC 0x00000001 + INT MaxHcdRecursion; /* max HCD recurion level */ +}BDCONTEXT, *PBDCONTEXT; + +#define BD_DEFAULT_CONFIG_FLAGS 0x00000000 +#define IsQueueBusy(pRequestQueue) (pRequestQueue)->Busy +#define MarkQueueBusy(pRequestQueue) (pRequestQueue)->Busy = TRUE +#define MarkQueueNotBusy(pRequestQueue) (pRequestQueue)->Busy = FALSE + +#define CLEAR_INTERNAL_REQ_FLAGS(pReq) (pReq)->Flags &= ~(UINT)((SDREQ_FLAGS_RESP_SPI_CONVERTED | \ + SDREQ_FLAGS_FORCE_DEFERRED_COMPLETE)) + +/* macros to insert request into the queue */ +#define QueueRequest(pReqQ,pReq) SDListInsertTail(&(pReqQ)->Queue,&(pReq)->SDList) +#define QueueRequestToFront(pReqQ,pReq) SDListInsertHead(&(pReqQ)->Queue,&(pReq)->SDList) + +/* macros to remove an item from the head of the queue */ +static INLINE PSDREQUEST DequeueRequest(PSDREQUESTQUEUE pRequestQueue) { + PSDLIST pItem; + pItem = SDListRemoveItemFromHead(&pRequestQueue->Queue); + if (pItem != NULL) { + return CONTAINING_STRUCT(pItem, SDREQUEST, SDList); + } + return NULL; +}; + +static INLINE SDIO_STATUS InitializeRequestQueue(PSDREQUESTQUEUE pRequestQueue) { + SDLIST_INIT(&pRequestQueue->Queue); + MarkQueueNotBusy(pRequestQueue); + return SDIO_STATUS_SUCCESS; +} + +static INLINE void CleanupRequestQueue(PSDREQUESTQUEUE pRequestQueue) { + +} + +/* for bus driver internal use only */ +SDIO_STATUS _SDIO_BusDriverInitialize(void); +SDIO_STATUS _SDIO_BusGetDefaultSettings(PBDCONTEXT pBdc); +void _SDIO_BusDriverCleanup(void); +SDIO_STATUS RemoveAllFunctions(void); +SDIO_STATUS RemoveHcdFunctions(PSDHCD pHcd); +PSDDEVICE AllocateDevice(PSDHCD pHcd); +BOOL AddDeviceToList(PSDDEVICE pDevice); +SDIO_STATUS DeleteDevices(PSDHCD pHcd); +SDIO_STATUS NotifyDeviceRemove(PSDDEVICE pDevice); +extern PBDCONTEXT pBusContext; +extern const CT_VERSION_CODE g_Version; +SDIO_STATUS _SDIO_RegisterHostController(PSDHCD pHcd); +SDIO_STATUS _SDIO_UnregisterHostController(PSDHCD pHcd); +SDIO_STATUS _SDIO_HandleHcdEvent(PSDHCD pHcd, HCD_EVENT Event); +SDIO_STATUS _SDIO_RegisterFunction(PSDFUNCTION pFunction); +SDIO_STATUS _SDIO_UnregisterFunction(PSDFUNCTION pFunction); +SDIO_STATUS _SDIO_CheckResponse(PSDHCD pHcd, PSDREQUEST pReq, SDHCD_RESPONSE_CHECK_MODE CheckMode); +SDIO_STATUS ProbeForFunction(PSDDEVICE pDevice, PSDHCD pHcd); +SDIO_STATUS SDInitializeCard(PSDHCD pHcd); +SDIO_STATUS SDQuerySDMMCInfo(PSDDEVICE pDevice); +SDIO_STATUS SDQuerySDIOInfo(PSDDEVICE pDevice); +SDIO_STATUS SDEnableFunction(PSDDEVICE pDevice, PSDCONFIG_FUNC_ENABLE_DISABLE_DATA pEnData); +SDIO_STATUS SDAllocFreeSlotCurrent(PSDDEVICE pDevice, BOOL Allocate, PSDCONFIG_FUNC_SLOT_CURRENT_DATA pData); +SDIO_STATUS SDMaskUnmaskFunctionIRQ(PSDDEVICE pDevice, BOOL Mask); +SDIO_STATUS SDFunctionAckInterrupt(PSDDEVICE pDevice); +SDIO_STATUS SDSPIModeEnableDisableCRC(PSDDEVICE pDevice,BOOL Enable); +SDIO_STATUS IssueBusConfig(PSDDEVICE pDev, PSDCONFIG pConfig); +SDIO_STATUS IssueBusRequest(PSDDEVICE pDev, PSDREQUEST pReq); +PSDREQUEST IssueAllocRequest(PSDDEVICE pDev); +void IssueFreeRequest(PSDDEVICE pDev, PSDREQUEST pReq); +PSDREQUEST AllocateRequest(void); +void FreeRequest(PSDREQUEST pReq); +PSIGNAL_ITEM AllocateSignal(void); +void FreeSignal(PSIGNAL_ITEM pSignal); +SDIO_STATUS InitializeTimers(void); +SDIO_STATUS CleanupTimers(void); +SDIO_STATUS QueueTimer(INT TimerID, UINT32 TimeOut); +SDIO_STATUS DeviceAttach(PSDHCD pHcd); +SDIO_STATUS DeviceDetach(PSDHCD pHcd); +SDIO_STATUS DeviceInterrupt(PSDHCD pHcd); +SDIO_STATUS CardInitSetup(PSDHCD pHcd); +void RunCardDetect(void); +void SDIO_NotifyTimerTriggered(INT TimerID); +SDIO_STATUS TestPresence(PSDHCD pHcd, + CARD_INFO_FLAGS TestType, + PSDREQUEST pReq); +#define _IssueSimpleBusRequest(pHcd,Cmd,Arg,Flags,pReqToUse) \ + _IssueBusRequestBd((pHcd),(Cmd),(Arg),(Flags),(pReqToUse),NULL,0) + +SDIO_STATUS Do_OS_IncHcdReference(PSDHCD pHcd); +SDIO_STATUS Do_OS_DecHcdReference(PSDHCD pHcd); +SDIO_STATUS TryNoIrqPendingCheck(PSDDEVICE pDev); + + /* check API version compatibility of an HCD or function driver to a stack major/minor version + if the driver version is greater than the major number, we are compatible + if the driver version is equal, then we check if the minor is greater than or equal + we don't have to check for the less than major, because the bus driver never loads + drivers with different major numbers ... + if the busdriver compiled version major is greater than the major version being checked this + macro will resolved to ALWAYS true thus optimizing the code to not check the HCD since + as a rule we never load an HCD with a lower major number */ +#define CHECK_API_VERSION_COMPAT(p,major,minor) \ + ((CT_SDIO_STACK_VERSION_MAJOR(CT_SDIO_STACK_VERSION_CODE) > (major)) || \ + (GET_SDIO_STACK_VERSION_MINOR((p)) >= (minor))) + +static INLINE SDIO_STATUS OS_IncHcdReference(PSDHCD pHcd) { + /* this API was added in version 2.3 which requires access to a field in the HCD structure */ + if (CHECK_API_VERSION_COMPAT(pHcd,2,3)) { + /* we can safely call the OS-dependent function */ + return Do_OS_IncHcdReference(pHcd); + } + return SDIO_STATUS_SUCCESS; +} + +static INLINE SDIO_STATUS OS_DecHcdReference(PSDHCD pHcd) { + /* this API was added in version 2.3 which requires access to a field in the HCD structure */ + if (CHECK_API_VERSION_COMPAT(pHcd,2,3)) { + /* we can safely call the OS-dependent function */ + return Do_OS_DecHcdReference(pHcd); + } + return SDIO_STATUS_SUCCESS; +} + +SDIO_STATUS _IssueBusRequestBd(PSDHCD pHcd, + UINT8 Cmd, + UINT32 Argument, + SDREQUEST_FLAGS Flags, + PSDREQUEST pReqToUse, + PVOID pData, + INT Length); + +SDIO_STATUS IssueRequestToHCD(PSDHCD pHcd,PSDREQUEST pReq); + +#define CALL_HCD_CONFIG(pHcd,pCfg) (pHcd)->pConfigure((pHcd),(pCfg)) + /* macro to force all requests to be asynchronous in the HCD */ +static INLINE BOOL ForceAllRequestsAsync(void) { + return (pBusContext->ConfigFlags & BD_CONFIG_SDREQ_FORCE_ALL_ASYNC); +} + +static INLINE SDIO_STATUS CallHcdRequest(PSDHCD pHcd) { + + if (pHcd->pCurrentRequest->Flags & SDREQ_FLAGS_PSEUDO) { + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: PSEUDO Request 0x%X \n", + (INT)pHcd->pCurrentRequest)); + /* return successful completion so that processing can finish */ + return SDIO_STATUS_SUCCESS; + } + + if (ForceAllRequestsAsync()) { + /* all requests must be completed(indicated) in a separate context */ + pHcd->pCurrentRequest->Flags |= SDREQ_FLAGS_FORCE_DEFERRED_COMPLETE; + } else { + /* otherwise perform a test on flags in the HCD */ + if (!CHECK_API_VERSION_COMPAT(pHcd,2,6) && + AtomicTest_Set(&pHcd->HcdFlags, HCD_REQUEST_CALL_BIT)) { + + /* bit was already set, this is a recursive call, + * we need to tell the HCD to complete the + * request in a separate context */ + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Recursive CallHcdRequest \n")); + pHcd->pCurrentRequest->Flags |= SDREQ_FLAGS_FORCE_DEFERRED_COMPLETE; + } + } + #if DEBUG + { + SDIO_STATUS status; + BOOL forceDeferred; + forceDeferred = pHcd->pCurrentRequest->Flags & SDREQ_FLAGS_FORCE_DEFERRED_COMPLETE; + status = pHcd->pRequest(pHcd); + if (forceDeferred) { + /* status better be pending... */ + DBG_ASSERT(status == SDIO_STATUS_PENDING); + } + return status; + } + #else + return pHcd->pRequest(pHcd); + #endif + +} + +/* note the caller of this macro must take the HCD lock to protect the count */ +#define CHECK_HCD_RECURSE(pHcd,pReq) \ +{ \ + (pHcd)->Recursion++; \ + DBG_ASSERT((pHcd)->Recursion < MAX_HCD_RECURSION_RUNAWAY); \ + if ((pHcd)->Recursion > pBusContext->MaxHcdRecursion) { \ + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Recursive Request Count Exceeded (%d) \n",(pHcd)->Recursion)); \ + (pReq)->Flags |= SDREQ_FLAGS_FORCE_DEFERRED_COMPLETE; \ + } \ +} + +/* InternalFlags bit number settings */ +#define SDBD_INIT 1 +#define SDBD_PENDING 15 +#define SDBD_ALLOC_IRQ_SAFE 2 + +#define SDBD_ALLOC_IRQ_SAFE_MASK (1 << SDBD_ALLOC_IRQ_SAFE) + +static void INLINE DoRequestCompletion(PSDREQUEST pReq, PSDHCD pHcd) { + CLEAR_INTERNAL_REQ_FLAGS(pReq); + if (pReq->pCompletion != NULL) { + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Calling completion on request:0x%X, CMD:%d \n", + (INT)pReq, pReq->Command)); + /* call completion routine, mark request reusable */ + AtomicTest_Clear(&pReq->InternalFlags, SDBD_PENDING); + pReq->pCompletion(pReq); + } else { + /* mark request reusable */ + AtomicTest_Clear(&pReq->InternalFlags, SDBD_PENDING); + } +} + +THREAD_RETURN CardDetectHelperFunction(POSKERNEL_HELPER pHelper); +THREAD_RETURN SDIOIrqHelperFunction(POSKERNEL_HELPER pHelper); + +void ConvertSPI_Response(PSDREQUEST pReq, UINT8 *pRespBuffer); + +static INLINE SDIO_STATUS PostCardDetectEvent(PBDCONTEXT pSDB, HCD_EVENT Event, PSDHCD pHcd) { + HCD_EVENT_MESSAGE message; + SDIO_STATUS status; + message.Event = Event; + message.pHcd = pHcd; + + if (pHcd != NULL) { + /* increment HCD reference count to process this HCD message */ + status = OS_IncHcdReference(pHcd); + if (!SDIO_SUCCESS(status)) { + return status; + } + } + /* post card detect message */ + status = SDLIB_PostMessage(pSDB->pCardDetectMsgQueue, &message, sizeof(message)); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: PostCardDetectEvent error status %d\n",status)); + if (pHcd != NULL) { + /* decrement count */ + OS_DecHcdReference(pHcd); + } + return status; + } + /* wake card detect helper */ + DBG_PRINT(SDIODBG_HCD_EVENTS, ("SDIO Bus Driver: PostCardDetectEvent waking\n")); + return SD_WAKE_OS_HELPER(&pSDB->CardDetectHelper); +}; + +/* initialize device fields */ +static INLINE void InitDeviceData(PSDHCD pHcd, PSDDEVICE pDevice) { + ZERO_POBJECT(pDevice); + SDLIST_INIT(&pDevice->SDList); + SDLIST_INIT(&pDevice->FuncListLink); + pDevice->pRequest = IssueBusRequest; + pDevice->pConfigure = IssueBusConfig; + pDevice->AllocRequest = IssueAllocRequest; + pDevice->FreeRequest = IssueFreeRequest; + /* set card flags in the ID */ + pDevice->pId[0].CardFlags = pHcd->CardProperties.Flags; + pDevice->pFunction = NULL; + pDevice->pHcd = pHcd; + SET_SDIO_STACK_VERSION(pDevice); +} + +/* de-initialize device fields */ +static INLINE void DeinitDeviceData(PSDDEVICE pDevice) { +} + +/* reset hcd state */ +static INLINE void ResetHcdState(PSDHCD pHcd) { + ZERO_POBJECT(&pHcd->CardProperties); + pHcd->PendingHelperIrqs = 0; + pHcd->PendingIrqAcks = 0; + pHcd->IrqsEnabled = 0; + pHcd->pCurrentRequest = NULL; + pHcd->IrqProcState = SDHCD_IDLE; + /* mark this device as special */ + pHcd->pPseudoDev->pId[0].CardFlags = CARD_PSEUDO; + pHcd->SlotCurrentAllocated = 0; +} + +static INLINE SDIO_STATUS _IssueConfig(PSDHCD pHcd, + SDCONFIG_COMMAND Command, + PVOID pData, + INT Length){ + SDCONFIG configHdr; + SET_SDCONFIG_CMD_INFO(&configHdr,Command,pData,Length); + return CALL_HCD_CONFIG(pHcd,&configHdr); +} + +/* prototypes */ +#define _AcquireHcdLock(pHcd)CriticalSectionAcquireSyncIrq(&(pHcd)->HcdCritSection) +#define _ReleaseHcdLock(pHcd)CriticalSectionReleaseSyncIrq(&(pHcd)->HcdCritSection) + +#define AcquireHcdLock(pDev) CriticalSectionAcquireSyncIrq(&(pDev)->pHcd->HcdCritSection) +#define ReleaseHcdLock(pDev) CriticalSectionReleaseSyncIrq(&(pDev)->pHcd->HcdCritSection) + +SDIO_STATUS OS_AddDevice(PSDDEVICE pDevice, PSDFUNCTION pFunction); +void OS_RemoveDevice(PSDDEVICE pDevice); +SDIO_STATUS OS_InitializeDevice(PSDDEVICE pDevice, PSDFUNCTION pFunction); +SDIO_STATUS SetOperationalBusMode(PSDDEVICE pDevice, + PSDCONFIG_BUS_MODE_DATA pBusMode); +void FreeDevice(PSDDEVICE pDevice); +BOOL IsPotentialIdMatch(PSD_PNP_INFO pIdsDev, PSD_PNP_INFO pIdsFuncList); + + +#define CHECK_FUNCTION_DRIVER_VERSION(pF) \ + (GET_SDIO_STACK_VERSION_MAJOR((pF)) == CT_SDIO_STACK_VERSION_MAJOR(g_Version)) +#define CHECK_HCD_DRIVER_VERSION(pH) \ + (GET_SDIO_STACK_VERSION_MAJOR((pH)) == CT_SDIO_STACK_VERSION_MAJOR(g_Version)) + +/* CLARIFICATION on SDREQ_FLAGS_PSEUDO and SDREQ_FLAGS_BARRIER flags : + * + * A request marked as PSEUDO is synchronized with bus requests and is not a true request + * that is issued to an HCD. + * + * A request marked with a BARRIER flag requires that the completion routine be called + * before the next bus request starts. This is required for HCD requests that can change + * bus or clock modes. Changing the clock or bus mode while a bus request is pending + * can cause problems. + * + * + * + * */ +#define SD_PSEUDO_REQ_FLAGS \ + (SDREQ_FLAGS_PSEUDO | SDREQ_FLAGS_BARRIER | SDREQ_FLAGS_TRANS_ASYNC) + +#endif /*___BUSDRIVER_H___*/ diff --git a/drivers/sdio/stack/busdriver/sdio_bus.c b/drivers/sdio/stack/busdriver/sdio_bus.c new file mode 100644 index 00000000000..ffc1e9f958d --- /dev/null +++ b/drivers/sdio/stack/busdriver/sdio_bus.c @@ -0,0 +1,2120 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_bus.c + +@abstract: OS independent bus driver support +@category abstract: HD_Reference Host Controller Driver Interfaces. +@category abstract: PD_Reference + Peripheral Driver Interfaces. + +#notes: this file supports the HCD's and generic functions + +@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 of 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 +#include +#include "_busdriver.h" + +/* list of host controller bus drivers */ +PBDCONTEXT pBusContext = NULL; +static void CleanUpBusResources(void); +static SDIO_STATUS AllocateBusResources(void); +static PSIGNAL_ITEM BuildSignal(void); +static void DestroySignal(PSIGNAL_ITEM pSignal); + +const CT_VERSION_CODE g_Version = CT_SDIO_STACK_VERSION_CODE; +/* + * _SDIO_BusDriverInitialize - call once on driver loading + * +*/ +SDIO_STATUS _SDIO_BusDriverInitialize(void) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Version: %d.%d\n", + CT_SDIO_STACK_VERSION_MAJOR(g_Version),CT_SDIO_STACK_VERSION_MINOR(g_Version))); + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: enter _SDIO_BusDriverInitialize\n")); + + do { + /* allocate our internal data initialize it */ + pBusContext = KernelAlloc(sizeof(BDCONTEXT)); + if (pBusContext == NULL) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_BusDriverInitialize can't allocate memory.\n")); + status = SDIO_STATUS_NO_RESOURCES; + break; + } + memset(pBusContext,0,sizeof(BDCONTEXT)); + SDLIST_INIT(&pBusContext->RequestList); + SDLIST_INIT(&pBusContext->HcdList); + SDLIST_INIT(&pBusContext->DeviceList); + SDLIST_INIT(&pBusContext->FunctionList); + SDLIST_INIT(&pBusContext->SignalList); + + /* setup defaults */ + pBusContext->RequestRetries = SDMMC_DEFAULT_CMD_RETRIES; + pBusContext->CardReadyPollingRetry = SDMMC_DEFAULT_CARD_READY_RETRIES; + pBusContext->PowerSettleDelay = SDMMC_POWER_SETTLE_DELAY; + pBusContext->DefaultOperClock = MMC_HS_MAX_BUS_CLOCK; + pBusContext->DefaultBusMode = SDCONFIG_BUS_WIDTH_4_BIT; + pBusContext->RequestListSize = SDBUS_DEFAULT_REQ_LIST_SIZE; + pBusContext->SignalSemListSize = SDBUS_DEFAULT_REQ_SIG_SIZE; + pBusContext->CDPollingInterval = SDBUS_DEFAULT_CD_POLLING_INTERVAL; + pBusContext->DefaultOperBlockLen = SDMMC_DEFAULT_BYTES_PER_BLOCK; + pBusContext->DefaultOperBlockCount = SDMMC_DEFAULT_BLOCKS_PER_TRANS; + pBusContext->ConfigFlags = BD_DEFAULT_CONFIG_FLAGS; + pBusContext->CMD13PollingMultiplier = SDMMC_CMD13_POLLING_MULTIPLIER; + pBusContext->MaxHcdRecursion = MAX_HCD_REQ_RECURSION; + + /* get overrides for the defaults */ + status = _SDIO_BusGetDefaultSettings(pBusContext); + if (!SDIO_SUCCESS(status)) { + break; + } + + pBusContext->MaxRequestAllocations = pBusContext->RequestListSize << 1; + pBusContext->MaxSignalAllocations = pBusContext->SignalSemListSize << 1; + + status = CriticalSectionInit(&pBusContext->RequestListCritSection); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_BusDriverInitialize can't CriticalSectionInit.\n")); + break; + } + status = SemaphoreInitialize(&pBusContext->HcdListSem, 1); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_BusDriverInitialize can't SemaphoreInitialize HcdListSem.\n")); + break; + } + status = SemaphoreInitialize(&pBusContext->DeviceListSem, 1); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_BusDriverInitialize can't SemaphoreInitialize DeviceListSem.\n")); + break; + } + status = SemaphoreInitialize(&pBusContext->FunctionListSem, 1); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_BusDriverInitialize can't SemaphoreInitialize FunctionListSem.\n")); + break; + } + status = AllocateBusResources(); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_BusDriverInitialize can't AllocateBusResources.\n")); + break; + } + + pBusContext->InitMask |= RESOURCE_INIT; + + pBusContext->pCardDetectMsgQueue = SDLIB_CreateMessageQueue(MAX_CARD_DETECT_MSGS, + sizeof(HCD_EVENT_MESSAGE)); + + if (NULL == pBusContext->pCardDetectMsgQueue) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_BusDriverInitialize can't CreateMessageQueue.\n")); + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + status = SDLIB_OSCreateHelper(&pBusContext->CardDetectHelper, + CardDetectHelperFunction, + NULL); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_BusDriverInitialize can't OSCreateHelper.\n")); + break; + } + + pBusContext->InitMask |= HELPER_INIT; + + status = InitializeTimers(); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_BusDriverInitialize can't InitializeTimers.\n")); + break; + } + pBusContext->InitMask |= BD_TIMER_INIT; + } while(FALSE); + + if (!SDIO_SUCCESS(status)) { + _SDIO_BusDriverCleanup(); + } + + return status; +} + + +/* + * _SDIO_BusDriverBusDriverCleanup - call once on driver unloading + * +*/ +void _SDIO_BusDriverCleanup(void) { + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: _SDIO_BusDriverCleanup\n")); + + if (pBusContext->InitMask & BD_TIMER_INIT) { + CleanupTimers(); + } + + if (pBusContext->InitMask & HELPER_INIT) { + SDLIB_OSDeleteHelper(&pBusContext->CardDetectHelper); + } + + if (pBusContext->pCardDetectMsgQueue != NULL) { + SDLIB_DeleteMessageQueue(pBusContext->pCardDetectMsgQueue); + pBusContext->pCardDetectMsgQueue = NULL; + } + /* remove functions */ + RemoveAllFunctions(); + /* cleanup all devices */ + DeleteDevices(NULL); + CleanUpBusResources(); + CriticalSectionDelete(&pBusContext->RequestListCritSection); + SemaphoreDelete(&pBusContext->HcdListSem); + SemaphoreDelete(&pBusContext->DeviceListSem); + SemaphoreDelete(&pBusContext->FunctionListSem); + KernelFree(pBusContext); + pBusContext = NULL; + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: _SDIO_BusDriverCleanup\n")); +} + + +/* cleanup hcd */ +static void CleanupHcd(PSDHCD pHcd) +{ + SDLIB_OSDeleteHelper(&pHcd->SDIOIrqHelper); + CleanupRequestQueue(&pHcd->CompletedRequestQueue); + CleanupRequestQueue(&pHcd->RequestQueue); + CriticalSectionDelete(&pHcd->HcdCritSection); + SemaphoreDelete(&pHcd->ConfigureOpsSem); + pHcd->pCurrentRequest = NULL; + if (pHcd->pPseudoDev != NULL) { + FreeDevice(pHcd->pPseudoDev); + pHcd->pPseudoDev = NULL; + } +} + +/* set up the hcd */ +static SDIO_STATUS SetupHcd(PSDHCD pHcd) +{ + SDIO_STATUS status; + + ZERO_POBJECT(&pHcd->SDIOIrqHelper); + ZERO_POBJECT(&pHcd->ConfigureOpsSem); + ZERO_POBJECT(&pHcd->HcdCritSection); + ZERO_POBJECT(&pHcd->RequestQueue); + ZERO_POBJECT(&pHcd->CompletedRequestQueue); + pHcd->pPseudoDev = NULL; + pHcd->Recursion = 0; + + do { + + pHcd->pPseudoDev = AllocateDevice(pHcd); + + if (NULL == pHcd->pPseudoDev) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + ResetHcdState(pHcd); + + status = SemaphoreInitialize(&pHcd->ConfigureOpsSem,1); + if (!SDIO_SUCCESS(status)) { + break; + } + status = CriticalSectionInit(&pHcd->HcdCritSection); + if (!SDIO_SUCCESS(status)) { + break; + } + status = InitializeRequestQueue(&pHcd->RequestQueue); + if (!SDIO_SUCCESS(status)) { + break; + } + status = InitializeRequestQueue(&pHcd->CompletedRequestQueue); + if (!SDIO_SUCCESS(status)) { + break; + } + /* create SDIO Irq helper */ + status = SDLIB_OSCreateHelper(&pHcd->SDIOIrqHelper, + SDIOIrqHelperFunction, + (PVOID)pHcd); + } while(FALSE); + + if (!SDIO_SUCCESS(status)) { + /* undo what we did */ + CleanupHcd(pHcd); + } + return status; +} + + +/* + * _SDIO_RegisterHostController - register a host controller bus driver + * +*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Register a host controller driver with the bus driver. + + @function name: SDIO_RegisterHostController + @prototype: SDIO_STATUS SDIO_RegisterHostController (PSDHCD pHcd) + @category: HD_Reference + + @input: pHcd - the host controller definition structure. + + @output: none + + @return: SDIO_STATUS - SDIO_STATUS_SUCCESS when successful. + + @notes: Each host controller driver must register with the bus driver when loaded. + The driver registers an SDHCD structure initialized with hardware properties + and callback functions for bus requests and configuration. On multi-slot + hardware ,each slot should be registered with a separate SDHCD structure. + The bus driver views each slot as a seperate host controller object. + The driver should be prepared to receive configuration requests before + this call returns. The host controller driver must unregister itself when + shutting down. + + @example: Registering a host controller driver: + static SDHCD Hcd = { + .pName = "sdio_custom_hcd", + .Version = CT_SDIO_STACK_VERSION_CODE, // set stack version code + .SlotNumber = 0, // bus driver internal use + .Attributes = SDHCD_ATTRIB_BUS_1BIT | SDHCD_ATTRIB_BUS_4BIT | SDHCD_ATTRIB_MULTI_BLK_IRQ + SDHCD_ATTRIB_AUTO_CMD12 , + .MaxBytesPerBlock = 2048 // each data block can be up to 2048 bytes + .MaxBlocksPerTrans = 1024, // each data transaction can consist of 1024 blocks + .MaxSlotCurrent = 500, // max FET switch current rating + .SlotVoltageCaps = SLOT_POWER_3_3V, // only 3.3V operation + .SlotVoltagePreferred = SLOT_POWER_3_3V, + .MaxClockRate = 24000000, // 24 Mhz max operation + .pContext = &HcdContext, // set our driver context + .pRequest = HcdRequest, // set SDIO bus request callback + .pConfigure = HcdConfig, // set SDIO bus configuration callback + }; + if (!SDIO_SUCCESS((status = SDIO_RegisterHostController(&Hcd)))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO HCD - failed to register with host, status =%d\n", + status)); + } + + @see also: SDIO_UnregisterHostController + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDIO_RegisterHostController(PSDHCD pHcd) { + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: _SDIO_RegisterHostController - %s\n",pHcd->pName)); + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: Host Controller Stack Version: %d.%d \n", + GET_SDIO_STACK_VERSION_MAJOR(pHcd),GET_SDIO_STACK_VERSION_MINOR(pHcd))); + + if (!CHECK_HCD_DRIVER_VERSION(pHcd)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: HCD Major Version Mismatch (hcd = %d, bus driver = %d)\n", + GET_SDIO_STACK_VERSION_MAJOR(pHcd), CT_SDIO_STACK_VERSION_MAJOR(g_Version))); + return SDIO_STATUS_INVALID_PARAMETER; + } + /* setup hcd */ + status = SetupHcd(pHcd); + if (!SDIO_SUCCESS(status)) { + return status; + } + + do { + INT slotNumber; + + /* protect the HCD list */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->HcdListSem)))) { + break; /* wait interrupted */ + } + /* find a unique number for this HCD, must be done under semaphore protection */ + slotNumber = FirstClearBit(&pBusContext->HcdInUseField); + if (slotNumber < 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_RegisterHostController, error, slotNumber exceeded\n")); + /* fake something */ + slotNumber = 31; + } + SetBit(&pBusContext->HcdInUseField, slotNumber); + pHcd->SlotNumber = slotNumber; + /* add HCD to the end of the internal list */ + SDListAdd(&pBusContext->HcdList , &pHcd->SDList); + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->HcdListSem)))) { + break; /* wait interrupted */ + } + if (pHcd->Attributes & SDHCD_ATTRIB_SLOT_POLLING) { + /* post message to card detect helper to do polling */ + PostCardDetectEvent(pBusContext, EVENT_HCD_CD_POLLING, NULL); + } + } while (FALSE); + + if (!SDIO_SUCCESS(status)) { + CleanupHcd(pHcd); + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_RegisterHostController, error 0x%X.\n", status)); + } + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: _SDIO_RegisterHostController\n")); + return status; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Unregister a host controller driver with the bus driver. + + @function name: SDIO_UnregisterHostController + @prototype: SDIO_STATUS SDIO_UnregisterHostController (PSDHCD pHcd) + @category: HD_Reference + + @input: pHcd - the host controller definition structure that was registered. + + @output: none + + @return: SDIO_STATUS - SDIO_STATUS_SUCCESS when successful. + + @notes: Each host controller driver must unregister with the bus driver when + unloading. The driver is responsible for halting any outstanding I/O + operations. The bus driver will automatically unload function drivers + that may be attached assigned to cards inserted into slots. + + @example: Unregistering a host controller driver: + if (!SDIO_SUCCESS((status = SDIO_UnregisterHostController(&Hcd)))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO HCD - failed to unregister with host, status =%d\n", + status)); + } + + @see also: SDIO_RegisterHostController + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDIO_UnregisterHostController(PSDHCD pHcd) { + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: _SDIO_UnregisterHostController\n")); + + /* remove functions associated with the HCD */ + RemoveHcdFunctions(pHcd); + /* remove any devices associated with the HCD */ + DeleteDevices(pHcd); + /* wait for the message queue to be empty, so we don't have any delayed requests going + to this device */ + while(!SDLIB_IsQueueEmpty(pBusContext->pCardDetectMsgQueue)) { + /* wait for the messages to be handled */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: _SDIO_UnregisterHostController, waiting on messages\n")); + OSSleep(250); + } + + /* protect the HCD list */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->HcdListSem)))) { + goto cleanup; /* wait interrupted */ + } + ClearBit(&pBusContext->HcdInUseField, pHcd->SlotNumber); + /* delete HCD from list */ + SDListRemove(&pHcd->SDList); + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->HcdListSem)))) { + goto cleanup; /* wait interrupted */ + } + /* cleanup anything we allocated */ + CleanupHcd(pHcd); + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: _SDIO_UnregisterHostController\n")); + return status; +cleanup: + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_UnregisterHostController, error 0x%X.\n", status)); + return status; +} + +/* documentation headers only for Request and Configure */ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: The bus driver calls the request callback to start an SDIO bus transaction. + @function name: Request + @prototype: SDIO_STATUS (*pRequest) (struct _SDHCD *pHcd) + @category: HD_Reference + + @input: pHcd - the host controller structure that was registered + + @output: none + + @return: SDIO_STATUS + + @notes: + The bus driver maintains an internal queue of SDREQUEST structures submited by function + drivers. The driver should use request macros to obtain a pointer to the current SDREQUEST + at the head of the queue. The driver can access the fields of the current request in order + to program hardware appropriately. Once the request completes, the driver should update + the current request information (final status, response bytes and/or data) and call + SDIO_HandleHcdEvent() with the event type of EVENT_HCD_TRANSFER_DONE. + The bus driver will remove the current request from the head of the queue and start the next + request. + + @example: Example of a typical Request callback: + SDIO_STATUS HcdRequest(PSDHCD pHcd) + { + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDHCD_DRIVER_CONTEXT pHct = (PSDHCD_DRIVER_CONTEXT)pHcd->pContext; + UINT32 temp = 0; + PSDREQUEST pReq; + // get the current request + pReq = GET_CURRENT_REQUEST(pHcd); + DBG_ASSERT(pReq != NULL); + // get controller settings based on response type + switch (GET_SDREQ_RESP_TYPE(pReq->Flags)) { + case SDREQ_FLAGS_NO_RESP: + break; + case SDREQ_FLAGS_RESP_R1: + case SDREQ_FLAGS_RESP_MMC_R4: + case SDREQ_FLAGS_RESP_MMC_R5: + case SDREQ_FLAGS_RESP_R6: + case SDREQ_FLAGS_RESP_SDIO_R5: + temp |= CMDDAT_RES_R1_R4_R5; + break; + case SDREQ_FLAGS_RESP_R1B: + temp |= (CMDDAT_RES_R1_R4_R5 | CMDAT_RES_BUSY); + break; + case SDREQ_FLAGS_RESP_R2: + temp |= CMDDAT_RES_R2; + break; + case SDREQ_FLAGS_RESP_R3: + case SDREQ_FLAGS_RESP_SDIO_R4: + temp |= CMDDAT_RES_R3; + break; + } + // check for data + if (pReq->Flags & SDREQ_FLAGS_DATA_TRANS){ + temp |= CMDDAT_DATA_EN; + // set data remaining count + pReq->DataRemaining = pReq->BlockLen * pReq->BlockCount; + DBG_PRINT(TRACE_DATA, ("SDIO %s Data Transfer, Blocks:%d, BlockLen:%d, Total:%d \n", + IS_SDREQ_WRITE_DATA(pReq->Flags) ? "TX":"RX", + pReq->BlockCount, pReq->BlockLen, pReq->DataRemaining)); + if (IS_SDREQ_WRITE_DATA(pReq->Flags)) { + // write operation + } + } + // .... program hardware, interrupt handler will complete request + return SDIO_STATUS_PENDING; + } + + @see also: SDIO_HandleHcdEvent + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: The bus driver calls the configure callback to set various options + and modes in the host controller hardware. + + @function name: Configure + @prototype: SDIO_STATUS (*pConfigure) (struct _SDHCD *pHcd, PSDCONFIG pConfig) + @category: HD_Reference + + @input: pHcd - the host controller structure that was registered + @input: pConfig - configuration request structure + + @output: none + + @return: SDIO_STATUS + + @notes: + The host controller driver recieves configuration requests for options + such as slot voltage, bus width, clock rates and interrupt detection. + The bus driver guarantees that only one configuration option request + can be issued at a time. + + @example: Example of a typical configure callback: + SDIO_STATUS HcdConfig(PSDHCD pHcd, PSDCONFIG pConfig) + { + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDHCD_DRIVER_CONTEXT pHct = (PSDHCD_DRIVER_CONTEXT)pHcd->pContext; + UINT16 command; + // get command + command = GET_SDCONFIG_CMD(pConfig); + // decode command + switch (command){ + case SDCONFIG_GET_WP: + if (GetGpioPinLevel(pHct,SDIO_CARD_WP_GPIO) == WP_POLARITY) { + *((SDCONFIG_WP_VALUE *)pConfig->pData) = 1; + } else { + *((SDCONFIG_WP_VALUE *)pConfig->pData) = 0; + } + break; + case SDCONFIG_SEND_INIT_CLOCKS: + ClockStartStop(pHct,CLOCK_ON); + // sleep a little, should be at least 80 clocks at our lowest clock setting + status = OSSleep(100); + ClockStartStop(pHct,CLOCK_OFF); + break; + case SDCONFIG_SDIO_INT_CTRL: + if (GET_SDCONFIG_CMD_DATA(PSDCONFIG_SDIO_INT_CTRL_DATA,pConfig)->SlotIRQEnable) { + // request to enable IRQ detection + } else { + // request to disable IRQ detectioon + } + break; + case SDCONFIG_SDIO_REARM_INT: + // request to re-arm the card IRQ detection logic + break; + case SDCONFIG_BUS_MODE_CTRL: + // request to set bus mode + { + // get bus mode data structure + PSDCONFIG_BUS_MODE_DATA pBusMode = + GET_SDCONFIG_CMD_DATA(PSDCONFIG_SDIO_INT_CTRL_DATA,pConfig); + // set bus mode based on settings in bus mode structure + // bus mode : pBusMode->BusModeFlags + // clock rate : pBusMode->ClockRate + } + break; + case SDCONFIG_POWER_CTRL: + // request to set power/voltage + { + PSDCONFIG_POWER_CTRL_DATA pPowerSetting = + GET_SDCONFIG_CMD_DATA(PSDCONFIG_POWER_CTRL_DATA,pConfig); + if (pPowerSetting->SlotPowerEnable) { + // turn on slot power + // + } else { + // turn off slot power + } + DBG_PRINT(PXA_TRACE_CONFIG, ("SDIO PXA255 PwrControl: En:%d, VCC:0x%X \n", + pPowerSetting->SlotPowerEnable, + pPowerSetting->SlotPowerVoltageMask)); + } + break; + default: + // unsupported + status = SDIO_STATUS_INVALID_PARAMETER; + } + return status; + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + + +/* + * Allocate a Device instance + */ +PSDDEVICE AllocateDevice(PSDHCD pHcd) +{ + PSDDEVICE pDevice; + + pDevice = KernelAlloc(sizeof(SDDEVICE)); + if (pDevice != NULL) { + InitDeviceData(pHcd,pDevice); + } + return pDevice; +} + + +/* + * Free a Device instance + */ +void FreeDevice(PSDDEVICE pDevice) +{ + DeinitDeviceData(pDevice); + KernelFree(pDevice); +} +/* + * add this device to the list + */ +BOOL AddDeviceToList(PSDDEVICE pDevice) +{ + BOOL success = FALSE; + + do { + /* protect the driver list */ + if (!SDIO_SUCCESS(SemaphorePendInterruptable(&pBusContext->DeviceListSem))) { + break; /* wait interrupted */ + } + + /* add new device to the internal list */ + SDListAdd(&pBusContext->DeviceList , &pDevice->SDList); + + if (!SDIO_SUCCESS(SemaphorePost(&pBusContext->DeviceListSem))) { + break; + } + + success = TRUE; + } while (FALSE); + + return success; +} + +/* + * Delete device associated with the HCD + * if pHCD is NULL this function cleans up all devices, the caller + * better have cleaned up functions first! + */ +SDIO_STATUS DeleteDevices(PSDHCD pHcd) +{ + SDIO_STATUS status; + PSDDEVICE pDevice; + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: DeleteDevices hcd:0x%X \n", (INT)pHcd)); + /* protect the device list */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->DeviceListSem)))) { + goto cleanup; /* wait interrupted */ + } + SDITERATE_OVER_LIST_ALLOW_REMOVE(&pBusContext->DeviceList,pDevice,SDDEVICE,SDList) { + /* only remove devices for the hcd or if we are cleaning up all */ + if ((NULL == pHcd) || (pDevice->pHcd == pHcd)) { + SDListRemove(&pDevice->SDList); + DeinitDeviceData(pDevice); + FreeDevice(pDevice); + } + }SDITERATE_END; + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->DeviceListSem)))) { + goto cleanup; /* wait interrupted */ + } + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: DeleteDevices \n")); + return status; +cleanup: + DBG_PRINT(SDDBG_ERROR, ("-SDIO Bus Driver: DeleteDevice, error exit 0x%X\n", status)); + return status; +} + + +static SDIO_STATUS AllocateBusResources(void) +{ + INT ii; + PSDREQUEST pReq; + PSIGNAL_ITEM pSignal; + + DBG_PRINT(SDDBG_TRACE, + ("+SDIO Bus Driver: AllocateBusResources (R:%d,S:%d) (CR:%d,MR:%d)(CS:%d,MS:%d) \n", + pBusContext->RequestListSize, + pBusContext->SignalSemListSize, + pBusContext->CurrentRequestAllocations,pBusContext->MaxRequestAllocations, + pBusContext->CurrentSignalAllocations,pBusContext->MaxSignalAllocations)); + + /* allocate some initial requests */ + for (ii = 0; ii < pBusContext->RequestListSize; ii++) { + pReq = AllocateRequest(); + if (pReq == NULL) { + break; + } + /* free requests adds the request to the list */ + FreeRequest(pReq); + } + + for (ii = 0; ii < pBusContext->SignalSemListSize; ii++) { + pSignal = AllocateSignal(); + if (pSignal == NULL) { + break; + } + /* freeing it adds it to the list */ + FreeSignal(pSignal); + } + + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: AllocateBusResources\n")); + return SDIO_STATUS_SUCCESS; +} + + +/* cleanup bus resources */ +static void CleanUpBusResources(void) +{ + PSDLIST pItem; + PSDREQUEST pReq; + PSIGNAL_ITEM pSignal; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: CleanUpBusResources (CR:%d,MR:%d)(CS:%d,MS:%d) \n", + pBusContext->CurrentRequestAllocations,pBusContext->MaxRequestAllocations, + pBusContext->CurrentSignalAllocations,pBusContext->MaxSignalAllocations)); + + while(1) { + pItem = SDListRemoveItemFromHead(&pBusContext->RequestList); + if (NULL == pItem) { + break; + } + /* free the request */ + pReq = CONTAINING_STRUCT(pItem, SDREQUEST, SDList); + if (pReq->InternalFlags & SDBD_ALLOC_IRQ_SAFE_MASK) { + KernelFreeIrqSafe(pReq); + } else { + KernelFree(pReq); + } + pBusContext->CurrentRequestAllocations--; + } + + if (pBusContext->CurrentRequestAllocations != 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Request allocations are not ZERO! (CR:%d)\n", + pBusContext->CurrentRequestAllocations)); + } + + while(1) { + pItem = SDListRemoveItemFromHead(&pBusContext->SignalList); + if (NULL == pItem) { + break; + } + pSignal = CONTAINING_STRUCT(pItem, SIGNAL_ITEM, SDList); + DestroySignal(pSignal); + pBusContext->CurrentSignalAllocations--; + } + + if (pBusContext->CurrentSignalAllocations != 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Signal allocations are not ZERO! (CR:%d)\n", + pBusContext->CurrentRequestAllocations)); + } + + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: CleanUpBusResources\n")); +} + + +/* free a request to the lookaside list */ +void FreeRequest(PSDREQUEST pReq) +{ + SDIO_STATUS status; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + status = CriticalSectionAcquireSyncIrq(&pBusContext->RequestListCritSection); + /* protect request list */ + if (!SDIO_SUCCESS(status)) { + return; + } + + if ((pBusContext->CurrentRequestAllocations <= pBusContext->MaxRequestAllocations) || + !(pReq->InternalFlags & SDBD_ALLOC_IRQ_SAFE_MASK)) { + /* add it to the list */ + SDListAdd(&pBusContext->RequestList, &pReq->SDList); + /* we will hold onto this one */ + pReq = NULL; + } else { + /* decrement count */ + pBusContext->CurrentRequestAllocations--; + } + + status = CriticalSectionReleaseSyncIrq(&pBusContext->RequestListCritSection); + + if (pReq != NULL) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Free Request allocation (CR:%d,MR:%d)\n", + pBusContext->CurrentRequestAllocations,pBusContext->MaxRequestAllocations)); + if (pReq->InternalFlags & SDBD_ALLOC_IRQ_SAFE_MASK) { + KernelFreeIrqSafe(pReq); + } else { + /* we should never free the ones that were normally allocated */ + DBG_ASSERT(FALSE); + } + } +} + +/* allocate a request from the lookaside list */ +PSDREQUEST AllocateRequest(void) +{ + PSDLIST pItem; + SDIO_STATUS status; + PSDREQUEST pReq = NULL; + ATOMIC_FLAGS internalflags; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + + status = CriticalSectionAcquireSyncIrq(&pBusContext->RequestListCritSection); + + if (!SDIO_SUCCESS(status)) { + return NULL; + } + + if (pBusContext->InitMask & RESOURCE_INIT) { + /* check the list, we are now running... */ + pItem = SDListRemoveItemFromHead(&pBusContext->RequestList); + } else { + /* we are loading the list with requests at initialization */ + pItem = NULL; + } + status = CriticalSectionReleaseSyncIrq(&pBusContext->RequestListCritSection); + + if (pItem != NULL) { + pReq = CONTAINING_STRUCT(pItem, SDREQUEST, SDList); + } else { + if (pBusContext->InitMask & RESOURCE_INIT) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Request List empty..allocating new one (irq-safe) (CR:%d,MR:%d)\n", + pBusContext->CurrentRequestAllocations,pBusContext->MaxRequestAllocations)); + /* the resource list was already allocated, we must be running now. + * at run-time, we allocate using the safe IRQ */ + pReq = (PSDREQUEST)KernelAllocIrqSafe(sizeof(SDREQUEST)); + /* mark that this one was created using IRQ safe allocation */ + internalflags = SDBD_ALLOC_IRQ_SAFE_MASK; + } else { + /* use the normal allocation since we are called at initialization */ + pReq = (PSDREQUEST)KernelAlloc(sizeof(SDREQUEST)); + internalflags = 0; + } + + if (pReq != NULL) { + pReq->InternalFlags = internalflags; + /* keep track of allocations */ + status = CriticalSectionAcquireSyncIrq(&pBusContext->RequestListCritSection); + pBusContext->CurrentRequestAllocations++; + status = CriticalSectionReleaseSyncIrq(&pBusContext->RequestListCritSection); + } + } + + + if (pReq != NULL) { + /* preserve internal flags */ + internalflags = pReq->InternalFlags; + ZERO_POBJECT(pReq); + pReq->InternalFlags = internalflags; + } + + return pReq; +} + +void DestroySignal(PSIGNAL_ITEM pSignal) +{ + SignalDelete(&pSignal->Signal); + KernelFree(pSignal); +} + +PSIGNAL_ITEM BuildSignal(void) +{ + PSIGNAL_ITEM pSignal; + + pSignal = (PSIGNAL_ITEM)KernelAlloc(sizeof(SIGNAL_ITEM)); + if (pSignal != NULL) { + /* initialize signal */ + if (!SDIO_SUCCESS(SignalInitialize(&pSignal->Signal))) { + KernelFree(pSignal); + pSignal = NULL; + } + } + return pSignal; +} +/* free a signal*/ +void FreeSignal(PSIGNAL_ITEM pSignal) +{ + SDIO_STATUS status; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + status = CriticalSectionAcquireSyncIrq(&pBusContext->RequestListCritSection); + + if (!SDIO_SUCCESS(status)) { + return; + } + + if (pBusContext->CurrentSignalAllocations <= pBusContext->MaxSignalAllocations) { + /* add it to the list */ + SDListAdd(&pBusContext->SignalList, &pSignal->SDList); + /* flag that we are holding onto it */ + pSignal = NULL; + } else { + /* decrement count */ + pBusContext->CurrentSignalAllocations--; + } + + status = CriticalSectionReleaseSyncIrq(&pBusContext->RequestListCritSection); + + if (pSignal != NULL) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Free signal allocation (CS:%d,MS:%d)\n", + pBusContext->CurrentSignalAllocations,pBusContext->MaxSignalAllocations)); + DestroySignal(pSignal); + } +} + +/* allocate a signal from the list */ +PSIGNAL_ITEM AllocateSignal(void) +{ + PSDLIST pItem; + PSIGNAL_ITEM pSignal; + SDIO_STATUS status; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + status = CriticalSectionAcquireSyncIrq(&pBusContext->RequestListCritSection); + + if (!SDIO_SUCCESS(status)) { + return NULL; + } + + if (pBusContext->InitMask & RESOURCE_INIT) { + /* check the list */ + pItem = SDListRemoveItemFromHead(&pBusContext->SignalList); + } else { + /* we are loading the list */ + pItem = NULL; + } + + status = CriticalSectionReleaseSyncIrq(&pBusContext->RequestListCritSection); + if (pItem != NULL) { + /* return the one from the list */ + pSignal = CONTAINING_STRUCT(pItem, SIGNAL_ITEM, SDList); + } else { + if (pBusContext->InitMask & RESOURCE_INIT) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Signal List empty..allocating new one (CS:%d,MS:%d)\n", + pBusContext->CurrentSignalAllocations,pBusContext->MaxSignalAllocations)); + } + /* just allocate one */ + pSignal = BuildSignal(); + status = CriticalSectionAcquireSyncIrq(&pBusContext->RequestListCritSection); + if (pSignal != NULL) { + pBusContext->CurrentSignalAllocations++; + } + status = CriticalSectionReleaseSyncIrq(&pBusContext->RequestListCritSection); + } + + + return pSignal; +} + +/* + * Issus Bus Request (exposed to function drivers) +*/ +PSDREQUEST IssueAllocRequest(PSDDEVICE pDev) +{ + return AllocateRequest(); +} + +/* + * Free Request (exposed to function drivers) +*/ +void IssueFreeRequest(PSDDEVICE pDev, PSDREQUEST pReq) +{ + FreeRequest(pReq); +} + +/* + * Issus Bus Request (exposed to function drivers) +*/ +SDIO_STATUS IssueBusRequest(PSDDEVICE pDev, PSDREQUEST pReq) +{ + pReq->pFunction = pDev->pFunction; + return IssueRequestToHCD(pDev->pHcd,pReq); +} + + + /* completion routine for HCD configs, this is synchronized with normal bus requests */ +static void HcdConfigComplete(PSDREQUEST pReq) +{ + + pReq->Status = CALL_HCD_CONFIG((PSDHCD)pReq->pDataBuffer, (PSDCONFIG)pReq->pCompleteContext); + + SignalSet(&((PSIGNAL_ITEM)pReq->pHcdContext)->Signal); +} + +SDIO_STATUS SendSyncedHcdBusConfig(PSDDEVICE pDevice, PSDCONFIG pConfig) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDREQUEST pReq = NULL; + PSIGNAL_ITEM pSignal = NULL; + + do { + + pSignal = AllocateSignal(); + if (NULL == pSignal) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + pReq = AllocateRequest(); + if (NULL == pReq) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + /* issue pseudo request to sync this with bus requests */ + pReq->pCompletion = HcdConfigComplete; + pReq->pCompleteContext = pConfig; + /* re-use hcd context to store the signal since this request + * never actually goes to an HCD */ + pReq->pHcdContext = pSignal; + pReq->pDataBuffer = pDevice->pHcd; + /* flag this as barrier in case it may change the bus mode of the HCD */ + pReq->Flags = SDREQ_FLAGS_PSEUDO | SDREQ_FLAGS_BARRIER | SDREQ_FLAGS_TRANS_ASYNC; + pReq->Status = SDIO_STATUS_SUCCESS; + + /* issue request */ + status = IssueRequestToHCD(pDevice->pHcd,pReq); + + } while (FALSE); + + if (SDIO_SUCCESS(status)) { + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Config Request Sync-Op waiting....\n")); + status = SignalWait(&pSignal->Signal); + + if (SDIO_SUCCESS(status)) { + /* return the result of the configuration request */ + status = pReq->Status; + } + } + + /* cleanup */ + if (pReq != NULL) { + FreeRequest(pReq); + } + + if (pSignal != NULL) { + FreeSignal(pSignal); + } + + return status; +} + +/* + * Issus bus Configuration (exposed to function drivers) +*/ +SDIO_STATUS IssueBusConfig(PSDDEVICE pDev, PSDCONFIG pConfig) +{ + SDIO_STATUS status; + INT cmdLength; + UINT8 debugLevel = SDDBG_ERROR; + + cmdLength = GET_SDCONFIG_CMD_LEN(pConfig); + status = SDIO_STATUS_INVALID_PARAMETER; + + do { + /* check buffers and length */ + if (IS_SDCONFIG_CMD_GET(pConfig) || IS_SDCONFIG_CMD_PUT(pConfig)) { + if ((GET_SDCONFIG_CMD_DATA(PVOID,pConfig) == NULL) || (0 == cmdLength)) { + break; + } + } + + switch (GET_SDCONFIG_CMD(pConfig)) { + case SDCONFIG_FUNC_ACK_IRQ: + status = SDFunctionAckInterrupt(pDev); + break; + case SDCONFIG_FUNC_ENABLE_DISABLE: + if (cmdLength < sizeof(SDCONFIG_FUNC_ENABLE_DISABLE_DATA)) { + break; + } + status = SDEnableFunction(pDev, + GET_SDCONFIG_CMD_DATA(PSDCONFIG_FUNC_ENABLE_DISABLE_DATA,pConfig)); + break; + case SDCONFIG_FUNC_UNMASK_IRQ: + status = SDMaskUnmaskFunctionIRQ(pDev,FALSE); + break; + case SDCONFIG_FUNC_MASK_IRQ: + status = SDMaskUnmaskFunctionIRQ(pDev,TRUE); + break; + case SDCONFIG_FUNC_SPI_MODE_DISABLE_CRC: + status = SDSPIModeEnableDisableCRC(pDev,FALSE); + break; + case SDCONFIG_FUNC_SPI_MODE_ENABLE_CRC: + status = SDSPIModeEnableDisableCRC(pDev,TRUE); + break; + case SDCONFIG_FUNC_ALLOC_SLOT_CURRENT: + status = SDAllocFreeSlotCurrent(pDev, + TRUE, + GET_SDCONFIG_CMD_DATA(PSDCONFIG_FUNC_SLOT_CURRENT_DATA,pConfig)); + break; + case SDCONFIG_FUNC_FREE_SLOT_CURRENT: + status = SDAllocFreeSlotCurrent(pDev, FALSE, NULL); + break; + case SDCONFIG_FUNC_CHANGE_BUS_MODE: + + status = SetOperationalBusMode(pDev, + GET_SDCONFIG_CMD_DATA(PSDCONFIG_BUS_MODE_DATA, + pConfig)); + break; + case SDCONFIG_FUNC_NO_IRQ_PEND_CHECK: + status = TryNoIrqPendingCheck(pDev); + break; + default: + + if (GET_SDCONFIG_CMD(pConfig) & SDCONFIG_FLAGS_HC_CONFIG) { + /* synchronize config requests with busrequests */ + status = SendSyncedHcdBusConfig(pDev,pConfig); + } else { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: IssueBusConfig - unknown command:0x%X \n", + GET_SDCONFIG_CMD(pConfig))); + status = SDIO_STATUS_INVALID_PARAMETER; + } + break; + } + } while(FALSE); + + if (!SDIO_SUCCESS(status)) { + + if(status == SDIO_STATUS_FUNC_ENABLE_TIMEOUT ){ /* reduce debug level to avoid timeout error messages */ + debugLevel = SDDBG_TRACE; + } + + + DBG_PRINT(debugLevel, + ("SDIO Bus Driver: IssueBusConfig - Error in command:0x%X, Buffer:0x%X, Length:%d Err:%d\n", + GET_SDCONFIG_CMD(pConfig), + GET_SDCONFIG_CMD_DATA(INT,pConfig), + cmdLength, status)); + } + return status; +} + +/* start a request */ +static INLINE SDIO_STATUS StartHcdRequest(PSDHCD pHcd, PSDREQUEST pReq) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + if ((pReq->pFunction != NULL) && (pReq->pFunction->Flags & SDFUNCTION_FLAG_REMOVING)) { + /* this device or function is going away, fail any new requests */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: StartHcdRequest, fail request 0x%X, device is removing\n", (UINT)pReq)); + pReq->Status = SDIO_STATUS_CANCELED; + return SDIO_STATUS_SDREQ_QUEUE_FAILED; + } + + status = _AcquireHcdLock(pHcd); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to acquire HCD request lock: Err:%d\n", status)); + pReq->Status = SDIO_STATUS_SDREQ_QUEUE_FAILED; + return SDIO_STATUS_SDREQ_QUEUE_FAILED; + } + + if (pReq->Flags & SDREQ_FLAGS_QUEUE_HEAD) { + /* caller wants this request queued to the head */ + + /* a completion routine for a barrier request is called + * while the queue is busy. A barrier request can + * insert a new request at the head of the queue */ + DBG_ASSERT(IsQueueBusy(&pHcd->RequestQueue)); + QueueRequestToFront(&pHcd->RequestQueue,pReq); + } else { + /* insert in queue at tail */ + QueueRequest(&pHcd->RequestQueue,pReq); + + /* is queue busy ? */ + if (IsQueueBusy(&pHcd->RequestQueue)) { + /* release lock */ + status = _ReleaseHcdLock(pHcd); + /* controller is busy already, no need to call the hcd */ + return SDIO_STATUS_PENDING; + } + /* mark it as busy */ + MarkQueueBusy(&pHcd->RequestQueue); + } + + /* remove item from head and set current request */ + SET_CURRENT_REQUEST(pHcd, DequeueRequest(&pHcd->RequestQueue)); + if (CHECK_API_VERSION_COMPAT(pHcd,2,6)) { + CHECK_HCD_RECURSE(pHcd, pHcd->pCurrentRequest); + } + /* release lock */ + status = _ReleaseHcdLock(pHcd); + /* controller was not busy, call into HCD to process current request */ + status = CallHcdRequest(pHcd); + return status; +} + + +/* used by CMD12,CMD13 to save the original completion routine */ +#define GET_BD_RSV_REQUEST_COMPLETION(pR) (PSDEQUEST_COMPLETION)(pR)->pBdRsv1 +#define SET_BD_RSV_REQUEST_COMPLETION(pR,c) (pR)->pBdRsv1 = (PVOID)(c) + +/* used by CMD12 processing to save/restore the original data transfer status */ +#define GET_BD_RSV_ORIG_STATUS(pR) (SDIO_STATUS)(pR)->pBdRsv2 +#define SET_BD_RSV_ORIG_STATUS(pR,s) (pR)->pBdRsv2 = (PVOID)(s) + +/* used by CMD13 processing to get/set polling count */ +#define GET_BD_RSV_STATUS_POLL_COUNT(pR) (INT)(pR)->pBdRsv2 +#define SET_BD_RSV_STATUS_POLL_COUNT(pR,s) (pR)->pBdRsv2 = (PVOID)(s) + +/* used by CMD55 processing to save the second part of the request */ +#define GET_BD_RSV_ORIG_REQ(pR) (PSDREQUEST)(pR)->pBdRsv1 +#define SET_BD_RSV_ORIG_REQ(pR,r) (pR)->pBdRsv1 = (PVOID)(r) + +/* used by all to save HCD */ +#define GET_BD_RSV_HCD(pR) (PSDHCD)(pR)->pBdRsv3 +#define SET_BD_RSV_HCD(pR,h) (pR)->pBdRsv3 = (PVOID)(h) + +static void CMD13CompletionBarrier(PSDREQUEST pReq); + +static INLINE void SetupCMD13(PSDHCD pHcd, PSDREQUEST pReq) +{ + pReq->Command = CMD13; + /* sequence must be atomic, queue it to the head and flag as a barrier */ + pReq->Flags = SDREQ_FLAGS_QUEUE_HEAD | SDREQ_FLAGS_BARRIER | SDREQ_FLAGS_TRANS_ASYNC; + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + pReq->Argument = 0; + pReq->Flags |= SDREQ_FLAGS_RESP_R2; + } else { + pReq->Flags |= SDREQ_FLAGS_RESP_R1; + pReq->Argument |= pHcd->CardProperties.RCA << 16; + } + /* insert completion */ + pReq->pCompletion = CMD13CompletionBarrier; +} + +/* CMD13 (GET STATUS) completion */ +static void CMD13CompletionBarrier(PSDREQUEST pReq) +{ + PSDEQUEST_COMPLETION pOrigCompletion = GET_BD_RSV_REQUEST_COMPLETION(pReq); + PSDHCD pHcd = GET_BD_RSV_HCD(pReq); + INT pollingCount = GET_BD_RSV_STATUS_POLL_COUNT(pReq); + BOOL doCompletion = TRUE; + UINT32 cardStatus; + + DBG_ASSERT(pOrigCompletion != NULL); + DBG_ASSERT(pHcd != NULL); + DBG_PRINT(SDIODBG_REQUESTS, ("+SDIO Bus Driver: CMD13CompletionBarrier (cnt:%d) \n",pollingCount)); + + do { + if (!SDIO_SUCCESS(pReq->Status)) { + break; + } + + cardStatus = SD_R1_GET_CARD_STATUS(pReq->Response); + + if (cardStatus & SD_CS_TRANSFER_ERRORS) { + DBG_PRINT(SDIODBG_REQUESTS,("SDIO Bus Driver: Card transfer errors : 0x%X \n",cardStatus)); + pReq->Status = SDIO_STATUS_PROGRAM_STATUS_ERROR; + break; + } + + if (SD_CS_GET_STATE(cardStatus) != SD_CS_STATE_PRG) { + DBG_PRINT(SDIODBG_REQUESTS,("SDIO Bus Driver: Card programming done \n")); + break; + } + + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Card still programming.. \n")); + pollingCount--; + + if (pollingCount < 0) { + pReq->Status = SDIO_STATUS_PROGRAM_TIMEOUT; + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: card programming timeout!\n")); + break; + } + + doCompletion = FALSE; + /* keep trying */ + SET_BD_RSV_STATUS_POLL_COUNT(pReq, pollingCount); + SetupCMD13(pHcd,pReq); + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: re-issuing CMD13 \n")); + /* re-issue */ + IssueRequestToHCD(pHcd, pReq); + + } while (FALSE); + + + if (doCompletion) { + /* restore original completion routine */ + pReq->pCompletion = pOrigCompletion; + /* call original completion routine */ + pOrigCompletion(pReq); + } + + DBG_PRINT(SDIODBG_REQUESTS, ("-SDIO Bus Driver: CMD13CompletionBarrier \n")); +} + +/* command 13 (GET STATUS) preparation */ +static void PrepCMD13Barrier(PSDREQUEST pReq) +{ + SDIO_STATUS status = pReq->Status; + PSDHCD pHcd = GET_BD_RSV_HCD(pReq); + INT pollingCount; + PSDEQUEST_COMPLETION pOrigCompletion = GET_BD_RSV_REQUEST_COMPLETION(pReq); + + DBG_ASSERT(pHcd != NULL); + DBG_ASSERT(pOrigCompletion != NULL); + + DBG_PRINT(SDIODBG_REQUESTS, ("+SDIO Bus Driver: PrepCMD13Barrier \n")); + + if (SDIO_SUCCESS(status)) { + /* re-use the request for CMD13 */ + SetupCMD13(pHcd,pReq); + /* set polling count to a multiple of the Block count, if the BlockCount was + * zeroed by the HCD, then set it to 1X multiplier */ + pollingCount = max(pBusContext->CMD13PollingMultiplier, + pBusContext->CMD13PollingMultiplier * (INT)pReq->BlockCount); + /* initialize count */ + SET_BD_RSV_STATUS_POLL_COUNT(pReq, pollingCount); + /* re-issue it, we can call IssueRequest here since we are re-using the request */ + IssueRequestToHCD(pHcd, pReq); + } else { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Request Failure (%d) , CMD13 bypassed.\n",status)); + /* call the original completion routine */ + pOrigCompletion(pReq); + } + + DBG_PRINT(SDIODBG_REQUESTS, ("-SDIO Bus Driver: PrepCMD13Barrier (%d) \n",status)); +} + +/* CMD12 completion */ +static void CMD12Completion(PSDREQUEST pReq) +{ + PSDEQUEST_COMPLETION pOrigCompletion = GET_BD_RSV_REQUEST_COMPLETION(pReq); + + DBG_ASSERT(pOrigCompletion != NULL); + + DBG_PRINT(SDIODBG_REQUESTS, ("+SDIO Bus Driver: CMD12Completion \n")); + + /* restore original completion routine */ + pReq->pCompletion = pOrigCompletion; + + if (SDIO_SUCCESS(pReq->Status)) { + /* if CMD12 succeeds, we want to return the result of the original + * request */ + pReq->Status = GET_BD_RSV_ORIG_STATUS(pReq); + DBG_PRINT(SDIODBG_REQUESTS, + ("SDIO Bus Driver: PrepCMD12Completion original status %d \n",pReq->Status)); + } + /* call original completion routine */ + pOrigCompletion(pReq); + + DBG_PRINT(SDIODBG_REQUESTS, ("-SDIO Bus Driver: CMD12Completion \n")); +} + +/* CMD12 preparation */ +static void PrepCMD12Barrier(PSDREQUEST pReq) +{ + + SDIO_STATUS status = pReq->Status; + PSDHCD pHcd = GET_BD_RSV_HCD(pReq); + PSDEQUEST_COMPLETION pOrigCompletion = GET_BD_RSV_REQUEST_COMPLETION(pReq); + + DBG_ASSERT(pHcd != NULL); + DBG_ASSERT(pOrigCompletion != NULL); + + DBG_PRINT(SDIODBG_REQUESTS, ("+SDIO Bus Driver: PrepCMD12Barrier \n")); + + if (SDIO_SUCCESS(status) || /* only issue CMD12 on success or specific bus errors */ + (SDIO_STATUS_BUS_READ_TIMEOUT == status) || + (SDIO_STATUS_BUS_READ_CRC_ERR == status) || + (SDIO_STATUS_BUS_WRITE_ERROR == status)) { + if (!CHECK_API_VERSION_COMPAT(pHcd,2,6)) { + if (!ForceAllRequestsAsync()) { + /* clear the call bit as an optimization, note clearing it wholesale here will + * allow request processing to recurse one more level */ + AtomicTest_Clear(&pHcd->HcdFlags, HCD_REQUEST_CALL_BIT); + } + } + /* re-use the request for CMD12 */ + pReq->Command = CMD12; + pReq->Argument = 0; + + /* if the data transfer was successful, check for transfer check */ + if (SDIO_SUCCESS(status) && + (pReq->Flags & SDREQ_FLAGS_AUTO_TRANSFER_STATUS)) { + /* original data request requires a transfer status check, which is another + * barrier request */ + pReq->Flags = SDREQ_FLAGS_RESP_R1B | SDREQ_FLAGS_QUEUE_HEAD | SDREQ_FLAGS_BARRIER | + SDREQ_FLAGS_TRANS_ASYNC; + DBG_PRINT(SDIODBG_REQUESTS, ("-SDIO Bus Driver: PrepCMD12Barrier , chaining CMD13 \n")); + /* switch out completion to send the CMD13 next */ + pReq->pCompletion = PrepCMD13Barrier; + } else { + pReq->Flags = SDREQ_FLAGS_RESP_R1B | SDREQ_FLAGS_QUEUE_HEAD | SDREQ_FLAGS_TRANS_ASYNC; + pReq->pCompletion = CMD12Completion; + } + + /* save the original data transfer request status */ + SET_BD_RSV_ORIG_STATUS(pReq,status); + /* re-issue it, we can call IssueRequest here since we are re-using the request */ + IssueRequestToHCD(pHcd, pReq); + } else { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Request Failure (%d) , CMD12 bypassed.\n",status)); + /* call the original completion routine */ + pOrigCompletion(pReq); + } + + DBG_PRINT(SDIODBG_REQUESTS, ("-SDIO Bus Driver: PrepCMD12Barrier (%d) \n",status)); +} + + +/* CMD55 barrier - this is a special barrier completion routine, we have to submit the second + * part of the command command sequence atomically */ +static void CMD55CompletionBarrier(PSDREQUEST pReq) +{ + SDIO_STATUS status = pReq->Status; + PSDREQUEST pOrigReq = GET_BD_RSV_ORIG_REQ(pReq); + PSDHCD pHcd = GET_BD_RSV_HCD(pReq); + BOOL doCompletion = FALSE; + + DBG_ASSERT(pOrigReq != NULL); + DBG_ASSERT(pHcd != NULL); + + DBG_PRINT(SDIODBG_REQUESTS, ("+SDIO Bus Driver: CMD55Completion \n")); + + do { + + if (!SDIO_SUCCESS(status)) { + /* command 55 failed */ + pOrigReq->Status = status; + doCompletion = TRUE; + break; + } + + if (!(SD_R1_GET_CARD_STATUS(pReq->Response) & SD_CS_APP_CMD)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Card is not accepting CMD55, status:0x%X \n", + SD_R1_GET_CARD_STATUS(pReq->Response))); + pOrigReq->Status = SDIO_STATUS_INVALID_COMMAND; + doCompletion = TRUE; + break; + } + + if (!CHECK_API_VERSION_COMPAT(pHcd,2,6)) { + if (!ForceAllRequestsAsync()) { + AtomicTest_Clear(&pHcd->HcdFlags, HCD_REQUEST_CALL_BIT); + } + } + + /* flag the original request to queue to the head */ + pOrigReq->Flags |= SDREQ_FLAGS_QUEUE_HEAD; + /* submit original request, we cannot call IssueRequestHCD() here because the + * original request has already gone through IssueRequestHCD() already */ + status = StartHcdRequest(pHcd, pOrigReq); + + if (SDIO_STATUS_PENDING == status) { + break; + } + + pOrigReq->Status = status; + + if (SDIO_STATUS_SDREQ_QUEUE_FAILED == status) { + /* never made it to the queue */ + doCompletion = TRUE; + break; + } + + /* request completed in-line */ + _SDIO_HandleHcdEvent(pHcd, EVENT_HCD_TRANSFER_DONE); + + } while (FALSE); + + if (doCompletion) { + DoRequestCompletion(pOrigReq, pHcd); + } + + /* free the CMD55 request */ + FreeRequest(pReq); + + DBG_PRINT(SDIODBG_REQUESTS, ("-SDIO Bus Driver: CMD55Completion \n")); +} + + +/* synch completion routine */ +static void SynchCompletion(PSDREQUEST pRequest) +{ + PSIGNAL_ITEM pSignal; + + pSignal = (PSIGNAL_ITEM)pRequest->pCompleteContext; + DBG_ASSERT(pSignal != NULL); + if (!SDIO_SUCCESS(SignalSet(&pSignal->Signal))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: SynchCompletion - signal failed \n")); + } + +} + +/* + * Issue a request to the host controller + * + * + * The following flags are handled internally by the bus driver to guarantee atomicity. + * + * SDREQ_FLAGS_APP_CMD - SD Extended commands requiring CMD55 to precede the actual command + * SDREQ_FLAGS_AUTO_CMD12 - Memory Card Data transfer needs CMD12 to stop transfer + * (multi-block reads/writes) + * SDREQ_FLAGS_AUTO_TRANSFER_STATUS - Memory card data transfer needs transfer status polling + * using CMD13 + * + * These request flags require additional commands prepended or appended to the original command + * + * The order of command execution : + * + * Order Condition Command Issued + * ------------------------------------------------------------- + * 1. If APP_CMD CMD55 issued. + * 2. Always Caller command issued. + * 3. If AUTO_CMD12 CMD12 issued. + * 4. If AUTO_TRANSFER_STATUS CMD13 issued until card programming is complete +*/ +SDIO_STATUS IssueRequestToHCD(PSDHCD pHcd, PSDREQUEST pReq) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSIGNAL_ITEM pSignal = NULL; + BOOL handleFailedReqSubmit = FALSE; + + CLEAR_INTERNAL_REQ_FLAGS(pReq); + + do { + /* mark request in-use */ + ATOMIC_FLAGS internal = AtomicTest_Set(&pReq->InternalFlags, SDBD_PENDING); + if (internal & (1<Flags & SDREQ_FLAGS_TRANS_ASYNC)) { + /* caller wants synchronous operation, insert our completion routine */ + pReq->pCompletion = SynchCompletion; + pSignal = AllocateSignal(); + if (NULL == pSignal) { + status = SDIO_STATUS_NO_RESOURCES; + pReq->Status = SDIO_STATUS_NO_RESOURCES; + handleFailedReqSubmit = TRUE; + /* no need to continue */ + break; + } + pReq->pCompleteContext = (PVOID)pSignal; + } + + if ((pReq->Flags & SDREQ_FLAGS_AUTO_CMD12) && + !(pHcd->Attributes & SDHCD_ATTRIB_AUTO_CMD12) && + !(IS_HCD_BUS_MODE_SPI(pHcd) && IS_SDREQ_WRITE_DATA(pReq->Flags))) { + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Auto CMD12 on Request:0x%08X \n",(INT)pReq)); + /* caller wants CMD12 auto-issued and the HCD does not support it */ + /* setup caller's request as a barrier and replace their completion routine */ + pReq->Flags |= SDREQ_FLAGS_BARRIER; + /* take off the flag, since the BD will be issuing it */ + pReq->Flags &= ~SDREQ_FLAGS_AUTO_CMD12; + /* save original completion */ + SET_BD_RSV_REQUEST_COMPLETION(pReq,pReq->pCompletion); + /* save the HCD we are on */ + SET_BD_RSV_HCD(pReq,pHcd); + /* use completion for preping CMD12 */ + pReq->pCompletion = PrepCMD12Barrier; + } + + if (pReq->Flags & SDREQ_FLAGS_AUTO_TRANSFER_STATUS) { + /* caller wants transfer status checked. If a CMD12 + * barrier request has been setup we let the CMD12 completion take care + * of setting up the transfer check */ + if (pReq->pCompletion != PrepCMD12Barrier) { + /* make CMD13 prep a barrier */ + pReq->Flags |= SDREQ_FLAGS_BARRIER; + /* save original completion */ + SET_BD_RSV_REQUEST_COMPLETION(pReq,pReq->pCompletion); + /* save the HCD we are on */ + SET_BD_RSV_HCD(pReq,pHcd); + /* use completion for preping CMD13 */ + pReq->pCompletion = PrepCMD13Barrier; + } + } + + /* check app command, the two command sequence must be handled atomically */ + if (pReq->Flags & SDREQ_FLAGS_APP_CMD) { + PSDREQUEST pCmd55; + /* allocate request to handle initial CMD55 command */ + pCmd55 = AllocateRequest(); + if (NULL == pCmd55) { + status = SDIO_STATUS_NO_RESOURCES; + pReq->Status = SDIO_STATUS_NO_RESOURCES; + /* complete the caller's request with error */ + handleFailedReqSubmit = TRUE; + /* no need to continue */ + break; + } + /* first submit CMD55 */ + /* set RCA */ + pCmd55->Argument = pHcd->CardProperties.RCA << 16; + /* mark as a barrier request */ + pCmd55->Flags = SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_BARRIER | SDREQ_FLAGS_TRANS_ASYNC; + pCmd55->Command = CMD55; + /* call our barrier completion routine when done */ + pCmd55->pCompletion = CMD55CompletionBarrier; + /* save request and target HCD */ + SET_BD_RSV_ORIG_REQ(pCmd55,pReq); + SET_BD_RSV_HCD(pCmd55,pHcd); + /* recursively start the CMD55 request, since the CMD55 is a barrier + * request, it's completion routine will submit the actual request + * atomically */ + status = IssueRequestToHCD(pHcd, pCmd55); + + } else { + /* start the normal request */ + status = StartHcdRequest(pHcd,pReq); + } + + + if (SDIO_STATUS_SDREQ_QUEUE_FAILED == status) { + handleFailedReqSubmit = TRUE; + /* no need to continue, clean up at the end */ + break; + } + + /* at this point, the request was either queued or was processed by the + * HCD */ + + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: HCD returned status:%d on request: 0x%X, (CMD:%d) \n", + status, (INT)pReq, pReq->Command)); + + if (status != SDIO_STATUS_PENDING) { + /* the HCD completed the request within the HCD request callback, + * check and see if this is a synchronous request */ + if (pSignal != NULL) { + /* it was synchronous */ + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Sync-Op signal wait bypassed \n")); + /* NULL out completion info, there's no need to + * signal the semaphore */ + pReq->pCompletion = NULL; + + } else { + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Async operation completed in-line \n")); + /* this was an async call, always return pending */ + status = SDIO_STATUS_PENDING; + } + /* process this completed transfer on behalf of the HCD */ + _SDIO_HandleHcdEvent(pHcd, EVENT_HCD_TRANSFER_DONE); + + /* done processing */ + break; + } + /* I/O is now pending, could be sync or async */ + /* check for synch op */ + if (pSignal != NULL) { + /* wait for completion */ + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Sync-Op signal waiting....\n")); + /* this is not interruptable, as the HCD must complete it. */ + status = SignalWait(&pSignal->Signal); + /* don't need the signal anymore */ + FreeSignal(pSignal); + pSignal = NULL; + + /* note: it is safe to touch pReq since we own + * the completion routine for synch transfers */ + + /* check signal wait status */ + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_TRACE, + ("SDIO Bus Driver - IssueRequestToHCD: Synch transfer - signal wait failed, cancelling req 0X%X\n", + (UINT)pReq)); + pReq->Status = SDIO_STATUS_CANCELED; + status = SDIO_STATUS_CANCELED; + break; + } + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Sync-Op woke up\n")); + /* return the completion status of the request */ + status = pReq->Status; + } else { + DBG_PRINT(SDIODBG_REQUESTS, ("SDIO Bus Driver: Async operation Pending \n")); + } + + } while (FALSE); + + /* see if we need to clean up failed submissions */ + if (handleFailedReqSubmit) { + /* make sure this is cleared */ + AtomicTest_Clear(&pReq->InternalFlags, SDBD_PENDING); + /* the request processing failed before it was submitted to the HCD */ + /* note: since it never made it to the queue we can touch pReq */ + if (pReq->Flags & SDREQ_FLAGS_TRANS_ASYNC) { + /* for ASYNC requests, we need to call the completion routine */ + DoRequestCompletion(pReq, pHcd); + /* return pending for all ASYNC requests */ + status = SDIO_STATUS_PENDING; + } + } + + /* check if we need to clean up the signal */ + if (pSignal != NULL) { + /* make sure this is freed */ + FreeSignal(pSignal); + } + /* return status */ + return status; +} + +/* documentation for configuration requests */ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Enable or Disable the SDIO Function + + @function name: SDCONFIG_FUNC_ENABLE_DISABLE + @prototype: SDCONFIG_FUNC_ENABLE_DISABLE + @category: PD_Reference + + @input: SDCONFIG_FUNC_ENABLE_DISABLE_DATA - Enable Data structure + + @output: none + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + uses the SDCONFIG_FUNC_ENABLE_DISABLE_DATA structure. The caller must set the + EnableFlags and specify the TimeOut value in milliseconds. The TimeOut + value is used for polling the I/O ready bit. This command returns a status + of SDIO_STATUS_FUNC_ENABLE_TIMEOUT if the ready bit was not set/cleared + by the card within the timeout period. + + @example: Example of enabling an I/O function: + fData.EnableFlags = SDCONFIG_ENABLE_FUNC; + fData.TimeOut = 500; + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_ENABLE_DISABLE, + &fData, + sizeof(fData)); + + @see also: SDLIB_IssueConfig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Unmask the function's IRQ + + @function name: SDCONFIG_FUNC_UNMASK_IRQ + @prototype: SDCONFIG_FUNC_UNMASK_IRQ + @category: PD_Reference + + @input: none + + @output: none + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + unmasks the IRQ for the I/O function. This request sets the function's + interrupt enable bit in the INTENABLE register in the + common register space. + + @example: Example of unmasking interrupt : + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_UNMASK_IRQ, + NULL, + 0); + + @see also: SDCONFIG_FUNC_MASK_IRQ + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Mask the function's IRQ + + @function name: SDCONFIG_FUNC_MASK_IRQ + @prototype: SDCONFIG_FUNC_MASK_IRQ + @category: PD_Reference + + @input: none + + @output: none + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + masks the IRQ for the I/O function. + + @example: Example of unmasking interrupt : + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_MASK_IRQ, + NULL, + 0); + + @see also: SDCONFIG_FUNC_UNMASK_IRQ + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Acknowledge that the function's IRQ has been handled + + @function name: SDCONFIG_FUNC_ACK_IRQ + @prototype: SDCONFIG_FUNC_ACK_IRQ + @category: PD_Reference + + @input: none + + @output: none + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + indicates to the bus driver that the function driver has handled the + interrupt. The bus driver will notify the host controller to unmask the + interrupt source. SDIO interrupts are level triggered and are masked at the + host controller level until all function drivers have indicated that they + have handled their respective interrupt. This command can be issued in either + the IRQ handler or asynchronous IRQ handler. + + @example: Example of acknowledging an interrupt : + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_ACK_IRQ, + NULL, + 0); + + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Disable SD/MMC/SDIO card CRC checking. + + @function name: SDCONFIG_FUNC_SPI_MODE_DISABLE_CRC + @prototype: SDCONFIG_FUNC_SPI_MODE_DISABLE_CRC + @category: PD_Reference + + @input: none + + @output: none + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + issues CMD59 to disable SPI-CRC checking and requests the host controller + driver to stop checking the CRC. This is typically used in systems where + CRC checking is not required and performance is improved if the CRC checking + is ommitted (i.e. SPI implementations without hardware CRC support). + + @example: Example of disabling SPI CRC checking: + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_SPI_MODE_DISABLE_CRC, + NULL, + 0); + + @see also: SDCONFIG_FUNC_SPI_MODE_ENABLE_CRC + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Enable SD/MMC/SDIO card CRC checking. + + @function name: SDCONFIG_FUNC_SPI_MODE_ENABLE_CRC + @prototype: SDCONFIG_FUNC_SPI_MODE_ENABLE_CRC + @category: PD_Reference + + @input: none + + @output: none + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + issues CMD59 to enable SPI-CRC checking and requests the host controller + driver to generate valid CRCs for commands and data as well as + check the CRC in responses and incomming data blocks. + + @example: Example of enabling SPI CRC checking: + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_SPI_MODE_ENABLE_CRC, + NULL, + 0); + + @see also: SDCONFIG_FUNC_SPI_MODE_DISABLE_CRC + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Allocate slot current for a card function. + + @function name: SDCONFIG_FUNC_ALLOC_SLOT_CURRENT + @prototype: SDCONFIG_FUNC_ALLOC_SLOT_CURRENT + @category: PD_Reference + + @input: SDCONFIG_FUNC_SLOT_CURRENT_DATA + + @output: SDCONFIG_FUNC_SLOT_CURRENT_DATA + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + requests an allocation of slot current to satisfy the power requirements + of the function. The command uses the SDCONFIG_FUNC_SLOT_CURRENT_DATA + data structure to pass the required current in mA. Slot current allocation + is not cummulative and this command should only be issued once by each function + driver with the worse case slot current usage. + The command returns SDIO_STATUS_NO_RESOURCES if the + requirement cannot be met by the host hardware. The SlotCurrent field will + contain the remaining current available to the slot. The slot current should + be allocated before the function is enabled using SDCONFIG_FUNC_ENABLE_DISABLE. + When a function driver is unloaded it should free the slot current allocation + by using the SDCONFIG_FUNC_FREE_SLOT_CURRENT command. + + @example: Example of allocating slot current: + slotCurrent.SlotCurrent = 150; // 150 mA + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_ALLOC_SLOT_CURRENT, + &slotCurrent, + sizeof(slotCurrent)); + + + @see also: SDCONFIG_FUNC_FREE_SLOT_CURRENT + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Free slot current for a card function. + + @function name: SDCONFIG_FUNC_FREE_SLOT_CURRENT + @prototype: SDCONFIG_FUNC_FREE_SLOT_CURRENT + @category: PD_Reference + + @input: none + + @output: none + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + frees the allocated current for a card function. This command should be + issued only once (per function) and only after an allocation was successfully made. + + @example: Example of freeing slot current: + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_FREE_SLOT_CURRENT, + NULL, + 0); + + @see also: SDCONFIG_FUNC_ALLOC_SLOT_CURRENT + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Set the bus mode for the SD/SDIO card. + + @function name: SDCONFIG_FUNC_CHANGE_BUS_MODE + @prototype: SDCONFIG_FUNC_CHANGE_BUS_MODE + @category: PD_Reference + + @input: none + + @output: none + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + alters the card's bus mode (width and clock rate) to a driver specified + value. The driver must read the current bus mode flags, modify if necessary + and pass the value in the SDCONFIG_BUS_MODE_DATA structure. + If the bus width is changed (1 or 4 bit) the caller must adjust the mode flags + for the new width. Cards cannot be switched between 1/4 bit and SPI mode. + Switching to or from SPI mode requires a power cycle. Adjustments to the clock + rate is immediate on the next bus transaction. The actual clock rate value is + limited by the host controller and is reported in the ClockRate field when the + command completes successfully. + The bus mode change is card wide and may affect other SDIO functions on + multi-function cards. Use this feature with caution. This feature should NOT be + used to dynamically control clock rates during runtime and should only be used + at card initialization. Changing the bus mode must be done with SDIO function + interrupts masked. + This request can block and must only be called from a schedulable context. + + @example: Example of changing the clock rate: + SDCONFIG_BUS_MODE_DATA busSettings; + ZERO_OBJECT(busSettings); + // get current bus flags and keep the same bus width + busSettings.BusModeFlags = SDDEVICE_GET_BUSMODE_FLAGS(pInstance->pDevice); + busSettings.ClockRate = 8000000; // adjust clock to 8 Mhz + // issue config request to override clock rate + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_CHANGE_BUS_MODE, + &busSettings, + sizeof(SDCONFIG_BUS_MODE_DATA)); + + @see also: SDDEVICE_GET_BUSMODE_FLAGS + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the debug level of the underlying host controller driver. + + @function name: SDCONFIG_GET_HCD_DEBUG + @prototype: SDCONFIG_GET_HCD_DEBUG + @category: PD_Reference + + @input: none + + @output: CT_DEBUG_LEVEL + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + requests the current debug level of the HCD driver. This API is useful for + saving the current debug level of the HCD prior to issuing SDCONFIG_SET_HCD_DEBUG + in order to increase the verbosity of the HCD. This API should be used only for + debugging purposes. If multiple functions attempt to save and set the HCD debug + level simultanously, the final debug level will be unknown. Not all HCDs support + this command. + + @example: Example of saving the debug level: + CT_DEBUG_LEVEL savedDebug; + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_GET_HCD_DEBUG, + &savedDebug, + sizeof(savedDebug)); + + @see also: SDCONFIG_SET_HCD_DEBUG + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Set the debug level of the underlying host controller driver. + + @function name: SDCONFIG_SET_HCD_DEBUG + @prototype: SDCONFIG_SET_HCD_DEBUG + @category: PD_Reference + + @input: CT_DEBUG_LEVEL + + @output: none + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command + sets the current debug level of the HCD driver. This API is useful for + setting the debug level of the HCD programatically for debugging purposes. + If multiple functions attempt to save and set the HCD debug + level simultanously, the final debug level will be unknown. Not all HCDs support + this request. + + @example: Example of setting the debug level: + CT_DEBUG_LEVEL setDebug = 15; + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_GET_HCD_DEBUG, + &setDebug, + sizeof(setDebug)); + + @see also: SDCONFIG_GET_HCD_DEBUG + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Instruct the bus driver to not check the SDIO card interrupt pending + register on card interrupts, if possible. + + @function name: SDCONFIG_FUNC_NO_IRQ_PEND_CHECK + @prototype: SDCONFIG_FUNC_NO_IRQ_PEND_CHECK + @category: PD_Reference + + @input: none + + @output: none + + @return: SDIO Status + + @notes: This command code is used in the SDLIB_IssueConfig() API. The command instructs the + bus driver to skip checking the card interrupt pending register on each card + interrupt. The bus driver will assume the function is interrupting and immediately start + the interrupt processing stage. This option is only valid for single function cards. + The bus driver will reject the command for a card with more than 1 function. + For single function cards, this can improve interrupt response time. + + @example: Example of skipping IRQ pending checks: + + status = SDLIB_IssueConfig(pInstance->pDevice, + SDCONFIG_FUNC_NO_IRQ_PEND_CHECK, + NULL, + 0); + + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ diff --git a/drivers/sdio/stack/busdriver/sdio_bus_events.c b/drivers/sdio/stack/busdriver/sdio_bus_events.c new file mode 100644 index 00000000000..5b3148d6752 --- /dev/null +++ b/drivers/sdio/stack/busdriver/sdio_bus_events.c @@ -0,0 +1,1040 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@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 of 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; + } + } + + /* 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); + + /* 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; + case EVENT_HCD_CD_POLLING: + /* run detector */ + RunCardDetect(); + break; + 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; +} + + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 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")); +} + + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 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)) { + INT dbgLvl; + dbgLvl = DBG_GET_DEBUG_LEVEL(); + DBG_SET_DEBUG_LEVEL(SDDBG_WARN); + status = CardInitSetup(pHcd); + DBG_SET_DEBUG_LEVEL(dbgLvl); + 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; + } + /* 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; + } + } 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)) { + /* 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); + } + } 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; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 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) { + 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) { + 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); + } + +} diff --git a/drivers/sdio/stack/busdriver/sdio_bus_misc.c b/drivers/sdio/stack/busdriver/sdio_bus_misc.c new file mode 100644 index 00000000000..c5c73810782 --- /dev/null +++ b/drivers/sdio/stack/busdriver/sdio_bus_misc.c @@ -0,0 +1,3122 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_bus_misc.c + +@abstract: OS independent bus driver support + +#notes: this file contains miscellaneous control functions + +@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 of 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 + + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + IssueBusRequestBd - issue a bus request + Input: pHcd - HCD object + Cmd - command to issue + Argument - command argument + Flags - request flags + + Output: pReqToUse - request to use (if caller wants response data) + Return: SDIO Status + Notes: This function only issues 1 block data transfers + This function issues the request synchronously +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _IssueBusRequestBd(PSDHCD pHcd, + UINT8 Cmd, + UINT32 Argument, + SDREQUEST_FLAGS Flags, + PSDREQUEST pReqToUse, + PVOID pData, + INT Length) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDREQUEST pReq; + + if (NULL == pReqToUse) { + /* caller doesn't care about the response data, allocate locally */ + pReq = AllocateRequest(); + if (NULL == pReq) { + return SDIO_STATUS_NO_RESOURCES; + } + } else { + /* use the caller's request buffer */ + pReq = pReqToUse; + } + + pReq->Argument = Argument; + pReq->Flags = Flags; + pReq->Command = Cmd; + if (pReq->Flags & SDREQ_FLAGS_DATA_TRANS) { + pReq->pDataBuffer = pData; + pReq->BlockCount = 1; + pReq->BlockLen = Length; + } + + status = IssueRequestToHCD(pHcd,pReq); + + if (NULL == pReqToUse) { + DBG_ASSERT(pReq != NULL); + FreeRequest(pReq); + } + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ConvertVoltageCapsToOCRMask - initialize card + Input: VoltageCaps - voltage cap to look up + Return: 32 bit OCR mask + Notes: this function sets voltage for +- 10% + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static UINT32 ConvertVoltageCapsToOCRMask(SLOT_VOLTAGE_MASK VoltageCaps) +{ + UINT32 ocrMask; + + ocrMask = 0; + + if (VoltageCaps & SLOT_POWER_3_3V) { + ocrMask |= SD_OCR_3_2_TO_3_3_VDD | SD_OCR_3_3_TO_3_4_VDD; + } + if (VoltageCaps & SLOT_POWER_3_0V) { + ocrMask |= SD_OCR_2_9_TO_3_0_VDD | SD_OCR_3_0_TO_3_1_VDD; + } + if (VoltageCaps & SLOT_POWER_2_8V) { + ocrMask |= SD_OCR_2_7_TO_2_8_VDD | SD_OCR_2_8_TO_2_9_VDD; + } + if (VoltageCaps & SLOT_POWER_2_0V) { + ocrMask |= SD_OCR_1_9_TO_2_0_VDD | SD_OCR_2_0_TO_2_1_VDD; + } + if (VoltageCaps & SLOT_POWER_1_8V) { + ocrMask |= SD_OCR_1_7_TO_1_8_VDD | SD_OCR_1_8_TO_1_9_VDD; + } + if (VoltageCaps & SLOT_POWER_1_6V) { + ocrMask |= SD_OCR_1_6_TO_1_7_VDD; + } + + return ocrMask; +} + +static UINT32 GetUsableOCRValue(UINT32 CardOCR, UINT32 SlotOCRMask) +{ + INT i; + UINT32 mask = 0; + + for (i = 0; i < 32; i++) { + mask = 1 << i; + if ((SlotOCRMask & mask) && (CardOCR & mask)) { + return mask; + } + } + + return mask; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + GetPowerSetting - power up the SDIO card + Input: pHcd - HCD object + pOCRvalue - OCR value of the card + Output: pOCRvalue - OCR to actually use + Return: power setting for HCD based on card's OCR, zero indicates unsupported + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static SLOT_VOLTAGE_MASK GetPowerSetting(PSDHCD pHcd, UINT32 *pOCRvalue) +{ + UINT32 ocrMask; + SLOT_VOLTAGE_MASK hcdVoltage = 0; + SLOT_VOLTAGE_MASK hcdVMask; + INT i; + + /* check preferred value */ + ocrMask = ConvertVoltageCapsToOCRMask(pHcd->SlotVoltagePreferred); + if (ocrMask & *pOCRvalue) { + /* using preferred voltage */ + *pOCRvalue = GetUsableOCRValue(*pOCRvalue, ocrMask); + hcdVoltage = pHcd->SlotVoltagePreferred; + } else { + /* walk through the slot voltage caps and find a match */ + for (i = 0; i < 8; i++) { + hcdVMask = (1 << i); + if (hcdVMask & pHcd->SlotVoltageCaps) { + ocrMask = ConvertVoltageCapsToOCRMask((SLOT_VOLTAGE_MASK)(pHcd->SlotVoltageCaps & hcdVMask)); + if (ocrMask & *pOCRvalue) { + /* found a match */ + *pOCRvalue = GetUsableOCRValue(*pOCRvalue, ocrMask); + hcdVoltage = pHcd->SlotVoltageCaps & hcdVMask; + break; + } + } + } + } + + return hcdVoltage; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + TestPresence - test the presence of a card/function + Input: pHcd - HCD object + TestType - type of test to perform + Output: pReq - Request to use (optional) + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS TestPresence(PSDHCD pHcd, + CARD_INFO_FLAGS TestType, + PSDREQUEST pReq) +{ + SDIO_STATUS status = SDIO_STATUS_ERROR; + + switch (TestType) { + case CARD_SDIO: + /* issue CMD5 */ + status = _IssueSimpleBusRequest(pHcd,CMD5,0, + SDREQ_FLAGS_RESP_SDIO_R4 | SDREQ_FLAGS_RESP_SKIP_SPI_FILT,pReq); + break; + case CARD_SD: + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + /* ACMD41 just starts initialization when in SPI mode, argument is ignored + * Note: In SPI mode ACMD41 uses an R1 response */ + status = _IssueSimpleBusRequest(pHcd,ACMD41,0, + SDREQ_FLAGS_APP_CMD | SDREQ_FLAGS_RESP_R1,pReq); + + } else { + /* issue ACMD41 with OCR value of zero */ + /* ACMD41 on SD uses an R3 response */ + status = _IssueSimpleBusRequest(pHcd,ACMD41,0, + SDREQ_FLAGS_APP_CMD | SDREQ_FLAGS_RESP_R3,pReq); + } + break; + case CARD_MMC: + /* issue CMD1 */ + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + /* note: in SPI mode an R1 response is used */ + status = _IssueSimpleBusRequest(pHcd,CMD1,0,SDREQ_FLAGS_RESP_R1,pReq); + } else { + status = _IssueSimpleBusRequest(pHcd,CMD1,0,SDREQ_FLAGS_RESP_R3,pReq); + } + break; + default: + DBG_ASSERT(FALSE); + break; + } + + return status; +} +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ReadOCR - read the OCR + Input: pHcd - HCD object + ReadType - type of read to perform + OCRValue - OCR value to use as an argument + Output: pReq - Request to use + pOCRValueRd - OCR value read back (can be NULL) + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static SDIO_STATUS ReadOCR(PSDHCD pHcd, + CARD_INFO_FLAGS ReadType, + PSDREQUEST pReq, + UINT32 OCRValue, + UINT32 *pOCRValueRd) +{ + SDIO_STATUS status = SDIO_STATUS_ERROR; + + switch (ReadType) { + case CARD_SDIO: + /* CMD5 for SDIO cards */ + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + /* skip the SPI filter, we will decode the response here */ + status = _IssueSimpleBusRequest(pHcd,CMD5, + OCRValue, + SDREQ_FLAGS_RESP_SDIO_R4 | + SDREQ_FLAGS_RESP_SKIP_SPI_FILT, + pReq); + } else { + /* native SD */ + status = _IssueSimpleBusRequest(pHcd,CMD5, + OCRValue, + SDREQ_FLAGS_RESP_SDIO_R4, + pReq); + } + break; + case CARD_SD: + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + /* CMD58 is used to read the OCR */ + status = _IssueSimpleBusRequest(pHcd,CMD58, + 0, /* argument ignored */ + (SDREQ_FLAGS_RESP_R3 | SDREQ_FLAGS_RESP_SKIP_SPI_FILT), + pReq); + } else { + /* SD Native uses ACMD41 */ + status = _IssueSimpleBusRequest(pHcd,ACMD41, + OCRValue, + SDREQ_FLAGS_APP_CMD | SDREQ_FLAGS_RESP_R3, + pReq); + } + break; + case CARD_MMC: + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + /* CMD58 is used to read the OCR */ + status = _IssueSimpleBusRequest(pHcd,CMD58, + 0, /* argument ignored */ + (SDREQ_FLAGS_RESP_R3 | SDREQ_FLAGS_RESP_SKIP_SPI_FILT), + pReq); + } else { + /* MMC Native uses CMD1 */ + status = _IssueSimpleBusRequest(pHcd,CMD1, + OCRValue, SDREQ_FLAGS_RESP_R3, + pReq); + } + break; + default: + DBG_ASSERT(FALSE); + break; + } + + if (SDIO_SUCCESS(status) && (pOCRValueRd != NULL)) { + *pOCRValueRd = 0; + /* someone wants the OCR read back */ + switch (ReadType) { + case CARD_SDIO: + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + *pOCRValueRd = SPI_SDIO_R4_GET_OCR(pReq->Response); + } else { + *pOCRValueRd = SD_SDIO_R4_GET_OCR(pReq->Response); + } + break; + case CARD_SD: + case CARD_MMC: + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + *pOCRValueRd = SPI_R3_GET_OCR(pReq->Response); + } else { + *pOCRValueRd = SD_R3_GET_OCR(pReq->Response); + } + break; + default: + DBG_ASSERT(FALSE); + break; + } + } + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + PollCardReady - poll card till it's ready + Input: pHcd - HCD object + OCRValue - OCR value to poll with + PollType - polling type (based on card type) + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS PollCardReady(PSDHCD pHcd, UINT32 OCRValue, CARD_INFO_FLAGS PollType) +{ + INT cardReadyRetry; + SDIO_STATUS status; + PSDREQUEST pReq; + + if (!((PollType == CARD_SDIO) || (PollType == CARD_SD) || (PollType == CARD_MMC))) { + DBG_ASSERT(FALSE); + return SDIO_STATUS_INVALID_PARAMETER; + } + + pReq = AllocateRequest(); + if (NULL == pReq) { + return SDIO_STATUS_NO_RESOURCES; + } + + status = SDIO_STATUS_SUCCESS; + cardReadyRetry = pBusContext->CardReadyPollingRetry; + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Polling card ready, Using OCR:0x%8.8X, Poll Type:0x%X\n", + OCRValue,PollType)); + + /* now issue CMD with the actual OCR as an argument until the card is ready */ + while (cardReadyRetry) { + if (IS_HCD_BUS_MODE_SPI(pHcd) && !(PollType == CARD_SDIO)) { + if (PollType == CARD_MMC) { + /* under SPI mode for MMC cards, we need to issue CMD1 and + * check the response for the "in-idle" bit */ + status = _IssueSimpleBusRequest(pHcd, + CMD1, + 0, + SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_RESP_SKIP_SPI_FILT, + pReq); + } else if (PollType == CARD_SD) { + /* under SPI mode for SD cards, we need to issue ACMD41 and + * check the response for the "in-idle" bit */ + status = _IssueSimpleBusRequest(pHcd, + ACMD41, + 0, + SDREQ_FLAGS_RESP_R1 | + SDREQ_FLAGS_APP_CMD | + SDREQ_FLAGS_RESP_SKIP_SPI_FILT, + pReq); + } else { + DBG_ASSERT(FALSE); + } + } else { + /* for SD/MMC in native mode and SDIO (all modes) we need to read the OCR register */ + /* read the OCR using the supplied OCR value as an argument, we don't care about the + * actual OCR read-back, but we are interested in the response */ + status = ReadOCR(pHcd,PollType,pReq,OCRValue,NULL); + } + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to issue CMD to poll ready \n")); + break; + } + if (PollType == CARD_SDIO) { + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + if (SPI_SDIO_R4_IS_CARD_READY(pReq->Response)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SDIO Card Ready! (SPI) \n")); + break; + } + } else { + if (SD_SDIO_R4_IS_CARD_READY(pReq->Response)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SDIO Card Ready! \n")); + break; + } + } + } else if ((PollType == CARD_SD) || (PollType == CARD_MMC)) { + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + /* check response when MMC or SD cards operate in SPI mode */ + if (!(GET_SPI_R1_RESP_TOKEN(pReq->Response) & SPI_CS_STATE_IDLE)) { + /* card is no longer in idle */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD/MMC Card (SPI mode) is ready! \n")); + break; + } + } else { + /* check the OCR busy bit */ + if (SD_R3_IS_CARD_READY(pReq->Response)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD/MMC (Native Mode) Card Ready! \n")); + break; + } + } + } else { + DBG_ASSERT(FALSE); + } + cardReadyRetry--; + /* delay */ + status = OSSleep(OCR_READY_CHECK_DELAY_MS); + if (!SDIO_SUCCESS(status)){ + break; + } + } + + if (0 == cardReadyRetry) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Card Ready timeout! \n")); + status = SDIO_STATUS_DEVICE_ERROR; + } + + FreeRequest(pReq); + + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + AdjustSlotPower - adjust slot power + Input: pHcd - HCD object + Output: pOCRvalue - ocr value to use + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static SDIO_STATUS AdjustSlotPower(PSDHCD pHcd, UINT32 *pOCRvalue) +{ + SDCONFIG_POWER_CTRL_DATA pwrSetting; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + ZERO_OBJECT(pwrSetting); + DBG_PRINT(SDDBG_TRACE, + ("SDIO Bus Driver: Adjusting Slot Power, Requesting adjustment for OCR:0x%8.8X \n", + *pOCRvalue)); + + do { + pwrSetting.SlotPowerEnable = TRUE; + /* get optimal power setting */ + pwrSetting.SlotPowerVoltageMask = GetPowerSetting(pHcd, pOCRvalue); + if (0 == pwrSetting.SlotPowerVoltageMask) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: No matching voltage for OCR \n")); + status = SDIO_STATUS_DEVICE_ERROR; + break; + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Slot Pwr Mask 0x%X for OCR:0x%8.8X \n", + pwrSetting.SlotPowerVoltageMask,*pOCRvalue)); + status = _IssueConfig(pHcd,SDCONFIG_POWER_CTRL,&pwrSetting,sizeof(pwrSetting)); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set power in hcd \n")); + break; + } + /* delay for power to settle */ + OSSleep(pBusContext->PowerSettleDelay); + /* save off for drivers */ + pHcd->CardProperties.CardVoltage = pwrSetting.SlotPowerVoltageMask; + + } while (FALSE); + + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ConvertEncodedTransSpeed - convert encoded TRANS_SPEED value to a clock rate + Input: TransSpeedValue - encoded transfer speed value + Output: + Return: appropriate SD clock rate + Notes: This function returns a rate of 0, if it could not be determined. + This function can check tran speed values for SD,SDIO and MMC cards +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static SD_BUSCLOCK_RATE ConvertEncodedTransSpeed(UINT8 TransSpeedValue) +{ + SD_BUSCLOCK_RATE transfMul = 0; + UINT8 timeVal = 0; + + switch (TransSpeedValue & TRANSFER_UNIT_MULTIPIER_MASK) { + case 0: + transfMul = 10000; + break; + case 1: + transfMul = 100000; + break; + case 2: + transfMul = 1000000; + break; + case 3: + transfMul = 10000000; + break; + default: + transfMul = 0; + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: Card transfer multipler is wrong (val=0x%X)! \n", + TransSpeedValue)); + break; + } + + switch ((TransSpeedValue & TIME_VALUE_MASK) >> TIME_VALUE_SHIFT) { + case 1: timeVal = 10; break; + case 2: timeVal = 12; break; + case 3: timeVal = 13; break; + case 4: timeVal = 15; break; + case 5: timeVal = 20; break; + case 6: timeVal = 25; break; + case 7: timeVal = 30; break; + case 8: timeVal = 35; break; + case 9: timeVal = 40; break; + case 10: timeVal = 45; break; + case 11: timeVal = 50; break; + case 12: timeVal = 55; break; + case 13: timeVal = 60; break; + case 14: timeVal = 70; break; + case 15: timeVal = 80; break; + default: timeVal = 0; + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: Card time value is wrong (val=0x%X)! \n", + TransSpeedValue)); + break; + } + + if ((transfMul != 0) && (timeVal != 0)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Card Reported Max: %d Hz (0x%X) \n", + (timeVal*transfMul), TransSpeedValue)); + return timeVal*transfMul; + } + + return 0; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SelectDeselectCard - Select or deselect a card + Input: pHcd - HCD object + Select - select the card + Output: + Return: status + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static SDIO_STATUS SelectDeselectCard(PSDHCD pHcd, BOOL Select) +{ + SDIO_STATUS status; + + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + /* SPI mode cards do not support selection */ + status = SDIO_STATUS_SUCCESS; + } else { + if (!Select) { + /* deselect, note that deselecting a card does not return a response */ + status = _IssueSimpleBusRequest(pHcd, + CMD7,0, + SDREQ_FLAGS_NO_RESP,NULL); + } else { + /* select */ + status = _IssueSimpleBusRequest(pHcd, + CMD7,(pHcd->CardProperties.RCA << 16), + SDREQ_FLAGS_RESP_R1B,NULL); + } + } + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Failed to %s card, RCA:0x%X Err:%d \n", + (Select ? "Select":"Deselect"), pHcd->CardProperties.RCA, status)); + } + return status; +} + +/* reorder a buffer by swapping MSB with LSB */ +static void ReorderBuffer(UINT8 *pBuffer, INT Bytes) +{ + UINT8 *pEnd; + UINT8 temp; + + DBG_ASSERT(!(Bytes & 1)); + /* point to the end */ + pEnd = &pBuffer[Bytes - 1]; + /* divide in half */ + Bytes = Bytes >> 1; + + while (Bytes) { + temp = *pBuffer; + /* swap bytes */ + *pBuffer = *pEnd; + *pEnd = temp; + pBuffer++; + pEnd--; + Bytes--; + } +} + +#define ADJUST_OPER_CLOCK(pBusMode,Clock) \ + (pBusMode)->ClockRate = min((SD_BUSCLOCK_RATE)(Clock),(pBusMode)->ClockRate) +#define ADJUST_OPER_BLOCK_LEN(pCaps,Length) \ + (pCaps)->OperBlockLenLimit = min((UINT16)(Length),(pCaps)->OperBlockLenLimit) +#define ADJUST_OPER_BLOCK_COUNT(pCaps,Count) \ + (pCaps)->OperBlockCountLimit = min((UINT16)(Count),(pCaps)->OperBlockCountLimit) + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + GetBusParameters - Get bus parameters for a card + Input: pHcd - HCD object + pBusMode - current bus mode on entry + Output: pBusMode - new adjusted bus mode + Return: status + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static SDIO_STATUS GetBusParameters(PSDHCD pHcd, PSDCONFIG_BUS_MODE_DATA pBusMode) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UINT8 temp; + UINT32 tplAddr; + struct SDIO_FUNC_EXT_COMMON_TPL func0ext; + UINT8 scrRegister[SD_SCR_BYTES]; + SD_BUSCLOCK_RATE cardReportedRate = 0; + PSDREQUEST pReq = NULL; + BOOL spiMode = FALSE; + + + if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_SPI) { + spiMode = TRUE; + } + + if (!spiMode) { + /* set highest bus mode bus driver is allowing (non-SPI), the code below will + * adjust to lower or equal settings */ + pBusMode->BusModeFlags = pBusContext->DefaultBusMode; + } + /* set operational parameters */ + pBusMode->ClockRate = pBusContext->DefaultOperClock; + pHcd->CardProperties.OperBlockLenLimit = pBusContext->DefaultOperBlockLen; + pHcd->CardProperties.OperBlockCountLimit = pBusContext->DefaultOperBlockCount; + + /* adjust operational block counts and length to match HCD */ + ADJUST_OPER_BLOCK_LEN(&pHcd->CardProperties,pHcd->MaxBytesPerBlock); + ADJUST_OPER_BLOCK_COUNT(&pHcd->CardProperties,pHcd->MaxBlocksPerTrans); + /* limit operational clock to the max clock rate */ + ADJUST_OPER_CLOCK(pBusMode,pHcd->MaxClockRate); + + if (!spiMode) { + /* check HCD bus mode */ + if (!(pHcd->Attributes & SDHCD_ATTRIB_BUS_4BIT) || + ((pHcd->CardProperties.Flags & CARD_SDIO) && + (pHcd->Attributes & SDHCD_ATTRIB_NO_4BIT_IRQ)) ) { + + if (pHcd->Attributes & SDHCD_ATTRIB_BUS_4BIT) { + DBG_PRINT(SDDBG_WARN, + ("SDIO Card Detected, but host does not support IRQs in 4 bit mode - dropping to 1 bit. \n")); + } + /* force to 1 bit mode */ + SDCONFIG_SET_BUS_WIDTH(pBusMode->BusModeFlags, SDCONFIG_BUS_WIDTH_1_BIT); + } + } + + /* now do various card inquiries to drop the bus mode or clock + * none of these checks can raise the bus mode or clock higher that what + * was initialized above */ + do { + if (pHcd->CardProperties.Flags & (CARD_SD | CARD_MMC)) { + /* allocate a request for response data we'll need */ + pReq = AllocateRequest(); + if (NULL == pReq) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + } + + if (!spiMode && (pHcd->CardProperties.Flags & CARD_MMC)) { + /* MMC cards all run in 1 bit mode */ + SDCONFIG_SET_BUS_WIDTH(pBusMode->BusModeFlags, SDCONFIG_BUS_WIDTH_1_BIT); + } + + if (pHcd->CardProperties.Flags & CARD_SD) { + DBG_ASSERT(pReq != NULL); + DBG_PRINT(SDDBG_TRACE, ("Getting SCR from SD Card..\n")); + /* read SCR (requires data transfer) to get supported modes */ + status = _IssueBusRequestBd(pHcd,ACMD51,0, + SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_APP_CMD | + SDREQ_FLAGS_DATA_TRANS, + pReq,&scrRegister,SD_SCR_BYTES); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_WARN, ("SD card does not have SCR. \n")); + if (!spiMode) { + /* switch it to 1 bit mode */ + SDCONFIG_SET_BUS_WIDTH(pBusMode->BusModeFlags, SDCONFIG_BUS_WIDTH_1_BIT); + } + status = SDIO_STATUS_SUCCESS; + } else { + /* we have to reorder this buffer since the SCR is sent MSB first on the data + * data bus */ + ReorderBuffer(scrRegister,SD_SCR_BYTES); + /* got the SCR */ + DBG_PRINT(SDDBG_TRACE, ("SD SCR StructRev:0x%X, Flags:0x%X \n", + GET_SD_SCR_STRUCT_VER(scrRegister), + GET_SD_SCR_BUSWIDTHS_FLAGS(scrRegister))); + /* set the revision */ + switch (GET_SD_SCR_SDSPEC_VER(scrRegister)) { + case SCR_SD_SPEC_1_00: + DBG_PRINT(SDDBG_TRACE, ("SD Spec Revision 1.01 \n")); + pHcd->CardProperties.SD_MMC_Revision = SD_REVISION_1_01; + break; + case SCR_SD_SPEC_1_10: + DBG_PRINT(SDDBG_TRACE, ("SD Spec Revision 1.10 \n")); + pHcd->CardProperties.SD_MMC_Revision = SD_REVISION_1_10; + break; + default: + DBG_PRINT(SDDBG_WARN, ("SD Spec Revision is greater than 1.10 \n")); + pHcd->CardProperties.SD_MMC_Revision = SD_REVISION_1_10; + break; + } + + if (!(GET_SD_SCR_BUSWIDTHS(scrRegister) & SCR_BUS_SUPPORTS_4_BIT)) { + if (!spiMode) { + DBG_PRINT(SDDBG_WARN, ("SD SCR reports 1bit only Mode \n")); + /* switch it to 1 bit mode */ + SDCONFIG_SET_BUS_WIDTH(pBusMode->BusModeFlags, SDCONFIG_BUS_WIDTH_1_BIT); + } + } + } + } + + if (pHcd->CardProperties.Flags & (CARD_SD | CARD_MMC)) { + DBG_ASSERT(pReq != NULL); + /* de-select the card in order to get the CSD */ + status = SelectDeselectCard(pHcd,FALSE); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to deselect card before getting CSD \n")); + break; + } + /* Get CSD for SD or MMC cards */ + if (spiMode) { + /* in SPI mode, getting the CSD requires a read data transfer */ + status = _IssueBusRequestBd(pHcd,CMD9,0, + SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_TRANS, + pReq, + pHcd->CardProperties.CardCSD, + MAX_CSD_CID_BYTES); + if (SDIO_SUCCESS(status)) { + /* when the CSD is sent over in SPI data mode, it comes to us in MSB first + * and thus is not ordered correctly as defined in the SD spec */ + ReorderBuffer(pHcd->CardProperties.CardCSD,MAX_CSD_CID_BYTES); + } + } else { + status = _IssueSimpleBusRequest(pHcd, + CMD9, + (pHcd->CardProperties.RCA << 16), + SDREQ_FLAGS_RESP_R2, + pReq); + if (SDIO_SUCCESS(status)) { + /* save the CSD */ + memcpy(pHcd->CardProperties.CardCSD,pReq->Response,MAX_CARD_RESPONSE_BYTES); + } + } + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get CSD, Err:%d \n", + status)); + break; + } + /* for MMC cards, the spec version is in the CSD */ + if (pHcd->CardProperties.Flags & CARD_MMC) { + DBG_PRINT(SDDBG_TRACE, ("MMC Spec version : (0x%2.2X) \n", + GET_MMC_SPEC_VERSION(pHcd->CardProperties.CardCSD))); + switch (GET_MMC_SPEC_VERSION(pHcd->CardProperties.CardCSD)) { + case MMC_SPEC_1_0_TO_1_2: + case MMC_SPEC_1_4: + case MMC_SPEC_2_0_TO_2_2: + DBG_PRINT(SDDBG_WARN, ("MMC Spec version less than 3.1 \n")); + pHcd->CardProperties.SD_MMC_Revision = MMC_REVISION_1_0_2_2; + break; + case MMC_SPEC_3_1: + DBG_PRINT(SDDBG_TRACE, ("MMC Spec version 3.1 \n")); + pHcd->CardProperties.SD_MMC_Revision = MMC_REVISION_3_1; + break; + case MMC_SPEC_4_0_TO_4_1: + DBG_PRINT(SDDBG_TRACE, ("MMC Spec version 4.0-4.1 \n")); + pHcd->CardProperties.SD_MMC_Revision = MMC_REVISION_4_0; + break; + default: + pHcd->CardProperties.SD_MMC_Revision = MMC_REVISION_3_1; + DBG_PRINT(SDDBG_WARN, ("MMC Spec version greater than 4.1\n")); + break; + } + } + /* re-select the card */ + status = SelectDeselectCard(pHcd,TRUE); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to re-select card after getting CSD \n")); + break; + } + } + + if ((pHcd->CardProperties.Flags & CARD_SD) && + !(pHcd->CardProperties.Flags & CARD_SDIO) && + SDDEVICE_IS_SD_REV_GTEQ_1_10(pHcd->pPseudoDev) && + (pHcd->Attributes & SDHCD_ATTRIB_SD_HIGH_SPEED) && + !spiMode) { + UINT32 arg; + PUINT8 pSwitchStatusBlock = KernelAlloc(SD_SWITCH_FUNC_STATUS_BLOCK_BYTES); + + if (NULL == pSwitchStatusBlock) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + arg = SD_SWITCH_FUNC_ARG_GROUP_CHECK(SD_SWITCH_HIGH_SPEED_GROUP, + SD_SWITCH_HIGH_SPEED_FUNC_NO); + + /* for 1.10 SD cards, check if high speed mode is supported */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Checking SD Card for switchable functions (CMD6 arg:0x%X)\n",arg)); + + /* issue simple data transfer request to read the switch status */ + status = _IssueBusRequestBd(pHcd, + CMD6, + arg, + SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_TRANS, + pReq, + pSwitchStatusBlock, + SD_SWITCH_FUNC_STATUS_BLOCK_BYTES); + + if (SDIO_SUCCESS(status)) { + UINT16 switchGroupMask; + /* need to reorder this since cards send this MSB first */ + ReorderBuffer(pSwitchStatusBlock,SD_SWITCH_FUNC_STATUS_BLOCK_BYTES); + switchGroupMask = SD_SWITCH_FUNC_STATUS_GET_GRP_BIT_MASK(pSwitchStatusBlock,SD_SWITCH_HIGH_SPEED_GROUP); + DBG_PRINT(SDDBG_TRACE, ("SD Card Switch Status Group1 Mask:0x%X Max Current:%d\n", + switchGroupMask, SD_SWITCH_FUNC_STATUS_GET_MAX_CURRENT(pSwitchStatusBlock) )); + if (SD_SWITCH_FUNC_STATUS_GET_MAX_CURRENT(pSwitchStatusBlock) == 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: SD Switch Status block has zero max current \n")); + SDLIB_PrintBuffer(pSwitchStatusBlock, + SD_SWITCH_FUNC_STATUS_BLOCK_BYTES, + "SDIO Bus Driver: SD Switch Status Block Error"); + } else { + /* check HS support */ + if (switchGroupMask & (1 << SD_SWITCH_HIGH_SPEED_FUNC_NO)) { + DBG_PRINT(SDDBG_TRACE, ("SD Card Supports High Speed Mode\n")); + /* set the rate, this will override the CSD value */ + cardReportedRate = SD_HS_MAX_BUS_CLOCK; + pBusMode->BusModeFlags |= SDCONFIG_BUS_MODE_SD_HS; + } + } + } else { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get SD Switch Status block (%d)\n", status)); + /* just fall through, we'll handle this like a normal SD card */ + status = SDIO_STATUS_SUCCESS; + } + + KernelFree(pSwitchStatusBlock); + } + + if ((pHcd->CardProperties.Flags & CARD_MMC) && + SDDEVICE_IS_MMC_REV_GTEQ_4_0(pHcd->pPseudoDev) && + (pHcd->Attributes & SDHCD_ATTRIB_MMC_HIGH_SPEED) && + !spiMode) { + /* for MMC cards, get the Extended CSD to get the High speed and + * wide bus paramaters */ + + PUINT8 pExtData = KernelAlloc(MMC_EXT_CSD_SIZE); + + if (NULL == pExtData) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + /* issue simple data transfer request to read the extended CSD */ + status = _IssueBusRequestBd(pHcd,MMC_CMD8,0, + SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_TRANS, + pReq, + pExtData, + MMC_EXT_CSD_SIZE); + if (SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_TRACE, ("MMC Ext CSD Version: 0x%X Card Type: 0x%X\n", + pExtData[MMC_EXT_VER_OFFSET],pExtData[MMC_EXT_CARD_TYPE_OFFSET])); + /* check HS support */ + if (pExtData[MMC_EXT_CARD_TYPE_OFFSET] & MMC_EXT_CARD_TYPE_HS_52) { + /* try 52 Mhz */ + cardReportedRate = 52000000; + pBusMode->BusModeFlags |= SDCONFIG_BUS_MODE_MMC_HS; + } else if (pExtData[MMC_EXT_CARD_TYPE_OFFSET] & MMC_EXT_CARD_TYPE_HS_26) { + /* try 26MHZ */ + cardReportedRate = 26000000; + pBusMode->BusModeFlags |= SDCONFIG_BUS_MODE_MMC_HS; + } else { + /* doesn't report high speed capable */ + cardReportedRate = 0; + } + + if (cardReportedRate && !spiMode) { + /* figure out the bus mode */ + if (pHcd->Attributes & SDHCD_ATTRIB_BUS_MMC8BIT) { + SDCONFIG_SET_BUS_WIDTH(pBusMode->BusModeFlags, SDCONFIG_BUS_WIDTH_MMC8_BIT); + } else if (pHcd->Attributes & SDHCD_ATTRIB_BUS_4BIT) { + SDCONFIG_SET_BUS_WIDTH(pBusMode->BusModeFlags, SDCONFIG_BUS_WIDTH_4_BIT); + } else { + /* we leave it to default to 1 bit mode */ + } + } + } else { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get MMC Extended CSD \n")); + /* just fall through, we'll do without the extended information + * and run it like a legacy MMC card */ + status = SDIO_STATUS_SUCCESS; + } + + KernelFree(pExtData); + } + + if (pHcd->CardProperties.Flags & (CARD_SD | CARD_MMC)) { + + if (0 == cardReportedRate) { + /* extract rate from CSD only if it was not set by earlier tests */ + cardReportedRate = ConvertEncodedTransSpeed( + GET_SD_CSD_TRANS_SPEED(pHcd->CardProperties.CardCSD)); + /* fall through and test for zero again */ + } + + if (cardReportedRate != 0) { + /* adjust clock based on what the card can handle */ + ADJUST_OPER_CLOCK(pBusMode,cardReportedRate); + } else { + /* something is wrong with the CSD */ + if (DBG_GET_DEBUG_LEVEL() >= SDDBG_TRACE) { + SDLIB_PrintBuffer(pHcd->CardProperties.CardCSD, + MAX_CARD_RESPONSE_BYTES, + "SDIO Bus Driver: CSD Dump"); + } + /* can't figure out the card rate, so set reasonable defaults */ + if (pHcd->CardProperties.Flags & CARD_SD) { + ADJUST_OPER_CLOCK(pBusMode,SD_MAX_BUS_CLOCK); + } else { + ADJUST_OPER_CLOCK(pBusMode,MMC_MAX_BUS_CLOCK); + } + } + } + + /* note, we do SDIO card "after" SD in case this is a combo card */ + if (pHcd->CardProperties.Flags & CARD_SDIO) { + /* read card capabilities */ + status = Cmd52ReadByteCommon(pHcd->pPseudoDev, + SDIO_CARD_CAPS_REG, + &pHcd->CardProperties.SDIOCaps); + if (!SDIO_SUCCESS(status)) { + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Card Caps: 0x%X \n",pHcd->CardProperties.SDIOCaps)); + if (pHcd->CardProperties.SDIOCaps & SDIO_CAPS_LOW_SPEED) { + /* adjust max clock for LS device */ + ADJUST_OPER_CLOCK(pBusMode,SDIO_LOW_SPEED_MAX_BUS_CLOCK); + /* adjust bus if LS device does not support 4 bit mode */ + if (!(pHcd->CardProperties.SDIOCaps & SDIO_CAPS_4BIT_LS)) { + if (!spiMode) { + /* low speed device does not support 4 bit mode, force us to 1 bit */ + SDCONFIG_SET_BUS_WIDTH(pBusMode->BusModeFlags, + SDCONFIG_BUS_WIDTH_1_BIT); + } + } + } + + /* check if 1.2 card supports high speed mode, checking HCD as well*/ + if (SDDEVICE_IS_SDIO_REV_GTEQ_1_20(pHcd->pPseudoDev) && + (pHcd->Attributes & SDHCD_ATTRIB_SD_HIGH_SPEED) && + !spiMode) { + UCHAR hsControl = 0; + + status = Cmd52ReadByteCommon(pHcd->pPseudoDev, + SDIO_HS_CONTROL_REG, + &hsControl); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_TRACE, + ("SDIO Failed to read high speed control (%d) \n",status)); + /* reset status and continue */ + status = SDIO_STATUS_SUCCESS; + } else { + if (hsControl & SDIO_HS_CONTROL_SHS) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Card Supports High Speed Mode\n")); + pBusMode->BusModeFlags |= SDCONFIG_BUS_MODE_SD_HS; + } + } + + } + + cardReportedRate = 0; + temp = sizeof(func0ext); + tplAddr = pHcd->CardProperties.CommonCISPtr; + /* get the FUNCE tuple */ + status = SDLIB_FindTuple(pHcd->pPseudoDev, + CISTPL_FUNCE, + &tplAddr, + (PUINT8)&func0ext, + &temp); + if (!SDIO_SUCCESS(status) || (temp < sizeof(func0ext))) { + DBG_PRINT(SDDBG_WARN, ("SDIO Function 0 Ext. Tuple Missing (Got size:%d) \n", temp)); + /* reset status */ + status = SDIO_STATUS_SUCCESS; + } else { + /* convert encoded value to rate */ + cardReportedRate = ConvertEncodedTransSpeed(func0ext.MaxTransSpeed); + } + + if (cardReportedRate != 0) { + if (pBusMode->BusModeFlags & SDCONFIG_BUS_MODE_SD_HS) { + if (cardReportedRate <= SD_MAX_BUS_CLOCK) { + DBG_PRINT(SDDBG_WARN, + ("SDIO Function tuple reports clock:%d Hz, with advertised High Speed support \n", cardReportedRate)); + /* back off high speed support */ + pBusMode->BusModeFlags &= ~SDCONFIG_BUS_MODE_SD_HS; + } + } else { + if (cardReportedRate > SD_MAX_BUS_CLOCK) { + DBG_PRINT(SDDBG_WARN, + ("SDIO Function tuple reports clock:%d Hz, without advertising High Speed support..using 25Mhz \n", cardReportedRate)); + cardReportedRate = SD_MAX_BUS_CLOCK; + } + } + /* adjust clock based on what the card can handle */ + ADJUST_OPER_CLOCK(pBusMode,cardReportedRate); + + } else { + /* set a reasonable default */ + ADJUST_OPER_CLOCK(pBusMode,SD_MAX_BUS_CLOCK); + } + } + } while (FALSE); + + if (pReq != NULL) { + FreeRequest(pReq); + } + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SetOperationalBusMode - set operational bus mode + Input: pDevice - pDevice that is requesting the change + pBusMode - operational bus mode + Output: pBusMode - on return will have the actual clock rate set + Return: status + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SetOperationalBusMode(PSDDEVICE pDevice, + PSDCONFIG_BUS_MODE_DATA pBusMode) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UCHAR regData; + UINT32 arg; + UINT32 switcharg; + PSDHCD pHcd = pDevice->pHcd; + + /* synchronize access for updating bus mode settings */ + status = SemaphorePendInterruptable(&pDevice->pHcd->ConfigureOpsSem); + if (!SDIO_SUCCESS(status)) { + return status; + } + + do { + + if (!IS_CARD_PRESENT(pHcd)) { + /* for an empty slot (a Pseudo dev was passed in) we still allow the + * bus mode to be set for the card detect + * polling */ + status = _IssueConfig(pHcd,SDCONFIG_BUS_MODE_CTRL,pBusMode,sizeof(SDCONFIG_BUS_MODE_DATA)); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set bus mode in hcd : Err:%d \n", + status)); + } + /* nothing more to do */ + break; + } + + + if ((pBusMode->BusModeFlags == SDDEVICE_GET_BUSMODE_FLAGS(pDevice)) && + (pBusMode->ClockRate == SDDEVICE_GET_OPER_CLOCK(pDevice))) { + DBG_PRINT(SDDBG_TRACE, + ("SDIO Bus Driver: Bus mode already set, nothing to do\n")); + pBusMode->ActualClockRate = SDDEVICE_GET_OPER_CLOCK(pDevice); + break; + } + + if (pBusMode->BusModeFlags & SDCONFIG_BUS_MODE_MMC_HS) { + if (!(pHcd->Attributes & SDHCD_ATTRIB_MMC_HIGH_SPEED)) { + status = SDIO_STATUS_INVALID_PARAMETER; + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: HCD does not support MMC High Speed\n")); + break; + } + } + + if (pBusMode->BusModeFlags & SDCONFIG_BUS_MODE_SD_HS) { + if (!(pHcd->Attributes & SDHCD_ATTRIB_SD_HIGH_SPEED)) { + status = SDIO_STATUS_INVALID_PARAMETER; + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: HCD does not support SD High Speed\n")); + break; + } + } + + /* before we set the operational clock and mode, configure the clock for high + * speed mode on the card , if necessary */ + if ((pHcd->CardProperties.Flags & CARD_MMC) && + (pBusMode->BusModeFlags & SDCONFIG_BUS_MODE_MMC_HS) && + !(SDDEVICE_GET_BUSMODE_FLAGS(pDevice) & SDCONFIG_BUS_MODE_MMC_HS)) { + + switcharg = MMC_SWITCH_BUILD_ARG(MMC_SWITCH_CMD_SET0, + MMC_SWITCH_WRITE_BYTE, + MMC_EXT_HS_TIMING_OFFSET, + MMC_EXT_HS_TIMING_ENABLE); + status = _IssueSimpleBusRequest(pHcd, + MMC_CMD_SWITCH, + switcharg, + SDREQ_FLAGS_RESP_R1B, + NULL); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: Failed to switch MMC High Speed Mode (arg:0x%X): %d \n", + switcharg, status)); + break; + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: High Speed MMC enabled (arg:0x%X)\n", + switcharg)); + } + + /* before setting bus mode and clock in the HCD, switch card to high speed mode + * if necessary */ + if ((pHcd->CardProperties.Flags & CARD_SD) && + (pBusMode->BusModeFlags & SDCONFIG_BUS_MODE_SD_HS) && + !(SDDEVICE_GET_BUSMODE_FLAGS(pDevice) & SDCONFIG_BUS_MODE_SD_HS)) { + UINT32 arg; + PUINT8 pSwitchStatusBlock; + + pSwitchStatusBlock = KernelAlloc(SD_SWITCH_FUNC_STATUS_BLOCK_BYTES); + + if (NULL == pSwitchStatusBlock) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + /* set high speed group */ + arg = SD_SWITCH_FUNC_ARG_GROUP_SET(SD_SWITCH_HIGH_SPEED_GROUP, + SD_SWITCH_HIGH_SPEED_FUNC_NO); + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Setting SD Card for High Speed mode (CMD6 arg:0x%X)\n",arg)); + + /* issue simple data transfer request to switch modes */ + status = _IssueBusRequestBd(pHcd, + CMD6, + arg, + SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_TRANS, + NULL, + pSwitchStatusBlock, + SD_SWITCH_FUNC_STATUS_BLOCK_BYTES); + + if (SDIO_SUCCESS(status)) { + ReorderBuffer(pSwitchStatusBlock,SD_SWITCH_FUNC_STATUS_BLOCK_BYTES); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD High Speed Result, Got Max Current:%d mA, SwitchResult:0x%X \n", + SD_SWITCH_FUNC_STATUS_GET_MAX_CURRENT(pSwitchStatusBlock), + SDSwitchGetSwitchResult(pSwitchStatusBlock, SD_SWITCH_HIGH_SPEED_GROUP))); + if (SD_SWITCH_FUNC_STATUS_GET_MAX_CURRENT(pSwitchStatusBlock) == 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Error in Status Block after High Speed Switch (current==0) \n")); + status = SDIO_STATUS_DEVICE_ERROR; + } + if (SDSwitchGetSwitchResult(pSwitchStatusBlock, SD_SWITCH_HIGH_SPEED_GROUP) != + SD_SWITCH_HIGH_SPEED_FUNC_NO) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: Error in Status Block after High Speed Switch (Group1 did not switch) \n")); + status = SDIO_STATUS_DEVICE_ERROR; + } + if (SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD High Speed Mode Enabled \n")); + } else { + SDLIB_PrintBuffer(pSwitchStatusBlock, + SD_SWITCH_FUNC_STATUS_BLOCK_BYTES, + "SDIO Bus Driver: SD Switch Status Block Error"); + } + } else { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to Set SD High Speed Mode (%d) \n",status)); + } + KernelFree(pSwitchStatusBlock); + + if (!SDIO_SUCCESS(status)) { + break; + } + } + + /* enable/disable high speed mode for SDIO card */ + if (pHcd->CardProperties.Flags & CARD_SDIO) { + BOOL doSet = TRUE; + + if ((pBusMode->BusModeFlags & SDCONFIG_BUS_MODE_SD_HS) && + !(SDDEVICE_GET_BUSMODE_FLAGS(pDevice) & SDCONFIG_BUS_MODE_SD_HS)) { + /* enable */ + regData = SDIO_HS_CONTROL_EHS; + } else if (!(pBusMode->BusModeFlags & SDCONFIG_BUS_MODE_SD_HS) && + (SDDEVICE_GET_BUSMODE_FLAGS(pDevice) & SDCONFIG_BUS_MODE_SD_HS)) { + /* disable */ + regData = 0; + } else { + /* do nothing */ + doSet = FALSE; + } + + if (doSet) { + status = Cmd52WriteByteCommon(pDevice, + SDIO_HS_CONTROL_REG, + ®Data); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to %s HS mode in SDIO card : Err:%d\n", + (SDIO_HS_CONTROL_EHS == regData) ? "enable":"disable" , status)); + break; + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver:SDIO Card %s for High Speed mode \n", + (SDIO_HS_CONTROL_EHS == regData) ? "enabled":"disabled" )); + } + } + } + + /* use synchronize-with-bus request version, this may have been requested by a + * function driver */ + status = SDLIB_IssueConfig(pDevice, + SDCONFIG_BUS_MODE_CTRL, + pBusMode, + sizeof(SDCONFIG_BUS_MODE_DATA)); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set bus mode in hcd : Err:%d \n", + status)); + break; + } + + /* check requested bus width against the current mode */ + if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == + SDCONFIG_GET_BUSWIDTH(pHcd->CardProperties.BusMode)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Bus mode set, no width change\n")); + break; + } + + if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_SPI) { + /* nothing more to do for SPI */ + break; + } + + /* set the bus width for SD and combo cards */ + if (pHcd->CardProperties.Flags & CARD_SD) { + if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_4_BIT) { + /* turn off card detect resistor */ + status = _IssueSimpleBusRequest(pHcd, + ACMD42, + 0, /* disable CD */ + SDREQ_FLAGS_APP_CMD | SDREQ_FLAGS_RESP_R1, + NULL); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: Failed to disable CD Res: %d \n", + status)); /* this should be okay */ + } + arg = SD_ACMD6_BUS_WIDTH_4_BIT; + } else { + /* don't need to turn off CD in 1 bit mode, just set mode */ + arg = SD_ACMD6_BUS_WIDTH_1_BIT; + + } + /* set the bus width */ + status = _IssueSimpleBusRequest(pHcd, + ACMD6, + arg, /* set bus mode */ + SDREQ_FLAGS_APP_CMD | SDREQ_FLAGS_RESP_R1, + NULL); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set bus width: %d \n", + status)); + break; + } + } + /* set bus width for SDIO cards */ + if (pHcd->CardProperties.Flags & CARD_SDIO) { + /* default */ + regData = CARD_DETECT_DISABLE | SDIO_BUS_WIDTH_1_BIT; + + if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_4_BIT) { + /* turn off card detect resistor and set buswidth */ + regData = CARD_DETECT_DISABLE | SDIO_BUS_WIDTH_4_BIT; + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Enabling 4 bit mode on card \n")); + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Enabling 1 bit mode on card \n")); + } + status = Cmd52WriteByteCommon(pDevice, + SDIO_BUS_IF_REG, + ®Data); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set bus mode in Card : Err:%d\n", + status)); + break; + } + + /* check for 4-bit interrupt detect mode */ + if ((SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_4_BIT) && + (pHcd->CardProperties.SDIOCaps & SDIO_CAPS_INT_MULTI_BLK) && + (pHcd->Attributes & SDHCD_ATTRIB_MULTI_BLK_IRQ)) { + /* enable interrupts between blocks, this doesn't actually turn on interrupts + * it merely allows interrupts to be asserted in the inter-block gap */ + pHcd->CardProperties.SDIOCaps |= SDIO_CAPS_ENB_INT_MULTI_BLK; + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: 4-Bit Multi-blk Interrupt support enabled\n")); + } else { + /* make sure this is disabled */ + pHcd->CardProperties.SDIOCaps &= ~SDIO_CAPS_ENB_INT_MULTI_BLK; + } + + status = Cmd52WriteByteCommon(pDevice, + SDIO_CARD_CAPS_REG, + &pHcd->CardProperties.SDIOCaps); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to update Card Caps register Err:%d\n", + status)); + break; + } + } + + /* set data bus width for MMC */ + if (pHcd->CardProperties.Flags & CARD_MMC) { + UINT8 buswidth = 0; + + if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_4_BIT) { + buswidth = MMC_EXT_BUS_WIDTH_4_BIT; + } else if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_MMC8_BIT) { + buswidth = MMC_EXT_BUS_WIDTH_8_BIT; + } else { + /* normal 1 bit mode .. nothing to do */ + break; + } + /* now set the bus mode on the card */ + switcharg = MMC_SWITCH_BUILD_ARG(MMC_SWITCH_CMD_SET0, + MMC_SWITCH_WRITE_BYTE, + MMC_EXT_BUS_WIDTH_OFFSET, + buswidth); + + status = _IssueSimpleBusRequest(pHcd, + MMC_CMD_SWITCH, + switcharg, + SDREQ_FLAGS_RESP_R1B, + NULL); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set MMC bus width (arg:0x%X): %d \n", + switcharg, status)); + break; + } + + if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_4_BIT) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: 4 bit MMC mode enabled (arg:0x%X) \n", + switcharg)); + } else if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_MMC8_BIT) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: 8-Bit MMC mode enabled (arg:0x%X) \n", + switcharg)); + } + } + + } while (FALSE); + + if (SDIO_SUCCESS(status)) { + /* set the operating mode */ + pHcd->CardProperties.BusMode = pBusMode->BusModeFlags; + /* set the actual clock rate */ + pHcd->CardProperties.OperBusClock = pBusMode->ActualClockRate; + } + + SemaphorePost(&pDevice->pHcd->ConfigureOpsSem); + + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + CardInitSetup - setup host for card initialization + Input: pHcd - HCD object + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS CardInitSetup(PSDHCD pHcd) +{ + SDCONFIG_INIT_CLOCKS_DATA initClocks; + SDCONFIG_BUS_MODE_DATA busMode; + UINT32 OCRvalue; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + ZERO_OBJECT(initClocks); + ZERO_OBJECT(busMode); + /* setup defaults */ + initClocks.NumberOfClocks = SDMMC_MIN_INIT_CLOCKS; + busMode.ClockRate = SD_INIT_BUS_CLOCK; + + /* check for SPI only */ + if (pHcd->Attributes & SDHCD_ATTRIB_BUS_SPI) { + /* SPI cards startup in non-CRC mode with the exception of CMD0, the + * HCDs must issue CMD0 with the correct CRC , the spec shows that a + * CMD 0 sequence is 0x40,0x00,0x00,0x00,0x00,0x95 */ + busMode.BusModeFlags = SDCONFIG_BUS_WIDTH_SPI | SDCONFIG_BUS_MODE_SPI_NO_CRC; + } + /* check if host supports 1 bit mode */ + /* TODO : if host supports power switching, we can + * could initialize cards in SPI mode first */ + if (pHcd->Attributes & SDHCD_ATTRIB_BUS_1BIT) { + busMode.BusModeFlags = SDCONFIG_BUS_WIDTH_1_BIT; + } + + /* set initial VDD, starting at the highest allowable voltage and working + * our way down */ + if (pHcd->SlotVoltageCaps & SLOT_POWER_3_3V) { + OCRvalue = SD_OCR_3_2_TO_3_3_VDD; + } else if (pHcd->SlotVoltageCaps & SLOT_POWER_3_0V) { + OCRvalue = SD_OCR_2_9_TO_3_0_VDD; + } else if (pHcd->SlotVoltageCaps & SLOT_POWER_2_8V) { + OCRvalue = SD_OCR_2_7_TO_2_8_VDD; + } else if (pHcd->SlotVoltageCaps & SLOT_POWER_2_0V) { + OCRvalue = SD_OCR_1_9_TO_2_0_VDD; + } else if (pHcd->SlotVoltageCaps & SLOT_POWER_1_8V) { + OCRvalue = SD_OCR_1_7_TO_1_8_VDD; + } else if (pHcd->SlotVoltageCaps & SLOT_POWER_1_6V) { + OCRvalue = SD_OCR_1_6_TO_1_7_VDD; + } else { + DBG_ASSERT(FALSE); + OCRvalue = 0; + } + + do { + /* power up the card */ + status = AdjustSlotPower(pHcd, &OCRvalue); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to adjust slot power \n")); + break; + } + status = SetOperationalBusMode(pHcd->pPseudoDev,&busMode); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set bus mode \n")); + break; + } + status = _IssueConfig(pHcd,SDCONFIG_SEND_INIT_CLOCKS,&initClocks,sizeof(initClocks)); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to send init clocks in hcd \n")); + break; + } + + } while(FALSE); + + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SDInitializeCard - initialize card + Input: pHcd - HCD object + Output: pProperties - card properties + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SDInitializeCard(PSDHCD pHcd) +{ + SDCONFIG_BUS_MODE_DATA busMode; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDREQUEST pReq = NULL; + UINT32 OCRvalue; + UINT32 tplAddr; + UINT8 temp; + struct SDIO_MANFID_TPL manfid; + SDCONFIG_WP_VALUE wpValue; + UINT8 cisBuffer[3]; + + OCRvalue = 0; + + do { + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Initializing card in SPI mode \n")); + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Initializing card in MMC/SD mode \n")); + } + + pReq = AllocateRequest(); + if (NULL == pReq) { + status = SDIO_STATUS_NO_RESOURCES; + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: failed to allocate bus request \n")); + break; + } + memset(pReq, 0, sizeof(SDREQUEST)); + + status = CardInitSetup(pHcd); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to setup card \n")); + break; + } + status = _IssueConfig(pHcd,SDCONFIG_GET_WP,&wpValue,sizeof(wpValue)); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: host doesn't support Write Protect \n")); + } else { + if (wpValue) { + pHcd->CardProperties.Flags |= CARD_SD_WP; + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: SD WP switch is on \n")); + } + } + + if (!(pHcd->Attributes & SDHCD_ATTRIB_SLOT_POLLING) && + IS_HCD_BUS_MODE_SPI(pHcd)) { + /* for non-slot polling HCDs operating in SPI mode + * issue CMD0 to reset card state and to place the card + * in SPI mode. If slot polling is used, the polling thread + * will have already issued a CMD0 to place the card in SPI mode*/ + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + INT ii = 256; + status = SDIO_STATUS_ERROR; + /* if the CMD0 fails, retry it. Some cards have a hard time getting into SPI mode.*/ + while ((!SDIO_SUCCESS(status)) && (ii-- >= 0)) { + status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_RESP_R1,pReq); + OSSleep(20); + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: cmd0 go SPI retries:(256) %d\n", ii)); + + } else { + status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_NO_RESP,pReq); + } + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: go-idle failed! \n")); + break; + } + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Looking for SDIO.. \n")); + /* check for SDIO card by trying to read it's OCR */ + status = ReadOCR(pHcd,CARD_SDIO,pReq,0,&OCRvalue); + if (SDIO_SUCCESS(status)) { + /* we got a response, this is an SDIO card */ + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + /* handle SPI */ + pHcd->CardProperties.IOFnCount = SPI_SDIO_R4_GET_IO_FUNC_COUNT(pReq->Response); + if (SPI_SDIO_R4_IS_MEMORY_PRESENT(pReq->Response)) { + /* flag an SD function exists */ + pHcd->CardProperties.Flags |= CARD_SD; + } + } else { + /* handle native SD */ + pHcd->CardProperties.IOFnCount = SD_SDIO_R4_GET_IO_FUNC_COUNT(pReq->Response); + if (SD_SDIO_R4_IS_MEMORY_PRESENT(pReq->Response)) { + /* flag an SD function exists */ + pHcd->CardProperties.Flags |= CARD_SD; + } + + } + if (0 == pHcd->CardProperties.IOFnCount) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SDIO Card reports no functions \n")); + status = SDIO_STATUS_DEVICE_ERROR; + pHcd->CardProperties.Flags = 0; + break; + } + pHcd->CardProperties.Flags |= CARD_SDIO; + + DBG_PRINT(SDDBG_TRACE, + ("SDIO Bus Driver: SDIO Card, Functions: %d Card Info Flags:0x%X OCR:0x%8.8X\n", + pHcd->CardProperties.IOFnCount, pHcd->CardProperties.Flags, OCRvalue)); + /* adjust slot power for this SDIO card */ + status = AdjustSlotPower(pHcd, &OCRvalue); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set power in hcd \n")); + break; + } + /* poll for SDIO card ready */ + status = PollCardReady(pHcd,OCRvalue,CARD_SDIO); + if (!SDIO_SUCCESS(status)) { + break; + } + } else if (status != SDIO_STATUS_BUS_RESP_TIMEOUT){ + /* major error in hcd, bail */ + break; + } + + /* check if this is an SDIO-only card before continuing */ + if (!(pHcd->CardProperties.Flags & CARD_SD) && (pHcd->CardProperties.Flags & CARD_SDIO)) { + /* this is an SDIO card with no memory function */ + goto prepareCard; + } + + if (!(pHcd->CardProperties.Flags & CARD_SDIO)) { + /* issue go idle only if we did not find an SDIO function in our earlier test */ + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_RESP_R1,pReq); + } else { + status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_NO_RESP,pReq); + } + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: go-idle failed! \n")); + break; + } + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Looking for SD Memory.. \n")); + /* SD Memory Card checking */ + /* test for present of SD card (stand-alone or combo card) */ + status = TestPresence(pHcd, CARD_SD, pReq); + if (SDIO_SUCCESS(status)) { + /* there is an SD Card present, could be part of a combo system */ + pHcd->CardProperties.Flags |= CARD_SD; + if (0 == OCRvalue) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD Memory card detected. \n")); + /* no OCR value on entry this is a stand-alone card, go and get it*/ + status = ReadOCR(pHcd,CARD_SD,pReq,0,&OCRvalue); + if (!SDIO_SUCCESS(status) || (OCRvalue == 0)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get OCR (status:%d) \n", + status)); + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD Card Reports OCR:0x%8.8X \n", OCRvalue)); + status = AdjustSlotPower(pHcd, &OCRvalue); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to adjust power \n")); + break; + } + } else { + DBG_ASSERT((pHcd->CardProperties.Flags & (CARD_SD | CARD_SDIO))); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SDIO Combo Card detected \n")); + } + /* poll for SD card ready */ + status = PollCardReady(pHcd,OCRvalue,CARD_SD); + if (!SDIO_SUCCESS(status)) { + /* check if this card has an SDIO function */ + if (pHcd->CardProperties.Flags & CARD_SDIO) { + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: Combo Detected but SD memory function failed \n")); + /* allow SDIO functions to load normally */ + status = SDIO_STATUS_SUCCESS; + /* remove SD flag */ + pHcd->CardProperties.Flags &= ~CARD_SD; + } else { + break; + } + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD Memory ready. \n")); + } + /* we're done, no need to check for MMC */ + goto prepareCard; + } else if (status != SDIO_STATUS_BUS_RESP_TIMEOUT){ + /* major error in hcd, bail */ + break; + } + + /* MMC card checking */ + /* if we get here, these better not be set */ + DBG_ASSERT(!(pHcd->CardProperties.Flags & (CARD_SD | CARD_SDIO))); + /* issue go idle */ + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_RESP_R1,pReq); + } else { + status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_NO_RESP,pReq); + } + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: go-idle failed! \n")); + break; + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Looking for MMC.. \n")); + status = TestPresence(pHcd, CARD_MMC, pReq); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: unknown card detected \n")); + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: MMC Card Detected \n")); + pHcd->CardProperties.Flags |= CARD_MMC; + /* read the OCR value */ + status = ReadOCR(pHcd,CARD_MMC,pReq,0,&OCRvalue); + if (!SDIO_SUCCESS(status) || (OCRvalue == 0)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Failed to get OCR (status:%d)", + status)); + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: MMC Card Reports OCR:0x%8.8X \n", OCRvalue)); + /* adjust power */ + status = AdjustSlotPower(pHcd, &OCRvalue); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to adjust power \n")); + break; + } + /* poll for MMC card ready */ + status = PollCardReady(pHcd,OCRvalue,CARD_MMC); + if (!SDIO_SUCCESS(status)) { + break; + } + /* fall through and prepare MMC card */ + +prepareCard: + /* we're done figuring out what was inserted, and setting up + * optimal slot voltage, now we need to prepare the card */ + if (!IS_HCD_BUS_MODE_SPI(pHcd) && + (pHcd->CardProperties.Flags & (CARD_SD | CARD_MMC))) { + /* non-SPI SD or MMC cards need to be moved to the "ident" state before we can get the + * RCA or select the card using the new RCA */ + status = _IssueSimpleBusRequest(pHcd,CMD2,0,SDREQ_FLAGS_RESP_R2,pReq); + if (!SDIO_SUCCESS(status)){ + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: failed to move SD/MMC card into ident state \n")); + break; + } + } + + if (!IS_HCD_BUS_MODE_SPI(pHcd)) { + /* non-SPI mode cards need their RCA's setup */ + if (pHcd->CardProperties.Flags & (CARD_SD | CARD_SDIO)) { + /* issue CMD3 to get RCA on SD/SDIO cards */ + status = _IssueSimpleBusRequest(pHcd,CMD3,0,SDREQ_FLAGS_RESP_R6,pReq); + if (!SDIO_SUCCESS(status)){ + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: failed to get RCA for SD/SDIO card \n")); + break; + } + pHcd->CardProperties.RCA = SD_R6_GET_RCA(pReq->Response); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD/SDIO RCA:0x%X \n", + pHcd->CardProperties.RCA)); + } else if (pHcd->CardProperties.Flags & CARD_MMC) { + /* for MMC cards, we have to assign a relative card address */ + /* just a non-zero number */ + pHcd->CardProperties.RCA = 1; + /* issue CMD3 to set the RCA for MMC cards */ + status = _IssueSimpleBusRequest(pHcd, + CMD3,(pHcd->CardProperties.RCA << 16), + SDREQ_FLAGS_RESP_R1,pReq); + if (!SDIO_SUCCESS(status)){ + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: failed to set RCA for MMC card! (err=%d) \n",status)); + break; + } + } else { + DBG_ASSERT(FALSE); + } + } + /* select the card in order to get the rest of the card info, applies + * to SDIO/SD/MMC cards*/ + status = SelectDeselectCard(pHcd, TRUE); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: failed to select card! \n")); + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver, Card now Selected.. \n")); + + if (pHcd->CardProperties.Flags & CARD_SDIO) { + /* read SDIO revision register */ + status = Cmd52ReadByteCommon(pHcd->pPseudoDev, CCCR_SDIO_REVISION_REG, &temp); + if (!SDIO_SUCCESS(status)) { + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Revision Reg: 0x%X \n", temp)); + switch (temp & SDIO_REV_MASK) { + case SDIO_REV_1_00: + DBG_PRINT(SDDBG_TRACE, ("SDIO Spec Revision 1.00 \n")); + pHcd->CardProperties.SDIORevision = SDIO_REVISION_1_00; + break; + case SDIO_REV_1_10: + DBG_PRINT(SDDBG_TRACE, ("SDIO Spec Revision 1.10 \n")); + pHcd->CardProperties.SDIORevision = SDIO_REVISION_1_10; + break; + case SDIO_REV_1_20: + DBG_PRINT(SDDBG_TRACE, ("SDIO Spec Revision 1.20 \n")); + pHcd->CardProperties.SDIORevision = SDIO_REVISION_1_20; + break; + default: + DBG_PRINT(SDDBG_WARN, ("SDIO Warning: unknown SDIO revision, treating like 1.0 device \n")); + pHcd->CardProperties.SDIORevision = SDIO_REVISION_1_00; + break; + } + /* get the common CIS ptr */ + status = Cmd52ReadMultipleCommon(pHcd->pPseudoDev, + SDIO_CMN_CIS_PTR_LOW_REG, + cisBuffer, + 3); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get CIS ptr, Err:%d", status)); + break; + } + /* this is endian-safe*/ + pHcd->CardProperties.CommonCISPtr = ((UINT32)cisBuffer[0]) | + (((UINT32)cisBuffer[1]) << 8) | + (((UINT32)cisBuffer[2]) << 16); + + DBG_PRINT(SDDBG_TRACE, ("SDIO Card CIS Ptr: 0x%X \n", pHcd->CardProperties.CommonCISPtr)); + temp = sizeof(manfid); + tplAddr = pHcd->CardProperties.CommonCISPtr; + /* get the MANFID tuple */ + status = SDLIB_FindTuple(pHcd->pPseudoDev, + CISTPL_MANFID, + &tplAddr, + (PUINT8)&manfid, + &temp); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: Failed to get MANFID tuple err:%d \n", status)); + status = SDIO_STATUS_SUCCESS; + } else { + /* save this off so that it can be copied into each SDIO Func's SDDEVICE structure */ + pHcd->CardProperties.SDIO_ManufacturerCode = + CT_LE16_TO_CPU_ENDIAN(manfid.ManufacturerCode); + pHcd->CardProperties.SDIO_ManufacturerID = + CT_LE16_TO_CPU_ENDIAN(manfid.ManufacturerInfo); + DBG_PRINT(SDDBG_TRACE, ("SDIO MANFID:0x%X, MANFINFO:0x%X \n", + pHcd->CardProperties.SDIO_ManufacturerID, + pHcd->CardProperties.SDIO_ManufacturerCode)); + } + + if (pHcd->CardProperties.SDIORevision >= SDIO_REVISION_1_10) { + /* read power control */ + status = Cmd52ReadByteCommon(pHcd->pPseudoDev, SDIO_POWER_CONTROL_REG, &temp); + if (SDIO_SUCCESS(status)) { + /* check for power control support which indicates the card may use more + * than 200 mA */ + if (temp & SDIO_POWER_CONTROL_SMPC) { + /* check that the host can support this. */ + if (pHcd->MaxSlotCurrent >= SDIO_EMPC_CURRENT_THRESHOLD) { + temp = SDIO_POWER_CONTROL_EMPC; + /* enable power control on the card */ + status = Cmd52WriteByteCommon(pHcd->pPseudoDev, SDIO_POWER_CONTROL_REG, &temp); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Busdriver: failed to enable power control (%d) \n",status)); + break; + } + /* mark that the card is high power */ + pHcd->CardProperties.Flags |= CARD_HIPWR; + + DBG_PRINT(SDDBG_TRACE, + ("SDIO Busdriver: Power Control Enabled on SDIO (1.10 or greater) card \n")); + } else { + DBG_PRINT(SDDBG_WARN, + ("SDIO Busdriver: Card can operate higher than 200mA, host cannot (max:%d) \n", + pHcd->MaxSlotCurrent)); + /* this is not fatal, the card should operate at a reduced rate */ + } + } else { + DBG_PRINT(SDDBG_TRACE, + ("SDIO Busdriver: SDIO 1.10 (or greater) card draws less than 200mA \n")); + } + } else { + DBG_PRINT(SDDBG_WARN, + ("SDIO Busdriver: failed to get POWER CONTROL REG (%d) \n",status)); + /* fall through and continue on at reduced mode */ + } + } + } + /* get the current bus parameters */ + busMode.BusModeFlags = pHcd->CardProperties.BusMode; + busMode.ClockRate = pHcd->CardProperties.OperBusClock; + /* get the rest of the bus parameters like clock and supported bus width */ + status = GetBusParameters(pHcd,&busMode); + if (!SDIO_SUCCESS(status)) { + break; + } + + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + /* check HCD if it wants to run without SPI CRC */ + if (pHcd->Attributes & SDHCD_ATTRIB_NO_SPI_CRC) { + /* hcd would rather not run with CRC we don't need to tell the card since SPI mode + * cards power up with CRC initially disabled */ + busMode.BusModeFlags |= SDCONFIG_BUS_MODE_SPI_NO_CRC; + } else { + /* first enable SPI CRC checking if the HCD can handle it */ + status = SDSPIModeEnableDisableCRC(pHcd->pPseudoDev, TRUE); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: Failed to set Enable SPI CRC on card \n")); + break; + } + } + } + + status = SetOperationalBusMode(pHcd->pPseudoDev, &busMode); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set operational bus mode\n")); + break; + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Oper. Mode: Clock:%d, Bus:0x%X \n", + pHcd->CardProperties.OperBusClock,pHcd->CardProperties.BusMode)); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Card in TRANS state, Ready: CardInfo Flags 0x%X \n", + pHcd->CardProperties.Flags)); + + } while (FALSE); + + if (pReq != NULL) { + FreeRequest(pReq); + } + + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SDQuerySDMMCInfo - query MMC card info + Input: pDevice - device + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SDQuerySDMMCInfo(PSDDEVICE pDevice) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDREQUEST pReq = NULL; + UINT8 CID[MAX_CSD_CID_BYTES]; + + do { + pReq = AllocateRequest(); + if (NULL == pReq) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + /* de-select the card */ + status = SelectDeselectCard(pDevice->pHcd,FALSE); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to deselect card before getting CID \n")); + break; + } + + if (SDDEVICE_IS_BUSMODE_SPI(pDevice)) { + /* in SPI mode, getting the CSD requires a data transfer */ + status = _IssueBusRequestBd(pDevice->pHcd,CMD10,0, + SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_TRANS, + pReq, + CID, + MAX_CSD_CID_BYTES); + if (SDIO_SUCCESS(status)) { + /* in SPI mode we need to reorder to the CID since SPI data comes in MSB first*/ + ReorderBuffer(CID,MAX_CSD_CID_BYTES); + } + } else { + /* get the CID */ + status = _IssueSimpleBusRequest(pDevice->pHcd, + CMD10, + (SDDEVICE_GET_CARD_RCA(pDevice) << 16), + SDREQ_FLAGS_RESP_R2, + pReq); + if (SDIO_SUCCESS(status)) { + /* extract it from the reponse */ + memcpy(CID,pReq->Response,MAX_CSD_CID_BYTES); + } + } + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_WARN, ("SDQuerySDMMCInfo: failed to get CID. \n")); + status = SDIO_STATUS_SUCCESS; + } else { + pDevice->pId[0].SDMMC_ManfacturerID = GET_SD_CID_MANFID(CID); + pDevice->pId[0].SDMMC_OEMApplicationID = GET_SD_CID_OEMID(CID); +#if DEBUG + { + char pBuf[7]; + + pBuf[0] = GET_SD_CID_PN_1(CID); + pBuf[1] = GET_SD_CID_PN_2(CID); + pBuf[2] = GET_SD_CID_PN_3(CID); + pBuf[3] = GET_SD_CID_PN_4(CID); + pBuf[4] = GET_SD_CID_PN_5(CID); + if (pDevice->pHcd->CardProperties.Flags & CARD_MMC) { + pBuf[5] = GET_SD_CID_PN_6(CID); + pBuf[6] = 0; + } else { + pBuf[5] = 0; + } + DBG_PRINT(SDDBG_TRACE, ("SDQuerySDMMCInfo: Product String: %s\n", pBuf)); + } +#endif + DBG_PRINT(SDDBG_TRACE, ("SDQuerySDMMCInfo: ManfID: 0x%X, OEMID:0x%X \n", + pDevice->pId[0].SDMMC_ManfacturerID, pDevice->pId[0].SDMMC_OEMApplicationID)); + } + /* re-select card */ + status = SelectDeselectCard(pDevice->pHcd,TRUE); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to re-select card after getting CID \n")); + break; + } + } while (FALSE); + + if (pReq != NULL) { + FreeRequest(pReq); + } + + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SDQuerySDIOInfo - query SDIO card info + Input: pDevice - the device + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SDQuerySDIOInfo(PSDDEVICE pDevice) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UINT32 faddress; + UINT8 fInfo; + UINT32 nextTpl; + UINT8 tplLength; + UINT8 cisPtrBuffer[3]; + struct SDIO_FUNC_EXT_FUNCTION_TPL_1_1 funcTuple; + + /* use the card-wide SDIO manufacturer code and ID previously read.*/ + pDevice->pId[0].SDIO_ManufacturerCode = pDevice->pHcd->CardProperties.SDIO_ManufacturerCode; + pDevice->pId[0].SDIO_ManufacturerID = pDevice->pHcd->CardProperties.SDIO_ManufacturerID; + + /* calculate function base address */ + faddress = CalculateFBROffset(SDDEVICE_GET_SDIO_FUNCNO(pDevice)); + DBG_ASSERT(faddress != 0); + + do { + status = Cmd52ReadByteCommon(pDevice, + FBR_FUNC_INFO_REG_OFFSET(faddress), + &fInfo); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get function info, Err:%d , using Class:UNKNOWN\n", status)); + fInfo = 0; + pDevice->pId[0].SDIO_FunctionClass = 0; + status = SDIO_STATUS_SUCCESS; + } else { + pDevice->pId[0].SDIO_FunctionClass = fInfo & FUNC_INFO_DEVICE_CODE_MASK; + } + + if ((FUNC_INFO_DEVICE_CODE_LAST == pDevice->pId[0].SDIO_FunctionClass) && + SDDEVICE_IS_SDIO_REV_GTEQ_1_10(pDevice)) { + /* if the device code is the last one, check for 1.1 revision and get the + * extended code */ + status = Cmd52ReadByteCommon(pDevice, + FBR_FUNC_EXT_DEVICE_CODE_OFFSET(faddress), + &(pDevice->pId[0].SDIO_FunctionClass)); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get 1.1 extended DC, Err:%d\n", + status)); + break; + } + } + + /* get the function CIS ptr */ + status = Cmd52ReadMultipleCommon(pDevice, + FBR_FUNC_CIS_LOW_OFFSET(faddress), + cisPtrBuffer, + 3); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get FN CIS ptr, Err:%d\n", status)); + break; + } + /* endian safe */ + pDevice->DeviceInfo.AsSDIOInfo.FunctionCISPtr = ((UINT32)cisPtrBuffer[0]) | + (((UINT32)cisPtrBuffer[1]) << 8) | + (((UINT32)cisPtrBuffer[2]) << 16); + + DBG_PRINT(SDDBG_TRACE, ("SDIO Function:%d, Class:%d FnCISPtr:0x%X \n", + SDDEVICE_GET_SDIO_FUNCNO(pDevice), + pDevice->pId[0].SDIO_FunctionClass,pDevice->DeviceInfo.AsSDIOInfo.FunctionCISPtr)); + + if (fInfo & FUNC_INFO_SUPPORTS_CSA_MASK) { + /* get the function CSA ptr */ + status = Cmd52ReadMultipleCommon(pDevice, + FBR_FUNC_CSA_LOW_OFFSET(faddress), + cisPtrBuffer, + 3); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get FN CSA ptr, Err:%d \n", status)); + break; + } + /* endian safe */ + pDevice->DeviceInfo.AsSDIOInfo.FunctionCSAPtr = ((UINT32)cisPtrBuffer[0]) | + (((UINT32)cisPtrBuffer[1]) << 8) | + (((UINT32)cisPtrBuffer[2]) << 16); + + } + + nextTpl = SDDEVICE_GET_SDIO_FUNC_CISPTR(pDevice); + /* look for the funce TPL */ + tplLength = sizeof(funcTuple); + /* go get the func CE tuple */ + status = SDLIB_FindTuple(pDevice, + CISTPL_FUNCE, + &nextTpl, + (PUINT8)&funcTuple, + &tplLength); + + if (!SDIO_SUCCESS(status)){ + /* handles case of bad CIS or missing tupple, allow function driver to handle */ + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: Failed to get FuncCE Tuple: %d \n", status)); + status = SDIO_STATUS_SUCCESS; + break; + } + /* set the max block size */ + pDevice->DeviceInfo.AsSDIOInfo.FunctionMaxBlockSize = + CT_LE16_TO_CPU_ENDIAN(funcTuple.CommonInfo.MaxBlockSize); + + DBG_PRINT(SDDBG_TRACE, ("SDIO Function:%d, MaxBlocks:%d \n", + SDDEVICE_GET_SDIO_FUNCNO(pDevice), + pDevice->DeviceInfo.AsSDIOInfo.FunctionMaxBlockSize)); + + /* check for MANFID function tuple (SDIO 1.1 or greater) */ + if (SDDEVICE_IS_SDIO_REV_GTEQ_1_10(pDevice)) { + struct SDIO_MANFID_TPL manfid; + nextTpl = SDDEVICE_GET_SDIO_FUNC_CISPTR(pDevice); + tplLength = sizeof(manfid); + /* get the MANFID tuple */ + status = SDLIB_FindTuple(pDevice, + CISTPL_MANFID, + &nextTpl, + (PUINT8)&manfid, + &tplLength); + if (SDIO_SUCCESS(status)) { + /* this function has a MANFID tuple */ + pDevice->pId[0].SDIO_ManufacturerCode = + CT_LE16_TO_CPU_ENDIAN(manfid.ManufacturerCode); + pDevice->pId[0].SDIO_ManufacturerID = + CT_LE16_TO_CPU_ENDIAN(manfid.ManufacturerInfo); + DBG_PRINT(SDDBG_TRACE, ("SDIO 1.1 (Function Specific) MANFID:0x%X, MANFINFO:0x%X \n", + pDevice->pId[0].SDIO_ManufacturerID, + pDevice->pId[0].SDIO_ManufacturerCode)); + } else { + DBG_PRINT(SDDBG_WARN, ("SDIO 1.1, No CISTPL_MANFID Tuple in FUNC CIS \n")); + status = SDIO_STATUS_SUCCESS; + } + } + } while (FALSE); + + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SDEnableFunction - enable function + Input: pDevice - the device/function + pEnData - enable data; + Output: + Return: status + Notes: Note, this performs synchronous calls +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SDEnableFunction(PSDDEVICE pDevice, PSDCONFIG_FUNC_ENABLE_DISABLE_DATA pEnData) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UINT8 registerValue; + UINT8 mask; + FUNC_ENABLE_TIMEOUT retry; + + /* take the configure op lock to make this atomic */ + status = SemaphorePendInterruptable(&pDevice->pHcd->ConfigureOpsSem); + if (!SDIO_SUCCESS(status)) { + return status; + } + + status = SDIO_STATUS_INVALID_PARAMETER; + do { + if (!(pDevice->pHcd->CardProperties.Flags & CARD_SDIO)){ + /* nothing to do if it's not an SDIO card */ + break; + } + + if (!((SDDEVICE_GET_SDIO_FUNCNO(pDevice) >= SDIO_FIRST_FUNCTION_NUMBER) && + (SDDEVICE_GET_SDIO_FUNCNO(pDevice) <= SDIO_LAST_FUNCTION_NUMBER))){ + DBG_ASSERT(FALSE); + break; + } + /* make sure there is a timeout value */ + if (0 == pEnData->TimeOut) { + break; + } + + mask = 1 << SDDEVICE_GET_SDIO_FUNCNO(pDevice); + /* read the enable register */ + status = Cmd52ReadByteCommon(pDevice, SDIO_ENABLE_REG, ®isterValue); + if (!SDIO_SUCCESS(status)){ + break; + } + if (pEnData->EnableFlags & SDCONFIG_ENABLE_FUNC) { + /* set the enable register bit */ + registerValue |= mask; + } else { + /* clear the bit */ + registerValue &= ~mask; + } + + DBG_PRINT(SDDBG_TRACE, + ("SDIO Bus Driver %s Function, Mask:0x%X Enable Reg Value:0x%2.2X\n", + (pEnData->EnableFlags & SDCONFIG_ENABLE_FUNC) ? "Enabling":"Disabling", + mask, + registerValue)); + + /* write it back out */ + status = Cmd52WriteByteCommon(pDevice, SDIO_ENABLE_REG, ®isterValue); + if (!SDIO_SUCCESS(status)){ + break; + } + /* now poll the ready bit until it sets or clears */ + retry = pEnData->TimeOut; + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Function Enable/Disable Polling: %d retries \n", + retry)); + while (retry) { + status = Cmd52ReadByteCommon(pDevice, SDIO_READY_REG, ®isterValue); + if (!SDIO_SUCCESS(status)){ + break; + } + if (pEnData->EnableFlags & SDCONFIG_ENABLE_FUNC) { + /* if the bit is set, the device is ready */ + if (registerValue & mask) { + /* device ready */ + break; + } + } else { + if (!(registerValue & mask)) { + /* device is no longer ready */ + break; + } + } + /* sleep before trying again */ + status = OSSleep(1); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("OSSleep Failed! \n")); + break; + } + retry--; + } + + if (0 == retry) { + status = SDIO_STATUS_FUNC_ENABLE_TIMEOUT; + break; + } + + } while (FALSE); + + SemaphorePost(&pDevice->pHcd->ConfigureOpsSem); + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SDAllocFreeSlotCurrent - allocate or free slot current + Input: pDevice - the device/function + Allocate - Allocate current, else free + pData - slotcurrent data (non-NULL if Allocate is TRUE) + Output: + Return: status + Notes: if the function returns SDIO_STATUS_NO_RESOURCES, the pData->SlotCurrent field is + updated with the available current +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SDAllocFreeSlotCurrent(PSDDEVICE pDevice, BOOL Allocate, PSDCONFIG_FUNC_SLOT_CURRENT_DATA pData) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: SDAllocFreeSlotCurrent\n")); + + /* take the configure op lock to make this atomic */ + status = SemaphorePendInterruptable(&pDevice->pHcd->ConfigureOpsSem); + if (!SDIO_SUCCESS(status)) { + return status; + } + + status = SDIO_STATUS_INVALID_PARAMETER; + do { + /* check the current budget and allocate */ + if (Allocate) { + if (0 == pData->SlotCurrent) { + /* caller must specify current requirement for the power mode */ + break; + } + if (pDevice->SlotCurrentAlloc != 0) { + /* slot current has already been allocated, caller needs to free + * first */ + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Slot Current Already allocated! \n")); + break; + } + if (((UINT32)pDevice->pHcd->SlotCurrentAllocated + (UINT32)pData->SlotCurrent) > + (UINT32)pDevice->pHcd->MaxSlotCurrent) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Slot Current Budget exceeded, Requesting: %d, Allocated already: %d, Max: %d \n", + pData->SlotCurrent, pDevice->pHcd->SlotCurrentAllocated, + pDevice->pHcd->MaxSlotCurrent)); + status = SDIO_STATUS_NO_RESOURCES; + /* return remaining */ + pData->SlotCurrent = pDevice->pHcd->MaxSlotCurrent - + pDevice->pHcd->SlotCurrentAllocated; + break; + } + /* bump up allocation */ + pDevice->pHcd->SlotCurrentAllocated += pData->SlotCurrent; + /* save this off for the call to free slot current */ + pDevice->SlotCurrentAlloc = pData->SlotCurrent; + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Slot Current Requested: %d, New Total: %d, Max: %d \n", + pData->SlotCurrent, pDevice->pHcd->SlotCurrentAllocated, + pDevice->pHcd->MaxSlotCurrent)); + + } else { + if (0 == pDevice->SlotCurrentAlloc) { + /* no allocation */ + break; + } + /* return the allocation back */ + if (pDevice->SlotCurrentAlloc <= pDevice->pHcd->SlotCurrentAllocated) { + pDevice->pHcd->SlotCurrentAllocated -= pDevice->SlotCurrentAlloc; + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Slot Current Freed: %d, New Total: %d, Max: %d \n", + pDevice->SlotCurrentAlloc, pDevice->pHcd->SlotCurrentAllocated, + pDevice->pHcd->MaxSlotCurrent)); + } else { + DBG_ASSERT(FALSE); + } + + /* make sure this is zeroed */ + pDevice->SlotCurrentAlloc = 0; + } + + status = SDIO_STATUS_SUCCESS; + + } while (FALSE); + + SemaphorePost(&pDevice->pHcd->ConfigureOpsSem); + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: SDAllocFreeSlotCurrent, %d\n", status)); + return status; +} + +static void RawHcdIrqControl(PSDHCD pHcd, BOOL Enable) +{ + SDIO_STATUS status; + SDCONFIG_SDIO_INT_CTRL_DATA irqData; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + ZERO_OBJECT(irqData); + + status = _AcquireHcdLock(pHcd); + if (!SDIO_SUCCESS(status)) { + return; + } + + do { + /* for raw devices, we simply enable/disable in the HCD only */ + if (Enable) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver (RAW) Unmasking Int \n")); + irqData.IRQDetectMode = IRQ_DETECT_RAW; + irqData.SlotIRQEnable = TRUE; + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver (RAW) Masking Int \n")); + irqData.SlotIRQEnable = FALSE; + } + + status = _IssueConfig(pHcd,SDCONFIG_SDIO_INT_CTRL, + (PVOID)&irqData, sizeof(irqData)); + + if (!SDIO_SUCCESS(status)){ + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver failed to enable/disable IRQ in (RAW) hcd :%d\n", + status)); + } + + } while (FALSE); + + status = _ReleaseHcdLock(pHcd); +} + +static void RawHcdEnableIrqPseudoComplete(PSDREQUEST pReq) +{ + if (SDIO_SUCCESS(pReq->Status)) { + RawHcdIrqControl((PSDHCD)pReq->pCompleteContext, TRUE); + } + FreeRequest(pReq); +} + +static void RawHcdDisableIrqPseudoComplete(PSDREQUEST pReq) +{ + RawHcdIrqControl((PSDHCD)pReq->pCompleteContext, FALSE); + FreeRequest(pReq); +} + +static void HcdIrqControl(PSDHCD pHcd, BOOL Enable) +{ + SDIO_STATUS status; + SDCONFIG_SDIO_INT_CTRL_DATA irqData; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + ZERO_OBJECT(irqData); + + status = _AcquireHcdLock(pHcd); + if (!SDIO_SUCCESS(status)) { + return; + } + + do { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: HcdIrqControl (%s), IrqsEnabled:0x%X \n", + Enable ? "Enable":"Disable",pHcd->IrqsEnabled )); + + if (Enable) { + irqData.SlotIRQEnable = TRUE; + } else { + irqData.SlotIRQEnable = FALSE; + } + /* setup HCD to enable/disable it's detection hardware */ + if (irqData.SlotIRQEnable) { + /* set the IRQ detection mode */ + switch (SDCONFIG_GET_BUSWIDTH(pHcd->CardProperties.BusMode)) { + case SDCONFIG_BUS_WIDTH_SPI: + irqData.IRQDetectMode = IRQ_DETECT_SPI; + break; + case SDCONFIG_BUS_WIDTH_1_BIT: + irqData.IRQDetectMode = IRQ_DETECT_1_BIT; + break; + case SDCONFIG_BUS_WIDTH_4_BIT: + irqData.IRQDetectMode = IRQ_DETECT_4_BIT; + /* check card and HCD for 4bit multi-block interrupt support */ + if ((pHcd->CardProperties.SDIOCaps & SDIO_CAPS_INT_MULTI_BLK) && + (pHcd->Attributes & SDHCD_ATTRIB_MULTI_BLK_IRQ)) { + /* note: during initialization of the card, the mult-blk IRQ support + * is enabled in card caps register */ + irqData.IRQDetectMode |= IRQ_DETECT_MULTI_BLK; + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver enabling IRQ in multi-block mode:\n")); + } + break; + default: + DBG_ASSERT(FALSE); + break; + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver enabling IRQ in HCD Mode:0x%X\n", + irqData.IRQDetectMode)); + } + + status = _IssueConfig(pHcd,SDCONFIG_SDIO_INT_CTRL, + (PVOID)&irqData, sizeof(irqData)); + if (!SDIO_SUCCESS(status)){ + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver failed to enable/disable IRQ in hcd %d\n", + status)); + } + + } while (FALSE); + + status = _ReleaseHcdLock(pHcd); +} + +static BOOL CheckWriteIntEnableSuccess(PSDREQUEST pReq) +{ + if (!SDIO_SUCCESS(pReq->Status)){ + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get write INT Enable register Err:%d\n", + pReq->Status)); + return FALSE; + } + + if (SD_R5_GET_RESP_FLAGS(pReq->Response) & SD_R5_ERRORS) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: WriteIntEnableComplete CMD52 resp error: 0x%X \n", + SD_R5_GET_RESP_FLAGS(pReq->Response))); + return FALSE; + } + + return TRUE; +} + +static void HcdIrqEnableComplete(PSDREQUEST pReq) +{ + if (CheckWriteIntEnableSuccess(pReq)) { + /* configure HCD */ + HcdIrqControl((PSDHCD)pReq->pCompleteContext, TRUE); + } + FreeRequest(pReq); +} + +static void HcdIrqDisableComplete(PSDREQUEST pReq) +{ + CheckWriteIntEnableSuccess(pReq); + HcdIrqControl((PSDHCD)pReq->pCompleteContext, FALSE); + FreeRequest(pReq); +} + +static void WriteIntEnableComplete(PSDREQUEST pReq) +{ + if (CheckWriteIntEnableSuccess(pReq)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Wrote INT Enable value:0x%X \n", + (INT)pReq->pCompleteContext)); + } + FreeRequest(pReq); +} + +static void HcdAckComplete(PSDREQUEST pReq) +{ + SDIO_STATUS status; + DBG_PRINT(SDIODBG_FUNC_IRQ, ("SDIO Bus Driver: Hcd (0x%X) Irq Ack \n", + (INT)pReq->pCompleteContext)); + /* re-arm the HCD */ + status = _IssueConfig((PSDHCD)pReq->pCompleteContext,SDCONFIG_SDIO_REARM_INT,NULL,0); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: HCD Re-Arm failed : %d\n", + status)); + } + FreeRequest(pReq); +} +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SDFunctionAckInterrupt - handle device interrupt acknowledgement + Input: pDevice - the device + Output: + Return: + Notes: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SDFunctionAckInterrupt(PSDDEVICE pDevice) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UCHAR mask; + PSDREQUEST pReq = NULL; + BOOL setHcd = FALSE; + SDIO_STATUS status2; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + pReq = AllocateRequest(); + if (NULL == pReq) { + return SDIO_STATUS_NO_RESOURCES; + } + + status = _AcquireHcdLock(pDevice->pHcd); + + if (!SDIO_SUCCESS(status)) { + FreeRequest(pReq); + return status; + } + + do { + if (!((SDDEVICE_GET_SDIO_FUNCNO(pDevice) >= SDIO_FIRST_FUNCTION_NUMBER) && + (SDDEVICE_GET_SDIO_FUNCNO(pDevice) <= SDIO_LAST_FUNCTION_NUMBER))){ + status = SDIO_STATUS_INVALID_PARAMETER; + DBG_ASSERT(FALSE); + break; + } + mask = 1 << SDDEVICE_GET_SDIO_FUNCNO(pDevice); + if (pDevice->pHcd->PendingIrqAcks & mask) { + /* clear the ack bit in question */ + pDevice->pHcd->PendingIrqAcks &= ~mask; + if (0 == pDevice->pHcd->PendingIrqAcks) { + pDevice->pHcd->IrqProcState = SDHCD_IDLE; + /* no pending acks, so re-arm if irqs are stilled enabled */ + if (pDevice->pHcd->IrqsEnabled) { + setHcd = TRUE; + /* issue pseudo request to sync this with bus requests */ + pReq->Status = SDIO_STATUS_SUCCESS; + pReq->pCompletion = HcdAckComplete; + pReq->pCompleteContext = pDevice->pHcd; + pReq->Flags = SD_PSEUDO_REQ_FLAGS; + } + } + } else { + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: AckInterrupt: no IRQ pending on Function :%d, \n", + SDDEVICE_GET_SDIO_FUNCNO(pDevice))); + } + } while (FALSE); + + status2 = ReleaseHcdLock(pDevice); + + if (pReq != NULL) { + if (SDIO_SUCCESS(status) && (setHcd)) { + /* issue request */ + IssueRequestToHCD(pDevice->pHcd,pReq); + } else { + FreeRequest(pReq); + } + } + + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SDMaskUnmaskFunctionIRQ - mask/unmask function IRQ + Input: pDevice - the device/function + MaskInt - mask interrupt + Output: + Return: status + Notes: Note, this function can be called from an ISR or completion context +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SDMaskUnmaskFunctionIRQ(PSDDEVICE pDevice, BOOL MaskInt) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UINT8 mask; + UINT8 controlVal; + BOOL setHcd; + PSDREQUEST pReq = NULL; + SDIO_STATUS status2; + + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + setHcd = FALSE; + + pReq = AllocateRequest(); + if (NULL == pReq) { + return SDIO_STATUS_NO_RESOURCES; + } + + status = _AcquireHcdLock(pDevice->pHcd); + + if (!SDIO_SUCCESS(status)) { + FreeRequest(pReq); + return status; + } + + do { + + if (pDevice->pHcd->CardProperties.Flags & CARD_RAW) { + if (!MaskInt) { + if (!pDevice->pHcd->IrqsEnabled) { + pReq->pCompletion = RawHcdEnableIrqPseudoComplete; + setHcd = TRUE; + pDevice->pHcd->IrqsEnabled = 1 << 1; + } + } else { + if (pDevice->pHcd->IrqsEnabled) { + pReq->pCompletion = RawHcdDisableIrqPseudoComplete; + setHcd = TRUE; + pDevice->pHcd->IrqsEnabled = 0; + } + } + + if (setHcd) { + /* hcd IRQ control requests must be synched with outstanding + * bus requests so we issue a pseudo bus request */ + pReq->pCompleteContext = pDevice->pHcd; + pReq->Flags = SD_PSEUDO_REQ_FLAGS; + pReq->Status = SDIO_STATUS_SUCCESS; + } else { + /* no request to submit, just free it */ + FreeRequest(pReq); + pReq = NULL; + } + /* we're done, submit the bus request if any */ + break; + } + + if (!(pDevice->pHcd->CardProperties.Flags & CARD_SDIO)){ + /* nothing to do if it's not an SDIO card */ + DBG_ASSERT(FALSE); + status = SDIO_STATUS_INVALID_PARAMETER; + break; + } + + if (!((SDDEVICE_GET_SDIO_FUNCNO(pDevice) >= SDIO_FIRST_FUNCTION_NUMBER) && + (SDDEVICE_GET_SDIO_FUNCNO(pDevice) <= SDIO_LAST_FUNCTION_NUMBER))){ + status = SDIO_STATUS_INVALID_PARAMETER; + DBG_ASSERT(FALSE); + break; + } + + mask = 1 << SDDEVICE_GET_SDIO_FUNCNO(pDevice); + if (!MaskInt) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver Unmasking Int, Mask:0x%X\n", mask)); + /* check interrupts that were enabled on entry */ + if (0 == pDevice->pHcd->IrqsEnabled) { + /* need to turn on interrupts in HCD */ + setHcd = TRUE; + /* use this completion routine */ + pReq->pCompletion = HcdIrqEnableComplete; + } + /* set the enable bit, in the shadow register */ + pDevice->pHcd->IrqsEnabled |= mask; + /* make sure control value includes the master enable */ + controlVal = pDevice->pHcd->IrqsEnabled | SDIO_INT_MASTER_ENABLE; + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver Masking Int, Mask:0x%X\n", mask)); + /* clear the bit */ + pDevice->pHcd->IrqsEnabled &= ~mask; + /* check and see if this clears all the bits */ + if (0 == pDevice->pHcd->IrqsEnabled){ + /* if none of the functions are enabled, clear this register */ + controlVal = 0; + /* disable in host */ + setHcd = TRUE; + /* use this completion routine */ + pReq->pCompletion = HcdIrqDisableComplete; + } else { + /* set control value making sure master enable is left on */ + controlVal = pDevice->pHcd->IrqsEnabled | SDIO_INT_MASTER_ENABLE; + } + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver INT_ENABLE_REG value:0x%X\n", controlVal)); + /* setup bus request to update the mask register */ + SDIO_SET_CMD52_WRITE_ARG(pReq->Argument,0,SDIO_INT_ENABLE_REG,controlVal); + pReq->Command = CMD52; + pReq->Flags = SDREQ_FLAGS_TRANS_ASYNC | SDREQ_FLAGS_RESP_SDIO_R5; + + if (setHcd) { + /* make this a barrier request and set context*/ + pReq->Flags |= SDREQ_FLAGS_BARRIER; + pReq->pCompleteContext = pDevice->pHcd; + } else { + /* does not require an update to the HCD */ + pReq->pCompleteContext = (PVOID)(UINT32)controlVal; + pReq->pCompletion = WriteIntEnableComplete; + } + + } while (FALSE); + + status2 = _ReleaseHcdLock(pDevice->pHcd); + + if (pReq != NULL) { + if (SDIO_SUCCESS(status)) { + /* issue request */ + IssueRequestToHCD(pDevice->pHcd,pReq); + } else { + FreeRequest(pReq); + } + } + + return status; +} + + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SDSPIModeEnableDisableCRC - Enable/Disable SPI Mode CRC checking + Input: pDevice - the device/function + Enable - Enable CRC + Output: + Return: status + Notes: Note, this function can be called from an ISR or completion context +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SDSPIModeEnableDisableCRC(PSDDEVICE pDevice,BOOL Enable) +{ + SDCONFIG_BUS_MODE_DATA busMode; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UINT32 cmdARG = 0; + + if (!SDDEVICE_IS_BUSMODE_SPI(pDevice)) { + return SDIO_STATUS_INVALID_PARAMETER; + } + //??we should make these atomic using a barrier + + /* get the current mode and clock */ + busMode.BusModeFlags = pDevice->pHcd->CardProperties.BusMode; + busMode.ClockRate = pDevice->pHcd->CardProperties.OperBusClock; + + if (Enable) { + /* clear the no-CRC flag */ + busMode.BusModeFlags &= ~SDCONFIG_BUS_MODE_SPI_NO_CRC; + cmdARG = SD_CMD59_CRC_ON; + } else { + busMode.BusModeFlags |= SDCONFIG_BUS_MODE_SPI_NO_CRC; + cmdARG = SD_CMD59_CRC_OFF; + } + + do { + /* issue CMD59 to turn on/off CRC */ + status = _IssueSimpleBusRequest(pDevice->pHcd, + CMD59, + cmdARG, + SDREQ_FLAGS_RESP_R1, + NULL); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed issue CMD59 (arg=0x%X) Err:%d \n", + cmdARG, status)); + break; + } + if (Enable) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: CRC Enabled in SPI mode \n")); + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: CRC Disabled in SPI mode \n")); + } + status = SetOperationalBusMode(pDevice,&busMode); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set SPI NO CRC mode in hcd : Err:%d \n", + status)); + break; + } + } while (FALSE); + + return status; +} + + +static UINT32 ConvertSPIStatusToSDCardStatus(UINT8 SpiR1, UINT8 SpiR2) +{ + UINT32 cardStatus = 0; + + if (SpiR1 != 0) { + /* convert the error */ + if (SpiR1 & SPI_CS_ERASE_RESET) { + cardStatus |= SD_CS_ERASE_RESET; + } + if (SpiR1 & SPI_CS_ILLEGAL_CMD) { + cardStatus |= SD_CS_ILLEGAL_CMD_ERR; + } + if (SpiR1 & SPI_CS_CMD_CRC_ERR) { + cardStatus |= SD_CS_PREV_CMD_CRC_ERR; + } + if (SpiR1 & SPI_CS_ERASE_SEQ_ERR) { + cardStatus |= SD_CS_ERASE_SEQ_ERR; + } + if (SpiR1 & SPI_CS_ADDRESS_ERR) { + cardStatus |= SD_CS_ADDRESS_ERR; + } + if (SpiR1 & SPI_CS_PARAM_ERR) { + cardStatus |= SD_CS_CMD_OUT_OF_RANGE; + } + } + + if (SpiR2 != 0) { + /* convert the error */ + if (SpiR2 & SPI_CS_CARD_IS_LOCKED) { + cardStatus |= SD_CS_CARD_LOCKED; + } + if (SpiR2 & SPI_CS_LOCK_UNLOCK_FAILED) { + /* this bit is shared, just set both */ + cardStatus |= (SD_CS_LK_UNLK_FAILED | SD_CS_WP_ERASE_SKIP); + } + if (SpiR2 & SPI_CS_ERROR) { + cardStatus |= SD_CS_GENERAL_ERR; + } + if (SpiR2 & SPI_CS_INTERNAL_ERROR) { + cardStatus |= SD_CS_CARD_INTERNAL_ERR; + } + if (SpiR2 & SPI_CS_ECC_FAILED) { + cardStatus |= SD_CS_ECC_FAILED; + } + if (SpiR2 & SPI_CS_WP_VIOLATION) { + cardStatus |= SD_CS_WP_ERR; + } + if (SpiR2 & SPI_CS_ERASE_PARAM_ERR) { + cardStatus |= SD_CS_ERASE_PARAM_ERR; + } + if (SpiR2 & SPI_CS_OUT_OF_RANGE) { + cardStatus |= SD_CS_CMD_OUT_OF_RANGE; + } + } + + return cardStatus; +} +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ConvertSPI_Response - filter the SPI response and convert it to an SD Response + Input: pReq - request + Output: pReq - modified response, if pRespBuffer is not NULL + pRespBuffer - converted response (optional) + Return: + Notes: This function converts a SPI response into an SD response. A caller + can supply a buffer instead. + For SPI bus operation the HCD must send the SPI response as + a stream of bytes, the highest byte contains the first received byte from the + card. This function only filters simple responses (R1 primarily). +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void ConvertSPI_Response(PSDREQUEST pReq, UINT8 *pRespBuffer) +{ + + UINT32 cardStatus; + + if (pReq->Flags & SDREQ_FLAGS_RESP_SPI_CONVERTED) { + /* already converted */ + return; + } + if (NULL == pRespBuffer) { + pRespBuffer = pReq->Response; + } + + switch (GET_SDREQ_RESP_TYPE(pReq->Flags)) { + case SDREQ_FLAGS_RESP_R1: + case SDREQ_FLAGS_RESP_R1B: + cardStatus = ConvertSPIStatusToSDCardStatus(GET_SPI_R1_RESP_TOKEN(pReq->Response), + 0); + if (CMD55 == pReq->Command) { + /* we emulate this since SPI does not have such a bit */ + cardStatus |= SD_CS_APP_CMD; + } + /* stuff the SD card status */ + SD_R1_SET_CMD_STATUS(pRespBuffer,cardStatus); + /* stuff the command */ + SD_R1_SET_CMD(pRespBuffer,pReq->Command); + pReq->Flags |= SDREQ_FLAGS_RESP_SPI_CONVERTED; + break; + case SDREQ_FLAGS_RESP_SDIO_R5: + { + UINT8 respFlags; + UINT8 readData; + + readData = GET_SPI_SDIO_R5_RESPONSE_RDATA(pReq->Response); + respFlags = GET_SPI_SDIO_R5_RESP_TOKEN(pReq->Response); + + pRespBuffer[SD_R5_RESP_FLAGS_OFFSET] = 0; + if (respFlags != 0) { + if (respFlags & SPI_R5_ILLEGAL_CMD) { + pRespBuffer[SD_R5_RESP_FLAGS_OFFSET] |= SD_R5_ILLEGAL_CMD; + } + if (respFlags & SPI_R5_CMD_CRC) { + pRespBuffer[SD_R5_RESP_FLAGS_OFFSET] |= SD_R5_RESP_CMD_ERR; + } + if (respFlags & SPI_R5_FUNC_ERR) { + pRespBuffer[SD_R5_RESP_FLAGS_OFFSET] |= SD_R5_INVALID_FUNC; + } + if (respFlags & SPI_R5_PARAM_ERR) { + pRespBuffer[SD_R5_RESP_FLAGS_OFFSET] |= SD_R5_ARG_RANGE_ERR; + } + } + /* stuff read data */ + pRespBuffer[SD_SDIO_R5_READ_DATA_OFFSET] = readData; + /* stuff the command */ + SD_R5_SET_CMD(pRespBuffer,pReq->Command); + } + pReq->Flags |= SDREQ_FLAGS_RESP_SPI_CONVERTED; + break; + case SDREQ_FLAGS_RESP_R2: + /* for CMD13 and ACMD13 , SPI uses it's own R2 response format (2 bytes) */ + /* the issue of CMD13 needs to change the response flag to R2 */ + if (CMD13 == pReq->Command) { + cardStatus = ConvertSPIStatusToSDCardStatus( + GET_SPI_R2_RESP_TOKEN(pReq->Response), + GET_SPI_R2_STATUS_TOKEN(pReq->Response)); + /* stuff the SD card status */ + SD_R1_SET_CMD_STATUS(pRespBuffer,cardStatus); + /* stuff the command */ + SD_R1_SET_CMD(pRespBuffer,pReq->Command); + pReq->Flags |= SDREQ_FLAGS_RESP_SPI_CONVERTED; + break; + } + /* no other commands should be using R2 when using SPI, if they are + * they should be bypassing the filter */ + DBG_ASSERT(FALSE); + break; + default: + /* for all others: + * + * SDREQ_FLAGS_RESP_R6 - SPI mode does not use RCA + * SDREQ_FLAGS_RESP_R3 - bus driver handles this internally + * SDREQ_FLAGS_RESP_SDIO_R4 - bus driver handles this internally + * + */ + DBG_PRINT(SDDBG_ERROR, ("ConvertSPI_Response - invalid response type:0x%2.2X", + GET_SDREQ_RESP_TYPE(pReq->Flags))); + DBG_ASSERT(FALSE); + break; + } +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Check an SD/MMC/SDIO response. + + @function name: SDIO_CheckResponse + @prototype: SDIO_STATUS SDIO_CheckResponse(PSDHCD pHcd, PSDREQUEST pReq, SDHCD_RESPONSE_CHECK_MODE CheckMode) + @category: HD_Reference + + @input: pHcd - the host controller definition structure. + @input: pReq - request containing the response + @input: CheckMode - mode + + @return: SDIO_STATUS + + @notes: Host controller drivers must call into this function to validate various command + responses before continuing with data transfers or for decoding received SPI tokens. + The CheckMode option determines the type of validation to perform. + if (CheckMode == SDHCD_CHECK_DATA_TRANS_OK) : + The host controller must check the card response to determine whether it + is safe to perform a data transfer. This API only checks commands that + involve data transfers and checks various status fields in the command response. + If the card cannot accept data, this function will return a non-successful status that + should be treated as a request failure. The host driver should complete the request with the + returned status. Host controller should only call this function in preparation for a + data transfer. + if (CheckMode == SDHCD_CHECK_SPI_TOKEN) : + This API checks the SPI token and returns a timeout status if the illegal command bit is + set. This simulates the behavior of SD 1/4 bit operation where illegal commands result in + a command timeout. A driver that supports SPI mode should pass every response to this + function to determine the appropriate error status to complete the request with. If the + API returns success, the response indicates that the card accepted the command. + + @example: Checking the response before starting the data transfer : + if (SDIO_SUCCESS(status) && (pReq->Flags & SDREQ_FLAGS_DATA_TRANS)) { + // check the response to see if we should continue with data + status = SDIO_CheckResponse(pHcd, pReq, SDHCD_CHECK_DATA_TRANS_OK); + if (SDIO_SUCCESS(status)) { + .... start data transfer phase + } else { + ... card response indicates that the card cannot handle data + // set completion status + pRequest->Status = status; + } + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + _SDIO_CheckResponse - check response on behalf of the host controller + Input: pHcd - host controller + pReq - request containing the response + CheckMode - mode + Output: + Return: status + Notes: + + CheckMode == SDHCD_CHECK_DATA_TRANS_OK : + The host controller requests a check on the response to determine whether it + is okay to perform a data transfer. This function only filters on commands that + involve data. Host controller should only call this function in preparation for a + data transfer. + + CheckMode == SDHCD_CHECK_SPI_TOKEN : + The bus driver checks the SPI token and returns a timeout status if the illegal command bit is + set. This simulates the behavior of SD native operation. + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDIO_CheckResponse(PSDHCD pHcd, PSDREQUEST pReq, SDHCD_RESPONSE_CHECK_MODE CheckMode) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + if (CheckMode == SDHCD_CHECK_DATA_TRANS_OK) { + UINT32 cardStatus; + UINT8 *pResponse; + UINT8 convertedResponse[MAX_CARD_RESPONSE_BYTES]; + + if (!(pReq->Flags & SDREQ_FLAGS_DATA_TRANS) || + (pReq->Flags & SDREQ_FLAGS_DATA_SKIP_RESP_CHK) || + (GET_SDREQ_RESP_TYPE(pReq->Flags) == SDREQ_FLAGS_NO_RESP)) { + return SDIO_STATUS_SUCCESS; + } + pResponse = pReq->Response; + /* check SPI mode */ + if (IS_HCD_BUS_MODE_SPI(pHcd)) { + if (!(pReq->Flags & SDREQ_FLAGS_RESP_SKIP_SPI_FILT)) { + /* apply conversion */ + ConvertSPI_Response(pReq, NULL); + } else { + /* temporarily convert the response, without altering the original */ + ConvertSPI_Response(pReq, convertedResponse); + /* point to the converted one */ + pResponse = convertedResponse; + } + } + + switch (GET_SDREQ_RESP_TYPE(pReq->Flags)) { + case SDREQ_FLAGS_RESP_R1: + case SDREQ_FLAGS_RESP_R1B: + cardStatus = SD_R1_GET_CARD_STATUS(pResponse); + if (!(cardStatus & + (SD_CS_ILLEGAL_CMD_ERR | SD_CS_CARD_INTERNAL_ERR | SD_CS_GENERAL_ERR))) { + /* okay for data */ + break; + } + /* figure out what it was */ + if (cardStatus & SD_CS_ILLEGAL_CMD_ERR) { + status = SDIO_STATUS_DATA_STATE_INVALID; + } else { + status = SDIO_STATUS_DATA_ERROR_UNKNOWN; + } + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Check Response Error. R1 CardStatus:0x%X \n", + cardStatus)); + break; + case SDREQ_FLAGS_RESP_SDIO_R5: + cardStatus = SD_R5_GET_RESP_FLAGS(pResponse); + if (!(cardStatus & SD_R5_CURRENT_CMD_ERRORS)){ + /* all okay */ + break; + } + + status = ConvertCMD52ResponseToSDIOStatus((UINT8)cardStatus); + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Check Response Error. R5 CardStatus:0x%X \n", + cardStatus)); + break; + default: + break; + } + + return status; + } + + { + UINT8 spiToken; + + /* handle SPI token validation */ + switch (GET_SDREQ_RESP_TYPE(pReq->Flags)) { + case SDREQ_FLAGS_RESP_R2: + spiToken = GET_SPI_R2_RESP_TOKEN(pReq->Response); + break; + case SDREQ_FLAGS_RESP_SDIO_R5: + spiToken = GET_SPI_SDIO_R5_RESP_TOKEN(pReq->Response); + break; + case SDREQ_FLAGS_RESP_R3: + spiToken = GET_SPI_R3_RESP_TOKEN(pReq->Response); + break; + case SDREQ_FLAGS_RESP_SDIO_R4: + spiToken = GET_SPI_SDIO_R4_RESP_TOKEN(pReq->Response); + break; + default: + /* all other tokesn are SPI R1 type */ + spiToken = GET_SPI_R1_RESP_TOKEN(pReq->Response); + break; + } + + if ((GET_SDREQ_RESP_TYPE(pReq->Flags) == SDREQ_FLAGS_RESP_SDIO_R5) || + (GET_SDREQ_RESP_TYPE(pReq->Flags) == SDREQ_FLAGS_RESP_SDIO_R4)) { + /* handle SDIO status tokens */ + if ((spiToken & SPI_R5_ILLEGAL_CMD) || + (spiToken & SPI_R5_CMD_CRC)) { + status = SDIO_STATUS_BUS_RESP_TIMEOUT; + } + } else { + /* handle all other status tokens */ + if ((spiToken & SPI_CS_ILLEGAL_CMD) || + (spiToken & SPI_CS_CMD_CRC_ERR)) { + status = SDIO_STATUS_BUS_RESP_TIMEOUT; + } + } + } + + return status; +} + diff --git a/drivers/sdio/stack/busdriver/sdio_bus_os.c b/drivers/sdio/stack/busdriver/sdio_bus_os.c new file mode 100644 index 00000000000..dbdb9554f81 --- /dev/null +++ b/drivers/sdio/stack/busdriver/sdio_bus_os.c @@ -0,0 +1,832 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_bus_os.c + +@abstract: Linux implementation module + +#notes: includes module load and unload functions + +@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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* debug level for this module*/ +#define DBG_DECLARE 3; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +void pnp_remove_card_device(struct pnp_dev *dev); +#include +#include +#include "_busdriver.h" + +#define DESCRIPTION "SDIO Bus Driver" +#define AUTHOR "Atheros Communications, Inc." + +/* debug print parameter */ +/* configuration and default parameters */ +static int RequestRetries = SDMMC_DEFAULT_CMD_RETRIES; +module_param(RequestRetries, int, 0644); +MODULE_PARM_DESC(RequestRetries, "number of command retries"); +static int CardReadyPollingRetry = SDMMC_DEFAULT_CARD_READY_RETRIES; +module_param(CardReadyPollingRetry, int, 0644); +MODULE_PARM_DESC(CardReadyPollingRetry, "number of card ready retries"); +static int PowerSettleDelay = SDMMC_POWER_SETTLE_DELAY; +module_param(PowerSettleDelay, int, 0644); +MODULE_PARM_DESC(PowerSettleDelay, "delay in ms for power to settle after power changes"); +static int DefaultOperClock = 52000000; +module_param(DefaultOperClock, int, 0644); +MODULE_PARM_DESC(DefaultOperClock, "maximum operational clock limit"); +static int DefaultBusMode = SDCONFIG_BUS_WIDTH_4_BIT; +module_param(DefaultBusMode, int, 0644); +MODULE_PARM_DESC(DefaultBusMode, "default bus mode: see SDCONFIG_BUS_WIDTH_xxx"); +static int RequestListSize = SDBUS_DEFAULT_REQ_LIST_SIZE; +module_param(RequestListSize, int, 0644); +MODULE_PARM_DESC(RequestListSize, ""); +static int SignalSemListSize = SDBUS_DEFAULT_REQ_SIG_SIZE; +module_param(SignalSemListSize, int, 0644); +MODULE_PARM_DESC(SignalSemListSize, ""); +static int CDPollingInterval = SDBUS_DEFAULT_CD_POLLING_INTERVAL; +module_param(CDPollingInterval, int, 0644); +MODULE_PARM_DESC(CDPollingInterval, ""); +static int DefaultOperBlockLen = SDMMC_DEFAULT_BYTES_PER_BLOCK; +module_param(DefaultOperBlockLen, int, 0644); +MODULE_PARM_DESC(DefaultOperBlockLen, "operational block length"); +static int DefaultOperBlockCount = SDMMC_DEFAULT_BLOCKS_PER_TRANS; +module_param(DefaultOperBlockCount, int, 0644); +MODULE_PARM_DESC(DefaultOperBlockCount, "operational block count"); +static int ConfigFlags = BD_DEFAULT_CONFIG_FLAGS; +module_param(ConfigFlags, int, 0644); +MODULE_PARM_DESC(ConfigFlags, "config flags"); + +static int HcdRCount = MAX_HCD_REQ_RECURSION; +module_param(HcdRCount, int, 0644); +MODULE_PARM_DESC(HcdRCount, "HCD request recursion count"); + +static void CardDetect_WorkItem( +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +void *context); +#else +struct work_struct *ignored); +#endif +static void CardDetect_TimerFunc(unsigned long Context); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +static DECLARE_WORK(CardDetectPollWork, CardDetect_WorkItem +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +, 0); +#else +); +#endif +#endif +static int RegisterDriver(PSDFUNCTION pFunction); +static int UnregisterDriver(PSDFUNCTION pFunction); + +static struct timer_list CardDetectTimer; + +#define SDDEVICE_FROM_OSDEVICE(pOSDevice) container_of(pOSDevice, SDDEVICE, Device) +#define SDFUNCTION_FROM_OSDRIVER(pOSDriver) container_of(pOSDriver, SDFUNCTION, Driver) + + +/* + * SDIO_RegisterHostController - register a host controller bus driver +*/ +SDIO_STATUS SDIO_RegisterHostController(PSDHCD pHcd) { + /* we are the exported verison, call the internal verison */ + return _SDIO_RegisterHostController(pHcd); +} + +/* + * SDIO_UnregisterHostController - unregister a host controller bus driver +*/ +SDIO_STATUS SDIO_UnregisterHostController(PSDHCD pHcd) { + /* we are the exported verison, call the internal verison */ + return _SDIO_UnregisterHostController(pHcd); +} + +/* + * SDIO_RegisterFunction - register a function driver +*/ +SDIO_STATUS SDIO_RegisterFunction(PSDFUNCTION pFunction) { + int error; + SDIO_STATUS status; + + DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - SDIO_RegisterFunction\n")); + + /* since we do PnP registration first, we need to check the version */ + if (!CHECK_FUNCTION_DRIVER_VERSION(pFunction)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: Function Major Version Mismatch (hcd = %d, bus driver = %d)\n", + GET_SDIO_STACK_VERSION_MAJOR(pFunction), CT_SDIO_STACK_VERSION_MAJOR(g_Version))); + return SDIO_STATUS_INVALID_PARAMETER; + } + + /* we are the exported verison, call the internal verison after registering with the bus + we handle probes internally to the bus driver */ + if ((error = RegisterDriver(pFunction)) < 0) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO BusDriver - SDIO_RegisterFunction, failed to register with system bus driver: %d\n", + error)); + status = OSErrorToSDIOError(error); + } else { + status = _SDIO_RegisterFunction(pFunction); + if (!SDIO_SUCCESS(status)) { + UnregisterDriver(pFunction); + } + } + + return status; +} + +/* + * SDIO_UnregisterFunction - unregister a function driver +*/ +SDIO_STATUS SDIO_UnregisterFunction(PSDFUNCTION pFunction) { + SDIO_STATUS status; + /* we are the exported verison, call the internal verison */ + status = _SDIO_UnregisterFunction(pFunction); + UnregisterDriver(pFunction); + return status; +} + +/* + * SDIO_HandleHcdEvent - tell core an event occurred +*/ +SDIO_STATUS SDIO_HandleHcdEvent(PSDHCD pHcd, HCD_EVENT Event) { + /* we are the exported verison, call the internal verison */ + DBG_PRINT(SDIODBG_HCD_EVENTS, ("SDIO Bus Driver: SDIO_HandleHcdEvent, event type 0x%X, HCD:0x%X\n", + Event, (UINT)pHcd)); + return _SDIO_HandleHcdEvent(pHcd, Event); +} + +/* get default settings */ +SDIO_STATUS _SDIO_BusGetDefaultSettings(PBDCONTEXT pBdc) +{ + /* these defaults are module params */ + pBdc->RequestRetries = RequestRetries; + pBdc->CardReadyPollingRetry = CardReadyPollingRetry; + pBdc->PowerSettleDelay = PowerSettleDelay; + pBdc->DefaultOperClock = DefaultOperClock; + pBdc->DefaultBusMode = DefaultBusMode; + pBdc->RequestListSize = RequestListSize; + pBdc->SignalSemListSize = SignalSemListSize; + pBdc->CDPollingInterval = CDPollingInterval; + pBdc->DefaultOperBlockLen = DefaultOperBlockLen; + pBdc->DefaultOperBlockCount = DefaultOperBlockCount; + pBdc->ConfigFlags = ConfigFlags; + pBdc->MaxHcdRecursion = HcdRCount; + return SDIO_STATUS_SUCCESS; +} + +static void CardDetect_TimerFunc(unsigned long Context) +{ + DBG_PRINT(SDIODBG_CD_TIMER, ("+ SDIO BusDriver Card Detect Timer\n")); + + /* timers run in an ISR context and cannot block or sleep, so we need + * to queue a work item to call the bus driver timer notification */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + if (schedule_work(&CardDetectPollWork) <= 0) { + DBG_PRINT(SDDBG_ERROR, ("Failed to queue Card Detect timer!\n")); + } +#else + CardDetect_WorkItem(NULL); +#endif + DBG_PRINT(SDIODBG_CD_TIMER, ("- SDIO BusDriver Card Detect Timer\n")); +} + +/* + * Initialize any timers we are using +*/ +SDIO_STATUS InitializeTimers(void) +{ + init_timer(&CardDetectTimer); + CardDetectTimer.function = CardDetect_TimerFunc; + CardDetectTimer.data = 0; + return SDIO_STATUS_SUCCESS; +} + +/* + * cleanup timers +*/ +SDIO_STATUS CleanupTimers(void) +{ + del_timer(&CardDetectTimer); + return SDIO_STATUS_SUCCESS; +} + + +/* + * Queue a timer, Timeout is in milliseconds +*/ +SDIO_STATUS QueueTimer(INT TimerID, UINT32 TimeOut) +{ + UINT32 delta; + + /* convert timeout to ticks */ + delta = (TimeOut * HZ)/1000; + if (delta == 0) { + delta = 1; + } + DBG_PRINT(SDIODBG_CD_TIMER, ("SDIO BusDriver - SDIO_QueueTimer System Ticks Per Sec:%d \n",HZ)); + DBG_PRINT(SDIODBG_CD_TIMER, ("SDIO BusDriver - SDIO_QueueTimer TimerID: %d TimeOut:%d MS, requires %d Ticks\n", + TimerID,TimeOut,delta)); + switch (TimerID) { + case SDIOBUS_CD_TIMER_ID: + CardDetectTimer.expires = jiffies + delta; + add_timer(&CardDetectTimer); + break; + default: + return SDIO_STATUS_INVALID_PARAMETER; + } + + return SDIO_STATUS_SUCCESS; +} + +/* check a response on behalf of the host controller, to allow it to proceed with a + * data transfer */ +SDIO_STATUS SDIO_CheckResponse(PSDHCD pHcd, PSDREQUEST pReq, SDHCD_RESPONSE_CHECK_MODE CheckMode) +{ + return _SDIO_CheckResponse(pHcd,pReq,CheckMode); +} + +/* + * CardDetect_WorkItem - the work item for handling card detect polling interrupt +*/ +static void CardDetect_WorkItem( +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +void *context) +#else +struct work_struct *ignored) +#endif +{ + /* call bus driver function */ + SDIO_NotifyTimerTriggered(SDIOBUS_CD_TIMER_ID); +} + +/* + * OS_IncHcdReference - increment host controller driver reference count +*/ +SDIO_STATUS Do_OS_IncHcdReference(PSDHCD pHcd) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + do { + if (NULL == pHcd->pModule) { + /* hcds that are 2.3 or higher should set this */ + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: HCD:%s should set module ptr!\n", + (pHcd->pName != NULL) ? pHcd->pName : "Unknown")); + break; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + if (!try_module_get(pHcd->pModule)) { + status = SDIO_STATUS_ERROR; + } +#else + if (!try_inc_mod_count(pHcd->pModule)) { + status = SDIO_STATUS_ERROR; + } +#endif + + } while (FALSE); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: HCD:%s failed to get module\n", + (pHcd->pName != NULL) ? pHcd->pName : "Unknown")); + } + + return status; +} + +/* + * OS_DecHcdReference - decrement host controller driver reference count +*/ +SDIO_STATUS Do_OS_DecHcdReference(PSDHCD pHcd) +{ + if (pHcd->pModule != NULL) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + module_put(pHcd->pModule); +#else + /* 2.4 or lower */ + __MOD_DEC_USE_COUNT(pHcd->pModule); +#endif + } + return SDIO_STATUS_SUCCESS; +} + +/****************************************************************************************/ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#include + +#if !defined(CONFIG_PNP) +#error "CONFIG_PNP not defined" +#endif + +static ULONG InUseDevices = 0; +static spinlock_t InUseDevicesLock = SPIN_LOCK_UNLOCKED; + +static const struct pnp_device_id pnp_idtable[] = { + {"SD_XXXX", 0} +}; +static int sdio_get_resources(struct pnp_dev * pDev, struct pnp_resource_table * res) +{ + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - sdio_get_resources: %s\n", + pDev->dev.bus_id)); + return 0; +} +static int sdio_set_resources(struct pnp_dev * pDev, struct pnp_resource_table * res) +{ + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - sdio_set_resources: %s\n", + pDev->dev.bus_id)); + return 0; +} + +static int sdio_disable_resources(struct pnp_dev *pDev) +{ + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - sdio_disable_resources: %s\n", + pDev->dev.bus_id)); + if (pDev != NULL) { + pDev->active = 0; + } + return 0; +} +void release(struct device * pDev) { + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - release: %s\n", + pDev->bus_id)); + return; +} +struct pnp_protocol sdio_protocol = { + .name = "SDIO", + .get = sdio_get_resources, + .set = sdio_set_resources, + .disable = sdio_disable_resources, + .dev.release = release, +}; + +/* + * driver_probe - probe for OS based driver +*/ +static int driver_probe(struct pnp_dev* pOSDevice, const struct pnp_device_id *pId) +{ + PSDDEVICE pDevice = SDDEVICE_FROM_OSDEVICE(pOSDevice); + PSDFUNCTION pFunction = pDevice->Device.dev.driver_data; + + if (pFunction == NULL) { + return -1; + } + + if (strcmp(pFunction->pName, pOSDevice->dev.driver->name) == 0) { + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - driver_probe, match: %s/%s driver: %s\n", + pOSDevice->dev.bus_id, pFunction->pName, pOSDevice->dev.driver->name)); + return 1; + } else { + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - driver_probe, no match: %s/%s driver: %s\n", + pOSDevice->dev.bus_id, pFunction->pName, pOSDevice->dev.driver->name)); + return -1; + } +/* if (pOSDevice->id != NULL) { + if (strcmp(pOSDevice->id->id, pId->id) == 0) { + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - driver_probe, match: %s/%s\n", + pOSDevice->dev.bus_id, pId->id)); + return 1; + } + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - driver_probe, did not match: %s/%s/%s\n", + pOSDevice->dev.bus_id, pId->id, pOSDevice->id->id)); + } else { + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - driver_probe, did not match: %s/%s\n", + pOSDevice->dev.bus_id, pId->id)); + } + return -1; +*/ +//?? if (pDevice->Device.dev.driver_data != NULL) { +//?? if (pDevice->Device.dev.driver_data == pFunction) { +//?? if (pDevice->Device.data != NULL) { +//?? if (pDevice->Device.data == pFunction) { +//?? DBG_PRINT(SDDBG_TRACE, +//?? ("SDIO BusDriver - driver_probe, match: %s\n", +//?? pOSDevice->dev.bus_id)); +//?? return 1; +//?? } +//?? } + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - driver_probe, match: %s\n", + pOSDevice->dev.bus_id)); + return 1; +} + +static int RegisterDriver(PSDFUNCTION pFunction) +{ + memset(&pFunction->Driver, 0, sizeof(pFunction->Driver)); + pFunction->Driver.name = pFunction->pName; + pFunction->Driver.probe = driver_probe; + pFunction->Driver.id_table = pnp_idtable; + pFunction->Driver.flags = PNP_DRIVER_RES_DO_NOT_CHANGE; + + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - SDIO_RegisterFunction, registering driver: %s\n", + pFunction->Driver.name)); + return pnp_register_driver(&pFunction->Driver); +} + +static int UnregisterDriver(PSDFUNCTION pFunction) +{ + DBG_PRINT(SDDBG_TRACE, + ("+SDIO BusDriver - UnregisterDriver, driver: %s\n", + pFunction->Driver.name)); + pnp_unregister_driver(&pFunction->Driver); + DBG_PRINT(SDDBG_TRACE, + ("-SDIO BusDriver - UnregisterDriver\n")); + return 0; +} + +/* + * OS_InitializeDevice - initialize device that will be registered +*/ +SDIO_STATUS OS_InitializeDevice(PSDDEVICE pDevice, PSDFUNCTION pFunction) +{ + struct pnp_id *pFdname; + memset(&pDevice->Device, 0, sizeof(pDevice->Device)); + pDevice->Device.dev.driver_data = (PVOID)pFunction; +//?? pDevice->Device.data = (PVOID)pFunction; +//?? pDevice->Device.dev.driver = &pFunction->Driver.driver; +//?? pDevice->Device.driver = &pFunction->Driver; +//?? pDevice->Device.dev.release = release; + /* get a unique device number, must be done with locks held */ + spin_lock(&InUseDevicesLock); + pDevice->Device.number = FirstClearBit(&InUseDevices); + SetBit(&InUseDevices, pDevice->Device.number); + spin_unlock(&InUseDevicesLock); + pDevice->Device.capabilities = PNP_REMOVABLE | PNP_DISABLE; + pDevice->Device.protocol = &sdio_protocol; + pDevice->Device.active = 1; + + pnp_init_resource_table(&pDevice->Device.res); + + pFdname = KernelAlloc(sizeof(struct pnp_id)); + + if (NULL == pFdname) { + return SDIO_STATUS_NO_RESOURCES; + } + /* set the id as slot number/function number */ + snprintf(pFdname->id, sizeof(pFdname->id), "SD_%02X%02X", + pDevice->pHcd->SlotNumber, (UINT)SDDEVICE_GET_SDIO_FUNCNO(pDevice)); + pFdname->next = NULL; + DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - OS_InitializeDevice adding id: %s\n", + pFdname->id)); + pnp_add_id(pFdname, &pDevice->Device); + + /* deal with DMA settings */ + if (pDevice->pHcd->pDmaDescription != NULL) { + pDevice->Device.dev.dma_mask = &pDevice->pHcd->pDmaDescription->Mask; + pDevice->Device.dev.coherent_dma_mask = pDevice->pHcd->pDmaDescription->Mask; + } + + return SDIO_STATUS_SUCCESS; +} + +/* + * OS_AddDevice - must be pre-initialized with OS_InitializeDevice +*/ +SDIO_STATUS OS_AddDevice(PSDDEVICE pDevice, PSDFUNCTION pFunction) +{ + int error; + DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - OS_AddDevice adding function: %s\n", + pFunction->pName)); + error = pnp_add_device(&pDevice->Device); + if (error < 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BusDriver - OS_AddDevice failed pnp_add_device: %d\n", + error)); + } + /* replace the buggy pnp's release */ + pDevice->Device.dev.release = release; + + return OSErrorToSDIOError(error); +} + +/* + * OS_RemoveDevice - unregister device with driver and bus +*/ +void OS_RemoveDevice(PSDDEVICE pDevice) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - OS_RemoveDevice \n")); + pnp_remove_card_device(&pDevice->Device); + spin_lock(&InUseDevicesLock); + ClearBit(&InUseDevices, pDevice->Device.number); + spin_unlock(&InUseDevicesLock); + + if (pDevice->Device.id != NULL) { + KernelFree(pDevice->Device.id); + pDevice->Device.id = NULL; + } +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Add OS device to bus driver. + + @function name: SDIO_BusAddOSDevice + @category: HD_Reference + + @output: pDma - descrip[tion of support DMA or NULL + @output: pDriver - assigned driver object + @output: pDevice - assigned device object + + @return: SDIO_STATUS - SDIO_STATUS_SUCCESS when successful. + + @notes: If the HCD does not register with the driver sub-system directly (like in the PCI case), + then it should register with the bus driver to obtain OS dependent device objects. + All input structures should be maintained throughout the life of the driver. + + @example: getting device objects: + typedef struct _SDHCD_DRIVER { + OS_PNPDEVICE HcdDevice; / * the OS device for this HCD * / + OS_PNPDRIVER HcdDriver; / * the OS driver for this HCD * / + SDDMA_DESCRIPTION Dma; / * driver DMA description * / + }SDHCD_DRIVER, *PSDHCD_DRIVER; + + typedef struct _SDHCD_DRIVER_CONTEXT { + PTEXT pDescription; / * human readable device decsription * / + SDLIST DeviceList; / * the list of current devices handled by this driver * / + OS_SEMAPHORE DeviceListSem; / * protection for the DeviceList * / + UINT DeviceCount; / * number of devices currently installed * / + SDHCD_DRIVER Driver; / * OS dependent driver specific info * / + }SDHCD_DRIVER_CONTEXT, *PSDHCD_DRIVER_CONTEXT; + + static SDHCD_DRIVER_CONTEXT HcdContext = { + .pDescription = DESCRIPTION, + .DeviceCount = 0, + .Driver.HcdDevice.name = "sdio_xxx_hcd", + .Driver.HcdDriver.name = "sdio_xxx_hcd", + } + ..... + status = SDIO_BusAddOSDevice(NULL, &HcdContext.Driver, &HcdContext.Device); + if (SDIO_SUCCESS(status) { + return Probe(&HcdContext.Device); + } + return SDIOErrorToOSError(status); + + @see also: SDIO_BusRemoveOSDevice + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SDIO_BusAddOSDevice(PSDDMA_DESCRIPTION pDma, POS_PNPDRIVER pDriver, POS_PNPDEVICE pDevice) +{ + int err; + struct pnp_id *pFdname; + struct pnp_device_id *pFdid; + static int slotNumber = 0; /* we just use an increasing count for the slots number */ + + if (pDma != NULL) { + pDevice->dev.dma_mask = &pDma->Mask; + pDevice->dev.coherent_dma_mask = pDma->Mask; + } + DBG_PRINT(SDDBG_ERROR, + ("SDIO BusDriver - SDIO_GetBusOSDevice, registering driver: %s DMAmask: 0x%x\n", + pDriver->name, (UINT)*pDevice->dev.dma_mask)); + pFdid = KernelAlloc(sizeof(struct pnp_device_id)*2); + /* set the id as slot number/function number */ + snprintf(pFdid[0].id, sizeof(pFdid[0].id), "SD_%02X08", + slotNumber++); + pFdid[0].driver_data = 0; + pFdid[1].id[0] = '\0'; + pFdid[1].driver_data = 0; + + pDriver->id_table = pFdid; + pDriver->flags = PNP_DRIVER_RES_DO_NOT_CHANGE; + err = pnp_register_driver(pDriver); + if (err < 0) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO BusDriver - SDIO_GetBusOSDevice, failed registering driver: %s, err: %d\n", + pDriver->name, err)); + return OSErrorToSDIOError(err); + } + + pDevice->protocol = &sdio_protocol; + pDevice->capabilities = PNP_REMOVABLE | PNP_DISABLE; + pDevice->active = 1; + + pFdname = KernelAlloc(sizeof(struct pnp_id)); + /* set the id as slot number/function number */ + snprintf(pFdname->id, sizeof(pFdname->id), "SD_%02X08", + 0); //??pDevice->pHcd->SlotNumber);//?????fix this, slotnumber isn't vaialble yet + pFdname->next = NULL; + pnp_add_id(pFdname, pDevice); + + /* get a unique device number */ + spin_lock(&InUseDevicesLock); + pDevice->number = FirstClearBit(&InUseDevices); + SetBit(&InUseDevices, pDevice->number); + spin_unlock(&InUseDevicesLock); + pnp_init_resource_table(&pDevice->res); + err = pnp_add_device(pDevice); + if (err < 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO BusDriver - SDIO_GetBusOSDevice failed pnp_device_add: %d\n", + err)); + pnp_unregister_driver(pDriver); + } + /* replace the buggy pnp's release */ + pDevice->dev.release = release; + return OSErrorToSDIOError(err); +} + +/**+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Return OS device from bus driver. + + @function name: SDIO_BusRemoveOSDevice + @category: HD_Reference + + @input: pDriver - setup PNP driver object + @input: pDevice - setup PNP device object + + @return: none + + + @example: returning device objects: + SDIO_BusRemoveOSDevice(&HcdContext.Driver, &HcdContext.Device); + + + @see also: SDIO_BusAddOSDevice + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void SDIO_BusRemoveOSDevice(POS_PNPDRIVER pDriver, POS_PNPDEVICE pDevice) +{ + DBG_PRINT(SDDBG_ERROR, + ("SDIO BusDriver - SDIO_PutBusOSDevice, unregistering driver: %s\n", + pDriver->name)); + + pnp_remove_card_device(pDevice); + if (pDevice->id != NULL) { + KernelFree(pDevice->id); + pDevice->id = NULL; + } + + spin_lock(&InUseDevicesLock); + ClearBit(&InUseDevices, pDevice->number); + spin_unlock(&InUseDevicesLock); + + pnp_unregister_driver(pDriver); + if (pDriver->id_table != NULL) { + KernelFree((void *)pDriver->id_table); + pDriver->id_table = NULL; + } + +} + + +/* + * module init +*/ +static int __init sdio_busdriver_init(void) { + SDIO_STATUS status; + int error; + REL_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: loaded\n")); + if (!SDIO_SUCCESS((status = _SDIO_BusDriverInitialize()))) { + return SDIOErrorToOSError(status); + } + /* register the sdio bus */ + error = pnp_register_protocol(&sdio_protocol); + if (error < 0) { + REL_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: failed to register bus device, %d\n", error)); + _SDIO_BusDriverCleanup(); + return error; + } + return 0; +} + +/* + * module cleanup +*/ +static void __exit sdio_busdriver_cleanup(void) { + REL_PRINT(SDDBG_TRACE, ("SDIO unloaded\n")); + _SDIO_BusDriverCleanup(); + pnp_unregister_protocol(&sdio_protocol); +DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - unloaded 1\n")); +} +EXPORT_SYMBOL(SDIO_BusAddOSDevice); +EXPORT_SYMBOL(SDIO_BusRemoveOSDevice); + +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) + /* 2.4 */ +static int RegisterDriver(PSDFUNCTION pFunction) +{ + return 0; +} + +static int UnregisterDriver(PSDFUNCTION pFunction) +{ + DBG_PRINT(SDDBG_TRACE, + ("+-SDIO BusDriver - UnregisterDriver, driver: \n")); + return 0; +} + +/* + * OS_InitializeDevice - initialize device that will be registered +*/ +SDIO_STATUS OS_InitializeDevice(PSDDEVICE pDevice, PSDFUNCTION pFunction) +{ + return SDIO_STATUS_SUCCESS; +} + +/* + * OS_AddDevice - must be pre-initialized with OS_InitializeDevice +*/ +SDIO_STATUS OS_AddDevice(PSDDEVICE pDevice, PSDFUNCTION pFunction) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - OS_AddDevice adding function: %s\n", + pFunction->pName)); + return SDIO_STATUS_SUCCESS; + +} + +/* + * OS_RemoveDevice - unregister device with driver and bus +*/ +void OS_RemoveDevice(PSDDEVICE pDevice) +{ + DBG_PRINT(SDDBG_TRACE, ("SDIO BusDriver - OS_RemoveDevice \n")); +} + +/* + * module init +*/ +static int __init sdio_busdriver_init(void) { + SDIO_STATUS status; + REL_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: loaded\n")); + if (!SDIO_SUCCESS((status = _SDIO_BusDriverInitialize()))) { + return SDIOErrorToOSError(status); + } + return 0; +} + +/* + * module cleanup +*/ +static void __exit sdio_busdriver_cleanup(void) { + REL_PRINT(SDDBG_TRACE, ("SDIO unloaded\n")); + _SDIO_BusDriverCleanup(); +} +#else ////KERNEL_VERSION +#error "unsupported kernel version: "UTS_RELEASE +#endif //KERNEL_VERSION + +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION(DESCRIPTION); +MODULE_AUTHOR(AUTHOR); + +module_init(sdio_busdriver_init); +module_exit(sdio_busdriver_cleanup); +EXPORT_SYMBOL(SDIO_RegisterHostController); +EXPORT_SYMBOL(SDIO_UnregisterHostController); +EXPORT_SYMBOL(SDIO_HandleHcdEvent); +EXPORT_SYMBOL(SDIO_CheckResponse); +EXPORT_SYMBOL(SDIO_RegisterFunction); +EXPORT_SYMBOL(SDIO_UnregisterFunction); diff --git a/drivers/sdio/stack/busdriver/sdio_function.c b/drivers/sdio/stack/busdriver/sdio_function.c new file mode 100644 index 00000000000..78b8e17999a --- /dev/null +++ b/drivers/sdio/stack/busdriver/sdio_function.c @@ -0,0 +1,715 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_function.c + +@abstract: OS independent bus driver support for function drivers + +@notes: This file supports the interface between SDIO function drivers and the bus driver. + +@notice: Copyright (c), 2004-2005 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 of 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" + +static SDIO_STATUS ProbeForDevice(PSDFUNCTION pFunction); + +#ifdef CT_MAN_CODE_CHECK +static UINT16 ManCodeCheck = CT_MAN_CODE_CHECK; +#endif + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Register a function driver with the bus driver. + + @function name: SDIO_RegisterFunction + @prototype: SDIO_STATUS SDIO_RegisterFunction(PSDFUNCTION pFunction) + @category: PD_Reference + @input: pFunction - the function definition structure. + + @output: none + + @return: SDIO_STATUS - SDIO_STATUS_SUCCESS when succesful. + + @notes: Each function driver must register with the bus driver once upon loading. + The calling function must be prepared to receive a Probe callback before + this function returns. This will occur when an perpheral device is already + pluugged in that is supported by this function. + The function driver should unregister itself when exiting. + The bus driver checks for possible function drivers to support a device + in reverse registration order. + + @example: Registering a function driver: + //list of devices supported by this function driver + static SD_PNP_INFO Ids[] = { + {.SDIO_ManufacturerID = 0xaa55, + .SDIO_ManufacturerCode = 0x5555, + .SDIO_FunctionNo = 1}, + {} //list is null termintaed + }; + static GENERIC_FUNCTION_CONTEXT FunctionContext = { + .Function.pName = "sdio_generic", //name of the device + .Function.Version = CT_SDIO_STACK_VERSION_CODE, // set stack version + .Function.MaxDevices = 1, //maximum number of devices supported by this driver + .Function.NumDevices = 0, //current number of devices, always zero to start + .Function.pIds = Ids, //the list of devices supported by this device + .Function.pProbe = Probe, //pointer to the function drivers Probe function + // that will be called when a possibly supported device + // is inserted. + .Function.pRemove = Remove, //pointer to the function drivers Remove function + / that will be called when a device is removed. + .Function.pContext = &FunctionContext, //data value that will be passed into Probe and + // Remove callbacks. + }; + SDIO_STATUS status; + status = SDIO_RegisterFunction(&FunctionContext.Function) + if (!SDIO_SUCCESS(status)) { + ...failed to register + } + + @see also: SDIO_UnregisterFunction + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDIO_RegisterFunction(PSDFUNCTION pFunction) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + +#ifdef CT_MAN_CODE_CHECK + DBG_PRINT(SDDBG_TRACE, + ("SDIO Bus Driver: _SDIO_RegisterFunction: WARNING, this version is locked to Memory cards and SDIO cards with JEDEC IDs of: 0x%X\n", + ManCodeCheck)); +#else + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: _SDIO_RegisterFunction\n")); +#endif + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: Function Driver Stack Version: %d.%d \n", + GET_SDIO_STACK_VERSION_MAJOR(pFunction),GET_SDIO_STACK_VERSION_MINOR(pFunction))); + + if (!CHECK_FUNCTION_DRIVER_VERSION(pFunction)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Bus Driver: Function Major Version Mismatch (hcd = %d, bus driver = %d)\n", + GET_SDIO_STACK_VERSION_MAJOR(pFunction), CT_SDIO_STACK_VERSION_MAJOR(g_Version))); + return SDIO_STATUS_INVALID_PARAMETER; + } + + + /* sanity check the driver */ + if ((pFunction == NULL) || + (pFunction->pProbe == NULL) || + (pFunction->pIds == NULL)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_RegisterFunction, invalid registration data\n")); + return SDIO_STATUS_INVALID_PARAMETER; + } + /* protect the function list and add the function */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + SignalInitialize(&pFunction->CleanupReqSig); + SDLIST_INIT(&pFunction->DeviceList); + SDListAdd(&pBusContext->FunctionList, &pFunction->SDList); + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + + /* see if we have devices for this new function driver */ + ProbeForDevice(pFunction); + + return status; +cleanup: + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: _SDIO_RegisterFunction, error exit 0x%X\n", status)); + return status; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Unregister a function driver with the bus driver. + + @function name: SDIO_UnregisterFunction + @prototype: SDIO_STATUS SDIO_UnregisterFunction(PSDFUNCTION pFunction) + @category: PD_Reference + + @input: pFunction - the function definition structure. + + @output: none + + @return: SDIO_STATUS - SDIO_STATUS_SUCCESS when succesful. + + @notes: Each function driver must unregister from the bus driver when the function driver + exits. + A function driver must disconnect from any interrupts before calling this function. + + @example: Unregistering a function driver: + SDIO_UnregisterFunction(&FunctionContext.Function); + + @see also: SDIO_RegisterFunction + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDIO_UnregisterFunction(PSDFUNCTION pFunction) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDDEVICE pDevice; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: _SDIO_UnregisterFunction\n")); + + /* protect the function list and synchronize with Probe() and Remove()*/ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + /* remove this function from the function list */ + SDListRemove(&pFunction->SDList); + /* now remove this function as the handler for any of its devices */ + SDITERATE_OVER_LIST_ALLOW_REMOVE(&pFunction->DeviceList, pDevice, SDDEVICE,FuncListLink) { + if (pDevice->pFunction == pFunction) { + /* notify removal */ + NotifyDeviceRemove(pDevice); + } + }SDITERATE_END; + + SignalDelete(&pFunction->CleanupReqSig); + + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: _SDIO_UnregisterFunction\n")); + return status; + +cleanup: + DBG_PRINT(SDDBG_ERROR, ("-SDIO Bus Driver: _SDIO_UnregisterFunction, error exit 0x%X\n", status)); + return status; +} + +/* documentation headers only for Probe and Remove */ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: This function is called by the Busdriver when a device is inserted that can be supported by this function driver. + + @function name: Probe + @prototype: BOOL (*pProbe)(struct _SDFUNCTION *pFunction, struct _SDDEVICE *pDevice) + @category: PD_Reference + + @input: pFunction - the function definition structure that was passed to Busdriver + via the SDIO_RegisterFunction. + @input: pDevice - the description of the newly inserted device. + + @output: none + + @return: TRUE - this function driver will suport this device + FALSE - this function driver will not support this device + + @notes: The Busdriver calls the Probe function of a function driver to inform it that device is + available for the function driver to control. The function driver should initialize the + device and be pepared to acceopt any interrupts from the device before returning. + + @example: Example of typical Probe function callback: + static BOOL Probe(PSDFUNCTION pFunction, PSDDEVICE pDevice) { + ...get the our context info passed into the SDIO_RegisterFunction + PSDXXX_DRIVER_CONTEXT pFunctionContext = + (PSDXXX_DRIVER_CONTEXT)pFunction->pContext; + SDIO_STATUS status; + //test the identification of this device and ensure we want to support it + // we can test based on class, or use more specific tests on SDIO_ManufacturerID, etc. + if (pDevice->pId[0].SDIO_FunctionClass == XXX) { + DBG_PRINT(SDDBG_TRACE, ("SDIO XXX Function: Probe - card matched (0x%X/0x%X/0x%X)\n", + pDevice->pId[0].SDIO_ManufacturerID, + pDevice->pId[0].SDIO_ManufacturerCode, + pDevice->pId[0].SDIO_FunctionNo)); + ... + + @see also: SDIO_RegisterFunction + @see also: Remove + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +BOOL FilterPnpInfo(PSDDEVICE pDevice) +{ +#ifdef CT_MAN_CODE_CHECK + if (pDevice->pId[0].CardFlags & CARD_SDIO) { + if (pDevice->pId[0].SDIO_ManufacturerCode != ManCodeCheck) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO Card with JEDEC ID:0x%X , not Allowed! Driver check halted. " + "Please Contact sales@codetelligence.com.\n", + pDevice->pId[0].SDIO_ManufacturerCode)); + return FALSE; + } + } + return TRUE; +#else + return TRUE; +#endif +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: This function is called by the Busdriver when a device controlled by this function + function driver is removed. + + @function name: Remove + @prototype: void (*pRemove)(struct _SDFUNCTION *pFunction, struct _SDDEVICE *pDevice) + @category: PD_Reference + + @input: pFunction - the function definition structure that was passed to Busdriver + via the SDIO_RegisterFunction. + @input: pDevice - the description of the device being removed. + + @output: none + + @return: none + + @notes: The Busdriver calls the Remove function of a function driver to inform it that device it + was supporting has been removed. The device has already been removed, so no further I/O + to the device can be performed. + + @example: Example of typical Remove function callback: + void Remove(PSDFUNCTION pFunction, PSDDEVICE pDevice) { + // get the our context info passed into the SDIO_RegisterFunction + PSDXXX_DRIVER_CONTEXT pFunctionContext = + (PSDXXX_DRIVER_CONTEXT)pFunction->pContext; + ...free any acquired resources. + + @see also: SDIO_RegisterFunction + @see also: Probe + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/* + * ProbeForFunction - look for a function driver to handle this card + * +*/ +SDIO_STATUS ProbeForFunction(PSDDEVICE pDevice, PSDHCD pHcd) { + SDIO_STATUS status; + PSDLIST pList; + PSDFUNCTION pFunction; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: ProbeForFunction\n")); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForFunction - Dump of Device PNP Data: \n")); + DBG_PRINT(SDDBG_TRACE, (" Card Flags 0x%X \n", pDevice->pId[0].CardFlags)); + if (pDevice->pId[0].CardFlags & CARD_SDIO) { + DBG_PRINT(SDDBG_TRACE, (" SDIO MANF: 0x%X \n", pDevice->pId[0].SDIO_ManufacturerID)); + DBG_PRINT(SDDBG_TRACE, (" SDIO MANFCODE: 0x%X \n", pDevice->pId[0].SDIO_ManufacturerCode)); + DBG_PRINT(SDDBG_TRACE, (" SDIO FuncNo: %d \n", pDevice->pId[0].SDIO_FunctionNo)); + DBG_PRINT(SDDBG_TRACE, (" SDIO FuncClass: %d \n", pDevice->pId[0].SDIO_FunctionClass)); + } + if (pDevice->pId[0].CardFlags & (CARD_MMC | CARD_SD)) { + DBG_PRINT(SDDBG_TRACE, (" SDMMC MANFID: 0x%X \n",pDevice->pId[0].SDMMC_ManfacturerID)); + DBG_PRINT(SDDBG_TRACE, (" SDMMC OEMID: 0x%X \n",pDevice->pId[0].SDMMC_OEMApplicationID)); + } + + if (!FilterPnpInfo(pDevice)) { + status = SDIO_STATUS_SUCCESS; + goto cleanup; + } + + /* protect the function list */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + + /* protect against ProbeForDevice */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->DeviceListSem)))) { + /* release the function list semaphore we just took */ + SemaphorePost(&pBusContext->FunctionListSem); + goto cleanup; + } + + if (pDevice->pFunction != NULL) { + /* device already has a function driver handling it */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForFunction, device already has function\n")); + /* release function list */ + SemaphorePost(&pBusContext->DeviceListSem); + /* release function list */ + SemaphorePost(&pBusContext->FunctionListSem); + /* just return success */ + status = SDIO_STATUS_SUCCESS; + goto cleanup; + } + + /* release device list */ + SemaphorePost(&pBusContext->DeviceListSem); + + /* walk functions looking for one that can handle this device */ + SDITERATE_OVER_LIST(&pBusContext->FunctionList, pList) { + pFunction = CONTAINING_STRUCT(pList, SDFUNCTION, SDList); + if (pFunction->NumDevices >= pFunction->MaxDevices) { + /* function can't support any more devices */ + continue; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForFunction - checking: %s \n", + pFunction->pName)); + + /* see if this function handles this device */ + if (IsPotentialIdMatch(pDevice->pId, pFunction->pIds)) { + if (!FilterPnpInfo(pDevice)) { + break; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForFunction -Got Match, probing: %s \n", + pFunction->pName)); + /* we need to setup with the OS bus driver before the probe, so probe can + do OS operations. */ + OS_InitializeDevice(pDevice, pFunction); + if (!SDIO_SUCCESS(OS_AddDevice(pDevice, pFunction))) { + break; + } + /* close enough match, ask the function driver if it supports us */ + if (pFunction->pProbe(pFunction, pDevice)) { + /* she accepted the device, add to list */ + pDevice->pFunction = pFunction; + SDListAdd(&pFunction->DeviceList, &pDevice->FuncListLink); + pFunction->NumDevices++; + break; + } else { + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: %s did not claim the device \n", + pFunction->pName)); + /* didn't take this device */ + OS_RemoveDevice(pDevice); + } + + } + } + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: ProbeForFunction\n")); + return status; +cleanup: + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: ProbeForFunction, error exit 0x%X\n", status)); + return status; +} + +/* + * ProbeForDevice - look for a device that this function driver supports + * +*/ +static SDIO_STATUS ProbeForDevice(PSDFUNCTION pFunction) { + SDIO_STATUS status; + PSDLIST pList; + PSDDEVICE pDevice; + + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForDevice\n")); + if (pFunction->NumDevices >= pFunction->MaxDevices) { + /* function can't support any more devices */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForDevice, too many devices in function\n")); + return SDIO_STATUS_SUCCESS; + } + + /* protect the driver list */ + if (!SDIO_SUCCESS((status = SemaphorePendInterruptable(&pBusContext->DeviceListSem)))) { + goto cleanup; /* wait interrupted */ + } + /* walk device list */ + SDITERATE_OVER_LIST(&pBusContext->DeviceList, pList) { + pDevice = CONTAINING_STRUCT(pList, SDDEVICE, SDList); + if (pDevice->pFunction != NULL) { + /* device already has a function driver handling it */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForDevice, device already has function\n")); + continue; + } + /* see if this function handles this device */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: ProbeForDevice, matching ID:%d %d class:%d\n", + pDevice->pId[0].SDIO_ManufacturerID, + pDevice->pId[0].SDIO_FunctionNo, + pDevice->pId[0].SDIO_FunctionClass)); + if (IsPotentialIdMatch(pDevice->pId, pFunction->pIds)) { + if (!FilterPnpInfo(pDevice)) { + break; + } + /* we need to setup with the OS bus driver before the probe, so probe can + do OS operations. */ + OS_InitializeDevice(pDevice, pFunction); + if (!SDIO_SUCCESS(OS_AddDevice(pDevice, pFunction))) { + break; + } + /* close enough match, ask the function driver if it supports us */ + if (pFunction->pProbe(pFunction, pDevice)) { + /* she accepted the device, add to list */ + pDevice->pFunction = pFunction; + SDListAdd(&pFunction->DeviceList, &pDevice->FuncListLink); + pFunction->NumDevices++; + break; + } else { + DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: %s did not claim the device \n", + pFunction->pName)); + /* didn't take this device */ + OS_RemoveDevice(pDevice); + } + } + } + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->DeviceListSem)))) { + goto cleanup; /* wait interrupted */ + } + + return status; +cleanup: + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: ProbeForDevice, error exit 0x%X\n", status)); + return status; +} + +#if 0 +static void DumpPnpEntry(PSD_PNP_INFO pInfo) +{ + DBG_PRINT(SDDBG_TRACE, ("Function PnpInfo Dump: \n")); + DBG_PRINT(SDDBG_TRACE, (" Card Flags 0x%X \n", pInfo->CardFlags)); + DBG_PRINT(SDDBG_TRACE, (" SDIO MANF: 0x%X \n", pInfo->SDIO_ManufacturerID)); + DBG_PRINT(SDDBG_TRACE, (" SDIO MANFCODE: 0x%X \n", pInfo->SDIO_ManufacturerCode)); + DBG_PRINT(SDDBG_TRACE, (" SDIO FuncNo: %d \n", pInfo->SDIO_FunctionNo)); + DBG_PRINT(SDDBG_TRACE, (" SDIO FuncClass: %d \n", pInfo->SDIO_FunctionClass)); + DBG_PRINT(SDDBG_TRACE, (" SDMMC MANFID: 0x%X \n", pInfo->SDMMC_ManfacturerID)); + DBG_PRINT(SDDBG_TRACE, (" SDMMC OEMID: 0x%X \n", pInfo->SDMMC_OEMApplicationID)); +} +#endif +/* + * IsPotentialIdMatch - test for potential device match + * +*/ +BOOL IsPotentialIdMatch(PSD_PNP_INFO pIdsDev, PSD_PNP_INFO pIdsFuncList) { + PSD_PNP_INFO pTFn; + BOOL match = FALSE; + + for (pTFn = pIdsFuncList;!IS_LAST_SDPNPINFO_ENTRY(pTFn);pTFn++) { + //DumpPnpEntry(pTFn); + /* check specific SDIO Card manufacturer ID, Code and Function number */ + if ((pIdsDev->SDIO_ManufacturerID != 0) && + (pTFn->SDIO_ManufacturerID != 0) && + (pIdsDev->SDIO_ManufacturerID == pTFn->SDIO_ManufacturerID) && + (pIdsDev->SDIO_ManufacturerCode == pTFn->SDIO_ManufacturerCode) && + ((pIdsDev->SDIO_FunctionNo == pTFn->SDIO_FunctionNo) || + (pTFn->SDIO_FunctionNo == 0)) ) { + match = TRUE; + break; + } + /* check generic function class */ + if ((pIdsDev->SDIO_FunctionClass != 0) && + (pTFn->SDIO_FunctionClass != 0) && + (pIdsDev->SDIO_FunctionClass == pTFn->SDIO_FunctionClass)) { + match = TRUE; + break; + } + /* check specific SDMMC MANFID and APPLICATION ID, NOTE SANDISK + * uses a MANFID of zero! */ + if ((pTFn->SDMMC_OEMApplicationID != 0) && + (pIdsDev->SDMMC_ManfacturerID == pTFn->SDMMC_ManfacturerID) && + (pIdsDev->SDMMC_OEMApplicationID == pTFn->SDMMC_OEMApplicationID)) { + match = TRUE; + break; + } + + /* check generic SD Card */ + if ((pIdsDev->CardFlags & CARD_SD) && + (pTFn->CardFlags & CARD_SD)){ + match = TRUE; + break; + } + + /* check generic MMC Card */ + if ((pIdsDev->CardFlags & CARD_MMC) && + (pTFn->CardFlags & CARD_MMC)){ + match = TRUE; + break; + } + + /* check raw Card */ + if ((pIdsDev->CardFlags & CARD_RAW) && + (pTFn->CardFlags & CARD_RAW)){ + match = TRUE; + break; + } + } + + return match; +} + +/* + * NotifyDeviceRemove - tell function driver on this device that the device is being removed + * +*/ +SDIO_STATUS NotifyDeviceRemove(PSDDEVICE pDevice) { + SDIO_STATUS status; + SDREQUESTQUEUE cancelQueue; + PSDREQUEST pReq; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + InitializeRequestQueue(&cancelQueue); + + if ((pDevice->pFunction != NULL) && + (pDevice->pFunction->pRemove != NULL)){ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: removing device 0x%X\n", (INT)pDevice)); + /* fail any outstanding requests for this device */ + /* acquire lock for request queue */ + status = _AcquireHcdLock(pDevice->pHcd); + if (!SDIO_SUCCESS(status)) { + return status; + } + /* mark the function to block any more requests comming down */ + pDevice->pFunction->Flags |= SDFUNCTION_FLAG_REMOVING; + /* walk through HCD queue and remove this function's requests */ + SDITERATE_OVER_LIST_ALLOW_REMOVE(&pDevice->pHcd->RequestQueue.Queue, pReq, SDREQUEST, SDList) { + if (pReq->pFunction == pDevice->pFunction) { + /* cancel this request, as this device or function is being removed */ + /* note that these request are getting completed out of order */ + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - NotifyDeviceRemove: canceling req 0x%X\n", (UINT)pReq)); + pReq->Status = SDIO_STATUS_CANCELED; + /* remove it from the HCD queue */ + SDListRemove(&pReq->SDList); + /* add it to the cancel queue */ + QueueRequest(&cancelQueue, pReq); + } + }SDITERATE_END; + + status = _ReleaseHcdLock(pDevice->pHcd); + + /* now empty the cancel queue if anything is in there */ + while (TRUE) { + pReq = DequeueRequest(&cancelQueue); + if (NULL == pReq) { + break; + } + /* complete the request */ + DoRequestCompletion(pReq, pDevice->pHcd); + } + /* re-acquire the lock to deal with the current request */ + status = _AcquireHcdLock(pDevice->pHcd); + if (!SDIO_SUCCESS(status)) { + return status; + } + /* now deal with the current request */ + pReq = GET_CURRENT_REQUEST(pDevice->pHcd); + if ((pReq !=NULL) && (pReq->pFunction == pDevice->pFunction) && (pReq->pFunction != NULL)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - NotifyDeviceRemove: Outstanding Req 0x%X on HCD: 0x%X.. waiting...\n", + (UINT)pReq, (UINT)pDevice->pHcd)); + /* the outstanding request on this device is for the function being removed */ + pReq->Flags |= SDREQ_FLAGS_CANCELED; + /* wait for this request to get completed normally */ + status = _ReleaseHcdLock(pDevice->pHcd); + SignalWait(&pDevice->pFunction->CleanupReqSig); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver - NotifyDeviceRemove: Outstanding HCD Req 0x%X completed \n", (UINT)pReq)); + } else { + /* release lock */ + status = _ReleaseHcdLock(pDevice->pHcd); + } + + /* synchronize with ISR SYNC Handlers */ + status = SemaphorePendInterruptable(&pBusContext->DeviceListSem); + if (!SDIO_SUCCESS(status)) { + return status; + } + /* call this devices Remove function */ + pDevice->pFunction->pRemove(pDevice->pFunction,pDevice); + pDevice->pFunction->NumDevices--; + /* make sure the sync handler is NULLed out */ + pDevice->pIrqFunction = NULL; + SemaphorePost(&pBusContext->DeviceListSem); + + OS_RemoveDevice(pDevice); + /* detach this device from the function list it belongs to */ + SDListRemove(&pDevice->FuncListLink); + pDevice->pFunction->Flags &= ~SDFUNCTION_FLAG_REMOVING; + pDevice->pFunction = NULL; + } + return SDIO_STATUS_SUCCESS; +} + + +/* + * RemoveHcdFunctions - remove all functions attached to an HCD + * +*/ +SDIO_STATUS RemoveHcdFunctions(PSDHCD pHcd) { + SDIO_STATUS status; + PSDLIST pList; + PSDFUNCTION pFunction; + PSDDEVICE pDevice; + DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: RemoveHcdFunctions\n")); + + /* walk through the functions and remove the ones associated with this HCD */ + /* protect the driver list */ + if (!SDIO_SUCCESS((status = SemaphorePend(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + /* mark that card is being removed */ + pHcd->CardProperties.CardState |= CARD_STATE_REMOVED; + SDITERATE_OVER_LIST(&pBusContext->FunctionList, pList) { + pFunction = CONTAINING_STRUCT(pList, SDFUNCTION, SDList); + DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: scanning function 0x%X, %s\n", (INT)pFunction, + (pFunction == NULL)?"NULL":pFunction->pName)); + + /* walk the devices on this function and look for a match */ + SDITERATE_OVER_LIST_ALLOW_REMOVE(&pFunction->DeviceList, pDevice, SDDEVICE,FuncListLink) { + if (pDevice->pHcd == pHcd) { + /* match, remove it */ + NotifyDeviceRemove(pDevice); + } + SDITERATE_END; + SDITERATE_END; + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->FunctionListSem)))) { + goto cleanup; /* wait interrupted */ + } + DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: RemoveHcdFunctions\n")); + return SDIO_STATUS_SUCCESS; + +cleanup: + DBG_PRINT(SDDBG_ERROR, ("-SDIO Bus Driver: RemoveHcdFunctions, error exit 0x%X\n", status)); + return status; +} + +/* + * RemoveAllFunctions - remove all functions attached + * +*/ +SDIO_STATUS RemoveAllFunctions() +{ + SDIO_STATUS status; + PSDLIST pList; + PSDHCD pHcd; + + /* walk through the HCDs */ + /* protect the driver list */ + if (!SDIO_SUCCESS((status = SemaphorePend(&pBusContext->HcdListSem)))) { + goto cleanup; /* wait interrupted */ + } + SDITERATE_OVER_LIST(&pBusContext->HcdList, pList) { + pHcd = CONTAINING_STRUCT(pList, SDHCD, SDList); + /* remove the functions */ + RemoveHcdFunctions(pHcd); + } + if (!SDIO_SUCCESS((status = SemaphorePost(&pBusContext->HcdListSem)))) { + goto cleanup; /* wait interrupted */ + } + return SDIO_STATUS_SUCCESS; +cleanup: + DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: RemoveAllFunctions, error exit 0x%X\n", status)); + return status; +} + diff --git a/drivers/sdio/stack/lib/Makefile b/drivers/sdio/stack/lib/Makefile new file mode 100644 index 00000000000..44fa03897ea --- /dev/null +++ b/drivers/sdio/stack/lib/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SDIO) += sdio_lib.o +sdio_lib-objs := sdio_lib_c.o sdio_lib_os.o diff --git a/drivers/sdio/stack/lib/_sdio_lib.h b/drivers/sdio/stack/lib/_sdio_lib.h new file mode 100644 index 00000000000..28762b0f6c2 --- /dev/null +++ b/drivers/sdio/stack/lib/_sdio_lib.h @@ -0,0 +1,50 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: _sdio_lib.h + +@abstract: SDIO Lib internal include + +#notes: + +@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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef ___SDIO_LIB_H___ +#define ___SDIO_LIB_H___ + +#endif /* ___SDIO_LIB_H___*/ diff --git a/drivers/sdio/stack/lib/sdio_lib_c.c b/drivers/sdio/stack/lib/sdio_lib_c.c new file mode 100644 index 00000000000..4bc5a83ecf6 --- /dev/null +++ b/drivers/sdio/stack/lib/sdio_lib_c.c @@ -0,0 +1,908 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_lib_c.c + +@abstract: OS independent SDIO library functions +@category abstract: Support_Reference Support Functions. + +@notes: Support functions for device I/O + +@notice: Copyright (c), 2004-2005 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 of 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 SDLIB_ + +#include +#include +#include +#include +#include "_sdio_lib.h" + +#define _Cmd52WriteByteCommon(pDev, Address, pValue) \ + _SDLIB_IssueCMD52((pDev),0,(Address),(pValue),1,TRUE) +#define _Cmd52ReadByteCommon(pDev, Address, pValue) \ + _SDLIB_IssueCMD52((pDev),0,(Address),pValue,1,FALSE) +#define _Cmd52ReadMultipleCommon(pDev, Address, pBuf,length) \ + _SDLIB_IssueCMD52((pDev),0,(Address),(pBuf),(length),FALSE) + +/* inline version */ +static INLINE void _iSDLIB_SetupCMD52Request(UINT8 FuncNo, + UINT32 Address, + BOOL Write, + UINT8 WriteData, + PSDREQUEST pRequest) { + if (Write) { + SDIO_SET_CMD52_ARG(pRequest->Argument,CMD52_WRITE, + FuncNo, + CMD52_NORMAL_WRITE,Address,WriteData); + } else { + SDIO_SET_CMD52_ARG(pRequest->Argument,CMD52_READ,FuncNo,0,Address,0x00); + } + + pRequest->Flags = SDREQ_FLAGS_RESP_SDIO_R5; + pRequest->Command = CMD52; +} + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Setup cmd52 requests + + @function name: SDLIB_SetupCMD52Request + @prototype: void SDLIB_SetupCMD52Request(UINT8 FuncNo, + UINT32 Address, + BOOL Write, + UINT8 WriteData, + PSDREQUEST pRequest) + @category: PD_Reference + + @input: FunctionNo - function number. + @input: Address - I/O address, 17-bit register address. + @input: Write - TRUE if a write operation, FALSE for reads. + @input: WriteData - write data, byte to write if write operation. + + @output: pRequest - request is updated with cmd52 parameters + + @return: none + + @notes: This function does not perform any I/O. For register reads, the completion + routine can use the SD_R5_GET_READ_DATA() macro to extract the register value. + The routine should also extract the response flags using the SD_R5_GET_RESP_FLAGS() + macro and check the flags with the SD_R5_ERRORS mask. + + @example: Getting the register value from the completion routine: + flags = SD_R5_GET_RESP_FLAGS(pRequest->Response); + if (flags & SD_R5_ERRORS) { + ... errors + } else { + registerValue = SD_R5_GET_READ_DATA(pRequest->Response); + } + + @see also: SDLIB_IssueCMD52 + @see also: SDDEVICE_CALL_REQUEST_FUNC + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void _SDLIB_SetupCMD52Request(UINT8 FuncNo, + UINT32 Address, + BOOL Write, + UINT8 WriteData, + PSDREQUEST pRequest) +{ + _iSDLIB_SetupCMD52Request(FuncNo,Address,Write,WriteData,pRequest); +} + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Issue a CMD52 to read or write a register + + @function name: SDLIB_IssueCMD52 + @prototype: SDIO_STATUS SDLIB_IssueCMD52(PSDDEVICE pDevice, + UINT8 FuncNo, + UINT32 Address, + PUINT8 pData, + INT ByteCount, + BOOL Write) + @category: PD_Reference + @input: pDevice - the device that is the target of the command. + @input: FunctionNo - function number of the target. + @input: Address - 17-bit register address. + @input: ByteCount - number of bytes to read or write, + @input: Write - TRUE if a write operation, FALSE for reads. + @input: pData - data buffer for writes. + + @output: pData - data buffer for writes. + + @return: SDIO Status + + @notes: This function will allocate a request and issue multiple byte reads or writes + to satisfy the ByteCount requested. This function is fully synchronous and will block + the caller. + + @see also: SDLIB_SetupCMD52Request + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDLIB_IssueCMD52(PSDDEVICE pDevice, + UINT8 FuncNo, + UINT32 Address, + PUINT8 pData, + INT ByteCount, + BOOL Write) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + PSDREQUEST pReq = NULL; + + pReq = SDDeviceAllocRequest(pDevice); + + if (NULL == pReq) { + return SDIO_STATUS_NO_RESOURCES; + } + + while (ByteCount) { + _iSDLIB_SetupCMD52Request(FuncNo,Address,Write,*pData,pReq); + status = SDDEVICE_CALL_REQUEST_FUNC(pDevice,pReq); + if (!SDIO_SUCCESS(status)) { + break; + } + + status = ConvertCMD52ResponseToSDIOStatus(SD_R5_GET_RESP_FLAGS(pReq->Response)); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Library: CMD52 resp error: 0x%X \n", + SD_R5_GET_RESP_FLAGS(pReq->Response))); + break; + } + if (!Write) { + /* store the byte */ + *pData = SD_R5_GET_READ_DATA(pReq->Response); + } + pData++; + Address++; + ByteCount--; + } + + SDDeviceFreeRequest(pDevice,pReq); + return status; +} + + + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Find a device's tuple. + + @function name: SDLIB_FindTuple + @prototype: SDIO_STATUS SDLIB_FindTuple(PSDDEVICE pDevice, + UINT8 Tuple, + UINT32 *pTupleScanAddress, + PUINT8 pBuffer, + UINT8 *pLength) + + @category: PD_Reference + @input: pDevice - the device that is the target of the command. + @input: Tuple - 8-bit ID of tuple to find + @input: pTupleScanAddress - On entry pTupleScanAddress is the adddress to start scanning + @input: pLength - length of pBuffer + + @output: pBuffer - storage for tuple + @output: pTupleScanAddress - address of the next tuple + @output: pLength - length of tuple read + + @return: status + + @notes: It is possible to have the same tuple ID multiple times with different lengths. This function + blocks and is fully synchronous. + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDLIB_FindTuple(PSDDEVICE pDevice, + UINT8 Tuple, + UINT32 *pTupleScanAddress, + PUINT8 pBuffer, + UINT8 *pLength) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UINT32 scanStart = *pTupleScanAddress; + UINT8 tupleCode; + UINT8 tupleLink; + + /* sanity check */ + if (scanStart < SDIO_CIS_AREA_BEGIN) { + return SDIO_STATUS_CIS_OUT_OF_RANGE; + } + + while (TRUE) { + /* check for end */ + if (scanStart > SDIO_CIS_AREA_END) { + status = SDIO_STATUS_TUPLE_NOT_FOUND; + break; + } + /* get the code */ + status = _Cmd52ReadByteCommon(pDevice, scanStart, &tupleCode); + if (!SDIO_SUCCESS(status)) { + break; + } + if (CISTPL_END == tupleCode) { + /* found the end */ + status = SDIO_STATUS_TUPLE_NOT_FOUND; + break; + } + /* bump past tuple code */ + scanStart++; + /* get the tuple link value */ + status = _Cmd52ReadByteCommon(pDevice, scanStart, &tupleLink); + if (!SDIO_SUCCESS(status)) { + break; + } + /* bump past tuple link*/ + scanStart++; + /* check tuple we just found */ + if (tupleCode == Tuple) { + DBG_PRINT(SDDBG_TRACE, ("SDIO Library: Tuple:0x%2.2X Found at Address:0x%X, TupleLink:0x%X \n", + Tuple, (scanStart - 2), tupleLink)); + if (tupleLink != CISTPL_LINK_END) { + /* return the next scan address to the caller */ + *pTupleScanAddress = scanStart + tupleLink; + } else { + /* the tuple link is an end marker */ + *pTupleScanAddress = 0xFFFFFFFF; + } + /* go get the tuple */ + status = _Cmd52ReadMultipleCommon(pDevice, scanStart,pBuffer,min(*pLength,tupleLink)); + if (SDIO_SUCCESS(status)) { + /* set the actual return length */ + *pLength = min(*pLength,tupleLink); + } + /* break out of loop */ + break; + } + /*increment past this entire tuple */ + scanStart += tupleLink; + } + + return status; +} + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Issue an SDIO configuration command. + + @function name: SDLIB_IssueConfig + @prototype: SDIO_STATUS _SDLIB_IssueConfig(PSDDEVICE pDevice, + SDCONFIG_COMMAND Command, + PVOID pData, + INT Length) + + @category: PD_Reference + @input: pDevice - the device that is the target of the command. + @input: Command - command to send, see example. + @input: pData - command's data + @input: Length length of pData + + @output: pData - updated on commands that return data. + + @return: SDIO Status + + @example: Command and data pairs: + Type Data + SDCONFIG_GET_WP SDCONFIG_WP_VALUE + SDCONFIG_SEND_INIT_CLOCKS none + SDCONFIG_SDIO_INT_CTRL SDCONFIG_SDIO_INT_CTRL_DATA + SDCONFIG_SDIO_REARM_INT none + SDCONFIG_BUS_MODE_CTRL SDCONFIG_BUS_MODE_DATA + SDCONFIG_POWER_CTRL SDCONFIG_POWER_CTRL_DATA + + @notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDLIB_IssueConfig(PSDDEVICE pDevice, + SDCONFIG_COMMAND Command, + PVOID pData, + INT Length) +{ + SDCONFIG configHdr; + SET_SDCONFIG_CMD_INFO(&configHdr,Command,pData,Length); + return SDDEVICE_CALL_CONFIG_FUNC(pDevice,&configHdr); +} + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Set function block size + + @function name: SDLIB_SetFunctionBlockSize + @prototype: SDIO_STATUS SDLIB_SetFunctionBlockSize(PSDDEVICE pDevice, + UINT16 BlockSize) + + @category: PD_Reference + @input: pDevice - the device that is the target of the command. + @input: BlockSize - block size to set in function + + @output: none + + @return: SDIO Status + + @notes: Issues CMD52 to set the block size. This function is fully synchronous and may + block. + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDLIB_SetFunctionBlockSize(PSDDEVICE pDevice, + UINT16 BlockSize) +{ + UINT8 data[2]; + + /* endian safe */ + data[0] = (UINT8)BlockSize; + data[1] = (UINT8)(BlockSize >> 8); + /* write the function blk size control register */ + return _SDLIB_IssueCMD52(pDevice, + 0, /* function 0 register space */ + FBR_FUNC_BLK_SIZE_LOW_OFFSET(CalculateFBROffset( + SDDEVICE_GET_SDIO_FUNCNO(pDevice))), + data, + 2, + TRUE); +} + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Print a buffer to the debug output + + @function name: SDLIB_PrintBuffer + @prototype: void SDLIB_PrintBuffer(PUCHAR pBuffer, INT Length, PTEXT pDescription) + @category: Support_Reference + + @input: pBuffer - Hex buffer to be printed. + @input: Length - length of pBuffer. + @input: pDescription - String title to be printed above the dump. + + @output: none + + @return: none + + @notes: Prints the buffer by converting to ASCII and using REL_PRINT() with 16 + bytes per line. + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void _SDLIB_PrintBuffer(PUCHAR pBuffer, INT Length, PTEXT pDescription) +{ + TEXT line[49]; + TEXT address[5]; + TEXT ascii[17]; + TEXT temp[5]; + INT i; + UCHAR num; + USHORT offset = 0; + + REL_PRINT(0, + ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n")); + if (pDescription != NULL) { + REL_PRINT(0, ("Description: %s \n\n",pDescription)); + } else { + REL_PRINT(0, ("Description: NONE \n\n")); + } + REL_PRINT(0, + ("Offset Data ASCII \n")); + REL_PRINT(0, + ("--------------------------------------------------------------------------\n")); + + while (Length) { + line[0] = (TEXT)0; + ascii[0] = (TEXT)0; + address[0] = (TEXT)0; + sprintf(address,"%4.4X",offset); + for (i = 0; i < 16; i++) { + if (Length != 0) { + num = *pBuffer; + sprintf(temp,"%2.2X ",num); + strcat(line,temp); + if ((num >= 0x20) && (num <= 0x7E)) { + sprintf(temp,"%c",*pBuffer); + } else { + sprintf(temp,"%c",0x2e); + } + strcat(ascii,temp); + pBuffer++; + Length--; + } else { + /* pad partial line with spaces */ + strcat(line," "); + strcat(ascii," "); + } + } + REL_PRINT(0,("%s %s %s\n", address, line, ascii)); + offset += 16; + } + REL_PRINT(0, + ("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n")); + +} + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get default operational current + + @function name: SDLIB_GetDefaultOpCurrent + @prototype: SDIO_STATUS SDLIB_GetDefaultOpCurrent(PSDDEVICE pDevice, SD_SLOT_CURRENT *pOpCurrent) + @category: PD_Reference + + @input: pDevice - the device that is the target of the command. + + @output: pOpCurrent - operational current in mA. + + @return: SDIO_STATUS + + @notes: This routine reads the function's CISTPL_FUNCE tuple for the default operational + current. For SDIO 1.0 devices this value is read from the 8-bit TPLFE_OP_MAX_PWR + field. For SDIO 1.1 devices, the HP MAX power field is used only if the device is + operating in HIPWR mode. Otherwise the 8-bit TPLFE_OP_MAX_PWR field is used. + Some systems may restrict high power/current mode and force cards to operate in a + legacy (< 200mA) mode. This function is fully synchronous and will block the caller. + + @example: Getting the default operational current for this function: + // get default operational current + status = SDLIB_GetDefaultOpCurrent(pDevice, &slotCurrent); + if (!SDIO_SUCCESS(status)) { + .. failed + } + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _SDLIB_GetDefaultOpCurrent(PSDDEVICE pDevice, SD_SLOT_CURRENT *pOpCurrent) +{ + UINT32 nextTpl; + UINT8 tplLength; + struct SDIO_FUNC_EXT_FUNCTION_TPL_1_1 funcTuple; + SDIO_STATUS status; + + /* get the FUNCE tuple */ + nextTpl = SDDEVICE_GET_SDIO_FUNC_CISPTR(pDevice); + tplLength = sizeof(funcTuple); + /* go get the function Extension tuple */ + status = _SDLIB_FindTuple(pDevice, + CISTPL_FUNCE, + &nextTpl, + (PUINT8)&funcTuple, + &tplLength); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDLIB_GetDefaultOpCurrent: Failed to get FuncE Tuple: %d \n", status)); + return status; + } + /* use the operational power (8-bit) value of current in mA as default*/ + *pOpCurrent = funcTuple.CommonInfo.OpMaxPwr; + if ((tplLength >= sizeof(funcTuple)) && (SDDEVICE_IS_SDIO_REV_GTEQ_1_10(pDevice))) { + /* we have a 1.1 tuple */ + /* check for HIPWR mode */ + if (SDDEVICE_GET_CARD_FLAGS(pDevice) & CARD_HIPWR) { + /* use the maximum operational power (16 bit ) from the tuple */ + *pOpCurrent = CT_LE16_TO_CPU_ENDIAN(funcTuple.HiPwrMaxPwr); + } + } + return SDIO_STATUS_SUCCESS; +} + + +static INLINE void FreeMessageBlock(PSDMESSAGE_QUEUE pQueue, PSDMESSAGE_BLOCK pMsg) { + SDListInsertHead(&pQueue->FreeMessageList, &pMsg->SDList); +} +static INLINE void QueueMessageBlock(PSDMESSAGE_QUEUE pQueue, PSDMESSAGE_BLOCK pMsg) { + SDListInsertTail(&pQueue->MessageList, &pMsg->SDList); +} +static INLINE void QueueMessageToHead(PSDMESSAGE_QUEUE pQueue, PSDMESSAGE_BLOCK pMsg) { + SDListInsertHead(&pQueue->MessageList, &pMsg->SDList); +} + +static INLINE PSDMESSAGE_BLOCK GetFreeMessageBlock(PSDMESSAGE_QUEUE pQueue) { + PSDLIST pItem = SDListRemoveItemFromHead(&pQueue->FreeMessageList); + if (pItem != NULL) { + return CONTAINING_STRUCT(pItem, SDMESSAGE_BLOCK , SDList); + } + return NULL; +} +static INLINE PSDMESSAGE_BLOCK GetQueuedMessage(PSDMESSAGE_QUEUE pQueue) { + PSDLIST pItem = SDListRemoveItemFromHead(&pQueue->MessageList); + if (pItem != NULL) { + return CONTAINING_STRUCT(pItem, SDMESSAGE_BLOCK , SDList); + } + return NULL; +} + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Create a message queue + + @function name: SDLIB_CreateMessageQueue + @prototype: PSDMESSAGE_QUEUE SDLIB_CreateMessageQueue(INT MaxMessages, INT MaxMessageLength) + @category: Support_Reference + + @input: MaxMessages - Maximum number of messages this queue supports + @input: MaxMessageLength - Maximum size of each message + + @return: Message queue object, NULL on failure + + @notes: This function creates a simple first-in-first-out message queue. The caller must determine + the maximum number of messages the queue supports and the size of each message. This + function will pre-allocate memory for each message. A producer of data posts a message + using SDLIB_PostMessage with a user defined data structure. A consumer of this data + can retrieve the message (in FIFO order) using SDLIB_GetMessage. A message queue does not + provide a signaling mechanism for notifying a consumer of data. Notifying a consumer is + user defined. + + @see also: SDLIB_DeleteMessageQueue, SDLIB_GetMessage, SDLIB_PostMessage. + + @example: Creating a message queue: + typedef struct _MyMessage { + UINT8 Code; + PVOID pDataBuffer; + } MyMessage; + // create message queue, 16 messages max. + pMsgQueue = SDLIB_CreateMessageQueue(16,sizeof(MyMessage)); + if (NULL == pMsgQueue) { + .. failed + } + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +PSDMESSAGE_QUEUE _CreateMessageQueue(INT MaxMessages, INT MaxMessageLength) +{ + PSDMESSAGE_QUEUE pQueue = NULL; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + INT ii; + PSDMESSAGE_BLOCK pMsg; + + do { + pQueue = (PSDMESSAGE_QUEUE)KernelAlloc(sizeof(SDMESSAGE_QUEUE)); + + if (NULL == pQueue) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + SDLIST_INIT(&pQueue->MessageList); + SDLIST_INIT(&pQueue->FreeMessageList); + pQueue->MaxMessageLength = MaxMessageLength; + status = CriticalSectionInit(&pQueue->MessageCritSection); + if (!SDIO_SUCCESS(status)) { + break; + } + /* allocate message blocks */ + for (ii = 0; ii < MaxMessages; ii++) { + pMsg = (PSDMESSAGE_BLOCK)KernelAlloc(sizeof(SDMESSAGE_BLOCK) + MaxMessageLength -1); + if (NULL == pMsg) { + break; + } + FreeMessageBlock(pQueue, pMsg); + } + + if (0 == ii) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + + } while (FALSE); + + if (!SDIO_SUCCESS(status)) { + if (pQueue != NULL) { + _DeleteMessageQueue(pQueue); + pQueue = NULL; + } + } + return pQueue; +} + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Delete a message queue + + @function name: SDLIB_DeleteMessageQueue + @prototype: void SDLIB_DeleteMessageQueue(PSDMESSAGE_QUEUE pQueue) + @category: Support_Reference + + @input: pQueue - message queue to delete + + @notes: This function flushes the message queue and frees all memory allocated for + messages. + + @see also: SDLIB_CreateMessageQueue + + @example: Deleting a message queue: + if (pMsgQueue != NULL) { + SDLIB_DeleteMessageQueue(pMsgQueue); + } + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void _DeleteMessageQueue(PSDMESSAGE_QUEUE pQueue) +{ + PSDMESSAGE_BLOCK pMsg; + SDIO_STATUS status; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + status = CriticalSectionAcquireSyncIrq(&pQueue->MessageCritSection); + + /* cleanup free list */ + while (1) { + pMsg = GetFreeMessageBlock(pQueue); + if (pMsg != NULL) { + KernelFree(pMsg); + } else { + break; + } + } + /* cleanup any in the queue */ + while (1) { + pMsg = GetQueuedMessage(pQueue); + if (pMsg != NULL) { + KernelFree(pMsg); + } else { + break; + } + } + + status = CriticalSectionReleaseSyncIrq(&pQueue->MessageCritSection); + CriticalSectionDelete(&pQueue->MessageCritSection); + KernelFree(pQueue); + +} + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Post a message queue + + @function name: SDLIB_PostMessage + @prototype: SDIO_STATUS SDLIB_PostMessage(PSDMESSAGE_QUEUE pQueue, PVOID pMessage, INT MessageLength) + @category: Support_Reference + + @input: pQueue - message queue to post to + @input: pMessage - message to post + @input: MessageLength - length of message (for validation) + + @return: SDIO_STATUS + + @notes: The message queue uses an internal list of user defined message structures. When + posting a message the message is copied into an allocated structure and queued. The memory + pointed to by pMessage does not need to be allocated and can reside on the stack. + The length of the message to post can be smaller that the maximum message size. This allows + for variable length messages up to the maximum message size. This + function returns SDIO_STATUS_NO_RESOURCES, if the message queue is full. This + function returns SDIO_STATUS_BUFFER_TOO_SMALL, if the message size exceeds the maximum + size of a message. Posting and getting messsages from a message queue is safe in any + driver context. + + @see also: SDLIB_CreateMessageQueue , SDLIB_GetMessage + + @example: Posting a message + MyMessage message; + // set up message + message.code = MESSAGE_DATA_READY; + message.pData = pInstance->pDataBuffers[currentIndex]; + // post message + status = SDLIB_PostMessage(pInstance->pReadQueue,&message,sizeof(message)); + if (!SDIO_SUCCESS(status)) { + // failed + } + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _PostMessage(PSDMESSAGE_QUEUE pQueue, PVOID pMessage, INT MessageLength) +{ + SDIO_STATUS status2; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDMESSAGE_BLOCK pMsg; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + if (MessageLength > pQueue->MaxMessageLength) { + return SDIO_STATUS_BUFFER_TOO_SMALL; + } + + status = CriticalSectionAcquireSyncIrq(&pQueue->MessageCritSection); + if (!SDIO_SUCCESS(status)) { + return status; + } + + do { + /* get a message block */ + pMsg = GetFreeMessageBlock(pQueue); + if (NULL == pMsg) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + /* copy the message */ + memcpy(pMsg->MessageStart,pMessage,MessageLength); + /* set the length of the message */ + pMsg->MessageLength = MessageLength; + /* queue the message to the list */ + QueueMessageBlock(pQueue,pMsg); + } while (FALSE); + + status2 = CriticalSectionReleaseSyncIrq(&pQueue->MessageCritSection); + return status; +} + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get a message from a message queue + + @function name: SDLIB_GetMessage + @prototype: SDIO_STATUS SDLIB_GetMessage(PSDMESSAGE_QUEUE pQueue, PVOID pData, INT *pBufferLength) + @category: Support_Reference + + @input: pQueue - message queue to retreive a message from + @input: pBufferLength - on entry, the length of the data buffer + @output: pData - buffer to hold the message + @output: pBufferLength - on return, contains the number of bytes copied + + @return: SDIO_STATUS + + @notes: The message queue uses an internal list of user defined message structures. The message is + dequeued (FIFO order) and copied to the callers buffer. The internal allocation for the message + is returned back to the message queue. This function returns SDIO_STATUS_NO_MORE_MESSAGES + if the message queue is empty. If the length of the buffer is smaller than the length of + the message at the head of the queue,this function returns SDIO_STATUS_BUFFER_TOO_SMALL and + returns the required length in pBufferLength. + + @see also: SDLIB_CreateMessageQueue , SDLIB_PostMessage + + @example: Getting a message + MyMessage message; + INT length; + // set length + length = sizeof(message); + // post message + status = SDLIB_GetMessage(pInstance->pReadQueue,&message,&length); + if (!SDIO_SUCCESS(status)) { + // failed + } + + @example: Checking queue for a message and getting the size of the message + INT length; + // use zero length to get the size of the message + length = 0; + status = SDLIB_GetMessage(pInstance->pReadQueue,NULL,&length); + if (status == SDIO_STATUS_NO_MORE_MESSAGES) { + // no messages in queue + } else if (status == SDIO_STATUS_BUFFER_TOO_SMALL) { + // message exists in queue and length of message is returned + messageSizeInQueue = length; + } else { + // some other failure + } + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS _GetMessage(PSDMESSAGE_QUEUE pQueue, PVOID pData, INT *pBufferLength) +{ + SDIO_STATUS status2; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDMESSAGE_BLOCK pMsg; + CT_DECLARE_IRQ_SYNC_CONTEXT(); + + status = CriticalSectionAcquireSyncIrq(&pQueue->MessageCritSection); + if (!SDIO_SUCCESS(status)) { + return status; + } + + do { + pMsg = GetQueuedMessage(pQueue); + if (NULL == pMsg) { + status = SDIO_STATUS_NO_MORE_MESSAGES; + break; + } + if (*pBufferLength < pMsg->MessageLength) { + /* caller buffer is too small */ + *pBufferLength = pMsg->MessageLength; + /* stick it back to the front */ + QueueMessageToHead(pQueue, pMsg); + status = SDIO_STATUS_BUFFER_TOO_SMALL; + break; + } + /* copy the message to the callers buffer */ + memcpy(pData,pMsg->MessageStart,pMsg->MessageLength); + /* return actual length */ + *pBufferLength = pMsg->MessageLength; + /* return this message block back to the free list */ + FreeMessageBlock(pQueue, pMsg); + + } while (FALSE); + + status2 = CriticalSectionReleaseSyncIrq(&pQueue->MessageCritSection); + + return status; +} + +/* the following documents the OS helper APIs */ + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Create an OS-specific helper task/thread + + @function name: SDLIB_OSCreateHelper + @prototype: SDIO_STATUS SDLIB_OSCreateHelper(POSKERNEL_HELPER pHelper, + PHELPER_FUNCTION pFunction, + PVOID pContext) + @category: Support_Reference + + @input: pHelper - caller allocated helper object + @input: pFunction - helper function + @input: pContext - helper context + + @return: SDIO_STATUS + + @notes: This function creates a helper task/thread that runs in a new execution context. The newly + created task/thread invokes the helper function. The thread/task exits when the helper + function returns. The helper function has the prototype of: + THREAD_RETURN HelperFunction(POSKERNEL_HELPER pHelper) + The helper function usually implements a while loop and suspends execution using + SD_WAIT_FOR_WAKEUP(). On exit the helper function can return an OS-specific THREAD_RETURN + code (usually zero). The helper function executes in a fully schedule-able context and + can block on semaphores and sleep. + + @see also: SDLIB_OSDeleteHelper , SD_WAIT_FOR_WAKEUP + + @example: A thread helper function: + THREAD_RETURN HelperFunction(POSKERNEL_HELPER pHelper) + { + SDIO_STATUS status; + PMYCONTEXT pContext = (PMYCONTEXT)SD_GET_OS_HELPER_CONTEXT(pHelper); + // wait for wake up + while(1) { + status = SD_WAIT_FOR_WAKEUP(pHelper); + if (!SDIO_SUCCESS(status)) { + break; + } + if (SD_IS_HELPER_SHUTTING_DOWN(pHelper)) { + //... shutting down + break; + } + // handle wakeup... + } + return 0; + } + + @example: Creating a helper: + status = SDLIB_OSCreateHelper(&pInstance->OSHelper,HelperFunction,pInstance); + if (!SDIO_SUCCESS(status)) { + // failed + } + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Delete an OS helper task/thread + + @function name: SDLIB_OSDeleteHelper + @prototype: void SDLIB_OSDeleteHelper(POSKERNEL_HELPER pHelper) + @category: Support_Reference + + @input: pHelper - caller allocated helper object + + @notes: This function wakes the helper and waits(blocks) until the helper exits. The caller can + only pass an OS helper structure that was initialized sucessfully by + SDLIB_OSCreateHelper. The caller must be in a schedulable context. + + @see also: SDLIB_OSCreateHelper + + @example: Deleting a helper: + if (pInstance->HelperCreated) { + // clean up the helper if we successfully created it + SDLIB_OSDeleteHelper(&pInstance->OSHelper); + } + + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + + diff --git a/drivers/sdio/stack/lib/sdio_lib_os.c b/drivers/sdio/stack/lib/sdio_lib_os.c new file mode 100644 index 00000000000..55363d04ffd --- /dev/null +++ b/drivers/sdio/stack/lib/sdio_lib_os.c @@ -0,0 +1,251 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_function_os.c + +@abstract: Linux implementation module for SDIO library + +#notes: includes module load and unload functions + +@notice: Copyright (c), 2004 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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +/* debug level for this module*/ +#define DBG_DECLARE 4; +#include + +#include +#include +#include + +#include +#include +#include "_sdio_lib.h" + +#define DESCRIPTION "SDIO Kernel Library" +#define AUTHOR "Atheros Communications, Inc." + +/* proxies */ +SDIO_STATUS SDLIB_IssueCMD52(PSDDEVICE pDevice, + UINT8 FuncNo, + UINT32 Address, + PUINT8 pData, + INT ByteCount, + BOOL Write) +{ + return _SDLIB_IssueCMD52(pDevice,FuncNo,Address,pData,ByteCount,Write); +} + +SDIO_STATUS SDLIB_FindTuple(PSDDEVICE pDevice, + UINT8 Tuple, + UINT32 *pTupleScanAddress, + PUINT8 pBuffer, + UINT8 *pLength) +{ + return _SDLIB_FindTuple(pDevice,Tuple,pTupleScanAddress,pBuffer,pLength); +} + +SDIO_STATUS SDLIB_IssueConfig(PSDDEVICE pDevice, + SDCONFIG_COMMAND Command, + PVOID pData, + INT Length) +{ + return _SDLIB_IssueConfig(pDevice,Command,pData,Length); +} + +void SDLIB_PrintBuffer(PUCHAR pBuffer,INT Length,PTEXT pDescription) +{ + _SDLIB_PrintBuffer(pBuffer,Length,pDescription); +} + +SDIO_STATUS SDLIB_SetFunctionBlockSize(PSDDEVICE pDevice, + UINT16 BlockSize) +{ + return _SDLIB_SetFunctionBlockSize(pDevice,BlockSize); +} + +void SDLIB_SetupCMD52Request(UINT8 FuncNo, + UINT32 Address, + BOOL Write, + UINT8 WriteData, + PSDREQUEST pRequest) +{ + _SDLIB_SetupCMD52Request(FuncNo,Address,Write,WriteData,pRequest); +} + +SDIO_STATUS SDLIB_GetDefaultOpCurrent(PSDDEVICE pDevice, SD_SLOT_CURRENT *pOpCurrent) +{ + return _SDLIB_GetDefaultOpCurrent(pDevice,pOpCurrent); +} + +/* helper function launcher */ +INT HelperLaunch(PVOID pContext) +{ + INT exit; + /* call function */ + exit = ((POSKERNEL_HELPER)pContext)->pHelperFunc((POSKERNEL_HELPER)pContext); + complete_and_exit(&((POSKERNEL_HELPER)pContext)->Completion, exit); + return exit; +} + +/* + * OSCreateHelper - create a worker kernel thread +*/ +SDIO_STATUS SDLIB_OSCreateHelper(POSKERNEL_HELPER pHelper, + PHELPER_FUNCTION pFunction, + PVOID pContext) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + memset(pHelper,0,sizeof(OSKERNEL_HELPER)); + + do { + pHelper->pContext = pContext; + pHelper->pHelperFunc = pFunction; + status = SignalInitialize(&pHelper->WakeSignal); + if (!SDIO_SUCCESS(status)) { + break; + } + init_completion(&pHelper->Completion); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + pHelper->pTask = kthread_create(HelperLaunch, + (PVOID)pHelper, + "SDIO Helper"); + if (NULL == pHelper->pTask) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + wake_up_process(pHelper->pTask); +#else + /* 2.4 */ + pHelper->pTask = kernel_thread(HelperLaunch, + (PVOID)pHelper, + (CLONE_FS | CLONE_FILES | SIGCHLD)); + if (pHelper->pTask < 0) { + DBG_PRINT(SDDBG_TRACE, + ("SDIO BusDriver - OSCreateHelper, failed to create thread\n")); + } +#endif + + } while (FALSE); + + if (!SDIO_SUCCESS(status)) { + SDLIB_OSDeleteHelper(pHelper); + } + return status; +} + +/* + * OSDeleteHelper - delete thread created with OSCreateHelper +*/ +void SDLIB_OSDeleteHelper(POSKERNEL_HELPER pHelper) +{ + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + if (pHelper->pTask != NULL) { +#else + /* 2.4 */ + if (pHelper->pTask >= 0) { +#endif + pHelper->ShutDown = TRUE; + SignalSet(&pHelper->WakeSignal); + /* wait for thread to exit */ + wait_for_completion(&pHelper->Completion); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) + pHelper->pTask = NULL; +#else + /* 2.4 */ + pHelper->pTask = 0; +#endif + } + + SignalDelete(&pHelper->WakeSignal); +} + +/* + * module init +*/ +static int __init sdio_lib_init(void) { + REL_PRINT(SDDBG_TRACE, ("SDIO Library load\n")); + return 0; +} + +/* + * module cleanup +*/ +static void __exit sdio_lib_cleanup(void) { + REL_PRINT(SDDBG_TRACE, ("SDIO Library unload\n")); +} + +PSDMESSAGE_QUEUE SDLIB_CreateMessageQueue(INT MaxMessages, INT MaxMessageLength) +{ + return _CreateMessageQueue(MaxMessages,MaxMessageLength); + +} +void SDLIB_DeleteMessageQueue(PSDMESSAGE_QUEUE pQueue) +{ + _DeleteMessageQueue(pQueue); +} + +SDIO_STATUS SDLIB_PostMessage(PSDMESSAGE_QUEUE pQueue, PVOID pMessage, INT MessageLength) +{ + return _PostMessage(pQueue,pMessage,MessageLength); +} + +SDIO_STATUS SDLIB_GetMessage(PSDMESSAGE_QUEUE pQueue, PVOID pData, INT *pBufferLength) +{ + return _GetMessage(pQueue,pData,pBufferLength); +} + +MODULE_LICENSE("GPL and additional rights"); +MODULE_DESCRIPTION(DESCRIPTION); +MODULE_AUTHOR(AUTHOR); +module_init(sdio_lib_init); +module_exit(sdio_lib_cleanup); +EXPORT_SYMBOL(SDLIB_IssueCMD52); +EXPORT_SYMBOL(SDLIB_FindTuple); +EXPORT_SYMBOL(SDLIB_IssueConfig); +EXPORT_SYMBOL(SDLIB_PrintBuffer); +EXPORT_SYMBOL(SDLIB_SetFunctionBlockSize); +EXPORT_SYMBOL(SDLIB_SetupCMD52Request); +EXPORT_SYMBOL(SDLIB_GetDefaultOpCurrent); +EXPORT_SYMBOL(SDLIB_OSCreateHelper); +EXPORT_SYMBOL(SDLIB_OSDeleteHelper); +EXPORT_SYMBOL(SDLIB_CreateMessageQueue); +EXPORT_SYMBOL(SDLIB_DeleteMessageQueue); +EXPORT_SYMBOL(SDLIB_PostMessage); +EXPORT_SYMBOL(SDLIB_GetMessage); diff --git a/drivers/sdio/stack/platform/Makefile b/drivers/sdio/stack/platform/Makefile new file mode 100644 index 00000000000..14b36126035 --- /dev/null +++ b/drivers/sdio/stack/platform/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SDIO) += sdio_platform.o +sdio_platform-objs := sdioplatformdriver.o \ No newline at end of file diff --git a/drivers/sdio/stack/platform/sdioplatformdriver.c b/drivers/sdio/stack/platform/sdioplatformdriver.c new file mode 100644 index 00000000000..d5520fc2908 --- /dev/null +++ b/drivers/sdio/stack/platform/sdioplatformdriver.c @@ -0,0 +1,300 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdioplatformdriver.c + +@abstract: Linux implementation module for SDIO pltaform driver + +#notes: + +@notice: Copyright (c), 2006 Atheros Communications, Inc. + +@license: 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. + + + + * + * 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 of 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 DESCRIPTION "SDIO Platform Driver" +#define AUTHOR "Atheros Communications, Inc." + +//??for .h + +struct sdioplatform_peripheral { + struct list_head node; + struct sdioplatform_controller *controller; + struct device dev; +}; +struct sdioplatform_driver { + struct device_driver drv; + int (*probe)(struct sdioplatform_peripheral *); + void (*remove)(struct sdioplatform_peripheral *); + int (*suspend)(struct sdioplatform_peripheral *, pm_message_t); + int (*resume)(struct sdioplatform_peripheral *); +}; + + +struct sdioplatform_controller { + struct device *dev; +}; +struct sdioplatform_controller_driver { + struct device_driver drv; + int (*probe)(struct sdioplatform_controller *); + void (*remove)(struct sdioplatform_controller *); + int (*suspend)(struct sdioplatform_controller *, pm_message_t); + int (*resume)(struct sdioplatform_controller *); +}; + + + +#define device_to_sdioplatform_peripheral(d) container_of(d, struct sdioplatform_peripheral, dev) +#define driver_to_sdioplatform_driver(d) container_of(d, struct sdioplatform_driver, drv) + +#define device_to_sdioplatform_controller(d) container_of(d, struct sdioplatform_controller, dev) +#define driver_to_sdioplatform_controller_driver(d) container_of(d, struct sdioplatform_controller_driver, drv) + +#define SDIOPLATFORM_ATTR(name, fmt, args...) \ +static ssize_t sdio_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct sdioplatform_peripheral *peripheral = device_to_sdioplatform_peripheral(dev); \ + return sprintf(buf, fmt, args); \ +} + +SDIOPLATFORM_ATTR(bus_id, "%s\n", bus_id); +#define SDIOPLATFORM_ATTR_RO(name) __ATTR(name, S_IRUGO, sdioplatform_##name##_show, NULL) + +static struct device_attribute sdioplatform_dev_attrs[] = { + SDIOPLATFORM_ATTR_RO(bus_id), + __ATTR_NULL +}; + +static struct bus_type sdioplatform_bus_type = { + .name = "sdioplatform", + .dev_attrs = sdioplatform_dev_attrs, + .match = sdioplatform_bus_match, + .hotplug = NULL, + .suspend = sdioplatform_bus_suspend, + .resume = sdioplatform_bus_resume, +}; + + +/* controller functions */ +static int sdioplatform_controllerdrv_probe(struct device *dev) +{ + struct sdioplatform_controller_driver *drv = driver_to_sdioplatform_controller_driver(dev->driver); + struct sdioplatform_controller *controller = device_to_sdioplatform_controller(dev); + + return drv->probe(controller); +} + +static int sdioplatform_controllerdrv_remove(struct device *dev) +{ + struct sdioplatform_controller_driver *drv = driver_to_sdioplatform_controller_driver(dev->driver); + struct sdioplatform_controller *controller = device_to_sdioplatform_controller(dev); + + return drv->remove(controller); +} + +/* + * sdioplatform_register_controller_driver - register a controller driver + */ +int sdioplatform_register_controller_driver(struct sdioplatform_controller_driver *drv) +{ + drv->drv.bus = &sdioplatform_bus_type; + drv->drv.probe = sdioplatform_controllerdrv_probe; + drv->drv.remove = sdioplatform_controllerdrv_remove; + return driver_register(&drv->drv); +} + +/* + * sdioplatform_unregister_controller_driver - unregister a controller driver + */ +void sdioplatform_unregister_controller_driver(struct sdioplatform_driver *drv) +{ + driver_unregister(&drv->drv); +} + +/* + * sdioplatform_add_controller - register a controller device + */ +int sdioplatform_add_controller(char *name, struct sdioplatform_controller *dev) +{ + if (!dev) { + return -EINVAL; + } + strncpy(dev->dev.bus_id, BUS_ID_SIZE, name); + return device_register(&dev->dev); +} + +/* + * sdioplatform_remove_controller - unregister a controller device + */ +int sdioplatform_remove_controller(char *name, struct sdioplatform_controller *dev) +{ + if (!dev) { + return -EINVAL; + } + return device_unregister(&dev->dev); +} + +/* peripheral functions */ +static int sdioplatform_drv_probe(struct device *dev) +{ + struct sdioplatform_driver *drv = driver_to_sdioplatform_driver(dev->driver); + struct sdioplatform_peripheral *peripheral = device_to_sdioplatform_peripheral(dev); + + return drv->probe(peripheral); +} + +static int sdioplatform_controllerdrv_remove(struct device *dev) +{ + struct sdioplatform_controller_driver *drv = driver_to_sdioplatform_controller_driver(dev->driver); + struct sdioplatform_controller *controller = device_to_sdioplatform_controller(dev); + + return drv->remove(controller); +} + +/* + * sdioplatform_register_driver - register a driver + */ +int sdioplatform_register_driver(struct sdioplatform_driver *drv) +{ + drv->drv.bus = &sdioplatform_bus_type; + drv->drv.probe = sdioplatform_drv_probe; + drv->drv.remove = sdioplatform_drv_remove; + return driver_register(&drv->drv); +} + +/* + * sdioplatform_unregister_driver - unregister a driver + */ +void sdioplatform_unregister_driver(struct sdioplatform_driver *drv) +{ + driver_unregister(&drv->drv); +} + +/* + * sdioplatform_add_peripheral - register a peripheral device + */ +int sdioplatform_add_peripheral(char *name, struct sdioplatform_peripheral *dev) +{ + if (!dev) { + return -EINVAL; + } + strncpy(dev->dev.bus_id, BUS_ID_SIZE, name); + return device_register(&dev->dev); +} + +/* + * sdioplatform_remove_peripheral - unregister a peripheral device + */ +int sdioplatform_remove_peripheral(char *name, struct sdioplatform_peripheral *dev) +{ + if (!dev) { + return -EINVAL; + } + return device_unregister(&dev->dev); +} + + + + + +static int sdioplatform_bus_match(struct device *dev, struct device_driver *drv) +{ + /* probes handle the matching */ + return 1; +} + +static int sdioplatform_bus_suspend(struct device *dev, pm_message_t state) +{ + struct sdioplatform_driver *drv = driver_to_sdioplatform_driver(dev->driver); + struct sdioplatform_peripheral *peripheral = device_to_sdioplatform_peripheral(dev); + int ret = 0; + + if (peripheral->driver && drv->suspend) { + ret = drv->suspend(peripheral, state); + } + return ret; +} + +static int sdioplatform_bus_resume(struct device *dev) +{ + struct sdioplatform_driver *drv = driver_to_sdioplatform_driver(dev->driver); + struct sdioplatform_peripheral *peripheral = device_to_sdioplatform_peripheral(dev); + int ret = 0; + + if (peripheral->driver && drv->resume) { + ret = drv->resume(card); + } + return ret; +} + +/* + * module init +*/ +static int __init sdio_platformdriver_init(void) { + int ret = bus_register(&sdioplatform_bus_type); + return ret; +} + +/* + * module cleanup +*/ +static void __exit sdio_platformdriver_cleanup(void) { + REL_PRINT(SDDBG_TRACE, ("SDIO unloaded\n")); + _SDIO_BusDriverCleanup(); +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DESCRIPTION); +MODULE_AUTHOR(AUTHOR); + +module_init(sdio_platformdriver_init); +module_exit(sdio_platformdriver_cleanup); +EXPORT_SYMBOL(sdioplatform_register_controller_driver); +EXPORT_SYMBOL(sdioplatform_unregister_controller_driver); +EXPORT_SYMBOL(sdioplatform_add_controller); +EXPORT_SYMBOL(sdioplatform_remove_controller); +EXPORT_SYMBOL(sdioplatform_register_driver); +EXPORT_SYMBOL(sdioplatform_unregister_driver); +EXPORT_SYMBOL(sdioplatform_add_peripheral); +EXPORT_SYMBOL(sdioplatform_remove_peripheral); + + + diff --git a/include/linux/sdio/_sdio_defs.h b/include/linux/sdio/_sdio_defs.h new file mode 100644 index 00000000000..a3f5542061c --- /dev/null +++ b/include/linux/sdio/_sdio_defs.h @@ -0,0 +1,638 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: _sdio_defs.h + +@abstract: SD/SDIO definitions + +@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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef ___SDIO_DEFS_H___ +#define ___SDIO_DEFS_H___ + +#define SD_INIT_BUS_CLOCK 100000 /* initialization clock in hz */ +#define SPI_INIT_BUS_CLOCK 100000 /* initialization clock in hz */ +#define SD_MAX_BUS_CLOCK 25000000 /* max clock speed in hz */ +#define SD_HS_MAX_BUS_CLOCK 50000000 /* SD high speed max clock speed in hz */ +#define SDIO_LOW_SPEED_MAX_BUS_CLOCK 400000 /* max low speed clock in hz */ +#define SDMMC_MIN_INIT_CLOCKS 80 /* minimun number of initialization clocks */ +#define SDIO_EMPC_CURRENT_THRESHOLD 300 /* SDIO 1.10 , EMPC (mA) threshold, we add some overhead */ + +/* commands */ +#define CMD0 0 +#define CMD1 1 +#define CMD2 2 +#define CMD3 3 +#define CMD4 4 +#define CMD5 5 +#define CMD6 6 +#define CMD7 7 +#define CMD9 9 +#define CMD10 10 +#define CMD12 12 +#define CMD13 13 +#define CMD15 15 +#define CMD16 16 +#define CMD17 17 +#define CMD18 18 +#define CMD24 24 +#define CMD25 25 +#define CMD27 27 +#define CMD28 28 +#define CMD29 29 +#define CMD30 30 +#define CMD32 32 +#define CMD33 33 +#define CMD38 38 +#define CMD42 42 +#define CMD52 52 +#define CMD53 53 +#define CMD55 55 +#define CMD56 56 +#define CMD58 58 +#define CMD59 59 +#define ACMD6 6 +#define ACMD13 13 +#define ACMD22 22 +#define ACMD23 23 +#define ACMD41 41 +#define ACMD42 42 +#define ACMD51 51 + +#define SD_ACMD6_BUS_WIDTH_1_BIT 0x00 +#define SD_ACMD6_BUS_WIDTH_4_BIT 0x02 + +#define SD_CMD59_CRC_OFF 0x00000000 +#define SD_CMD59_CRC_ON 0x00000001 + +/* SD/SPI max response size */ +#define SD_MAX_CMD_RESPONSE_BYTES SD_R2_RESPONSE_BYTES + +#define SD_R1_RESPONSE_BYTES 6 +#define SD_R1B_RESPONSE_BYTES SD_R1_RESPONSE_BYTES +#define SD_R1_GET_CMD(pR) ((pR)[5] & 0xC0)) +#define SD_R1_SET_CMD(pR,cmd) (pR)[5] = (cmd) & 0xC0 +#define SD_R1_GET_CARD_STATUS(pR) (((UINT32)((pR)[1])) | \ + (((UINT32)((pR)[2])) << 8) | \ + (((UINT32)((pR)[3])) << 16) | \ + (((UINT32)((pR)[4])) << 24) ) +#define SD_R1_SET_CMD_STATUS(pR,status) \ +{ \ + (pR)[1] = (UINT8)(status); \ + (pR)[2] = (UINT8)((status) >> 8); \ + (pR)[3] = (UINT8)((status) >> 16); \ + (pR)[4] = (UINT8)((status) >> 24); \ +} + +/* SD R1 card status bit masks */ +#define SD_CS_CMD_OUT_OF_RANGE ((UINT32)(1 << 31)) +#define SD_CS_ADDRESS_ERR (1 << 30) +#define SD_CS_BLK_LEN_ERR (1 << 29) +#define SD_CS_ERASE_SEQ_ERR (1 << 28) +#define SD_CS_ERASE_PARAM_ERR (1 << 27) +#define SD_CS_WP_ERR (1 << 26) +#define SD_CS_CARD_LOCKED (1 << 25) +#define SD_CS_LK_UNLK_FAILED (1 << 24) +#define SD_CS_PREV_CMD_CRC_ERR (1 << 23) +#define SD_CS_ILLEGAL_CMD_ERR (1 << 22) +#define SD_CS_ECC_FAILED (1 << 21) +#define SD_CS_CARD_INTERNAL_ERR (1 << 20) +#define SD_CS_GENERAL_ERR (1 << 19) +#define SD_CS_CSD_OVERWR_ERR (1 << 16) +#define SD_CS_WP_ERASE_SKIP (1 << 15) +#define SD_CS_ECC_DISABLED (1 << 14) +#define SD_CS_ERASE_RESET (1 << 13) +#define SD_CS_GET_STATE(status) (((status) >> 9) & 0x0f) +#define SD_CS_SET_STATE(status, state) \ +{ \ + (status) &= ~(0x0F << 9); \ + (status) |= (state) << 9 \ +} + +#define SD_CS_TRANSFER_ERRORS \ + ( SD_CS_ADDRESS_ERR | \ + SD_CS_BLK_LEN_ERR | \ + SD_CS_ERASE_SEQ_ERR | \ + SD_CS_ERASE_PARAM_ERR | \ + SD_CS_WP_ERR | \ + SD_CS_ECC_FAILED | \ + SD_CS_CARD_INTERNAL_ERR | \ + SD_CS_GENERAL_ERR ) + +#define SD_CS_STATE_IDLE 0 +#define SD_CS_STATE_READY 1 +#define SD_CS_STATE_IDENT 2 +#define SD_CS_STATE_STBY 3 +#define SD_CS_STATE_TRANS 4 +#define SD_CS_STATE_DATA 5 +#define SD_CS_STATE_RCV 6 +#define SD_CS_STATE_PRG 7 +#define SD_CS_STATE_DIS 8 +#define SD_CS_READY_FOR_DATA (1 << 8) +#define SD_CS_APP_CMD (1 << 5) +#define SD_CS_AKE_SEQ_ERR (1 << 3) + +/* SD R2 response */ +#define SD_R2_RESPONSE_BYTES 17 +#define MAX_CSD_CID_BYTES 16 +#define SD_R2_SET_STUFF_BITS(pR) (pR)[16] = 0x3F +#define GET_SD_CSD_TRANS_SPEED(pR) (pR)[12] +#define GET_SD_CID_MANFID(pR) (pR)[15] +#define GET_SD_CID_PN_1(pR) (pR)[12] +#define GET_SD_CID_PN_2(pR) (pR)[11] +#define GET_SD_CID_PN_3(pR) (pR)[10] +#define GET_SD_CID_PN_4(pR) (pR)[9] +#define GET_SD_CID_PN_5(pR) (pR)[8] +#define GET_SD_CID_PN_6(pR) (pR)[7] + +#define GET_SD_CID_OEMID(pR) ((((UINT16)(pR)[14]) << 8 )| (UINT16)((pR)[13])) +#define SDMMC_OCR_VOLTAGE_MASK 0x7FFFFFFF +/* SD R3 response */ +#define SD_R3_RESPONSE_BYTES 6 +#define SD_R3_GET_OCR(pR) ((((UINT32)((pR)[1])) | \ + (((UINT32)((pR)[2])) << 8) | \ + (((UINT32)((pR)[3])) << 16) | \ + (((UINT32)((pR)[4])) << 24)) & SDMMC_OCR_VOLTAGE_MASK) +#define SD_R3_IS_CARD_READY(pR) (((pR)[4] & 0x80) == 0x80) + +/* OCR bit definitions */ +#define SD_OCR_CARD_PWR_UP_STATUS ((UINT32)(1 << 31)) +#define SD_OCR_3_5_TO_3_6_VDD (1 << 23) +#define SD_OCR_3_4_TO_3_5_VDD (1 << 22) +#define SD_OCR_3_3_TO_3_4_VDD (1 << 21) +#define SD_OCR_3_2_TO_3_3_VDD (1 << 20) +#define SD_OCR_3_1_TO_3_2_VDD (1 << 19) +#define SD_OCR_3_0_TO_3_1_VDD (1 << 18) +#define SD_OCR_2_9_TO_3_0_VDD (1 << 17) +#define SD_OCR_2_8_TO_2_9_VDD (1 << 16) +#define SD_OCR_2_7_TO_2_8_VDD (1 << 15) +#define SD_OCR_2_6_TO_2_7_VDD (1 << 14) +#define SD_OCR_2_5_TO_2_6_VDD (1 << 13) +#define SD_OCR_2_4_TO_2_5_VDD (1 << 12) +#define SD_OCR_2_3_TO_2_4_VDD (1 << 11) +#define SD_OCR_2_2_TO_2_3_VDD (1 << 10) +#define SD_OCR_2_1_TO_2_2_VDD (1 << 9) +#define SD_OCR_2_0_TO_2_1_VDD (1 << 8) +#define SD_OCR_1_9_TO_2_0_VDD (1 << 7) +#define SD_OCR_1_8_TO_1_9_VDD (1 << 6) +#define SD_OCR_1_7_TO_1_8_VDD (1 << 5) +#define SD_OCR_1_6_TO_1_7_VDD (1 << 4) + +/* SD Status data block */ +#define SD_STATUS_DATA_BYTES 64 +#define SDS_GET_DATA_WIDTH(buffer) ((buffer)[0] & 0xC0) +#define SDS_BUS_1_BIT 0x00 +#define SDS_BUS_4_BIT 0x80 +#define SDS_GET_SECURE_MODE(buffer) ((buffer)[0] & 0x20) +#define SDS_CARD_SECURE_MODE 0x20 +#define SDS_GET_CARD_TYPE(buffer) ((buffer)[60] & 0x0F) +#define SDS_SD_CARD_RW 0x00 +#define SDS_SD_CARD_ROM 0x01 + +/* SD R6 response */ +#define SD_R6_RESPONSE_BYTES 6 +#define SD_R6_GET_RCA(pR) ((UINT16)((pR)[3]) | (((UINT16)((pR)[4])) << 8)) +#define SD_R6_GET_CS(pR) ((UINT16)((pR)[1]) | (((UINT16)((pR)[2])) << 8)) + +/* SD Configuration Register (SCR) */ +#define SD_SCR_BYTES 8 +#define SCR_REV_1_0 0x00 +#define SCR_SD_SPEC_1_00 0x00 +#define SCR_SD_SPEC_1_10 0x01 +#define SCR_BUS_SUPPORTS_1_BIT 0x01 +#define SCR_BUS_SUPPORTS_4_BIT 0x04 +#define SCR_SD_SECURITY_MASK 0x70 +#define SCR_SD_NO_SECURITY 0x00 +#define SCR_SD_SECURITY_1_0 0x10 +#define SCR_SD_SECURITY_2_0 0x20 +#define SCR_DATA_STATUS_1_AFTER_ERASE 0x80 + +#define GET_SD_SCR_STRUCT_VER(pB) ((pB)[7] >> 4) +#define GET_SD_SCR_SDSPEC_VER(pB) ((pB)[7] & 0x0F) +#define GET_SD_SCR_BUSWIDTHS(pB) ((pB)[6] & 0x0F) +#define GET_SD_SCR_BUSWIDTHS_FLAGS(pB) (pB)[6] +#define GET_SD_SCR_SECURITY(pB) (((pB)[6] >> 4) & 0x07) +#define GET_SD_SCR_DATA_STAT_AFTER_ERASE(pB) (((pB)[6] >> 7) & 0x01) + +/* SDIO R4 Response */ +#define SD_SDIO_R4_RESPONSE_BYTES 6 +#define SD_SDIO_R4_GET_OCR(pR) ((UINT32)((pR)[1]) | \ + (((UINT32)(pR)[2]) << 8) | \ + (((UINT32)(pR)[3]) << 16)) +#define SD_SDIO_R4_IS_MEMORY_PRESENT(pR) (((pR)[4] & 0x08) == 0x08) +#define SD_SDIO_R4_GET_IO_FUNC_COUNT(pR) (((pR)[4] >> 4) & 0x07) +#define SD_SDIO_R4_IS_CARD_READY(pR) (((pR)[4] & 0x80) == 0x80) + +/* SDIO R5 response */ +#define SD_SDIO_R5_RESPONSE_BYTES 6 +#define SD_SDIO_R5_READ_DATA_OFFSET 1 +#define SD_R5_GET_READ_DATA(pR) (pR)[SD_SDIO_R5_READ_DATA_OFFSET] +#define SD_R5_RESP_FLAGS_OFFSET 2 +#define SD_R5_GET_RESP_FLAGS(pR) (pR)[SD_R5_RESP_FLAGS_OFFSET] +#define SD_R5_SET_CMD(pR,cmd) (pR)[5] = (cmd) & 0xC0 +#define SD_R5_RESP_CMD_ERR (1 << 7) /* for previous cmd */ +#define SD_R5_ILLEGAL_CMD (1 << 6) +#define SD_R5_GENERAL_ERR (1 << 3) +#define SD_R5_INVALID_FUNC (1 << 1) +#define SD_R5_ARG_RANGE_ERR (1 << 0) +#define SD_R5_CURRENT_CMD_ERRORS (SD_R5_ILLEGAL_CMD | SD_R5_GENERAL_ERR \ + | SD_R5_INVALID_FUNC | SD_R5_ARG_RANGE_ERR) +#define SD_R5_ERRORS (SD_R5_CURRENT_CMD_ERRORS) + +#define SD_R5_GET_IO_STATE(pR) (((pR)[2] >> 4) & 0x03) +#define SD_R5_STATE_DIS 0x00 +#define SD_R5_STATE_CMD 0x01 +#define SD_R5_STATE_TRN 0x02 + +/* SDIO Modified R6 Response */ +#define SD_SDIO_R6_RESPONSE_BYTES 6 +#define SD_SDIO_R6_GET_RCA(pR) ((UINT16)((pR)[3]) | ((UINT16)((pR)[4]) << 8)) +#define SD_SDIO_R6_GET_CSTAT(pR)((UINT16)((pR)[1]) | ((UINT16)((pR)[2]) << 8)) + +/* SPI mode R1 response */ +#define SPI_R1_RESPONSE_BYTES 1 +#define GET_SPI_R1_RESP_TOKEN(pR) (pR)[0] +#define SPI_CS_STATE_IDLE 0x01 +#define SPI_CS_ERASE_RESET (1 << 1) +#define SPI_CS_ILLEGAL_CMD (1 << 2) +#define SPI_CS_CMD_CRC_ERR (1 << 3) +#define SPI_CS_ERASE_SEQ_ERR (1 << 4) +#define SPI_CS_ADDRESS_ERR (1 << 5) +#define SPI_CS_PARAM_ERR (1 << 6) +#define SPI_CS_ERR_MASK 0x7c + +/* SPI mode R2 response */ +#define SPI_R2_RESPONSE_BYTES 2 +#define GET_SPI_R2_RESP_TOKEN(pR) (pR)[1] +#define GET_SPI_R2_STATUS_TOKEN(pR) (pR)[0] +/* the first response byte is defined above */ +/* the second response byte is defined below */ +#define SPI_CS_CARD_IS_LOCKED (1 << 0) +#define SPI_CS_LOCK_UNLOCK_FAILED (1 << 1) +#define SPI_CS_ERROR (1 << 2) +#define SPI_CS_INTERNAL_ERROR (1 << 3) +#define SPI_CS_ECC_FAILED (1 << 4) +#define SPI_CS_WP_VIOLATION (1 << 5) +#define SPI_CS_ERASE_PARAM_ERR (1 << 6) +#define SPI_CS_OUT_OF_RANGE (1 << 7) + +/* SPI mode R3 response */ +#define SPI_R3_RESPONSE_BYTES 5 +#define SPI_R3_GET_OCR(pR) ((((UINT32)((pR)[0])) | \ + (((UINT32)((pR)[1])) << 8) | \ + (((UINT32)((pR)[2])) << 16) | \ + (((UINT32)((pR)[3])) << 24)) & SDMMC_OCR_VOLTAGE_MASK) +#define SPI_R3_IS_CARD_READY(pR) (((pR)[3] & 0x80) == 0x80) +#define GET_SPI_R3_RESP_TOKEN(pR) (pR)[4] + +/* SPI mode SDIO R4 response */ +#define SPI_SDIO_R4_RESPONSE_BYTES 5 +#define SPI_SDIO_R4_GET_OCR(pR) ((UINT32)((pR)[0]) | \ + (((UINT32)(pR)[1]) << 8) | \ + (((UINT32)(pR)[2]) << 16)) +#define SPI_SDIO_R4_IS_MEMORY_PRESENT(pR) (((pR)[3] & 0x08) == 0x08) +#define SPI_SDIO_R4_GET_IO_FUNC_COUNT(pR) (((pR)[3] >> 4) & 0x07) +#define SPI_SDIO_R4_IS_CARD_READY(pR) (((pR)[3] & 0x80) == 0x80) +#define GET_SPI_SDIO_R4_RESP_TOKEN(pR) (pR)[4] + +/* SPI Mode SDIO R5 response */ +#define SPI_SDIO_R5_RESPONSE_BYTES 2 +#define GET_SPI_SDIO_R5_RESP_TOKEN(pR) (pR)[1] +#define GET_SPI_SDIO_R5_RESPONSE_RDATA(pR) (pR)[0] +#define SPI_R5_IDLE_STATE 0x01 +#define SPI_R5_ILLEGAL_CMD (1 << 2) +#define SPI_R5_CMD_CRC (1 << 3) +#define SPI_R5_FUNC_ERR (1 << 4) +#define SPI_R5_PARAM_ERR (1 << 6) + +/* SDIO COMMAND 52 Definitions */ +#define CMD52_READ 0 +#define CMD52_WRITE 1 +#define CMD52_READ_AFTER_WRITE 1 +#define CMD52_NORMAL_WRITE 0 +#define SDIO_SET_CMD52_ARG(arg,rw,func,raw,address,writedata) \ + (arg) = (((rw) & 1) << 31) | \ + (((func) & 0x7) << 28) | \ + (((raw) & 1) << 27) | \ + (1 << 26) | \ + (((address) & 0x1FFFF) << 9) | \ + (1 << 8) | \ + ((writedata) & 0xFF) +#define SDIO_SET_CMD52_READ_ARG(arg,func,address) \ + SDIO_SET_CMD52_ARG(arg,CMD52_READ,(func),0,address,0x00) +#define SDIO_SET_CMD52_WRITE_ARG(arg,func,address,value) \ + SDIO_SET_CMD52_ARG(arg,CMD52_WRITE,(func),CMD52_NORMAL_WRITE,address,value) + +/* SDIO COMMAND 53 Definitions */ +#define CMD53_READ 0 +#define CMD53_WRITE 1 +#define CMD53_BLOCK_BASIS 1 +#define CMD53_BYTE_BASIS 0 +#define CMD53_FIXED_ADDRESS 0 +#define CMD53_INCR_ADDRESS 1 +#define SDIO_SET_CMD53_ARG(arg,rw,func,mode,opcode,address,bytes_blocks) \ + (arg) = (((rw) & 1) << 31) | \ + (((func) & 0x7) << 28) | \ + (((mode) & 1) << 27) | \ + (((opcode) & 1) << 26) | \ + (((address) & 0x1FFFF) << 9) | \ + ((bytes_blocks) & 0x1FF) + +#define SDIO_MAX_LENGTH_BYTE_BASIS 512 +#define SDIO_MAX_BLOCKS_BLOCK_BASIS 511 +#define SDIO_MAX_BYTES_PER_BLOCK 2048 +#define SDIO_COMMON_AREA_FUNCTION_NUMBER 0 +#define SDIO_FIRST_FUNCTION_NUMBER 1 +#define SDIO_LAST_FUNCTION_NUMBER 7 + +#define CMD53_CONVERT_BYTE_BASIS_BLK_LENGTH_PARAM(b) (((b) < SDIO_MAX_LENGTH_BYTE_BASIS) ? (b) : 0) +#define CMD53_CONVERT_BLOCK_BASIS_BLK_COUNT_PARAM(b) (((b) <= SDIO_MAX_BLOCKS_BLOCK_BASIS) ? (b) : 0) + + +/* SDIO COMMON Registers */ + +/* revision register */ +#define CCCR_SDIO_REVISION_REG 0x00 +#define CCCR_REV_MASK 0x0F +#define CCCR_REV_1_0 0x00 +#define CCCR_REV_1_1 0x01 +#define SDIO_REV_MASK 0xF0 +#define SDIO_REV_1_00 0x00 +#define SDIO_REV_1_10 0x10 +#define SDIO_REV_1_20 0x20 +/* SD physical spec revision */ +#define SD_SPEC_REVISION_REG 0x01 +#define SD_REV_MASK 0x0F +#define SD_REV_1_01 0x00 +#define SD_REV_1_10 0x01 +/* I/O Enable */ +#define SDIO_ENABLE_REG 0x02 +/* I/O Ready */ +#define SDIO_READY_REG 0x03 +/* Interrupt Enable */ +#define SDIO_INT_ENABLE_REG 0x04 +#define SDIO_INT_MASTER_ENABLE 0x01 +#define SDIO_INT_ALL_ENABLE 0xFE +/* Interrupt Pending */ +#define SDIO_INT_PENDING_REG 0x05 +#define SDIO_INT_PEND_MASK 0xFE +/* I/O Abort */ +#define SDIO_IO_ABORT_REG 0x06 +#define SDIO_IO_RESET (1 << 3) +/* Bus Interface */ +#define SDIO_BUS_IF_REG 0x07 +#define CARD_DETECT_DISABLE 0x80 +#define SDIO_BUS_WIDTH_1_BIT 0x00 +#define SDIO_BUS_WIDTH_4_BIT 0x02 +/* Card Capabilities */ +#define SDIO_CARD_CAPS_REG 0x08 +#define SDIO_CAPS_CMD52_WHILE_DATA 0x01 /* card can issue CMD52 while data transfer */ +#define SDIO_CAPS_MULTI_BLOCK 0x02 /* card supports multi-block data transfers */ +#define SDIO_CAPS_READ_WAIT 0x04 /* card supports read-wait protocol */ +#define SDIO_CAPS_SUSPEND_RESUME 0x08 /* card supports I/O function suspend/resume */ +#define SDIO_CAPS_INT_MULTI_BLK 0x10 /* interrupts between multi-block data capable */ +#define SDIO_CAPS_ENB_INT_MULTI_BLK 0x20 /* enable ints between muli-block data */ +#define SDIO_CAPS_LOW_SPEED 0x40 /* low speed card */ +#define SDIO_CAPS_4BIT_LS 0x80 /* 4 bit low speed card */ +/* Common CIS pointer */ +#define SDIO_CMN_CIS_PTR_LOW_REG 0x09 +#define SDIO_CMN_CIS_PTR_MID_REG 0x0a +#define SDIO_CMN_CIS_PTR_HI_REG 0x0b +/* Bus suspend */ +#define SDIO_BUS_SUSPEND_REG 0x0c +#define SDIO_FUNC_SUSPEND_STATUS_MASK 0x01 /* selected function is suspended */ +#define SDIO_SUSPEND_FUNCTION 0x02 /* suspend the current selected function */ +/* Function select (for bus suspension) */ +#define SDIO_FUNCTION_SELECT_REG 0x0d +#define SDIO_SUSPEND_FUNCTION_0 0x00 +#define SDIO_SUSPEND_MEMORY_FUNC_MASK 0x08 +/* Function Execution */ +#define SDIO_FUNCTION_EXEC_REG 0x0e +#define SDIO_MEMORY_FUNC_EXEC_MASK 0x01 +/* Function Ready */ +#define SDIO_FUNCTION_READY_REG 0x0f +#define SDIO_MEMORY_FUNC_BUSY_MASK 0x01 + +/* power control 1.10 only */ +#define SDIO_POWER_CONTROL_REG 0x12 +#define SDIO_POWER_CONTROL_SMPC 0x01 +#define SDIO_POWER_CONTROL_EMPC 0x02 + +/* high speed control , 1.20 only */ +#define SDIO_HS_CONTROL_REG 0x13 +#define SDIO_HS_CONTROL_SHS 0x01 +#define SDIO_HS_CONTROL_EHS 0x02 + +/* Function Base Registers */ +#define xFUNCTION_FBR_OFFSET(funcNo) (0x100*(funcNo)) +/* offset calculation that does not use multiplication */ +static INLINE UINT32 CalculateFBROffset(UCHAR FuncNo) { + UCHAR i = FuncNo; + UINT32 offset = 0; + while (i) { + offset += 0x100; + i--; + } + return offset; +} +/* Function info */ +#define FBR_FUNC_INFO_REG_OFFSET(fbr) ((fbr) + 0x00) +#define FUNC_INFO_SUPPORTS_CSA_MASK 0x40 +#define FUNC_INFO_ENABLE_CSA 0x80 +#define FUNC_INFO_DEVICE_CODE_MASK 0x0F +#define FUNC_INFO_DEVICE_CODE_LAST 0x0F +#define FBR_FUNC_EXT_DEVICE_CODE_OFFSET(fbr) ((fbr) + 0x01) +/* Function Power selection */ +#define FBR_FUNC_POWER_SELECT_OFFSET(fbr) ((fbr) + 0x02) +#define FUNC_POWER_SELECT_SPS 0x01 +#define FUNC_POWER_SELECT_EPS 0x02 +/* Function CIS ptr */ +#define FBR_FUNC_CIS_LOW_OFFSET(fbr) ((fbr) + 0x09) +#define FBR_FUNC_CIS_MID_OFFSET(fbr) ((fbr) + 0x0a) +#define FBR_FUNC_CIS_HI_OFFSET(fbr) ((fbr) + 0x0b) +/* Function CSA ptr */ +#define FBR_FUNC_CSA_LOW_OFFSET(fbr) ((fbr) + 0x0c) +#define FBR_FUNC_CSA_MID_OFFSET(fbr) ((fbr) + 0x0d) +#define FBR_FUNC_CSA_HI_OFFSET(fbr) ((fbr) + 0x0e) +/* Function CSA data window */ +#define FBR_FUNC_CSA_DATA_OFFSET(fbr) ((fbr) + 0x0f) +/* Function Block Size Control */ +#define FBR_FUNC_BLK_SIZE_LOW_OFFSET(fbr) ((fbr) + 0x10) +#define FBR_FUNC_BLK_SIZE_HI_OFFSET(fbr) ((fbr) + 0x11) +#define SDIO_CIS_AREA_BEGIN 0x00001000 +#define SDIO_CIS_AREA_END 0x00017fff +/* Tuple definitions */ +#define CISTPL_NULL 0x00 +#define CISTPL_CHECKSUM 0x10 +#define CISTPL_VERS_1 0x15 +#define CISTPL_ALTSTR 0x16 +#define CISTPL_MANFID 0x20 +#define CISTPL_FUNCID 0x21 +#define CISTPL_FUNCE 0x22 +#define CISTPL_VENDOR 0x91 +#define CISTPL_END 0xff +#define CISTPL_LINK_END 0xff + + +/* these structures must be packed */ + +/* Manufacturer ID tuple */ +struct SDIO_MANFID_TPL { + UINT16 ManufacturerCode; /* jedec code */ + UINT16 ManufacturerInfo; /* manufacturer specific code */ +}CT_PACK_STRUCT; + +/* Function ID Tuple */ +struct SDIO_FUNC_ID_TPL { + UINT8 DeviceCode; /* device code */ + UINT8 InitMask; /* system initialization mask (not used) */ +}CT_PACK_STRUCT; + + /* Extended Function Tuple (Common) */ +struct SDIO_FUNC_EXT_COMMON_TPL { + UINT8 Type; /* type */ + UINT16 Func0_MaxBlockSize; /* max function 0 block transfer size */ + UINT8 MaxTransSpeed; /* max transfer speed (encoded) */ +#define TRANSFER_UNIT_MULTIPIER_MASK 0x07 +#define TIME_VALUE_MASK 0x78 +#define TIME_VALUE_SHIFT 3 +}CT_PACK_STRUCT; + +/* Extended Function Tuple (Per Function) */ +struct SDIO_FUNC_EXT_FUNCTION_TPL { + UINT8 Type; /* type */ +#define SDIO_FUNC_INFO_WAKEUP_SUPPORT 0x01 + UINT8 FunctionInfo; /* function info */ + UINT8 SDIORev; /* revision */ + UINT32 CardPSN; /* product serial number */ + UINT32 CSASize; /* CSA size */ + UINT8 CSAProperties; /* CSA properties */ + UINT16 MaxBlockSize; /* max block size for block transfers */ + UINT32 FunctionOCR; /* optimal function OCR */ + UINT8 OpMinPwr; /* operational min power */ + UINT8 OpAvgPwr; /* operational average power */ + UINT8 OpMaxPwr; /* operation maximum power */ + UINT8 SbMinPwr; /* standby minimum power */ + UINT8 SbAvgPwr; /* standby average power */ + UINT8 SbMaxPwr; /* standby maximum power */ + UINT16 MinBandWidth; /* minimum bus bandwidth */ + UINT16 OptBandWidth; /* optimalbus bandwitdh */ +}CT_PACK_STRUCT; + +struct SDIO_FUNC_EXT_FUNCTION_TPL_1_1 { + struct SDIO_FUNC_EXT_FUNCTION_TPL CommonInfo; /* from 1.0*/ + UINT16 EnableTimeOut; /* timeout for enable */ + UINT16 OperPwrMaxPwr; + UINT16 OperPwrAvgPwr; + UINT16 HiPwrMaxPwr; + UINT16 HiPwrAvgPwr; + UINT16 LowPwrMaxPwr; + UINT16 LowPwrAvgPwr; +}CT_PACK_STRUCT; + +static INLINE SDIO_STATUS ConvertCMD52ResponseToSDIOStatus(UINT8 CMD52ResponseFlags) { + if (!(CMD52ResponseFlags & SD_R5_ERRORS)) { + return SDIO_STATUS_SUCCESS; + } + if (CMD52ResponseFlags & SD_R5_ILLEGAL_CMD) { + return SDIO_STATUS_DATA_STATE_INVALID; + } else if (CMD52ResponseFlags & SD_R5_INVALID_FUNC) { + return SDIO_STATUS_INVALID_FUNC; + } else if (CMD52ResponseFlags & SD_R5_ARG_RANGE_ERR) { + return SDIO_STATUS_FUNC_ARG_ERROR; + } else { + return SDIO_STATUS_DATA_ERROR_UNKNOWN; + } +} + +/* CMD6 mode switch definitions */ + +#define SD_SWITCH_FUNC_CHECK 0 +#define SD_SWITCH_FUNC_SET ((UINT32)(1 << 31)) +#define SD_FUNC_NO_SELECT_MASK 0x00FFFFFF +#define SD_SWITCH_GRP_1 0 +#define SD_SWITCH_GRP_2 1 +#define SD_SWITCH_GRP_3 2 +#define SD_SWITCH_GRP_4 3 +#define SD_SWITCH_GRP_5 4 +#define SD_SWITCH_GRP_6 5 + +#define SD_SWITCH_HIGH_SPEED_GROUP SD_SWITCH_GRP_1 +#define SD_SWITCH_HIGH_SPEED_FUNC_NO 1 + +#define SD_SWITCH_MAKE_SHIFT(grp) ((grp) * 4) + +#define SD_SWITCH_MAKE_GRP_PATTERN(FuncGrp,FuncNo) \ + ((SD_FUNC_NO_SELECT_MASK & (~(0xF << SD_SWITCH_MAKE_SHIFT(FuncGrp)))) | \ + (((FuncNo) & 0xF) << SD_SWITCH_MAKE_SHIFT(FuncGrp))) \ + +#define SD_SWITCH_FUNC_ARG_GROUP_CHECK(FuncGrp,FuncNo) \ + (SD_SWITCH_FUNC_CHECK | SD_SWITCH_MAKE_GRP_PATTERN(FuncGrp,FuncNo)) + +#define SD_SWITCH_FUNC_ARG_GROUP_SET(FuncGrp,FuncNo) \ + (SD_SWITCH_FUNC_SET | SD_SWITCH_MAKE_GRP_PATTERN(FuncGrp,FuncNo)) + +#define SD_SWITCH_FUNC_STATUS_BLOCK_BYTES 64 + +#define SD_SWITCH_FUNC_STATUS_GET_GRP_BIT_MASK(pBuffer,FuncGrp) \ + (USHORT)((pBuffer)[50 + ((FuncGrp)*2)] | ((pBuffer)[51 + ((FuncGrp)*2)] << 8)) + +#define SD_SWITCH_FUNC_STATUS_GET_MAX_CURRENT(pBuffer) \ + (USHORT)((pBuffer)[62] | ((pBuffer)[63] << 8)) + +static INLINE UINT8 SDSwitchGetSwitchResult(PUINT8 pBuffer, UINT8 FuncGrp) +{ + switch (FuncGrp) { + case 0: + return (pBuffer[47] & 0xF); + case 1: + return (pBuffer[47] >> 4); + case 2: + return (pBuffer[48] & 0xF); + case 3: + return (pBuffer[48] >> 4); + case 4: + return (pBuffer[49] & 0xF); + case 5: + return (pBuffer[49] >> 4); + default: + return 0xF; + } +} + +#endif diff --git a/include/linux/sdio/ctsystem.h b/include/linux/sdio/ctsystem.h new file mode 100644 index 00000000000..4f727397434 --- /dev/null +++ b/include/linux/sdio/ctsystem.h @@ -0,0 +1,115 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: cpsystem.h + +@abstract: common system include file. + +@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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __CPSYSTEM_H___ +#define __CPSYSTEM_H___ + +/* SDIO stack status defines */ +/* < 0 error, >0 warning, 0 success */ +#define SDIO_IS_WARNING(status) ((status) > 0) +#define SDIO_IS_ERROR(status) ((status) < 0) +#define SDIO_SUCCESS(status) ((SDIO_STATUS)(status) >= 0) +#define SDIO_STATUS_SUCCESS 0 +#define SDIO_STATUS_ERROR -1 +#define SDIO_STATUS_INVALID_PARAMETER -2 +#define SDIO_STATUS_PENDING 3 +#define SDIO_STATUS_DEVICE_NOT_FOUND -4 +#define SDIO_STATUS_DEVICE_ERROR -5 +#define SDIO_STATUS_INTERRUPTED -6 +#define SDIO_STATUS_NO_RESOURCES -7 +#define SDIO_STATUS_CANCELED -8 +#define SDIO_STATUS_BUFFER_TOO_SMALL -9 +#define SDIO_STATUS_NO_MORE_MESSAGES -10 +#define SDIO_STATUS_BUS_RESP_TIMEOUT -20 /* response timed-out */ +#define SDIO_STATUS_BUS_READ_TIMEOUT -21 /* read data timed-out */ +#define SDIO_STATUS_BUS_READ_CRC_ERR -22 /* data CRC failed */ +#define SDIO_STATUS_BUS_WRITE_ERROR -23 /* write failed */ +#define SDIO_STATUS_BUS_RESP_CRC_ERR -24 /* response received with a CRC error */ +#define SDIO_STATUS_INVALID_TUPLE_LENGTH -25 /* tuple length was invalid */ +#define SDIO_STATUS_TUPLE_NOT_FOUND -26 /* tuple could not be found */ +#define SDIO_STATUS_CIS_OUT_OF_RANGE -27 /* CIS is out of range in the tuple scan */ +#define SDIO_STATUS_FUNC_ENABLE_TIMEOUT -28 /* card timed out enabling or disabling */ +#define SDIO_STATUS_DATA_STATE_INVALID -29 /* card is in an invalid state for data */ +#define SDIO_STATUS_DATA_ERROR_UNKNOWN -30 /* card cannot process data transfer */ +#define SDIO_STATUS_INVALID_FUNC -31 /* sdio request is not valid for the function */ +#define SDIO_STATUS_FUNC_ARG_ERROR -32 /* sdio request argument is invalid or out of range */ +#define SDIO_STATUS_INVALID_COMMAND -33 /* SD COMMAND is invalid for the card state */ +#define SDIO_STATUS_SDREQ_QUEUE_FAILED -34 /* request failed to insert into queue */ +#define SDIO_STATUS_BUS_RESP_TIMEOUT_SHIFTABLE -35 /* response timed-out, possibily shiftable to correct */ +#define SDIO_STATUS_UNSUPPORTED -36 /* not supported */ +#define SDIO_STATUS_PROGRAM_TIMEOUT -37 /* memory card programming timeout */ +#define SDIO_STATUS_PROGRAM_STATUS_ERROR -38 /* memory card programming errors */ + +#include + +/* get structure from contained field */ +#define CONTAINING_STRUCT(address, struct_type, field_name)\ + ((struct_type *)((ULONG_PTR)(address) - (ULONG_PTR)(&((struct_type *)0)->field_name))) + +#define ZERO_OBJECT(obj) memset(&(obj),0,sizeof(obj)) +#define ZERO_POBJECT(pObj) memset((pObj),0,sizeof(*(pObj))) + + +/* bit field support functions */ +static INLINE void SetBit(PULONG pField, UINT position) { + *pField |= 1 << position; +} +static INLINE void ClearBit(PULONG pField, UINT position) { + *pField &= ~(1 << position); +} +static INLINE BOOL IsBitSet(PULONG pField, UINT position) { + return (*pField & (1 << position)); +} +static INLINE INT FirstClearBit(PULONG pField) { + UINT ii; + for(ii = 0; ii < sizeof(ULONG)*8; ii++) { + if (!IsBitSet(pField, ii)) { + return ii; + } + } + /* no clear bits found */ + return -1; +} + +#endif /* __CPSYSTEM_H___ */ diff --git a/include/linux/sdio/ctsystem_linux.h b/include/linux/sdio/ctsystem_linux.h new file mode 100644 index 00000000000..0de89a66d7b --- /dev/null +++ b/include/linux/sdio/ctsystem_linux.h @@ -0,0 +1,983 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: ctsystem_linux.h + +@abstract: common system include file for Linux. + +@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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __CPSYSTEM_LINUX_H___ +#define __CPSYSTEM_LINUX_H___ + +/* #define DBG_TIMESTAMP 1 */ +#define SD_TRACK_REQ 1 + +/* LINUX support */ +#include + +#ifndef KERNEL_VERSION + #error KERNEL_VERSION macro not defined! +#endif + +#ifndef LINUX_VERSION_CODE + #error LINUX_VERSION_CODE macro not defined! +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef DBG_TIMESTAMP +#include +#endif /* DBG_TIMESTAMP */ +#ifndef in_atomic + /* released version of 2.6.9 */ +#include +#endif +#include +#include + +/* generic types */ +typedef unsigned char UCHAR; +typedef unsigned char * PUCHAR; +typedef char TEXT; +typedef char * PTEXT; +typedef unsigned short USHORT; +typedef unsigned short* PUSHORT; +typedef unsigned int UINT; +typedef unsigned int* PUINT; +typedef int INT; +typedef int* PINT; +typedef unsigned long ULONG; +typedef unsigned long* PULONG; +typedef u8 UINT8; +typedef u16 UINT16; +typedef u32 UINT32; +typedef u8* PUINT8; +typedef u16* PUINT16; +typedef u32* PUINT32; +typedef unsigned char * ULONG_PTR; +typedef void* PVOID; +typedef unsigned char BOOL; +typedef BOOL* PBOOL; +typedef int SDIO_STATUS; +typedef int SYSTEM_STATUS; +typedef unsigned int EVENT_TYPE; +typedef unsigned int EVENT_ARG; +typedef unsigned int* PEVENT_TYPE; +typedef struct semaphore OS_SEMAPHORE; +typedef struct semaphore* POS_SEMAPHORE; +typedef struct semaphore OS_SIGNAL; /* OS signals are just semaphores */ +typedef struct semaphore* POS_SIGNAL; +typedef spinlock_t OS_CRITICALSECTION; +typedef spinlock_t *POS_CRITICALSECTION; +typedef int SDPOWER_STATE; +typedef unsigned long ATOMIC_FLAGS; +typedef INT THREAD_RETURN; +typedef dma_addr_t DMA_ADDRESS; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) +typedef struct task_struct* PKERNEL_TASK; +typedef struct device_driver OS_DRIVER; +typedef struct device_driver* POS_DRIVER; +typedef struct device OS_DEVICE; +typedef struct device* POS_DEVICE; +typedef struct pnp_driver OS_PNPDRIVER; +typedef struct pnp_driver* POS_PNPDRIVER; +typedef struct pnp_dev OS_PNPDEVICE; +typedef struct pnp_dev* POS_PNPDEVICE; +typedef struct module* POS_MODULE; +#else +/* 2.4 */ +typedef int PKERNEL_TASK; +typedef PVOID OS_DRIVER; +typedef PVOID* POS_DRIVER; +typedef PVOID OS_DEVICE; +typedef PVOID* POS_DEVICE; +typedef PVOID OS_PNPDRIVER; +typedef PVOID* POS_PNPDRIVER; +typedef PVOID OS_PNPDEVICE; +typedef PVOID* POS_PNPDEVICE; +typedef struct module* POS_MODULE; +#define module_param(a,b,c) MODULE_PARM(a, "i") +#endif + +typedef int CT_DEBUG_LEVEL; + + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef NULL +#define NULL ((PVOID)0) +#endif +#define SDDMA_DESCRIPTION_FLAG_DMA 0x1 /* DMA enabled */ +#define SDDMA_DESCRIPTION_FLAG_SGDMA 0x2 /* Scatter-Gather DMA enabled */ +typedef struct _SDDMA_DESCRIPTION { + UINT16 Flags; /* SDDMA_DESCRIPTION_FLAG_xxx */ + UINT16 MaxDescriptors; /* number of supported scatter gather entries */ + UINT32 MaxBytesPerDescriptor; /* maximum bytes in a DMA descriptor entry */ + u64 Mask; /* dma address mask */ + UINT32 AddressAlignment; /* dma address alignment mask, least significant bits indicate illegal address bits */ + UINT32 LengthAlignment; /* dma buffer length alignment mask, least significant bits indicate illegal length bits */ +}SDDMA_DESCRIPTION, *PSDDMA_DESCRIPTION; +typedef struct scatterlist SDDMA_DESCRIPTOR, *PSDDMA_DESCRIPTOR; + +#define INLINE inline +#define CT_PACK_STRUCT __attribute__ ((packed)) + +#define CT_DECLARE_MODULE_PARAM_INTEGER(p) module_param(p, int, 0644); + +/* debug print macros */ +//#define SDDBG_KERNEL_PRINT_LEVEL KERN_DEBUG +#define SDDBG_KERNEL_PRINT_LEVEL KERN_ALERT +#define DBG_MASK_NONE 0x0 +#define DBG_MASK_HCD 0x100 +#define DBG_MASK_LIB 0x200 +#define DBG_MASK_BUS 0x400 + +/* debug output levels, this must be order low number to higher */ +#define SDDBG_ERROR 3 +#define SDDBG_WARN 4 +#define SDDBG_DEBUG 6 +#define SDDBG_TRACE 7 +#define SDDBG_ALL 0xff + +#define DBG_LEVEL_NONE 0 +#define DBG_LEVEL_ERROR SDDBG_ERROR +#define DBG_LEVEL_WARN SDDBG_WARN +#define DBG_LEVEL_DEBUG SDDBG_DEBUG +#define DBG_LEVEL_TRACE SDDBG_TRACE +#define DBG_LEVEL_ALL SDDBG_ALL + +#define DBG_GET_LEVEL(lvl) ((lvl) & 0xff) +#define DBG_GET_MASK(lvl) (((lvl) & 0xff00)) + +#define DBG_SDIO_MASK (DBG_MASK_NONE | DBG_LEVEL_DEBUG) + +#define DEBUG 1 + +#ifdef DEBUG + +#define DBG_ASSERT(test) \ +{ \ + if (!(test)) { \ + DBG_PRINT(SDDBG_ERROR, ("Debug Assert Caught, File %s, Line: %d, Test:%s \n",__FILE__, __LINE__,#test)); \ + } \ +} +#define DBG_ASSERT_WITH_MSG(test,s) \ +{ \ + if (!(test)) { \ + DBG_PRINT(SDDBG_ERROR, ("Assert:%s File %s, Line: %d \n",(s),__FILE__, __LINE__)); \ + } \ +} + +#define DBG_PRINT(lvl, args)\ + do {\ + if (DBG_GET_LEVEL(lvl) <= (DBG_SDIO_MASK & 0xff)) \ + printk(_DBG_PRINTX_ARG args); \ + } while(0); + +#else /* DEBUG */ + +#define DBG_PRINT(lvl, str) +#define DBG_ASSERT(test) +#define DBG_ASSERT_WITH_MSG(test,s) +#endif /* DEBUG */ + +#define _DBG_PRINTX_ARG(arg...) arg /* unroll the parens around the var args*/ +#define DBG_GET_DEBUG_LEVEL() DBG_GET_LEVEL(DBG_SDIO_MASK) +#define DBG_SET_DEBUG_LEVEL(v) +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Print a string to the debugger or console + + @function name: REL_PRINT + @prototype: void REL_PRINT(INT Level, string) + @category: Support_Reference + @input: Level - debug level for the print + + @output: none + + @return: + + @notes: If Level is less than the current debug level, the print will be + issued. This print cannot be conditionally compiled. + @see also: DBG_PRINT + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define REL_PRINT(lvl, args)\ + {if (lvl <= DBG_GET_DEBUG_LEVEL())\ + printk(SDDBG_KERNEL_PRINT_LEVEL _DBG_PRINTX_ARG args);\ + } +/* debug output levels, this must be order low number to higher */ +#define SDDBG_ERROR 3 +#define SDDBG_WARN 4 +#define SDDBG_DEBUG 6 +#define SDDBG_TRACE 7 + +#ifdef DBG_CRIT_SECTION_RECURSE + /* this macro thows an exception if the lock is recursively taken + * the kernel must be configured with: CONFIG_DEBUG_SPINLOCK=y */ +#define call_spin_lock(pCrit) \ +{ \ + UINT32 unlocked = 1; \ + if ((pCrit)->lock) {unlocked = 0;} \ + spin_lock_bh(pCrit); \ + if (!unlocked) { \ + unlocked = 0x01; \ + unlocked = *((volatile UINT32 *)unlocked); \ + } \ +} + +#define call_spin_lock_irqsave(pCrit,isc) \ +{ \ + UINT32 unlocked = 1; \ + if ((pCrit)->lock) {unlocked = 0;} \ + spin_lock_irqsave(pCrit,isc); \ + if (!unlocked) { \ + unlocked = 0x01; \ + unlocked = *((volatile UINT32 *)unlocked); \ + } \ +} + +#else +#define call_spin_lock(s) spin_lock_bh(s) +#define call_spin_lock_irqsave(s,isc) spin_lock_irqsave(s,isc) +#endif + +#define call_spin_unlock(s) spin_unlock_bh((s)) +#define call_spin_unlock_irqrestore(s,isc) spin_unlock_irqrestore(s,isc) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) +#define NonSchedulable() (in_atomic() || irqs_disabled()) +#else +#define NonSchedulable() (irqs_disabled()) +#endif +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Initialize a critical section object. + + @function name: CriticalSectionInit + @prototype: SDIO_STATUS CriticalSectionInit(POS_CRITICALSECTION pCrit) + @category: Support_Reference + @output: pCrit - pointer to critical section to initialize + + @return: SDIO_STATUS_SUCCESS on success. + + @notes: CriticalSectionDelete() must be called to cleanup any resources + associated with the critical section. + + @see also: CriticalSectionDelete, CriticalSectionAcquire, CriticalSectionRelease + @example: To initialize a critical section: + status = CriticalSectionInit(&pDevice->ListLock); + if (!SDIO_SUCCESS(status)) { + .. failed + return status; + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline SDIO_STATUS CriticalSectionInit(POS_CRITICALSECTION pCrit) { + spin_lock_init(pCrit); + return SDIO_STATUS_SUCCESS; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Acquire a critical section lock. + + @function name: CriticalSectionAcquire + @prototype: SDIO_STATUS CriticalSectionAcquire(POS_CRITICALSECTION pCrit) + @category: Support_Reference + + @input: pCrit - pointer to critical section that was initialized + + @return: SDIO_STATUS_SUCCESS on success. + + @notes: The critical section lock is acquired when this function returns + SDIO_STATUS_SUCCESS. Use CriticalSectionRelease() to release + the critical section lock. + + @see also: CriticalSectionRelease + + @example: To acquire a critical section lock: + status = CriticalSectionAcquire(&pDevice->ListLock); + if (!SDIO_SUCCESS(status)) { + .. failed + return status; + } + ... access protected data + // unlock + status = CriticalSectionRelease(&pDevice->ListLock); + if (!SDIO_SUCCESS(status)) { + .. failed + return status; + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline SDIO_STATUS CriticalSectionAcquire(POS_CRITICALSECTION pCrit) { + call_spin_lock(pCrit); + return SDIO_STATUS_SUCCESS; +} + +// macro-tized versions +#define CriticalSectionAcquire_M(pCrit) \ + SDIO_STATUS_SUCCESS; call_spin_lock(pCrit) +#define CriticalSectionRelease_M(pCrit) \ + SDIO_STATUS_SUCCESS; call_spin_unlock(pCrit) + +#define CT_DECLARE_IRQ_SYNC_CONTEXT() unsigned long _ctSyncFlags + +#define CriticalSectionAcquireSyncIrq(pCrit) \ + SDIO_STATUS_SUCCESS; call_spin_lock_irqsave(pCrit,_ctSyncFlags) + +#define CriticalSectionReleaseSyncIrq(pCrit) \ + SDIO_STATUS_SUCCESS; call_spin_unlock_irqrestore(pCrit,_ctSyncFlags) + + + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Release a critical section lock. + + @function name: CriticalSectionRelease + @prototype: SDIO_STATUS CriticalSectionRelease(POS_CRITICALSECTION pCrit) + @category: Support_Reference + + @input: pCrit - pointer to critical section that was initialized + + @return: SDIO_STATUS_SUCCESS on success. + + @notes: The critical section lock is released when this function returns + SDIO_STATUS_SUCCESS. + + @see also: CriticalSectionAcquire + + @example: see CriticalSectionAcquire + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline SDIO_STATUS CriticalSectionRelease(POS_CRITICALSECTION pCrit) { + call_spin_unlock(pCrit); + return SDIO_STATUS_SUCCESS; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Cleanup a critical section object + + @function name: CriticalSectionDelete + @prototype: void CriticalSectionDelete(POS_CRITICALSECTION pCrit) + @category: Support_Reference + + @input: pCrit - an initialized critical section object + + @return: SDIO_STATUS_SUCCESS on success. + + @notes: + + @see also: CriticalSectionInit, CriticalSectionAcquire, CriticalSectionRelease + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline void CriticalSectionDelete(POS_CRITICALSECTION pCrit) { + return; +} + +/* internal use */ +static inline SDIO_STATUS SignalInitialize(POS_SIGNAL pSignal) { + sema_init(pSignal, 0); + return SDIO_STATUS_SUCCESS; +} +/* internal use */ +static inline void SignalDelete(POS_SIGNAL pSignal) { + return; +} +/* internal use */ +static inline SDIO_STATUS SignalWaitInterruptible(POS_SIGNAL pSignal) { + DBG_ASSERT_WITH_MSG(!NonSchedulable(),"SignalWaitInterruptible not allowed\n"); + if (down_interruptible(pSignal) == 0) { + return SDIO_STATUS_SUCCESS; + } else { + return SDIO_STATUS_INTERRUPTED; + } +} +/* internal use */ +static inline SDIO_STATUS SignalWait(POS_SIGNAL pSignal) { + DBG_ASSERT_WITH_MSG(!NonSchedulable(),"SignalWait not allowed\n"); + down(pSignal); + return SDIO_STATUS_SUCCESS; +} + +/* internal use */ +static inline SDIO_STATUS SignalSet(POS_SIGNAL pSignal) { + up(pSignal); + return SDIO_STATUS_SUCCESS; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Initialize a semaphore object. + + @function name: SemaphoreInitialize + @prototype: SDIO_STATUS SemaphoreInitialize(POS_SEMAPHORE pSem, UINT value) + @category: Support_Reference + + @input: value - initial value of the semaphore + + @output: pSem - pointer to a semaphore object to initialize + + @return: SDIO_STATUS_SUCCESS on success. + + @notes: SemaphoreDelete() must be called to cleanup any resources + associated with the semaphore + + @see also: SemaphoreDelete, SemaphorePend, SemaphorePendInterruptable + + @example: To initialize a semaphore: + status = SemaphoreInitialize(&pDevice->ResourceSem,1); + if (!SDIO_SUCCESS(status)) { + .. failed + return status; + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline SDIO_STATUS SemaphoreInitialize(POS_SEMAPHORE pSem, UINT value) { + sema_init(pSem, value); + return SDIO_STATUS_SUCCESS; +} +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Cleanup a semaphore object. + + @function name: SemaphoreDelete + @prototype: void SemaphoreDelete(POS_SEMAPHORE pSem) + @category: Support_Reference + + @input: pSem - pointer to a semaphore object to cleanup + + @return: + + @notes: + + @see also: SemaphoreInitialize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline void SemaphoreDelete(POS_SEMAPHORE pSem) { + return; +} +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Acquire the semaphore or pend if the resource is not available + + @function name: SemaphorePend + @prototype: SDIO_STATUS SemaphorePend(POS_SEMAPHORE pSem) + @category: Support_Reference + + @input: pSem - pointer to an initialized semaphore object + + @return: SDIO_STATUS_SUCCESS on success. + + @notes: If the semaphore count is zero this function blocks until the count + becomes non-zero, otherwise the count is decremented and execution + continues. While waiting, the task/thread cannot be interrupted. + If the task or thread should be interruptible, use SemaphorePendInterruptible. + On some OSes SemaphorePend and SemaphorePendInterruptible behave the same. + + @see also: SemaphorePendInterruptable, SemaphorePost + @example: To wait for a resource using a semaphore: + status = SemaphorePend(&pDevice->ResourceSem); + if (!SDIO_SUCCESS(status)) { + .. failed + return status; + } + ... resource acquired + SemaphorePost(&pDevice->ResourceSem); + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline SDIO_STATUS SemaphorePend(POS_SEMAPHORE pSem) { + DBG_ASSERT_WITH_MSG(!NonSchedulable(),"SemaphorePend not allowed\n"); + down(pSem); + return SDIO_STATUS_SUCCESS; +} +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Acquire the semaphore or pend if the resource is not available + + @function name: SemaphorePendInterruptable + @prototype: SDIO_STATUS SemaphorePendInterruptable(POS_SEMAPHORE pSem) + @category: Support_Reference + + @input: pSem - pointer to an initialized semaphore object + + @return: SDIO_STATUS_SUCCESS on success. + + @notes: If the semaphore count is zero this function blocks until the count + becomes non-zero, otherwise the count is decremented and execution + continues. While waiting, the task/thread can be interrupted. + If the task or thread should not be interruptible, use SemaphorePend. + + @see also: SemaphorePend, SemaphorePost + @example: To wait for a resource using a semaphore: + status = SemaphorePendInterruptable(&pDevice->ResourceSem); + if (!SDIO_SUCCESS(status)) { + .. failed, could have been interrupted + return status; + } + ... resource acquired + SemaphorePost(&pDevice->ResourceSem); + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline SDIO_STATUS SemaphorePendInterruptable(POS_SEMAPHORE pSem) { + DBG_ASSERT_WITH_MSG(!NonSchedulable(),"SemaphorePendInterruptable not allowed\n"); + if (down_interruptible(pSem) == 0) { + return SDIO_STATUS_SUCCESS; + } else { + return SDIO_STATUS_INTERRUPTED; + } +} +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Post a semaphore. + + @function name: SemaphorePost + @prototype: SDIO_STATUS SemaphorePost(POS_SEMAPHORE pSem) + @category: Support_Reference + + @input: pSem - pointer to an initialized semaphore object + + @return: SDIO_STATUS_SUCCESS on success. + + @notes: This function increments the semaphore count. + + @see also: SemaphorePend, SemaphorePendInterruptable. + @example: Posting a semaphore: + status = SemaphorePendInterruptable(&pDevice->ResourceSem); + if (!SDIO_SUCCESS(status)) { + .. failed, could have been interrupted + return status; + } + ... resource acquired + // post the semaphore + SemaphorePost(&pDevice->ResourceSem); + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline SDIO_STATUS SemaphorePost(POS_SEMAPHORE pSem) { + DBG_ASSERT_WITH_MSG(!NonSchedulable(),"SemaphorePost not allowed\n"); + up(pSem); + return SDIO_STATUS_SUCCESS; +} + + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Allocate a block of kernel accessible memory + + @function name: KernelAlloc + @prototype: PVOID KernelAlloc(UINT size) + @category: Support_Reference + + @input: size - size of memory block to allocate + + @return: pointer to the allocated memory, NULL if allocation failed + + @notes: For operating systems that use paging, the allocated memory is always + non-paged memory. Caller should only use KernelFree() to release the + block of memory. This call can potentially block and should only be called + from a schedulable context. Use KernelAllocIrqSafe() if the allocation + must be made from a non-schedulable context. + + @see also: KernelFree, KernelAllocIrqSafe + @example: allocating memory: + pBlock = KernelAlloc(1024); + if (pBlock == NULL) { + .. failed, no memory + return SDIO_STATUS_INSUFFICIENT_RESOURCES; + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline PVOID KernelAlloc(UINT size) { + PVOID pMem = kmalloc(size, GFP_KERNEL); + if (pMem != NULL) { memset(pMem,0,size); } + return pMem; +} +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Free a block of kernel accessible memory. + + @function name: KernelFree + @prototype: void KernelFree(PVOID ptr) + @category: Support_Reference + + @input: ptr - pointer to memory allocated with KernelAlloc() + + @return: + + @notes: Caller should only use KernelFree() to release memory that was allocated + with KernelAlloc(). + + @see also: KernelAlloc + @example: KernelFree(pBlock); + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline void KernelFree(PVOID ptr) { + kfree(ptr); +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Allocate a block of kernel accessible memory in an IRQ-safe manner + + @function name: KernelAllocIrqSafe + @prototype: PVOID KernelAllocIrqSafe(UINT size) + @category: Support_Reference + + @input: size - size of memory block to allocate + + @return: pointer to the allocated memory, NULL if allocation failed + + @notes: This variant of KernelAlloc allows the allocation of small blocks of + memory from an ISR or from a context where scheduling has been disabled. + The allocations should be small as the memory is typically allocated + from a critical heap. The caller should only use KernelFreeIrqSafe() + to release the block of memory. + + @see also: KernelAlloc, KernelFreeIrqSafe + @example: allocating memory: + pBlock = KernelAllocIrqSafe(16); + if (pBlock == NULL) { + .. failed, no memory + return SDIO_STATUS_INSUFFICIENT_RESOURCES; + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline PVOID KernelAllocIrqSafe(UINT size) { + return kmalloc(size, GFP_ATOMIC); +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Free a block of kernel accessible memory. + + @function name: KernelFreeIrqSafe + @prototype: void KernelFreeIrqSafe(PVOID ptr) + @category: Support_Reference + + @input: ptr - pointer to memory allocated with KernelAllocIrqSafe() + + @return: + + @notes: Caller should only use KernelFreeIrqSafe() to release memory that was allocated + with KernelAllocIrqSafe(). + + @see also: KernelAllocIrqSafe + @example: KernelFreeIrqSafe(pBlock); + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline void KernelFreeIrqSafe(PVOID ptr) { + kfree(ptr); +} + +/* error status conversions */ +static inline SYSTEM_STATUS SDIOErrorToOSError(SDIO_STATUS status) { + switch (status) { + case SDIO_STATUS_SUCCESS: + return 0; + case SDIO_STATUS_INVALID_PARAMETER: + return -EINVAL; + case SDIO_STATUS_PENDING: + return -EAGAIN; /* try again */ + case SDIO_STATUS_DEVICE_NOT_FOUND: + return -ENXIO; + case SDIO_STATUS_DEVICE_ERROR: + return -EIO; + case SDIO_STATUS_INTERRUPTED: + return -EINTR; + case SDIO_STATUS_NO_RESOURCES: + return -ENOMEM; + case SDIO_STATUS_ERROR: + default: + return -EFAULT; + } +} +static inline SDIO_STATUS OSErrorToSDIOError(SYSTEM_STATUS status) { + if (status >=0) { + return SDIO_STATUS_SUCCESS; + } + switch (status) { + case -EINVAL: + return SDIO_STATUS_INVALID_PARAMETER; + case -ENXIO: + return SDIO_STATUS_DEVICE_NOT_FOUND; + case -EIO: + return SDIO_STATUS_DEVICE_ERROR; + case -EINTR: + return SDIO_STATUS_INTERRUPTED; + case -ENOMEM: + return SDIO_STATUS_NO_RESOURCES; + case -EFAULT: + return SDIO_STATUS_ERROR; + default: + return SDIO_STATUS_ERROR; + } +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Sleep or delay the execution context for a number of milliseconds. + + @function name: OSSleep + @prototype: SDIO_STATUS OSSleep(INT SleepInterval) + @category: Support_Reference + + @input: SleepInterval - time in milliseconds to put the execution context to sleep + + @return: SDIO_STATUS_SUCCESS if sleep succeeded. + + @notes: Caller should be in a context that allows it to sleep or block. The + minimum duration of sleep may be greater than 1 MS on some platforms and OSes. + + @see also: OSSleep + @example: Using sleep to delay + EnableSlotPower(pSlot); + // wait for power to settle + status = OSSleep(100); + if (!SDIO_SUCCESS(status)){ + // failed.. + } + + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static inline SDIO_STATUS OSSleep(INT SleepInterval) { + UINT32 delta; + + DBG_ASSERT_WITH_MSG(!NonSchedulable(),"OSSleep not allowed\n"); + /* convert timeout to ticks */ + delta = (SleepInterval * HZ)/1000; + if (delta == 0) { + delta = 1; + } + set_current_state(TASK_INTERRUPTIBLE); + if (schedule_timeout(delta) != 0) { + return SDIO_STATUS_INTERRUPTED; + } + return SDIO_STATUS_SUCCESS; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: get the OSs device object + + @function name: SD_GET_OS_DEVICE + @prototype: POS_DEVICE SD_GET_OS_DEVICE(PSDDEVICE pDevice) + @category: Support_Reference + + @input: pDevice - the device on the HCD + + @return: pointer to the OSs device + + @see also: + @example: obtain low level device + pFunctionContext->GpsDevice.Port.dev = SD_GET_OS_DEVICE(pDevice); + + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SD_GET_OS_DEVICE(pDevice) &((pDevice)->Device.dev) + + +#ifdef __iomem + /* new type checking in 2.6.9 */ + /* I/O Access macros */ +#define _READ_DWORD_REG(reg) \ + readl((const volatile void __iomem *)(reg)) +#define _READ_WORD_REG(reg) \ + readw((const volatile void __iomem *)(reg)) +#define _READ_BYTE_REG(reg) \ + readb((const volatile void __iomem *)(reg)) +#define _WRITE_DWORD_REG(reg,value) \ + writel((value),(volatile void __iomem *)(reg)) +#define _WRITE_WORD_REG(reg,value) \ + writew((value),(volatile void __iomem *)(reg)) +#define _WRITE_BYTE_REG(reg,value) \ + writeb((value),(volatile void __iomem *)(reg)) +#else + /* I/O Access macros */ +#define _READ_DWORD_REG(reg) \ + readl((reg)) +#define _READ_WORD_REG(reg) \ + readw((reg)) +#define _READ_BYTE_REG(reg) \ + readb((reg)) +#define _WRITE_DWORD_REG(reg,value) \ + writel((value),(reg)) +#define _WRITE_WORD_REG(reg,value) \ + writew((value),(reg)) +#define _WRITE_BYTE_REG(reg,value) \ + writeb((value),(reg)) +#endif + /* atomic operators */ +static inline ATOMIC_FLAGS AtomicTest_Set(volatile ATOMIC_FLAGS *pValue, INT BitNo) { + return test_and_set_bit(BitNo,(ATOMIC_FLAGS *)pValue); +} +static inline ATOMIC_FLAGS AtomicTest_Clear(volatile ATOMIC_FLAGS *pValue, INT BitNo) { + return test_and_clear_bit(BitNo,(ATOMIC_FLAGS *)pValue); +} + +struct _OSKERNEL_HELPER; + +typedef THREAD_RETURN (*PHELPER_FUNCTION)(struct _OSKERNEL_HELPER *); + +typedef struct _OSKERNEL_HELPER { + PKERNEL_TASK pTask; + BOOL ShutDown; + OS_SIGNAL WakeSignal; + struct completion Completion; + PVOID pContext; + PHELPER_FUNCTION pHelperFunc; +}OSKERNEL_HELPER, *POSKERNEL_HELPER; + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Wake the helper thread + + @function name: SD_WAKE_OS_HELPER + @prototype: SD_WAKE_OS_HELPER(POSKERNEL_HELPER pOSHelper) + @category: Support_Reference + + @input: pOSHelper - the OS helper object + + @return: SDIO_STATUS + + @see also: SDLIB_OSCreateHelper + + @example: Waking up a helper thread + status = SD_WAKE_OS_HELPER(&pInstance->OSHelper); + + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SD_WAKE_OS_HELPER(p) SignalSet(&(p)->WakeSignal) +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Obtains the context for the helper function + + @function name: SD_GET_OS_HELPER_CONTEXT + @prototype: SD_GET_OS_HELPER_CONTEXT(POSKERNEL_HELPER pOSHelper) + @category: Support_Reference + + @input: pOSHelper - the OS helper object + + @return: helper specific context + + @notes: This macro should only be called by the function associated with + the helper object. + + @see also: SDLIB_OSCreateHelper + + @example: Getting the helper specific context + PMYCONTEXT pContext = (PMYCONTEXT)SD_GET_OS_HELPER_CONTEXT(pHelper); + + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SD_GET_OS_HELPER_CONTEXT(p) (p)->pContext +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Check helper function shut down flag. + + @function name: SD_IS_HELPER_SHUTTING_DOWN + @prototype: SD_IS_HELPER_SHUTTING_DOWN(POSKERNEL_HELPER pOSHelper) + @category: Support_Reference + + @input: pOSHelper - the OS helper object + + @return: TRUE if shutting down, else FALSE + + @notes: This macro should only be called by the function associated with + the helper object. The function should call this macro when it + unblocks from the call to SD_WAIT_FOR_WAKEUP(). If this function + returns TRUE, the function should clean up and exit. + + @see also: SDLIB_OSCreateHelper , SD_WAIT_FOR_WAKEUP + + @example: Checking for shutdown + while(1) { + status = SD_WAIT_FOR_WAKEUP(pHelper); + if (!SDIO_SUCCESS(status)) { + break; + } + if (SD_IS_HELPER_SHUTTING_DOWN(pHelper)) { + ... shutting down + break; + } + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SD_IS_HELPER_SHUTTING_DOWN(p) (p)->ShutDown +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Suspend and wait for wakeup signal + + @function name: SD_WAIT_FOR_WAKEUP + @prototype: SD_WAIT_FOR_WAKEUP(POSKERNEL_HELPER pOSHelper) + @category: Support_Reference + + @input: pOSHelper - the OS helper object + + @return: SDIO_STATUS + + @notes: This macro should only be called by the function associated with + the helper object. The function should call this function to suspend (block) + itself and wait for a wake up signal. The function should always check + whether the function should exit by calling SD_IS_HELPER_SHUTTING_DOWN. + + @see also: SDLIB_OSCreateHelper , SD_IS_HELPER_SHUTTING_DOWN + + @example: block on the wake signal + while(1) { + status = SD_WAIT_FOR_WAKEUP(pHelper); + if (!SDIO_SUCCESS(status)) { + break; + } + if (SD_IS_HELPER_SHUTTING_DOWN(pHelper)) { + ... shutting down + break; + } + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SD_WAIT_FOR_WAKEUP(p) SignalWait(&(p)->WakeSignal); + +#define CT_LE16_TO_CPU_ENDIAN(x) __le16_to_cpu(x) +#define CT_LE32_TO_CPU_ENDIAN(x) __le32_to_cpu(x) +#define CT_CPU_ENDIAN_TO_LE16(x) __cpu_to_le16(x) +#define CT_CPU_ENDIAN_TO_LE32(x) __cpu_to_le32(x) + +#define CT_CPU_ENDIAN_TO_BE16(x) __cpu_to_be16(x) +#define CT_CPU_ENDIAN_TO_BE32(x) __cpu_to_be32(x) +#define CT_BE16_TO_CPU_ENDIAN(x) __be16_to_cpu(x) +#define CT_BE32_TO_CPU_ENDIAN(x) __be32_to_cpu(x) +#endif /* __CPSYSTEM_LINUX_H___ */ + diff --git a/include/linux/sdio/mmc_defs.h b/include/linux/sdio/mmc_defs.h new file mode 100644 index 00000000000..576ebd76d67 --- /dev/null +++ b/include/linux/sdio/mmc_defs.h @@ -0,0 +1,103 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: mmc_defs.h + +@abstract: MMC definitions not already defined in _sdio_defs.h + +@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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef ___MMC_DEFS_H___ +#define ___MMC_DEFS_H___ + +#define MMC_MAX_BUS_CLOCK 20000000 /* max clock speed in hz */ +#define MMC_HS_MAX_BUS_CLOCK 52000000 /* MMC PLUS (high speed) max clock rate in hz */ + +/* R2 (CSD) macros */ +#define GET_MMC_CSD_TRANS_SPEED(pR) (pR)[12] +#define GET_MMC_SPEC_VERSION(pR) (((pR)[15] >> 2) & 0x0F) +#define MMC_SPEC_1_0_TO_1_2 0x00 +#define MMC_SPEC_1_4 0x01 +#define MMC_SPEC_2_0_TO_2_2 0x02 +#define MMC_SPEC_3_1 0x03 +#define MMC_SPEC_4_0_TO_4_1 0x04 + +#define MMC_CMD_SWITCH 6 +#define MMC_CMD8 8 + +#define MMC_SWITCH_CMD_SET 0 +#define MMC_SWITCH_SET_BITS 1 +#define MMC_SWITCH_CLEAR_BITS 2 +#define MMC_SWITCH_WRITE_BYTE 3 +#define MMC_SWITCH_CMD_SET0 0 +#define MMC_SWITCH_BUILD_ARG(cmdset,access,index,value) \ + (((cmdset) & 0x07) | (((access) & 0x03) << 24) | (((index) & 0xFF) << 16) | (((value) & 0xFF) << 8)) + +#define MMC_EXT_CSD_SIZE 512 + +#define MMC_EXT_S_CMD_SET_OFFSET 504 +#define MMC_EXT_MIN_PERF_W_8_52_OFFSET 210 +#define MMC_EXT_MIN_PERF_R_8_52_OFFSET 209 +#define MMC_EXT_MIN_PERF_W_8_26_4_52_OFFSET 208 +#define MMC_EXT_MIN_PERF_R_8_26_4_52_OFFSET 207 +#define MMC_EXT_MIN_PERF_W_4_26_OFFSET 206 +#define MMC_EXT_MIN_PERF_R_4_56_OFFSET 205 +#define MMC_EXT_PWR_CL_26_360_OFFSET 203 +#define MMC_EXT_PWR_CL_52_360_OFFSET 202 +#define MMC_EXT_PWR_CL_26_195_OFFSET 201 +#define MMC_EXT_PWR_CL_52_195_OFFSET 200 +#define MMC_EXT_GET_PWR_CLASS(reg) ((reg) & 0xF) +#define MMC_EXT_MAX_PWR_CLASSES 16 +#define MMC_EXT_CARD_TYPE_OFFSET 196 +#define MMC_EXT_CARD_TYPE_HS_52 (1 << 1) +#define MMC_EXT_CARD_TYPE_HS_26 (1 << 0) +#define MMC_EXT_CSD_VER_OFFSET 194 +#define MMC_EXT_VER_OFFSET 192 +#define MMC_EXT_VER_1_0 0 +#define MMC_EXT_VER_1_1 1 +#define MMC_EXT_CMD_SET_OFFSET 191 +#define MMC_EXT_CMD_SET_REV_OFFSET 189 +#define MMC_EXT_PWR_CLASS_OFFSET 187 +#define MMC_EXT_HS_TIMING_OFFSET 185 +#define MMC_EXT_HS_TIMING_ENABLE 0x01 +#define MMC_EXT_BUS_WIDTH_OFFSET 183 +#define MMC_EXT_BUS_WIDTH_1_BIT 0x00 +#define MMC_EXT_BUS_WIDTH_4_BIT 0x01 +#define MMC_EXT_BUS_WIDTH_8_BIT 0x02 + +#endif diff --git a/include/linux/sdio/sdio_busdriver.h b/include/linux/sdio/sdio_busdriver.h new file mode 100644 index 00000000000..b431d3de565 --- /dev/null +++ b/include/linux/sdio/sdio_busdriver.h @@ -0,0 +1,1435 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_busdriver.h + +@abstract: include file for registration of SDIO function drivers + and SDIO host controller bus drivers. + +@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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __SDIO_BUSDRIVER_H___ +#define __SDIO_BUSDRIVER_H___ + +typedef UINT8 CT_VERSION_CODE; +#define CT_SDIO_STACK_VERSION_CODE ((CT_VERSION_CODE)0x26) /* version code that must be set in various structures */ +#define CT_SDIO_STACK_VERSION_MAJOR(v) (((v) & 0xF0) >> 4) +#define CT_SDIO_STACK_VERSION_MINOR(v) (((v) & 0x0F)) +#define SET_SDIO_STACK_VERSION(p) (p)->Version = CT_SDIO_STACK_VERSION_CODE +#define GET_SDIO_STACK_VERSION(p) (p)->Version +#define GET_SDIO_STACK_VERSION_MAJOR(p) CT_SDIO_STACK_VERSION_MAJOR(GET_SDIO_STACK_VERSION(p)) +#define GET_SDIO_STACK_VERSION_MINOR(p) CT_SDIO_STACK_VERSION_MINOR(GET_SDIO_STACK_VERSION(p)) +#include "sdlist.h" + +/* card flags */ +typedef UINT16 CARD_INFO_FLAGS; +#define CARD_MMC 0x0001 /* Multi-media card */ +#define CARD_SD 0x0002 /* SD-Memory present */ +#define CARD_SDIO 0x0004 /* SDIO present */ +#define CARD_RAW 0x0008 /* Raw card */ +#define CARD_COMBO (CARD_SD | CARD_SDIO) /* SDIO with SD */ +#define CARD_TYPE_MASK 0x000F /* card type mask */ +#define CARD_SD_WP 0x0010 /* SD WP on */ +#define CARD_PSEUDO 0x0020 /* pseudo card (internal use) */ +#define CARD_HIPWR 0x0040 /* card can use more than 200mA (SDIO 1.1 or greater)*/ +#define GET_CARD_TYPE(flags) ((flags) & CARD_TYPE_MASK) + +/* bus mode and clock rate */ +typedef UINT32 SD_BUSCLOCK_RATE; /* clock rate in hz */ +typedef UINT16 SD_BUSMODE_FLAGS; +#define SDCONFIG_BUS_WIDTH_RESERVED 0x00 +#define SDCONFIG_BUS_WIDTH_SPI 0x01 +#define SDCONFIG_BUS_WIDTH_1_BIT 0x02 +#define SDCONFIG_BUS_WIDTH_4_BIT 0x03 +#define SDCONFIG_BUS_WIDTH_MMC8_BIT 0x04 +#define SDCONFIG_BUS_WIDTH_MASK 0x0F +#define SDCONFIG_SET_BUS_WIDTH(flags,width) \ +{ \ + (flags) &= ~SDCONFIG_BUS_WIDTH_MASK; \ + (flags) |= (width); \ +} +#define SDCONFIG_GET_BUSWIDTH(flags) ((flags) & SDCONFIG_BUS_WIDTH_MASK) +#define SDCONFIG_BUS_MODE_SPI_NO_CRC 0x40 /* SPI bus is operating with NO CRC */ +#define SDCONFIG_BUS_MODE_SD_HS 0x80 /* set interface to SD high speed mode */ +#define SDCONFIG_BUS_MODE_MMC_HS 0x20 /* set interface to MMC high speed mode */ + +typedef UINT16 SD_SLOT_CURRENT; /* slot current in mA */ + +typedef UINT8 SLOT_VOLTAGE_MASK; /* slot voltage */ +#define SLOT_POWER_3_3V 0x01 +#define SLOT_POWER_3_0V 0x02 +#define SLOT_POWER_2_8V 0x04 +#define SLOT_POWER_2_0V 0x08 +#define SLOT_POWER_1_8V 0x10 +#define SLOT_POWER_1_6V 0x20 + +#define MAX_CARD_RESPONSE_BYTES 17 + +/* plug and play information for SD cards */ +typedef struct _SD_PNP_INFO { + UINT16 SDIO_ManufacturerCode; /* JEDEC Code */ + UINT16 SDIO_ManufacturerID; /* manf-specific ID */ + UINT8 SDIO_FunctionNo; /* function number 1-7 */ + UINT8 SDIO_FunctionClass; /* function class */ + UINT8 SDMMC_ManfacturerID; /* card CID's MANF-ID */ + UINT16 SDMMC_OEMApplicationID; /* card CID's OEMAPP-ID */ + CARD_INFO_FLAGS CardFlags; /* card flags */ +}SD_PNP_INFO, *PSD_PNP_INFO; + +#define IS_LAST_SDPNPINFO_ENTRY(id)\ + (((id)->SDIO_ManufacturerCode == 0) &&\ + ((id)->SDIO_ManufacturerID == 0) &&\ + ((id)->SDIO_FunctionNo == 0) &&\ + ((id)->SDIO_FunctionClass == 0) &&\ + ((id)->SDMMC_OEMApplicationID == 0) && \ + ((id)->CardFlags == 0)) + +/* card properties */ +typedef struct _CARD_PROPERTIES { + UINT8 IOFnCount; /* number of I/O functions */ + UINT8 SDIORevision; /* SDIO revision */ +#define SDIO_REVISION_1_00 0x00 +#define SDIO_REVISION_1_10 0x01 +#define SDIO_REVISION_1_20 0x02 + UINT8 SD_MMC_Revision; /* SD or MMC revision */ +#define SD_REVISION_1_01 0x00 +#define SD_REVISION_1_10 0x01 +#define MMC_REVISION_1_0_2_2 0x00 +#define MMC_REVISION_3_1 0x01 +#define MMC_REVISION_4_0 0x02 + UINT16 SDIO_ManufacturerCode; /* JEDEC Code */ + UINT16 SDIO_ManufacturerID; /* manf-specific ID */ + UINT32 CommonCISPtr; /* common CIS ptr */ + UINT16 RCA; /* relative card address */ + UINT8 SDIOCaps; /* SDIO card capabilities (refer to SDIO spec for decoding) */ + UINT8 CardCSD[MAX_CARD_RESPONSE_BYTES]; /* for SD/MMC cards */ + CARD_INFO_FLAGS Flags; /* card flags */ + SD_BUSCLOCK_RATE OperBusClock; /* operational bus clock (based on HCD limit)*/ + SD_BUSMODE_FLAGS BusMode; /* current card bus mode */ + UINT16 OperBlockLenLimit; /* operational bytes per block length limit*/ + UINT16 OperBlockCountLimit; /* operational number of blocks per transfer limit */ + UINT8 CardState; /* card state flags */ + SLOT_VOLTAGE_MASK CardVoltage; /* card operational voltage */ +#define CARD_STATE_REMOVED 0x01 +}CARD_PROPERTIES, *PCARD_PROPERTIES; + +/* SDREQUEST request flags */ +typedef UINT32 SDREQUEST_FLAGS; +/* write operation */ +#define SDREQ_FLAGS_DATA_WRITE 0x8000 +/* has data (read or write) */ +#define SDREQ_FLAGS_DATA_TRANS 0x4000 +/* command is an atomic APP command, requiring CMD55 to be issued */ +#define SDREQ_FLAGS_APP_CMD 0x2000 +/* transfer should be handled asynchronously */ +#define SDREQ_FLAGS_TRANS_ASYNC 0x1000 +/* host should skip the SPI response filter for this command */ +#define SDREQ_FLAGS_RESP_SKIP_SPI_FILT 0x0800 +/* host should skip the response check for this data transfer */ +#define SDREQ_FLAGS_DATA_SKIP_RESP_CHK 0x0400 +/* flag requesting a CMD12 be automatically issued by host controller */ +#define SDREQ_FLAGS_AUTO_CMD12 0x0200 +/* flag indicating that the data buffer meets HCD's DMA restrictions */ +#define SDREQ_FLAGS_DATA_DMA 0x0010 +/* indicate to host that this is a short and quick transfer, the HCD may optimize + * this request to reduce interrupt overhead */ +#define SDREQ_FLAGS_DATA_SHORT_TRANSFER 0x00010000 +/* indicate to the host that this is a raw request */ +#define SDREQ_FLAGS_RAW 0x00020000 +/* auto data transfer status check for MMC and Memory cards */ +#define SDREQ_FLAGS_AUTO_TRANSFER_STATUS 0x00100000 + +#define SDREQ_FLAGS_UNUSED1 0x00200000 +#define SDREQ_FLAGS_UNUSED2 0x00400000 +#define SDREQ_FLAGS_UNUSED3 0x00800000 +#define SDREQ_FLAGS_UNUSED4 0x01000000 +#define SDREQ_FLAGS_UNUSED5 0x02000000 + +/* the following flags are internal use only */ +#define SDREQ_FLAGS_FORCE_DEFERRED_COMPLETE 0x0100 +/* flag indicating that response has been converted (internal use) */ +#define SDREQ_FLAGS_RESP_SPI_CONVERTED 0x0040 +/* request was cancelled - internal use only */ +#define SDREQ_FLAGS_CANCELED 0x0020 +/* a barrier operation */ +#define SDREQ_FLAGS_BARRIER 0x00040000 +/* a pseudo bus request */ +#define SDREQ_FLAGS_PSEUDO 0x00080000 +/* queue to the head */ +#define SDREQ_FLAGS_QUEUE_HEAD 0x04000000 + +#define SDREQ_FLAGS_I_UNUSED1 0x08000000 +#define SDREQ_FLAGS_I_UNUSED2 0x10000000 +#define SDREQ_FLAGS_I_UNUSED3 0x20000000 +#define SDREQ_FLAGS_I_UNUSED4 0x40000000 +#define SDREQ_FLAGS_I_UNUSED5 0x80000000 + +/* response type mask */ +#define SDREQ_FLAGS_RESP_MASK 0x000F +#define GET_SDREQ_RESP_TYPE(flags) ((flags) & SDREQ_FLAGS_RESP_MASK) +#define IS_SDREQ_WRITE_DATA(flags) ((flags) & SDREQ_FLAGS_DATA_WRITE) +#define IS_SDREQ_DATA_TRANS(flags) ((flags) & SDREQ_FLAGS_DATA_TRANS) +#define IS_SDREQ_RAW(flags) ((flags) & SDREQ_FLAGS_RAW) +#define IS_SDREQ_FORCE_DEFERRED_COMPLETE(flags) ((flags) & SDREQ_FLAGS_FORCE_DEFERRED_COMPLETE) +#define SDREQ_FLAGS_NO_RESP 0x0000 +#define SDREQ_FLAGS_RESP_R1 0x0001 +#define SDREQ_FLAGS_RESP_R1B 0x0002 +#define SDREQ_FLAGS_RESP_R2 0x0003 +#define SDREQ_FLAGS_RESP_R3 0x0004 +#define SDREQ_FLAGS_RESP_MMC_R4 0x0005 /* not supported, for future use */ +#define SDREQ_FLAGS_RESP_MMC_R5 0x0006 /* not supported, for future use */ +#define SDREQ_FLAGS_RESP_R6 0x0007 +#define SDREQ_FLAGS_RESP_SDIO_R4 0x0008 +#define SDREQ_FLAGS_RESP_SDIO_R5 0x0009 + +struct _SDREQUEST; +struct _SDFUNCTION; + +typedef void (*PSDEQUEST_COMPLETION)(struct _SDREQUEST *); + +/* defines SD/MMC and SDIO requests for the RAW-mode API */ +typedef struct _SDREQUEST { + SDLIST SDList; /* internal use list*/ + UINT32 Argument; /* SD/SDIO/MMC 32 bit argument */ + SDREQUEST_FLAGS Flags; /* request flags */ + ATOMIC_FLAGS InternalFlags; /* internal use flags */ + UINT8 Command; /* SD/SDIO/MMC 8 bit command */ + UINT8 Response[MAX_CARD_RESPONSE_BYTES]; /* buffer for CMD response */ + UINT16 BlockCount; /* number of blocks to send/rcv */ + UINT16 BlockLen; /* length of each block */ + UINT16 DescriptorCount; /* number of DMA descriptor entries in pDataBuffer if DMA */ + PVOID pDataBuffer; /* starting address of buffer (or ptr to PSDDMA_DESCRIPTOR*/ + UINT32 DataRemaining; /* number of bytes remaining in the transfer (internal use) */ + PVOID pHcdContext; /* internal use context */ + PSDEQUEST_COMPLETION pCompletion; /* function driver completion routine */ + PVOID pCompleteContext; /* function driver completion context */ + SDIO_STATUS Status; /* completion status */ + struct _SDFUNCTION* pFunction; /* function driver that generated request (internal use)*/ + INT RetryCount; /* number of times to retry on error, non-data cmds only */ + PVOID pBdRsv1; /* reserved */ + PVOID pBdRsv2; + PVOID pBdRsv3; +}SDREQUEST, *PSDREQUEST; + + /* a request queue */ +typedef struct _SDREQUESTQUEUE { + SDLIST Queue; /* the queue of requests */ + BOOL Busy; /* busy flag */ +}SDREQUESTQUEUE, *PSDREQUESTQUEUE; + + +typedef UINT16 SDCONFIG_COMMAND; +/* SDCONFIG request flags */ +/* get operation */ +#define SDCONFIG_FLAGS_DATA_GET 0x8000 +/* put operation */ +#define SDCONFIG_FLAGS_DATA_PUT 0x4000 +/* host controller */ +#define SDCONFIG_FLAGS_HC_CONFIG 0x2000 +/* both */ +#define SDCONFIG_FLAGS_DATA_BOTH (SDCONFIG_FLAGS_DATA_GET | SDCONFIG_FLAGS_DATA_PUT) +/* no data */ +#define SDCONFIG_FLAGS_DATA_NONE 0x0000 + +/* SDCONFIG commands */ +#define SDCONFIG_GET_HCD_DEBUG (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_GET | 275) +#define SDCONFIG_SET_HCD_DEBUG (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_PUT | 276) + +/* custom hcd commands */ +#define SDCONFIG_GET_HOST_CUSTOM (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_GET | 300) +#define SDCONFIG_PUT_HOST_CUSTOM (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_PUT | 301) + +/* function commands */ +#define SDCONFIG_FUNC_ENABLE_DISABLE (SDCONFIG_FLAGS_DATA_PUT | 18) +#define SDCONFIG_FUNC_UNMASK_IRQ (SDCONFIG_FLAGS_DATA_NONE | 21) +#define SDCONFIG_FUNC_MASK_IRQ (SDCONFIG_FLAGS_DATA_NONE | 22) +#define SDCONFIG_FUNC_ACK_IRQ (SDCONFIG_FLAGS_DATA_NONE | 23) +#define SDCONFIG_FUNC_SPI_MODE_DISABLE_CRC (SDCONFIG_FLAGS_DATA_NONE | 24) +#define SDCONFIG_FUNC_SPI_MODE_ENABLE_CRC (SDCONFIG_FLAGS_DATA_NONE | 25) +#define SDCONFIG_FUNC_ALLOC_SLOT_CURRENT (SDCONFIG_FLAGS_DATA_PUT | 26) +#define SDCONFIG_FUNC_FREE_SLOT_CURRENT (SDCONFIG_FLAGS_DATA_NONE | 27) +#define SDCONFIG_FUNC_CHANGE_BUS_MODE (SDCONFIG_FLAGS_DATA_BOTH | 28) +#define SDCONFIG_FUNC_CHANGE_BUS_MODE_ASYNC (SDCONFIG_FLAGS_DATA_BOTH | 29) +#define SDCONFIG_FUNC_NO_IRQ_PEND_CHECK (SDCONFIG_FLAGS_DATA_NONE | 30) + +typedef UINT8 FUNC_ENABLE_DISABLE_FLAGS; +typedef UINT32 FUNC_ENABLE_TIMEOUT; + + /* function enable */ +typedef struct _SDCONFIG_FUNC_ENABLE_DISABLE_DATA { +#define SDCONFIG_DISABLE_FUNC 0x0000 +#define SDCONFIG_ENABLE_FUNC 0x0001 + FUNC_ENABLE_DISABLE_FLAGS EnableFlags; /* enable flags*/ + FUNC_ENABLE_TIMEOUT TimeOut; /* timeout in milliseconds */ + void (*pOpComplete)(PVOID Context, SDIO_STATUS status); /* reserved */ + PVOID pOpCompleteContext; /* reserved */ +}SDCONFIG_FUNC_ENABLE_DISABLE_DATA, *PSDCONFIG_FUNC_ENABLE_DISABLE_DATA; + + /* slot current allocation data */ +typedef struct _SDCONFIG_FUNC_SLOT_CURRENT_DATA { + SD_SLOT_CURRENT SlotCurrent; /* slot current to request in mA*/ +}SDCONFIG_FUNC_SLOT_CURRENT_DATA, *PSDCONFIG_FUNC_SLOT_CURRENT_DATA; + +/* slot bus mode configuration */ +typedef struct _SDCONFIG_BUS_MODE_DATA { + SD_BUSCLOCK_RATE ClockRate; /* clock rate in Hz */ + SD_BUSMODE_FLAGS BusModeFlags; /* bus mode flags */ + SD_BUSCLOCK_RATE ActualClockRate; /* actual rate in KHz */ +}SDCONFIG_BUS_MODE_DATA, *PSDCONFIG_BUS_MODE_DATA; + +/* defines configuration requests for the HCD */ +typedef struct _SDCONFIG { + SDCONFIG_COMMAND Cmd; /* configuration command */ + PVOID pData; /* configuration data */ + INT DataLength; /* config data length */ +}SDCONFIG, *PSDCONFIG; + +#define SET_SDCONFIG_CMD_INFO(pHdr,cmd,pC,len) \ +{ \ + (pHdr)->Cmd = (cmd); \ + (pHdr)->pData = (PVOID)(pC); \ + (pHdr)->DataLength = (len); \ +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get a pointer to the configuration command data. + + @function name: GET_SDCONFIG_CMD + @prototype: UNIT16 GET_SDCONFIG_CMD (PSDCONFIG pCommand) + @category: HD_Reference + + @input: pCommand - config command structure. + + @return: command code + + @notes: Implemented as a macro. This macro returns the command code for this + configuration request. + + @example: getting the command code: + cmd = GET_SDCONFIG_CMD(pConfig); + switch (cmd) { + case SDCONFIG_GET_WP: + .. get write protect switch position + break; + ... + } + + @see also: GET_SDCONFIG_CMD_LEN, GET_SDCONFIG_CMD_DATA + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define GET_SDCONFIG_CMD(pBuffer) ((pBuffer)->Cmd) +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get a pointer to the configuration command data. + + @function name: GET_SDCONFIG_CMD_LEN + @prototype: INT GET_SDCONFIG_CMD_LEN (PSDCONFIG pCommand) + @category: HD_Reference + + @input: pCommand - config command structure. + + @return: length of config command data + + @notes: Implemented as a macro. Host controller drivers can use this macro to extract + the number of bytes of command specific data. This can be used to validate the + config data buffer size. + + @example: getting the data length: + length = GET_SDCONFIG_CMD_LEN(pConfig); + if (length < CUSTOM_COMMAND_XXX_SIZE) { + ... invalid length + } + + @see also: GET_SDCONFIG_CMD, GET_SDCONFIG_CMD_DATA + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define GET_SDCONFIG_CMD_LEN(pBuffer) ((pBuffer)->DataLength) +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get a pointer to the configuration command data. + + @function name: GET_SDCONFIG_CMD_DATA + @prototype: (casted ptr) GET_SDCONFIG_CMD_DATA (type, PSDCONFIG pCommand) + @category: HD_Reference + + @input: type - pointer type to cast the returned pointer to. + pCommand - config command structure. + + @return: type-casted pointer to the command's data + + @notes: Implemented as a macro. Host controller drivers can use this macro to extract + a pointer to the command specific data in an HCD configuration request. + + @example: getting the pointer: + // get interrupt control data + pIntControl = GET_SDCONFIG_CMD_DATA(PSDCONFIG_SDIO_INT_CTRL_DATA,pConfig); + if (pIntControl->SlotIRQEnable) { + ... enable slot IRQ detection + } + + @see also: GET_SDCONFIG_CMD, GET_SDCONFIG_CMD_LEN + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define GET_SDCONFIG_CMD_DATA(type,pBuffer) ((type)((pBuffer)->pData)) +#define IS_SDCONFIG_CMD_GET(pBuffer) ((pBuffer)->Cmd & SDCONFIG_FLAGS_DATA_GET) +#define IS_SDCONFIG_CMD_PUT(pBuffer) ((pBuffer)->Cmd & SDCONFIG_FLAGS_DATA_PUT) + +struct _SDDEVICE; +struct _SDHCD; + +typedef UINT8 SD_FUNCTION_FLAGS; +#define SDFUNCTION_FLAG_REMOVING 0x01 + +/* function driver registration structure */ +typedef struct _SDFUNCTION { + CT_VERSION_CODE Version; /* version code of the SDIO stack */ + SDLIST SDList; /* internal use list*/ + PTEXT pName; /* name of registering driver */ + UINT MaxDevices; /* maximum number of devices supported by this function */ + UINT NumDevices; /* number of devices supported by this function */ + PSD_PNP_INFO pIds; /* null terminated table of supported devices*/ + BOOL (*pProbe)(struct _SDFUNCTION *pFunction, struct _SDDEVICE *pDevice);/* New device inserted */ + /* Device removed (NULL if not a hot-plug capable driver) */ + void (*pRemove)(struct _SDFUNCTION *pFunction, struct _SDDEVICE *pDevice); + SDIO_STATUS (*pSuspend)(struct _SDFUNCTION *pFunction, SDPOWER_STATE state); /* Device suspended */ + SDIO_STATUS (*pResume)(struct _SDFUNCTION *pFunction); /* Device woken up */ + /* Enable wake event */ + SDIO_STATUS (*pWake) (struct _SDFUNCTION *pFunction, SDPOWER_STATE state, BOOL enable); + PVOID pContext; /* function driver use data */ + OS_PNPDRIVER Driver; /* driver registration with base system */ + SDLIST DeviceList; /* the list of devices this driver is using*/ + OS_SIGNAL CleanupReqSig; /* wait for requests completion on cleanup (internal use) */ + SD_FUNCTION_FLAGS Flags; /* internal flags (internal use) */ +}SDFUNCTION, *PSDFUNCTION; + +typedef UINT8 HCD_EVENT; + + /* device info for SDIO functions */ +typedef struct _SDIO_DEVICE_INFO { + UINT32 FunctionCISPtr; /* function's CIS ptr */ + UINT32 FunctionCSAPtr; /* function's CSA ptr */ + UINT16 FunctionMaxBlockSize; /* function's reported max block size */ +}SDIO_DEVICE_INFO, *PSDIO_DEVICE_INFO; + + /* device info for SD/MMC card functions */ +typedef struct _SDMMC_INFO{ + UINT8 Unused; /* reserved */ +}SDMMC_INFO, *PSDMMC_INFO; + + /* union of SDIO function and device info */ +typedef union _SDDEVICE_INFO { + SDIO_DEVICE_INFO AsSDIOInfo; + SDMMC_INFO AsSDMMCInfo; +}SDDEVICE_INFO, *PSDDEVICE_INFO; + + +typedef UINT8 SD_DEVICE_FLAGS; +#define SDDEVICE_FLAG_REMOVING 0x01 + +/* inserted device description, describes an inserted card */ +typedef struct _SDDEVICE { + SDLIST SDList; /* internal use list*/ + SDLIST FuncListLink; /* internal use list */ + /* read/write request function */ + SDIO_STATUS (*pRequest)(struct _SDDEVICE *pDev, PSDREQUEST req); + /* get/set configuration */ + SDIO_STATUS (*pConfigure)(struct _SDDEVICE *pDev, PSDCONFIG config); + PSDREQUEST (*AllocRequest)(struct _SDDEVICE *pDev); /* allocate a request */ + void (*FreeRequest)(struct _SDDEVICE *pDev, PSDREQUEST pReq); /* free the request */ + void (*pIrqFunction)(PVOID pContext); /* interrupt routine, synchronous calls allowed */ + void (*pIrqAsyncFunction)(PVOID pContext); /* async IRQ function , asynch only calls */ + PVOID IrqContext; /* irq context */ + PVOID IrqAsyncContext; /* irq async context */ + PSDFUNCTION pFunction; /* function driver supporting this device */ + struct _SDHCD *pHcd; /* host controller this device is on (internal use) */ + SDDEVICE_INFO DeviceInfo; /* device info */ + SD_PNP_INFO pId[1]; /* id of this device */ + OS_PNPDEVICE Device; /* device registration with base system */ + SD_SLOT_CURRENT SlotCurrentAlloc; /* allocated slot current for this device/function (internal use) */ + SD_DEVICE_FLAGS Flags; /* internal use flags */ + CT_VERSION_CODE Version; /* version code of the bus driver */ +}SDDEVICE, *PSDDEVICE; + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get SDIO Bus Driver Version Major number + + @function name: SDDEVICE_GET_VERSION_MAJOR + @prototype: INT SDDEVICE_GET_VERSION_MAJOR(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: integer value for the major version + + @notes: Implemented as a macro. + + @see also: SDDEVICE_GET_VERSION_MINOR + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_VERSION_MAJOR(pDev) (GET_SDIO_STACK_VERSION_MAJOR(pDev)) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get SDIO Bus Driver Version Minor number + + @function name: SDDEVICE_GET_VERSION_MINOR + @prototype: INT SDDEVICE_GET_VERSION_MINOR(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: integer value for the minor version + + @notes: Implemented as a macro. + + @see also: SDDEVICE_GET_VERSION_MAJOR + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_VERSION_MINOR(pDev) (GET_SDIO_STACK_VERSION_MINOR(pDev)) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Test the SDIO revision for greater than or equal to 1.10 + + @function name: SDDEVICE_IS_SDIO_REV_GTEQ_1_10 + @prototype: BOOL SDDEVICE_IS_SDIO_REV_GTEQ_1_10(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: TRUE if the revision is greater than or equal to 1.10 + + @notes: Implemented as a macro. + + @see also: SDDEVICE_IS_SD_REV_GTEQ_1_10 + @see also: SDDEVICE_IS_MMC_REV_GTEQ_4_0 + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_IS_SDIO_REV_GTEQ_1_10(pDev) ((pDev)->pHcd->CardProperties.SDIORevision >= SDIO_REVISION_1_10) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Test the SDIO revision for greater than or equal to 1.20 + + @function name: SDDEVICE_IS_SDIO_REV_GTEQ_1_20 + @prototype: BOOL SDDEVICE_IS_SDIO_REV_GTEQ_1_20(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: TRUE if the revision is greater than or equal to 1.20 + + @notes: Implemented as a macro. + + @see also: SDDEVICE_IS_SD_REV_GTEQ_1_10 + @see also: SDDEVICE_IS_SDIO_REV_GTEQ_1_10 + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_IS_SDIO_REV_GTEQ_1_20(pDev) ((pDev)->pHcd->CardProperties.SDIORevision >= SDIO_REVISION_1_20) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Test the SD revision for greater than or equal to 1.10 + + @function name: SDDEVICE_IS_SD_REV_GTEQ_1_10 + @prototype: BOOL SDDEVICE_IS_SD_REV_GTEQ_1_10(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: TRUE if the revision is greater than or equal to 1.10 + + @notes: Implemented as a macro. + + @see also: SDDEVICE_IS_SDIO_REV_GTEQ_1_10 + @see also: SDDEVICE_IS_MMC_REV_GTEQ_4_0 + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_IS_SD_REV_GTEQ_1_10(pDev) ((pDev)->pHcd->CardProperties.SD_MMC_Revision >= SD_REVISION_1_10) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Test the MMC revision for greater than or equal to 4.0 + + @function name: SDDEVICE_IS_MMC_REV_GTEQ_4_0 + @prototype: BOOL SDDEVICE_IS_MMC_REV_GTEQ_4_0(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: TRUE if the revision is greater than or equal to 4.0 + + @notes: Implemented as a macro. + + @see also: SDDEVICE_IS_SDIO_REV_GTEQ_1_10 + @see also: SDDEVICE_IS_SD_REV_GTEQ_1_10 + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_IS_MMC_REV_GTEQ_4_0(pDev) ((pDev)->pHcd->CardProperties.SD_MMC_Revision >= MMC_REVISION_4_0) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Test for write protect enabled + + @function name: SDDEVICE_IS_CARD_WP_ON + @prototype: BOOL SDDEVICE_IS_CARD_WP_ON(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: TRUE if device is write protected. + + @notes: Implemented as a macro. + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_IS_CARD_WP_ON(pDev) ((pDev)->pHcd->CardProperties.Flags & CARD_SD_WP) + + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the device's manufacturer specific ID + + @function name: SDDEVICE_GET_SDIO_MANFID + @prototype: UINT16 SDDEVICE_GET_SDIO_MANFID(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: function number + + @notes: Implemented as a macro. + + @see also: SDDEVICE_GET_SDIO_MANFCODE + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SDIO_MANFID(pDev) (pDev)->pId[0].SDIO_ManufacturerID + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the device's manufacturer code + + @function name: SDDEVICE_GET_SDIO_MANFCODE + @prototype: UINT16 SDDEVICE_GET_SDIO_MANFCODE(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: function number + + @notes: Implemented as a macro. + + @see also: SDDEVICE_GET_SDIO_MANFID + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SDIO_MANFCODE(pDev) (pDev)->pId[0].SDIO_ManufacturerCode + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the device's function number + + @function name: SDDEVICE_GET_SDIO_FUNCNO + @prototype: UINT8 SDDEVICE_GET_SDIO_FUNCNO(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: function number + + @notes: Implemented as a macro. + + @see also: SDDEVICE_GET_SDIO_FUNC_CLASS + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SDIO_FUNCNO(pDev) (pDev)->pId[0].SDIO_FunctionNo + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the functions's class + + @function name: SDDEVICE_GET_SDIO_FUNC_CLASS + @prototype: UINT8 SDDEVICE_GET_SDIO_FUNC_CLASS(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: class number + + @notes: Implemented as a macro. + + @see also: SDDEVICE_GET_SDIO_FUNCNO + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SDIO_FUNC_CLASS(pDev) (pDev)->pId[0].SDIO_FunctionClass + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the functions's Card Information Structure pointer + + @function name: SDDEVICE_GET_SDIO_FUNC_CISPTR + @prototype: UINT32 SDDEVICE_GET_SDIO_FUNC_CISPTR(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: CIS offset + + @notes: Implemented as a macro. + + @see also: SDDEVICE_GET_SDIO_FUNC_CSAPTR + @see also: SDDEVICE_GET_SDIO_COMMON_CISPTR + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SDIO_FUNC_CISPTR(pDev)(pDev)->DeviceInfo.AsSDIOInfo.FunctionCISPtr + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the functions's Code Stoarge Area pointer + + @function name: SDDEVICE_GET_SDIO_FUNC_CSAPTR + @prototype: UINT32 SDDEVICE_GET_SDIO_FUNC_CSAPTR(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: CSA offset + + @notes: Implemented as a macro. + + @see also: SDDEVICE_GET_SDIO_FUNC_CISPTR + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SDIO_FUNC_CSAPTR(pDev)(pDev)->DeviceInfo.AsSDIOInfo.FunctionCSAPtr + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the functions's maximum reported block size + + @function name: SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE + @prototype: UINT16 SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: block size + + @notes: Implemented as a macro. + + @see also: + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(pDev) (pDev)->DeviceInfo.AsSDIOInfo.FunctionMaxBlockSize + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the common Card Information Structure pointer + + @function name: SDDEVICE_GET_SDIO_COMMON_CISPTR + @prototype: UINT32 SDDEVICE_GET_SDIO_COMMON_CISPTR(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: Common CIS Address (in SDIO address space) + + @notes: Implemented as a macro. + + @see also: SDDEVICE_GET_SDIO_FUNC_CSAPTR + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SDIO_COMMON_CISPTR(pDev) (pDev)->pHcd->CardProperties.CommonCISPtr + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the card capabilities + + @function name: SDDEVICE_GET_SDIO_CARD_CAPS + @prototype: UINT8 SDDEVICE_GET_SDIO_CARD_CAPS(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: 8-bit card capabilities register + + @notes: Implemented as a macro. Refer to SDIO spec for decoding. + + @see also: SDDEVICE_GET_CARD_FLAGS + @see also: SDDEVICE_GET_SDIOCARD_CAPS + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SDIO_CARD_CAPS(pDev) (pDev)->pHcd->CardProperties.SDIOCaps + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the card flags + + @function name: SDDEVICE_GET_CARD_FLAGS + @prototype: CARD_INFO_FLAGS SDDEVICE_GET_CARD_FLAGS(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: flags + + @notes: Implemented as a macro. + + @example: Get card type: + CARD_INFO_FLAGS flags; + flags = SDDEVICE_GET_CARD_FLAGS(pDevice); + switch(GET_CARD_TYPE(flags)) { + case CARD_MMC: // Multi-media card + ... + case CARD_SD: // SD-Memory present + ... + case CARD_SDIO: // SDIO card present + ... + case CARD_COMBO: //SDIO card with SD + ... + } + if (flags & CARD_SD_WP) { + ...SD write protect on + } + + @see also: SDDEVICE_GET_SDIO_CARD_CAPS + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_CARD_FLAGS(pDev) (pDev)->pHcd->CardProperties.Flags + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the Relative Card Address register + + @function name: SDDEVICE_GET_CARD_RCA + @prototype: UINT16 SDDEVICE_GET_CARD_RCA(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: register address + + @notes: Implemented as a macro. Refer to SDIO spec for decoding. + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_CARD_RCA(pDev) (pDev)->pHcd->CardProperties.RCA + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get operational bus clock + + @function name: SDDEVICE_GET_OPER_CLOCK + @prototype: SD_BUSCLOCK_RATE SDDEVICE_GET_OPER_CLOCK(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: clock rate + + @notes: Implemented as a macro. Returns the current bus clock rate. + This may be lower than reported by the card due to Host Controller, + Bus driver, or power management limitations. + + @see also: SDDEVICE_GET_MAX_CLOCK + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_OPER_CLOCK(pDev) (pDev)->pHcd->CardProperties.OperBusClock + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get maximum bus clock + + @function name: SDDEVICE_GET_MAX_CLOCK + @prototype: SD_BUSCLOCK_RATE SDDEVICE_GET_MAX_CLOCK(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: clock rate + + @notes: To obtain the current maximum clock rate use SDDEVICE_GET_OPER_CLOCK(). + This rate my be lower than the host controllers maximum obtained using + SDDEVICE_GET_MAX_CLOCK(). + + @see also: SDDEVICE_GET_OPER_CLOCK + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_MAX_CLOCK(pDev) (pDev)->pHcd->MaxClockRate + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get operational maximum block length. + + @function name: SDDEVICE_GET_OPER_BLOCK_LEN + @prototype: UINT16 SDDEVICE_GET_OPER_BLOCK_LEN(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: block size in bytes + + @notes: Implemented as a macro. Returns the maximum current block length. + This may be lower than reported by the card due to Host Controller, + Bus driver, or power management limitations. + + @see also: SDDEVICE_GET_MAX_BLOCK_LEN + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_OPER_BLOCK_LEN(pDev) (pDev)->pHcd->CardProperties.OperBlockLenLimit + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get maximum block length. + + @function name: SDDEVICE_GET_MAX_BLOCK_LEN + @prototype: UINT16 SDDEVICE_GET_MAX_BLOCK_LEN(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: block size in bytes + + @notes: Implemented as a macro. Use SDDEVICE_GET_OPER_BLOCK_LEN to obtain + the current block length. + + @see also: SDDEVICE_GET_OPER_BLOCK_LEN + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_MAX_BLOCK_LEN(pDev) (pDev)->pHcd->MaxBytesPerBlock + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get operational maximum block count. + + @function name: SDDEVICE_GET_OPER_BLOCKS + @prototype: UINT16 SDDEVICE_GET_OPER_BLOCKS(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: maximum number of blocks per transaction. + + @notes: Implemented as a macro. Returns the maximum current block count. + This may be lower than reported by the card due to Host Controller, + Bus driver, or power management limitations. + + @see also: SDDEVICE_GET_MAX_BLOCK_LEN + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_OPER_BLOCKS(pDev) (pDev)->pHcd->CardProperties.OperBlockCountLimit + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get maximum block count. + + @function name: SDDEVICE_GET_MAX_BLOCKS + @prototype: UINT16 SDDEVICE_GET_MAX_BLOCKS(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: maximum number of blocks per transaction. + + @notes: Implemented as a macro. Use SDDEVICE_GET_OPER_BLOCKS to obtain + the current block count. + + @see also: SDDEVICE_GET_OPER_BLOCKS + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_MAX_BLOCKS(pDev) (pDev)->pHcd->MaxBlocksPerTrans + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get applied slot voltage + + @function name: SDDEVICE_GET_SLOT_VOLTAGE_MASK + @prototype: SLOT_VOLTAGE_MASK SDDEVICE_GET_SLOT_VOLTAGE_MASK(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: slot voltage mask + + @notes: This function returns the applied voltage on the slot. The voltage value is a + mask having the following values: + SLOT_POWER_3_3V + SLOT_POWER_3_0V + SLOT_POWER_2_8V + SLOT_POWER_2_0V + SLOT_POWER_1_8V + SLOT_POWER_1_6V + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SLOT_VOLTAGE_MASK(pDev) (pDev)->pHcd->CardProperties.CardVoltage + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the Card Specific Data Register. + + @function name: SDDEVICE_GET_CARDCSD + @prototype: PUINT8 SDDEVICE_GET_CARDCSD(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: UINT8 CardCSD[MAX_CARD_RESPONSE_BYTES] array of CSD data. + + @notes: Implemented as a macro. + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_CARDCSD(pDev) (pDev)->pHcd->CardProperties.CardCSD + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the bus mode flags + + @function name: SDDEVICE_GET_BUSMODE_FLAGS + @prototype: SD_BUSMODE_FLAGS SDDEVICE_GET_BUSMODE_FLAGS(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: + + @notes: Implemented as a macro. This function returns the raw bus mode flags. This + is useful for function drivers that wish to override the bus clock without + modifying the current bus mode. + + @see also: SDDEVICE_GET_BUSWIDTH + @see also: SDCONFIG_BUS_MODE_CTRL + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_BUSMODE_FLAGS(pDev) (pDev)->pHcd->CardProperties.BusMode + + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the bus width. + + @function name: SDDEVICE_GET_BUSWIDTH + @prototype: UINT8 SDDEVICE_GET_BUSWIDTH(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: bus width: SDCONFIG_BUS_WIDTH_SPI, SDCONFIG_BUS_WIDTH_1_BIT, SDCONFIG_BUS_WIDTH_4_BIT + + @notes: Implemented as a macro. + + @see also: SDDEVICE_IS_BUSMODE_SPI + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_BUSWIDTH(pDev) SDCONFIG_GET_BUSWIDTH((pDev)->pHcd->CardProperties.BusMode) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Is bus in SPI mode. + + @function name: SDDEVICE_IS_BUSMODE_SPI + @prototype: BOOL SDDEVICE_IS_BUSMODE_SPI(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: TRUE, SPI mode. + + @notes: Implemented as a macro. + + @see also: SDDEVICE_GET_BUSWIDTH + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_IS_BUSMODE_SPI(pDev) (SDDEVICE_GET_BUSWIDTH(pDev) == SDCONFIG_BUS_WIDTH_SPI) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Send a request to a device. + + @function name: SDDEVICE_CALL_REQUEST_FUNC + @prototype: SDIO_STATUS SDDEVICE_CALL_REQUEST_FUNC(PSDDEVICE pDevice, PSDREQUEST pRequest) + @category: PD_Reference + + @input: pDevice - the target device for this request + @input: pRequest - the request to be sent + + @output: none + + @return: SDIO_STATUS + + @notes: Sends a request to the specified device. If the request is successfully sent, then + the response flags can be checked to detemine the result of the request. + + @example: Example of sending a request to a device: + PSDREQUEST pReq = NULL; + //allocate a request + pReq = SDDeviceAllocRequest(pDevice); + if (NULL == pReq) { + return SDIO_STATUS_NO_RESOURCES; + } + //initialize the request + SDLIB_SetupCMD52Request(FuncNo, Address, Write, *pData, pReq); + //send the request to the target + status = SDDEVICE_CALL_REQUEST_FUNC(pDevice,pReq); + if (!SDIO_SUCCESS(status)) { + break; + } + //check the request response (based on the request type) + if (SD_R5_GET_RESP_FLAGS(pReq->Response) & SD_R5_ERRORS) { + ... + } + if (!Write) { + // store the byte + *pData = SD_R5_GET_READ_DATA(pReq->Response); + } + //free the request + SDDeviceFreeRequest(pDevice,pReq); + ... + + @see also: SDDeviceAllocRequest + @see also: SDDEVICE_CALL_CONFIG_FUNC + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_CALL_REQUEST_FUNC(pDev,pReq) (pDev)->pRequest((pDev),(pReq)) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Send configuration to a device. + + @function name: SDDEVICE_CALL_CONFIG_FUNC + @prototype: SDIO_STATUS SDDEVICE_CALL_CONFIG_FUNC(PSDDEVICE pDevice, PSDCONFIG pConfigure) + @category: PD_Reference + + @input: pDevice - the target device for this request + @input: pConfigure - configuration request + + @output: none + + @return: SDIO_STATUS + + @notes: Sends a configuration request to the specified device. + + @example: Example of sending a request to a device: + SDCONFIG configHdr; + SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData; + fData.EnableFlags = SDCONFIG_ENABLE_FUNC; + fData.TimeOut = 500; + SET_SDCONFIG_CMD_INFO(&configHdr, SDCONFIG_FUNC_ENABLE_DISABLE, fData, sizeof(fData)); + return SDDEVICE_CALL_CONFIG_FUNC(pDevice, &configHdr); + + @see also: SDLIB_IssueConfig + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_CALL_CONFIG_FUNC(pDev,pCfg) (pDev)->pConfigure((pDev),(pCfg)) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Allocate a request structure. + + @function name: SDDeviceAllocRequest + @prototype: PSDREQUEST SDDeviceAllocRequest(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: request pointer or NULL if not available. + + @notes: This function must not be called in a non-schedulable (interrupts off) context. + Allocating memory on some OSes may block. + + @see also: SDDEVICE_CALL_REQUEST_FUNC + @see also: SDDeviceFreeRequest + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDeviceAllocRequest(pDev) (pDev)->AllocRequest((pDev)) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Free a request structure. + + @function name: SDDeviceFreeRequest + @prototype: void SDDeviceFreeRequest(PSDDEVICE pDevice, PSDREQUEST pRequest) + @category: PD_Reference + + @input: pDevice - the target device for this request + @input: pRequest - request allocated by SDDeviceAllocRequest(). + + @output: none + + @return: none + + @notes: This function must not be called in a non-schedulable (interrupts off) context. + Freeing memory on some OSes may block. + + @see also: SDDEVICE_CALL_REQUEST_FUNC + @see also: SDDeviceAllocRequest + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDeviceFreeRequest(pDev,pReq) (pDev)->FreeRequest((pDev),pReq) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Register an interrupt handler for a device. + + @function name: SDDEVICE_SET_IRQ_HANDLER + @prototype: void SDDEVICE_SET_IRQ_HANDLER(PSDDEVICE pDevice, + void (*pIrqFunction)(PVOID pContext), + PVOID pContext) + @category: PD_Reference + + @input: pDevice - the target device for this request + @input: pIrqFunction - the interrupt function to execute. + @input: pContext - context value passed into interrupt routine. + + @output: none + + @return: none + + @notes: The registered routine will be called upon each card interrupt. + The interrupt function should acknowledge the interrupt when it is + ready to handle more interrupts using: + SDLIB_IssueConfig(pDevice, SDCONFIG_FUNC_ACK_IRQ, NULL, 0); + The interrupt handler can perform synchronous request calls. + + @see also: SDDEVICE_SET_ASYNC_IRQ_HANDLER + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_SET_IRQ_HANDLER(pDev,pFn,pContext) \ +{ \ + (pDev)->pIrqFunction = (pFn); \ + (pDev)->IrqContext = (PVOID)(pContext); \ +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Register an asynchronous interrupt handler for a device. + + @function name: SDDEVICE_SET_ASYNC_IRQ_HANDLER + @prototype: void SDDEVICE_SET_ASYNC_IRQ_HANDLER(PSDDEVICE pDevice, + void (*pIrqAsyncFunction)(PVOID pContext), + PVOID pContext) + @category: PD_Reference + + @input: pDevice - the target device for this request + @input: pIrqAsyncFunction - the interrupt function to execute. + @input: pContext - context value passed into interrupt routine. + + @output: none + + @return: none + + @notes: The registered routine will be called upon each card interrupt. + The interrupt function should acknowledge the interrupt when it is + ready to handle more interrupts using: + SDLIB_IssueConfig(pDevice, SDCONFIG_FUNC_ACK_IRQ, NULL, 0); + The interrupt handler can not perform any synchronous request calls. + Using this call provides a faster interrupt dispatch, but limits all + requests to asynchronous mode. + + @see also: SDDEVICE_SET_IRQ_HANDLER + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_SET_ASYNC_IRQ_HANDLER(pDev,pFn,pContext) \ +{ \ + (pDev)->pIrqAsyncFunction = (pFn); \ + (pDev)->IrqAsyncContext = (PVOID)(pContext); \ +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the SDIO capabilities rgeister. + + @function name: SDDEVICE_GET_SDIOCARD_CAPS + @prototype: UINT8 SDDEVICE_GET_SDIOCARD_CAPS(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: SD capabilities + + @notes: See SD specification for decoding of these capabilities. + + @see also: SDDEVICE_GET_SDIO_CARD_CAPS + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SDIOCARD_CAPS(pDev) (pDev)->pHcd->CardProperties.SDIOCaps + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get HCD driver name + + @function name: SDDEVICE_GET_HCDNAME + @prototype: PTEXT SDDEVICE_GET_HCDNAME(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the target device for this request + + @output: none + + @return: pointer to a string containing the name of the underlying HCD + + @notes: Implemented as a macro. + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_HCDNAME(pDev) (pDev)->pHcd->pName + + +#define SDDEVICE_CALL_IRQ_HANDLER(pDev) (pDev)->pIrqFunction((pDev)->IrqContext) +#define SDDEVICE_CALL_IRQ_ASYNC_HANDLER(pDev) (pDev)->pIrqAsyncFunction((pDev)->IrqAsyncContext) + + +#define SDDEVICE_SET_SDIO_FUNCNO(pDev,Num) (pDev)->pId[0].SDIO_FunctionNo = (Num) +#define SDDEVICE_IS_CARD_REMOVED(pDev) ((pDev)->pHcd->CardProperties.CardState & \ + CARD_STATE_REMOVED) + + +typedef enum _SDHCD_IRQ_PROC_STATE { + SDHCD_IDLE = 0, + SDHCD_IRQ_PENDING = 1, + SDHCD_IRQ_HELPER = 2 +}SDHCD_IRQ_PROC_STATE, *PSDHCD_IRQ_PROC_STATE; + +/* host controller bus driver registration structure */ +typedef struct _SDHCD { + CT_VERSION_CODE Version; /* version code of the SDIO stack */ + SDLIST SDList; /* internal use list*/ + PTEXT pName; /* name of registering host/slot driver */ + UINT32 Attributes; /* attributes of host controller */ + UINT16 MaxBytesPerBlock; /* max bytes per block */ + UINT16 MaxBlocksPerTrans; /* max blocks per transaction */ + SD_SLOT_CURRENT MaxSlotCurrent; /* max current per slot in milli-amps */ + UINT8 SlotNumber; /* sequential slot number for this HCD, set by bus driver */ + SD_BUSCLOCK_RATE MaxClockRate; /* max clock rate in hz */ + SLOT_VOLTAGE_MASK SlotVoltageCaps; /* slot voltage capabilities */ + SLOT_VOLTAGE_MASK SlotVoltagePreferred; /* preferred slot voltage */ + PVOID pContext; /* host controller driver use data */ + SDIO_STATUS (*pRequest)(struct _SDHCD *pHcd); + /* get/set configuration */ + SDIO_STATUS (*pConfigure)(struct _SDHCD *pHcd, PSDCONFIG pConfig); + /* everything below this line is for bus driver use */ + OS_SEMAPHORE ConfigureOpsSem; /* semaphore to make specific configure ops atomic, internal use */ + OS_CRITICALSECTION HcdCritSection; /* critical section to protect hcd data structures (internal use) */ + SDREQUESTQUEUE RequestQueue; /* request queue, internal use */ + PSDREQUEST pCurrentRequest; /* current request we are working on */ + CARD_PROPERTIES CardProperties; /* properties for the currently inserted card*/ + OSKERNEL_HELPER SDIOIrqHelper; /* synch IRQ helper, internal use */ + SDDEVICE *pPseudoDev; /* pseudo device used for initialization (internal use) */ + UINT8 PendingHelperIrqs; /* IRQ helper pending IRQs */ + UINT8 PendingIrqAcks; /* pending IRQ acks from function drivers */ + UINT8 IrqsEnabled; /* current irq enabled mask */ + SDHCD_IRQ_PROC_STATE IrqProcState; /* irq processing state */ + POS_DEVICE pDevice; /* device registration with base system */ + SD_SLOT_CURRENT SlotCurrentAllocated; /* slot current allocated (internal use ) */ + ATOMIC_FLAGS HcdFlags; /* HCD Flags */ +#define HCD_REQUEST_CALL_BIT 0 +#define HCD_IRQ_NO_PEND_CHECK 1 /* HCD flag to bypass interrupt pending register + check, typically done on single function cards */ + SDREQUESTQUEUE CompletedRequestQueue; /* completed request queue, internal use */ + PSDDMA_DESCRIPTION pDmaDescription; /* description of HCD's DMA capabilities */ + POS_MODULE pModule; /* OS-specific module information */ + INT Recursion; /* recursion level */ + PVOID Reserved1; + PVOID Reserved2; +}SDHCD, *PSDHCD; + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get a pointer to the HCD's DMA description + + @function name: SDGET_DMA_DESCRIPTION + @prototype: PSDDMA_DESCRIPTION SDGET_DMA_DESCRIPTION(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - device structure + + @return: PSDDMA_DESCRIPTION or NULL if no DMA support + + @notes: Implemented as a macro. + + @example: getting the current request: + PSDDMA_DESCRIPTION pDmaDescrp = SDGET_DMA_DESCRIPTION(pDevice); + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDGET_DMA_DESCRIPTION(pDevice) (pDevice)->pHcd->pDmaDescription + + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get the logical slot number the device is assigned to. + + @function name: SDDEVICE_GET_SLOT_NUMBER + @prototype: UINT8 SDDEVICE_GET_SLOT_NUMBER(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - device structure + + @return: unsigned number representing the slot number + + @notes: Implemented as a macro. This value is unique for each physical slot in the system + and assigned by the bus driver. Devices on a multi-function card will share the same + slot number. + + @example: getting the slot number: + UINT8 thisSlot = SDDEVICE_GET_SLOT_NUMBER(pDevice); + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDDEVICE_GET_SLOT_NUMBER(pDevice) (pDevice)->pHcd->SlotNumber + +/* for function use */ +SDIO_STATUS SDIO_RegisterFunction(PSDFUNCTION pFunction); +SDIO_STATUS SDIO_UnregisterFunction(PSDFUNCTION pFunction); + +#include "sdio_hcd_defs.h" +#endif /* __SDIO_BUSDRIVER_H___ */ diff --git a/include/linux/sdio/sdio_hcd_defs.h b/include/linux/sdio/sdio_hcd_defs.h new file mode 100644 index 00000000000..178246923f7 --- /dev/null +++ b/include/linux/sdio/sdio_hcd_defs.h @@ -0,0 +1,219 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_hcd_defs.h + +@abstract: host controller driver definitions + +@notice: Copyright (c), 2005-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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __SDIO_HCD_DEFS_H___ +#define __SDIO_HCD_DEFS_H___ + + /* write protect switch position data */ +typedef UINT8 SDCONFIG_WP_VALUE; + + /* HC commands */ +#define SDCONFIG_SEND_INIT_CLOCKS (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_PUT | 1) +#define SDCONFIG_SDIO_INT_CTRL (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_PUT | 2) +#define SDCONFIG_SDIO_REARM_INT (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_NONE | 3) +#define SDCONFIG_BUS_MODE_CTRL (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_BOTH | 4) +#define SDCONFIG_POWER_CTRL (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_PUT | 5) +#define SDCONFIG_GET_WP (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_GET | 6) + + /* slot init clocks control */ +typedef struct _SDCONFIG_INIT_CLOCKS_DATA { + UINT16 NumberOfClocks; /* number of clocks to issue in the current bus mode*/ +}SDCONFIG_INIT_CLOCKS_DATA, *PSDCONFIG_INIT_CLOCKS_DATA; + +/* slot power control */ +typedef struct _SDCONFIG_POWER_CTRL_DATA { + BOOL SlotPowerEnable; /* turn on/off slot power */ + SLOT_VOLTAGE_MASK SlotPowerVoltageMask; /* slot power voltage mask */ +}SDCONFIG_POWER_CTRL_DATA, *PSDCONFIG_POWER_CTRL_DATA; + +typedef UINT8 SDIO_IRQ_MODE_FLAGS; +/* SDIO Interrupt control */ +typedef struct _SDCONFIG_SDIO_INT_CTRL_DATA { + BOOL SlotIRQEnable; /* turn on/off Slot IRQ detection */ + SDIO_IRQ_MODE_FLAGS IRQDetectMode; /* slot IRQ detect mode , only valid if Enabled = TRUE */ +#define IRQ_DETECT_RAW 0x00 +#define IRQ_DETECT_MULTI_BLK 0x01 +#define IRQ_DETECT_4_BIT 0x02 +#define IRQ_DETECT_1_BIT 0x04 +#define IRQ_DETECT_SPI 0x08 +}SDCONFIG_SDIO_INT_CTRL_DATA, *PSDCONFIG_SDIO_INT_CTRL_DATA; + +/* card insert */ +#define EVENT_HCD_ATTACH 1 +/* card remove */ +#define EVENT_HCD_DETACH 2 +/* card slot interrupt */ +#define EVENT_HCD_SDIO_IRQ_PENDING 3 +/* transfer done */ +#define EVENT_HCD_TRANSFER_DONE 4 +/* (internal use only) */ +#define EVENT_HCD_CD_POLLING 5 +/* NOP */ +#define EVENT_HCD_NOP 0 + +/* attrib_flags */ +#define SDHCD_ATTRIB_SUPPORTS_POWER 0x0001 /* host controller driver supports power managment */ +#define SDHCD_ATTRIB_BUS_1BIT 0x0002 /* SD Native 1 - bit mode */ +#define SDHCD_ATTRIB_BUS_4BIT 0x0004 /* SD Native 4 - bit mode */ +#define SDHCD_ATTRIB_BUS_SPI 0x0008 /* SPI mode capable */ +#define SDHCD_ATTRIB_READ_WAIT 0x0010 /* read wait supported (SD-only) */ +#define SDHCD_ATTRIB_MULTI_BLK_IRQ 0x0020 /* interrupts between multi-block capable (SD-only) */ +#define SDHCD_ATTRIB_BUS_MMC8BIT 0x0040 /* MMC 8-bit */ +#define SDHCD_ATTRIB_SLOT_POLLING 0x0080 /* requires slot polling for Card Detect */ +#define SDHCD_ATTRIB_POWER_SWITCH 0x0100 /* host has power switch control, must be set if SPI + mode can be switched to 1 or 4 bit mode */ +#define SDHCD_ATTRIB_NO_SPI_CRC 0x0200 /* when in SPI mode, + host wants to run without SPI CRC */ +#define SDHCD_ATTRIB_AUTO_CMD12 0x0400 /* host controller supports auto CMD12 */ +#define SDHCD_ATTRIB_NO_4BIT_IRQ 0x0800 /* host controller does not support 4 bit IRQ mode*/ +#define SDHCD_ATTRIB_RAW_MODE 0x1000 /* host controller is a raw mode hcd*/ +#define SDHCD_ATTRIB_SD_HIGH_SPEED 0x2000 /* host controller supports SD high speed interface */ +#define SDHCD_ATTRIB_MMC_HIGH_SPEED 0x4000 /* host controller supports MMC high speed interface */ + +#define IS_CARD_PRESENT(pHcd) ((pHcd)->CardProperties.Flags & CARD_TYPE_MASK) +#define SET_CURRENT_REQUEST(pHcd,Req) (pHcd)->pCurrentRequest = (Req) +#define IS_HCD_RAW(pHcd) ((pHcd)->Attributes & SDHCD_ATTRIB_RAW_MODE) +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get a pointer to the current bus request for a host controller + + @function name: GET_CURRENT_REQUEST + @prototype: PSDREQUEST GET_CURRENT_REQUEST (PSDHCD pHcd) + @category: HD_Reference + + @input: pHcd - host structure + + @return: current SD/SDIO bus request being worked on + + @notes: Implemented as a macro. This macro returns the current SD request that is + being worked on. + + @example: getting the current request: + pReq = GET_CURRENT_REQUEST(&pHct->Hcd); + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define GET_CURRENT_REQUEST(pHcd) (pHcd)->pCurrentRequest +#define GET_CURRENT_BUS_WIDTH(pHcd) SDCONFIG_GET_BUSWIDTH((pHcd)->CardProperties.BusMode) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Get host controller's current operational bus clock + + @function name: SDHCD_GET_OPER_CLOCK + @prototype: SD_BUSCLOCK_RATE SDHCD_GET_OPER_CLOCK(PSDHCD pHcd) + @category: HD_Reference + + @input: pHcd - the registered host structure + + @output: none + + @return: clock rate + + @notes: Implemented as a macro. Returns the current bus clock rate. + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDHCD_GET_OPER_CLOCK(pHcd) (pHcd)->CardProperties.OperBusClock +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Is host controller operating in SPI mode + + @function name: IS_HCD_BUS_MODE_SPI + @prototype: BOOL IS_HCD_BUS_MODE_SPI (PSDHCD pHcd) + @category: HD_Reference + + @input: pHcd - host structure + + @return: TRUE if in SPI mode + + @notes: Implemented as a macro. Host controllers that operate in SPI mode + dynamically can use this macro to check for SPI operation. + + @example: testing for SPI mode: + if (IS_HCD_BUS_MODE_SPI(&pHct->Hcd)) { + .. in spi mode + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define IS_HCD_BUS_MODE_SPI(pHcd) (GET_CURRENT_BUS_WIDTH(pHcd) == SDCONFIG_BUS_WIDTH_SPI) + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Is host controller using SPI in non-CRC mode + + @function name: IS_HCD_BUS_MODE_SPI_NO_CRC + @prototype: BOOL IS_HCD_BUS_MODE_SPI_NO_CRC(PSDHCD pHcd) + @category: HD_Reference + + @input: pHcd - host structure + + @return: TRUE if CRC mode is off + + @notes: Implemented as a macro. SPI-capable cards and systems can operate in + non-CRC protected mode. In this mode the host controller should ignore + CRC fields and/or disable CRC generation when issuing command or data + packets. This option is useful for software based SPI mode where CRC + should be turned off in order to reduce processing overhead. + + @example: test for non-CRC SPI mode: + if (IS_HCD_BUS_MODE_SPI_NO_CRC(&pHct->Hcd)) { + .. disable CRC checking in hardware. + } + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define IS_HCD_BUS_MODE_SPI_NO_CRC(pHcd) ((pHcd)->CardProperties.BusMode & \ + SDCONFIG_BUS_MODE_SPI_NO_CRC) + +typedef UINT8 SDHCD_RESPONSE_CHECK_MODE; +/* have SDIO core check the response token and see if it is okay to continue with + * the data portion */ +#define SDHCD_CHECK_DATA_TRANS_OK 0x01 +/* have SDIO core check the SPI token received */ +#define SDHCD_CHECK_SPI_TOKEN 0x02 + +/* prototypes */ +/* for HCD use */ +SDIO_STATUS SDIO_RegisterHostController(PSDHCD pHcd); +SDIO_STATUS SDIO_UnregisterHostController(PSDHCD pHcd); +SDIO_STATUS SDIO_HandleHcdEvent(PSDHCD pHcd, HCD_EVENT Event); +SDIO_STATUS SDIO_CheckResponse(PSDHCD pHcd, PSDREQUEST pReq, SDHCD_RESPONSE_CHECK_MODE CheckMode); +SDIO_STATUS SDIO_BusAddOSDevice(PSDDMA_DESCRIPTION pDma, POS_PNPDRIVER pDriver, POS_PNPDEVICE pDevice); +void SDIO_BusRemoveOSDevice(POS_PNPDRIVER pDriver, POS_PNPDEVICE pDevice); + +#endif /* __SDIO_BUSDRIVER_H___ */ diff --git a/include/linux/sdio/sdio_lib.h b/include/linux/sdio/sdio_lib.h new file mode 100644 index 00000000000..ac0cbd7a5cd --- /dev/null +++ b/include/linux/sdio/sdio_lib.h @@ -0,0 +1,270 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_lib.h + +@abstract: SDIO Library include + +#notes: + +@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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __SDIO_LIB_H___ +#define __SDIO_LIB_H___ + +#ifdef UNDER_CE +#include "wince\sdio_lib_wince.h" +#endif /* WINCE */ + +#define CMD52_DO_READ FALSE +#define CMD52_DO_WRITE TRUE + + /* read/write macros to any function */ +#define Cmd52WriteByteFunc(pDev,Func,Address,pValue) \ + SDLIB_IssueCMD52((pDev),(Func),(Address),(pValue),1,CMD52_DO_WRITE) +#define Cmd52ReadByteFunc(pDev,Func,Address,pValue) \ + SDLIB_IssueCMD52((pDev),(Func),(Address),pValue,1,CMD52_DO_READ) +#define Cmd52ReadMultipleFunc(pDev,Func, Address, pBuf,length) \ + SDLIB_IssueCMD52((pDev),(Func),(Address),(pBuf),(length),CMD52_DO_READ) + + /* macros to access common registers */ +#define Cmd52WriteByteCommon(pDev, Address, pValue) \ + Cmd52WriteByteFunc((pDev),0,(Address),(pValue)) +#define Cmd52ReadByteCommon(pDev, Address, pValue) \ + Cmd52ReadByteFunc((pDev),0,(Address),(pValue)) +#define Cmd52ReadMultipleCommon(pDev, Address, pBuf,length) \ + Cmd52ReadMultipleFunc((pDev),0,(Address),(pBuf),(length)) + +#define SDLIB_SetupCMD52RequestAsync(f,a,w,wd,pR) \ +{ \ + SDLIB_SetupCMD52Request((f),(a),(w),(wd),(pR)); \ + (pR)->Flags |= SDREQ_FLAGS_TRANS_ASYNC; \ +} + + /* a message block */ +typedef struct _SDMESSAGE_BLOCK { + SDLIST SDList; /* list entry */ + INT MessageLength; /* number of bytes in this message */ + UINT8 MessageStart[1]; /* message start */ +}SDMESSAGE_BLOCK, *PSDMESSAGE_BLOCK; + + /* message queue */ +typedef struct _SDMESSAGE_QUEUE { + SDLIST MessageList; /* message list */ + OS_CRITICALSECTION MessageCritSection; /* message semaphore */ + SDLIST FreeMessageList; /* free message list */ + INT MaxMessageLength; /* max message block length */ +}SDMESSAGE_QUEUE, *PSDMESSAGE_QUEUE; + +/* internal library prototypes that can be proxied */ +SDIO_STATUS _SDLIB_IssueCMD52(PSDDEVICE pDevice, + UINT8 FuncNo, + UINT32 Address, + PUINT8 pData, + INT ByteCount, + BOOL Write); +SDIO_STATUS _SDLIB_FindTuple(PSDDEVICE pDevice, + UINT8 Tuple, + UINT32 *pTupleScanAddress, + PUINT8 pBuffer, + UINT8 *pLength); +SDIO_STATUS _SDLIB_IssueConfig(PSDDEVICE pDevice, + SDCONFIG_COMMAND Command, + PVOID pData, + INT Length); +void _SDLIB_PrintBuffer(PUCHAR pBuffer, INT Length,PTEXT pDescription); +void _SDLIB_SetupCMD52Request(UINT8 FuncNo, + UINT32 Address, + BOOL Write, + UINT8 WriteData, + PSDREQUEST pRequest); +SDIO_STATUS _SDLIB_SetFunctionBlockSize(PSDDEVICE pDevice, + UINT16 BlockSize); + +SDIO_STATUS _SDLIB_GetDefaultOpCurrent(PSDDEVICE pDevice, + SD_SLOT_CURRENT *pOpCurrent); +PSDMESSAGE_QUEUE _CreateMessageQueue(INT MaxMessages, INT MaxMessageLength); +void _DeleteMessageQueue(PSDMESSAGE_QUEUE pQueue); +SDIO_STATUS _PostMessage(PSDMESSAGE_QUEUE pQueue, PVOID pMessage, INT MessageLength); +SDIO_STATUS _GetMessage(PSDMESSAGE_QUEUE pQueue, PVOID pData, INT *pBufferLength); + +#ifdef CTSYSTEM_NO_FUNCTION_PROXIES + /* OS port requires no proxy functions, use methods directly from the library */ +#define SDLIB_IssueCMD52 _SDLIB_IssueCMD52 +#define SDLIB_SetupCMD52Request _SDLIB_SetupCMD52Request +#define SDLIB_FindTuple _SDLIB_FindTuple +#define SDLIB_IssueConfig _SDLIB_IssueConfig +#define SDLIB_SetFunctionBlockSize _SDLIB_SetFunctionBlockSize +#define SDLIB_GetDefaultOpCurrent _SDLIB_GetDefaultOpCurrent +#define SDLIB_CreateMessageQueue _CreateMessageQueue +#define SDLIB_DeleteMessageQueue _DeleteMessageQueue +#define SDLIB_PostMessage _PostMessage +#define SDLIB_GetMessage _GetMessage +#define SDLIB_PrintBuffer _SDLIB_PrintBuffer +#else + +/* proxied versions */ +SDIO_STATUS SDLIB_IssueCMD52(PSDDEVICE pDevice, + UINT8 FuncNo, + UINT32 Address, + PUINT8 pData, + INT ByteCount, + BOOL Write); + +void SDLIB_SetupCMD52Request(UINT8 FuncNo, + UINT32 Address, + BOOL Write, + UINT8 WriteData, + PSDREQUEST pRequest); + +SDIO_STATUS SDLIB_FindTuple(PSDDEVICE pDevice, + UINT8 Tuple, + UINT32 *pTupleScanAddress, + PUINT8 pBuffer, + UINT8 *pLength); + +SDIO_STATUS SDLIB_IssueConfig(PSDDEVICE pDevice, + SDCONFIG_COMMAND Command, + PVOID pData, + INT Length); + +SDIO_STATUS SDLIB_SetFunctionBlockSize(PSDDEVICE pDevice, + UINT16 BlockSize); + +void SDLIB_PrintBuffer(PUCHAR pBuffer, INT Length,PTEXT pDescription); + +SDIO_STATUS SDLIB_GetDefaultOpCurrent(PSDDEVICE pDevice, SD_SLOT_CURRENT *pOpCurrent); + +PSDMESSAGE_QUEUE SDLIB_CreateMessageQueue(INT MaxMessages, INT MaxMessageLength); + +void SDLIB_DeleteMessageQueue(PSDMESSAGE_QUEUE pQueue); + +SDIO_STATUS SDLIB_PostMessage(PSDMESSAGE_QUEUE pQueue, PVOID pMessage, INT MessageLength); + +SDIO_STATUS SDLIB_GetMessage(PSDMESSAGE_QUEUE pQueue, PVOID pData, INT *pBufferLength); +#endif /* CTSYSTEM_NO_FUNCTION_PROXIES */ + + +SDIO_STATUS SDLIB_OSCreateHelper(POSKERNEL_HELPER pHelper, + PHELPER_FUNCTION pFunction, + PVOID pContext); + +void SDLIB_OSDeleteHelper(POSKERNEL_HELPER pHelper); + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Check message queue is empty + + @function name: SDLIB_IsQueueEmpty + @prototype: BOOL SDLIB_IsQueueEmpty(PSDMESSAGE_QUEUE pQueue) + @category: Support_Reference + + @input: pQueue - message queue to check + + @return: TRUE if empty else false + + @see also: SDLIB_CreateMessageQueue + + @example: Check message queue : + if (SDLIB_IsQueueEmpty(pInstance->pQueue)) { + .. message queue is empty + } ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static INLINE BOOL SDLIB_IsQueueEmpty(PSDMESSAGE_QUEUE pQueue) { + return SDLIST_IS_EMPTY(&pQueue->MessageList); +} + + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Issue an I/O abort request + + @function name: SDLIB_IssueIOAbort + @prototype: SDIO_STATUS SDLIB_IssueIOAbort(PSDDEVICE pDevice) + @category: PD_Reference + + @input: pDevice - the device that is the target of this request + + @return: SDIO_STATUS + + @notes: This procedure can be called to issue an I/O abort request to an I/O function. + This procedure cannot be used to abort a data (block) transfer already in progress. + It is intended to be used when a data (block) transfer completes with an error and only if + the I/O function requires an abort action. Some I/O functions may automatically + recover from such failures and not require this action. This function issues + the abort command synchronously and can potentially block. + If an async request is required, you must allocate a request and use + SDLIB_SetupIOAbortAsync() to prepare the request. + + @example: Issuing I/O Abort synchronously : + .. check status from last block operation: + if (status == SDIO_STATUS_BUS_READ_TIMEOUT) { + .. on failure, issue I/O abort + status2 = SDLIB_IssueIOAbort(pDevice); + } + Issuing I/O Abort asynchronously: + ... allocate a request + ... setup the request: + SDLIB_SetupIOAbortAsync(pDevice,pReq); + pReq->pCompletion = myIOAbortCompletion; + pReq->pCompleteContext = pDevice; + status = SDDEVICE_CALL_REQUEST_FUNC(pDevice,pReq); + + @see also: SDLIB_SetupIOAbortAsync ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +static INLINE SDIO_STATUS SDLIB_IssueIOAbort(PSDDEVICE pDevice) { + UINT8 value = SDDEVICE_GET_SDIO_FUNCNO(pDevice); + return Cmd52WriteByteCommon(pDevice,0x06,&value); +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + @function: Setup an I/O abort request for async operation + + @function name: SDLIB_SetupIOAbortAsync + @prototype: SDLIB_SetupIOAbortAsync(PSDDEVICE pDevice, PSDREQUEST pRequest) + @category: PD_Reference + + @input: pDevice - the device that is the target of this request + pRequest - the request to set up + + @see also: SDLIB_IssueIOAbort + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#define SDLIB_SetupIOAbortAsync(pDevice, pReq) \ + SDLIB_SetupCMD52RequestAsync(0,0x06,TRUE,SDDEVICE_GET_SDIO_FUNCNO(pDevice),(pReq)) + + +#endif /* __SDIO_LIB_H___*/ diff --git a/include/linux/sdio/sdlist.h b/include/linux/sdio/sdlist.h new file mode 100644 index 00000000000..dc35e1ce639 --- /dev/null +++ b/include/linux/sdio/sdlist.h @@ -0,0 +1,141 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdlist.h + +@abstract: OS independent list functions + +@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 of 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 + * + * + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __SDLIST_H___ +#define __SDLIST_H___ + +/* list functions */ +/* pointers for the list */ +typedef struct _SDLIST { + struct _SDLIST *pPrev; + struct _SDLIST *pNext; +}SDLIST, *PSDLIST; +/* + * SDLIST_INIT , circular list +*/ +#define SDLIST_INIT(pList)\ + {(pList)->pPrev = pList; (pList)->pNext = pList;} +#define SDLIST_INIT_DECLARE(List)\ + SDLIST List = {&List, &List} + + +#define SDLIST_IS_EMPTY(pList) (((pList)->pPrev == (pList)) && ((pList)->pNext == (pList))) +#define SDLIST_GET_ITEM_AT_HEAD(pList) (pList)->pNext +#define SDLIST_GET_ITEM_AT_TAIL(pList) (pList)->pPrev +/* + * SDITERATE_OVER_LIST pStart is the list, pTemp is a temp list member + * NOT: do not use this function if the items in the list are deleted inside the + * iteration loop +*/ +#define SDITERATE_OVER_LIST(pStart, pTemp) \ + for((pTemp) =(pStart)->pNext; pTemp != (pStart); (pTemp) = (pTemp)->pNext) + + +/* safe iterate macro that allows the item to be removed from the list + * the iteration continues to the next item in the list + */ +#define SDITERATE_OVER_LIST_ALLOW_REMOVE(pStart,pItem,st,offset) \ +{ \ + PSDLIST pTemp; \ + pTemp = (pStart)->pNext; \ + while (pTemp != (pStart)) { \ + (pItem) = CONTAINING_STRUCT(pTemp,st,offset); \ + pTemp = pTemp->pNext; \ + +#define SDITERATE_END }} + +/* + * SDListInsertTail - insert pAdd to the end of the list +*/ +static INLINE PSDLIST SDListInsertTail(PSDLIST pList, PSDLIST pAdd) { + /* this assert catches when an item is added twice */ + DBG_ASSERT(pAdd->pNext != pList); + /* insert at tail */ + pAdd->pPrev = pList->pPrev; + pAdd->pNext = pList; + pList->pPrev->pNext = pAdd; + pList->pPrev = pAdd; + return pAdd; +} + +/* + * SDListInsertHead - insert pAdd into the head of the list +*/ +static INLINE PSDLIST SDListInsertHead(PSDLIST pList, PSDLIST pAdd) { + /* this assert catches when an item is added twice */ + DBG_ASSERT(pAdd->pPrev != pList); + /* insert at head */ + pAdd->pPrev = pList; + pAdd->pNext = pList->pNext; + pList->pNext->pPrev = pAdd; + pList->pNext = pAdd; + return pAdd; +} + +#define SDListAdd(pList,pItem) SDListInsertHead((pList),(pItem)) +/* + * SDListRemove - remove pDel from list +*/ +static INLINE PSDLIST SDListRemove(PSDLIST pDel) { + pDel->pNext->pPrev = pDel->pPrev; + pDel->pPrev->pNext = pDel->pNext; + /* point back to itself just to be safe, incase remove is called again */ + pDel->pNext = pDel; + pDel->pPrev = pDel; + return pDel; +} + +/* + * SDListRemoveItemFromHead - get a list item from the head +*/ +static INLINE PSDLIST SDListRemoveItemFromHead(PSDLIST pList) { + PSDLIST pItem = NULL; + if (pList->pNext != pList) { + pItem = pList->pNext; + /* remove the first item from head */ + SDListRemove(pItem); + } + return pItem; +} +#endif /* __SDLIST_H___ */ -- cgit v1.2.3