diff options
author | mirko <mirko@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2008-12-12 00:02:36 +0000 |
---|---|---|
committer | mirko <mirko@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2008-12-12 00:02:36 +0000 |
commit | cc3146824acbf585f487a803b4a2eadf36b9b47d (patch) | |
tree | efab142ef89b35ce42e8a79e028eff15be362af8 /target/linux/s3c24xx/patches/0055-atheros_2_0_sdio_stack.patch.patch | |
parent | 22b3854919b7cb44d725eb7a7efd018ad9aec25c (diff) |
add support for target 3c24xx (more known as Openmoko GTA02 "Freerunner") and merge it with the openmoko-target and the work Michael Buesch <mb> did
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@13609 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/s3c24xx/patches/0055-atheros_2_0_sdio_stack.patch.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches/0055-atheros_2_0_sdio_stack.patch.patch | 13982 |
1 files changed, 13982 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches/0055-atheros_2_0_sdio_stack.patch.patch b/target/linux/s3c24xx/patches/0055-atheros_2_0_sdio_stack.patch.patch new file mode 100755 index 0000000000..e29f050afe --- /dev/null +++ b/target/linux/s3c24xx/patches/0055-atheros_2_0_sdio_stack.patch.patch @@ -0,0 +1,13982 @@ +From 79b0634b39757c62f4e61256c12ddf8e7b3783c4 Mon Sep 17 00:00:00 2001 +From: mokopatches <mokopatches@openmoko.org> +Date: Fri, 25 Jul 2008 22:21:24 +0100 +Subject: [PATCH] atheros_2_0_sdio_stack.patch + +--- + arch/arm/Kconfig | 2 + + drivers/Kconfig | 2 + + drivers/Makefile | 1 + + drivers/sdio/Kconfig | 17 + + drivers/sdio/Makefile | 4 + + drivers/sdio/stack/Makefile | 1 + + drivers/sdio/stack/busdriver/Makefile | 2 + + drivers/sdio/stack/busdriver/_busdriver.h | 466 ++++ + drivers/sdio/stack/busdriver/sdio_bus.c | 2120 +++++++++++++++ + drivers/sdio/stack/busdriver/sdio_bus_events.c | 1040 +++++++ + drivers/sdio/stack/busdriver/sdio_bus_misc.c | 3122 ++++++++++++++++++++++ + drivers/sdio/stack/busdriver/sdio_bus_os.c | 832 ++++++ + drivers/sdio/stack/busdriver/sdio_function.c | 715 +++++ + drivers/sdio/stack/lib/Makefile | 2 + + drivers/sdio/stack/lib/_sdio_lib.h | 50 + + drivers/sdio/stack/lib/sdio_lib_c.c | 908 +++++++ + drivers/sdio/stack/lib/sdio_lib_os.c | 251 ++ + drivers/sdio/stack/platform/Makefile | 2 + + drivers/sdio/stack/platform/sdioplatformdriver.c | 300 +++ + include/linux/sdio/_sdio_defs.h | 638 +++++ + include/linux/sdio/ctsystem.h | 115 + + include/linux/sdio/ctsystem_linux.h | 983 +++++++ + include/linux/sdio/mmc_defs.h | 103 + + include/linux/sdio/sdio_busdriver.h | 1435 ++++++++++ + include/linux/sdio/sdio_hcd_defs.h | 219 ++ + include/linux/sdio/sdio_lib.h | 270 ++ + include/linux/sdio/sdlist.h | 141 + + 27 files changed, 13741 insertions(+), 0 deletions(-) + create mode 100644 drivers/sdio/Kconfig + create mode 100644 drivers/sdio/Makefile + create mode 100644 drivers/sdio/stack/Makefile + create mode 100644 drivers/sdio/stack/busdriver/Makefile + create mode 100644 drivers/sdio/stack/busdriver/_busdriver.h + create mode 100644 drivers/sdio/stack/busdriver/sdio_bus.c + create mode 100644 drivers/sdio/stack/busdriver/sdio_bus_events.c + create mode 100644 drivers/sdio/stack/busdriver/sdio_bus_misc.c + create mode 100644 drivers/sdio/stack/busdriver/sdio_bus_os.c + create mode 100644 drivers/sdio/stack/busdriver/sdio_function.c + create mode 100644 drivers/sdio/stack/lib/Makefile + create mode 100644 drivers/sdio/stack/lib/_sdio_lib.h + create mode 100644 drivers/sdio/stack/lib/sdio_lib_c.c + create mode 100644 drivers/sdio/stack/lib/sdio_lib_os.c + create mode 100644 drivers/sdio/stack/platform/Makefile + create mode 100644 drivers/sdio/stack/platform/sdioplatformdriver.c + create mode 100644 include/linux/sdio/_sdio_defs.h + create mode 100644 include/linux/sdio/ctsystem.h + create mode 100644 include/linux/sdio/ctsystem_linux.h + create mode 100644 include/linux/sdio/mmc_defs.h + create mode 100644 include/linux/sdio/sdio_busdriver.h + create mode 100644 include/linux/sdio/sdio_hcd_defs.h + create mode 100644 include/linux/sdio/sdio_lib.h + create mode 100644 include/linux/sdio/sdlist.h + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index b786e68..e394067 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1169,6 +1169,8 @@ source "drivers/hid/Kconfig" + + source "drivers/usb/Kconfig" + ++source "drivers/sdio/Kconfig" ++ + source "drivers/mmc/Kconfig" + + source "drivers/leds/Kconfig" +diff --git a/drivers/Kconfig b/drivers/Kconfig +index 59f33fa..f5900b7 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -78,6 +78,8 @@ source "drivers/hid/Kconfig" + + source "drivers/usb/Kconfig" + ++source "drivers/sdio/Kconfig" ++ + source "drivers/mmc/Kconfig" + + source "drivers/memstick/Kconfig" +diff --git a/drivers/Makefile b/drivers/Makefile +index f65deda..ce63a01 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -80,6 +80,7 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/ + obj-$(CONFIG_CPU_IDLE) += cpuidle/ + 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 0000000..14bf5e3 +--- /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 0000000..f56aa0f +--- /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 0000000..ff0e24d +--- /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 0000000..1130e2d +--- /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 0000000..a85aed1 +--- /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 0000000..ffc1e9f +--- /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 0000000..5b3148d +--- /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 0000000..c5c7381 +--- /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, ++ ®Data); ++ ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to %s HS mode in SDIO card : Err:%d\n", ++ (SDIO_HS_CONTROL_EHS == regData) ? "enable":"disable" , status)); ++ break; ++ } else { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver:SDIO Card %s for High Speed mode \n", ++ (SDIO_HS_CONTROL_EHS == regData) ? "enabled":"disabled" )); ++ } ++ } ++ } ++ ++ /* use synchronize-with-bus request version, this may have been requested by a ++ * function driver */ ++ status = SDLIB_IssueConfig(pDevice, ++ SDCONFIG_BUS_MODE_CTRL, ++ pBusMode, ++ sizeof(SDCONFIG_BUS_MODE_DATA)); ++ ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set bus mode in hcd : Err:%d \n", ++ status)); ++ break; ++ } ++ ++ /* check requested bus width against the current mode */ ++ if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == ++ SDCONFIG_GET_BUSWIDTH(pHcd->CardProperties.BusMode)) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Bus mode set, no width change\n")); ++ break; ++ } ++ ++ if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_SPI) { ++ /* nothing more to do for SPI */ ++ break; ++ } ++ ++ /* set the bus width for SD and combo cards */ ++ if (pHcd->CardProperties.Flags & CARD_SD) { ++ if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_4_BIT) { ++ /* turn off card detect resistor */ ++ status = _IssueSimpleBusRequest(pHcd, ++ ACMD42, ++ 0, /* disable CD */ ++ SDREQ_FLAGS_APP_CMD | SDREQ_FLAGS_RESP_R1, ++ NULL); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: Failed to disable CD Res: %d \n", ++ status)); /* this should be okay */ ++ } ++ arg = SD_ACMD6_BUS_WIDTH_4_BIT; ++ } else { ++ /* don't need to turn off CD in 1 bit mode, just set mode */ ++ arg = SD_ACMD6_BUS_WIDTH_1_BIT; ++ ++ } ++ /* set the bus width */ ++ status = _IssueSimpleBusRequest(pHcd, ++ ACMD6, ++ arg, /* set bus mode */ ++ SDREQ_FLAGS_APP_CMD | SDREQ_FLAGS_RESP_R1, ++ NULL); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set bus width: %d \n", ++ status)); ++ break; ++ } ++ } ++ /* set bus width for SDIO cards */ ++ if (pHcd->CardProperties.Flags & CARD_SDIO) { ++ /* default */ ++ regData = CARD_DETECT_DISABLE | SDIO_BUS_WIDTH_1_BIT; ++ ++ if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_4_BIT) { ++ /* turn off card detect resistor and set buswidth */ ++ regData = CARD_DETECT_DISABLE | SDIO_BUS_WIDTH_4_BIT; ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Enabling 4 bit mode on card \n")); ++ } else { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Enabling 1 bit mode on card \n")); ++ } ++ status = Cmd52WriteByteCommon(pDevice, ++ SDIO_BUS_IF_REG, ++ ®Data); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set bus mode in Card : Err:%d\n", ++ status)); ++ break; ++ } ++ ++ /* check for 4-bit interrupt detect mode */ ++ if ((SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_4_BIT) && ++ (pHcd->CardProperties.SDIOCaps & SDIO_CAPS_INT_MULTI_BLK) && ++ (pHcd->Attributes & SDHCD_ATTRIB_MULTI_BLK_IRQ)) { ++ /* enable interrupts between blocks, this doesn't actually turn on interrupts ++ * it merely allows interrupts to be asserted in the inter-block gap */ ++ pHcd->CardProperties.SDIOCaps |= SDIO_CAPS_ENB_INT_MULTI_BLK; ++ ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: 4-Bit Multi-blk Interrupt support enabled\n")); ++ } else { ++ /* make sure this is disabled */ ++ pHcd->CardProperties.SDIOCaps &= ~SDIO_CAPS_ENB_INT_MULTI_BLK; ++ } ++ ++ status = Cmd52WriteByteCommon(pDevice, ++ SDIO_CARD_CAPS_REG, ++ &pHcd->CardProperties.SDIOCaps); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to update Card Caps register Err:%d\n", ++ status)); ++ break; ++ } ++ } ++ ++ /* set data bus width for MMC */ ++ if (pHcd->CardProperties.Flags & CARD_MMC) { ++ UINT8 buswidth = 0; ++ ++ if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_4_BIT) { ++ buswidth = MMC_EXT_BUS_WIDTH_4_BIT; ++ } else if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_MMC8_BIT) { ++ buswidth = MMC_EXT_BUS_WIDTH_8_BIT; ++ } else { ++ /* normal 1 bit mode .. nothing to do */ ++ break; ++ } ++ /* now set the bus mode on the card */ ++ switcharg = MMC_SWITCH_BUILD_ARG(MMC_SWITCH_CMD_SET0, ++ MMC_SWITCH_WRITE_BYTE, ++ MMC_EXT_BUS_WIDTH_OFFSET, ++ buswidth); ++ ++ status = _IssueSimpleBusRequest(pHcd, ++ MMC_CMD_SWITCH, ++ switcharg, ++ SDREQ_FLAGS_RESP_R1B, ++ NULL); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set MMC bus width (arg:0x%X): %d \n", ++ switcharg, status)); ++ break; ++ } ++ ++ if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_4_BIT) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: 4 bit MMC mode enabled (arg:0x%X) \n", ++ switcharg)); ++ } else if (SDCONFIG_GET_BUSWIDTH(pBusMode->BusModeFlags) == SDCONFIG_BUS_WIDTH_MMC8_BIT) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: 8-Bit MMC mode enabled (arg:0x%X) \n", ++ switcharg)); ++ } ++ } ++ ++ } while (FALSE); ++ ++ if (SDIO_SUCCESS(status)) { ++ /* set the operating mode */ ++ pHcd->CardProperties.BusMode = pBusMode->BusModeFlags; ++ /* set the actual clock rate */ ++ pHcd->CardProperties.OperBusClock = pBusMode->ActualClockRate; ++ } ++ ++ SemaphorePost(&pDevice->pHcd->ConfigureOpsSem); ++ ++ return status; ++} ++ ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ CardInitSetup - setup host for card initialization ++ Input: pHcd - HCD object ++ Output: ++ Return: ++ Notes: ++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++SDIO_STATUS CardInitSetup(PSDHCD pHcd) ++{ ++ SDCONFIG_INIT_CLOCKS_DATA initClocks; ++ SDCONFIG_BUS_MODE_DATA busMode; ++ UINT32 OCRvalue; ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ ++ ZERO_OBJECT(initClocks); ++ ZERO_OBJECT(busMode); ++ /* setup defaults */ ++ initClocks.NumberOfClocks = SDMMC_MIN_INIT_CLOCKS; ++ busMode.ClockRate = SD_INIT_BUS_CLOCK; ++ ++ /* check for SPI only */ ++ if (pHcd->Attributes & SDHCD_ATTRIB_BUS_SPI) { ++ /* SPI cards startup in non-CRC mode with the exception of CMD0, the ++ * HCDs must issue CMD0 with the correct CRC , the spec shows that a ++ * CMD 0 sequence is 0x40,0x00,0x00,0x00,0x00,0x95 */ ++ busMode.BusModeFlags = SDCONFIG_BUS_WIDTH_SPI | SDCONFIG_BUS_MODE_SPI_NO_CRC; ++ } ++ /* check if host supports 1 bit mode */ ++ /* TODO : if host supports power switching, we can ++ * could initialize cards in SPI mode first */ ++ if (pHcd->Attributes & SDHCD_ATTRIB_BUS_1BIT) { ++ busMode.BusModeFlags = SDCONFIG_BUS_WIDTH_1_BIT; ++ } ++ ++ /* set initial VDD, starting at the highest allowable voltage and working ++ * our way down */ ++ if (pHcd->SlotVoltageCaps & SLOT_POWER_3_3V) { ++ OCRvalue = SD_OCR_3_2_TO_3_3_VDD; ++ } else if (pHcd->SlotVoltageCaps & SLOT_POWER_3_0V) { ++ OCRvalue = SD_OCR_2_9_TO_3_0_VDD; ++ } else if (pHcd->SlotVoltageCaps & SLOT_POWER_2_8V) { ++ OCRvalue = SD_OCR_2_7_TO_2_8_VDD; ++ } else if (pHcd->SlotVoltageCaps & SLOT_POWER_2_0V) { ++ OCRvalue = SD_OCR_1_9_TO_2_0_VDD; ++ } else if (pHcd->SlotVoltageCaps & SLOT_POWER_1_8V) { ++ OCRvalue = SD_OCR_1_7_TO_1_8_VDD; ++ } else if (pHcd->SlotVoltageCaps & SLOT_POWER_1_6V) { ++ OCRvalue = SD_OCR_1_6_TO_1_7_VDD; ++ } else { ++ DBG_ASSERT(FALSE); ++ OCRvalue = 0; ++ } ++ ++ do { ++ /* power up the card */ ++ status = AdjustSlotPower(pHcd, &OCRvalue); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to adjust slot power \n")); ++ break; ++ } ++ status = SetOperationalBusMode(pHcd->pPseudoDev,&busMode); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set bus mode \n")); ++ break; ++ } ++ status = _IssueConfig(pHcd,SDCONFIG_SEND_INIT_CLOCKS,&initClocks,sizeof(initClocks)); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to send init clocks in hcd \n")); ++ break; ++ } ++ ++ } while(FALSE); ++ ++ return status; ++} ++ ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ SDInitializeCard - initialize card ++ Input: pHcd - HCD object ++ Output: pProperties - card properties ++ Return: ++ Notes: ++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++SDIO_STATUS SDInitializeCard(PSDHCD pHcd) ++{ ++ SDCONFIG_BUS_MODE_DATA busMode; ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ PSDREQUEST pReq = NULL; ++ UINT32 OCRvalue; ++ UINT32 tplAddr; ++ UINT8 temp; ++ struct SDIO_MANFID_TPL manfid; ++ SDCONFIG_WP_VALUE wpValue; ++ UINT8 cisBuffer[3]; ++ ++ OCRvalue = 0; ++ ++ do { ++ if (IS_HCD_BUS_MODE_SPI(pHcd)) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Initializing card in SPI mode \n")); ++ } else { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Initializing card in MMC/SD mode \n")); ++ } ++ ++ pReq = AllocateRequest(); ++ if (NULL == pReq) { ++ status = SDIO_STATUS_NO_RESOURCES; ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: failed to allocate bus request \n")); ++ break; ++ } ++ memset(pReq, 0, sizeof(SDREQUEST)); ++ ++ status = CardInitSetup(pHcd); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to setup card \n")); ++ break; ++ } ++ status = _IssueConfig(pHcd,SDCONFIG_GET_WP,&wpValue,sizeof(wpValue)); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: host doesn't support Write Protect \n")); ++ } else { ++ if (wpValue) { ++ pHcd->CardProperties.Flags |= CARD_SD_WP; ++ DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: SD WP switch is on \n")); ++ } ++ } ++ ++ if (!(pHcd->Attributes & SDHCD_ATTRIB_SLOT_POLLING) && ++ IS_HCD_BUS_MODE_SPI(pHcd)) { ++ /* for non-slot polling HCDs operating in SPI mode ++ * issue CMD0 to reset card state and to place the card ++ * in SPI mode. If slot polling is used, the polling thread ++ * will have already issued a CMD0 to place the card in SPI mode*/ ++ if (IS_HCD_BUS_MODE_SPI(pHcd)) { ++ INT ii = 256; ++ status = SDIO_STATUS_ERROR; ++ /* if the CMD0 fails, retry it. Some cards have a hard time getting into SPI mode.*/ ++ while ((!SDIO_SUCCESS(status)) && (ii-- >= 0)) { ++ status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_RESP_R1,pReq); ++ OSSleep(20); ++ } ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: cmd0 go SPI retries:(256) %d\n", ii)); ++ ++ } else { ++ status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_NO_RESP,pReq); ++ } ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: go-idle failed! \n")); ++ break; ++ } ++ } ++ ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Looking for SDIO.. \n")); ++ /* check for SDIO card by trying to read it's OCR */ ++ status = ReadOCR(pHcd,CARD_SDIO,pReq,0,&OCRvalue); ++ if (SDIO_SUCCESS(status)) { ++ /* we got a response, this is an SDIO card */ ++ if (IS_HCD_BUS_MODE_SPI(pHcd)) { ++ /* handle SPI */ ++ pHcd->CardProperties.IOFnCount = SPI_SDIO_R4_GET_IO_FUNC_COUNT(pReq->Response); ++ if (SPI_SDIO_R4_IS_MEMORY_PRESENT(pReq->Response)) { ++ /* flag an SD function exists */ ++ pHcd->CardProperties.Flags |= CARD_SD; ++ } ++ } else { ++ /* handle native SD */ ++ pHcd->CardProperties.IOFnCount = SD_SDIO_R4_GET_IO_FUNC_COUNT(pReq->Response); ++ if (SD_SDIO_R4_IS_MEMORY_PRESENT(pReq->Response)) { ++ /* flag an SD function exists */ ++ pHcd->CardProperties.Flags |= CARD_SD; ++ } ++ ++ } ++ if (0 == pHcd->CardProperties.IOFnCount) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SDIO Card reports no functions \n")); ++ status = SDIO_STATUS_DEVICE_ERROR; ++ pHcd->CardProperties.Flags = 0; ++ break; ++ } ++ pHcd->CardProperties.Flags |= CARD_SDIO; ++ ++ DBG_PRINT(SDDBG_TRACE, ++ ("SDIO Bus Driver: SDIO Card, Functions: %d Card Info Flags:0x%X OCR:0x%8.8X\n", ++ pHcd->CardProperties.IOFnCount, pHcd->CardProperties.Flags, OCRvalue)); ++ /* adjust slot power for this SDIO card */ ++ status = AdjustSlotPower(pHcd, &OCRvalue); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set power in hcd \n")); ++ break; ++ } ++ /* poll for SDIO card ready */ ++ status = PollCardReady(pHcd,OCRvalue,CARD_SDIO); ++ if (!SDIO_SUCCESS(status)) { ++ break; ++ } ++ } else if (status != SDIO_STATUS_BUS_RESP_TIMEOUT){ ++ /* major error in hcd, bail */ ++ break; ++ } ++ ++ /* check if this is an SDIO-only card before continuing */ ++ if (!(pHcd->CardProperties.Flags & CARD_SD) && (pHcd->CardProperties.Flags & CARD_SDIO)) { ++ /* this is an SDIO card with no memory function */ ++ goto prepareCard; ++ } ++ ++ if (!(pHcd->CardProperties.Flags & CARD_SDIO)) { ++ /* issue go idle only if we did not find an SDIO function in our earlier test */ ++ if (IS_HCD_BUS_MODE_SPI(pHcd)) { ++ status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_RESP_R1,pReq); ++ } else { ++ status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_NO_RESP,pReq); ++ } ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: go-idle failed! \n")); ++ break; ++ } ++ } ++ ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Looking for SD Memory.. \n")); ++ /* SD Memory Card checking */ ++ /* test for present of SD card (stand-alone or combo card) */ ++ status = TestPresence(pHcd, CARD_SD, pReq); ++ if (SDIO_SUCCESS(status)) { ++ /* there is an SD Card present, could be part of a combo system */ ++ pHcd->CardProperties.Flags |= CARD_SD; ++ if (0 == OCRvalue) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD Memory card detected. \n")); ++ /* no OCR value on entry this is a stand-alone card, go and get it*/ ++ status = ReadOCR(pHcd,CARD_SD,pReq,0,&OCRvalue); ++ if (!SDIO_SUCCESS(status) || (OCRvalue == 0)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get OCR (status:%d) \n", ++ status)); ++ break; ++ } ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD Card Reports OCR:0x%8.8X \n", OCRvalue)); ++ status = AdjustSlotPower(pHcd, &OCRvalue); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to adjust power \n")); ++ break; ++ } ++ } else { ++ DBG_ASSERT((pHcd->CardProperties.Flags & (CARD_SD | CARD_SDIO))); ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SDIO Combo Card detected \n")); ++ } ++ /* poll for SD card ready */ ++ status = PollCardReady(pHcd,OCRvalue,CARD_SD); ++ if (!SDIO_SUCCESS(status)) { ++ /* check if this card has an SDIO function */ ++ if (pHcd->CardProperties.Flags & CARD_SDIO) { ++ DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: Combo Detected but SD memory function failed \n")); ++ /* allow SDIO functions to load normally */ ++ status = SDIO_STATUS_SUCCESS; ++ /* remove SD flag */ ++ pHcd->CardProperties.Flags &= ~CARD_SD; ++ } else { ++ break; ++ } ++ } else { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD Memory ready. \n")); ++ } ++ /* we're done, no need to check for MMC */ ++ goto prepareCard; ++ } else if (status != SDIO_STATUS_BUS_RESP_TIMEOUT){ ++ /* major error in hcd, bail */ ++ break; ++ } ++ ++ /* MMC card checking */ ++ /* if we get here, these better not be set */ ++ DBG_ASSERT(!(pHcd->CardProperties.Flags & (CARD_SD | CARD_SDIO))); ++ /* issue go idle */ ++ if (IS_HCD_BUS_MODE_SPI(pHcd)) { ++ status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_RESP_R1,pReq); ++ } else { ++ status = _IssueSimpleBusRequest(pHcd,CMD0,0,SDREQ_FLAGS_NO_RESP,pReq); ++ } ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: go-idle failed! \n")); ++ break; ++ } ++ ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Looking for MMC.. \n")); ++ status = TestPresence(pHcd, CARD_MMC, pReq); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: unknown card detected \n")); ++ break; ++ } ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: MMC Card Detected \n")); ++ pHcd->CardProperties.Flags |= CARD_MMC; ++ /* read the OCR value */ ++ status = ReadOCR(pHcd,CARD_MMC,pReq,0,&OCRvalue); ++ if (!SDIO_SUCCESS(status) || (OCRvalue == 0)) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Failed to get OCR (status:%d)", ++ status)); ++ break; ++ } ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: MMC Card Reports OCR:0x%8.8X \n", OCRvalue)); ++ /* adjust power */ ++ status = AdjustSlotPower(pHcd, &OCRvalue); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to adjust power \n")); ++ break; ++ } ++ /* poll for MMC card ready */ ++ status = PollCardReady(pHcd,OCRvalue,CARD_MMC); ++ if (!SDIO_SUCCESS(status)) { ++ break; ++ } ++ /* fall through and prepare MMC card */ ++ ++prepareCard: ++ /* we're done figuring out what was inserted, and setting up ++ * optimal slot voltage, now we need to prepare the card */ ++ if (!IS_HCD_BUS_MODE_SPI(pHcd) && ++ (pHcd->CardProperties.Flags & (CARD_SD | CARD_MMC))) { ++ /* non-SPI SD or MMC cards need to be moved to the "ident" state before we can get the ++ * RCA or select the card using the new RCA */ ++ status = _IssueSimpleBusRequest(pHcd,CMD2,0,SDREQ_FLAGS_RESP_R2,pReq); ++ if (!SDIO_SUCCESS(status)){ ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: failed to move SD/MMC card into ident state \n")); ++ break; ++ } ++ } ++ ++ if (!IS_HCD_BUS_MODE_SPI(pHcd)) { ++ /* non-SPI mode cards need their RCA's setup */ ++ if (pHcd->CardProperties.Flags & (CARD_SD | CARD_SDIO)) { ++ /* issue CMD3 to get RCA on SD/SDIO cards */ ++ status = _IssueSimpleBusRequest(pHcd,CMD3,0,SDREQ_FLAGS_RESP_R6,pReq); ++ if (!SDIO_SUCCESS(status)){ ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: failed to get RCA for SD/SDIO card \n")); ++ break; ++ } ++ pHcd->CardProperties.RCA = SD_R6_GET_RCA(pReq->Response); ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: SD/SDIO RCA:0x%X \n", ++ pHcd->CardProperties.RCA)); ++ } else if (pHcd->CardProperties.Flags & CARD_MMC) { ++ /* for MMC cards, we have to assign a relative card address */ ++ /* just a non-zero number */ ++ pHcd->CardProperties.RCA = 1; ++ /* issue CMD3 to set the RCA for MMC cards */ ++ status = _IssueSimpleBusRequest(pHcd, ++ CMD3,(pHcd->CardProperties.RCA << 16), ++ SDREQ_FLAGS_RESP_R1,pReq); ++ if (!SDIO_SUCCESS(status)){ ++ DBG_PRINT(SDDBG_ERROR, ++ ("SDIO Bus Driver: failed to set RCA for MMC card! (err=%d) \n",status)); ++ break; ++ } ++ } else { ++ DBG_ASSERT(FALSE); ++ } ++ } ++ /* select the card in order to get the rest of the card info, applies ++ * to SDIO/SD/MMC cards*/ ++ status = SelectDeselectCard(pHcd, TRUE); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: failed to select card! \n")); ++ break; ++ } ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver, Card now Selected.. \n")); ++ ++ if (pHcd->CardProperties.Flags & CARD_SDIO) { ++ /* read SDIO revision register */ ++ status = Cmd52ReadByteCommon(pHcd->pPseudoDev, CCCR_SDIO_REVISION_REG, &temp); ++ if (!SDIO_SUCCESS(status)) { ++ break; ++ } ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Revision Reg: 0x%X \n", temp)); ++ switch (temp & SDIO_REV_MASK) { ++ case SDIO_REV_1_00: ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Spec Revision 1.00 \n")); ++ pHcd->CardProperties.SDIORevision = SDIO_REVISION_1_00; ++ break; ++ case SDIO_REV_1_10: ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Spec Revision 1.10 \n")); ++ pHcd->CardProperties.SDIORevision = SDIO_REVISION_1_10; ++ break; ++ case SDIO_REV_1_20: ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Spec Revision 1.20 \n")); ++ pHcd->CardProperties.SDIORevision = SDIO_REVISION_1_20; ++ break; ++ default: ++ DBG_PRINT(SDDBG_WARN, ("SDIO Warning: unknown SDIO revision, treating like 1.0 device \n")); ++ pHcd->CardProperties.SDIORevision = SDIO_REVISION_1_00; ++ break; ++ } ++ /* get the common CIS ptr */ ++ status = Cmd52ReadMultipleCommon(pHcd->pPseudoDev, ++ SDIO_CMN_CIS_PTR_LOW_REG, ++ cisBuffer, ++ 3); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get CIS ptr, Err:%d", status)); ++ break; ++ } ++ /* this is endian-safe*/ ++ pHcd->CardProperties.CommonCISPtr = ((UINT32)cisBuffer[0]) | ++ (((UINT32)cisBuffer[1]) << 8) | ++ (((UINT32)cisBuffer[2]) << 16); ++ ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Card CIS Ptr: 0x%X \n", pHcd->CardProperties.CommonCISPtr)); ++ temp = sizeof(manfid); ++ tplAddr = pHcd->CardProperties.CommonCISPtr; ++ /* get the MANFID tuple */ ++ status = SDLIB_FindTuple(pHcd->pPseudoDev, ++ CISTPL_MANFID, ++ &tplAddr, ++ (PUINT8)&manfid, ++ &temp); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: Failed to get MANFID tuple err:%d \n", status)); ++ status = SDIO_STATUS_SUCCESS; ++ } else { ++ /* save this off so that it can be copied into each SDIO Func's SDDEVICE structure */ ++ pHcd->CardProperties.SDIO_ManufacturerCode = ++ CT_LE16_TO_CPU_ENDIAN(manfid.ManufacturerCode); ++ pHcd->CardProperties.SDIO_ManufacturerID = ++ CT_LE16_TO_CPU_ENDIAN(manfid.ManufacturerInfo); ++ DBG_PRINT(SDDBG_TRACE, ("SDIO MANFID:0x%X, MANFINFO:0x%X \n", ++ pHcd->CardProperties.SDIO_ManufacturerID, ++ pHcd->CardProperties.SDIO_ManufacturerCode)); ++ } ++ ++ if (pHcd->CardProperties.SDIORevision >= SDIO_REVISION_1_10) { ++ /* read power control */ ++ status = Cmd52ReadByteCommon(pHcd->pPseudoDev, SDIO_POWER_CONTROL_REG, &temp); ++ if (SDIO_SUCCESS(status)) { ++ /* check for power control support which indicates the card may use more ++ * than 200 mA */ ++ if (temp & SDIO_POWER_CONTROL_SMPC) { ++ /* check that the host can support this. */ ++ if (pHcd->MaxSlotCurrent >= SDIO_EMPC_CURRENT_THRESHOLD) { ++ temp = SDIO_POWER_CONTROL_EMPC; ++ /* enable power control on the card */ ++ status = Cmd52WriteByteCommon(pHcd->pPseudoDev, SDIO_POWER_CONTROL_REG, &temp); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ++ ("SDIO Busdriver: failed to enable power control (%d) \n",status)); ++ break; ++ } ++ /* mark that the card is high power */ ++ pHcd->CardProperties.Flags |= CARD_HIPWR; ++ ++ DBG_PRINT(SDDBG_TRACE, ++ ("SDIO Busdriver: Power Control Enabled on SDIO (1.10 or greater) card \n")); ++ } else { ++ DBG_PRINT(SDDBG_WARN, ++ ("SDIO Busdriver: Card can operate higher than 200mA, host cannot (max:%d) \n", ++ pHcd->MaxSlotCurrent)); ++ /* this is not fatal, the card should operate at a reduced rate */ ++ } ++ } else { ++ DBG_PRINT(SDDBG_TRACE, ++ ("SDIO Busdriver: SDIO 1.10 (or greater) card draws less than 200mA \n")); ++ } ++ } else { ++ DBG_PRINT(SDDBG_WARN, ++ ("SDIO Busdriver: failed to get POWER CONTROL REG (%d) \n",status)); ++ /* fall through and continue on at reduced mode */ ++ } ++ } ++ } ++ /* get the current bus parameters */ ++ busMode.BusModeFlags = pHcd->CardProperties.BusMode; ++ busMode.ClockRate = pHcd->CardProperties.OperBusClock; ++ /* get the rest of the bus parameters like clock and supported bus width */ ++ status = GetBusParameters(pHcd,&busMode); ++ if (!SDIO_SUCCESS(status)) { ++ break; ++ } ++ ++ if (IS_HCD_BUS_MODE_SPI(pHcd)) { ++ /* check HCD if it wants to run without SPI CRC */ ++ if (pHcd->Attributes & SDHCD_ATTRIB_NO_SPI_CRC) { ++ /* hcd would rather not run with CRC we don't need to tell the card since SPI mode ++ * cards power up with CRC initially disabled */ ++ busMode.BusModeFlags |= SDCONFIG_BUS_MODE_SPI_NO_CRC; ++ } else { ++ /* first enable SPI CRC checking if the HCD can handle it */ ++ status = SDSPIModeEnableDisableCRC(pHcd->pPseudoDev, TRUE); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ++ ("SDIO Bus Driver: Failed to set Enable SPI CRC on card \n")); ++ break; ++ } ++ } ++ } ++ ++ status = SetOperationalBusMode(pHcd->pPseudoDev, &busMode); ++ ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set operational bus mode\n")); ++ break; ++ } ++ ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Oper. Mode: Clock:%d, Bus:0x%X \n", ++ pHcd->CardProperties.OperBusClock,pHcd->CardProperties.BusMode)); ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Card in TRANS state, Ready: CardInfo Flags 0x%X \n", ++ pHcd->CardProperties.Flags)); ++ ++ } while (FALSE); ++ ++ if (pReq != NULL) { ++ FreeRequest(pReq); ++ } ++ ++ return status; ++} ++ ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ SDQuerySDMMCInfo - query MMC card info ++ Input: pDevice - device ++ Output: ++ Return: ++ Notes: ++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++SDIO_STATUS SDQuerySDMMCInfo(PSDDEVICE pDevice) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ PSDREQUEST pReq = NULL; ++ UINT8 CID[MAX_CSD_CID_BYTES]; ++ ++ do { ++ pReq = AllocateRequest(); ++ if (NULL == pReq) { ++ status = SDIO_STATUS_NO_RESOURCES; ++ break; ++ } ++ /* de-select the card */ ++ status = SelectDeselectCard(pDevice->pHcd,FALSE); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to deselect card before getting CID \n")); ++ break; ++ } ++ ++ if (SDDEVICE_IS_BUSMODE_SPI(pDevice)) { ++ /* in SPI mode, getting the CSD requires a data transfer */ ++ status = _IssueBusRequestBd(pDevice->pHcd,CMD10,0, ++ SDREQ_FLAGS_RESP_R1 | SDREQ_FLAGS_DATA_TRANS, ++ pReq, ++ CID, ++ MAX_CSD_CID_BYTES); ++ if (SDIO_SUCCESS(status)) { ++ /* in SPI mode we need to reorder to the CID since SPI data comes in MSB first*/ ++ ReorderBuffer(CID,MAX_CSD_CID_BYTES); ++ } ++ } else { ++ /* get the CID */ ++ status = _IssueSimpleBusRequest(pDevice->pHcd, ++ CMD10, ++ (SDDEVICE_GET_CARD_RCA(pDevice) << 16), ++ SDREQ_FLAGS_RESP_R2, ++ pReq); ++ if (SDIO_SUCCESS(status)) { ++ /* extract it from the reponse */ ++ memcpy(CID,pReq->Response,MAX_CSD_CID_BYTES); ++ } ++ } ++ ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_WARN, ("SDQuerySDMMCInfo: failed to get CID. \n")); ++ status = SDIO_STATUS_SUCCESS; ++ } else { ++ pDevice->pId[0].SDMMC_ManfacturerID = GET_SD_CID_MANFID(CID); ++ pDevice->pId[0].SDMMC_OEMApplicationID = GET_SD_CID_OEMID(CID); ++#if DEBUG ++ { ++ char pBuf[7]; ++ ++ pBuf[0] = GET_SD_CID_PN_1(CID); ++ pBuf[1] = GET_SD_CID_PN_2(CID); ++ pBuf[2] = GET_SD_CID_PN_3(CID); ++ pBuf[3] = GET_SD_CID_PN_4(CID); ++ pBuf[4] = GET_SD_CID_PN_5(CID); ++ if (pDevice->pHcd->CardProperties.Flags & CARD_MMC) { ++ pBuf[5] = GET_SD_CID_PN_6(CID); ++ pBuf[6] = 0; ++ } else { ++ pBuf[5] = 0; ++ } ++ DBG_PRINT(SDDBG_TRACE, ("SDQuerySDMMCInfo: Product String: %s\n", pBuf)); ++ } ++#endif ++ DBG_PRINT(SDDBG_TRACE, ("SDQuerySDMMCInfo: ManfID: 0x%X, OEMID:0x%X \n", ++ pDevice->pId[0].SDMMC_ManfacturerID, pDevice->pId[0].SDMMC_OEMApplicationID)); ++ } ++ /* re-select card */ ++ status = SelectDeselectCard(pDevice->pHcd,TRUE); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to re-select card after getting CID \n")); ++ break; ++ } ++ } while (FALSE); ++ ++ if (pReq != NULL) { ++ FreeRequest(pReq); ++ } ++ ++ return status; ++} ++ ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ SDQuerySDIOInfo - query SDIO card info ++ Input: pDevice - the device ++ Output: ++ Return: ++ Notes: ++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++SDIO_STATUS SDQuerySDIOInfo(PSDDEVICE pDevice) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ UINT32 faddress; ++ UINT8 fInfo; ++ UINT32 nextTpl; ++ UINT8 tplLength; ++ UINT8 cisPtrBuffer[3]; ++ struct SDIO_FUNC_EXT_FUNCTION_TPL_1_1 funcTuple; ++ ++ /* use the card-wide SDIO manufacturer code and ID previously read.*/ ++ pDevice->pId[0].SDIO_ManufacturerCode = pDevice->pHcd->CardProperties.SDIO_ManufacturerCode; ++ pDevice->pId[0].SDIO_ManufacturerID = pDevice->pHcd->CardProperties.SDIO_ManufacturerID; ++ ++ /* calculate function base address */ ++ faddress = CalculateFBROffset(SDDEVICE_GET_SDIO_FUNCNO(pDevice)); ++ DBG_ASSERT(faddress != 0); ++ ++ do { ++ status = Cmd52ReadByteCommon(pDevice, ++ FBR_FUNC_INFO_REG_OFFSET(faddress), ++ &fInfo); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get function info, Err:%d , using Class:UNKNOWN\n", status)); ++ fInfo = 0; ++ pDevice->pId[0].SDIO_FunctionClass = 0; ++ status = SDIO_STATUS_SUCCESS; ++ } else { ++ pDevice->pId[0].SDIO_FunctionClass = fInfo & FUNC_INFO_DEVICE_CODE_MASK; ++ } ++ ++ if ((FUNC_INFO_DEVICE_CODE_LAST == pDevice->pId[0].SDIO_FunctionClass) && ++ SDDEVICE_IS_SDIO_REV_GTEQ_1_10(pDevice)) { ++ /* if the device code is the last one, check for 1.1 revision and get the ++ * extended code */ ++ status = Cmd52ReadByteCommon(pDevice, ++ FBR_FUNC_EXT_DEVICE_CODE_OFFSET(faddress), ++ &(pDevice->pId[0].SDIO_FunctionClass)); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get 1.1 extended DC, Err:%d\n", ++ status)); ++ break; ++ } ++ } ++ ++ /* get the function CIS ptr */ ++ status = Cmd52ReadMultipleCommon(pDevice, ++ FBR_FUNC_CIS_LOW_OFFSET(faddress), ++ cisPtrBuffer, ++ 3); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get FN CIS ptr, Err:%d\n", status)); ++ break; ++ } ++ /* endian safe */ ++ pDevice->DeviceInfo.AsSDIOInfo.FunctionCISPtr = ((UINT32)cisPtrBuffer[0]) | ++ (((UINT32)cisPtrBuffer[1]) << 8) | ++ (((UINT32)cisPtrBuffer[2]) << 16); ++ ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Function:%d, Class:%d FnCISPtr:0x%X \n", ++ SDDEVICE_GET_SDIO_FUNCNO(pDevice), ++ pDevice->pId[0].SDIO_FunctionClass,pDevice->DeviceInfo.AsSDIOInfo.FunctionCISPtr)); ++ ++ if (fInfo & FUNC_INFO_SUPPORTS_CSA_MASK) { ++ /* get the function CSA ptr */ ++ status = Cmd52ReadMultipleCommon(pDevice, ++ FBR_FUNC_CSA_LOW_OFFSET(faddress), ++ cisPtrBuffer, ++ 3); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get FN CSA ptr, Err:%d \n", status)); ++ break; ++ } ++ /* endian safe */ ++ pDevice->DeviceInfo.AsSDIOInfo.FunctionCSAPtr = ((UINT32)cisPtrBuffer[0]) | ++ (((UINT32)cisPtrBuffer[1]) << 8) | ++ (((UINT32)cisPtrBuffer[2]) << 16); ++ ++ } ++ ++ nextTpl = SDDEVICE_GET_SDIO_FUNC_CISPTR(pDevice); ++ /* look for the funce TPL */ ++ tplLength = sizeof(funcTuple); ++ /* go get the func CE tuple */ ++ status = SDLIB_FindTuple(pDevice, ++ CISTPL_FUNCE, ++ &nextTpl, ++ (PUINT8)&funcTuple, ++ &tplLength); ++ ++ if (!SDIO_SUCCESS(status)){ ++ /* handles case of bad CIS or missing tupple, allow function driver to handle */ ++ DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: Failed to get FuncCE Tuple: %d \n", status)); ++ status = SDIO_STATUS_SUCCESS; ++ break; ++ } ++ /* set the max block size */ ++ pDevice->DeviceInfo.AsSDIOInfo.FunctionMaxBlockSize = ++ CT_LE16_TO_CPU_ENDIAN(funcTuple.CommonInfo.MaxBlockSize); ++ ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Function:%d, MaxBlocks:%d \n", ++ SDDEVICE_GET_SDIO_FUNCNO(pDevice), ++ pDevice->DeviceInfo.AsSDIOInfo.FunctionMaxBlockSize)); ++ ++ /* check for MANFID function tuple (SDIO 1.1 or greater) */ ++ if (SDDEVICE_IS_SDIO_REV_GTEQ_1_10(pDevice)) { ++ struct SDIO_MANFID_TPL manfid; ++ nextTpl = SDDEVICE_GET_SDIO_FUNC_CISPTR(pDevice); ++ tplLength = sizeof(manfid); ++ /* get the MANFID tuple */ ++ status = SDLIB_FindTuple(pDevice, ++ CISTPL_MANFID, ++ &nextTpl, ++ (PUINT8)&manfid, ++ &tplLength); ++ if (SDIO_SUCCESS(status)) { ++ /* this function has a MANFID tuple */ ++ pDevice->pId[0].SDIO_ManufacturerCode = ++ CT_LE16_TO_CPU_ENDIAN(manfid.ManufacturerCode); ++ pDevice->pId[0].SDIO_ManufacturerID = ++ CT_LE16_TO_CPU_ENDIAN(manfid.ManufacturerInfo); ++ DBG_PRINT(SDDBG_TRACE, ("SDIO 1.1 (Function Specific) MANFID:0x%X, MANFINFO:0x%X \n", ++ pDevice->pId[0].SDIO_ManufacturerID, ++ pDevice->pId[0].SDIO_ManufacturerCode)); ++ } else { ++ DBG_PRINT(SDDBG_WARN, ("SDIO 1.1, No CISTPL_MANFID Tuple in FUNC CIS \n")); ++ status = SDIO_STATUS_SUCCESS; ++ } ++ } ++ } while (FALSE); ++ ++ return status; ++} ++ ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ SDEnableFunction - enable function ++ Input: pDevice - the device/function ++ pEnData - enable data; ++ Output: ++ Return: status ++ Notes: Note, this performs synchronous calls ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++SDIO_STATUS SDEnableFunction(PSDDEVICE pDevice, PSDCONFIG_FUNC_ENABLE_DISABLE_DATA pEnData) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ UINT8 registerValue; ++ UINT8 mask; ++ FUNC_ENABLE_TIMEOUT retry; ++ ++ /* take the configure op lock to make this atomic */ ++ status = SemaphorePendInterruptable(&pDevice->pHcd->ConfigureOpsSem); ++ if (!SDIO_SUCCESS(status)) { ++ return status; ++ } ++ ++ status = SDIO_STATUS_INVALID_PARAMETER; ++ do { ++ if (!(pDevice->pHcd->CardProperties.Flags & CARD_SDIO)){ ++ /* nothing to do if it's not an SDIO card */ ++ break; ++ } ++ ++ if (!((SDDEVICE_GET_SDIO_FUNCNO(pDevice) >= SDIO_FIRST_FUNCTION_NUMBER) && ++ (SDDEVICE_GET_SDIO_FUNCNO(pDevice) <= SDIO_LAST_FUNCTION_NUMBER))){ ++ DBG_ASSERT(FALSE); ++ break; ++ } ++ /* make sure there is a timeout value */ ++ if (0 == pEnData->TimeOut) { ++ break; ++ } ++ ++ mask = 1 << SDDEVICE_GET_SDIO_FUNCNO(pDevice); ++ /* read the enable register */ ++ status = Cmd52ReadByteCommon(pDevice, SDIO_ENABLE_REG, ®isterValue); ++ if (!SDIO_SUCCESS(status)){ ++ break; ++ } ++ if (pEnData->EnableFlags & SDCONFIG_ENABLE_FUNC) { ++ /* set the enable register bit */ ++ registerValue |= mask; ++ } else { ++ /* clear the bit */ ++ registerValue &= ~mask; ++ } ++ ++ DBG_PRINT(SDDBG_TRACE, ++ ("SDIO Bus Driver %s Function, Mask:0x%X Enable Reg Value:0x%2.2X\n", ++ (pEnData->EnableFlags & SDCONFIG_ENABLE_FUNC) ? "Enabling":"Disabling", ++ mask, ++ registerValue)); ++ ++ /* write it back out */ ++ status = Cmd52WriteByteCommon(pDevice, SDIO_ENABLE_REG, ®isterValue); ++ if (!SDIO_SUCCESS(status)){ ++ break; ++ } ++ /* now poll the ready bit until it sets or clears */ ++ retry = pEnData->TimeOut; ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Function Enable/Disable Polling: %d retries \n", ++ retry)); ++ while (retry) { ++ status = Cmd52ReadByteCommon(pDevice, SDIO_READY_REG, ®isterValue); ++ if (!SDIO_SUCCESS(status)){ ++ break; ++ } ++ if (pEnData->EnableFlags & SDCONFIG_ENABLE_FUNC) { ++ /* if the bit is set, the device is ready */ ++ if (registerValue & mask) { ++ /* device ready */ ++ break; ++ } ++ } else { ++ if (!(registerValue & mask)) { ++ /* device is no longer ready */ ++ break; ++ } ++ } ++ /* sleep before trying again */ ++ status = OSSleep(1); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("OSSleep Failed! \n")); ++ break; ++ } ++ retry--; ++ } ++ ++ if (0 == retry) { ++ status = SDIO_STATUS_FUNC_ENABLE_TIMEOUT; ++ break; ++ } ++ ++ } while (FALSE); ++ ++ SemaphorePost(&pDevice->pHcd->ConfigureOpsSem); ++ return status; ++} ++ ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ SDAllocFreeSlotCurrent - allocate or free slot current ++ Input: pDevice - the device/function ++ Allocate - Allocate current, else free ++ pData - slotcurrent data (non-NULL if Allocate is TRUE) ++ Output: ++ Return: status ++ Notes: if the function returns SDIO_STATUS_NO_RESOURCES, the pData->SlotCurrent field is ++ updated with the available current ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++SDIO_STATUS SDAllocFreeSlotCurrent(PSDDEVICE pDevice, BOOL Allocate, PSDCONFIG_FUNC_SLOT_CURRENT_DATA pData) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ ++ DBG_PRINT(SDDBG_TRACE, ("+SDIO Bus Driver: SDAllocFreeSlotCurrent\n")); ++ ++ /* take the configure op lock to make this atomic */ ++ status = SemaphorePendInterruptable(&pDevice->pHcd->ConfigureOpsSem); ++ if (!SDIO_SUCCESS(status)) { ++ return status; ++ } ++ ++ status = SDIO_STATUS_INVALID_PARAMETER; ++ do { ++ /* check the current budget and allocate */ ++ if (Allocate) { ++ if (0 == pData->SlotCurrent) { ++ /* caller must specify current requirement for the power mode */ ++ break; ++ } ++ if (pDevice->SlotCurrentAlloc != 0) { ++ /* slot current has already been allocated, caller needs to free ++ * first */ ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Slot Current Already allocated! \n")); ++ break; ++ } ++ if (((UINT32)pDevice->pHcd->SlotCurrentAllocated + (UINT32)pData->SlotCurrent) > ++ (UINT32)pDevice->pHcd->MaxSlotCurrent) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Slot Current Budget exceeded, Requesting: %d, Allocated already: %d, Max: %d \n", ++ pData->SlotCurrent, pDevice->pHcd->SlotCurrentAllocated, ++ pDevice->pHcd->MaxSlotCurrent)); ++ status = SDIO_STATUS_NO_RESOURCES; ++ /* return remaining */ ++ pData->SlotCurrent = pDevice->pHcd->MaxSlotCurrent - ++ pDevice->pHcd->SlotCurrentAllocated; ++ break; ++ } ++ /* bump up allocation */ ++ pDevice->pHcd->SlotCurrentAllocated += pData->SlotCurrent; ++ /* save this off for the call to free slot current */ ++ pDevice->SlotCurrentAlloc = pData->SlotCurrent; ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Slot Current Requested: %d, New Total: %d, Max: %d \n", ++ pData->SlotCurrent, pDevice->pHcd->SlotCurrentAllocated, ++ pDevice->pHcd->MaxSlotCurrent)); ++ ++ } else { ++ if (0 == pDevice->SlotCurrentAlloc) { ++ /* no allocation */ ++ break; ++ } ++ /* return the allocation back */ ++ if (pDevice->SlotCurrentAlloc <= pDevice->pHcd->SlotCurrentAllocated) { ++ pDevice->pHcd->SlotCurrentAllocated -= pDevice->SlotCurrentAlloc; ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Slot Current Freed: %d, New Total: %d, Max: %d \n", ++ pDevice->SlotCurrentAlloc, pDevice->pHcd->SlotCurrentAllocated, ++ pDevice->pHcd->MaxSlotCurrent)); ++ } else { ++ DBG_ASSERT(FALSE); ++ } ++ ++ /* make sure this is zeroed */ ++ pDevice->SlotCurrentAlloc = 0; ++ } ++ ++ status = SDIO_STATUS_SUCCESS; ++ ++ } while (FALSE); ++ ++ SemaphorePost(&pDevice->pHcd->ConfigureOpsSem); ++ DBG_PRINT(SDDBG_TRACE, ("-SDIO Bus Driver: SDAllocFreeSlotCurrent, %d\n", status)); ++ return status; ++} ++ ++static void RawHcdIrqControl(PSDHCD pHcd, BOOL Enable) ++{ ++ SDIO_STATUS status; ++ SDCONFIG_SDIO_INT_CTRL_DATA irqData; ++ CT_DECLARE_IRQ_SYNC_CONTEXT(); ++ ++ ZERO_OBJECT(irqData); ++ ++ status = _AcquireHcdLock(pHcd); ++ if (!SDIO_SUCCESS(status)) { ++ return; ++ } ++ ++ do { ++ /* for raw devices, we simply enable/disable in the HCD only */ ++ if (Enable) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver (RAW) Unmasking Int \n")); ++ irqData.IRQDetectMode = IRQ_DETECT_RAW; ++ irqData.SlotIRQEnable = TRUE; ++ } else { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver (RAW) Masking Int \n")); ++ irqData.SlotIRQEnable = FALSE; ++ } ++ ++ status = _IssueConfig(pHcd,SDCONFIG_SDIO_INT_CTRL, ++ (PVOID)&irqData, sizeof(irqData)); ++ ++ if (!SDIO_SUCCESS(status)){ ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver failed to enable/disable IRQ in (RAW) hcd :%d\n", ++ status)); ++ } ++ ++ } while (FALSE); ++ ++ status = _ReleaseHcdLock(pHcd); ++} ++ ++static void RawHcdEnableIrqPseudoComplete(PSDREQUEST pReq) ++{ ++ if (SDIO_SUCCESS(pReq->Status)) { ++ RawHcdIrqControl((PSDHCD)pReq->pCompleteContext, TRUE); ++ } ++ FreeRequest(pReq); ++} ++ ++static void RawHcdDisableIrqPseudoComplete(PSDREQUEST pReq) ++{ ++ RawHcdIrqControl((PSDHCD)pReq->pCompleteContext, FALSE); ++ FreeRequest(pReq); ++} ++ ++static void HcdIrqControl(PSDHCD pHcd, BOOL Enable) ++{ ++ SDIO_STATUS status; ++ SDCONFIG_SDIO_INT_CTRL_DATA irqData; ++ CT_DECLARE_IRQ_SYNC_CONTEXT(); ++ ++ ZERO_OBJECT(irqData); ++ ++ status = _AcquireHcdLock(pHcd); ++ if (!SDIO_SUCCESS(status)) { ++ return; ++ } ++ ++ do { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: HcdIrqControl (%s), IrqsEnabled:0x%X \n", ++ Enable ? "Enable":"Disable",pHcd->IrqsEnabled )); ++ ++ if (Enable) { ++ irqData.SlotIRQEnable = TRUE; ++ } else { ++ irqData.SlotIRQEnable = FALSE; ++ } ++ /* setup HCD to enable/disable it's detection hardware */ ++ if (irqData.SlotIRQEnable) { ++ /* set the IRQ detection mode */ ++ switch (SDCONFIG_GET_BUSWIDTH(pHcd->CardProperties.BusMode)) { ++ case SDCONFIG_BUS_WIDTH_SPI: ++ irqData.IRQDetectMode = IRQ_DETECT_SPI; ++ break; ++ case SDCONFIG_BUS_WIDTH_1_BIT: ++ irqData.IRQDetectMode = IRQ_DETECT_1_BIT; ++ break; ++ case SDCONFIG_BUS_WIDTH_4_BIT: ++ irqData.IRQDetectMode = IRQ_DETECT_4_BIT; ++ /* check card and HCD for 4bit multi-block interrupt support */ ++ if ((pHcd->CardProperties.SDIOCaps & SDIO_CAPS_INT_MULTI_BLK) && ++ (pHcd->Attributes & SDHCD_ATTRIB_MULTI_BLK_IRQ)) { ++ /* note: during initialization of the card, the mult-blk IRQ support ++ * is enabled in card caps register */ ++ irqData.IRQDetectMode |= IRQ_DETECT_MULTI_BLK; ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver enabling IRQ in multi-block mode:\n")); ++ } ++ break; ++ default: ++ DBG_ASSERT(FALSE); ++ break; ++ } ++ ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver enabling IRQ in HCD Mode:0x%X\n", ++ irqData.IRQDetectMode)); ++ } ++ ++ status = _IssueConfig(pHcd,SDCONFIG_SDIO_INT_CTRL, ++ (PVOID)&irqData, sizeof(irqData)); ++ if (!SDIO_SUCCESS(status)){ ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver failed to enable/disable IRQ in hcd %d\n", ++ status)); ++ } ++ ++ } while (FALSE); ++ ++ status = _ReleaseHcdLock(pHcd); ++} ++ ++static BOOL CheckWriteIntEnableSuccess(PSDREQUEST pReq) ++{ ++ if (!SDIO_SUCCESS(pReq->Status)){ ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to get write INT Enable register Err:%d\n", ++ pReq->Status)); ++ return FALSE; ++ } ++ ++ if (SD_R5_GET_RESP_FLAGS(pReq->Response) & SD_R5_ERRORS) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: WriteIntEnableComplete CMD52 resp error: 0x%X \n", ++ SD_R5_GET_RESP_FLAGS(pReq->Response))); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static void HcdIrqEnableComplete(PSDREQUEST pReq) ++{ ++ if (CheckWriteIntEnableSuccess(pReq)) { ++ /* configure HCD */ ++ HcdIrqControl((PSDHCD)pReq->pCompleteContext, TRUE); ++ } ++ FreeRequest(pReq); ++} ++ ++static void HcdIrqDisableComplete(PSDREQUEST pReq) ++{ ++ CheckWriteIntEnableSuccess(pReq); ++ HcdIrqControl((PSDHCD)pReq->pCompleteContext, FALSE); ++ FreeRequest(pReq); ++} ++ ++static void WriteIntEnableComplete(PSDREQUEST pReq) ++{ ++ if (CheckWriteIntEnableSuccess(pReq)) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: Wrote INT Enable value:0x%X \n", ++ (INT)pReq->pCompleteContext)); ++ } ++ FreeRequest(pReq); ++} ++ ++static void HcdAckComplete(PSDREQUEST pReq) ++{ ++ SDIO_STATUS status; ++ DBG_PRINT(SDIODBG_FUNC_IRQ, ("SDIO Bus Driver: Hcd (0x%X) Irq Ack \n", ++ (INT)pReq->pCompleteContext)); ++ /* re-arm the HCD */ ++ status = _IssueConfig((PSDHCD)pReq->pCompleteContext,SDCONFIG_SDIO_REARM_INT,NULL,0); ++ ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: HCD Re-Arm failed : %d\n", ++ status)); ++ } ++ FreeRequest(pReq); ++} ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ SDFunctionAckInterrupt - handle device interrupt acknowledgement ++ Input: pDevice - the device ++ Output: ++ Return: ++ Notes: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++SDIO_STATUS SDFunctionAckInterrupt(PSDDEVICE pDevice) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ UCHAR mask; ++ PSDREQUEST pReq = NULL; ++ BOOL setHcd = FALSE; ++ SDIO_STATUS status2; ++ CT_DECLARE_IRQ_SYNC_CONTEXT(); ++ ++ pReq = AllocateRequest(); ++ if (NULL == pReq) { ++ return SDIO_STATUS_NO_RESOURCES; ++ } ++ ++ status = _AcquireHcdLock(pDevice->pHcd); ++ ++ if (!SDIO_SUCCESS(status)) { ++ FreeRequest(pReq); ++ return status; ++ } ++ ++ do { ++ if (!((SDDEVICE_GET_SDIO_FUNCNO(pDevice) >= SDIO_FIRST_FUNCTION_NUMBER) && ++ (SDDEVICE_GET_SDIO_FUNCNO(pDevice) <= SDIO_LAST_FUNCTION_NUMBER))){ ++ status = SDIO_STATUS_INVALID_PARAMETER; ++ DBG_ASSERT(FALSE); ++ break; ++ } ++ mask = 1 << SDDEVICE_GET_SDIO_FUNCNO(pDevice); ++ if (pDevice->pHcd->PendingIrqAcks & mask) { ++ /* clear the ack bit in question */ ++ pDevice->pHcd->PendingIrqAcks &= ~mask; ++ if (0 == pDevice->pHcd->PendingIrqAcks) { ++ pDevice->pHcd->IrqProcState = SDHCD_IDLE; ++ /* no pending acks, so re-arm if irqs are stilled enabled */ ++ if (pDevice->pHcd->IrqsEnabled) { ++ setHcd = TRUE; ++ /* issue pseudo request to sync this with bus requests */ ++ pReq->Status = SDIO_STATUS_SUCCESS; ++ pReq->pCompletion = HcdAckComplete; ++ pReq->pCompleteContext = pDevice->pHcd; ++ pReq->Flags = SD_PSEUDO_REQ_FLAGS; ++ } ++ } ++ } else { ++ DBG_PRINT(SDDBG_WARN, ("SDIO Bus Driver: AckInterrupt: no IRQ pending on Function :%d, \n", ++ SDDEVICE_GET_SDIO_FUNCNO(pDevice))); ++ } ++ } while (FALSE); ++ ++ status2 = ReleaseHcdLock(pDevice); ++ ++ if (pReq != NULL) { ++ if (SDIO_SUCCESS(status) && (setHcd)) { ++ /* issue request */ ++ IssueRequestToHCD(pDevice->pHcd,pReq); ++ } else { ++ FreeRequest(pReq); ++ } ++ } ++ ++ return status; ++} ++ ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ SDMaskUnmaskFunctionIRQ - mask/unmask function IRQ ++ Input: pDevice - the device/function ++ MaskInt - mask interrupt ++ Output: ++ Return: status ++ Notes: Note, this function can be called from an ISR or completion context ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++SDIO_STATUS SDMaskUnmaskFunctionIRQ(PSDDEVICE pDevice, BOOL MaskInt) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ UINT8 mask; ++ UINT8 controlVal; ++ BOOL setHcd; ++ PSDREQUEST pReq = NULL; ++ SDIO_STATUS status2; ++ ++ CT_DECLARE_IRQ_SYNC_CONTEXT(); ++ ++ setHcd = FALSE; ++ ++ pReq = AllocateRequest(); ++ if (NULL == pReq) { ++ return SDIO_STATUS_NO_RESOURCES; ++ } ++ ++ status = _AcquireHcdLock(pDevice->pHcd); ++ ++ if (!SDIO_SUCCESS(status)) { ++ FreeRequest(pReq); ++ return status; ++ } ++ ++ do { ++ ++ if (pDevice->pHcd->CardProperties.Flags & CARD_RAW) { ++ if (!MaskInt) { ++ if (!pDevice->pHcd->IrqsEnabled) { ++ pReq->pCompletion = RawHcdEnableIrqPseudoComplete; ++ setHcd = TRUE; ++ pDevice->pHcd->IrqsEnabled = 1 << 1; ++ } ++ } else { ++ if (pDevice->pHcd->IrqsEnabled) { ++ pReq->pCompletion = RawHcdDisableIrqPseudoComplete; ++ setHcd = TRUE; ++ pDevice->pHcd->IrqsEnabled = 0; ++ } ++ } ++ ++ if (setHcd) { ++ /* hcd IRQ control requests must be synched with outstanding ++ * bus requests so we issue a pseudo bus request */ ++ pReq->pCompleteContext = pDevice->pHcd; ++ pReq->Flags = SD_PSEUDO_REQ_FLAGS; ++ pReq->Status = SDIO_STATUS_SUCCESS; ++ } else { ++ /* no request to submit, just free it */ ++ FreeRequest(pReq); ++ pReq = NULL; ++ } ++ /* we're done, submit the bus request if any */ ++ break; ++ } ++ ++ if (!(pDevice->pHcd->CardProperties.Flags & CARD_SDIO)){ ++ /* nothing to do if it's not an SDIO card */ ++ DBG_ASSERT(FALSE); ++ status = SDIO_STATUS_INVALID_PARAMETER; ++ break; ++ } ++ ++ if (!((SDDEVICE_GET_SDIO_FUNCNO(pDevice) >= SDIO_FIRST_FUNCTION_NUMBER) && ++ (SDDEVICE_GET_SDIO_FUNCNO(pDevice) <= SDIO_LAST_FUNCTION_NUMBER))){ ++ status = SDIO_STATUS_INVALID_PARAMETER; ++ DBG_ASSERT(FALSE); ++ break; ++ } ++ ++ mask = 1 << SDDEVICE_GET_SDIO_FUNCNO(pDevice); ++ if (!MaskInt) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver Unmasking Int, Mask:0x%X\n", mask)); ++ /* check interrupts that were enabled on entry */ ++ if (0 == pDevice->pHcd->IrqsEnabled) { ++ /* need to turn on interrupts in HCD */ ++ setHcd = TRUE; ++ /* use this completion routine */ ++ pReq->pCompletion = HcdIrqEnableComplete; ++ } ++ /* set the enable bit, in the shadow register */ ++ pDevice->pHcd->IrqsEnabled |= mask; ++ /* make sure control value includes the master enable */ ++ controlVal = pDevice->pHcd->IrqsEnabled | SDIO_INT_MASTER_ENABLE; ++ } else { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver Masking Int, Mask:0x%X\n", mask)); ++ /* clear the bit */ ++ pDevice->pHcd->IrqsEnabled &= ~mask; ++ /* check and see if this clears all the bits */ ++ if (0 == pDevice->pHcd->IrqsEnabled){ ++ /* if none of the functions are enabled, clear this register */ ++ controlVal = 0; ++ /* disable in host */ ++ setHcd = TRUE; ++ /* use this completion routine */ ++ pReq->pCompletion = HcdIrqDisableComplete; ++ } else { ++ /* set control value making sure master enable is left on */ ++ controlVal = pDevice->pHcd->IrqsEnabled | SDIO_INT_MASTER_ENABLE; ++ } ++ } ++ ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver INT_ENABLE_REG value:0x%X\n", controlVal)); ++ /* setup bus request to update the mask register */ ++ SDIO_SET_CMD52_WRITE_ARG(pReq->Argument,0,SDIO_INT_ENABLE_REG,controlVal); ++ pReq->Command = CMD52; ++ pReq->Flags = SDREQ_FLAGS_TRANS_ASYNC | SDREQ_FLAGS_RESP_SDIO_R5; ++ ++ if (setHcd) { ++ /* make this a barrier request and set context*/ ++ pReq->Flags |= SDREQ_FLAGS_BARRIER; ++ pReq->pCompleteContext = pDevice->pHcd; ++ } else { ++ /* does not require an update to the HCD */ ++ pReq->pCompleteContext = (PVOID)(UINT32)controlVal; ++ pReq->pCompletion = WriteIntEnableComplete; ++ } ++ ++ } while (FALSE); ++ ++ status2 = _ReleaseHcdLock(pDevice->pHcd); ++ ++ if (pReq != NULL) { ++ if (SDIO_SUCCESS(status)) { ++ /* issue request */ ++ IssueRequestToHCD(pDevice->pHcd,pReq); ++ } else { ++ FreeRequest(pReq); ++ } ++ } ++ ++ return status; ++} ++ ++ ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ SDSPIModeEnableDisableCRC - Enable/Disable SPI Mode CRC checking ++ Input: pDevice - the device/function ++ Enable - Enable CRC ++ Output: ++ Return: status ++ Notes: Note, this function can be called from an ISR or completion context ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++SDIO_STATUS SDSPIModeEnableDisableCRC(PSDDEVICE pDevice,BOOL Enable) ++{ ++ SDCONFIG_BUS_MODE_DATA busMode; ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ UINT32 cmdARG = 0; ++ ++ if (!SDDEVICE_IS_BUSMODE_SPI(pDevice)) { ++ return SDIO_STATUS_INVALID_PARAMETER; ++ } ++ //??we should make these atomic using a barrier ++ ++ /* get the current mode and clock */ ++ busMode.BusModeFlags = pDevice->pHcd->CardProperties.BusMode; ++ busMode.ClockRate = pDevice->pHcd->CardProperties.OperBusClock; ++ ++ if (Enable) { ++ /* clear the no-CRC flag */ ++ busMode.BusModeFlags &= ~SDCONFIG_BUS_MODE_SPI_NO_CRC; ++ cmdARG = SD_CMD59_CRC_ON; ++ } else { ++ busMode.BusModeFlags |= SDCONFIG_BUS_MODE_SPI_NO_CRC; ++ cmdARG = SD_CMD59_CRC_OFF; ++ } ++ ++ do { ++ /* issue CMD59 to turn on/off CRC */ ++ status = _IssueSimpleBusRequest(pDevice->pHcd, ++ CMD59, ++ cmdARG, ++ SDREQ_FLAGS_RESP_R1, ++ NULL); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed issue CMD59 (arg=0x%X) Err:%d \n", ++ cmdARG, status)); ++ break; ++ } ++ if (Enable) { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: CRC Enabled in SPI mode \n")); ++ } else { ++ DBG_PRINT(SDDBG_TRACE, ("SDIO Bus Driver: CRC Disabled in SPI mode \n")); ++ } ++ status = SetOperationalBusMode(pDevice,&busMode); ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Failed to set SPI NO CRC mode in hcd : Err:%d \n", ++ status)); ++ break; ++ } ++ } while (FALSE); ++ ++ return status; ++} ++ ++ ++static UINT32 ConvertSPIStatusToSDCardStatus(UINT8 SpiR1, UINT8 SpiR2) ++{ ++ UINT32 cardStatus = 0; ++ ++ if (SpiR1 != 0) { ++ /* convert the error */ ++ if (SpiR1 & SPI_CS_ERASE_RESET) { ++ cardStatus |= SD_CS_ERASE_RESET; ++ } ++ if (SpiR1 & SPI_CS_ILLEGAL_CMD) { ++ cardStatus |= SD_CS_ILLEGAL_CMD_ERR; ++ } ++ if (SpiR1 & SPI_CS_CMD_CRC_ERR) { ++ cardStatus |= SD_CS_PREV_CMD_CRC_ERR; ++ } ++ if (SpiR1 & SPI_CS_ERASE_SEQ_ERR) { ++ cardStatus |= SD_CS_ERASE_SEQ_ERR; ++ } ++ if (SpiR1 & SPI_CS_ADDRESS_ERR) { ++ cardStatus |= SD_CS_ADDRESS_ERR; ++ } ++ if (SpiR1 & SPI_CS_PARAM_ERR) { ++ cardStatus |= SD_CS_CMD_OUT_OF_RANGE; ++ } ++ } ++ ++ if (SpiR2 != 0) { ++ /* convert the error */ ++ if (SpiR2 & SPI_CS_CARD_IS_LOCKED) { ++ cardStatus |= SD_CS_CARD_LOCKED; ++ } ++ if (SpiR2 & SPI_CS_LOCK_UNLOCK_FAILED) { ++ /* this bit is shared, just set both */ ++ cardStatus |= (SD_CS_LK_UNLK_FAILED | SD_CS_WP_ERASE_SKIP); ++ } ++ if (SpiR2 & SPI_CS_ERROR) { ++ cardStatus |= SD_CS_GENERAL_ERR; ++ } ++ if (SpiR2 & SPI_CS_INTERNAL_ERROR) { ++ cardStatus |= SD_CS_CARD_INTERNAL_ERR; ++ } ++ if (SpiR2 & SPI_CS_ECC_FAILED) { ++ cardStatus |= SD_CS_ECC_FAILED; ++ } ++ if (SpiR2 & SPI_CS_WP_VIOLATION) { ++ cardStatus |= SD_CS_WP_ERR; ++ } ++ if (SpiR2 & SPI_CS_ERASE_PARAM_ERR) { ++ cardStatus |= SD_CS_ERASE_PARAM_ERR; ++ } ++ if (SpiR2 & SPI_CS_OUT_OF_RANGE) { ++ cardStatus |= SD_CS_CMD_OUT_OF_RANGE; ++ } ++ } ++ ++ return cardStatus; ++} ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ ConvertSPI_Response - filter the SPI response and convert it to an SD Response ++ Input: pReq - request ++ Output: pReq - modified response, if pRespBuffer is not NULL ++ pRespBuffer - converted response (optional) ++ Return: ++ Notes: This function converts a SPI response into an SD response. A caller ++ can supply a buffer instead. ++ For SPI bus operation the HCD must send the SPI response as ++ a stream of bytes, the highest byte contains the first received byte from the ++ card. This function only filters simple responses (R1 primarily). ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++void ConvertSPI_Response(PSDREQUEST pReq, UINT8 *pRespBuffer) ++{ ++ ++ UINT32 cardStatus; ++ ++ if (pReq->Flags & SDREQ_FLAGS_RESP_SPI_CONVERTED) { ++ /* already converted */ ++ return; ++ } ++ if (NULL == pRespBuffer) { ++ pRespBuffer = pReq->Response; ++ } ++ ++ switch (GET_SDREQ_RESP_TYPE(pReq->Flags)) { ++ case SDREQ_FLAGS_RESP_R1: ++ case SDREQ_FLAGS_RESP_R1B: ++ cardStatus = ConvertSPIStatusToSDCardStatus(GET_SPI_R1_RESP_TOKEN(pReq->Response), ++ 0); ++ if (CMD55 == pReq->Command) { ++ /* we emulate this since SPI does not have such a bit */ ++ cardStatus |= SD_CS_APP_CMD; ++ } ++ /* stuff the SD card status */ ++ SD_R1_SET_CMD_STATUS(pRespBuffer,cardStatus); ++ /* stuff the command */ ++ SD_R1_SET_CMD(pRespBuffer,pReq->Command); ++ pReq->Flags |= SDREQ_FLAGS_RESP_SPI_CONVERTED; ++ break; ++ case SDREQ_FLAGS_RESP_SDIO_R5: ++ { ++ UINT8 respFlags; ++ UINT8 readData; ++ ++ readData = GET_SPI_SDIO_R5_RESPONSE_RDATA(pReq->Response); ++ respFlags = GET_SPI_SDIO_R5_RESP_TOKEN(pReq->Response); ++ ++ pRespBuffer[SD_R5_RESP_FLAGS_OFFSET] = 0; ++ if (respFlags != 0) { ++ if (respFlags & SPI_R5_ILLEGAL_CMD) { ++ pRespBuffer[SD_R5_RESP_FLAGS_OFFSET] |= SD_R5_ILLEGAL_CMD; ++ } ++ if (respFlags & SPI_R5_CMD_CRC) { ++ pRespBuffer[SD_R5_RESP_FLAGS_OFFSET] |= SD_R5_RESP_CMD_ERR; ++ } ++ if (respFlags & SPI_R5_FUNC_ERR) { ++ pRespBuffer[SD_R5_RESP_FLAGS_OFFSET] |= SD_R5_INVALID_FUNC; ++ } ++ if (respFlags & SPI_R5_PARAM_ERR) { ++ pRespBuffer[SD_R5_RESP_FLAGS_OFFSET] |= SD_R5_ARG_RANGE_ERR; ++ } ++ } ++ /* stuff read data */ ++ pRespBuffer[SD_SDIO_R5_READ_DATA_OFFSET] = readData; ++ /* stuff the command */ ++ SD_R5_SET_CMD(pRespBuffer,pReq->Command); ++ } ++ pReq->Flags |= SDREQ_FLAGS_RESP_SPI_CONVERTED; ++ break; ++ case SDREQ_FLAGS_RESP_R2: ++ /* for CMD13 and ACMD13 , SPI uses it's own R2 response format (2 bytes) */ ++ /* the issue of CMD13 needs to change the response flag to R2 */ ++ if (CMD13 == pReq->Command) { ++ cardStatus = ConvertSPIStatusToSDCardStatus( ++ GET_SPI_R2_RESP_TOKEN(pReq->Response), ++ GET_SPI_R2_STATUS_TOKEN(pReq->Response)); ++ /* stuff the SD card status */ ++ SD_R1_SET_CMD_STATUS(pRespBuffer,cardStatus); ++ /* stuff the command */ ++ SD_R1_SET_CMD(pRespBuffer,pReq->Command); ++ pReq->Flags |= SDREQ_FLAGS_RESP_SPI_CONVERTED; ++ break; ++ } ++ /* no other commands should be using R2 when using SPI, if they are ++ * they should be bypassing the filter */ ++ DBG_ASSERT(FALSE); ++ break; ++ default: ++ /* for all others: ++ * ++ * SDREQ_FLAGS_RESP_R6 - SPI mode does not use RCA ++ * SDREQ_FLAGS_RESP_R3 - bus driver handles this internally ++ * SDREQ_FLAGS_RESP_SDIO_R4 - bus driver handles this internally ++ * ++ */ ++ DBG_PRINT(SDDBG_ERROR, ("ConvertSPI_Response - invalid response type:0x%2.2X", ++ GET_SDREQ_RESP_TYPE(pReq->Flags))); ++ DBG_ASSERT(FALSE); ++ break; ++ } ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Check an SD/MMC/SDIO response. ++ ++ @function name: SDIO_CheckResponse ++ @prototype: SDIO_STATUS SDIO_CheckResponse(PSDHCD pHcd, PSDREQUEST pReq, SDHCD_RESPONSE_CHECK_MODE CheckMode) ++ @category: HD_Reference ++ ++ @input: pHcd - the host controller definition structure. ++ @input: pReq - request containing the response ++ @input: CheckMode - mode ++ ++ @return: SDIO_STATUS ++ ++ @notes: Host controller drivers must call into this function to validate various command ++ responses before continuing with data transfers or for decoding received SPI tokens. ++ The CheckMode option determines the type of validation to perform. ++ if (CheckMode == SDHCD_CHECK_DATA_TRANS_OK) : ++ The host controller must check the card response to determine whether it ++ is safe to perform a data transfer. This API only checks commands that ++ involve data transfers and checks various status fields in the command response. ++ If the card cannot accept data, this function will return a non-successful status that ++ should be treated as a request failure. The host driver should complete the request with the ++ returned status. Host controller should only call this function in preparation for a ++ data transfer. ++ if (CheckMode == SDHCD_CHECK_SPI_TOKEN) : ++ This API checks the SPI token and returns a timeout status if the illegal command bit is ++ set. This simulates the behavior of SD 1/4 bit operation where illegal commands result in ++ a command timeout. A driver that supports SPI mode should pass every response to this ++ function to determine the appropriate error status to complete the request with. If the ++ API returns success, the response indicates that the card accepted the command. ++ ++ @example: Checking the response before starting the data transfer : ++ if (SDIO_SUCCESS(status) && (pReq->Flags & SDREQ_FLAGS_DATA_TRANS)) { ++ // check the response to see if we should continue with data ++ status = SDIO_CheckResponse(pHcd, pReq, SDHCD_CHECK_DATA_TRANS_OK); ++ if (SDIO_SUCCESS(status)) { ++ .... start data transfer phase ++ } else { ++ ... card response indicates that the card cannot handle data ++ // set completion status ++ pRequest->Status = status; ++ } ++ } ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ _SDIO_CheckResponse - check response on behalf of the host controller ++ Input: pHcd - host controller ++ pReq - request containing the response ++ CheckMode - mode ++ Output: ++ Return: status ++ Notes: ++ ++ CheckMode == SDHCD_CHECK_DATA_TRANS_OK : ++ The host controller requests a check on the response to determine whether it ++ is okay to perform a data transfer. This function only filters on commands that ++ involve data. Host controller should only call this function in preparation for a ++ data transfer. ++ ++ CheckMode == SDHCD_CHECK_SPI_TOKEN : ++ The bus driver checks the SPI token and returns a timeout status if the illegal command bit is ++ set. This simulates the behavior of SD native operation. ++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++SDIO_STATUS _SDIO_CheckResponse(PSDHCD pHcd, PSDREQUEST pReq, SDHCD_RESPONSE_CHECK_MODE CheckMode) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ ++ if (CheckMode == SDHCD_CHECK_DATA_TRANS_OK) { ++ UINT32 cardStatus; ++ UINT8 *pResponse; ++ UINT8 convertedResponse[MAX_CARD_RESPONSE_BYTES]; ++ ++ if (!(pReq->Flags & SDREQ_FLAGS_DATA_TRANS) || ++ (pReq->Flags & SDREQ_FLAGS_DATA_SKIP_RESP_CHK) || ++ (GET_SDREQ_RESP_TYPE(pReq->Flags) == SDREQ_FLAGS_NO_RESP)) { ++ return SDIO_STATUS_SUCCESS; ++ } ++ pResponse = pReq->Response; ++ /* check SPI mode */ ++ if (IS_HCD_BUS_MODE_SPI(pHcd)) { ++ if (!(pReq->Flags & SDREQ_FLAGS_RESP_SKIP_SPI_FILT)) { ++ /* apply conversion */ ++ ConvertSPI_Response(pReq, NULL); ++ } else { ++ /* temporarily convert the response, without altering the original */ ++ ConvertSPI_Response(pReq, convertedResponse); ++ /* point to the converted one */ ++ pResponse = convertedResponse; ++ } ++ } ++ ++ switch (GET_SDREQ_RESP_TYPE(pReq->Flags)) { ++ case SDREQ_FLAGS_RESP_R1: ++ case SDREQ_FLAGS_RESP_R1B: ++ cardStatus = SD_R1_GET_CARD_STATUS(pResponse); ++ if (!(cardStatus & ++ (SD_CS_ILLEGAL_CMD_ERR | SD_CS_CARD_INTERNAL_ERR | SD_CS_GENERAL_ERR))) { ++ /* okay for data */ ++ break; ++ } ++ /* figure out what it was */ ++ if (cardStatus & SD_CS_ILLEGAL_CMD_ERR) { ++ status = SDIO_STATUS_DATA_STATE_INVALID; ++ } else { ++ status = SDIO_STATUS_DATA_ERROR_UNKNOWN; ++ } ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Check Response Error. R1 CardStatus:0x%X \n", ++ cardStatus)); ++ break; ++ case SDREQ_FLAGS_RESP_SDIO_R5: ++ cardStatus = SD_R5_GET_RESP_FLAGS(pResponse); ++ if (!(cardStatus & SD_R5_CURRENT_CMD_ERRORS)){ ++ /* all okay */ ++ break; ++ } ++ ++ status = ConvertCMD52ResponseToSDIOStatus((UINT8)cardStatus); ++ DBG_PRINT(SDDBG_ERROR, ("SDIO Bus Driver: Check Response Error. R5 CardStatus:0x%X \n", ++ cardStatus)); ++ break; ++ default: ++ break; ++ } ++ ++ return status; ++ } ++ ++ { ++ UINT8 spiToken; ++ ++ /* handle SPI token validation */ ++ switch (GET_SDREQ_RESP_TYPE(pReq->Flags)) { ++ case SDREQ_FLAGS_RESP_R2: ++ spiToken = GET_SPI_R2_RESP_TOKEN(pReq->Response); ++ break; ++ case SDREQ_FLAGS_RESP_SDIO_R5: ++ spiToken = GET_SPI_SDIO_R5_RESP_TOKEN(pReq->Response); ++ break; ++ case SDREQ_FLAGS_RESP_R3: ++ spiToken = GET_SPI_R3_RESP_TOKEN(pReq->Response); ++ break; ++ case SDREQ_FLAGS_RESP_SDIO_R4: ++ spiToken = GET_SPI_SDIO_R4_RESP_TOKEN(pReq->Response); ++ break; ++ default: ++ /* all other tokesn are SPI R1 type */ ++ spiToken = GET_SPI_R1_RESP_TOKEN(pReq->Response); ++ break; ++ } ++ ++ if ((GET_SDREQ_RESP_TYPE(pReq->Flags) == SDREQ_FLAGS_RESP_SDIO_R5) || ++ (GET_SDREQ_RESP_TYPE(pReq->Flags) == SDREQ_FLAGS_RESP_SDIO_R4)) { ++ /* handle SDIO status tokens */ ++ if ((spiToken & SPI_R5_ILLEGAL_CMD) || ++ (spiToken & SPI_R5_CMD_CRC)) { ++ status = SDIO_STATUS_BUS_RESP_TIMEOUT; ++ } ++ } else { ++ /* handle all other status tokens */ ++ if ((spiToken & SPI_CS_ILLEGAL_CMD) || ++ (spiToken & SPI_CS_CMD_CRC_ERR)) { ++ status = SDIO_STATUS_BUS_RESP_TIMEOUT; ++ } ++ } ++ } ++ ++ return status; ++} ++ +diff --git a/drivers/sdio/stack/busdriver/sdio_bus_os.c b/drivers/sdio/stack/busdriver/sdio_bus_os.c +new file mode 100644 +index 0000000..dbdb955 +--- /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 0000000..78b8e17 +--- /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 0000000..44fa038 +--- /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 0000000..28762b0 +--- /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 0000000..4bc5a83 +--- /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 0000000..55363d0 +--- /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 0000000..14b3612 +--- /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 0000000..d5520fc +--- /dev/null ++++ b/drivers/sdio/stack/platform/sdioplatformdriver.c +@@ -0,0 +1,300 @@ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++@file: sdioplatformdriver.c ++ ++@abstract: Linux implementation module for SDIO pltaform driver ++ ++#notes: ++ ++@notice: Copyright (c), 2006 Atheros Communications, Inc. ++ ++@license: This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License version 2 as ++ published by the Free Software Foundation. ++ ++ ++ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation; ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Portions of this code were developed with information supplied from the ++ * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: ++ * ++ * The following conditions apply to the release of the SD simplified specification (�Simplified ++ * Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete ++ * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided ++ * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified ++ * Specification may require a license from the SD Card Association or other third parties. ++ * Disclaimers: ++ * The information contained in the Simplified Specification is presented only as a standard ++ * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any ++ * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for ++ * any damages, any infringements of patents or other right of the SD Card Association or any third ++ * parties, which may result from its use. No license is granted by implication, estoppel or otherwise ++ * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall ++ * be construed as an obligation by the SD Card Association to disclose or distribute any technical ++ * information, know-how or other confidential information to any third party. ++ * ++ * ++ * The initial developers of the original code are Seung Yi and Paul Lever ++ * ++ * sdio@atheros.com ++ * ++ * ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++ ++#define DESCRIPTION "SDIO Platform Driver" ++#define AUTHOR "Atheros Communications, Inc." ++ ++//??for .h ++ ++struct sdioplatform_peripheral { ++ struct list_head node; ++ struct sdioplatform_controller *controller; ++ struct device dev; ++}; ++struct sdioplatform_driver { ++ struct device_driver drv; ++ int (*probe)(struct sdioplatform_peripheral *); ++ void (*remove)(struct sdioplatform_peripheral *); ++ int (*suspend)(struct sdioplatform_peripheral *, pm_message_t); ++ int (*resume)(struct sdioplatform_peripheral *); ++}; ++ ++ ++struct sdioplatform_controller { ++ struct device *dev; ++}; ++struct sdioplatform_controller_driver { ++ struct device_driver drv; ++ int (*probe)(struct sdioplatform_controller *); ++ void (*remove)(struct sdioplatform_controller *); ++ int (*suspend)(struct sdioplatform_controller *, pm_message_t); ++ int (*resume)(struct sdioplatform_controller *); ++}; ++ ++ ++ ++#define device_to_sdioplatform_peripheral(d) container_of(d, struct sdioplatform_peripheral, dev) ++#define driver_to_sdioplatform_driver(d) container_of(d, struct sdioplatform_driver, drv) ++ ++#define device_to_sdioplatform_controller(d) container_of(d, struct sdioplatform_controller, dev) ++#define driver_to_sdioplatform_controller_driver(d) container_of(d, struct sdioplatform_controller_driver, drv) ++ ++#define SDIOPLATFORM_ATTR(name, fmt, args...) \ ++static ssize_t sdio_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct sdioplatform_peripheral *peripheral = device_to_sdioplatform_peripheral(dev); \ ++ return sprintf(buf, fmt, args); \ ++} ++ ++SDIOPLATFORM_ATTR(bus_id, "%s\n", bus_id); ++#define SDIOPLATFORM_ATTR_RO(name) __ATTR(name, S_IRUGO, sdioplatform_##name##_show, NULL) ++ ++static struct device_attribute sdioplatform_dev_attrs[] = { ++ SDIOPLATFORM_ATTR_RO(bus_id), ++ __ATTR_NULL ++}; ++ ++static struct bus_type sdioplatform_bus_type = { ++ .name = "sdioplatform", ++ .dev_attrs = sdioplatform_dev_attrs, ++ .match = sdioplatform_bus_match, ++ .hotplug = NULL, ++ .suspend = sdioplatform_bus_suspend, ++ .resume = sdioplatform_bus_resume, ++}; ++ ++ ++/* controller functions */ ++static int sdioplatform_controllerdrv_probe(struct device *dev) ++{ ++ struct sdioplatform_controller_driver *drv = driver_to_sdioplatform_controller_driver(dev->driver); ++ struct sdioplatform_controller *controller = device_to_sdioplatform_controller(dev); ++ ++ return drv->probe(controller); ++} ++ ++static int sdioplatform_controllerdrv_remove(struct device *dev) ++{ ++ struct sdioplatform_controller_driver *drv = driver_to_sdioplatform_controller_driver(dev->driver); ++ struct sdioplatform_controller *controller = device_to_sdioplatform_controller(dev); ++ ++ return drv->remove(controller); ++} ++ ++/* ++ * sdioplatform_register_controller_driver - register a controller driver ++ */ ++int sdioplatform_register_controller_driver(struct sdioplatform_controller_driver *drv) ++{ ++ drv->drv.bus = &sdioplatform_bus_type; ++ drv->drv.probe = sdioplatform_controllerdrv_probe; ++ drv->drv.remove = sdioplatform_controllerdrv_remove; ++ return driver_register(&drv->drv); ++} ++ ++/* ++ * sdioplatform_unregister_controller_driver - unregister a controller driver ++ */ ++void sdioplatform_unregister_controller_driver(struct sdioplatform_driver *drv) ++{ ++ driver_unregister(&drv->drv); ++} ++ ++/* ++ * sdioplatform_add_controller - register a controller device ++ */ ++int sdioplatform_add_controller(char *name, struct sdioplatform_controller *dev) ++{ ++ if (!dev) { ++ return -EINVAL; ++ } ++ strncpy(dev->dev.bus_id, BUS_ID_SIZE, name); ++ return device_register(&dev->dev); ++} ++ ++/* ++ * sdioplatform_remove_controller - unregister a controller device ++ */ ++int sdioplatform_remove_controller(char *name, struct sdioplatform_controller *dev) ++{ ++ if (!dev) { ++ return -EINVAL; ++ } ++ return device_unregister(&dev->dev); ++} ++ ++/* peripheral functions */ ++static int sdioplatform_drv_probe(struct device *dev) ++{ ++ struct sdioplatform_driver *drv = driver_to_sdioplatform_driver(dev->driver); ++ struct sdioplatform_peripheral *peripheral = device_to_sdioplatform_peripheral(dev); ++ ++ return drv->probe(peripheral); ++} ++ ++static int sdioplatform_controllerdrv_remove(struct device *dev) ++{ ++ struct sdioplatform_controller_driver *drv = driver_to_sdioplatform_controller_driver(dev->driver); ++ struct sdioplatform_controller *controller = device_to_sdioplatform_controller(dev); ++ ++ return drv->remove(controller); ++} ++ ++/* ++ * sdioplatform_register_driver - register a driver ++ */ ++int sdioplatform_register_driver(struct sdioplatform_driver *drv) ++{ ++ drv->drv.bus = &sdioplatform_bus_type; ++ drv->drv.probe = sdioplatform_drv_probe; ++ drv->drv.remove = sdioplatform_drv_remove; ++ return driver_register(&drv->drv); ++} ++ ++/* ++ * sdioplatform_unregister_driver - unregister a driver ++ */ ++void sdioplatform_unregister_driver(struct sdioplatform_driver *drv) ++{ ++ driver_unregister(&drv->drv); ++} ++ ++/* ++ * sdioplatform_add_peripheral - register a peripheral device ++ */ ++int sdioplatform_add_peripheral(char *name, struct sdioplatform_peripheral *dev) ++{ ++ if (!dev) { ++ return -EINVAL; ++ } ++ strncpy(dev->dev.bus_id, BUS_ID_SIZE, name); ++ return device_register(&dev->dev); ++} ++ ++/* ++ * sdioplatform_remove_peripheral - unregister a peripheral device ++ */ ++int sdioplatform_remove_peripheral(char *name, struct sdioplatform_peripheral *dev) ++{ ++ if (!dev) { ++ return -EINVAL; ++ } ++ return device_unregister(&dev->dev); ++} ++ ++ ++ ++ ++ ++static int sdioplatform_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ /* probes handle the matching */ ++ return 1; ++} ++ ++static int sdioplatform_bus_suspend(struct device *dev, pm_message_t state) ++{ ++ struct sdioplatform_driver *drv = driver_to_sdioplatform_driver(dev->driver); ++ struct sdioplatform_peripheral *peripheral = device_to_sdioplatform_peripheral(dev); ++ int ret = 0; ++ ++ if (peripheral->driver && drv->suspend) { ++ ret = drv->suspend(peripheral, state); ++ } ++ return ret; ++} ++ ++static int sdioplatform_bus_resume(struct device *dev) ++{ ++ struct sdioplatform_driver *drv = driver_to_sdioplatform_driver(dev->driver); ++ struct sdioplatform_peripheral *peripheral = device_to_sdioplatform_peripheral(dev); ++ int ret = 0; ++ ++ if (peripheral->driver && drv->resume) { ++ ret = drv->resume(card); ++ } ++ return ret; ++} ++ ++/* ++ * module init ++*/ ++static int __init sdio_platformdriver_init(void) { ++ int ret = bus_register(&sdioplatform_bus_type); ++ return ret; ++} ++ ++/* ++ * module cleanup ++*/ ++static void __exit sdio_platformdriver_cleanup(void) { ++ REL_PRINT(SDDBG_TRACE, ("SDIO unloaded\n")); ++ _SDIO_BusDriverCleanup(); ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION(DESCRIPTION); ++MODULE_AUTHOR(AUTHOR); ++ ++module_init(sdio_platformdriver_init); ++module_exit(sdio_platformdriver_cleanup); ++EXPORT_SYMBOL(sdioplatform_register_controller_driver); ++EXPORT_SYMBOL(sdioplatform_unregister_controller_driver); ++EXPORT_SYMBOL(sdioplatform_add_controller); ++EXPORT_SYMBOL(sdioplatform_remove_controller); ++EXPORT_SYMBOL(sdioplatform_register_driver); ++EXPORT_SYMBOL(sdioplatform_unregister_driver); ++EXPORT_SYMBOL(sdioplatform_add_peripheral); ++EXPORT_SYMBOL(sdioplatform_remove_peripheral); ++ ++ ++ +diff --git a/include/linux/sdio/_sdio_defs.h b/include/linux/sdio/_sdio_defs.h +new file mode 100644 +index 0000000..a3f5542 +--- /dev/null ++++ b/include/linux/sdio/_sdio_defs.h +@@ -0,0 +1,638 @@ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++@file: _sdio_defs.h ++ ++@abstract: SD/SDIO definitions ++ ++@notice: Copyright (c), 2004-2006 Atheros Communications, Inc. ++ ++ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation; ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Portions of this code were developed with information supplied from the ++ * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: ++ * ++ * The following conditions apply to the release of the SD simplified specification (�Simplified ++ * Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete ++ * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided ++ * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified ++ * Specification may require a license from the SD Card Association or other third parties. ++ * Disclaimers: ++ * The information contained in the Simplified Specification is presented only as a standard ++ * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any ++ * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for ++ * any damages, any infringements of patents or other right of the SD Card Association or any third ++ * parties, which may result from its use. No license is granted by implication, estoppel or otherwise ++ * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall ++ * be construed as an obligation by the SD Card Association to disclose or distribute any technical ++ * information, know-how or other confidential information to any third party. ++ * ++ * ++ * The initial developers of the original code are Seung Yi and Paul Lever ++ * ++ * sdio@atheros.com ++ * ++ * ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#ifndef ___SDIO_DEFS_H___ ++#define ___SDIO_DEFS_H___ ++ ++#define SD_INIT_BUS_CLOCK 100000 /* initialization clock in hz */ ++#define SPI_INIT_BUS_CLOCK 100000 /* initialization clock in hz */ ++#define SD_MAX_BUS_CLOCK 25000000 /* max clock speed in hz */ ++#define SD_HS_MAX_BUS_CLOCK 50000000 /* SD high speed max clock speed in hz */ ++#define SDIO_LOW_SPEED_MAX_BUS_CLOCK 400000 /* max low speed clock in hz */ ++#define SDMMC_MIN_INIT_CLOCKS 80 /* minimun number of initialization clocks */ ++#define SDIO_EMPC_CURRENT_THRESHOLD 300 /* SDIO 1.10 , EMPC (mA) threshold, we add some overhead */ ++ ++/* commands */ ++#define CMD0 0 ++#define CMD1 1 ++#define CMD2 2 ++#define CMD3 3 ++#define CMD4 4 ++#define CMD5 5 ++#define CMD6 6 ++#define CMD7 7 ++#define CMD9 9 ++#define CMD10 10 ++#define CMD12 12 ++#define CMD13 13 ++#define CMD15 15 ++#define CMD16 16 ++#define CMD17 17 ++#define CMD18 18 ++#define CMD24 24 ++#define CMD25 25 ++#define CMD27 27 ++#define CMD28 28 ++#define CMD29 29 ++#define CMD30 30 ++#define CMD32 32 ++#define CMD33 33 ++#define CMD38 38 ++#define CMD42 42 ++#define CMD52 52 ++#define CMD53 53 ++#define CMD55 55 ++#define CMD56 56 ++#define CMD58 58 ++#define CMD59 59 ++#define ACMD6 6 ++#define ACMD13 13 ++#define ACMD22 22 ++#define ACMD23 23 ++#define ACMD41 41 ++#define ACMD42 42 ++#define ACMD51 51 ++ ++#define SD_ACMD6_BUS_WIDTH_1_BIT 0x00 ++#define SD_ACMD6_BUS_WIDTH_4_BIT 0x02 ++ ++#define SD_CMD59_CRC_OFF 0x00000000 ++#define SD_CMD59_CRC_ON 0x00000001 ++ ++/* SD/SPI max response size */ ++#define SD_MAX_CMD_RESPONSE_BYTES SD_R2_RESPONSE_BYTES ++ ++#define SD_R1_RESPONSE_BYTES 6 ++#define SD_R1B_RESPONSE_BYTES SD_R1_RESPONSE_BYTES ++#define SD_R1_GET_CMD(pR) ((pR)[5] & 0xC0)) ++#define SD_R1_SET_CMD(pR,cmd) (pR)[5] = (cmd) & 0xC0 ++#define SD_R1_GET_CARD_STATUS(pR) (((UINT32)((pR)[1])) | \ ++ (((UINT32)((pR)[2])) << 8) | \ ++ (((UINT32)((pR)[3])) << 16) | \ ++ (((UINT32)((pR)[4])) << 24) ) ++#define SD_R1_SET_CMD_STATUS(pR,status) \ ++{ \ ++ (pR)[1] = (UINT8)(status); \ ++ (pR)[2] = (UINT8)((status) >> 8); \ ++ (pR)[3] = (UINT8)((status) >> 16); \ ++ (pR)[4] = (UINT8)((status) >> 24); \ ++} ++ ++/* SD R1 card status bit masks */ ++#define SD_CS_CMD_OUT_OF_RANGE ((UINT32)(1 << 31)) ++#define SD_CS_ADDRESS_ERR (1 << 30) ++#define SD_CS_BLK_LEN_ERR (1 << 29) ++#define SD_CS_ERASE_SEQ_ERR (1 << 28) ++#define SD_CS_ERASE_PARAM_ERR (1 << 27) ++#define SD_CS_WP_ERR (1 << 26) ++#define SD_CS_CARD_LOCKED (1 << 25) ++#define SD_CS_LK_UNLK_FAILED (1 << 24) ++#define SD_CS_PREV_CMD_CRC_ERR (1 << 23) ++#define SD_CS_ILLEGAL_CMD_ERR (1 << 22) ++#define SD_CS_ECC_FAILED (1 << 21) ++#define SD_CS_CARD_INTERNAL_ERR (1 << 20) ++#define SD_CS_GENERAL_ERR (1 << 19) ++#define SD_CS_CSD_OVERWR_ERR (1 << 16) ++#define SD_CS_WP_ERASE_SKIP (1 << 15) ++#define SD_CS_ECC_DISABLED (1 << 14) ++#define SD_CS_ERASE_RESET (1 << 13) ++#define SD_CS_GET_STATE(status) (((status) >> 9) & 0x0f) ++#define SD_CS_SET_STATE(status, state) \ ++{ \ ++ (status) &= ~(0x0F << 9); \ ++ (status) |= (state) << 9 \ ++} ++ ++#define SD_CS_TRANSFER_ERRORS \ ++ ( SD_CS_ADDRESS_ERR | \ ++ SD_CS_BLK_LEN_ERR | \ ++ SD_CS_ERASE_SEQ_ERR | \ ++ SD_CS_ERASE_PARAM_ERR | \ ++ SD_CS_WP_ERR | \ ++ SD_CS_ECC_FAILED | \ ++ SD_CS_CARD_INTERNAL_ERR | \ ++ SD_CS_GENERAL_ERR ) ++ ++#define SD_CS_STATE_IDLE 0 ++#define SD_CS_STATE_READY 1 ++#define SD_CS_STATE_IDENT 2 ++#define SD_CS_STATE_STBY 3 ++#define SD_CS_STATE_TRANS 4 ++#define SD_CS_STATE_DATA 5 ++#define SD_CS_STATE_RCV 6 ++#define SD_CS_STATE_PRG 7 ++#define SD_CS_STATE_DIS 8 ++#define SD_CS_READY_FOR_DATA (1 << 8) ++#define SD_CS_APP_CMD (1 << 5) ++#define SD_CS_AKE_SEQ_ERR (1 << 3) ++ ++/* SD R2 response */ ++#define SD_R2_RESPONSE_BYTES 17 ++#define MAX_CSD_CID_BYTES 16 ++#define SD_R2_SET_STUFF_BITS(pR) (pR)[16] = 0x3F ++#define GET_SD_CSD_TRANS_SPEED(pR) (pR)[12] ++#define GET_SD_CID_MANFID(pR) (pR)[15] ++#define GET_SD_CID_PN_1(pR) (pR)[12] ++#define GET_SD_CID_PN_2(pR) (pR)[11] ++#define GET_SD_CID_PN_3(pR) (pR)[10] ++#define GET_SD_CID_PN_4(pR) (pR)[9] ++#define GET_SD_CID_PN_5(pR) (pR)[8] ++#define GET_SD_CID_PN_6(pR) (pR)[7] ++ ++#define GET_SD_CID_OEMID(pR) ((((UINT16)(pR)[14]) << 8 )| (UINT16)((pR)[13])) ++#define SDMMC_OCR_VOLTAGE_MASK 0x7FFFFFFF ++/* SD R3 response */ ++#define SD_R3_RESPONSE_BYTES 6 ++#define SD_R3_GET_OCR(pR) ((((UINT32)((pR)[1])) | \ ++ (((UINT32)((pR)[2])) << 8) | \ ++ (((UINT32)((pR)[3])) << 16) | \ ++ (((UINT32)((pR)[4])) << 24)) & SDMMC_OCR_VOLTAGE_MASK) ++#define SD_R3_IS_CARD_READY(pR) (((pR)[4] & 0x80) == 0x80) ++ ++/* OCR bit definitions */ ++#define SD_OCR_CARD_PWR_UP_STATUS ((UINT32)(1 << 31)) ++#define SD_OCR_3_5_TO_3_6_VDD (1 << 23) ++#define SD_OCR_3_4_TO_3_5_VDD (1 << 22) ++#define SD_OCR_3_3_TO_3_4_VDD (1 << 21) ++#define SD_OCR_3_2_TO_3_3_VDD (1 << 20) ++#define SD_OCR_3_1_TO_3_2_VDD (1 << 19) ++#define SD_OCR_3_0_TO_3_1_VDD (1 << 18) ++#define SD_OCR_2_9_TO_3_0_VDD (1 << 17) ++#define SD_OCR_2_8_TO_2_9_VDD (1 << 16) ++#define SD_OCR_2_7_TO_2_8_VDD (1 << 15) ++#define SD_OCR_2_6_TO_2_7_VDD (1 << 14) ++#define SD_OCR_2_5_TO_2_6_VDD (1 << 13) ++#define SD_OCR_2_4_TO_2_5_VDD (1 << 12) ++#define SD_OCR_2_3_TO_2_4_VDD (1 << 11) ++#define SD_OCR_2_2_TO_2_3_VDD (1 << 10) ++#define SD_OCR_2_1_TO_2_2_VDD (1 << 9) ++#define SD_OCR_2_0_TO_2_1_VDD (1 << 8) ++#define SD_OCR_1_9_TO_2_0_VDD (1 << 7) ++#define SD_OCR_1_8_TO_1_9_VDD (1 << 6) ++#define SD_OCR_1_7_TO_1_8_VDD (1 << 5) ++#define SD_OCR_1_6_TO_1_7_VDD (1 << 4) ++ ++/* SD Status data block */ ++#define SD_STATUS_DATA_BYTES 64 ++#define SDS_GET_DATA_WIDTH(buffer) ((buffer)[0] & 0xC0) ++#define SDS_BUS_1_BIT 0x00 ++#define SDS_BUS_4_BIT 0x80 ++#define SDS_GET_SECURE_MODE(buffer) ((buffer)[0] & 0x20) ++#define SDS_CARD_SECURE_MODE 0x20 ++#define SDS_GET_CARD_TYPE(buffer) ((buffer)[60] & 0x0F) ++#define SDS_SD_CARD_RW 0x00 ++#define SDS_SD_CARD_ROM 0x01 ++ ++/* SD R6 response */ ++#define SD_R6_RESPONSE_BYTES 6 ++#define SD_R6_GET_RCA(pR) ((UINT16)((pR)[3]) | (((UINT16)((pR)[4])) << 8)) ++#define SD_R6_GET_CS(pR) ((UINT16)((pR)[1]) | (((UINT16)((pR)[2])) << 8)) ++ ++/* SD Configuration Register (SCR) */ ++#define SD_SCR_BYTES 8 ++#define SCR_REV_1_0 0x00 ++#define SCR_SD_SPEC_1_00 0x00 ++#define SCR_SD_SPEC_1_10 0x01 ++#define SCR_BUS_SUPPORTS_1_BIT 0x01 ++#define SCR_BUS_SUPPORTS_4_BIT 0x04 ++#define SCR_SD_SECURITY_MASK 0x70 ++#define SCR_SD_NO_SECURITY 0x00 ++#define SCR_SD_SECURITY_1_0 0x10 ++#define SCR_SD_SECURITY_2_0 0x20 ++#define SCR_DATA_STATUS_1_AFTER_ERASE 0x80 ++ ++#define GET_SD_SCR_STRUCT_VER(pB) ((pB)[7] >> 4) ++#define GET_SD_SCR_SDSPEC_VER(pB) ((pB)[7] & 0x0F) ++#define GET_SD_SCR_BUSWIDTHS(pB) ((pB)[6] & 0x0F) ++#define GET_SD_SCR_BUSWIDTHS_FLAGS(pB) (pB)[6] ++#define GET_SD_SCR_SECURITY(pB) (((pB)[6] >> 4) & 0x07) ++#define GET_SD_SCR_DATA_STAT_AFTER_ERASE(pB) (((pB)[6] >> 7) & 0x01) ++ ++/* SDIO R4 Response */ ++#define SD_SDIO_R4_RESPONSE_BYTES 6 ++#define SD_SDIO_R4_GET_OCR(pR) ((UINT32)((pR)[1]) | \ ++ (((UINT32)(pR)[2]) << 8) | \ ++ (((UINT32)(pR)[3]) << 16)) ++#define SD_SDIO_R4_IS_MEMORY_PRESENT(pR) (((pR)[4] & 0x08) == 0x08) ++#define SD_SDIO_R4_GET_IO_FUNC_COUNT(pR) (((pR)[4] >> 4) & 0x07) ++#define SD_SDIO_R4_IS_CARD_READY(pR) (((pR)[4] & 0x80) == 0x80) ++ ++/* SDIO R5 response */ ++#define SD_SDIO_R5_RESPONSE_BYTES 6 ++#define SD_SDIO_R5_READ_DATA_OFFSET 1 ++#define SD_R5_GET_READ_DATA(pR) (pR)[SD_SDIO_R5_READ_DATA_OFFSET] ++#define SD_R5_RESP_FLAGS_OFFSET 2 ++#define SD_R5_GET_RESP_FLAGS(pR) (pR)[SD_R5_RESP_FLAGS_OFFSET] ++#define SD_R5_SET_CMD(pR,cmd) (pR)[5] = (cmd) & 0xC0 ++#define SD_R5_RESP_CMD_ERR (1 << 7) /* for previous cmd */ ++#define SD_R5_ILLEGAL_CMD (1 << 6) ++#define SD_R5_GENERAL_ERR (1 << 3) ++#define SD_R5_INVALID_FUNC (1 << 1) ++#define SD_R5_ARG_RANGE_ERR (1 << 0) ++#define SD_R5_CURRENT_CMD_ERRORS (SD_R5_ILLEGAL_CMD | SD_R5_GENERAL_ERR \ ++ | SD_R5_INVALID_FUNC | SD_R5_ARG_RANGE_ERR) ++#define SD_R5_ERRORS (SD_R5_CURRENT_CMD_ERRORS) ++ ++#define SD_R5_GET_IO_STATE(pR) (((pR)[2] >> 4) & 0x03) ++#define SD_R5_STATE_DIS 0x00 ++#define SD_R5_STATE_CMD 0x01 ++#define SD_R5_STATE_TRN 0x02 ++ ++/* SDIO Modified R6 Response */ ++#define SD_SDIO_R6_RESPONSE_BYTES 6 ++#define SD_SDIO_R6_GET_RCA(pR) ((UINT16)((pR)[3]) | ((UINT16)((pR)[4]) << 8)) ++#define SD_SDIO_R6_GET_CSTAT(pR)((UINT16)((pR)[1]) | ((UINT16)((pR)[2]) << 8)) ++ ++/* SPI mode R1 response */ ++#define SPI_R1_RESPONSE_BYTES 1 ++#define GET_SPI_R1_RESP_TOKEN(pR) (pR)[0] ++#define SPI_CS_STATE_IDLE 0x01 ++#define SPI_CS_ERASE_RESET (1 << 1) ++#define SPI_CS_ILLEGAL_CMD (1 << 2) ++#define SPI_CS_CMD_CRC_ERR (1 << 3) ++#define SPI_CS_ERASE_SEQ_ERR (1 << 4) ++#define SPI_CS_ADDRESS_ERR (1 << 5) ++#define SPI_CS_PARAM_ERR (1 << 6) ++#define SPI_CS_ERR_MASK 0x7c ++ ++/* SPI mode R2 response */ ++#define SPI_R2_RESPONSE_BYTES 2 ++#define GET_SPI_R2_RESP_TOKEN(pR) (pR)[1] ++#define GET_SPI_R2_STATUS_TOKEN(pR) (pR)[0] ++/* the first response byte is defined above */ ++/* the second response byte is defined below */ ++#define SPI_CS_CARD_IS_LOCKED (1 << 0) ++#define SPI_CS_LOCK_UNLOCK_FAILED (1 << 1) ++#define SPI_CS_ERROR (1 << 2) ++#define SPI_CS_INTERNAL_ERROR (1 << 3) ++#define SPI_CS_ECC_FAILED (1 << 4) ++#define SPI_CS_WP_VIOLATION (1 << 5) ++#define SPI_CS_ERASE_PARAM_ERR (1 << 6) ++#define SPI_CS_OUT_OF_RANGE (1 << 7) ++ ++/* SPI mode R3 response */ ++#define SPI_R3_RESPONSE_BYTES 5 ++#define SPI_R3_GET_OCR(pR) ((((UINT32)((pR)[0])) | \ ++ (((UINT32)((pR)[1])) << 8) | \ ++ (((UINT32)((pR)[2])) << 16) | \ ++ (((UINT32)((pR)[3])) << 24)) & SDMMC_OCR_VOLTAGE_MASK) ++#define SPI_R3_IS_CARD_READY(pR) (((pR)[3] & 0x80) == 0x80) ++#define GET_SPI_R3_RESP_TOKEN(pR) (pR)[4] ++ ++/* SPI mode SDIO R4 response */ ++#define SPI_SDIO_R4_RESPONSE_BYTES 5 ++#define SPI_SDIO_R4_GET_OCR(pR) ((UINT32)((pR)[0]) | \ ++ (((UINT32)(pR)[1]) << 8) | \ ++ (((UINT32)(pR)[2]) << 16)) ++#define SPI_SDIO_R4_IS_MEMORY_PRESENT(pR) (((pR)[3] & 0x08) == 0x08) ++#define SPI_SDIO_R4_GET_IO_FUNC_COUNT(pR) (((pR)[3] >> 4) & 0x07) ++#define SPI_SDIO_R4_IS_CARD_READY(pR) (((pR)[3] & 0x80) == 0x80) ++#define GET_SPI_SDIO_R4_RESP_TOKEN(pR) (pR)[4] ++ ++/* SPI Mode SDIO R5 response */ ++#define SPI_SDIO_R5_RESPONSE_BYTES 2 ++#define GET_SPI_SDIO_R5_RESP_TOKEN(pR) (pR)[1] ++#define GET_SPI_SDIO_R5_RESPONSE_RDATA(pR) (pR)[0] ++#define SPI_R5_IDLE_STATE 0x01 ++#define SPI_R5_ILLEGAL_CMD (1 << 2) ++#define SPI_R5_CMD_CRC (1 << 3) ++#define SPI_R5_FUNC_ERR (1 << 4) ++#define SPI_R5_PARAM_ERR (1 << 6) ++ ++/* SDIO COMMAND 52 Definitions */ ++#define CMD52_READ 0 ++#define CMD52_WRITE 1 ++#define CMD52_READ_AFTER_WRITE 1 ++#define CMD52_NORMAL_WRITE 0 ++#define SDIO_SET_CMD52_ARG(arg,rw,func,raw,address,writedata) \ ++ (arg) = (((rw) & 1) << 31) | \ ++ (((func) & 0x7) << 28) | \ ++ (((raw) & 1) << 27) | \ ++ (1 << 26) | \ ++ (((address) & 0x1FFFF) << 9) | \ ++ (1 << 8) | \ ++ ((writedata) & 0xFF) ++#define SDIO_SET_CMD52_READ_ARG(arg,func,address) \ ++ SDIO_SET_CMD52_ARG(arg,CMD52_READ,(func),0,address,0x00) ++#define SDIO_SET_CMD52_WRITE_ARG(arg,func,address,value) \ ++ SDIO_SET_CMD52_ARG(arg,CMD52_WRITE,(func),CMD52_NORMAL_WRITE,address,value) ++ ++/* SDIO COMMAND 53 Definitions */ ++#define CMD53_READ 0 ++#define CMD53_WRITE 1 ++#define CMD53_BLOCK_BASIS 1 ++#define CMD53_BYTE_BASIS 0 ++#define CMD53_FIXED_ADDRESS 0 ++#define CMD53_INCR_ADDRESS 1 ++#define SDIO_SET_CMD53_ARG(arg,rw,func,mode,opcode,address,bytes_blocks) \ ++ (arg) = (((rw) & 1) << 31) | \ ++ (((func) & 0x7) << 28) | \ ++ (((mode) & 1) << 27) | \ ++ (((opcode) & 1) << 26) | \ ++ (((address) & 0x1FFFF) << 9) | \ ++ ((bytes_blocks) & 0x1FF) ++ ++#define SDIO_MAX_LENGTH_BYTE_BASIS 512 ++#define SDIO_MAX_BLOCKS_BLOCK_BASIS 511 ++#define SDIO_MAX_BYTES_PER_BLOCK 2048 ++#define SDIO_COMMON_AREA_FUNCTION_NUMBER 0 ++#define SDIO_FIRST_FUNCTION_NUMBER 1 ++#define SDIO_LAST_FUNCTION_NUMBER 7 ++ ++#define CMD53_CONVERT_BYTE_BASIS_BLK_LENGTH_PARAM(b) (((b) < SDIO_MAX_LENGTH_BYTE_BASIS) ? (b) : 0) ++#define CMD53_CONVERT_BLOCK_BASIS_BLK_COUNT_PARAM(b) (((b) <= SDIO_MAX_BLOCKS_BLOCK_BASIS) ? (b) : 0) ++ ++ ++/* SDIO COMMON Registers */ ++ ++/* revision register */ ++#define CCCR_SDIO_REVISION_REG 0x00 ++#define CCCR_REV_MASK 0x0F ++#define CCCR_REV_1_0 0x00 ++#define CCCR_REV_1_1 0x01 ++#define SDIO_REV_MASK 0xF0 ++#define SDIO_REV_1_00 0x00 ++#define SDIO_REV_1_10 0x10 ++#define SDIO_REV_1_20 0x20 ++/* SD physical spec revision */ ++#define SD_SPEC_REVISION_REG 0x01 ++#define SD_REV_MASK 0x0F ++#define SD_REV_1_01 0x00 ++#define SD_REV_1_10 0x01 ++/* I/O Enable */ ++#define SDIO_ENABLE_REG 0x02 ++/* I/O Ready */ ++#define SDIO_READY_REG 0x03 ++/* Interrupt Enable */ ++#define SDIO_INT_ENABLE_REG 0x04 ++#define SDIO_INT_MASTER_ENABLE 0x01 ++#define SDIO_INT_ALL_ENABLE 0xFE ++/* Interrupt Pending */ ++#define SDIO_INT_PENDING_REG 0x05 ++#define SDIO_INT_PEND_MASK 0xFE ++/* I/O Abort */ ++#define SDIO_IO_ABORT_REG 0x06 ++#define SDIO_IO_RESET (1 << 3) ++/* Bus Interface */ ++#define SDIO_BUS_IF_REG 0x07 ++#define CARD_DETECT_DISABLE 0x80 ++#define SDIO_BUS_WIDTH_1_BIT 0x00 ++#define SDIO_BUS_WIDTH_4_BIT 0x02 ++/* Card Capabilities */ ++#define SDIO_CARD_CAPS_REG 0x08 ++#define SDIO_CAPS_CMD52_WHILE_DATA 0x01 /* card can issue CMD52 while data transfer */ ++#define SDIO_CAPS_MULTI_BLOCK 0x02 /* card supports multi-block data transfers */ ++#define SDIO_CAPS_READ_WAIT 0x04 /* card supports read-wait protocol */ ++#define SDIO_CAPS_SUSPEND_RESUME 0x08 /* card supports I/O function suspend/resume */ ++#define SDIO_CAPS_INT_MULTI_BLK 0x10 /* interrupts between multi-block data capable */ ++#define SDIO_CAPS_ENB_INT_MULTI_BLK 0x20 /* enable ints between muli-block data */ ++#define SDIO_CAPS_LOW_SPEED 0x40 /* low speed card */ ++#define SDIO_CAPS_4BIT_LS 0x80 /* 4 bit low speed card */ ++/* Common CIS pointer */ ++#define SDIO_CMN_CIS_PTR_LOW_REG 0x09 ++#define SDIO_CMN_CIS_PTR_MID_REG 0x0a ++#define SDIO_CMN_CIS_PTR_HI_REG 0x0b ++/* Bus suspend */ ++#define SDIO_BUS_SUSPEND_REG 0x0c ++#define SDIO_FUNC_SUSPEND_STATUS_MASK 0x01 /* selected function is suspended */ ++#define SDIO_SUSPEND_FUNCTION 0x02 /* suspend the current selected function */ ++/* Function select (for bus suspension) */ ++#define SDIO_FUNCTION_SELECT_REG 0x0d ++#define SDIO_SUSPEND_FUNCTION_0 0x00 ++#define SDIO_SUSPEND_MEMORY_FUNC_MASK 0x08 ++/* Function Execution */ ++#define SDIO_FUNCTION_EXEC_REG 0x0e ++#define SDIO_MEMORY_FUNC_EXEC_MASK 0x01 ++/* Function Ready */ ++#define SDIO_FUNCTION_READY_REG 0x0f ++#define SDIO_MEMORY_FUNC_BUSY_MASK 0x01 ++ ++/* power control 1.10 only */ ++#define SDIO_POWER_CONTROL_REG 0x12 ++#define SDIO_POWER_CONTROL_SMPC 0x01 ++#define SDIO_POWER_CONTROL_EMPC 0x02 ++ ++/* high speed control , 1.20 only */ ++#define SDIO_HS_CONTROL_REG 0x13 ++#define SDIO_HS_CONTROL_SHS 0x01 ++#define SDIO_HS_CONTROL_EHS 0x02 ++ ++/* Function Base Registers */ ++#define xFUNCTION_FBR_OFFSET(funcNo) (0x100*(funcNo)) ++/* offset calculation that does not use multiplication */ ++static INLINE UINT32 CalculateFBROffset(UCHAR FuncNo) { ++ UCHAR i = FuncNo; ++ UINT32 offset = 0; ++ while (i) { ++ offset += 0x100; ++ i--; ++ } ++ return offset; ++} ++/* Function info */ ++#define FBR_FUNC_INFO_REG_OFFSET(fbr) ((fbr) + 0x00) ++#define FUNC_INFO_SUPPORTS_CSA_MASK 0x40 ++#define FUNC_INFO_ENABLE_CSA 0x80 ++#define FUNC_INFO_DEVICE_CODE_MASK 0x0F ++#define FUNC_INFO_DEVICE_CODE_LAST 0x0F ++#define FBR_FUNC_EXT_DEVICE_CODE_OFFSET(fbr) ((fbr) + 0x01) ++/* Function Power selection */ ++#define FBR_FUNC_POWER_SELECT_OFFSET(fbr) ((fbr) + 0x02) ++#define FUNC_POWER_SELECT_SPS 0x01 ++#define FUNC_POWER_SELECT_EPS 0x02 ++/* Function CIS ptr */ ++#define FBR_FUNC_CIS_LOW_OFFSET(fbr) ((fbr) + 0x09) ++#define FBR_FUNC_CIS_MID_OFFSET(fbr) ((fbr) + 0x0a) ++#define FBR_FUNC_CIS_HI_OFFSET(fbr) ((fbr) + 0x0b) ++/* Function CSA ptr */ ++#define FBR_FUNC_CSA_LOW_OFFSET(fbr) ((fbr) + 0x0c) ++#define FBR_FUNC_CSA_MID_OFFSET(fbr) ((fbr) + 0x0d) ++#define FBR_FUNC_CSA_HI_OFFSET(fbr) ((fbr) + 0x0e) ++/* Function CSA data window */ ++#define FBR_FUNC_CSA_DATA_OFFSET(fbr) ((fbr) + 0x0f) ++/* Function Block Size Control */ ++#define FBR_FUNC_BLK_SIZE_LOW_OFFSET(fbr) ((fbr) + 0x10) ++#define FBR_FUNC_BLK_SIZE_HI_OFFSET(fbr) ((fbr) + 0x11) ++#define SDIO_CIS_AREA_BEGIN 0x00001000 ++#define SDIO_CIS_AREA_END 0x00017fff ++/* Tuple definitions */ ++#define CISTPL_NULL 0x00 ++#define CISTPL_CHECKSUM 0x10 ++#define CISTPL_VERS_1 0x15 ++#define CISTPL_ALTSTR 0x16 ++#define CISTPL_MANFID 0x20 ++#define CISTPL_FUNCID 0x21 ++#define CISTPL_FUNCE 0x22 ++#define CISTPL_VENDOR 0x91 ++#define CISTPL_END 0xff ++#define CISTPL_LINK_END 0xff ++ ++ ++/* these structures must be packed */ ++ ++/* Manufacturer ID tuple */ ++struct SDIO_MANFID_TPL { ++ UINT16 ManufacturerCode; /* jedec code */ ++ UINT16 ManufacturerInfo; /* manufacturer specific code */ ++}CT_PACK_STRUCT; ++ ++/* Function ID Tuple */ ++struct SDIO_FUNC_ID_TPL { ++ UINT8 DeviceCode; /* device code */ ++ UINT8 InitMask; /* system initialization mask (not used) */ ++}CT_PACK_STRUCT; ++ ++ /* Extended Function Tuple (Common) */ ++struct SDIO_FUNC_EXT_COMMON_TPL { ++ UINT8 Type; /* type */ ++ UINT16 Func0_MaxBlockSize; /* max function 0 block transfer size */ ++ UINT8 MaxTransSpeed; /* max transfer speed (encoded) */ ++#define TRANSFER_UNIT_MULTIPIER_MASK 0x07 ++#define TIME_VALUE_MASK 0x78 ++#define TIME_VALUE_SHIFT 3 ++}CT_PACK_STRUCT; ++ ++/* Extended Function Tuple (Per Function) */ ++struct SDIO_FUNC_EXT_FUNCTION_TPL { ++ UINT8 Type; /* type */ ++#define SDIO_FUNC_INFO_WAKEUP_SUPPORT 0x01 ++ UINT8 FunctionInfo; /* function info */ ++ UINT8 SDIORev; /* revision */ ++ UINT32 CardPSN; /* product serial number */ ++ UINT32 CSASize; /* CSA size */ ++ UINT8 CSAProperties; /* CSA properties */ ++ UINT16 MaxBlockSize; /* max block size for block transfers */ ++ UINT32 FunctionOCR; /* optimal function OCR */ ++ UINT8 OpMinPwr; /* operational min power */ ++ UINT8 OpAvgPwr; /* operational average power */ ++ UINT8 OpMaxPwr; /* operation maximum power */ ++ UINT8 SbMinPwr; /* standby minimum power */ ++ UINT8 SbAvgPwr; /* standby average power */ ++ UINT8 SbMaxPwr; /* standby maximum power */ ++ UINT16 MinBandWidth; /* minimum bus bandwidth */ ++ UINT16 OptBandWidth; /* optimalbus bandwitdh */ ++}CT_PACK_STRUCT; ++ ++struct SDIO_FUNC_EXT_FUNCTION_TPL_1_1 { ++ struct SDIO_FUNC_EXT_FUNCTION_TPL CommonInfo; /* from 1.0*/ ++ UINT16 EnableTimeOut; /* timeout for enable */ ++ UINT16 OperPwrMaxPwr; ++ UINT16 OperPwrAvgPwr; ++ UINT16 HiPwrMaxPwr; ++ UINT16 HiPwrAvgPwr; ++ UINT16 LowPwrMaxPwr; ++ UINT16 LowPwrAvgPwr; ++}CT_PACK_STRUCT; ++ ++static INLINE SDIO_STATUS ConvertCMD52ResponseToSDIOStatus(UINT8 CMD52ResponseFlags) { ++ if (!(CMD52ResponseFlags & SD_R5_ERRORS)) { ++ return SDIO_STATUS_SUCCESS; ++ } ++ if (CMD52ResponseFlags & SD_R5_ILLEGAL_CMD) { ++ return SDIO_STATUS_DATA_STATE_INVALID; ++ } else if (CMD52ResponseFlags & SD_R5_INVALID_FUNC) { ++ return SDIO_STATUS_INVALID_FUNC; ++ } else if (CMD52ResponseFlags & SD_R5_ARG_RANGE_ERR) { ++ return SDIO_STATUS_FUNC_ARG_ERROR; ++ } else { ++ return SDIO_STATUS_DATA_ERROR_UNKNOWN; ++ } ++} ++ ++/* CMD6 mode switch definitions */ ++ ++#define SD_SWITCH_FUNC_CHECK 0 ++#define SD_SWITCH_FUNC_SET ((UINT32)(1 << 31)) ++#define SD_FUNC_NO_SELECT_MASK 0x00FFFFFF ++#define SD_SWITCH_GRP_1 0 ++#define SD_SWITCH_GRP_2 1 ++#define SD_SWITCH_GRP_3 2 ++#define SD_SWITCH_GRP_4 3 ++#define SD_SWITCH_GRP_5 4 ++#define SD_SWITCH_GRP_6 5 ++ ++#define SD_SWITCH_HIGH_SPEED_GROUP SD_SWITCH_GRP_1 ++#define SD_SWITCH_HIGH_SPEED_FUNC_NO 1 ++ ++#define SD_SWITCH_MAKE_SHIFT(grp) ((grp) * 4) ++ ++#define SD_SWITCH_MAKE_GRP_PATTERN(FuncGrp,FuncNo) \ ++ ((SD_FUNC_NO_SELECT_MASK & (~(0xF << SD_SWITCH_MAKE_SHIFT(FuncGrp)))) | \ ++ (((FuncNo) & 0xF) << SD_SWITCH_MAKE_SHIFT(FuncGrp))) \ ++ ++#define SD_SWITCH_FUNC_ARG_GROUP_CHECK(FuncGrp,FuncNo) \ ++ (SD_SWITCH_FUNC_CHECK | SD_SWITCH_MAKE_GRP_PATTERN(FuncGrp,FuncNo)) ++ ++#define SD_SWITCH_FUNC_ARG_GROUP_SET(FuncGrp,FuncNo) \ ++ (SD_SWITCH_FUNC_SET | SD_SWITCH_MAKE_GRP_PATTERN(FuncGrp,FuncNo)) ++ ++#define SD_SWITCH_FUNC_STATUS_BLOCK_BYTES 64 ++ ++#define SD_SWITCH_FUNC_STATUS_GET_GRP_BIT_MASK(pBuffer,FuncGrp) \ ++ (USHORT)((pBuffer)[50 + ((FuncGrp)*2)] | ((pBuffer)[51 + ((FuncGrp)*2)] << 8)) ++ ++#define SD_SWITCH_FUNC_STATUS_GET_MAX_CURRENT(pBuffer) \ ++ (USHORT)((pBuffer)[62] | ((pBuffer)[63] << 8)) ++ ++static INLINE UINT8 SDSwitchGetSwitchResult(PUINT8 pBuffer, UINT8 FuncGrp) ++{ ++ switch (FuncGrp) { ++ case 0: ++ return (pBuffer[47] & 0xF); ++ case 1: ++ return (pBuffer[47] >> 4); ++ case 2: ++ return (pBuffer[48] & 0xF); ++ case 3: ++ return (pBuffer[48] >> 4); ++ case 4: ++ return (pBuffer[49] & 0xF); ++ case 5: ++ return (pBuffer[49] >> 4); ++ default: ++ return 0xF; ++ } ++} ++ ++#endif +diff --git a/include/linux/sdio/ctsystem.h b/include/linux/sdio/ctsystem.h +new file mode 100644 +index 0000000..4f72739 +--- /dev/null ++++ b/include/linux/sdio/ctsystem.h +@@ -0,0 +1,115 @@ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++@file: cpsystem.h ++ ++@abstract: common system include file. ++ ++@notice: Copyright (c), 2004-2006 Atheros Communications, Inc. ++ ++ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation; ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Portions of this code were developed with information supplied from the ++ * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: ++ * ++ * The following conditions apply to the release of the SD simplified specification (�Simplified ++ * Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete ++ * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided ++ * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified ++ * Specification may require a license from the SD Card Association or other third parties. ++ * Disclaimers: ++ * The information contained in the Simplified Specification is presented only as a standard ++ * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any ++ * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for ++ * any damages, any infringements of patents or other right of the SD Card Association or any third ++ * parties, which may result from its use. No license is granted by implication, estoppel or otherwise ++ * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall ++ * be construed as an obligation by the SD Card Association to disclose or distribute any technical ++ * information, know-how or other confidential information to any third party. ++ * ++ * ++ * The initial developers of the original code are Seung Yi and Paul Lever ++ * ++ * sdio@atheros.com ++ * ++ * ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#ifndef __CPSYSTEM_H___ ++#define __CPSYSTEM_H___ ++ ++/* SDIO stack status defines */ ++/* < 0 error, >0 warning, 0 success */ ++#define SDIO_IS_WARNING(status) ((status) > 0) ++#define SDIO_IS_ERROR(status) ((status) < 0) ++#define SDIO_SUCCESS(status) ((SDIO_STATUS)(status) >= 0) ++#define SDIO_STATUS_SUCCESS 0 ++#define SDIO_STATUS_ERROR -1 ++#define SDIO_STATUS_INVALID_PARAMETER -2 ++#define SDIO_STATUS_PENDING 3 ++#define SDIO_STATUS_DEVICE_NOT_FOUND -4 ++#define SDIO_STATUS_DEVICE_ERROR -5 ++#define SDIO_STATUS_INTERRUPTED -6 ++#define SDIO_STATUS_NO_RESOURCES -7 ++#define SDIO_STATUS_CANCELED -8 ++#define SDIO_STATUS_BUFFER_TOO_SMALL -9 ++#define SDIO_STATUS_NO_MORE_MESSAGES -10 ++#define SDIO_STATUS_BUS_RESP_TIMEOUT -20 /* response timed-out */ ++#define SDIO_STATUS_BUS_READ_TIMEOUT -21 /* read data timed-out */ ++#define SDIO_STATUS_BUS_READ_CRC_ERR -22 /* data CRC failed */ ++#define SDIO_STATUS_BUS_WRITE_ERROR -23 /* write failed */ ++#define SDIO_STATUS_BUS_RESP_CRC_ERR -24 /* response received with a CRC error */ ++#define SDIO_STATUS_INVALID_TUPLE_LENGTH -25 /* tuple length was invalid */ ++#define SDIO_STATUS_TUPLE_NOT_FOUND -26 /* tuple could not be found */ ++#define SDIO_STATUS_CIS_OUT_OF_RANGE -27 /* CIS is out of range in the tuple scan */ ++#define SDIO_STATUS_FUNC_ENABLE_TIMEOUT -28 /* card timed out enabling or disabling */ ++#define SDIO_STATUS_DATA_STATE_INVALID -29 /* card is in an invalid state for data */ ++#define SDIO_STATUS_DATA_ERROR_UNKNOWN -30 /* card cannot process data transfer */ ++#define SDIO_STATUS_INVALID_FUNC -31 /* sdio request is not valid for the function */ ++#define SDIO_STATUS_FUNC_ARG_ERROR -32 /* sdio request argument is invalid or out of range */ ++#define SDIO_STATUS_INVALID_COMMAND -33 /* SD COMMAND is invalid for the card state */ ++#define SDIO_STATUS_SDREQ_QUEUE_FAILED -34 /* request failed to insert into queue */ ++#define SDIO_STATUS_BUS_RESP_TIMEOUT_SHIFTABLE -35 /* response timed-out, possibily shiftable to correct */ ++#define SDIO_STATUS_UNSUPPORTED -36 /* not supported */ ++#define SDIO_STATUS_PROGRAM_TIMEOUT -37 /* memory card programming timeout */ ++#define SDIO_STATUS_PROGRAM_STATUS_ERROR -38 /* memory card programming errors */ ++ ++#include <linux/sdio/ctsystem_linux.h> ++ ++/* get structure from contained field */ ++#define CONTAINING_STRUCT(address, struct_type, field_name)\ ++ ((struct_type *)((ULONG_PTR)(address) - (ULONG_PTR)(&((struct_type *)0)->field_name))) ++ ++#define ZERO_OBJECT(obj) memset(&(obj),0,sizeof(obj)) ++#define ZERO_POBJECT(pObj) memset((pObj),0,sizeof(*(pObj))) ++ ++ ++/* bit field support functions */ ++static INLINE void SetBit(PULONG pField, UINT position) { ++ *pField |= 1 << position; ++} ++static INLINE void ClearBit(PULONG pField, UINT position) { ++ *pField &= ~(1 << position); ++} ++static INLINE BOOL IsBitSet(PULONG pField, UINT position) { ++ return (*pField & (1 << position)); ++} ++static INLINE INT FirstClearBit(PULONG pField) { ++ UINT ii; ++ for(ii = 0; ii < sizeof(ULONG)*8; ii++) { ++ if (!IsBitSet(pField, ii)) { ++ return ii; ++ } ++ } ++ /* no clear bits found */ ++ return -1; ++} ++ ++#endif /* __CPSYSTEM_H___ */ +diff --git a/include/linux/sdio/ctsystem_linux.h b/include/linux/sdio/ctsystem_linux.h +new file mode 100644 +index 0000000..0de89a6 +--- /dev/null ++++ b/include/linux/sdio/ctsystem_linux.h +@@ -0,0 +1,983 @@ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++@file: ctsystem_linux.h ++ ++@abstract: common system include file for Linux. ++ ++@notice: Copyright (c), 2004-2006 Atheros Communications, Inc. ++ ++ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation; ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Portions of this code were developed with information supplied from the ++ * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: ++ * ++ * The following conditions apply to the release of the SD simplified specification (�Simplified ++ * Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete ++ * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided ++ * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified ++ * Specification may require a license from the SD Card Association or other third parties. ++ * Disclaimers: ++ * The information contained in the Simplified Specification is presented only as a standard ++ * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any ++ * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for ++ * any damages, any infringements of patents or other right of the SD Card Association or any third ++ * parties, which may result from its use. No license is granted by implication, estoppel or otherwise ++ * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall ++ * be construed as an obligation by the SD Card Association to disclose or distribute any technical ++ * information, know-how or other confidential information to any third party. ++ * ++ * ++ * The initial developers of the original code are Seung Yi and Paul Lever ++ * ++ * sdio@atheros.com ++ * ++ * ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#ifndef __CPSYSTEM_LINUX_H___ ++#define __CPSYSTEM_LINUX_H___ ++ ++/* #define DBG_TIMESTAMP 1 */ ++#define SD_TRACK_REQ 1 ++ ++/* LINUX support */ ++#include <linux/version.h> ++ ++#ifndef KERNEL_VERSION ++ #error KERNEL_VERSION macro not defined! ++#endif ++ ++#ifndef LINUX_VERSION_CODE ++ #error LINUX_VERSION_CODE macro not defined! ++#endif ++ ++#include <linux/autoconf.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/spinlock.h> ++#include <linux/module.h> ++ ++#include <linux/interrupt.h> ++#include <linux/pnp.h> ++#include <asm/hardirq.h> ++#include <asm/semaphore.h> ++#include <asm/io.h> ++#include <asm/scatterlist.h> ++#ifdef DBG_TIMESTAMP ++#include <asm/timex.h> ++#endif /* DBG_TIMESTAMP */ ++#ifndef in_atomic ++ /* released version of 2.6.9 */ ++#include <linux/hardirq.h> ++#endif ++#include <linux/delay.h> ++#include <linux/device.h> ++ ++/* generic types */ ++typedef unsigned char UCHAR; ++typedef unsigned char * PUCHAR; ++typedef char TEXT; ++typedef char * PTEXT; ++typedef unsigned short USHORT; ++typedef unsigned short* PUSHORT; ++typedef unsigned int UINT; ++typedef unsigned int* PUINT; ++typedef int INT; ++typedef int* PINT; ++typedef unsigned long ULONG; ++typedef unsigned long* PULONG; ++typedef u8 UINT8; ++typedef u16 UINT16; ++typedef u32 UINT32; ++typedef u8* PUINT8; ++typedef u16* PUINT16; ++typedef u32* PUINT32; ++typedef unsigned char * ULONG_PTR; ++typedef void* PVOID; ++typedef unsigned char BOOL; ++typedef BOOL* PBOOL; ++typedef int SDIO_STATUS; ++typedef int SYSTEM_STATUS; ++typedef unsigned int EVENT_TYPE; ++typedef unsigned int EVENT_ARG; ++typedef unsigned int* PEVENT_TYPE; ++typedef struct semaphore OS_SEMAPHORE; ++typedef struct semaphore* POS_SEMAPHORE; ++typedef struct semaphore OS_SIGNAL; /* OS signals are just semaphores */ ++typedef struct semaphore* POS_SIGNAL; ++typedef spinlock_t OS_CRITICALSECTION; ++typedef spinlock_t *POS_CRITICALSECTION; ++typedef int SDPOWER_STATE; ++typedef unsigned long ATOMIC_FLAGS; ++typedef INT THREAD_RETURN; ++typedef dma_addr_t DMA_ADDRESS; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) ++typedef struct task_struct* PKERNEL_TASK; ++typedef struct device_driver OS_DRIVER; ++typedef struct device_driver* POS_DRIVER; ++typedef struct device OS_DEVICE; ++typedef struct device* POS_DEVICE; ++typedef struct pnp_driver OS_PNPDRIVER; ++typedef struct pnp_driver* POS_PNPDRIVER; ++typedef struct pnp_dev OS_PNPDEVICE; ++typedef struct pnp_dev* POS_PNPDEVICE; ++typedef struct module* POS_MODULE; ++#else ++/* 2.4 */ ++typedef int PKERNEL_TASK; ++typedef PVOID OS_DRIVER; ++typedef PVOID* POS_DRIVER; ++typedef PVOID OS_DEVICE; ++typedef PVOID* POS_DEVICE; ++typedef PVOID OS_PNPDRIVER; ++typedef PVOID* POS_PNPDRIVER; ++typedef PVOID OS_PNPDEVICE; ++typedef PVOID* POS_PNPDEVICE; ++typedef struct module* POS_MODULE; ++#define module_param(a,b,c) MODULE_PARM(a, "i") ++#endif ++ ++typedef int CT_DEBUG_LEVEL; ++ ++ ++#ifndef TRUE ++#define TRUE 1 ++#endif ++#ifndef FALSE ++#define FALSE 0 ++#endif ++#ifndef NULL ++#define NULL ((PVOID)0) ++#endif ++#define SDDMA_DESCRIPTION_FLAG_DMA 0x1 /* DMA enabled */ ++#define SDDMA_DESCRIPTION_FLAG_SGDMA 0x2 /* Scatter-Gather DMA enabled */ ++typedef struct _SDDMA_DESCRIPTION { ++ UINT16 Flags; /* SDDMA_DESCRIPTION_FLAG_xxx */ ++ UINT16 MaxDescriptors; /* number of supported scatter gather entries */ ++ UINT32 MaxBytesPerDescriptor; /* maximum bytes in a DMA descriptor entry */ ++ u64 Mask; /* dma address mask */ ++ UINT32 AddressAlignment; /* dma address alignment mask, least significant bits indicate illegal address bits */ ++ UINT32 LengthAlignment; /* dma buffer length alignment mask, least significant bits indicate illegal length bits */ ++}SDDMA_DESCRIPTION, *PSDDMA_DESCRIPTION; ++typedef struct scatterlist SDDMA_DESCRIPTOR, *PSDDMA_DESCRIPTOR; ++ ++#define INLINE inline ++#define CT_PACK_STRUCT __attribute__ ((packed)) ++ ++#define CT_DECLARE_MODULE_PARAM_INTEGER(p) module_param(p, int, 0644); ++ ++/* debug print macros */ ++//#define SDDBG_KERNEL_PRINT_LEVEL KERN_DEBUG ++#define SDDBG_KERNEL_PRINT_LEVEL KERN_ALERT ++#define DBG_MASK_NONE 0x0 ++#define DBG_MASK_HCD 0x100 ++#define DBG_MASK_LIB 0x200 ++#define DBG_MASK_BUS 0x400 ++ ++/* debug output levels, this must be order low number to higher */ ++#define SDDBG_ERROR 3 ++#define SDDBG_WARN 4 ++#define SDDBG_DEBUG 6 ++#define SDDBG_TRACE 7 ++#define SDDBG_ALL 0xff ++ ++#define DBG_LEVEL_NONE 0 ++#define DBG_LEVEL_ERROR SDDBG_ERROR ++#define DBG_LEVEL_WARN SDDBG_WARN ++#define DBG_LEVEL_DEBUG SDDBG_DEBUG ++#define DBG_LEVEL_TRACE SDDBG_TRACE ++#define DBG_LEVEL_ALL SDDBG_ALL ++ ++#define DBG_GET_LEVEL(lvl) ((lvl) & 0xff) ++#define DBG_GET_MASK(lvl) (((lvl) & 0xff00)) ++ ++#define DBG_SDIO_MASK (DBG_MASK_NONE | DBG_LEVEL_DEBUG) ++ ++#define DEBUG 1 ++ ++#ifdef DEBUG ++ ++#define DBG_ASSERT(test) \ ++{ \ ++ if (!(test)) { \ ++ DBG_PRINT(SDDBG_ERROR, ("Debug Assert Caught, File %s, Line: %d, Test:%s \n",__FILE__, __LINE__,#test)); \ ++ } \ ++} ++#define DBG_ASSERT_WITH_MSG(test,s) \ ++{ \ ++ if (!(test)) { \ ++ DBG_PRINT(SDDBG_ERROR, ("Assert:%s File %s, Line: %d \n",(s),__FILE__, __LINE__)); \ ++ } \ ++} ++ ++#define DBG_PRINT(lvl, args)\ ++ do {\ ++ if (DBG_GET_LEVEL(lvl) <= (DBG_SDIO_MASK & 0xff)) \ ++ printk(_DBG_PRINTX_ARG args); \ ++ } while(0); ++ ++#else /* DEBUG */ ++ ++#define DBG_PRINT(lvl, str) ++#define DBG_ASSERT(test) ++#define DBG_ASSERT_WITH_MSG(test,s) ++#endif /* DEBUG */ ++ ++#define _DBG_PRINTX_ARG(arg...) arg /* unroll the parens around the var args*/ ++#define DBG_GET_DEBUG_LEVEL() DBG_GET_LEVEL(DBG_SDIO_MASK) ++#define DBG_SET_DEBUG_LEVEL(v) ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Print a string to the debugger or console ++ ++ @function name: REL_PRINT ++ @prototype: void REL_PRINT(INT Level, string) ++ @category: Support_Reference ++ @input: Level - debug level for the print ++ ++ @output: none ++ ++ @return: ++ ++ @notes: If Level is less than the current debug level, the print will be ++ issued. This print cannot be conditionally compiled. ++ @see also: DBG_PRINT ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define REL_PRINT(lvl, args)\ ++ {if (lvl <= DBG_GET_DEBUG_LEVEL())\ ++ printk(SDDBG_KERNEL_PRINT_LEVEL _DBG_PRINTX_ARG args);\ ++ } ++/* debug output levels, this must be order low number to higher */ ++#define SDDBG_ERROR 3 ++#define SDDBG_WARN 4 ++#define SDDBG_DEBUG 6 ++#define SDDBG_TRACE 7 ++ ++#ifdef DBG_CRIT_SECTION_RECURSE ++ /* this macro thows an exception if the lock is recursively taken ++ * the kernel must be configured with: CONFIG_DEBUG_SPINLOCK=y */ ++#define call_spin_lock(pCrit) \ ++{ \ ++ UINT32 unlocked = 1; \ ++ if ((pCrit)->lock) {unlocked = 0;} \ ++ spin_lock_bh(pCrit); \ ++ if (!unlocked) { \ ++ unlocked = 0x01; \ ++ unlocked = *((volatile UINT32 *)unlocked); \ ++ } \ ++} ++ ++#define call_spin_lock_irqsave(pCrit,isc) \ ++{ \ ++ UINT32 unlocked = 1; \ ++ if ((pCrit)->lock) {unlocked = 0;} \ ++ spin_lock_irqsave(pCrit,isc); \ ++ if (!unlocked) { \ ++ unlocked = 0x01; \ ++ unlocked = *((volatile UINT32 *)unlocked); \ ++ } \ ++} ++ ++#else ++#define call_spin_lock(s) spin_lock_bh(s) ++#define call_spin_lock_irqsave(s,isc) spin_lock_irqsave(s,isc) ++#endif ++ ++#define call_spin_unlock(s) spin_unlock_bh((s)) ++#define call_spin_unlock_irqrestore(s,isc) spin_unlock_irqrestore(s,isc) ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,9) ++#define NonSchedulable() (in_atomic() || irqs_disabled()) ++#else ++#define NonSchedulable() (irqs_disabled()) ++#endif ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Initialize a critical section object. ++ ++ @function name: CriticalSectionInit ++ @prototype: SDIO_STATUS CriticalSectionInit(POS_CRITICALSECTION pCrit) ++ @category: Support_Reference ++ @output: pCrit - pointer to critical section to initialize ++ ++ @return: SDIO_STATUS_SUCCESS on success. ++ ++ @notes: CriticalSectionDelete() must be called to cleanup any resources ++ associated with the critical section. ++ ++ @see also: CriticalSectionDelete, CriticalSectionAcquire, CriticalSectionRelease ++ @example: To initialize a critical section: ++ status = CriticalSectionInit(&pDevice->ListLock); ++ if (!SDIO_SUCCESS(status)) { ++ .. failed ++ return status; ++ } ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline SDIO_STATUS CriticalSectionInit(POS_CRITICALSECTION pCrit) { ++ spin_lock_init(pCrit); ++ return SDIO_STATUS_SUCCESS; ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Acquire a critical section lock. ++ ++ @function name: CriticalSectionAcquire ++ @prototype: SDIO_STATUS CriticalSectionAcquire(POS_CRITICALSECTION pCrit) ++ @category: Support_Reference ++ ++ @input: pCrit - pointer to critical section that was initialized ++ ++ @return: SDIO_STATUS_SUCCESS on success. ++ ++ @notes: The critical section lock is acquired when this function returns ++ SDIO_STATUS_SUCCESS. Use CriticalSectionRelease() to release ++ the critical section lock. ++ ++ @see also: CriticalSectionRelease ++ ++ @example: To acquire a critical section lock: ++ status = CriticalSectionAcquire(&pDevice->ListLock); ++ if (!SDIO_SUCCESS(status)) { ++ .. failed ++ return status; ++ } ++ ... access protected data ++ // unlock ++ status = CriticalSectionRelease(&pDevice->ListLock); ++ if (!SDIO_SUCCESS(status)) { ++ .. failed ++ return status; ++ } ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline SDIO_STATUS CriticalSectionAcquire(POS_CRITICALSECTION pCrit) { ++ call_spin_lock(pCrit); ++ return SDIO_STATUS_SUCCESS; ++} ++ ++// macro-tized versions ++#define CriticalSectionAcquire_M(pCrit) \ ++ SDIO_STATUS_SUCCESS; call_spin_lock(pCrit) ++#define CriticalSectionRelease_M(pCrit) \ ++ SDIO_STATUS_SUCCESS; call_spin_unlock(pCrit) ++ ++#define CT_DECLARE_IRQ_SYNC_CONTEXT() unsigned long _ctSyncFlags ++ ++#define CriticalSectionAcquireSyncIrq(pCrit) \ ++ SDIO_STATUS_SUCCESS; call_spin_lock_irqsave(pCrit,_ctSyncFlags) ++ ++#define CriticalSectionReleaseSyncIrq(pCrit) \ ++ SDIO_STATUS_SUCCESS; call_spin_unlock_irqrestore(pCrit,_ctSyncFlags) ++ ++ ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Release a critical section lock. ++ ++ @function name: CriticalSectionRelease ++ @prototype: SDIO_STATUS CriticalSectionRelease(POS_CRITICALSECTION pCrit) ++ @category: Support_Reference ++ ++ @input: pCrit - pointer to critical section that was initialized ++ ++ @return: SDIO_STATUS_SUCCESS on success. ++ ++ @notes: The critical section lock is released when this function returns ++ SDIO_STATUS_SUCCESS. ++ ++ @see also: CriticalSectionAcquire ++ ++ @example: see CriticalSectionAcquire ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline SDIO_STATUS CriticalSectionRelease(POS_CRITICALSECTION pCrit) { ++ call_spin_unlock(pCrit); ++ return SDIO_STATUS_SUCCESS; ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Cleanup a critical section object ++ ++ @function name: CriticalSectionDelete ++ @prototype: void CriticalSectionDelete(POS_CRITICALSECTION pCrit) ++ @category: Support_Reference ++ ++ @input: pCrit - an initialized critical section object ++ ++ @return: SDIO_STATUS_SUCCESS on success. ++ ++ @notes: ++ ++ @see also: CriticalSectionInit, CriticalSectionAcquire, CriticalSectionRelease ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline void CriticalSectionDelete(POS_CRITICALSECTION pCrit) { ++ return; ++} ++ ++/* internal use */ ++static inline SDIO_STATUS SignalInitialize(POS_SIGNAL pSignal) { ++ sema_init(pSignal, 0); ++ return SDIO_STATUS_SUCCESS; ++} ++/* internal use */ ++static inline void SignalDelete(POS_SIGNAL pSignal) { ++ return; ++} ++/* internal use */ ++static inline SDIO_STATUS SignalWaitInterruptible(POS_SIGNAL pSignal) { ++ DBG_ASSERT_WITH_MSG(!NonSchedulable(),"SignalWaitInterruptible not allowed\n"); ++ if (down_interruptible(pSignal) == 0) { ++ return SDIO_STATUS_SUCCESS; ++ } else { ++ return SDIO_STATUS_INTERRUPTED; ++ } ++} ++/* internal use */ ++static inline SDIO_STATUS SignalWait(POS_SIGNAL pSignal) { ++ DBG_ASSERT_WITH_MSG(!NonSchedulable(),"SignalWait not allowed\n"); ++ down(pSignal); ++ return SDIO_STATUS_SUCCESS; ++} ++ ++/* internal use */ ++static inline SDIO_STATUS SignalSet(POS_SIGNAL pSignal) { ++ up(pSignal); ++ return SDIO_STATUS_SUCCESS; ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Initialize a semaphore object. ++ ++ @function name: SemaphoreInitialize ++ @prototype: SDIO_STATUS SemaphoreInitialize(POS_SEMAPHORE pSem, UINT value) ++ @category: Support_Reference ++ ++ @input: value - initial value of the semaphore ++ ++ @output: pSem - pointer to a semaphore object to initialize ++ ++ @return: SDIO_STATUS_SUCCESS on success. ++ ++ @notes: SemaphoreDelete() must be called to cleanup any resources ++ associated with the semaphore ++ ++ @see also: SemaphoreDelete, SemaphorePend, SemaphorePendInterruptable ++ ++ @example: To initialize a semaphore: ++ status = SemaphoreInitialize(&pDevice->ResourceSem,1); ++ if (!SDIO_SUCCESS(status)) { ++ .. failed ++ return status; ++ } ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline SDIO_STATUS SemaphoreInitialize(POS_SEMAPHORE pSem, UINT value) { ++ sema_init(pSem, value); ++ return SDIO_STATUS_SUCCESS; ++} ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Cleanup a semaphore object. ++ ++ @function name: SemaphoreDelete ++ @prototype: void SemaphoreDelete(POS_SEMAPHORE pSem) ++ @category: Support_Reference ++ ++ @input: pSem - pointer to a semaphore object to cleanup ++ ++ @return: ++ ++ @notes: ++ ++ @see also: SemaphoreInitialize +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline void SemaphoreDelete(POS_SEMAPHORE pSem) { ++ return; ++} ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Acquire the semaphore or pend if the resource is not available ++ ++ @function name: SemaphorePend ++ @prototype: SDIO_STATUS SemaphorePend(POS_SEMAPHORE pSem) ++ @category: Support_Reference ++ ++ @input: pSem - pointer to an initialized semaphore object ++ ++ @return: SDIO_STATUS_SUCCESS on success. ++ ++ @notes: If the semaphore count is zero this function blocks until the count ++ becomes non-zero, otherwise the count is decremented and execution ++ continues. While waiting, the task/thread cannot be interrupted. ++ If the task or thread should be interruptible, use SemaphorePendInterruptible. ++ On some OSes SemaphorePend and SemaphorePendInterruptible behave the same. ++ ++ @see also: SemaphorePendInterruptable, SemaphorePost ++ @example: To wait for a resource using a semaphore: ++ status = SemaphorePend(&pDevice->ResourceSem); ++ if (!SDIO_SUCCESS(status)) { ++ .. failed ++ return status; ++ } ++ ... resource acquired ++ SemaphorePost(&pDevice->ResourceSem); ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline SDIO_STATUS SemaphorePend(POS_SEMAPHORE pSem) { ++ DBG_ASSERT_WITH_MSG(!NonSchedulable(),"SemaphorePend not allowed\n"); ++ down(pSem); ++ return SDIO_STATUS_SUCCESS; ++} ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Acquire the semaphore or pend if the resource is not available ++ ++ @function name: SemaphorePendInterruptable ++ @prototype: SDIO_STATUS SemaphorePendInterruptable(POS_SEMAPHORE pSem) ++ @category: Support_Reference ++ ++ @input: pSem - pointer to an initialized semaphore object ++ ++ @return: SDIO_STATUS_SUCCESS on success. ++ ++ @notes: If the semaphore count is zero this function blocks until the count ++ becomes non-zero, otherwise the count is decremented and execution ++ continues. While waiting, the task/thread can be interrupted. ++ If the task or thread should not be interruptible, use SemaphorePend. ++ ++ @see also: SemaphorePend, SemaphorePost ++ @example: To wait for a resource using a semaphore: ++ status = SemaphorePendInterruptable(&pDevice->ResourceSem); ++ if (!SDIO_SUCCESS(status)) { ++ .. failed, could have been interrupted ++ return status; ++ } ++ ... resource acquired ++ SemaphorePost(&pDevice->ResourceSem); ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline SDIO_STATUS SemaphorePendInterruptable(POS_SEMAPHORE pSem) { ++ DBG_ASSERT_WITH_MSG(!NonSchedulable(),"SemaphorePendInterruptable not allowed\n"); ++ if (down_interruptible(pSem) == 0) { ++ return SDIO_STATUS_SUCCESS; ++ } else { ++ return SDIO_STATUS_INTERRUPTED; ++ } ++} ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Post a semaphore. ++ ++ @function name: SemaphorePost ++ @prototype: SDIO_STATUS SemaphorePost(POS_SEMAPHORE pSem) ++ @category: Support_Reference ++ ++ @input: pSem - pointer to an initialized semaphore object ++ ++ @return: SDIO_STATUS_SUCCESS on success. ++ ++ @notes: This function increments the semaphore count. ++ ++ @see also: SemaphorePend, SemaphorePendInterruptable. ++ @example: Posting a semaphore: ++ status = SemaphorePendInterruptable(&pDevice->ResourceSem); ++ if (!SDIO_SUCCESS(status)) { ++ .. failed, could have been interrupted ++ return status; ++ } ++ ... resource acquired ++ // post the semaphore ++ SemaphorePost(&pDevice->ResourceSem); ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline SDIO_STATUS SemaphorePost(POS_SEMAPHORE pSem) { ++ DBG_ASSERT_WITH_MSG(!NonSchedulable(),"SemaphorePost not allowed\n"); ++ up(pSem); ++ return SDIO_STATUS_SUCCESS; ++} ++ ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Allocate a block of kernel accessible memory ++ ++ @function name: KernelAlloc ++ @prototype: PVOID KernelAlloc(UINT size) ++ @category: Support_Reference ++ ++ @input: size - size of memory block to allocate ++ ++ @return: pointer to the allocated memory, NULL if allocation failed ++ ++ @notes: For operating systems that use paging, the allocated memory is always ++ non-paged memory. Caller should only use KernelFree() to release the ++ block of memory. This call can potentially block and should only be called ++ from a schedulable context. Use KernelAllocIrqSafe() if the allocation ++ must be made from a non-schedulable context. ++ ++ @see also: KernelFree, KernelAllocIrqSafe ++ @example: allocating memory: ++ pBlock = KernelAlloc(1024); ++ if (pBlock == NULL) { ++ .. failed, no memory ++ return SDIO_STATUS_INSUFFICIENT_RESOURCES; ++ } ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline PVOID KernelAlloc(UINT size) { ++ PVOID pMem = kmalloc(size, GFP_KERNEL); ++ if (pMem != NULL) { memset(pMem,0,size); } ++ return pMem; ++} ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Free a block of kernel accessible memory. ++ ++ @function name: KernelFree ++ @prototype: void KernelFree(PVOID ptr) ++ @category: Support_Reference ++ ++ @input: ptr - pointer to memory allocated with KernelAlloc() ++ ++ @return: ++ ++ @notes: Caller should only use KernelFree() to release memory that was allocated ++ with KernelAlloc(). ++ ++ @see also: KernelAlloc ++ @example: KernelFree(pBlock); ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline void KernelFree(PVOID ptr) { ++ kfree(ptr); ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Allocate a block of kernel accessible memory in an IRQ-safe manner ++ ++ @function name: KernelAllocIrqSafe ++ @prototype: PVOID KernelAllocIrqSafe(UINT size) ++ @category: Support_Reference ++ ++ @input: size - size of memory block to allocate ++ ++ @return: pointer to the allocated memory, NULL if allocation failed ++ ++ @notes: This variant of KernelAlloc allows the allocation of small blocks of ++ memory from an ISR or from a context where scheduling has been disabled. ++ The allocations should be small as the memory is typically allocated ++ from a critical heap. The caller should only use KernelFreeIrqSafe() ++ to release the block of memory. ++ ++ @see also: KernelAlloc, KernelFreeIrqSafe ++ @example: allocating memory: ++ pBlock = KernelAllocIrqSafe(16); ++ if (pBlock == NULL) { ++ .. failed, no memory ++ return SDIO_STATUS_INSUFFICIENT_RESOURCES; ++ } ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline PVOID KernelAllocIrqSafe(UINT size) { ++ return kmalloc(size, GFP_ATOMIC); ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Free a block of kernel accessible memory. ++ ++ @function name: KernelFreeIrqSafe ++ @prototype: void KernelFreeIrqSafe(PVOID ptr) ++ @category: Support_Reference ++ ++ @input: ptr - pointer to memory allocated with KernelAllocIrqSafe() ++ ++ @return: ++ ++ @notes: Caller should only use KernelFreeIrqSafe() to release memory that was allocated ++ with KernelAllocIrqSafe(). ++ ++ @see also: KernelAllocIrqSafe ++ @example: KernelFreeIrqSafe(pBlock); ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline void KernelFreeIrqSafe(PVOID ptr) { ++ kfree(ptr); ++} ++ ++/* error status conversions */ ++static inline SYSTEM_STATUS SDIOErrorToOSError(SDIO_STATUS status) { ++ switch (status) { ++ case SDIO_STATUS_SUCCESS: ++ return 0; ++ case SDIO_STATUS_INVALID_PARAMETER: ++ return -EINVAL; ++ case SDIO_STATUS_PENDING: ++ return -EAGAIN; /* try again */ ++ case SDIO_STATUS_DEVICE_NOT_FOUND: ++ return -ENXIO; ++ case SDIO_STATUS_DEVICE_ERROR: ++ return -EIO; ++ case SDIO_STATUS_INTERRUPTED: ++ return -EINTR; ++ case SDIO_STATUS_NO_RESOURCES: ++ return -ENOMEM; ++ case SDIO_STATUS_ERROR: ++ default: ++ return -EFAULT; ++ } ++} ++static inline SDIO_STATUS OSErrorToSDIOError(SYSTEM_STATUS status) { ++ if (status >=0) { ++ return SDIO_STATUS_SUCCESS; ++ } ++ switch (status) { ++ case -EINVAL: ++ return SDIO_STATUS_INVALID_PARAMETER; ++ case -ENXIO: ++ return SDIO_STATUS_DEVICE_NOT_FOUND; ++ case -EIO: ++ return SDIO_STATUS_DEVICE_ERROR; ++ case -EINTR: ++ return SDIO_STATUS_INTERRUPTED; ++ case -ENOMEM: ++ return SDIO_STATUS_NO_RESOURCES; ++ case -EFAULT: ++ return SDIO_STATUS_ERROR; ++ default: ++ return SDIO_STATUS_ERROR; ++ } ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Sleep or delay the execution context for a number of milliseconds. ++ ++ @function name: OSSleep ++ @prototype: SDIO_STATUS OSSleep(INT SleepInterval) ++ @category: Support_Reference ++ ++ @input: SleepInterval - time in milliseconds to put the execution context to sleep ++ ++ @return: SDIO_STATUS_SUCCESS if sleep succeeded. ++ ++ @notes: Caller should be in a context that allows it to sleep or block. The ++ minimum duration of sleep may be greater than 1 MS on some platforms and OSes. ++ ++ @see also: OSSleep ++ @example: Using sleep to delay ++ EnableSlotPower(pSlot); ++ // wait for power to settle ++ status = OSSleep(100); ++ if (!SDIO_SUCCESS(status)){ ++ // failed.. ++ } ++ ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static inline SDIO_STATUS OSSleep(INT SleepInterval) { ++ UINT32 delta; ++ ++ DBG_ASSERT_WITH_MSG(!NonSchedulable(),"OSSleep not allowed\n"); ++ /* convert timeout to ticks */ ++ delta = (SleepInterval * HZ)/1000; ++ if (delta == 0) { ++ delta = 1; ++ } ++ set_current_state(TASK_INTERRUPTIBLE); ++ if (schedule_timeout(delta) != 0) { ++ return SDIO_STATUS_INTERRUPTED; ++ } ++ return SDIO_STATUS_SUCCESS; ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: get the OSs device object ++ ++ @function name: SD_GET_OS_DEVICE ++ @prototype: POS_DEVICE SD_GET_OS_DEVICE(PSDDEVICE pDevice) ++ @category: Support_Reference ++ ++ @input: pDevice - the device on the HCD ++ ++ @return: pointer to the OSs device ++ ++ @see also: ++ @example: obtain low level device ++ pFunctionContext->GpsDevice.Port.dev = SD_GET_OS_DEVICE(pDevice); ++ ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SD_GET_OS_DEVICE(pDevice) &((pDevice)->Device.dev) ++ ++ ++#ifdef __iomem ++ /* new type checking in 2.6.9 */ ++ /* I/O Access macros */ ++#define _READ_DWORD_REG(reg) \ ++ readl((const volatile void __iomem *)(reg)) ++#define _READ_WORD_REG(reg) \ ++ readw((const volatile void __iomem *)(reg)) ++#define _READ_BYTE_REG(reg) \ ++ readb((const volatile void __iomem *)(reg)) ++#define _WRITE_DWORD_REG(reg,value) \ ++ writel((value),(volatile void __iomem *)(reg)) ++#define _WRITE_WORD_REG(reg,value) \ ++ writew((value),(volatile void __iomem *)(reg)) ++#define _WRITE_BYTE_REG(reg,value) \ ++ writeb((value),(volatile void __iomem *)(reg)) ++#else ++ /* I/O Access macros */ ++#define _READ_DWORD_REG(reg) \ ++ readl((reg)) ++#define _READ_WORD_REG(reg) \ ++ readw((reg)) ++#define _READ_BYTE_REG(reg) \ ++ readb((reg)) ++#define _WRITE_DWORD_REG(reg,value) \ ++ writel((value),(reg)) ++#define _WRITE_WORD_REG(reg,value) \ ++ writew((value),(reg)) ++#define _WRITE_BYTE_REG(reg,value) \ ++ writeb((value),(reg)) ++#endif ++ /* atomic operators */ ++static inline ATOMIC_FLAGS AtomicTest_Set(volatile ATOMIC_FLAGS *pValue, INT BitNo) { ++ return test_and_set_bit(BitNo,(ATOMIC_FLAGS *)pValue); ++} ++static inline ATOMIC_FLAGS AtomicTest_Clear(volatile ATOMIC_FLAGS *pValue, INT BitNo) { ++ return test_and_clear_bit(BitNo,(ATOMIC_FLAGS *)pValue); ++} ++ ++struct _OSKERNEL_HELPER; ++ ++typedef THREAD_RETURN (*PHELPER_FUNCTION)(struct _OSKERNEL_HELPER *); ++ ++typedef struct _OSKERNEL_HELPER { ++ PKERNEL_TASK pTask; ++ BOOL ShutDown; ++ OS_SIGNAL WakeSignal; ++ struct completion Completion; ++ PVOID pContext; ++ PHELPER_FUNCTION pHelperFunc; ++}OSKERNEL_HELPER, *POSKERNEL_HELPER; ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Wake the helper thread ++ ++ @function name: SD_WAKE_OS_HELPER ++ @prototype: SD_WAKE_OS_HELPER(POSKERNEL_HELPER pOSHelper) ++ @category: Support_Reference ++ ++ @input: pOSHelper - the OS helper object ++ ++ @return: SDIO_STATUS ++ ++ @see also: SDLIB_OSCreateHelper ++ ++ @example: Waking up a helper thread ++ status = SD_WAKE_OS_HELPER(&pInstance->OSHelper); ++ ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SD_WAKE_OS_HELPER(p) SignalSet(&(p)->WakeSignal) ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Obtains the context for the helper function ++ ++ @function name: SD_GET_OS_HELPER_CONTEXT ++ @prototype: SD_GET_OS_HELPER_CONTEXT(POSKERNEL_HELPER pOSHelper) ++ @category: Support_Reference ++ ++ @input: pOSHelper - the OS helper object ++ ++ @return: helper specific context ++ ++ @notes: This macro should only be called by the function associated with ++ the helper object. ++ ++ @see also: SDLIB_OSCreateHelper ++ ++ @example: Getting the helper specific context ++ PMYCONTEXT pContext = (PMYCONTEXT)SD_GET_OS_HELPER_CONTEXT(pHelper); ++ ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SD_GET_OS_HELPER_CONTEXT(p) (p)->pContext ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Check helper function shut down flag. ++ ++ @function name: SD_IS_HELPER_SHUTTING_DOWN ++ @prototype: SD_IS_HELPER_SHUTTING_DOWN(POSKERNEL_HELPER pOSHelper) ++ @category: Support_Reference ++ ++ @input: pOSHelper - the OS helper object ++ ++ @return: TRUE if shutting down, else FALSE ++ ++ @notes: This macro should only be called by the function associated with ++ the helper object. The function should call this macro when it ++ unblocks from the call to SD_WAIT_FOR_WAKEUP(). If this function ++ returns TRUE, the function should clean up and exit. ++ ++ @see also: SDLIB_OSCreateHelper , SD_WAIT_FOR_WAKEUP ++ ++ @example: Checking for shutdown ++ while(1) { ++ status = SD_WAIT_FOR_WAKEUP(pHelper); ++ if (!SDIO_SUCCESS(status)) { ++ break; ++ } ++ if (SD_IS_HELPER_SHUTTING_DOWN(pHelper)) { ++ ... shutting down ++ break; ++ } ++ } ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SD_IS_HELPER_SHUTTING_DOWN(p) (p)->ShutDown ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Suspend and wait for wakeup signal ++ ++ @function name: SD_WAIT_FOR_WAKEUP ++ @prototype: SD_WAIT_FOR_WAKEUP(POSKERNEL_HELPER pOSHelper) ++ @category: Support_Reference ++ ++ @input: pOSHelper - the OS helper object ++ ++ @return: SDIO_STATUS ++ ++ @notes: This macro should only be called by the function associated with ++ the helper object. The function should call this function to suspend (block) ++ itself and wait for a wake up signal. The function should always check ++ whether the function should exit by calling SD_IS_HELPER_SHUTTING_DOWN. ++ ++ @see also: SDLIB_OSCreateHelper , SD_IS_HELPER_SHUTTING_DOWN ++ ++ @example: block on the wake signal ++ while(1) { ++ status = SD_WAIT_FOR_WAKEUP(pHelper); ++ if (!SDIO_SUCCESS(status)) { ++ break; ++ } ++ if (SD_IS_HELPER_SHUTTING_DOWN(pHelper)) { ++ ... shutting down ++ break; ++ } ++ } ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SD_WAIT_FOR_WAKEUP(p) SignalWait(&(p)->WakeSignal); ++ ++#define CT_LE16_TO_CPU_ENDIAN(x) __le16_to_cpu(x) ++#define CT_LE32_TO_CPU_ENDIAN(x) __le32_to_cpu(x) ++#define CT_CPU_ENDIAN_TO_LE16(x) __cpu_to_le16(x) ++#define CT_CPU_ENDIAN_TO_LE32(x) __cpu_to_le32(x) ++ ++#define CT_CPU_ENDIAN_TO_BE16(x) __cpu_to_be16(x) ++#define CT_CPU_ENDIAN_TO_BE32(x) __cpu_to_be32(x) ++#define CT_BE16_TO_CPU_ENDIAN(x) __be16_to_cpu(x) ++#define CT_BE32_TO_CPU_ENDIAN(x) __be32_to_cpu(x) ++#endif /* __CPSYSTEM_LINUX_H___ */ ++ +diff --git a/include/linux/sdio/mmc_defs.h b/include/linux/sdio/mmc_defs.h +new file mode 100644 +index 0000000..576ebd7 +--- /dev/null ++++ b/include/linux/sdio/mmc_defs.h +@@ -0,0 +1,103 @@ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++@file: mmc_defs.h ++ ++@abstract: MMC definitions not already defined in _sdio_defs.h ++ ++@notice: Copyright (c), 2004-2006 Atheros Communications, Inc. ++ ++ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation; ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Portions of this code were developed with information supplied from the ++ * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: ++ * ++ * The following conditions apply to the release of the SD simplified specification (�Simplified ++ * Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete ++ * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided ++ * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified ++ * Specification may require a license from the SD Card Association or other third parties. ++ * Disclaimers: ++ * The information contained in the Simplified Specification is presented only as a standard ++ * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any ++ * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for ++ * any damages, any infringements of patents or other right of the SD Card Association or any third ++ * parties, which may result from its use. No license is granted by implication, estoppel or otherwise ++ * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall ++ * be construed as an obligation by the SD Card Association to disclose or distribute any technical ++ * information, know-how or other confidential information to any third party. ++ * ++ * ++ * The initial developers of the original code are Seung Yi and Paul Lever ++ * ++ * sdio@atheros.com ++ * ++ * ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#ifndef ___MMC_DEFS_H___ ++#define ___MMC_DEFS_H___ ++ ++#define MMC_MAX_BUS_CLOCK 20000000 /* max clock speed in hz */ ++#define MMC_HS_MAX_BUS_CLOCK 52000000 /* MMC PLUS (high speed) max clock rate in hz */ ++ ++/* R2 (CSD) macros */ ++#define GET_MMC_CSD_TRANS_SPEED(pR) (pR)[12] ++#define GET_MMC_SPEC_VERSION(pR) (((pR)[15] >> 2) & 0x0F) ++#define MMC_SPEC_1_0_TO_1_2 0x00 ++#define MMC_SPEC_1_4 0x01 ++#define MMC_SPEC_2_0_TO_2_2 0x02 ++#define MMC_SPEC_3_1 0x03 ++#define MMC_SPEC_4_0_TO_4_1 0x04 ++ ++#define MMC_CMD_SWITCH 6 ++#define MMC_CMD8 8 ++ ++#define MMC_SWITCH_CMD_SET 0 ++#define MMC_SWITCH_SET_BITS 1 ++#define MMC_SWITCH_CLEAR_BITS 2 ++#define MMC_SWITCH_WRITE_BYTE 3 ++#define MMC_SWITCH_CMD_SET0 0 ++#define MMC_SWITCH_BUILD_ARG(cmdset,access,index,value) \ ++ (((cmdset) & 0x07) | (((access) & 0x03) << 24) | (((index) & 0xFF) << 16) | (((value) & 0xFF) << 8)) ++ ++#define MMC_EXT_CSD_SIZE 512 ++ ++#define MMC_EXT_S_CMD_SET_OFFSET 504 ++#define MMC_EXT_MIN_PERF_W_8_52_OFFSET 210 ++#define MMC_EXT_MIN_PERF_R_8_52_OFFSET 209 ++#define MMC_EXT_MIN_PERF_W_8_26_4_52_OFFSET 208 ++#define MMC_EXT_MIN_PERF_R_8_26_4_52_OFFSET 207 ++#define MMC_EXT_MIN_PERF_W_4_26_OFFSET 206 ++#define MMC_EXT_MIN_PERF_R_4_56_OFFSET 205 ++#define MMC_EXT_PWR_CL_26_360_OFFSET 203 ++#define MMC_EXT_PWR_CL_52_360_OFFSET 202 ++#define MMC_EXT_PWR_CL_26_195_OFFSET 201 ++#define MMC_EXT_PWR_CL_52_195_OFFSET 200 ++#define MMC_EXT_GET_PWR_CLASS(reg) ((reg) & 0xF) ++#define MMC_EXT_MAX_PWR_CLASSES 16 ++#define MMC_EXT_CARD_TYPE_OFFSET 196 ++#define MMC_EXT_CARD_TYPE_HS_52 (1 << 1) ++#define MMC_EXT_CARD_TYPE_HS_26 (1 << 0) ++#define MMC_EXT_CSD_VER_OFFSET 194 ++#define MMC_EXT_VER_OFFSET 192 ++#define MMC_EXT_VER_1_0 0 ++#define MMC_EXT_VER_1_1 1 ++#define MMC_EXT_CMD_SET_OFFSET 191 ++#define MMC_EXT_CMD_SET_REV_OFFSET 189 ++#define MMC_EXT_PWR_CLASS_OFFSET 187 ++#define MMC_EXT_HS_TIMING_OFFSET 185 ++#define MMC_EXT_HS_TIMING_ENABLE 0x01 ++#define MMC_EXT_BUS_WIDTH_OFFSET 183 ++#define MMC_EXT_BUS_WIDTH_1_BIT 0x00 ++#define MMC_EXT_BUS_WIDTH_4_BIT 0x01 ++#define MMC_EXT_BUS_WIDTH_8_BIT 0x02 ++ ++#endif +diff --git a/include/linux/sdio/sdio_busdriver.h b/include/linux/sdio/sdio_busdriver.h +new file mode 100644 +index 0000000..b431d3d +--- /dev/null ++++ b/include/linux/sdio/sdio_busdriver.h +@@ -0,0 +1,1435 @@ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++@file: sdio_busdriver.h ++ ++@abstract: include file for registration of SDIO function drivers ++ and SDIO host controller bus drivers. ++ ++@notice: Copyright (c), 2004-2006 Atheros Communications, Inc. ++ ++ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation; ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Portions of this code were developed with information supplied from the ++ * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: ++ * ++ * The following conditions apply to the release of the SD simplified specification (�Simplified ++ * Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete ++ * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided ++ * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified ++ * Specification may require a license from the SD Card Association or other third parties. ++ * Disclaimers: ++ * The information contained in the Simplified Specification is presented only as a standard ++ * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any ++ * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for ++ * any damages, any infringements of patents or other right of the SD Card Association or any third ++ * parties, which may result from its use. No license is granted by implication, estoppel or otherwise ++ * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall ++ * be construed as an obligation by the SD Card Association to disclose or distribute any technical ++ * information, know-how or other confidential information to any third party. ++ * ++ * ++ * The initial developers of the original code are Seung Yi and Paul Lever ++ * ++ * sdio@atheros.com ++ * ++ * ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#ifndef __SDIO_BUSDRIVER_H___ ++#define __SDIO_BUSDRIVER_H___ ++ ++typedef UINT8 CT_VERSION_CODE; ++#define CT_SDIO_STACK_VERSION_CODE ((CT_VERSION_CODE)0x26) /* version code that must be set in various structures */ ++#define CT_SDIO_STACK_VERSION_MAJOR(v) (((v) & 0xF0) >> 4) ++#define CT_SDIO_STACK_VERSION_MINOR(v) (((v) & 0x0F)) ++#define SET_SDIO_STACK_VERSION(p) (p)->Version = CT_SDIO_STACK_VERSION_CODE ++#define GET_SDIO_STACK_VERSION(p) (p)->Version ++#define GET_SDIO_STACK_VERSION_MAJOR(p) CT_SDIO_STACK_VERSION_MAJOR(GET_SDIO_STACK_VERSION(p)) ++#define GET_SDIO_STACK_VERSION_MINOR(p) CT_SDIO_STACK_VERSION_MINOR(GET_SDIO_STACK_VERSION(p)) ++#include "sdlist.h" ++ ++/* card flags */ ++typedef UINT16 CARD_INFO_FLAGS; ++#define CARD_MMC 0x0001 /* Multi-media card */ ++#define CARD_SD 0x0002 /* SD-Memory present */ ++#define CARD_SDIO 0x0004 /* SDIO present */ ++#define CARD_RAW 0x0008 /* Raw card */ ++#define CARD_COMBO (CARD_SD | CARD_SDIO) /* SDIO with SD */ ++#define CARD_TYPE_MASK 0x000F /* card type mask */ ++#define CARD_SD_WP 0x0010 /* SD WP on */ ++#define CARD_PSEUDO 0x0020 /* pseudo card (internal use) */ ++#define CARD_HIPWR 0x0040 /* card can use more than 200mA (SDIO 1.1 or greater)*/ ++#define GET_CARD_TYPE(flags) ((flags) & CARD_TYPE_MASK) ++ ++/* bus mode and clock rate */ ++typedef UINT32 SD_BUSCLOCK_RATE; /* clock rate in hz */ ++typedef UINT16 SD_BUSMODE_FLAGS; ++#define SDCONFIG_BUS_WIDTH_RESERVED 0x00 ++#define SDCONFIG_BUS_WIDTH_SPI 0x01 ++#define SDCONFIG_BUS_WIDTH_1_BIT 0x02 ++#define SDCONFIG_BUS_WIDTH_4_BIT 0x03 ++#define SDCONFIG_BUS_WIDTH_MMC8_BIT 0x04 ++#define SDCONFIG_BUS_WIDTH_MASK 0x0F ++#define SDCONFIG_SET_BUS_WIDTH(flags,width) \ ++{ \ ++ (flags) &= ~SDCONFIG_BUS_WIDTH_MASK; \ ++ (flags) |= (width); \ ++} ++#define SDCONFIG_GET_BUSWIDTH(flags) ((flags) & SDCONFIG_BUS_WIDTH_MASK) ++#define SDCONFIG_BUS_MODE_SPI_NO_CRC 0x40 /* SPI bus is operating with NO CRC */ ++#define SDCONFIG_BUS_MODE_SD_HS 0x80 /* set interface to SD high speed mode */ ++#define SDCONFIG_BUS_MODE_MMC_HS 0x20 /* set interface to MMC high speed mode */ ++ ++typedef UINT16 SD_SLOT_CURRENT; /* slot current in mA */ ++ ++typedef UINT8 SLOT_VOLTAGE_MASK; /* slot voltage */ ++#define SLOT_POWER_3_3V 0x01 ++#define SLOT_POWER_3_0V 0x02 ++#define SLOT_POWER_2_8V 0x04 ++#define SLOT_POWER_2_0V 0x08 ++#define SLOT_POWER_1_8V 0x10 ++#define SLOT_POWER_1_6V 0x20 ++ ++#define MAX_CARD_RESPONSE_BYTES 17 ++ ++/* plug and play information for SD cards */ ++typedef struct _SD_PNP_INFO { ++ UINT16 SDIO_ManufacturerCode; /* JEDEC Code */ ++ UINT16 SDIO_ManufacturerID; /* manf-specific ID */ ++ UINT8 SDIO_FunctionNo; /* function number 1-7 */ ++ UINT8 SDIO_FunctionClass; /* function class */ ++ UINT8 SDMMC_ManfacturerID; /* card CID's MANF-ID */ ++ UINT16 SDMMC_OEMApplicationID; /* card CID's OEMAPP-ID */ ++ CARD_INFO_FLAGS CardFlags; /* card flags */ ++}SD_PNP_INFO, *PSD_PNP_INFO; ++ ++#define IS_LAST_SDPNPINFO_ENTRY(id)\ ++ (((id)->SDIO_ManufacturerCode == 0) &&\ ++ ((id)->SDIO_ManufacturerID == 0) &&\ ++ ((id)->SDIO_FunctionNo == 0) &&\ ++ ((id)->SDIO_FunctionClass == 0) &&\ ++ ((id)->SDMMC_OEMApplicationID == 0) && \ ++ ((id)->CardFlags == 0)) ++ ++/* card properties */ ++typedef struct _CARD_PROPERTIES { ++ UINT8 IOFnCount; /* number of I/O functions */ ++ UINT8 SDIORevision; /* SDIO revision */ ++#define SDIO_REVISION_1_00 0x00 ++#define SDIO_REVISION_1_10 0x01 ++#define SDIO_REVISION_1_20 0x02 ++ UINT8 SD_MMC_Revision; /* SD or MMC revision */ ++#define SD_REVISION_1_01 0x00 ++#define SD_REVISION_1_10 0x01 ++#define MMC_REVISION_1_0_2_2 0x00 ++#define MMC_REVISION_3_1 0x01 ++#define MMC_REVISION_4_0 0x02 ++ UINT16 SDIO_ManufacturerCode; /* JEDEC Code */ ++ UINT16 SDIO_ManufacturerID; /* manf-specific ID */ ++ UINT32 CommonCISPtr; /* common CIS ptr */ ++ UINT16 RCA; /* relative card address */ ++ UINT8 SDIOCaps; /* SDIO card capabilities (refer to SDIO spec for decoding) */ ++ UINT8 CardCSD[MAX_CARD_RESPONSE_BYTES]; /* for SD/MMC cards */ ++ CARD_INFO_FLAGS Flags; /* card flags */ ++ SD_BUSCLOCK_RATE OperBusClock; /* operational bus clock (based on HCD limit)*/ ++ SD_BUSMODE_FLAGS BusMode; /* current card bus mode */ ++ UINT16 OperBlockLenLimit; /* operational bytes per block length limit*/ ++ UINT16 OperBlockCountLimit; /* operational number of blocks per transfer limit */ ++ UINT8 CardState; /* card state flags */ ++ SLOT_VOLTAGE_MASK CardVoltage; /* card operational voltage */ ++#define CARD_STATE_REMOVED 0x01 ++}CARD_PROPERTIES, *PCARD_PROPERTIES; ++ ++/* SDREQUEST request flags */ ++typedef UINT32 SDREQUEST_FLAGS; ++/* write operation */ ++#define SDREQ_FLAGS_DATA_WRITE 0x8000 ++/* has data (read or write) */ ++#define SDREQ_FLAGS_DATA_TRANS 0x4000 ++/* command is an atomic APP command, requiring CMD55 to be issued */ ++#define SDREQ_FLAGS_APP_CMD 0x2000 ++/* transfer should be handled asynchronously */ ++#define SDREQ_FLAGS_TRANS_ASYNC 0x1000 ++/* host should skip the SPI response filter for this command */ ++#define SDREQ_FLAGS_RESP_SKIP_SPI_FILT 0x0800 ++/* host should skip the response check for this data transfer */ ++#define SDREQ_FLAGS_DATA_SKIP_RESP_CHK 0x0400 ++/* flag requesting a CMD12 be automatically issued by host controller */ ++#define SDREQ_FLAGS_AUTO_CMD12 0x0200 ++/* flag indicating that the data buffer meets HCD's DMA restrictions */ ++#define SDREQ_FLAGS_DATA_DMA 0x0010 ++/* indicate to host that this is a short and quick transfer, the HCD may optimize ++ * this request to reduce interrupt overhead */ ++#define SDREQ_FLAGS_DATA_SHORT_TRANSFER 0x00010000 ++/* indicate to the host that this is a raw request */ ++#define SDREQ_FLAGS_RAW 0x00020000 ++/* auto data transfer status check for MMC and Memory cards */ ++#define SDREQ_FLAGS_AUTO_TRANSFER_STATUS 0x00100000 ++ ++#define SDREQ_FLAGS_UNUSED1 0x00200000 ++#define SDREQ_FLAGS_UNUSED2 0x00400000 ++#define SDREQ_FLAGS_UNUSED3 0x00800000 ++#define SDREQ_FLAGS_UNUSED4 0x01000000 ++#define SDREQ_FLAGS_UNUSED5 0x02000000 ++ ++/* the following flags are internal use only */ ++#define SDREQ_FLAGS_FORCE_DEFERRED_COMPLETE 0x0100 ++/* flag indicating that response has been converted (internal use) */ ++#define SDREQ_FLAGS_RESP_SPI_CONVERTED 0x0040 ++/* request was cancelled - internal use only */ ++#define SDREQ_FLAGS_CANCELED 0x0020 ++/* a barrier operation */ ++#define SDREQ_FLAGS_BARRIER 0x00040000 ++/* a pseudo bus request */ ++#define SDREQ_FLAGS_PSEUDO 0x00080000 ++/* queue to the head */ ++#define SDREQ_FLAGS_QUEUE_HEAD 0x04000000 ++ ++#define SDREQ_FLAGS_I_UNUSED1 0x08000000 ++#define SDREQ_FLAGS_I_UNUSED2 0x10000000 ++#define SDREQ_FLAGS_I_UNUSED3 0x20000000 ++#define SDREQ_FLAGS_I_UNUSED4 0x40000000 ++#define SDREQ_FLAGS_I_UNUSED5 0x80000000 ++ ++/* response type mask */ ++#define SDREQ_FLAGS_RESP_MASK 0x000F ++#define GET_SDREQ_RESP_TYPE(flags) ((flags) & SDREQ_FLAGS_RESP_MASK) ++#define IS_SDREQ_WRITE_DATA(flags) ((flags) & SDREQ_FLAGS_DATA_WRITE) ++#define IS_SDREQ_DATA_TRANS(flags) ((flags) & SDREQ_FLAGS_DATA_TRANS) ++#define IS_SDREQ_RAW(flags) ((flags) & SDREQ_FLAGS_RAW) ++#define IS_SDREQ_FORCE_DEFERRED_COMPLETE(flags) ((flags) & SDREQ_FLAGS_FORCE_DEFERRED_COMPLETE) ++#define SDREQ_FLAGS_NO_RESP 0x0000 ++#define SDREQ_FLAGS_RESP_R1 0x0001 ++#define SDREQ_FLAGS_RESP_R1B 0x0002 ++#define SDREQ_FLAGS_RESP_R2 0x0003 ++#define SDREQ_FLAGS_RESP_R3 0x0004 ++#define SDREQ_FLAGS_RESP_MMC_R4 0x0005 /* not supported, for future use */ ++#define SDREQ_FLAGS_RESP_MMC_R5 0x0006 /* not supported, for future use */ ++#define SDREQ_FLAGS_RESP_R6 0x0007 ++#define SDREQ_FLAGS_RESP_SDIO_R4 0x0008 ++#define SDREQ_FLAGS_RESP_SDIO_R5 0x0009 ++ ++struct _SDREQUEST; ++struct _SDFUNCTION; ++ ++typedef void (*PSDEQUEST_COMPLETION)(struct _SDREQUEST *); ++ ++/* defines SD/MMC and SDIO requests for the RAW-mode API */ ++typedef struct _SDREQUEST { ++ SDLIST SDList; /* internal use list*/ ++ UINT32 Argument; /* SD/SDIO/MMC 32 bit argument */ ++ SDREQUEST_FLAGS Flags; /* request flags */ ++ ATOMIC_FLAGS InternalFlags; /* internal use flags */ ++ UINT8 Command; /* SD/SDIO/MMC 8 bit command */ ++ UINT8 Response[MAX_CARD_RESPONSE_BYTES]; /* buffer for CMD response */ ++ UINT16 BlockCount; /* number of blocks to send/rcv */ ++ UINT16 BlockLen; /* length of each block */ ++ UINT16 DescriptorCount; /* number of DMA descriptor entries in pDataBuffer if DMA */ ++ PVOID pDataBuffer; /* starting address of buffer (or ptr to PSDDMA_DESCRIPTOR*/ ++ UINT32 DataRemaining; /* number of bytes remaining in the transfer (internal use) */ ++ PVOID pHcdContext; /* internal use context */ ++ PSDEQUEST_COMPLETION pCompletion; /* function driver completion routine */ ++ PVOID pCompleteContext; /* function driver completion context */ ++ SDIO_STATUS Status; /* completion status */ ++ struct _SDFUNCTION* pFunction; /* function driver that generated request (internal use)*/ ++ INT RetryCount; /* number of times to retry on error, non-data cmds only */ ++ PVOID pBdRsv1; /* reserved */ ++ PVOID pBdRsv2; ++ PVOID pBdRsv3; ++}SDREQUEST, *PSDREQUEST; ++ ++ /* a request queue */ ++typedef struct _SDREQUESTQUEUE { ++ SDLIST Queue; /* the queue of requests */ ++ BOOL Busy; /* busy flag */ ++}SDREQUESTQUEUE, *PSDREQUESTQUEUE; ++ ++ ++typedef UINT16 SDCONFIG_COMMAND; ++/* SDCONFIG request flags */ ++/* get operation */ ++#define SDCONFIG_FLAGS_DATA_GET 0x8000 ++/* put operation */ ++#define SDCONFIG_FLAGS_DATA_PUT 0x4000 ++/* host controller */ ++#define SDCONFIG_FLAGS_HC_CONFIG 0x2000 ++/* both */ ++#define SDCONFIG_FLAGS_DATA_BOTH (SDCONFIG_FLAGS_DATA_GET | SDCONFIG_FLAGS_DATA_PUT) ++/* no data */ ++#define SDCONFIG_FLAGS_DATA_NONE 0x0000 ++ ++/* SDCONFIG commands */ ++#define SDCONFIG_GET_HCD_DEBUG (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_GET | 275) ++#define SDCONFIG_SET_HCD_DEBUG (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_PUT | 276) ++ ++/* custom hcd commands */ ++#define SDCONFIG_GET_HOST_CUSTOM (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_GET | 300) ++#define SDCONFIG_PUT_HOST_CUSTOM (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_PUT | 301) ++ ++/* function commands */ ++#define SDCONFIG_FUNC_ENABLE_DISABLE (SDCONFIG_FLAGS_DATA_PUT | 18) ++#define SDCONFIG_FUNC_UNMASK_IRQ (SDCONFIG_FLAGS_DATA_NONE | 21) ++#define SDCONFIG_FUNC_MASK_IRQ (SDCONFIG_FLAGS_DATA_NONE | 22) ++#define SDCONFIG_FUNC_ACK_IRQ (SDCONFIG_FLAGS_DATA_NONE | 23) ++#define SDCONFIG_FUNC_SPI_MODE_DISABLE_CRC (SDCONFIG_FLAGS_DATA_NONE | 24) ++#define SDCONFIG_FUNC_SPI_MODE_ENABLE_CRC (SDCONFIG_FLAGS_DATA_NONE | 25) ++#define SDCONFIG_FUNC_ALLOC_SLOT_CURRENT (SDCONFIG_FLAGS_DATA_PUT | 26) ++#define SDCONFIG_FUNC_FREE_SLOT_CURRENT (SDCONFIG_FLAGS_DATA_NONE | 27) ++#define SDCONFIG_FUNC_CHANGE_BUS_MODE (SDCONFIG_FLAGS_DATA_BOTH | 28) ++#define SDCONFIG_FUNC_CHANGE_BUS_MODE_ASYNC (SDCONFIG_FLAGS_DATA_BOTH | 29) ++#define SDCONFIG_FUNC_NO_IRQ_PEND_CHECK (SDCONFIG_FLAGS_DATA_NONE | 30) ++ ++typedef UINT8 FUNC_ENABLE_DISABLE_FLAGS; ++typedef UINT32 FUNC_ENABLE_TIMEOUT; ++ ++ /* function enable */ ++typedef struct _SDCONFIG_FUNC_ENABLE_DISABLE_DATA { ++#define SDCONFIG_DISABLE_FUNC 0x0000 ++#define SDCONFIG_ENABLE_FUNC 0x0001 ++ FUNC_ENABLE_DISABLE_FLAGS EnableFlags; /* enable flags*/ ++ FUNC_ENABLE_TIMEOUT TimeOut; /* timeout in milliseconds */ ++ void (*pOpComplete)(PVOID Context, SDIO_STATUS status); /* reserved */ ++ PVOID pOpCompleteContext; /* reserved */ ++}SDCONFIG_FUNC_ENABLE_DISABLE_DATA, *PSDCONFIG_FUNC_ENABLE_DISABLE_DATA; ++ ++ /* slot current allocation data */ ++typedef struct _SDCONFIG_FUNC_SLOT_CURRENT_DATA { ++ SD_SLOT_CURRENT SlotCurrent; /* slot current to request in mA*/ ++}SDCONFIG_FUNC_SLOT_CURRENT_DATA, *PSDCONFIG_FUNC_SLOT_CURRENT_DATA; ++ ++/* slot bus mode configuration */ ++typedef struct _SDCONFIG_BUS_MODE_DATA { ++ SD_BUSCLOCK_RATE ClockRate; /* clock rate in Hz */ ++ SD_BUSMODE_FLAGS BusModeFlags; /* bus mode flags */ ++ SD_BUSCLOCK_RATE ActualClockRate; /* actual rate in KHz */ ++}SDCONFIG_BUS_MODE_DATA, *PSDCONFIG_BUS_MODE_DATA; ++ ++/* defines configuration requests for the HCD */ ++typedef struct _SDCONFIG { ++ SDCONFIG_COMMAND Cmd; /* configuration command */ ++ PVOID pData; /* configuration data */ ++ INT DataLength; /* config data length */ ++}SDCONFIG, *PSDCONFIG; ++ ++#define SET_SDCONFIG_CMD_INFO(pHdr,cmd,pC,len) \ ++{ \ ++ (pHdr)->Cmd = (cmd); \ ++ (pHdr)->pData = (PVOID)(pC); \ ++ (pHdr)->DataLength = (len); \ ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get a pointer to the configuration command data. ++ ++ @function name: GET_SDCONFIG_CMD ++ @prototype: UNIT16 GET_SDCONFIG_CMD (PSDCONFIG pCommand) ++ @category: HD_Reference ++ ++ @input: pCommand - config command structure. ++ ++ @return: command code ++ ++ @notes: Implemented as a macro. This macro returns the command code for this ++ configuration request. ++ ++ @example: getting the command code: ++ cmd = GET_SDCONFIG_CMD(pConfig); ++ switch (cmd) { ++ case SDCONFIG_GET_WP: ++ .. get write protect switch position ++ break; ++ ... ++ } ++ ++ @see also: GET_SDCONFIG_CMD_LEN, GET_SDCONFIG_CMD_DATA ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define GET_SDCONFIG_CMD(pBuffer) ((pBuffer)->Cmd) ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get a pointer to the configuration command data. ++ ++ @function name: GET_SDCONFIG_CMD_LEN ++ @prototype: INT GET_SDCONFIG_CMD_LEN (PSDCONFIG pCommand) ++ @category: HD_Reference ++ ++ @input: pCommand - config command structure. ++ ++ @return: length of config command data ++ ++ @notes: Implemented as a macro. Host controller drivers can use this macro to extract ++ the number of bytes of command specific data. This can be used to validate the ++ config data buffer size. ++ ++ @example: getting the data length: ++ length = GET_SDCONFIG_CMD_LEN(pConfig); ++ if (length < CUSTOM_COMMAND_XXX_SIZE) { ++ ... invalid length ++ } ++ ++ @see also: GET_SDCONFIG_CMD, GET_SDCONFIG_CMD_DATA ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define GET_SDCONFIG_CMD_LEN(pBuffer) ((pBuffer)->DataLength) ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get a pointer to the configuration command data. ++ ++ @function name: GET_SDCONFIG_CMD_DATA ++ @prototype: (casted ptr) GET_SDCONFIG_CMD_DATA (type, PSDCONFIG pCommand) ++ @category: HD_Reference ++ ++ @input: type - pointer type to cast the returned pointer to. ++ pCommand - config command structure. ++ ++ @return: type-casted pointer to the command's data ++ ++ @notes: Implemented as a macro. Host controller drivers can use this macro to extract ++ a pointer to the command specific data in an HCD configuration request. ++ ++ @example: getting the pointer: ++ // get interrupt control data ++ pIntControl = GET_SDCONFIG_CMD_DATA(PSDCONFIG_SDIO_INT_CTRL_DATA,pConfig); ++ if (pIntControl->SlotIRQEnable) { ++ ... enable slot IRQ detection ++ } ++ ++ @see also: GET_SDCONFIG_CMD, GET_SDCONFIG_CMD_LEN ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define GET_SDCONFIG_CMD_DATA(type,pBuffer) ((type)((pBuffer)->pData)) ++#define IS_SDCONFIG_CMD_GET(pBuffer) ((pBuffer)->Cmd & SDCONFIG_FLAGS_DATA_GET) ++#define IS_SDCONFIG_CMD_PUT(pBuffer) ((pBuffer)->Cmd & SDCONFIG_FLAGS_DATA_PUT) ++ ++struct _SDDEVICE; ++struct _SDHCD; ++ ++typedef UINT8 SD_FUNCTION_FLAGS; ++#define SDFUNCTION_FLAG_REMOVING 0x01 ++ ++/* function driver registration structure */ ++typedef struct _SDFUNCTION { ++ CT_VERSION_CODE Version; /* version code of the SDIO stack */ ++ SDLIST SDList; /* internal use list*/ ++ PTEXT pName; /* name of registering driver */ ++ UINT MaxDevices; /* maximum number of devices supported by this function */ ++ UINT NumDevices; /* number of devices supported by this function */ ++ PSD_PNP_INFO pIds; /* null terminated table of supported devices*/ ++ BOOL (*pProbe)(struct _SDFUNCTION *pFunction, struct _SDDEVICE *pDevice);/* New device inserted */ ++ /* Device removed (NULL if not a hot-plug capable driver) */ ++ void (*pRemove)(struct _SDFUNCTION *pFunction, struct _SDDEVICE *pDevice); ++ SDIO_STATUS (*pSuspend)(struct _SDFUNCTION *pFunction, SDPOWER_STATE state); /* Device suspended */ ++ SDIO_STATUS (*pResume)(struct _SDFUNCTION *pFunction); /* Device woken up */ ++ /* Enable wake event */ ++ SDIO_STATUS (*pWake) (struct _SDFUNCTION *pFunction, SDPOWER_STATE state, BOOL enable); ++ PVOID pContext; /* function driver use data */ ++ OS_PNPDRIVER Driver; /* driver registration with base system */ ++ SDLIST DeviceList; /* the list of devices this driver is using*/ ++ OS_SIGNAL CleanupReqSig; /* wait for requests completion on cleanup (internal use) */ ++ SD_FUNCTION_FLAGS Flags; /* internal flags (internal use) */ ++}SDFUNCTION, *PSDFUNCTION; ++ ++typedef UINT8 HCD_EVENT; ++ ++ /* device info for SDIO functions */ ++typedef struct _SDIO_DEVICE_INFO { ++ UINT32 FunctionCISPtr; /* function's CIS ptr */ ++ UINT32 FunctionCSAPtr; /* function's CSA ptr */ ++ UINT16 FunctionMaxBlockSize; /* function's reported max block size */ ++}SDIO_DEVICE_INFO, *PSDIO_DEVICE_INFO; ++ ++ /* device info for SD/MMC card functions */ ++typedef struct _SDMMC_INFO{ ++ UINT8 Unused; /* reserved */ ++}SDMMC_INFO, *PSDMMC_INFO; ++ ++ /* union of SDIO function and device info */ ++typedef union _SDDEVICE_INFO { ++ SDIO_DEVICE_INFO AsSDIOInfo; ++ SDMMC_INFO AsSDMMCInfo; ++}SDDEVICE_INFO, *PSDDEVICE_INFO; ++ ++ ++typedef UINT8 SD_DEVICE_FLAGS; ++#define SDDEVICE_FLAG_REMOVING 0x01 ++ ++/* inserted device description, describes an inserted card */ ++typedef struct _SDDEVICE { ++ SDLIST SDList; /* internal use list*/ ++ SDLIST FuncListLink; /* internal use list */ ++ /* read/write request function */ ++ SDIO_STATUS (*pRequest)(struct _SDDEVICE *pDev, PSDREQUEST req); ++ /* get/set configuration */ ++ SDIO_STATUS (*pConfigure)(struct _SDDEVICE *pDev, PSDCONFIG config); ++ PSDREQUEST (*AllocRequest)(struct _SDDEVICE *pDev); /* allocate a request */ ++ void (*FreeRequest)(struct _SDDEVICE *pDev, PSDREQUEST pReq); /* free the request */ ++ void (*pIrqFunction)(PVOID pContext); /* interrupt routine, synchronous calls allowed */ ++ void (*pIrqAsyncFunction)(PVOID pContext); /* async IRQ function , asynch only calls */ ++ PVOID IrqContext; /* irq context */ ++ PVOID IrqAsyncContext; /* irq async context */ ++ PSDFUNCTION pFunction; /* function driver supporting this device */ ++ struct _SDHCD *pHcd; /* host controller this device is on (internal use) */ ++ SDDEVICE_INFO DeviceInfo; /* device info */ ++ SD_PNP_INFO pId[1]; /* id of this device */ ++ OS_PNPDEVICE Device; /* device registration with base system */ ++ SD_SLOT_CURRENT SlotCurrentAlloc; /* allocated slot current for this device/function (internal use) */ ++ SD_DEVICE_FLAGS Flags; /* internal use flags */ ++ CT_VERSION_CODE Version; /* version code of the bus driver */ ++}SDDEVICE, *PSDDEVICE; ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get SDIO Bus Driver Version Major number ++ ++ @function name: SDDEVICE_GET_VERSION_MAJOR ++ @prototype: INT SDDEVICE_GET_VERSION_MAJOR(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: integer value for the major version ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_GET_VERSION_MINOR ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_VERSION_MAJOR(pDev) (GET_SDIO_STACK_VERSION_MAJOR(pDev)) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get SDIO Bus Driver Version Minor number ++ ++ @function name: SDDEVICE_GET_VERSION_MINOR ++ @prototype: INT SDDEVICE_GET_VERSION_MINOR(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: integer value for the minor version ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_GET_VERSION_MAJOR ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_VERSION_MINOR(pDev) (GET_SDIO_STACK_VERSION_MINOR(pDev)) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Test the SDIO revision for greater than or equal to 1.10 ++ ++ @function name: SDDEVICE_IS_SDIO_REV_GTEQ_1_10 ++ @prototype: BOOL SDDEVICE_IS_SDIO_REV_GTEQ_1_10(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: TRUE if the revision is greater than or equal to 1.10 ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_IS_SD_REV_GTEQ_1_10 ++ @see also: SDDEVICE_IS_MMC_REV_GTEQ_4_0 ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_IS_SDIO_REV_GTEQ_1_10(pDev) ((pDev)->pHcd->CardProperties.SDIORevision >= SDIO_REVISION_1_10) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Test the SDIO revision for greater than or equal to 1.20 ++ ++ @function name: SDDEVICE_IS_SDIO_REV_GTEQ_1_20 ++ @prototype: BOOL SDDEVICE_IS_SDIO_REV_GTEQ_1_20(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: TRUE if the revision is greater than or equal to 1.20 ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_IS_SD_REV_GTEQ_1_10 ++ @see also: SDDEVICE_IS_SDIO_REV_GTEQ_1_10 ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_IS_SDIO_REV_GTEQ_1_20(pDev) ((pDev)->pHcd->CardProperties.SDIORevision >= SDIO_REVISION_1_20) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Test the SD revision for greater than or equal to 1.10 ++ ++ @function name: SDDEVICE_IS_SD_REV_GTEQ_1_10 ++ @prototype: BOOL SDDEVICE_IS_SD_REV_GTEQ_1_10(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: TRUE if the revision is greater than or equal to 1.10 ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_IS_SDIO_REV_GTEQ_1_10 ++ @see also: SDDEVICE_IS_MMC_REV_GTEQ_4_0 ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_IS_SD_REV_GTEQ_1_10(pDev) ((pDev)->pHcd->CardProperties.SD_MMC_Revision >= SD_REVISION_1_10) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Test the MMC revision for greater than or equal to 4.0 ++ ++ @function name: SDDEVICE_IS_MMC_REV_GTEQ_4_0 ++ @prototype: BOOL SDDEVICE_IS_MMC_REV_GTEQ_4_0(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: TRUE if the revision is greater than or equal to 4.0 ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_IS_SDIO_REV_GTEQ_1_10 ++ @see also: SDDEVICE_IS_SD_REV_GTEQ_1_10 ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_IS_MMC_REV_GTEQ_4_0(pDev) ((pDev)->pHcd->CardProperties.SD_MMC_Revision >= MMC_REVISION_4_0) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Test for write protect enabled ++ ++ @function name: SDDEVICE_IS_CARD_WP_ON ++ @prototype: BOOL SDDEVICE_IS_CARD_WP_ON(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: TRUE if device is write protected. ++ ++ @notes: Implemented as a macro. ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_IS_CARD_WP_ON(pDev) ((pDev)->pHcd->CardProperties.Flags & CARD_SD_WP) ++ ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the device's manufacturer specific ID ++ ++ @function name: SDDEVICE_GET_SDIO_MANFID ++ @prototype: UINT16 SDDEVICE_GET_SDIO_MANFID(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: function number ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_GET_SDIO_MANFCODE ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SDIO_MANFID(pDev) (pDev)->pId[0].SDIO_ManufacturerID ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the device's manufacturer code ++ ++ @function name: SDDEVICE_GET_SDIO_MANFCODE ++ @prototype: UINT16 SDDEVICE_GET_SDIO_MANFCODE(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: function number ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_GET_SDIO_MANFID ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SDIO_MANFCODE(pDev) (pDev)->pId[0].SDIO_ManufacturerCode ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the device's function number ++ ++ @function name: SDDEVICE_GET_SDIO_FUNCNO ++ @prototype: UINT8 SDDEVICE_GET_SDIO_FUNCNO(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: function number ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_GET_SDIO_FUNC_CLASS ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SDIO_FUNCNO(pDev) (pDev)->pId[0].SDIO_FunctionNo ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the functions's class ++ ++ @function name: SDDEVICE_GET_SDIO_FUNC_CLASS ++ @prototype: UINT8 SDDEVICE_GET_SDIO_FUNC_CLASS(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: class number ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_GET_SDIO_FUNCNO ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SDIO_FUNC_CLASS(pDev) (pDev)->pId[0].SDIO_FunctionClass ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the functions's Card Information Structure pointer ++ ++ @function name: SDDEVICE_GET_SDIO_FUNC_CISPTR ++ @prototype: UINT32 SDDEVICE_GET_SDIO_FUNC_CISPTR(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: CIS offset ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_GET_SDIO_FUNC_CSAPTR ++ @see also: SDDEVICE_GET_SDIO_COMMON_CISPTR ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SDIO_FUNC_CISPTR(pDev)(pDev)->DeviceInfo.AsSDIOInfo.FunctionCISPtr ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the functions's Code Stoarge Area pointer ++ ++ @function name: SDDEVICE_GET_SDIO_FUNC_CSAPTR ++ @prototype: UINT32 SDDEVICE_GET_SDIO_FUNC_CSAPTR(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: CSA offset ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_GET_SDIO_FUNC_CISPTR ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SDIO_FUNC_CSAPTR(pDev)(pDev)->DeviceInfo.AsSDIOInfo.FunctionCSAPtr ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the functions's maximum reported block size ++ ++ @function name: SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE ++ @prototype: UINT16 SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: block size ++ ++ @notes: Implemented as a macro. ++ ++ @see also: ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SDIO_FUNC_MAXBLKSIZE(pDev) (pDev)->DeviceInfo.AsSDIOInfo.FunctionMaxBlockSize ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the common Card Information Structure pointer ++ ++ @function name: SDDEVICE_GET_SDIO_COMMON_CISPTR ++ @prototype: UINT32 SDDEVICE_GET_SDIO_COMMON_CISPTR(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: Common CIS Address (in SDIO address space) ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_GET_SDIO_FUNC_CSAPTR ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SDIO_COMMON_CISPTR(pDev) (pDev)->pHcd->CardProperties.CommonCISPtr ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the card capabilities ++ ++ @function name: SDDEVICE_GET_SDIO_CARD_CAPS ++ @prototype: UINT8 SDDEVICE_GET_SDIO_CARD_CAPS(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: 8-bit card capabilities register ++ ++ @notes: Implemented as a macro. Refer to SDIO spec for decoding. ++ ++ @see also: SDDEVICE_GET_CARD_FLAGS ++ @see also: SDDEVICE_GET_SDIOCARD_CAPS ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SDIO_CARD_CAPS(pDev) (pDev)->pHcd->CardProperties.SDIOCaps ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the card flags ++ ++ @function name: SDDEVICE_GET_CARD_FLAGS ++ @prototype: CARD_INFO_FLAGS SDDEVICE_GET_CARD_FLAGS(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: flags ++ ++ @notes: Implemented as a macro. ++ ++ @example: Get card type: ++ CARD_INFO_FLAGS flags; ++ flags = SDDEVICE_GET_CARD_FLAGS(pDevice); ++ switch(GET_CARD_TYPE(flags)) { ++ case CARD_MMC: // Multi-media card ++ ... ++ case CARD_SD: // SD-Memory present ++ ... ++ case CARD_SDIO: // SDIO card present ++ ... ++ case CARD_COMBO: //SDIO card with SD ++ ... ++ } ++ if (flags & CARD_SD_WP) { ++ ...SD write protect on ++ } ++ ++ @see also: SDDEVICE_GET_SDIO_CARD_CAPS ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_CARD_FLAGS(pDev) (pDev)->pHcd->CardProperties.Flags ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the Relative Card Address register ++ ++ @function name: SDDEVICE_GET_CARD_RCA ++ @prototype: UINT16 SDDEVICE_GET_CARD_RCA(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: register address ++ ++ @notes: Implemented as a macro. Refer to SDIO spec for decoding. ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_CARD_RCA(pDev) (pDev)->pHcd->CardProperties.RCA ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get operational bus clock ++ ++ @function name: SDDEVICE_GET_OPER_CLOCK ++ @prototype: SD_BUSCLOCK_RATE SDDEVICE_GET_OPER_CLOCK(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: clock rate ++ ++ @notes: Implemented as a macro. Returns the current bus clock rate. ++ This may be lower than reported by the card due to Host Controller, ++ Bus driver, or power management limitations. ++ ++ @see also: SDDEVICE_GET_MAX_CLOCK ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_OPER_CLOCK(pDev) (pDev)->pHcd->CardProperties.OperBusClock ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get maximum bus clock ++ ++ @function name: SDDEVICE_GET_MAX_CLOCK ++ @prototype: SD_BUSCLOCK_RATE SDDEVICE_GET_MAX_CLOCK(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: clock rate ++ ++ @notes: To obtain the current maximum clock rate use SDDEVICE_GET_OPER_CLOCK(). ++ This rate my be lower than the host controllers maximum obtained using ++ SDDEVICE_GET_MAX_CLOCK(). ++ ++ @see also: SDDEVICE_GET_OPER_CLOCK ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_MAX_CLOCK(pDev) (pDev)->pHcd->MaxClockRate ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get operational maximum block length. ++ ++ @function name: SDDEVICE_GET_OPER_BLOCK_LEN ++ @prototype: UINT16 SDDEVICE_GET_OPER_BLOCK_LEN(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: block size in bytes ++ ++ @notes: Implemented as a macro. Returns the maximum current block length. ++ This may be lower than reported by the card due to Host Controller, ++ Bus driver, or power management limitations. ++ ++ @see also: SDDEVICE_GET_MAX_BLOCK_LEN ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_OPER_BLOCK_LEN(pDev) (pDev)->pHcd->CardProperties.OperBlockLenLimit ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get maximum block length. ++ ++ @function name: SDDEVICE_GET_MAX_BLOCK_LEN ++ @prototype: UINT16 SDDEVICE_GET_MAX_BLOCK_LEN(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: block size in bytes ++ ++ @notes: Implemented as a macro. Use SDDEVICE_GET_OPER_BLOCK_LEN to obtain ++ the current block length. ++ ++ @see also: SDDEVICE_GET_OPER_BLOCK_LEN ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_MAX_BLOCK_LEN(pDev) (pDev)->pHcd->MaxBytesPerBlock ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get operational maximum block count. ++ ++ @function name: SDDEVICE_GET_OPER_BLOCKS ++ @prototype: UINT16 SDDEVICE_GET_OPER_BLOCKS(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: maximum number of blocks per transaction. ++ ++ @notes: Implemented as a macro. Returns the maximum current block count. ++ This may be lower than reported by the card due to Host Controller, ++ Bus driver, or power management limitations. ++ ++ @see also: SDDEVICE_GET_MAX_BLOCK_LEN ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_OPER_BLOCKS(pDev) (pDev)->pHcd->CardProperties.OperBlockCountLimit ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get maximum block count. ++ ++ @function name: SDDEVICE_GET_MAX_BLOCKS ++ @prototype: UINT16 SDDEVICE_GET_MAX_BLOCKS(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: maximum number of blocks per transaction. ++ ++ @notes: Implemented as a macro. Use SDDEVICE_GET_OPER_BLOCKS to obtain ++ the current block count. ++ ++ @see also: SDDEVICE_GET_OPER_BLOCKS ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_MAX_BLOCKS(pDev) (pDev)->pHcd->MaxBlocksPerTrans ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get applied slot voltage ++ ++ @function name: SDDEVICE_GET_SLOT_VOLTAGE_MASK ++ @prototype: SLOT_VOLTAGE_MASK SDDEVICE_GET_SLOT_VOLTAGE_MASK(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: slot voltage mask ++ ++ @notes: This function returns the applied voltage on the slot. The voltage value is a ++ mask having the following values: ++ SLOT_POWER_3_3V ++ SLOT_POWER_3_0V ++ SLOT_POWER_2_8V ++ SLOT_POWER_2_0V ++ SLOT_POWER_1_8V ++ SLOT_POWER_1_6V ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SLOT_VOLTAGE_MASK(pDev) (pDev)->pHcd->CardProperties.CardVoltage ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the Card Specific Data Register. ++ ++ @function name: SDDEVICE_GET_CARDCSD ++ @prototype: PUINT8 SDDEVICE_GET_CARDCSD(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: UINT8 CardCSD[MAX_CARD_RESPONSE_BYTES] array of CSD data. ++ ++ @notes: Implemented as a macro. ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_CARDCSD(pDev) (pDev)->pHcd->CardProperties.CardCSD ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the bus mode flags ++ ++ @function name: SDDEVICE_GET_BUSMODE_FLAGS ++ @prototype: SD_BUSMODE_FLAGS SDDEVICE_GET_BUSMODE_FLAGS(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: ++ ++ @notes: Implemented as a macro. This function returns the raw bus mode flags. This ++ is useful for function drivers that wish to override the bus clock without ++ modifying the current bus mode. ++ ++ @see also: SDDEVICE_GET_BUSWIDTH ++ @see also: SDCONFIG_BUS_MODE_CTRL ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_BUSMODE_FLAGS(pDev) (pDev)->pHcd->CardProperties.BusMode ++ ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the bus width. ++ ++ @function name: SDDEVICE_GET_BUSWIDTH ++ @prototype: UINT8 SDDEVICE_GET_BUSWIDTH(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: bus width: SDCONFIG_BUS_WIDTH_SPI, SDCONFIG_BUS_WIDTH_1_BIT, SDCONFIG_BUS_WIDTH_4_BIT ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_IS_BUSMODE_SPI ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_BUSWIDTH(pDev) SDCONFIG_GET_BUSWIDTH((pDev)->pHcd->CardProperties.BusMode) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Is bus in SPI mode. ++ ++ @function name: SDDEVICE_IS_BUSMODE_SPI ++ @prototype: BOOL SDDEVICE_IS_BUSMODE_SPI(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: TRUE, SPI mode. ++ ++ @notes: Implemented as a macro. ++ ++ @see also: SDDEVICE_GET_BUSWIDTH ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_IS_BUSMODE_SPI(pDev) (SDDEVICE_GET_BUSWIDTH(pDev) == SDCONFIG_BUS_WIDTH_SPI) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Send a request to a device. ++ ++ @function name: SDDEVICE_CALL_REQUEST_FUNC ++ @prototype: SDIO_STATUS SDDEVICE_CALL_REQUEST_FUNC(PSDDEVICE pDevice, PSDREQUEST pRequest) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ @input: pRequest - the request to be sent ++ ++ @output: none ++ ++ @return: SDIO_STATUS ++ ++ @notes: Sends a request to the specified device. If the request is successfully sent, then ++ the response flags can be checked to detemine the result of the request. ++ ++ @example: Example of sending a request to a device: ++ PSDREQUEST pReq = NULL; ++ //allocate a request ++ pReq = SDDeviceAllocRequest(pDevice); ++ if (NULL == pReq) { ++ return SDIO_STATUS_NO_RESOURCES; ++ } ++ //initialize the request ++ SDLIB_SetupCMD52Request(FuncNo, Address, Write, *pData, pReq); ++ //send the request to the target ++ status = SDDEVICE_CALL_REQUEST_FUNC(pDevice,pReq); ++ if (!SDIO_SUCCESS(status)) { ++ break; ++ } ++ //check the request response (based on the request type) ++ if (SD_R5_GET_RESP_FLAGS(pReq->Response) & SD_R5_ERRORS) { ++ ... ++ } ++ if (!Write) { ++ // store the byte ++ *pData = SD_R5_GET_READ_DATA(pReq->Response); ++ } ++ //free the request ++ SDDeviceFreeRequest(pDevice,pReq); ++ ... ++ ++ @see also: SDDeviceAllocRequest ++ @see also: SDDEVICE_CALL_CONFIG_FUNC ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_CALL_REQUEST_FUNC(pDev,pReq) (pDev)->pRequest((pDev),(pReq)) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Send configuration to a device. ++ ++ @function name: SDDEVICE_CALL_CONFIG_FUNC ++ @prototype: SDIO_STATUS SDDEVICE_CALL_CONFIG_FUNC(PSDDEVICE pDevice, PSDCONFIG pConfigure) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ @input: pConfigure - configuration request ++ ++ @output: none ++ ++ @return: SDIO_STATUS ++ ++ @notes: Sends a configuration request to the specified device. ++ ++ @example: Example of sending a request to a device: ++ SDCONFIG configHdr; ++ SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData; ++ fData.EnableFlags = SDCONFIG_ENABLE_FUNC; ++ fData.TimeOut = 500; ++ SET_SDCONFIG_CMD_INFO(&configHdr, SDCONFIG_FUNC_ENABLE_DISABLE, fData, sizeof(fData)); ++ return SDDEVICE_CALL_CONFIG_FUNC(pDevice, &configHdr); ++ ++ @see also: SDLIB_IssueConfig ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_CALL_CONFIG_FUNC(pDev,pCfg) (pDev)->pConfigure((pDev),(pCfg)) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Allocate a request structure. ++ ++ @function name: SDDeviceAllocRequest ++ @prototype: PSDREQUEST SDDeviceAllocRequest(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: request pointer or NULL if not available. ++ ++ @notes: This function must not be called in a non-schedulable (interrupts off) context. ++ Allocating memory on some OSes may block. ++ ++ @see also: SDDEVICE_CALL_REQUEST_FUNC ++ @see also: SDDeviceFreeRequest ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDeviceAllocRequest(pDev) (pDev)->AllocRequest((pDev)) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Free a request structure. ++ ++ @function name: SDDeviceFreeRequest ++ @prototype: void SDDeviceFreeRequest(PSDDEVICE pDevice, PSDREQUEST pRequest) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ @input: pRequest - request allocated by SDDeviceAllocRequest(). ++ ++ @output: none ++ ++ @return: none ++ ++ @notes: This function must not be called in a non-schedulable (interrupts off) context. ++ Freeing memory on some OSes may block. ++ ++ @see also: SDDEVICE_CALL_REQUEST_FUNC ++ @see also: SDDeviceAllocRequest ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDeviceFreeRequest(pDev,pReq) (pDev)->FreeRequest((pDev),pReq) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Register an interrupt handler for a device. ++ ++ @function name: SDDEVICE_SET_IRQ_HANDLER ++ @prototype: void SDDEVICE_SET_IRQ_HANDLER(PSDDEVICE pDevice, ++ void (*pIrqFunction)(PVOID pContext), ++ PVOID pContext) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ @input: pIrqFunction - the interrupt function to execute. ++ @input: pContext - context value passed into interrupt routine. ++ ++ @output: none ++ ++ @return: none ++ ++ @notes: The registered routine will be called upon each card interrupt. ++ The interrupt function should acknowledge the interrupt when it is ++ ready to handle more interrupts using: ++ SDLIB_IssueConfig(pDevice, SDCONFIG_FUNC_ACK_IRQ, NULL, 0); ++ The interrupt handler can perform synchronous request calls. ++ ++ @see also: SDDEVICE_SET_ASYNC_IRQ_HANDLER ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_SET_IRQ_HANDLER(pDev,pFn,pContext) \ ++{ \ ++ (pDev)->pIrqFunction = (pFn); \ ++ (pDev)->IrqContext = (PVOID)(pContext); \ ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Register an asynchronous interrupt handler for a device. ++ ++ @function name: SDDEVICE_SET_ASYNC_IRQ_HANDLER ++ @prototype: void SDDEVICE_SET_ASYNC_IRQ_HANDLER(PSDDEVICE pDevice, ++ void (*pIrqAsyncFunction)(PVOID pContext), ++ PVOID pContext) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ @input: pIrqAsyncFunction - the interrupt function to execute. ++ @input: pContext - context value passed into interrupt routine. ++ ++ @output: none ++ ++ @return: none ++ ++ @notes: The registered routine will be called upon each card interrupt. ++ The interrupt function should acknowledge the interrupt when it is ++ ready to handle more interrupts using: ++ SDLIB_IssueConfig(pDevice, SDCONFIG_FUNC_ACK_IRQ, NULL, 0); ++ The interrupt handler can not perform any synchronous request calls. ++ Using this call provides a faster interrupt dispatch, but limits all ++ requests to asynchronous mode. ++ ++ @see also: SDDEVICE_SET_IRQ_HANDLER ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_SET_ASYNC_IRQ_HANDLER(pDev,pFn,pContext) \ ++{ \ ++ (pDev)->pIrqAsyncFunction = (pFn); \ ++ (pDev)->IrqAsyncContext = (PVOID)(pContext); \ ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the SDIO capabilities rgeister. ++ ++ @function name: SDDEVICE_GET_SDIOCARD_CAPS ++ @prototype: UINT8 SDDEVICE_GET_SDIOCARD_CAPS(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: SD capabilities ++ ++ @notes: See SD specification for decoding of these capabilities. ++ ++ @see also: SDDEVICE_GET_SDIO_CARD_CAPS ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SDIOCARD_CAPS(pDev) (pDev)->pHcd->CardProperties.SDIOCaps ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get HCD driver name ++ ++ @function name: SDDEVICE_GET_HCDNAME ++ @prototype: PTEXT SDDEVICE_GET_HCDNAME(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the target device for this request ++ ++ @output: none ++ ++ @return: pointer to a string containing the name of the underlying HCD ++ ++ @notes: Implemented as a macro. ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_HCDNAME(pDev) (pDev)->pHcd->pName ++ ++ ++#define SDDEVICE_CALL_IRQ_HANDLER(pDev) (pDev)->pIrqFunction((pDev)->IrqContext) ++#define SDDEVICE_CALL_IRQ_ASYNC_HANDLER(pDev) (pDev)->pIrqAsyncFunction((pDev)->IrqAsyncContext) ++ ++ ++#define SDDEVICE_SET_SDIO_FUNCNO(pDev,Num) (pDev)->pId[0].SDIO_FunctionNo = (Num) ++#define SDDEVICE_IS_CARD_REMOVED(pDev) ((pDev)->pHcd->CardProperties.CardState & \ ++ CARD_STATE_REMOVED) ++ ++ ++typedef enum _SDHCD_IRQ_PROC_STATE { ++ SDHCD_IDLE = 0, ++ SDHCD_IRQ_PENDING = 1, ++ SDHCD_IRQ_HELPER = 2 ++}SDHCD_IRQ_PROC_STATE, *PSDHCD_IRQ_PROC_STATE; ++ ++/* host controller bus driver registration structure */ ++typedef struct _SDHCD { ++ CT_VERSION_CODE Version; /* version code of the SDIO stack */ ++ SDLIST SDList; /* internal use list*/ ++ PTEXT pName; /* name of registering host/slot driver */ ++ UINT32 Attributes; /* attributes of host controller */ ++ UINT16 MaxBytesPerBlock; /* max bytes per block */ ++ UINT16 MaxBlocksPerTrans; /* max blocks per transaction */ ++ SD_SLOT_CURRENT MaxSlotCurrent; /* max current per slot in milli-amps */ ++ UINT8 SlotNumber; /* sequential slot number for this HCD, set by bus driver */ ++ SD_BUSCLOCK_RATE MaxClockRate; /* max clock rate in hz */ ++ SLOT_VOLTAGE_MASK SlotVoltageCaps; /* slot voltage capabilities */ ++ SLOT_VOLTAGE_MASK SlotVoltagePreferred; /* preferred slot voltage */ ++ PVOID pContext; /* host controller driver use data */ ++ SDIO_STATUS (*pRequest)(struct _SDHCD *pHcd); ++ /* get/set configuration */ ++ SDIO_STATUS (*pConfigure)(struct _SDHCD *pHcd, PSDCONFIG pConfig); ++ /* everything below this line is for bus driver use */ ++ OS_SEMAPHORE ConfigureOpsSem; /* semaphore to make specific configure ops atomic, internal use */ ++ OS_CRITICALSECTION HcdCritSection; /* critical section to protect hcd data structures (internal use) */ ++ SDREQUESTQUEUE RequestQueue; /* request queue, internal use */ ++ PSDREQUEST pCurrentRequest; /* current request we are working on */ ++ CARD_PROPERTIES CardProperties; /* properties for the currently inserted card*/ ++ OSKERNEL_HELPER SDIOIrqHelper; /* synch IRQ helper, internal use */ ++ SDDEVICE *pPseudoDev; /* pseudo device used for initialization (internal use) */ ++ UINT8 PendingHelperIrqs; /* IRQ helper pending IRQs */ ++ UINT8 PendingIrqAcks; /* pending IRQ acks from function drivers */ ++ UINT8 IrqsEnabled; /* current irq enabled mask */ ++ SDHCD_IRQ_PROC_STATE IrqProcState; /* irq processing state */ ++ POS_DEVICE pDevice; /* device registration with base system */ ++ SD_SLOT_CURRENT SlotCurrentAllocated; /* slot current allocated (internal use ) */ ++ ATOMIC_FLAGS HcdFlags; /* HCD Flags */ ++#define HCD_REQUEST_CALL_BIT 0 ++#define HCD_IRQ_NO_PEND_CHECK 1 /* HCD flag to bypass interrupt pending register ++ check, typically done on single function cards */ ++ SDREQUESTQUEUE CompletedRequestQueue; /* completed request queue, internal use */ ++ PSDDMA_DESCRIPTION pDmaDescription; /* description of HCD's DMA capabilities */ ++ POS_MODULE pModule; /* OS-specific module information */ ++ INT Recursion; /* recursion level */ ++ PVOID Reserved1; ++ PVOID Reserved2; ++}SDHCD, *PSDHCD; ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get a pointer to the HCD's DMA description ++ ++ @function name: SDGET_DMA_DESCRIPTION ++ @prototype: PSDDMA_DESCRIPTION SDGET_DMA_DESCRIPTION(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - device structure ++ ++ @return: PSDDMA_DESCRIPTION or NULL if no DMA support ++ ++ @notes: Implemented as a macro. ++ ++ @example: getting the current request: ++ PSDDMA_DESCRIPTION pDmaDescrp = SDGET_DMA_DESCRIPTION(pDevice); ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDGET_DMA_DESCRIPTION(pDevice) (pDevice)->pHcd->pDmaDescription ++ ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get the logical slot number the device is assigned to. ++ ++ @function name: SDDEVICE_GET_SLOT_NUMBER ++ @prototype: UINT8 SDDEVICE_GET_SLOT_NUMBER(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - device structure ++ ++ @return: unsigned number representing the slot number ++ ++ @notes: Implemented as a macro. This value is unique for each physical slot in the system ++ and assigned by the bus driver. Devices on a multi-function card will share the same ++ slot number. ++ ++ @example: getting the slot number: ++ UINT8 thisSlot = SDDEVICE_GET_SLOT_NUMBER(pDevice); ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDDEVICE_GET_SLOT_NUMBER(pDevice) (pDevice)->pHcd->SlotNumber ++ ++/* for function use */ ++SDIO_STATUS SDIO_RegisterFunction(PSDFUNCTION pFunction); ++SDIO_STATUS SDIO_UnregisterFunction(PSDFUNCTION pFunction); ++ ++#include "sdio_hcd_defs.h" ++#endif /* __SDIO_BUSDRIVER_H___ */ +diff --git a/include/linux/sdio/sdio_hcd_defs.h b/include/linux/sdio/sdio_hcd_defs.h +new file mode 100644 +index 0000000..1782469 +--- /dev/null ++++ b/include/linux/sdio/sdio_hcd_defs.h +@@ -0,0 +1,219 @@ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++@file: sdio_hcd_defs.h ++ ++@abstract: host controller driver definitions ++ ++@notice: Copyright (c), 2005-2006 Atheros Communications, Inc. ++ ++ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation; ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Portions of this code were developed with information supplied from the ++ * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: ++ * ++ * The following conditions apply to the release of the SD simplified specification (�Simplified ++ * Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete ++ * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided ++ * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified ++ * Specification may require a license from the SD Card Association or other third parties. ++ * Disclaimers: ++ * The information contained in the Simplified Specification is presented only as a standard ++ * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any ++ * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for ++ * any damages, any infringements of patents or other right of the SD Card Association or any third ++ * parties, which may result from its use. No license is granted by implication, estoppel or otherwise ++ * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall ++ * be construed as an obligation by the SD Card Association to disclose or distribute any technical ++ * information, know-how or other confidential information to any third party. ++ * ++ * ++ * The initial developers of the original code are Seung Yi and Paul Lever ++ * ++ * sdio@atheros.com ++ * ++ * ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#ifndef __SDIO_HCD_DEFS_H___ ++#define __SDIO_HCD_DEFS_H___ ++ ++ /* write protect switch position data */ ++typedef UINT8 SDCONFIG_WP_VALUE; ++ ++ /* HC commands */ ++#define SDCONFIG_SEND_INIT_CLOCKS (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_PUT | 1) ++#define SDCONFIG_SDIO_INT_CTRL (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_PUT | 2) ++#define SDCONFIG_SDIO_REARM_INT (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_NONE | 3) ++#define SDCONFIG_BUS_MODE_CTRL (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_BOTH | 4) ++#define SDCONFIG_POWER_CTRL (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_PUT | 5) ++#define SDCONFIG_GET_WP (SDCONFIG_FLAGS_HC_CONFIG | SDCONFIG_FLAGS_DATA_GET | 6) ++ ++ /* slot init clocks control */ ++typedef struct _SDCONFIG_INIT_CLOCKS_DATA { ++ UINT16 NumberOfClocks; /* number of clocks to issue in the current bus mode*/ ++}SDCONFIG_INIT_CLOCKS_DATA, *PSDCONFIG_INIT_CLOCKS_DATA; ++ ++/* slot power control */ ++typedef struct _SDCONFIG_POWER_CTRL_DATA { ++ BOOL SlotPowerEnable; /* turn on/off slot power */ ++ SLOT_VOLTAGE_MASK SlotPowerVoltageMask; /* slot power voltage mask */ ++}SDCONFIG_POWER_CTRL_DATA, *PSDCONFIG_POWER_CTRL_DATA; ++ ++typedef UINT8 SDIO_IRQ_MODE_FLAGS; ++/* SDIO Interrupt control */ ++typedef struct _SDCONFIG_SDIO_INT_CTRL_DATA { ++ BOOL SlotIRQEnable; /* turn on/off Slot IRQ detection */ ++ SDIO_IRQ_MODE_FLAGS IRQDetectMode; /* slot IRQ detect mode , only valid if Enabled = TRUE */ ++#define IRQ_DETECT_RAW 0x00 ++#define IRQ_DETECT_MULTI_BLK 0x01 ++#define IRQ_DETECT_4_BIT 0x02 ++#define IRQ_DETECT_1_BIT 0x04 ++#define IRQ_DETECT_SPI 0x08 ++}SDCONFIG_SDIO_INT_CTRL_DATA, *PSDCONFIG_SDIO_INT_CTRL_DATA; ++ ++/* card insert */ ++#define EVENT_HCD_ATTACH 1 ++/* card remove */ ++#define EVENT_HCD_DETACH 2 ++/* card slot interrupt */ ++#define EVENT_HCD_SDIO_IRQ_PENDING 3 ++/* transfer done */ ++#define EVENT_HCD_TRANSFER_DONE 4 ++/* (internal use only) */ ++#define EVENT_HCD_CD_POLLING 5 ++/* NOP */ ++#define EVENT_HCD_NOP 0 ++ ++/* attrib_flags */ ++#define SDHCD_ATTRIB_SUPPORTS_POWER 0x0001 /* host controller driver supports power managment */ ++#define SDHCD_ATTRIB_BUS_1BIT 0x0002 /* SD Native 1 - bit mode */ ++#define SDHCD_ATTRIB_BUS_4BIT 0x0004 /* SD Native 4 - bit mode */ ++#define SDHCD_ATTRIB_BUS_SPI 0x0008 /* SPI mode capable */ ++#define SDHCD_ATTRIB_READ_WAIT 0x0010 /* read wait supported (SD-only) */ ++#define SDHCD_ATTRIB_MULTI_BLK_IRQ 0x0020 /* interrupts between multi-block capable (SD-only) */ ++#define SDHCD_ATTRIB_BUS_MMC8BIT 0x0040 /* MMC 8-bit */ ++#define SDHCD_ATTRIB_SLOT_POLLING 0x0080 /* requires slot polling for Card Detect */ ++#define SDHCD_ATTRIB_POWER_SWITCH 0x0100 /* host has power switch control, must be set if SPI ++ mode can be switched to 1 or 4 bit mode */ ++#define SDHCD_ATTRIB_NO_SPI_CRC 0x0200 /* when in SPI mode, ++ host wants to run without SPI CRC */ ++#define SDHCD_ATTRIB_AUTO_CMD12 0x0400 /* host controller supports auto CMD12 */ ++#define SDHCD_ATTRIB_NO_4BIT_IRQ 0x0800 /* host controller does not support 4 bit IRQ mode*/ ++#define SDHCD_ATTRIB_RAW_MODE 0x1000 /* host controller is a raw mode hcd*/ ++#define SDHCD_ATTRIB_SD_HIGH_SPEED 0x2000 /* host controller supports SD high speed interface */ ++#define SDHCD_ATTRIB_MMC_HIGH_SPEED 0x4000 /* host controller supports MMC high speed interface */ ++ ++#define IS_CARD_PRESENT(pHcd) ((pHcd)->CardProperties.Flags & CARD_TYPE_MASK) ++#define SET_CURRENT_REQUEST(pHcd,Req) (pHcd)->pCurrentRequest = (Req) ++#define IS_HCD_RAW(pHcd) ((pHcd)->Attributes & SDHCD_ATTRIB_RAW_MODE) ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get a pointer to the current bus request for a host controller ++ ++ @function name: GET_CURRENT_REQUEST ++ @prototype: PSDREQUEST GET_CURRENT_REQUEST (PSDHCD pHcd) ++ @category: HD_Reference ++ ++ @input: pHcd - host structure ++ ++ @return: current SD/SDIO bus request being worked on ++ ++ @notes: Implemented as a macro. This macro returns the current SD request that is ++ being worked on. ++ ++ @example: getting the current request: ++ pReq = GET_CURRENT_REQUEST(&pHct->Hcd); ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define GET_CURRENT_REQUEST(pHcd) (pHcd)->pCurrentRequest ++#define GET_CURRENT_BUS_WIDTH(pHcd) SDCONFIG_GET_BUSWIDTH((pHcd)->CardProperties.BusMode) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Get host controller's current operational bus clock ++ ++ @function name: SDHCD_GET_OPER_CLOCK ++ @prototype: SD_BUSCLOCK_RATE SDHCD_GET_OPER_CLOCK(PSDHCD pHcd) ++ @category: HD_Reference ++ ++ @input: pHcd - the registered host structure ++ ++ @output: none ++ ++ @return: clock rate ++ ++ @notes: Implemented as a macro. Returns the current bus clock rate. ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDHCD_GET_OPER_CLOCK(pHcd) (pHcd)->CardProperties.OperBusClock ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Is host controller operating in SPI mode ++ ++ @function name: IS_HCD_BUS_MODE_SPI ++ @prototype: BOOL IS_HCD_BUS_MODE_SPI (PSDHCD pHcd) ++ @category: HD_Reference ++ ++ @input: pHcd - host structure ++ ++ @return: TRUE if in SPI mode ++ ++ @notes: Implemented as a macro. Host controllers that operate in SPI mode ++ dynamically can use this macro to check for SPI operation. ++ ++ @example: testing for SPI mode: ++ if (IS_HCD_BUS_MODE_SPI(&pHct->Hcd)) { ++ .. in spi mode ++ } ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define IS_HCD_BUS_MODE_SPI(pHcd) (GET_CURRENT_BUS_WIDTH(pHcd) == SDCONFIG_BUS_WIDTH_SPI) ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Is host controller using SPI in non-CRC mode ++ ++ @function name: IS_HCD_BUS_MODE_SPI_NO_CRC ++ @prototype: BOOL IS_HCD_BUS_MODE_SPI_NO_CRC(PSDHCD pHcd) ++ @category: HD_Reference ++ ++ @input: pHcd - host structure ++ ++ @return: TRUE if CRC mode is off ++ ++ @notes: Implemented as a macro. SPI-capable cards and systems can operate in ++ non-CRC protected mode. In this mode the host controller should ignore ++ CRC fields and/or disable CRC generation when issuing command or data ++ packets. This option is useful for software based SPI mode where CRC ++ should be turned off in order to reduce processing overhead. ++ ++ @example: test for non-CRC SPI mode: ++ if (IS_HCD_BUS_MODE_SPI_NO_CRC(&pHct->Hcd)) { ++ .. disable CRC checking in hardware. ++ } ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define IS_HCD_BUS_MODE_SPI_NO_CRC(pHcd) ((pHcd)->CardProperties.BusMode & \ ++ SDCONFIG_BUS_MODE_SPI_NO_CRC) ++ ++typedef UINT8 SDHCD_RESPONSE_CHECK_MODE; ++/* have SDIO core check the response token and see if it is okay to continue with ++ * the data portion */ ++#define SDHCD_CHECK_DATA_TRANS_OK 0x01 ++/* have SDIO core check the SPI token received */ ++#define SDHCD_CHECK_SPI_TOKEN 0x02 ++ ++/* prototypes */ ++/* for HCD use */ ++SDIO_STATUS SDIO_RegisterHostController(PSDHCD pHcd); ++SDIO_STATUS SDIO_UnregisterHostController(PSDHCD pHcd); ++SDIO_STATUS SDIO_HandleHcdEvent(PSDHCD pHcd, HCD_EVENT Event); ++SDIO_STATUS SDIO_CheckResponse(PSDHCD pHcd, PSDREQUEST pReq, SDHCD_RESPONSE_CHECK_MODE CheckMode); ++SDIO_STATUS SDIO_BusAddOSDevice(PSDDMA_DESCRIPTION pDma, POS_PNPDRIVER pDriver, POS_PNPDEVICE pDevice); ++void SDIO_BusRemoveOSDevice(POS_PNPDRIVER pDriver, POS_PNPDEVICE pDevice); ++ ++#endif /* __SDIO_BUSDRIVER_H___ */ +diff --git a/include/linux/sdio/sdio_lib.h b/include/linux/sdio/sdio_lib.h +new file mode 100644 +index 0000000..ac0cbd7 +--- /dev/null ++++ b/include/linux/sdio/sdio_lib.h +@@ -0,0 +1,270 @@ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++@file: sdio_lib.h ++ ++@abstract: SDIO Library include ++ ++#notes: ++ ++@notice: Copyright (c), 2004-2006 Atheros Communications, Inc. ++ ++ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation; ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Portions of this code were developed with information supplied from the ++ * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: ++ * ++ * The following conditions apply to the release of the SD simplified specification (�Simplified ++ * Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete ++ * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided ++ * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified ++ * Specification may require a license from the SD Card Association or other third parties. ++ * Disclaimers: ++ * The information contained in the Simplified Specification is presented only as a standard ++ * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any ++ * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for ++ * any damages, any infringements of patents or other right of the SD Card Association or any third ++ * parties, which may result from its use. No license is granted by implication, estoppel or otherwise ++ * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall ++ * be construed as an obligation by the SD Card Association to disclose or distribute any technical ++ * information, know-how or other confidential information to any third party. ++ * ++ * ++ * The initial developers of the original code are Seung Yi and Paul Lever ++ * ++ * sdio@atheros.com ++ * ++ * ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#ifndef __SDIO_LIB_H___ ++#define __SDIO_LIB_H___ ++ ++#ifdef UNDER_CE ++#include "wince\sdio_lib_wince.h" ++#endif /* WINCE */ ++ ++#define CMD52_DO_READ FALSE ++#define CMD52_DO_WRITE TRUE ++ ++ /* read/write macros to any function */ ++#define Cmd52WriteByteFunc(pDev,Func,Address,pValue) \ ++ SDLIB_IssueCMD52((pDev),(Func),(Address),(pValue),1,CMD52_DO_WRITE) ++#define Cmd52ReadByteFunc(pDev,Func,Address,pValue) \ ++ SDLIB_IssueCMD52((pDev),(Func),(Address),pValue,1,CMD52_DO_READ) ++#define Cmd52ReadMultipleFunc(pDev,Func, Address, pBuf,length) \ ++ SDLIB_IssueCMD52((pDev),(Func),(Address),(pBuf),(length),CMD52_DO_READ) ++ ++ /* macros to access common registers */ ++#define Cmd52WriteByteCommon(pDev, Address, pValue) \ ++ Cmd52WriteByteFunc((pDev),0,(Address),(pValue)) ++#define Cmd52ReadByteCommon(pDev, Address, pValue) \ ++ Cmd52ReadByteFunc((pDev),0,(Address),(pValue)) ++#define Cmd52ReadMultipleCommon(pDev, Address, pBuf,length) \ ++ Cmd52ReadMultipleFunc((pDev),0,(Address),(pBuf),(length)) ++ ++#define SDLIB_SetupCMD52RequestAsync(f,a,w,wd,pR) \ ++{ \ ++ SDLIB_SetupCMD52Request((f),(a),(w),(wd),(pR)); \ ++ (pR)->Flags |= SDREQ_FLAGS_TRANS_ASYNC; \ ++} ++ ++ /* a message block */ ++typedef struct _SDMESSAGE_BLOCK { ++ SDLIST SDList; /* list entry */ ++ INT MessageLength; /* number of bytes in this message */ ++ UINT8 MessageStart[1]; /* message start */ ++}SDMESSAGE_BLOCK, *PSDMESSAGE_BLOCK; ++ ++ /* message queue */ ++typedef struct _SDMESSAGE_QUEUE { ++ SDLIST MessageList; /* message list */ ++ OS_CRITICALSECTION MessageCritSection; /* message semaphore */ ++ SDLIST FreeMessageList; /* free message list */ ++ INT MaxMessageLength; /* max message block length */ ++}SDMESSAGE_QUEUE, *PSDMESSAGE_QUEUE; ++ ++/* internal library prototypes that can be proxied */ ++SDIO_STATUS _SDLIB_IssueCMD52(PSDDEVICE pDevice, ++ UINT8 FuncNo, ++ UINT32 Address, ++ PUINT8 pData, ++ INT ByteCount, ++ BOOL Write); ++SDIO_STATUS _SDLIB_FindTuple(PSDDEVICE pDevice, ++ UINT8 Tuple, ++ UINT32 *pTupleScanAddress, ++ PUINT8 pBuffer, ++ UINT8 *pLength); ++SDIO_STATUS _SDLIB_IssueConfig(PSDDEVICE pDevice, ++ SDCONFIG_COMMAND Command, ++ PVOID pData, ++ INT Length); ++void _SDLIB_PrintBuffer(PUCHAR pBuffer, INT Length,PTEXT pDescription); ++void _SDLIB_SetupCMD52Request(UINT8 FuncNo, ++ UINT32 Address, ++ BOOL Write, ++ UINT8 WriteData, ++ PSDREQUEST pRequest); ++SDIO_STATUS _SDLIB_SetFunctionBlockSize(PSDDEVICE pDevice, ++ UINT16 BlockSize); ++ ++SDIO_STATUS _SDLIB_GetDefaultOpCurrent(PSDDEVICE pDevice, ++ SD_SLOT_CURRENT *pOpCurrent); ++PSDMESSAGE_QUEUE _CreateMessageQueue(INT MaxMessages, INT MaxMessageLength); ++void _DeleteMessageQueue(PSDMESSAGE_QUEUE pQueue); ++SDIO_STATUS _PostMessage(PSDMESSAGE_QUEUE pQueue, PVOID pMessage, INT MessageLength); ++SDIO_STATUS _GetMessage(PSDMESSAGE_QUEUE pQueue, PVOID pData, INT *pBufferLength); ++ ++#ifdef CTSYSTEM_NO_FUNCTION_PROXIES ++ /* OS port requires no proxy functions, use methods directly from the library */ ++#define SDLIB_IssueCMD52 _SDLIB_IssueCMD52 ++#define SDLIB_SetupCMD52Request _SDLIB_SetupCMD52Request ++#define SDLIB_FindTuple _SDLIB_FindTuple ++#define SDLIB_IssueConfig _SDLIB_IssueConfig ++#define SDLIB_SetFunctionBlockSize _SDLIB_SetFunctionBlockSize ++#define SDLIB_GetDefaultOpCurrent _SDLIB_GetDefaultOpCurrent ++#define SDLIB_CreateMessageQueue _CreateMessageQueue ++#define SDLIB_DeleteMessageQueue _DeleteMessageQueue ++#define SDLIB_PostMessage _PostMessage ++#define SDLIB_GetMessage _GetMessage ++#define SDLIB_PrintBuffer _SDLIB_PrintBuffer ++#else ++ ++/* proxied versions */ ++SDIO_STATUS SDLIB_IssueCMD52(PSDDEVICE pDevice, ++ UINT8 FuncNo, ++ UINT32 Address, ++ PUINT8 pData, ++ INT ByteCount, ++ BOOL Write); ++ ++void SDLIB_SetupCMD52Request(UINT8 FuncNo, ++ UINT32 Address, ++ BOOL Write, ++ UINT8 WriteData, ++ PSDREQUEST pRequest); ++ ++SDIO_STATUS SDLIB_FindTuple(PSDDEVICE pDevice, ++ UINT8 Tuple, ++ UINT32 *pTupleScanAddress, ++ PUINT8 pBuffer, ++ UINT8 *pLength); ++ ++SDIO_STATUS SDLIB_IssueConfig(PSDDEVICE pDevice, ++ SDCONFIG_COMMAND Command, ++ PVOID pData, ++ INT Length); ++ ++SDIO_STATUS SDLIB_SetFunctionBlockSize(PSDDEVICE pDevice, ++ UINT16 BlockSize); ++ ++void SDLIB_PrintBuffer(PUCHAR pBuffer, INT Length,PTEXT pDescription); ++ ++SDIO_STATUS SDLIB_GetDefaultOpCurrent(PSDDEVICE pDevice, SD_SLOT_CURRENT *pOpCurrent); ++ ++PSDMESSAGE_QUEUE SDLIB_CreateMessageQueue(INT MaxMessages, INT MaxMessageLength); ++ ++void SDLIB_DeleteMessageQueue(PSDMESSAGE_QUEUE pQueue); ++ ++SDIO_STATUS SDLIB_PostMessage(PSDMESSAGE_QUEUE pQueue, PVOID pMessage, INT MessageLength); ++ ++SDIO_STATUS SDLIB_GetMessage(PSDMESSAGE_QUEUE pQueue, PVOID pData, INT *pBufferLength); ++#endif /* CTSYSTEM_NO_FUNCTION_PROXIES */ ++ ++ ++SDIO_STATUS SDLIB_OSCreateHelper(POSKERNEL_HELPER pHelper, ++ PHELPER_FUNCTION pFunction, ++ PVOID pContext); ++ ++void SDLIB_OSDeleteHelper(POSKERNEL_HELPER pHelper); ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Check message queue is empty ++ ++ @function name: SDLIB_IsQueueEmpty ++ @prototype: BOOL SDLIB_IsQueueEmpty(PSDMESSAGE_QUEUE pQueue) ++ @category: Support_Reference ++ ++ @input: pQueue - message queue to check ++ ++ @return: TRUE if empty else false ++ ++ @see also: SDLIB_CreateMessageQueue ++ ++ @example: Check message queue : ++ if (SDLIB_IsQueueEmpty(pInstance->pQueue)) { ++ .. message queue is empty ++ } +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static INLINE BOOL SDLIB_IsQueueEmpty(PSDMESSAGE_QUEUE pQueue) { ++ return SDLIST_IS_EMPTY(&pQueue->MessageList); ++} ++ ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Issue an I/O abort request ++ ++ @function name: SDLIB_IssueIOAbort ++ @prototype: SDIO_STATUS SDLIB_IssueIOAbort(PSDDEVICE pDevice) ++ @category: PD_Reference ++ ++ @input: pDevice - the device that is the target of this request ++ ++ @return: SDIO_STATUS ++ ++ @notes: This procedure can be called to issue an I/O abort request to an I/O function. ++ This procedure cannot be used to abort a data (block) transfer already in progress. ++ It is intended to be used when a data (block) transfer completes with an error and only if ++ the I/O function requires an abort action. Some I/O functions may automatically ++ recover from such failures and not require this action. This function issues ++ the abort command synchronously and can potentially block. ++ If an async request is required, you must allocate a request and use ++ SDLIB_SetupIOAbortAsync() to prepare the request. ++ ++ @example: Issuing I/O Abort synchronously : ++ .. check status from last block operation: ++ if (status == SDIO_STATUS_BUS_READ_TIMEOUT) { ++ .. on failure, issue I/O abort ++ status2 = SDLIB_IssueIOAbort(pDevice); ++ } ++ Issuing I/O Abort asynchronously: ++ ... allocate a request ++ ... setup the request: ++ SDLIB_SetupIOAbortAsync(pDevice,pReq); ++ pReq->pCompletion = myIOAbortCompletion; ++ pReq->pCompleteContext = pDevice; ++ status = SDDEVICE_CALL_REQUEST_FUNC(pDevice,pReq); ++ ++ @see also: SDLIB_SetupIOAbortAsync +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++static INLINE SDIO_STATUS SDLIB_IssueIOAbort(PSDDEVICE pDevice) { ++ UINT8 value = SDDEVICE_GET_SDIO_FUNCNO(pDevice); ++ return Cmd52WriteByteCommon(pDevice,0x06,&value); ++} ++ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++ @function: Setup an I/O abort request for async operation ++ ++ @function name: SDLIB_SetupIOAbortAsync ++ @prototype: SDLIB_SetupIOAbortAsync(PSDDEVICE pDevice, PSDREQUEST pRequest) ++ @category: PD_Reference ++ ++ @input: pDevice - the device that is the target of this request ++ pRequest - the request to set up ++ ++ @see also: SDLIB_IssueIOAbort ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#define SDLIB_SetupIOAbortAsync(pDevice, pReq) \ ++ SDLIB_SetupCMD52RequestAsync(0,0x06,TRUE,SDDEVICE_GET_SDIO_FUNCNO(pDevice),(pReq)) ++ ++ ++#endif /* __SDIO_LIB_H___*/ +diff --git a/include/linux/sdio/sdlist.h b/include/linux/sdio/sdlist.h +new file mode 100644 +index 0000000..dc35e1c +--- /dev/null ++++ b/include/linux/sdio/sdlist.h +@@ -0,0 +1,141 @@ ++/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++@file: sdlist.h ++ ++@abstract: OS independent list functions ++ ++@notice: Copyright (c), 2004-2006 Atheros Communications, Inc. ++ ++ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation; ++ * ++ * Software distributed under the License is distributed on an "AS ++ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++ * implied. See the License for the specific language governing ++ * rights and limitations under the License. ++ * ++ * Portions of this code were developed with information supplied from the ++ * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: ++ * ++ * The following conditions apply to the release of the SD simplified specification (�Simplified ++ * Specification�) by the SD Card Association. The Simplified Specification is a subset of the complete ++ * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided ++ * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified ++ * Specification may require a license from the SD Card Association or other third parties. ++ * Disclaimers: ++ * The information contained in the Simplified Specification is presented only as a standard ++ * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any ++ * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for ++ * any damages, any infringements of patents or other right of the SD Card Association or any third ++ * parties, which may result from its use. No license is granted by implication, estoppel or otherwise ++ * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall ++ * be construed as an obligation by the SD Card Association to disclose or distribute any technical ++ * information, know-how or other confidential information to any third party. ++ * ++ * ++ * The initial developers of the original code are Seung Yi and Paul Lever ++ * ++ * sdio@atheros.com ++ * ++ * ++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ ++#ifndef __SDLIST_H___ ++#define __SDLIST_H___ ++ ++/* list functions */ ++/* pointers for the list */ ++typedef struct _SDLIST { ++ struct _SDLIST *pPrev; ++ struct _SDLIST *pNext; ++}SDLIST, *PSDLIST; ++/* ++ * SDLIST_INIT , circular list ++*/ ++#define SDLIST_INIT(pList)\ ++ {(pList)->pPrev = pList; (pList)->pNext = pList;} ++#define SDLIST_INIT_DECLARE(List)\ ++ SDLIST List = {&List, &List} ++ ++ ++#define SDLIST_IS_EMPTY(pList) (((pList)->pPrev == (pList)) && ((pList)->pNext == (pList))) ++#define SDLIST_GET_ITEM_AT_HEAD(pList) (pList)->pNext ++#define SDLIST_GET_ITEM_AT_TAIL(pList) (pList)->pPrev ++/* ++ * SDITERATE_OVER_LIST pStart is the list, pTemp is a temp list member ++ * NOT: do not use this function if the items in the list are deleted inside the ++ * iteration loop ++*/ ++#define SDITERATE_OVER_LIST(pStart, pTemp) \ ++ for((pTemp) =(pStart)->pNext; pTemp != (pStart); (pTemp) = (pTemp)->pNext) ++ ++ ++/* safe iterate macro that allows the item to be removed from the list ++ * the iteration continues to the next item in the list ++ */ ++#define SDITERATE_OVER_LIST_ALLOW_REMOVE(pStart,pItem,st,offset) \ ++{ \ ++ PSDLIST pTemp; \ ++ pTemp = (pStart)->pNext; \ ++ while (pTemp != (pStart)) { \ ++ (pItem) = CONTAINING_STRUCT(pTemp,st,offset); \ ++ pTemp = pTemp->pNext; \ ++ ++#define SDITERATE_END }} ++ ++/* ++ * SDListInsertTail - insert pAdd to the end of the list ++*/ ++static INLINE PSDLIST SDListInsertTail(PSDLIST pList, PSDLIST pAdd) { ++ /* this assert catches when an item is added twice */ ++ DBG_ASSERT(pAdd->pNext != pList); ++ /* insert at tail */ ++ pAdd->pPrev = pList->pPrev; ++ pAdd->pNext = pList; ++ pList->pPrev->pNext = pAdd; ++ pList->pPrev = pAdd; ++ return pAdd; ++} ++ ++/* ++ * SDListInsertHead - insert pAdd into the head of the list ++*/ ++static INLINE PSDLIST SDListInsertHead(PSDLIST pList, PSDLIST pAdd) { ++ /* this assert catches when an item is added twice */ ++ DBG_ASSERT(pAdd->pPrev != pList); ++ /* insert at head */ ++ pAdd->pPrev = pList; ++ pAdd->pNext = pList->pNext; ++ pList->pNext->pPrev = pAdd; ++ pList->pNext = pAdd; ++ return pAdd; ++} ++ ++#define SDListAdd(pList,pItem) SDListInsertHead((pList),(pItem)) ++/* ++ * SDListRemove - remove pDel from list ++*/ ++static INLINE PSDLIST SDListRemove(PSDLIST pDel) { ++ pDel->pNext->pPrev = pDel->pPrev; ++ pDel->pPrev->pNext = pDel->pNext; ++ /* point back to itself just to be safe, incase remove is called again */ ++ pDel->pNext = pDel; ++ pDel->pPrev = pDel; ++ return pDel; ++} ++ ++/* ++ * SDListRemoveItemFromHead - get a list item from the head ++*/ ++static INLINE PSDLIST SDListRemoveItemFromHead(PSDLIST pList) { ++ PSDLIST pItem = NULL; ++ if (pList->pNext != pList) { ++ pItem = pList->pNext; ++ /* remove the first item from head */ ++ SDListRemove(pItem); ++ } ++ return pItem; ++} ++#endif /* __SDLIST_H___ */ +-- +1.5.6.3 + |