aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authormokopatches <mokopatches@openmoko.org>2008-11-19 17:03:22 +0000
committerwarmcat <andy@warmcat.com>2008-11-19 17:03:22 +0000
commite7ab2e913536616b9f17e3323a7f2b83e808a198 (patch)
tree7e56b72e9d8e1ac85e1624fd33c6218da983714c /drivers
parent0a5a3e0ff3050d7baeeb40f50cf3ea7c174ef21e (diff)
atheros_2_0_sdio_stack.patch
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/sdio/Kconfig17
-rw-r--r--drivers/sdio/Makefile4
-rw-r--r--drivers/sdio/stack/Makefile1
-rw-r--r--drivers/sdio/stack/busdriver/Makefile2
-rw-r--r--drivers/sdio/stack/busdriver/_busdriver.h466
-rw-r--r--drivers/sdio/stack/busdriver/sdio_bus.c2120
-rw-r--r--drivers/sdio/stack/busdriver/sdio_bus_events.c1040
-rw-r--r--drivers/sdio/stack/busdriver/sdio_bus_misc.c3122
-rw-r--r--drivers/sdio/stack/busdriver/sdio_bus_os.c832
-rw-r--r--drivers/sdio/stack/busdriver/sdio_function.c715
-rw-r--r--drivers/sdio/stack/lib/Makefile2
-rw-r--r--drivers/sdio/stack/lib/_sdio_lib.h50
-rw-r--r--drivers/sdio/stack/lib/sdio_lib_c.c908
-rw-r--r--drivers/sdio/stack/lib/sdio_lib_os.c251
-rw-r--r--drivers/sdio/stack/platform/Makefile2
-rw-r--r--drivers/sdio/stack/platform/sdioplatformdriver.c300
18 files changed, 9835 insertions, 0 deletions
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 <linux/sdio/sdio_lib.h>
+
+#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 <linux/sdio/ctsystem.h>
+#include <linux/sdio/sdio_busdriver.h>
+#include <linux/sdio/_sdio_defs.h>
+#include <linux/sdio/sdio_lib.h>
+#include <linux/sdio/mmc_defs.h>
+#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<<SDBD_PENDING)) {
+ DBG_ASSERT_WITH_MSG(FALSE,
+ "SDIO Bus Driver: IssueRequestToHCD - request already in use \n");
+ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Request already in use: 0x%X",(INT)pReq));
+ }
+
+ if (!(pReq->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 <linux/sdio/ctsystem.h>
+#include <linux/sdio/sdio_busdriver.h>
+#include <linux/sdio/sdio_lib.h>
+#include "_busdriver.h"
+#include <linux/sdio/_sdio_defs.h>
+#include <linux/sdio/mmc_defs.h>
+
+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 <linux/sdio/ctsystem.h>
+#include <linux/sdio/sdio_busdriver.h>
+#include <linux/sdio/sdio_lib.h>
+#include "_busdriver.h"
+#include <linux/sdio/_sdio_defs.h>
+#include <linux/sdio/mmc_defs.h>
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 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,
+ &regData);
+
+ 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,
+ &regData);
+ 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, &registerValue);
+ 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, &registerValue);
+ 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, &registerValue);
+ 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 <linux/sdio/ctsystem.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/pnp.h>
+void pnp_remove_card_device(struct pnp_dev *dev);
+#include <linux/sdio/sdio_busdriver.h>
+#include <linux/sdio/sdio_lib.h>
+#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 <linux/pnp.h>
+
+#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 <linux/sdio/ctsystem.h>
+#include <linux/sdio/sdio_busdriver.h>
+#include <linux/sdio/sdio_lib.h>
+#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 <linux/sdio/ctsystem.h>
+#include <linux/sdio/sdio_busdriver.h>
+#include <linux/sdio/_sdio_defs.h>
+#include <linux/sdio/sdio_lib.h>
+#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 <linux/sdio/ctsystem.h>
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kthread.h>
+
+#include <linux/sdio/sdio_busdriver.h>
+#include <linux/sdio/sdio_lib.h>
+#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);
+
+
+