--- drivers/sdio/hcd/pci_std/Makefile | 4 drivers/sdio/hcd/pci_std/sdio_hcd_os.c | 569 +++++++ drivers/sdio/hcd/stdhost/Makefile | 7 drivers/sdio/hcd/stdhost/sdio_std_hcd.c | 1663 ++++++++++++++++++++++ drivers/sdio/hcd/stdhost/sdio_std_hcd.h | 330 ++++ drivers/sdio/hcd/stdhost/sdio_std_hcd_linux.h | 132 + drivers/sdio/hcd/stdhost/sdio_std_hcd_linux_lib.h | 79 + drivers/sdio/hcd/stdhost/sdio_std_hcd_os.c | 826 ++++++++++ 8 files changed, 3610 insertions(+) Index: linux-2.6.22/drivers/sdio/hcd/pci_std/Makefile =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/hcd/pci_std/Makefile 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,4 @@ +obj-m += sdio_pcistd_hcd.o + +sdio_pcistd_hcd-objs := sdio_hcd_os.o ../stdhost/sdio_std_hcd.o ../stdhost/sdio_std_hcd_os.o + Index: linux-2.6.22/drivers/sdio/hcd/pci_std/sdio_hcd_os.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/hcd/pci_std/sdio_hcd_os.c 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,569 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_hcd_os.c + +@abstract: Standard PCI SDIO Host Controller Driver + +#notes: includes module load and unload functions + +@notice: Copyright (c), 2006 Atheros Communications, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +#include +#include +#include "../stdhost/sdio_std_hcd_linux.h" +#include "../stdhost/sdio_std_hcd.h" +#include "../stdhost/sdio_std_hcd_linux_lib.h" +#include + +#define DESCRIPTION "SDIO Standard PCI HCD" +#define AUTHOR "Atheros Communications, Inc." + +static INT ForceSDMA = 0; +module_param(ForceSDMA, int, 0444); +MODULE_PARM_DESC(ForceSDMA, "Force Host controller to use simple DMA if available"); + +#define PCI_CLASS_SYSTEM_SDIO 0x0805 + +/* the config space slot number and start for SD host */ +#define PCI_CONFIG_SLOT 0x40 +#define GET_SLOT_COUNT(config)\ + ((((config)>>4)& 0x7) +1) +#define GET_SLOT_FIRST(config)\ + ((config) & 0x7) +#define PCI_CONFIG_CLASS 0x09 +#define PCI_SD_DMA_SUPPORTED 0x01 + +#define SDIO_PCI_BAR_MAPPED 0x01 + +typedef enum _SDHCD_TYPE { + TYPE_CLASS, /* standard class device */ + TYPE_PCIELLEN, /* Tokyo Electron PCI Ellen card */ +}SDHCD_TYPE, *PSDHCD_TYPE; + + /* PCI devices supported */ +static const struct pci_device_id pci_ids [] = { + /* Ellen I */ + { + .vendor = 0x1679, .device = 0x3000, + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, + }, + { /* Ellen II Standard Host */ + .vendor = 0x10ee, .device = 0x0300, + .subvendor = 0x10b5, .subdevice = 0x9030, + }, + { /* Standard Host */ + PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_SDIO << 8, 0xFFFFFF00), + }, + { /* end: all zeroes */ } +}; + +MODULE_DEVICE_TABLE (pci, pci_ids); + +#define SDHCD_PCI_IRQ_HOOKED 0x01 + +static SYSTEM_STATUS Probe(struct pci_dev *pPCIdevice, const struct pci_device_id *pId); +static void Remove(struct pci_dev *pPCIdevice); +static irqreturn_t hcd_sdio_irq(int irq, void *context, struct pt_regs * r); +static SDIO_STATUS InitEllen(struct pci_dev *pPCIdevice); + +/* tell PCI bus driver about us */ +static struct pci_driver sdio_pci_driver = { + .name = "sdio_pcistd_hcd", + .id_table = pci_ids, + + .probe = Probe, + .remove = Remove, + +#ifdef CONFIG_PM + .suspend = NULL, + .resume = NULL, +#endif +}; + + + /* Advanced DMA description */ +SDDMA_DESCRIPTION HcdADMADefaults = { + .Flags = SDDMA_DESCRIPTION_FLAG_SGDMA, + .MaxDescriptors = SDHCD_MAX_ADMA_DESCRIPTOR, + .MaxBytesPerDescriptor = SDHCD_MAX_ADMA_LENGTH, + .Mask = SDHCD_ADMA_ADDRESS_MASK, + .AddressAlignment = SDHCD_ADMA_ALIGNMENT, + .LengthAlignment = SDHCD_ADMA_LENGTH_ALIGNMENT, +}; + + /* simple DMA descriptions */ +SDDMA_DESCRIPTION HcdSDMADefaults = { + .Flags = SDDMA_DESCRIPTION_FLAG_DMA, + .MaxDescriptors = 1, + .MaxBytesPerDescriptor = SDHCD_MAX_SDMA_LENGTH, + .Mask = SDHCD_SDMA_ADDRESS_MASK, + .AddressAlignment = SDHCD_SDMA_ALIGNMENT, + .LengthAlignment = SDHCD_SDMA_LENGTH_ALIGNMENT, +}; + +/* + * MapAddress - sets up the address for a given BAR +*/ +static int MapAddress(struct pci_dev *pPCIdevice, char *pName, UINT8 bar, PSDHCD_MEMORY pAddress) +{ + if (pci_resource_flags(pPCIdevice, bar) & PCI_BASE_ADDRESS_SPACE ) { + DBG_PRINT(SDDBG_WARN, ("SDIO STDPCI HCD: MapAddress, port I/O not supported\n")); + return -ENOMEM; + } + pAddress->Raw = pci_resource_start(pPCIdevice, bar); + pAddress->Length = pci_resource_len(pPCIdevice, bar); + if (!request_mem_region (pAddress->Raw, pAddress->Length, pName)) { + DBG_PRINT(SDDBG_WARN, ("SDIO STDPCI HCD: MapAddress - memory in use: 0x%X(0x%X)\n", + (UINT)pAddress->Raw, (UINT)pAddress->Length)); + return -EBUSY; + } + pAddress->pMapped = ioremap_nocache(pAddress->Raw, pAddress->Length); + if (pAddress->pMapped == NULL) { + DBG_PRINT(SDDBG_WARN, ("SDIO STDPCI HCD: MapAddress - unable to map memory\n")); + /* cleanup region */ + release_mem_region (pAddress->Raw, pAddress->Length); + return -EFAULT; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO STDPCI HCD: MapAddress - mapped memory: 0x%X(0x%X) to 0x%X\n", + (UINT)pAddress->Raw, (UINT)pAddress->Length, (UINT)pAddress->pMapped)); + return 0; +} + + +/* + * UnmapAddress - unmaps the address +*/ +static void UnmapAddress(PSDHCD_MEMORY pAddress) { + iounmap(pAddress->pMapped); + release_mem_region(pAddress->Raw, pAddress->Length); + pAddress->pMapped = NULL; +} + +/* + * CleanupPCIResources - cleanup PCI resources +*/ +static void CleanupPCIResources(struct pci_dev *pPCIdevice, + PSDHCD_INSTANCE pHcInstance) +{ + if (pHcInstance->OsSpecific.InitMask & SDIO_PCI_BAR_MAPPED) { + UnmapAddress(&pHcInstance->OsSpecific.Address); + pHcInstance->OsSpecific.InitMask &= ~SDIO_PCI_BAR_MAPPED; + } +} + +SDIO_STATUS SetUpOneSlotController(PSDHCD_CORE_CONTEXT pStdCore, + struct pci_dev *pPCIdevice, + UINT SlotNumber, + int BAR, + BOOL AllowDMA, + SDHCD_TYPE Type) +{ + SDIO_STATUS status = SDIO_STATUS_ERROR; + TEXT nameBuffer[SDHCD_MAX_DEVICE_NAME]; + PSDHCD_INSTANCE pHcInstance = NULL; + UINT startFlags = 0; + + do { + /* setup the name */ + snprintf(nameBuffer, SDHCD_MAX_DEVICE_NAME, "pcistd_%X:%i", + (UINT)pPCIdevice,SlotNumber); + + /* create the instance */ + pHcInstance = CreateStdHcdInstance(&pPCIdevice->dev, + SlotNumber, + nameBuffer); + + if (NULL == pHcInstance) { + status = SDIO_STATUS_NO_RESOURCES; + break; + } + /* map the memory BAR */ + status = MapAddress(pPCIdevice, + pHcInstance->Hcd.pName, + (UINT8)BAR, + &pHcInstance->OsSpecific.Address); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, + ("SDIO STDPCI HCD: Probe - failed to map device memory address %s 0x%X, status %d\n", + pHcInstance->Hcd.pName, (UINT)pci_resource_start(pPCIdevice, BAR), + status)); + break; + } + pHcInstance->OsSpecific.InitMask |= SDIO_PCI_BAR_MAPPED; + + pHcInstance->pRegs = pHcInstance->OsSpecific.Address.pMapped; + + if (!AllowDMA) { + startFlags |= START_HCD_FLAGS_FORCE_NO_DMA; + } + + if (ForceSDMA) { + startFlags |= START_HCD_FLAGS_FORCE_SDMA; + } + + /* startup this instance */ + status = AddStdHcdInstance(pStdCore, + pHcInstance, + startFlags, + NULL, + &HcdSDMADefaults, + &HcdADMADefaults); + + } while (FALSE); + + if (!SDIO_SUCCESS(status)) { + if (pHcInstance != NULL) { + CleanupPCIResources(pPCIdevice,pHcInstance); + DeleteStdHcdInstance(pHcInstance); + } + } else { + DBG_PRINT(SDDBG_TRACE, ("SDIO STDPCI Probe - HCD:0x%X ready! \n",(UINT)pHcInstance)); + } + + return status; +} + + +static void CleanUpHcdCore(struct pci_dev *pPCIdevice, PSDHCD_CORE_CONTEXT pStdCore) +{ + PSDHCD_INSTANCE pHcInstance; + + /* make sure interrupts are disabled */ + if (pStdCore->CoreReserved1 & SDHCD_PCI_IRQ_HOOKED) { + pStdCore->CoreReserved1 &= ~SDHCD_PCI_IRQ_HOOKED; + free_irq(pPCIdevice->irq, pStdCore); + } + + /* remove all hcd instances associated with this PCI device */ + while (1) { + pHcInstance = RemoveStdHcdInstance(pStdCore); + if (NULL == pHcInstance) { + /* no more instances */ + break; + } + DBG_PRINT(SDDBG_TRACE, (" SDIO STDPCI HCD: Remove - removed HC Instance:0x%X, HCD:0x%X\n", + (UINT)pHcInstance, (UINT)&pHcInstance->Hcd)); + /* hcd is now removed, we can clean it up */ + CleanupPCIResources(pPCIdevice,pHcInstance); + DeleteStdHcdInstance(pHcInstance); + } + + DeleteStdHostCore(pStdCore); +} + +/* + * Probe - probe to setup our device, if present +*/ +static SYSTEM_STATUS Probe(struct pci_dev *pPCIdevice, const struct pci_device_id *pId) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + int ii; + int count; + int firstBar; + int controllers = 0; + UINT8 config; + SDHCD_TYPE type = TYPE_CLASS; + BOOL dmaSupported = FALSE; + PSDHCD_CORE_CONTEXT pStdCore = NULL; + SYSTEM_STATUS err = 0; + + DBG_PRINT(SDDBG_TRACE, ("SDIO STDPCI HCD: Probe - probing for new device\n")); + if (NULL == pId) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STDPCI HCD: Probe - no device\n")); + return -EINVAL; + } + + if (pci_enable_device(pPCIdevice) < 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STDPCI HCD: Probe - failed to enable device\n")); + return -ENODEV; + } + + if ((pId->vendor == pci_ids[0].vendor) && (pId->device == pci_ids[0].device)) { + type = TYPE_PCIELLEN; + DBG_PRINT(SDDBG_TRACE, ("SDIO STDPCI HCD: Probe - found PCI Ellen type\n")); + } + + do { + + pStdCore = CreateStdHostCore(pPCIdevice); + + if (NULL == pStdCore) { + err = -ENOMEM; + break; + } + /* get the number of slots supported and the initial BAR for it */ + pci_read_config_byte(pPCIdevice, PCI_CONFIG_SLOT, &config); + count = GET_SLOT_COUNT(config); + firstBar = GET_SLOT_FIRST(config); + + if (type == TYPE_PCIELLEN) { + /* move the first bar to the right start place, the original ellen card used a PLX + * bridge chip which uses the first BAR for control registers */ + firstBar = 2; + status = InitEllen(pPCIdevice); + if (!SDIO_SUCCESS(status)) { + err = -ENODEV; + break; + } + } + + if (count > 0) { + DBG_PRINT(SDDBG_TRACE, ("SDIO STDPCI: Probe - slot count: %d, first BAR: %d\n", count, firstBar)); + } else { + DBG_PRINT(SDDBG_ERROR, ("SDIO STDPCI: Probe - no slots defined, first BAR: %d\n", firstBar)); + //pci_disable_device(pPCIdevice); + err = -ENODEV; + break; + } + /* see if bus mastering DMA is supported */ + pci_read_config_byte(pPCIdevice, PCI_CONFIG_CLASS, &config); + if (config & PCI_SD_DMA_SUPPORTED) { + dmaSupported = TRUE; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO STDPCI: Probe - DMA %s enabled in config space: %d\n", + (dmaSupported)? "is": "not", dmaSupported)); + + + /* setup an hcd instance for each bar that we have */ + for(ii = 0; ii < count; ii++, firstBar++) { + status = SetUpOneSlotController(pStdCore, + pPCIdevice, /* pci device instance */ + ii, /* std host slot number */ + firstBar, /* pci BAR for the registers */ + dmaSupported, /* PCI enabled DMA */ + type /* specific PCI card type */ + ); + if (SDIO_SUCCESS(status)) { + controllers++; + } + } + + if (0 == controllers) { + /* if none were created, error */ + err = -ENODEV; + break; + } + + /* enable the single PCI controller interrupt + Interrupts can be called from this point on */ + err = request_irq(pPCIdevice->irq, hcd_sdio_irq, SA_SHIRQ, + "stdhcdpci", pStdCore); + + if (err < 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STDPCI - probe, unable to map interrupt \n")); + break; + } + + pStdCore->CoreReserved1 |= SDHCD_PCI_IRQ_HOOKED; + + /* startup the hosts..., this will enable interrupts for card detect */ + status = StartStdHostCore(pStdCore); + + if (!SDIO_SUCCESS(status)) { + err = -ENODEV; + break; + } + + } while (FALSE); + + if (err < 0) { + if (pStdCore != NULL) { + CleanUpHcdCore(pPCIdevice,pStdCore); + } + } + return err; +} + +/* Remove - remove device + * perform the undo of the Probe +*/ +static void Remove(struct pci_dev *pPCIdevice) +{ + PSDHCD_CORE_CONTEXT pStdCore; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO STDPCI HCD: Remove - removing device\n")); + + pStdCore = GetStdHostCore(pPCIdevice); + + if (NULL == pStdCore) { + DBG_ASSERT(FALSE); + return; + } + + CleanUpHcdCore(pPCIdevice, pStdCore); + + /* DO NOT CALL : pci_disable_device(pPCIdevice); this destroys the bus mastering settings + * on the PCI device, on re-load DMA transfers will fail ***/ + + DBG_PRINT(SDDBG_TRACE, ("-SDIO STDPCI HCD: Remove\n")); + return; +} + +/* SDIO interrupt request */ +static irqreturn_t hcd_sdio_irq(int irq, void *context, struct pt_regs * r) +{ + irqreturn_t retStat; + + /* call shared handling ISR in case this is a mult-slot controller using 1 PCI IRQ. + * if this was not a mult-slot controller or each controller has it's own system + * interrupt, we could call HcdSDInterrupt((PSDHCD_INSTANCE)context)) instead */ + if (HandleSharedStdHostInterrupt((PSDHCD_CORE_CONTEXT)context)) { + retStat = IRQ_HANDLED; + } else { + retStat = IRQ_NONE; + } + + return retStat; +} + +/* + * module init +*/ +static int __init sdio_pci_hcd_init(void) { + SYSTEM_STATUS err; + + REL_PRINT(SDDBG_TRACE, ("+SDIO STDPCI HCD: loading....\n")); + InitStdHostLib(); + + /* register with the PCI bus driver */ + err = pci_module_init(&sdio_pci_driver); + if (err < 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STDPCI HCD: failed to register with system PCI bus driver, %d\n", + err)); + } + DBG_PRINT(SDDBG_TRACE, ("-SDIO STDPCI HCD \n")); + return err; +} + +/* + * module cleanup +*/ +static void __exit sdio_pci_hcd_cleanup(void) { + REL_PRINT(SDDBG_TRACE, ("+SDIO STDPCI HCD: unloaded\n")); + pci_unregister_driver(&sdio_pci_driver); + DeinitStdHostLib(); + DBG_PRINT(SDDBG_TRACE, ("-SDIO STDPCI HCD: leave sdio_pci_hcd_cleanup\n")); +} + +/* PLX 9030 control registers */ +#define INTCSR 0x4C +#define INTCSR_LINTi1ENABLE (1 << 0) +#define INTCSR_LINTi1STATUS (1 << 2) +#define INTCSR_LINTi2ENABLE (1 << 3) +#define INTCSR_LINTi2STATUS (1 << 5) +#define INTCSR_PCIINTENABLE (1 << 6) + +#define GPIOCTRL 0x54 +#define GPIO8_PIN_DIRECTION (1 << 25) +#define GPIO8_DATA_MASK (1 << 26) +#define GPIO3_PIN_SELECT (1 << 9) +#define GPIO3_PIN_DIRECTION (1 << 10) +#define GPIO3_DATA_MASK (1 << 11) +#define GPIO2_PIN_SELECT (1 << 6) +#define GPIO2_PIN_DIRECTION (1 << 7) +#define GPIO2_DATA_MASK (1 << 8) +#define GPIO4_PIN_SELECT (1 << 12) +#define GPIO4_PIN_DIRECTION (1 << 13) +#define GPIO4_DATA_MASK (1 << 14) + +#define GPIO_CONTROL(pDevice, on, GpioMask) \ +{ \ + UINT32 temp; \ + temp = READ_CONTROL_REG32((pDevice),GPIOCTRL); \ + if (on) temp |= (GpioMask); else temp &= ~(GpioMask); \ + WRITE_CONTROL_REG32((pDevice),GPIOCTRL, temp); \ +} + +static SDIO_STATUS InitEllen(struct pci_dev *pPCIdevice) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + SDHCD_MEMORY controlRegs; + UINT32 temp; + BOOL mapped = FALSE; + + do { + /* map the slots control register BAR */ + status = MapAddress(pPCIdevice, + "EllenPCI", + (UINT8)0, + &controlRegs); + + if (!SDIO_SUCCESS(status)) { + break; + } + + mapped = TRUE; + + temp = _READ_WORD_REG((((UINT32)controlRegs.pMapped) + INTCSR)); + + DBG_PRINT(SDDBG_TRACE, ("SDIO STDPCI HCD: InitEllen Init:INTCSR - 0x%X\n", (UINT)temp)); + + temp |= INTCSR_LINTi1ENABLE | INTCSR_LINTi2ENABLE | INTCSR_PCIINTENABLE; + + /* enable local to PCI interrupts */ + _WRITE_WORD_REG((((UINT32)controlRegs.pMapped) + INTCSR), (UINT16)temp); + + DBG_PRINT(SDDBG_TRACE, ("SDIO STDPCI HCD: InitEllen Wrote:INTCSR - 0x%X\n", (UINT)temp)); + + /* + temp = READ_CONTROL_REG32((pHcInstance),GPIOCTRL); + temp &= ~(GPIO3_PIN_SELECT | GPIO2_PIN_SELECT | GPIO4_PIN_SELECT); + temp |= (GPIO8_PIN_DIRECTION | GPIO3_PIN_DIRECTION | GPIO2_PIN_DIRECTION | GPIO4_PIN_DIRECTION); + WRITE_CONTROL_REG32((pHcInstance),GPIOCTRL, temp); + DBG_PRINT(SDDBG_TRACE, ("SDIO STDPCI HCD: InitEllen GPIOCTRL - 0x%X\n", (UINT)temp)); + */ + + TRACE_SIGNAL_DATA_WRITE(pHcInstance, FALSE); + TRACE_SIGNAL_DATA_READ(pHcInstance, FALSE); + TRACE_SIGNAL_DATA_ISR(pHcInstance, FALSE); + TRACE_SIGNAL_DATA_IOCOMP(pHcInstance, FALSE); + + } while (FALSE); + + if (mapped) { + UnmapAddress(&controlRegs); + } + return status; +} + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DESCRIPTION); +MODULE_AUTHOR(AUTHOR); + +module_init(sdio_pci_hcd_init); +module_exit(sdio_pci_hcd_cleanup); Index: linux-2.6.22/drivers/sdio/hcd/stdhost/Makefile =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/hcd/stdhost/Makefile 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,7 @@ +# +# SDIO standard host controller makefile +# +obj-m += sdio_std_hcd.o + +sdio_std_hcd-objs := sdio_std_hcd.o sdio_std_hcd_os.o + Index: linux-2.6.22/drivers/sdio/hcd/stdhost/sdio_std_hcd.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/hcd/stdhost/sdio_std_hcd.c 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,1663 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_std_hcd.c + +@abstract: SDIO standard host controller implementation + +#notes: OS independent code + +@notice: Copyright (c), 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 o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#include "sdio_std_hcd.h" + +#define CLOCK_ON TRUE +#define CLOCK_OFF FALSE + +SDIO_STATUS SetPowerLevel(PSDHCD_INSTANCE pHcInstance, BOOL On, SLOT_VOLTAGE_MASK Level); +SDIO_STATUS ProcessCommandDone(PSDHCD_INSTANCE pHcInstance, PSDREQUEST pReq, BOOL FromIsr); + + /* clock divisor table */ +SD_CLOCK_TBL_ENTRY SDClockDivisorTable[SD_CLOCK_MAX_ENTRIES] = +{ /* clock rate divisor, divisor setting */ + {1, 0x0000}, + {2, 0x0100}, + {4, 0x0200}, + {8, 0x0400}, + {16,0x0800}, + {32,0x1000}, + {64,0x2000}, + {128,0x4000}, + {256,0x8000}, +}; + + /* register polling change macro */ +#define WAIT_REGISTER32_CHANGE(pHcInstance, pStatus, reg,mask,cmp,timout) \ + {\ + if (!WaitRegisterBitsChange((pHcInstance), \ + (pStatus), \ + (reg), \ + (mask), \ + (cmp), \ + (timout))) { \ + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST - Reg Change Timeout : 0x%X src:%s, line:%d \n",\ + (reg),__FILE__, __LINE__)); \ + } \ + } + +#define WAIT_REGISTER32_CHANGE_OR(pHcInstance, pStatus, reg,mask,ormask,timout) \ + {\ + if (!WaitRegisterBitsChangeOR((pHcInstance), \ + (pStatus), \ + (reg), \ + (mask), \ + (ormask), \ + (timout))) { \ + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST - Reg Change Timeout : 0x%X src:%s, line:%d \n",\ + (reg),__FILE__, __LINE__)); \ + } \ + } + + + /* command data ready polling macro */ +#define WAIT_FOR_DAT_CMD_DAT_READY(pHcInstance, pStatus) \ + WAIT_REGISTER32_CHANGE(pHcInstance, \ + pStatus, \ + HOST_REG_PRESENT_STATE,(HOST_REG_PRESENT_STATE_BUFFER_COMMAND_INHIBIT_DAT | \ + HOST_REG_PRESENT_STATE_BUFFER_COMMAND_INHIBIT_CMD), \ + 0, pHcInstance->PresentStateWaitLimit) + + /* function to wait for a register bit(s) change */ +static BOOL WaitRegisterBitsChange(PSDHCD_INSTANCE pHcInstance, + SDIO_STATUS *pStatus, + UINT32 Reg, + UINT32 Mask, + UINT32 CompareMask, + UINT32 Count) +{ + while (Count) { + + if ((READ_HOST_REG32(pHcInstance, Reg) & Mask) == CompareMask) { + break; + } + + Count--; + } + + if (0 == Count) { + if (pStatus != NULL) { + *pStatus = SDIO_STATUS_ERROR; + } + return FALSE; + } + + if (pStatus != NULL) { + *pStatus = SDIO_STATUS_SUCCESS; + } + + return TRUE; +} + +static BOOL WaitRegisterBitsChangeOR(PSDHCD_INSTANCE pHcInstance, + SDIO_STATUS *pStatus, + UINT32 Reg, + UINT32 Mask, + UINT32 OrMask, + UINT32 Count) +{ + while (Count) { + + if ((READ_HOST_REG32(pHcInstance, Reg) & Mask) & OrMask) { + break; + } + + Count--; + } + + if (0 == Count) { + if (pStatus != NULL) { + *pStatus = SDIO_STATUS_ERROR; + } + return FALSE; + } + + if (pStatus != NULL) { + *pStatus = SDIO_STATUS_SUCCESS; + } + + return TRUE; +} + + /* reset command data line state machines */ +void _DoResetCmdDatLine(PSDHCD_INSTANCE pHcInstance) +{ /* issue reset */ + WRITE_HOST_REG32(pHcInstance, HOST_REG_SW_RESET, + (HOST_REG_SW_RST_CMD_LINE | HOST_REG_SW_RST_DAT_LINE)); + /* wait for bits to clear */ + WAIT_REGISTER32_CHANGE(pHcInstance, + NULL, + HOST_REG_SW_RESET, + HOST_REG_SW_RST_CMD_LINE | HOST_REG_SW_RST_DAT_LINE, + 0, + pHcInstance->ResetWaitLimit); +} + +#define ResetCmdDatLine(pHc) \ +{ \ + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST - **** reseting cmd data at line:%d \n",\ + __LINE__)); \ + _DoResetCmdDatLine((pHc)); \ +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + GetResponseData - get the response data + Input: pHcInstance - device context + pReq - the request + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void GetResponseData(PSDHCD_INSTANCE pHcInstance, PSDREQUEST pReq) +{ + INT dwordCount; + INT byteCount; + UINT32 readBuffer[4]; + INT ii; + + if (GET_SDREQ_RESP_TYPE(pReq->Flags) == SDREQ_FLAGS_NO_RESP) { + return; + } + + + byteCount = SD_DEFAULT_RESPONSE_BYTES; + if (GET_SDREQ_RESP_TYPE(pReq->Flags) == SDREQ_FLAGS_RESP_R2) { + byteCount = SD_R2_RESPONSE_BYTES; + } + dwordCount = (byteCount + 3) / 4; + + /* move data into read buffer */ + for (ii = 0; ii < dwordCount; ii++) { + readBuffer[ii] = READ_HOST_REG32(pHcInstance, HOST_REG_RESPONSE+(ii*4)); + } + + /* handle normal SD/MMC responses */ + + /* the standard host strips the CRC for all responses and puts them in + * a nice linear order */ + memcpy(&pReq->Response[1],readBuffer,byteCount); + + if (DBG_GET_DEBUG_LEVEL() >= STD_HOST_TRACE_REQUESTS) { + if (GET_SDREQ_RESP_TYPE(pReq->Flags) == SDREQ_FLAGS_RESP_R2) { + byteCount = 17; + } + SDLIB_PrintBuffer(pReq->Response,byteCount,"SDIO STD HOST - Response Dump"); + } + +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + DumpCurrentRequestInfo - debug dump + Input: pHcInstance - device context + Output: + Return: + Notes: This function debug prints the current request + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void DumpCurrentRequestInfo(PSDHCD_INSTANCE pHcInstance) +{ + PSDREQUEST pRequest = GET_CURRENT_REQUEST(&pHcInstance->Hcd); + if (pRequest != NULL) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST - Current Request Command:%d, ARG:0x%8.8X\n", + pRequest->Command, pRequest->Argument)); + if (IS_SDREQ_DATA_TRANS(pRequest->Flags)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST - Data %s, Blocks: %d, BlockLen:%d Remaining: %d \n", + IS_SDREQ_WRITE_DATA(pRequest->Flags) ? "WRITE":"READ", + pRequest->BlockCount, + pRequest->BlockLen, + pRequest->DataRemaining)); + } + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + TranslateSDError - check for an SD error + Input: pHcInstance - device context + Status - error interrupt status register value + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS TranslateSDError(PSDHCD_INSTANCE pHcInstance, UINT16 ErrorMask) +{ + SDIO_STATUS status = SDIO_STATUS_DEVICE_ERROR; + + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST - TranslateSDError :0x%X \n",ErrorMask)); + + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_CRCERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST - RESP CRC ERROR \n")); + status = SDIO_STATUS_BUS_RESP_CRC_ERR; + } + + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_DATATIMEOUTERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST - DATA TIMEOUT ERROR \n")); + status = SDIO_STATUS_BUS_READ_TIMEOUT; + } + + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_DATACRCERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST - READ or WRITE DATA CRC ERROR \n")); + status = SDIO_STATUS_BUS_READ_CRC_ERR; + } + + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_CMDTIMEOUTERR) { + if (pHcInstance->CardInserted) { + /* hide error if we are polling an empty slot */ + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST - RESPONSE TIMEOUT \n")); + } + status = SDIO_STATUS_BUS_RESP_TIMEOUT; + } + + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_VENDOR_MASK) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST HcdSDInterrupt vendor error 0x%X: \n", + (UINT)((ErrorMask & HOST_REG_ERROR_INT_STATUS_VENDOR_MASK) >> + HOST_REG_ERROR_INT_STATUS_VENDOR_SHIFT))); + } + + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_AUTOCMD12ERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST HcdSDInterrupt auto cmd12 error\n")); + } + + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_CURRENTLIMITERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST HcdSDInterrupt current limit error\n")); + } + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_DATAENDBITERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST HcdSDInterrupt data end bit error\n")); + } + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_DATATIMEOUTERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST HcdSDInterrupt data timeout error\n")); + } + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_CMDINDEXERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST HcdSDInterrupt CMD index error\n")); + } + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_CMDENDBITERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST HcdSDInterrupt CMD end bit error\n")); + } + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_CRCERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST HcdSDInterrupt CRC error\n")); + } + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_SDMAERR) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST HcdSDInterrupt SDMA error (system address:0x%X\n", + READ_HOST_REG32(pHcInstance, HOST_REG_SYSTEM_ADDRESS))); + } + if (ErrorMask & HOST_REG_ERROR_INT_STATUS_ADMAERR) { + UINT32 dmaErrStatus; + dmaErrStatus = READ_HOST_REG32(pHcInstance, HOST_REG_ADMA_ERR_STATUS); + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST HcdSDInterrupt ADMA error status: 0x%X \n", + dmaErrStatus)); + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST, ADMA Descriptor Start ADMA %s Address: 0x%X\n", + (dmaErrStatus & HOST_REG_ADMA_STATE_MASK) == HOST_REG_ADMA_STATE_FDS ? "Bad" : "Current", + READ_HOST_REG32(pHcInstance, HOST_REG_ADMA_ADDRESS))); + DumpDMADescriptorsInfo(pHcInstance); + } + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ClockStartStop - SD clock control + Input: pHcInstance - device object + On - turn on or off (TRUE/FALSE) + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void ClockStartStop(PSDHCD_INSTANCE pHcInstance, BOOL On) +{ + /* beware, an unprotected read-modify-write */ + UINT16 state; + + DBG_PRINT(STD_HOST_TRACE_CLOCK, ("SDIO STD HOST - ClockStartStop, %d\n", (UINT)On)); + + state = READ_HOST_REG16(pHcInstance, HOST_REG_CLOCK_CONTROL); + + if (On) { + state |= HOST_REG_CLOCK_CONTROL_SD_ENABLE; + WRITE_HOST_REG16(pHcInstance, HOST_REG_CLOCK_CONTROL, state); + } else { + state &= ~HOST_REG_CLOCK_CONTROL_SD_ENABLE; + WRITE_HOST_REG16(pHcInstance, HOST_REG_CLOCK_CONTROL, state); + } +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SetBusMode - Set Bus mode + Input: pHcInstance - device object + pMode - mode + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void SetBusMode(PSDHCD_INSTANCE pHcInstance, PSDCONFIG_BUS_MODE_DATA pMode) +{ + int ii; + int clockIndex; + UINT32 rate; + UINT16 state; + UINT8 control; + + DBG_PRINT(STD_HOST_TRACE_CONFIG , ("SDIO STD HOST - SetMode\n")); + + /* set clock index to the end, the table is sorted this way */ + clockIndex = SD_CLOCK_MAX_ENTRIES - 1; + pMode->ActualClockRate = (pHcInstance->BaseClock) / SDClockDivisorTable[clockIndex].ClockRateDivisor; + for (ii = 0; ii < SD_CLOCK_MAX_ENTRIES; ii++) { + rate = pHcInstance->BaseClock / SDClockDivisorTable[ii].ClockRateDivisor; + if (pMode->ClockRate >= rate) { + pMode->ActualClockRate = rate; + clockIndex = ii; + break; + } + } + + control = READ_HOST_REG8(pHcInstance, HOST_REG_CONTROL); + control &= ~HOST_REG_CONTROL_BUSWIDTH_BITS; + switch (SDCONFIG_GET_BUSWIDTH(pMode->BusModeFlags)) { + case SDCONFIG_BUS_WIDTH_1_BIT: + control |= HOST_REG_CONTROL_1BIT_WIDTH; + break; + case SDCONFIG_BUS_WIDTH_4_BIT: + control |= HOST_REG_CONTROL_4BIT_WIDTH; + break; + case SDCONFIG_BUS_WIDTH_MMC8_BIT: + control |= HOST_REG_CONTROL_EXTENDED_DATA; + break; + default: + DBG_PRINT(SDDBG_TRACE , ("SDIO STD HOST - SetMode, unknown bus width requested 0x%X\n", pMode->BusModeFlags)); + break; + } + if (pMode->BusModeFlags & SDCONFIG_BUS_MODE_SD_HS) { + control |= HOST_REG_CONTROL_HI_SPEED; + } + WRITE_HOST_REG8(pHcInstance, HOST_REG_CONTROL, control); + + /* set the clock divisor, unprotected read modify write */ + state = SDClockDivisorTable[clockIndex].RegisterValue | + (UINT16)HOST_REG_CLOCK_CONTROL_CLOCK_ENABLE; + + WRITE_HOST_REG16(pHcInstance, HOST_REG_CLOCK_CONTROL, state); + + /* wait for stable */ + while(!(READ_HOST_REG16(pHcInstance, HOST_REG_CLOCK_CONTROL) & + (UINT16)HOST_REG_CLOCK_CONTROL_CLOCK_STABLE)) { + ; + } + WRITE_HOST_REG16(pHcInstance, HOST_REG_CLOCK_CONTROL, state | + (UINT16)HOST_REG_CLOCK_CONTROL_SD_ENABLE); + + state = READ_HOST_REG16(pHcInstance, HOST_REG_CLOCK_CONTROL); + DBG_PRINT(STD_HOST_TRACE_CONFIG , ("SDIO STD HOST - Clock: %d Hz, ClockRate %d (%d) state:0x%X control:0x%X\n", + pMode->ActualClockRate, pMode->ClockRate, clockIndex, (UINT)state, (UINT)control)); + +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + HcdTransferTxData - data transmit transfer + Input: pHcInstance - device object + pReq - transfer request + Output: + Return: + Notes: writes request data + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +BOOL HcdTransferTxData(PSDHCD_INSTANCE pHcInstance, PSDREQUEST pReq) +{ + INT dataCopy; + PUINT8 pBuf; + + dataCopy = min(pReq->DataRemaining, (UINT)pReq->BlockLen); + pBuf = (PUINT8)pReq->pHcdContext; + + /* update remaining count */ + pReq->DataRemaining -= dataCopy; + /* set the block data */ + while(dataCopy) { + UINT32 outData = 0; + UINT count = 0; + if (dataCopy > 4) { + outData = ((UINT32)(*(pBuf+0))) | + (((UINT32)(*(pBuf+1))) << 8) | + (((UINT32)(*(pBuf+2))) << 16) | + (((UINT32)(*(pBuf+3))) << 24); + WRITE_HOST_REG32(pHcInstance, HOST_REG_BUFFER_DATA_PORT, outData); + dataCopy -= 4; + pBuf += 4; + } else { + for(count = 0; (dataCopy > 0) && (count < 4); count++) { + outData |= (*pBuf) << (count*8); + pBuf++; + dataCopy--; + } + WRITE_HOST_REG32(pHcInstance, HOST_REG_BUFFER_DATA_PORT, outData); + } + } + + /* update pointer position */ + pReq->pHcdContext = (PVOID)pBuf; + if (pReq->DataRemaining) { + return FALSE; + } + return TRUE; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + HcdTransferRxData - data receive transfer + Input: pHcInstance - device object + pReq - transfer request + Output: + Return: + Notes: reads request data + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void HcdTransferRxData(PSDHCD_INSTANCE pHcInstance, PSDREQUEST pReq) +{ + INT dataCopy; + PUINT8 pBuf; + + dataCopy = min(pReq->DataRemaining, (UINT)pReq->BlockLen); + pBuf = (PUINT8)pReq->pHcdContext; + + /* update remaining count */ + pReq->DataRemaining -= dataCopy; + /* set the block data */ + while(dataCopy) { + UINT32 inData; + UINT count = 0; + inData = READ_HOST_REG32(pHcInstance, HOST_REG_BUFFER_DATA_PORT); + for(count = 0; (dataCopy > 0) && (count < 4); count++) { + *pBuf = (inData >> (count*8)) & 0xFF; + dataCopy--; + pBuf++; + } + } + + /* update pointer position */ + pReq->pHcdContext = (PVOID)pBuf; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + HcdRequest - SD request handler + Input: pHcd - HCD object + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS HcdRequest(PSDHCD pHcd) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + PSDHCD_INSTANCE pHcInstance = (PSDHCD_INSTANCE)pHcd->pContext; + UINT16 temp; + PSDREQUEST pReq; + + pReq = GET_CURRENT_REQUEST(pHcd); + DBG_ASSERT(pReq != NULL); + + /* make sure clock is off */ + ClockStartStop(pHcInstance, CLOCK_OFF); + + /* make sure error ints are disabled */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_INT_ERR_SIGNAL_ENABLE,0); + + /* mask the remove while we are spinning on the CMD ready bits */ + MaskIrq(pHcInstance, HOST_REG_INT_STATUS_ALLOW_INSERT_REMOVE_ONLY,FALSE); + + do { + + if (pHcInstance->ShuttingDown) { + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST HcdRequest returning canceled\n")); + status = SDIO_STATUS_CANCELED; + break; + } + + switch (GET_SDREQ_RESP_TYPE(pReq->Flags)) { + case SDREQ_FLAGS_NO_RESP: + temp = 0x00; + break; + case SDREQ_FLAGS_RESP_R2: + temp = 0x01 | + HOST_REG_COMMAND_REGISTER_CRC_CHECK_ENABLE; + break; + case SDREQ_FLAGS_RESP_R3: + case SDREQ_FLAGS_RESP_SDIO_R4: + temp = 0x02; + break; + case SDREQ_FLAGS_RESP_R1: + case SDREQ_FLAGS_RESP_SDIO_R5: + case SDREQ_FLAGS_RESP_R6: + temp = 0x02 | HOST_REG_COMMAND_REGISTER_CRC_CHECK_ENABLE + | HOST_REG_COMMAND_REGISTER_CMD_INDEX_CHECK_ENABLE; + break; + case SDREQ_FLAGS_RESP_R1B: + temp = 0x03 | HOST_REG_COMMAND_REGISTER_CRC_CHECK_ENABLE + | HOST_REG_COMMAND_REGISTER_CMD_INDEX_CHECK_ENABLE; + break; + default: + temp = 0x00; + DBG_ASSERT(FALSE); + status = SDIO_STATUS_INVALID_PARAMETER; + break; + } + + if (!SDIO_SUCCESS(status)) { + break; + } + + /* check and see if the card is still there... on some + * host controller implementations card removal seems to prevent the + * controller from actually starting the request */ + WAIT_REGISTER32_CHANGE(pHcInstance, + &status, + HOST_REG_PRESENT_STATE, + HOST_REG_PRESENT_STATE_CARD_STATE_STABLE, + HOST_REG_PRESENT_STATE_CARD_STATE_STABLE, + pHcInstance->PresentStateWaitLimit); + + if (!SDIO_SUCCESS(status)) { + /* card detect could not stabilize, card might be ejecting */ + status = SDIO_STATUS_CANCELED; + break; + } + + if (!(READ_HOST_REG32(pHcInstance, HOST_REG_PRESENT_STATE) & + HOST_REG_PRESENT_STATE_CARD_INSERTED)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST Card Removed! \n")); + status = SDIO_STATUS_CANCELED; + break; + } + + /* start the clock */ + ClockStartStop(pHcInstance, CLOCK_ON); + WAIT_FOR_DAT_CMD_DAT_READY(pHcInstance, &status); + + if (!SDIO_SUCCESS(status)) { + ResetCmdDatLine(pHcInstance); + break; + } + + /* clear any error statuses */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS, HOST_REG_ERROR_INT_STATUS_ALL_ERR); + WRITE_HOST_REG16(pHcInstance, HOST_REG_NORMAL_INT_STATUS, HOST_REG_NORMAL_INT_STATUS_CLEAR_ALL); + + /* set the argument register */ + WRITE_HOST_REG32(pHcInstance, HOST_REG_ARGUMENT, pReq->Argument); + + if (pReq->Flags & SDREQ_FLAGS_DATA_TRANS){ + DBG_PRINT(STD_HOST_TRACE_DATA, ("SDIO STD HOST HcdRequest %s Data Transfer, Blocks:%d, BlockLen:%d \n", + IS_SDREQ_WRITE_DATA(pReq->Flags) ? "TX":"RX", + pReq->BlockCount, pReq->BlockLen)); + /* set the block size register */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_BLOCK_SIZE, pReq->BlockLen); + /* set block count register */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_BLOCK_COUNT, pReq->BlockCount); + /* set flag in command register */ + temp |= HOST_REG_COMMAND_REGISTER_DATA_PRESENT; + + pReq->DataRemaining = pReq->BlockLen * pReq->BlockCount; + + if (pReq->Flags & SDREQ_FLAGS_DATA_DMA) { + /* setup DMA , note, for SDMA, this routine could modify HOST_REG_BLOCK_SIZE*/ + status = SetUpHCDDMA(pHcInstance, pReq); + if (!SDIO_SUCCESS(status)) { + break; + } + } else { + /* use the context to hold where we are in the buffer */ + pReq->pHcdContext = pReq->pDataBuffer; + } + } + +#if 1 + { + UINT32 test; + + test = READ_HOST_REG32(pHcInstance, HOST_REG_ARGUMENT); + if (test != pReq->Argument) { + DBG_ASSERT(FALSE); + DBG_PRINT(SDDBG_ERROR, ("*********Argument:0x%X, Shouldbe:0x%X \n", + test,pReq->Argument)); + WRITE_HOST_REG32(pHcInstance, HOST_REG_ARGUMENT, pReq->Argument); + } + + if (pReq->Flags & SDREQ_FLAGS_DATA_TRANS){ + test = READ_HOST_REG16(pHcInstance, HOST_REG_BLOCK_SIZE) & HOST_REG_BLOCK_SIZE_LEN_MASK; + if (test != pReq->BlockLen) { + DBG_PRINT(SDDBG_ERROR, ("******BlockLength!!!! :0x%X, Should be:0x%X \n", + test,pReq->BlockLen)); + if ( IS_HCD_SDMA(pHcInstance)) { + WRITE_HOST_REG16(pHcInstance, + HOST_REG_BLOCK_SIZE, + pReq->BlockLen | (UINT16)HOST_REG_BLOCK_SIZE_DMA_512K_BOUNDARY); + } + } + test = READ_HOST_REG16(pHcInstance, HOST_REG_BLOCK_COUNT); + if (test != pReq->BlockCount) { + DBG_PRINT(SDDBG_ERROR, ("******BlockCount!!!! :0x%X, Should be:0x%X \n", + test,pReq->BlockCount)); + WRITE_HOST_REG16(pHcInstance, HOST_REG_BLOCK_COUNT, pReq->BlockCount); + } + } + } +#endif + + /* set transfer mode register */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_TRANSFER_MODE, + ((pReq->BlockCount > 1) ? HOST_REG_TRANSFER_MODE_MULTI_BLOCK:0) | + ((pReq->BlockCount > 1) ? HOST_REG_TRANSFER_MODE_BLOCKCOUNT_ENABLE:0) | + ((pReq->Flags & SDREQ_FLAGS_AUTO_CMD12) ? HOST_REG_TRANSFER_MODE_AUTOCMD12 : 0) | + ((IS_SDREQ_WRITE_DATA(pReq->Flags))? 0 : HOST_REG_TRANSFER_MODE_READ) | + ((pReq->Flags & SDREQ_FLAGS_DATA_DMA)? HOST_REG_TRANSFER_MODE_DMA_ENABLE : 0)); + + /* set command register, make sure it is clear to write */ + temp |= (pReq->Command << HOST_REG_COMMAND_REGISTER_CMD_SHIFT); + DBG_PRINT(STD_HOST_TRACE_REQUESTS, ("SDIO STD HOST HcdRequest - CMDDAT:0x%X (RespType:%d, Command:0x%X , Arg:0x%X) \n", + temp, GET_SDREQ_RESP_TYPE(pReq->Flags), pReq->Command, pReq->Argument)); + + /* enable error status */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_ERR_STATUS_ENABLE, HOST_REG_ERROR_INT_STATUS_ALL_ERR); + + if (SDHCD_GET_OPER_CLOCK(pHcd) < pHcInstance->ClockSpinLimit) { + /* clock rate is very low, need to use interrupts here */ + + /* enable error interrupts */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_INT_ERR_SIGNAL_ENABLE, + HOST_REG_ERROR_INT_STATUS_ALL_ERR); + + /* enable command complete IRQ */ + UnmaskIrq(pHcInstance, HOST_REG_INT_STATUS_CMD_COMPLETE_ENABLE,FALSE); + + /* enable error signal - hit it again */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_INT_ERR_SIGNAL_ENABLE, + HOST_REG_ERROR_INT_STATUS_ALL_ERR); + + /* enable error status - hit it again */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_ERR_STATUS_ENABLE, + HOST_REG_ERROR_INT_STATUS_ALL_ERR); + + DBG_PRINT(STD_HOST_TRACE_REQUESTS, ("SDIO STD HOST HcdRequest using interrupt for command done (%s). (clock:%d, ref:%d)\n", + (pReq->Flags & SDREQ_FLAGS_DATA_TRANS) ? "command only" : "with data", + SDHCD_GET_OPER_CLOCK(pHcd), + pHcInstance->ClockSpinLimit)); + + /* start the command */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_COMMAND_REGISTER, temp); + status = SDIO_STATUS_PENDING; + break; + } + + /* if we get here we are doing this inline using polling */ + + /* write command */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_COMMAND_REGISTER, temp); + + /* wait for command to finish */ + if (pReq->Flags & SDREQ_FLAGS_DATA_TRANS) { + WAIT_REGISTER32_CHANGE(pHcInstance, + &status, + HOST_REG_PRESENT_STATE, + HOST_REG_PRESENT_STATE_BUFFER_COMMAND_INHIBIT_CMD, + 0, pHcInstance->PresentStateWaitLimit); + } else { + WAIT_FOR_DAT_CMD_DAT_READY(pHcInstance, &status); + } + + if (!SDIO_SUCCESS(status)) { + ResetCmdDatLine(pHcInstance); + break; + } + + /* get errors */ + temp = READ_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS); + + if (temp != 0) { + status = TranslateSDError(pHcInstance, temp); + /* clear any existing errors - non-synchronized clear */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS, HOST_REG_ERROR_INT_STATUS_ALL_ERR); + /* reset command dat , just in case */ + ResetCmdDatLine(pHcInstance); + break; + } + + /* process the command */ + status = ProcessCommandDone(pHcInstance, pReq, FALSE); + + } while (FALSE); + + if (status != SDIO_STATUS_PENDING) { + if (!pHcInstance->KeepClockOn) { + ClockStartStop(pHcInstance, CLOCK_OFF); + } + pReq->Status = status; + /* cleanup DMA */ + if (pReq->Flags & SDREQ_FLAGS_DATA_DMA) { + /* cleanup DMA if it was setup */ + HcdTransferDataDMAEnd(pHcInstance,pReq); + } + if (IS_SDREQ_FORCE_DEFERRED_COMPLETE(pReq->Flags)) { + DBG_PRINT(STD_HOST_TRACE_REQUESTS, ("SDIO STD HOST deferring completion to work item \n")); + /* the HCD must do the indication in a separate context and return status pending */ + QueueEventResponse(pHcInstance, WORK_ITEM_IO_COMPLETE); + /* return pending */ + status = SDIO_STATUS_PENDING; + } else { + /* complete the request */ + DBG_PRINT(STD_HOST_TRACE_REQUESTS, ("SDIO STD HOST Command Done, status:%d \n", status)); + } + pHcInstance->Cancel = FALSE; + } else { + DBG_PRINT(STD_HOST_TRACE_REQUESTS, ("SDIO STD HOST Bus Request Pending.... \n")); + } + + /* now allow removal again */ + UnmaskIrq(pHcInstance, HOST_REG_INT_STATUS_ALLOW_INSERT_REMOVE_ONLY,FALSE); + return status; +} + + +SDIO_STATUS ProcessCommandDone(PSDHCD_INSTANCE pHcInstance, PSDREQUEST pReq, BOOL FromIsr) +{ + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + BOOL shortTransfer = FALSE; + UINT16 errors = 0; + + do { + + if (pHcInstance->Cancel) { + status = SDIO_STATUS_CANCELED; + break; + } + + /* get the response data for the command */ + GetResponseData(pHcInstance, pReq); + + /* check for data */ + if (!(pReq->Flags & SDREQ_FLAGS_DATA_TRANS)) { + /* no data phase, we're done */ + status = SDIO_STATUS_SUCCESS; + break; + } + /* check with the bus driver if it is okay to continue with data */ + status = SDIO_CheckResponse(&pHcInstance->Hcd, pReq, SDHCD_CHECK_DATA_TRANS_OK); + + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST : Response for Data transfer error :%d n",status)); + break; + } + + /* check for short transfer */ + if ((pReq->Flags & SDREQ_FLAGS_DATA_SHORT_TRANSFER) && + (pReq->DataRemaining <= STD_HOST_SHORT_TRANSFER_THRESHOLD) && + (SDHCD_GET_OPER_CLOCK(&pHcInstance->Hcd) >= pHcInstance->ClockSpinLimit)) { + /* we will do a short transfer */ + shortTransfer = TRUE; + break; + } + + /* normal data transfers involve interrupts */ + status = SDIO_STATUS_PENDING; + /* re-enable all errors for data transfers */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_INT_ERR_SIGNAL_ENABLE, + HOST_REG_ERROR_INT_STATUS_ALL_ERR); + if (pReq->Flags & SDREQ_FLAGS_DATA_DMA) { + /* handle DMA case */ + /* expecting interrupt */ + UnmaskIrq(pHcInstance, HOST_REG_INT_STATUS_TRANSFER_COMPLETE_ENABLE,FromIsr); + DBG_PRINT(STD_HOST_TRACE_DATA, ("SDIO STD HOST Pending DMA %s transfer \n", + IS_SDREQ_WRITE_DATA(pReq->Flags) ? "TX":"RX")); + break; + } + + if (IS_SDREQ_WRITE_DATA(pReq->Flags)) { + UINT16 ints; + /* write data, see if the buffer is ready, it should be */ + ints = READ_HOST_REG16(pHcInstance, HOST_REG_NORMAL_INT_STATUS); + if (ints & HOST_REG_NORMAL_INT_STATUS_BUFFER_WRITE_RDY) { + /* acknowledge it */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_NORMAL_INT_STATUS_BUFFER_WRITE_RDY); + /* send the initial buffer */ + /* transfer data */ + if (HcdTransferTxData(pHcInstance, pReq)) { + /* data fits in buffer */ + /* wait for transfer complete */ + UnmaskIrqFromIsr(pHcInstance, + HOST_REG_INT_STATUS_TRANSFER_COMPLETE_ENABLE); + break; + } + + /* fall through and enable write buffer interrupts */ + } + + /* expecting write buffer ready interrupt */ + UnmaskIrq(pHcInstance, + HOST_REG_INT_STATUS_BUFFER_WRITE_RDY_ENABLE, + FromIsr); + + } else { + /* expecting read buffer ready data */ + UnmaskIrq(pHcInstance, + HOST_REG_INT_STATUS_BUFFER_READ_RDY_ENABLE, + FromIsr); + } + + DBG_PRINT(STD_HOST_TRACE_DATA, ("SDIO STD HOST Pending %s transfer \n", + IS_SDREQ_WRITE_DATA(pReq->Flags) ? "TX":"RX")); + + } while (FALSE); + + /* check short transfer */ + while (shortTransfer) { + DBG_PRINT(STD_HOST_TRACE_DATA, ("SDIO STD Using Short Transfer (%d bytes) %s \n", + pReq->DataRemaining, IS_SDREQ_WRITE_DATA(pReq->Flags) ? "TX":"RX")); + + if (IS_SDREQ_WRITE_DATA(pReq->Flags)) { + /* wait for buffer ready */ + WAIT_REGISTER32_CHANGE(pHcInstance, + &status, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_NORMAL_INT_STATUS_BUFFER_WRITE_RDY, + HOST_REG_NORMAL_INT_STATUS_BUFFER_WRITE_RDY, + pHcInstance->BufferReadyWaitLimit); + + if (!SDIO_SUCCESS(status)) { + break; + } + /* acknowledge it */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_NORMAL_INT_STATUS_BUFFER_WRITE_RDY); + + /* transfer data */ + HcdTransferTxData(pHcInstance, pReq); + DBG_ASSERT(pReq->DataRemaining == 0); + + /* fall through for completion */ + } else { + + /* wait for read buffer ready */ + WAIT_REGISTER32_CHANGE_OR(pHcInstance, + &status, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_NORMAL_INT_STATUS_BUFFER_READ_RDY | + HOST_REG_NORMAL_INT_STATUS_ERROR, + HOST_REG_NORMAL_INT_STATUS_BUFFER_READ_RDY | + HOST_REG_NORMAL_INT_STATUS_ERROR, + pHcInstance->BufferReadyWaitLimit); + + if (!SDIO_SUCCESS(status)) { + break; + } + + errors = READ_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS); + + if (errors != 0) { + break; + } + /* acknowledge it */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_NORMAL_INT_STATUS_BUFFER_READ_RDY); + + /* unload buffer */ + HcdTransferRxData(pHcInstance, pReq); + DBG_ASSERT(pReq->DataRemaining == 0); + + /* fall through for completion */ + } + + /* wait for transfer complete */ + WAIT_REGISTER32_CHANGE_OR(pHcInstance, + &status, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_NORMAL_INT_STATUS_TRANSFER_COMPLETE | + HOST_REG_NORMAL_INT_STATUS_ERROR, + HOST_REG_NORMAL_INT_STATUS_TRANSFER_COMPLETE | + HOST_REG_NORMAL_INT_STATUS_ERROR, + pHcInstance->TransferCompleteWaitLimit); + + if (!SDIO_SUCCESS(status)) { + break; + } + + /* get final error status */ + errors = READ_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS); + + break; + } + + if (errors != 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD Using Short Transfer Errors! :0x%X \n",errors)); + status = TranslateSDError(pHcInstance, errors); + WRITE_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS, HOST_REG_ERROR_INT_STATUS_ALL_ERR); + } + + if (!SDIO_SUCCESS(status)) { + ResetCmdDatLine(pHcInstance); + } + + return status; +} +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + HcdConfig - HCD configuration handler + Input: pHcd - HCD object + pConfig - configuration setting + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS HcdConfig(PSDHCD pHcd, PSDCONFIG pConfig) +{ + PSDHCD_INSTANCE pHcInstance = (PSDHCD_INSTANCE)pHcd->pContext; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UINT16 command; + UINT32 temp; + + if(pHcInstance->ShuttingDown) { + DBG_PRINT(STD_HOST_TRACE_REQUESTS, ("SDIO STD HOST HcdConfig returning canceled\n")); + return SDIO_STATUS_CANCELED; + } + + command = GET_SDCONFIG_CMD(pConfig); + + switch (command){ + case SDCONFIG_GET_WP: + /* get write protect */ + temp = READ_HOST_REG32(pHcInstance, HOST_REG_PRESENT_STATE); + /* if write enabled, set WP value to zero */ + *((SDCONFIG_WP_VALUE *)pConfig->pData) = + (temp & HOST_REG_PRESENT_STATE_WRITE_ENABLED )? 0 : 1; + break; + case SDCONFIG_SEND_INIT_CLOCKS: + ClockStartStop(pHcInstance,CLOCK_ON); + /* should be at least 80 clocks at our lowest clock setting */ + status = OSSleep(100); + ClockStartStop(pHcInstance,CLOCK_OFF); + break; + case SDCONFIG_SDIO_INT_CTRL: + if (GET_SDCONFIG_CMD_DATA(PSDCONFIG_SDIO_INT_CTRL_DATA,pConfig)->SlotIRQEnable) { + { + SDIO_IRQ_MODE_FLAGS irqModeFlags; + UINT8 blockGapControl; + + irqModeFlags = GET_SDCONFIG_CMD_DATA(PSDCONFIG_SDIO_INT_CTRL_DATA,pConfig)->IRQDetectMode; + if (irqModeFlags & IRQ_DETECT_4_BIT) { + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST: 4 Bit IRQ mode \r\n")); + /* in 4 bit mode, the clock needs to be left on */ + pHcInstance->KeepClockOn = TRUE; + blockGapControl = READ_HOST_REG8(pHcInstance,HOST_REG_BLOCK_GAP); + if (irqModeFlags & IRQ_DETECT_MULTI_BLK) { + blockGapControl |= HOST_REG_INT_DETECT_AT_BLOCK_GAP; + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST: 4 Bit Multi-block IRQ detection enabled \r\n")); + } else { + // no interrupts between blocks + blockGapControl &= ~HOST_REG_INT_DETECT_AT_BLOCK_GAP; + } + WRITE_HOST_REG8(pHcInstance,HOST_REG_BLOCK_GAP,blockGapControl); + } else { + /* in 1 bit mode, the clock can be left off */ + pHcInstance->KeepClockOn = FALSE; + } + } + /* enable detection */ + EnableDisableSDIOIRQ(pHcInstance,TRUE,FALSE); + } else { + pHcInstance->KeepClockOn = FALSE; + EnableDisableSDIOIRQ(pHcInstance,FALSE,FALSE); + } + break; + case SDCONFIG_SDIO_REARM_INT: + /* re-enable IRQ detection */ + EnableDisableSDIOIRQ(pHcInstance,TRUE,FALSE); + break; + case SDCONFIG_BUS_MODE_CTRL: + SetBusMode(pHcInstance, (PSDCONFIG_BUS_MODE_DATA)(pConfig->pData)); + break; + case SDCONFIG_POWER_CTRL: + DBG_PRINT(STD_HOST_TRACE_CONFIG, ("SDIO STD HOST PwrControl: En:%d, VCC:0x%X \n", + GET_SDCONFIG_CMD_DATA(PSDCONFIG_POWER_CTRL_DATA,pConfig)->SlotPowerEnable, + GET_SDCONFIG_CMD_DATA(PSDCONFIG_POWER_CTRL_DATA,pConfig)->SlotPowerVoltageMask)); + status = SetPowerLevel(pHcInstance, + GET_SDCONFIG_CMD_DATA(PSDCONFIG_POWER_CTRL_DATA,pConfig)->SlotPowerEnable, + GET_SDCONFIG_CMD_DATA(PSDCONFIG_POWER_CTRL_DATA,pConfig)->SlotPowerVoltageMask); + break; + case SDCONFIG_GET_HCD_DEBUG: + *((CT_DEBUG_LEVEL *)pConfig->pData) = DBG_GET_DEBUG_LEVEL(); + break; + case SDCONFIG_SET_HCD_DEBUG: + DBG_SET_DEBUG_LEVEL(*((CT_DEBUG_LEVEL *)pConfig->pData)); + break; + default: + /* invalid request */ + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST Local HCD: HcdConfig - bad command: 0x%X\n", + command)); + status = SDIO_STATUS_INVALID_PARAMETER; + } + + return status; +} + + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SetPowerLevel - Set power level of board + Input: pHcInstance - device context + On - if true turns power on, else off + Level - SLOT_VOLTAGE_MASK level + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS SetPowerLevel(PSDHCD_INSTANCE pHcInstance, BOOL On, SLOT_VOLTAGE_MASK Level) +{ + UINT8 out; + UINT32 capCurrent; + + capCurrent = READ_HOST_REG32(pHcInstance, HOST_REG_MAX_CURRENT_CAPABILITIES); + + switch (Level) { + case SLOT_POWER_3_3V: + out = HOST_REG_POWER_CONTROL_VOLT_3_3; + /* extract */ + capCurrent = (capCurrent & HOST_REG_MAX_CURRENT_CAPABILITIES_3_3_MASK) >> + HOST_REG_MAX_CURRENT_CAPABILITIES_3_3_SHIFT; + break; + case SLOT_POWER_3_0V: + out = HOST_REG_POWER_CONTROL_VOLT_3_0; + /* extract */ + capCurrent = (capCurrent & HOST_REG_MAX_CURRENT_CAPABILITIES_3_0_MASK) >> + HOST_REG_MAX_CURRENT_CAPABILITIES_3_0_SHIFT; + break; + case SLOT_POWER_1_8V: + out = HOST_REG_POWER_CONTROL_VOLT_1_8; + /* extract */ + capCurrent = (capCurrent & HOST_REG_MAX_CURRENT_CAPABILITIES_1_8_MASK) >> + HOST_REG_MAX_CURRENT_CAPABILITIES_1_8_SHIFT; + break; + default: + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST SetPowerLevel - illegal power level %d\n", + (UINT)Level)); + return SDIO_STATUS_INVALID_PARAMETER; + } + + if (capCurrent != 0) { + /* convert to mA and set max current */ + pHcInstance->Hcd.MaxSlotCurrent = capCurrent * HOST_REG_MAX_CURRENT_CAPABILITIES_SCALER; + } else { + DBG_PRINT(SDDBG_WARN, ("SDIO STD HOST No Current Caps value for VMask:0x%X, using 200mA \n", + Level)); + /* set a value */ + pHcInstance->Hcd.MaxSlotCurrent = 200; + } + + if (On) { + out |= HOST_REG_POWER_CONTROL_ON; + } + + WRITE_HOST_REG8(pHcInstance, HOST_REG_POWER_CONTROL, out); + return SDIO_STATUS_SUCCESS; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + SetPowerOn - Set power on or off for card + Input: pHcInstance - device context + On - if true turns power on, else off + Output: + Return: + Notes: leavse the level alone + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void SetPowerOn(PSDHCD_INSTANCE pHcInstance, BOOL On) +{ + /* non-synchronized read modify write */ + UINT8 out = READ_HOST_REG8(pHcInstance, HOST_REG_POWER_CONTROL); + if (On) { + out |= HOST_REG_POWER_CONTROL_ON; + } else { + out &= ~HOST_REG_POWER_CONTROL_ON; + } + WRITE_HOST_REG8(pHcInstance, HOST_REG_POWER_CONTROL, out); + return; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + HcdInitialize - Initialize MMC controller + Input: pHcInstance - device context + Output: + Return: + Notes: I/O resources must be mapped before calling this function + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +SDIO_STATUS HcdInitialize(PSDHCD_INSTANCE pHcInstance) +{ + UINT32 caps; + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + UINT32 clockValue; + PTEXT pSpecVer; + DBG_PRINT(SDDBG_TRACE, ("+SDIO STD HOST HcdInitialize\n")); + + if (0 == pHcInstance->BufferReadyWaitLimit) { + /* initialize all these to defaults */ + pHcInstance->BufferReadyWaitLimit = 50000; + pHcInstance->TransferCompleteWaitLimit = 100000; + pHcInstance->PresentStateWaitLimit = 30000; + pHcInstance->ResetWaitLimit = 30000; + } + + /* reset the device */ + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST HcdInitialize, resetting\n")); + WRITE_HOST_REG8(pHcInstance, HOST_REG_SW_RESET, HOST_REG_SW_RESET_ALL); + /* wait for done */ + while(READ_HOST_REG8(pHcInstance, HOST_REG_SW_RESET) & HOST_REG_SW_RESET_ALL) + ; + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST HcdInitialize, reset\n")); + + /* turn off clock */ + ClockStartStop(pHcInstance, CLOCK_OFF); + /* display version info */ + switch(((READ_HOST_REG16(pHcInstance, HOST_REG_VERSION) >> HOST_REG_VERSION_VENDOR_VERSION_SHIFT) & + (UINT16)HOST_REG_VERSION_VENDOR_VERSION_MASK)) { + case 0: + pSpecVer = "SD Host Spec. 1.0"; + break; + case 1: + pSpecVer = "SD Host Spec. 2.0"; + break; + default: + pSpecVer = "SD Host Spec. **UNKNOWN**"; + } + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST HcdInitialize: Spec version: %s, Vendor version: %d\n", + pSpecVer, + (READ_HOST_REG16(pHcInstance, HOST_REG_VERSION) >> HOST_REG_VERSION_VENDOR_VERSION_SHIFT) & + HOST_REG_VERSION_VENDOR_VERSION_MASK)); + + /* get capabilities */ + caps = READ_HOST_REG32(pHcInstance, HOST_REG_CAPABILITIES); + /* save these */ + pHcInstance->Caps = caps; + + switch((caps & HOST_REG_CAPABILITIES_MAX_BLOCK_LEN_MASK) >> HOST_REG_CAPABILITIES_MAX_BLOCK_LEN_SHIFT) { + case 0x00: + pHcInstance->Hcd.MaxBytesPerBlock = 512; + break; + case 0x01: + pHcInstance->Hcd.MaxBytesPerBlock = 1024; + break; + case 0x02: + pHcInstance->Hcd.MaxBytesPerBlock = 2048; + break; + case 0x03: + pHcInstance->Hcd.MaxBytesPerBlock = 512; + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST invalid buffer length\n")); + status = SDIO_STATUS_DEVICE_ERROR; + break; + } + + clockValue = (caps & HOST_REG_CAPABILITIES_CLOCK_MASK) >> HOST_REG_CAPABILITIES_CLOCK_SHIFT; + if (clockValue != 0) { + /* convert to Hz */ + pHcInstance->BaseClock = clockValue*1000*1000; + } else { + DBG_PRINT(SDDBG_WARN, ("SDIO STD HOST base clock is zero! (caps:0x%X) \n",caps)); + /* fall through and see if a default was setup */ + } + if (pHcInstance->BaseClock == 0) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST invalid base clock setting\n")); + status = SDIO_STATUS_DEVICE_ERROR; + return status; + } + + pHcInstance->Hcd.MaxClockRate = pHcInstance->BaseClock; + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST Using clock %dHz, max. block %d, high speed %s, %s, %s, %s\n", + pHcInstance->BaseClock, pHcInstance->Hcd.MaxBytesPerBlock, + (caps & HOST_REG_CAPABILITIES_HIGH_SPEED)? "supported" : "not supported", + (caps & HOST_REG_CAPABILITIES_DMA)? "Std. DMA" : "", + (caps & HOST_REG_CAPABILITIES_ADMA)? "Adv. DMA" : "", + (caps & HOST_REG_CAPABILITIES_MMC8)? "MMC8bit" : "")); + + /* setup the supported voltages and max current */ + pHcInstance->Hcd.SlotVoltageCaps = 0; + /* max current is dynamically set based on the desired voltage, see SetPowerLevel() */ + pHcInstance->Hcd.MaxSlotCurrent = 0; + + if (caps & HOST_REG_CAPABILITIES_VOLT_1_8) { + pHcInstance->Hcd.SlotVoltageCaps |= SLOT_POWER_1_8V; + pHcInstance->Hcd.SlotVoltagePreferred = SLOT_POWER_1_8V; + } + if(caps & HOST_REG_CAPABILITIES_VOLT_3_0) { + pHcInstance->Hcd.SlotVoltageCaps |= SLOT_POWER_3_0V; + pHcInstance->Hcd.SlotVoltagePreferred = SLOT_POWER_3_0V; + } + if(caps & HOST_REG_CAPABILITIES_VOLT_3_3) { + pHcInstance->Hcd.SlotVoltageCaps |= SLOT_POWER_3_3V; + pHcInstance->Hcd.SlotVoltagePreferred = SLOT_POWER_3_3V; + } + + /* check host capabilities and back off some features */ + if (!(caps & HOST_REG_CAPABILITIES_HIGH_SPEED)) { + pHcInstance->Hcd.Attributes &= ~SDHCD_ATTRIB_SD_HIGH_SPEED; + } + + if (!(caps & HOST_REG_CAPABILITIES_MMC8)) { + pHcInstance->Hcd.Attributes &= ~SDHCD_ATTRIB_BUS_MMC8BIT; + pHcInstance->Hcd.Attributes &= ~SDHCD_ATTRIB_MMC_HIGH_SPEED; + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST HcdInitialize: caps: 0x%X, SlotVoltageCaps: 0x%X\n", + (UINT)caps, (UINT)pHcInstance->Hcd.SlotVoltageCaps)); + + /* set the default timeout */ + WRITE_HOST_REG8(pHcInstance, HOST_REG_TIMEOUT_CONTROL, pHcInstance->TimeOut); + + /* clear any existing errors and status */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_NORMAL_INT_STATUS, HOST_REG_NORMAL_INT_STATUS_CLEAR_ALL); + WRITE_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS, HOST_REG_ERROR_INT_STATUS_ALL_ERR); + /* enable error interrupts */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_ERR_STATUS_ENABLE, HOST_REG_ERROR_INT_STATUS_ALL_ERR); + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST HcdInitialize - error enable 16bit: 0x%X\n", READ_HOST_REG16(pHcInstance, HOST_REG_ERR_STATUS_ENABLE))); + + WRITE_HOST_REG32(pHcInstance, HOST_REG_ERR_STATUS_ENABLE, HOST_REG_ERROR_INT_STATUS_ALL_ERR); + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST HcdInitialize - error enable 32 bit: 0x%X\n", READ_HOST_REG32(pHcInstance, HOST_REG_ERR_STATUS_ENABLE))); + + /* leave disabled for now */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_INT_ERR_SIGNAL_ENABLE, (UINT16)0); + + /* enable statuses */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_INT_STATUS_ENABLE, HOST_REG_INT_STATUS_ALL); + + DBG_PRINT(SDDBG_TRACE, ("-SDIO STD HOST HcdInitialize\n")); + return status; +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + HcdDeinitialize - deactivate controller + Input: pHcInstance - context + Output: + Return: + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +void HcdDeinitialize(PSDHCD_INSTANCE pHcInstance) +{ + DBG_PRINT(SDDBG_TRACE, ("+SDIO STD HOST HcdDeinitialize\n")); + pHcInstance->KeepClockOn = FALSE; + MaskIrq(pHcInstance, HOST_REG_INT_STATUS_ALL,FALSE); + pHcInstance->ShuttingDown = TRUE; + /* disable error interrupts */ + /* clear any existing errors */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS, HOST_REG_ERROR_INT_STATUS_ALL_ERR); + /* disable error interrupts */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_INT_ERR_SIGNAL_ENABLE, 0); + WRITE_HOST_REG16(pHcInstance, HOST_REG_ERR_STATUS_ENABLE, 0); + ClockStartStop(pHcInstance, CLOCK_OFF); + SetPowerOn(pHcInstance, FALSE); + DBG_PRINT(SDDBG_TRACE, ("-SDIO STD HOST HcdDeinitialize\n")); +} + +/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + HcdSDInterrupt - process controller interrupt + Input: pHcInstance - context + Output: + Return: TRUE if interrupt was handled + Notes: + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +BOOL HcdSDInterrupt(PSDHCD_INSTANCE pHcInstance) +{ + UINT16 ints; + UINT16 errors; + UINT16 enables; + UINT16 statenables; + UINT16 errorenables; + PSDREQUEST pReq; + SDIO_STATUS status = SDIO_STATUS_PENDING; + + DBG_PRINT(STD_HOST_TRACE_INT, ("+SDIO STD HOST HcdSDInterrupt Int handler \n")); + + pReq = GET_CURRENT_REQUEST(&pHcInstance->Hcd); + + while (1) { + + ints = READ_HOST_REG16(pHcInstance, HOST_REG_NORMAL_INT_STATUS); + errors = READ_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS); + + enables = READ_HOST_REG16(pHcInstance, HOST_REG_INT_SIGNAL_ENABLE); + statenables = READ_HOST_REG16(pHcInstance, HOST_REG_INT_STATUS_ENABLE); + errorenables = READ_HOST_REG16(pHcInstance, HOST_REG_INT_ERR_SIGNAL_ENABLE); + DBG_PRINT(STD_HOST_TRACE_INT, + ("SDIO STD HOST HcdSDInterrupt, ints: 0x%X errors: 0x%x, sigenables: 0x%X, statenable: 0x%X errorenables:0x%X\n", + (UINT)ints, (UINT)errors, (UINT)enables, (UINT)statenables, (UINT)errorenables)); + + /* only look at ints and error ints that are enabled */ + ints &= enables; + errors &= errorenables; + + if ((ints == 0) && (errors == 0)) { + break; + } + /* clear any error statuses */ + WRITE_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS, errors); + + if (ints & HOST_REG_NORMAL_INT_STATUS_TRANSFER_COMPLETE) { + DBG_PRINT(STD_HOST_TRACE_INT, ("SDIO STD HOST HcdSDInterrupt clearing possible data timeout errors: 0x%X \n", + errors)); + errors &= ~HOST_REG_ERROR_INT_STATUS_DATATIMEOUTERR; + } + + if (errors != 0) { + status = TranslateSDError(pHcInstance, errors); + break; + } + + /* handle insert/removal */ + if (ints & + (HOST_REG_INT_STATUS_CARD_INSERT_ENABLE | HOST_REG_INT_STATUS_CARD_REMOVAL_ENABLE)){ + /* card was inserted or removed, clear interrupt */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_INT_STATUS_CARD_INSERT_ENABLE | + HOST_REG_INT_STATUS_CARD_REMOVAL_ENABLE); + /* mask card insert */ + MaskIrqFromIsr(pHcInstance, HOST_REG_INT_STATUS_ALLOW_INSERT_REMOVE_ONLY); + QueueEventResponse(pHcInstance, WORK_ITEM_CARD_DETECT); + /* we don't need to cancel any requests, every SD transaction is protected + by a timeout, we just let the timeout occur */ + /* continue and process interrupts */ + } + + /* deal with card interrupts */ + if ((pHcInstance->CardInserted) && + (ints & HOST_REG_INT_STATUS_CARD_INT_STAT_ENABLE)) { + DBG_PRINT(STD_HOST_TRACE_SDIO_INT, ("SDIO STD HOST: SDIO Card Interrupt Detected \n")); + /* SD card interrupt*/ + /* disable the interrupt, the user must clear the interrupt */ + EnableDisableSDIOIRQ(pHcInstance,FALSE,TRUE); + QueueEventResponse(pHcInstance, WORK_ITEM_SDIO_IRQ); + /* continue looking for other interrupt causes */ + } else if (ints & HOST_REG_INT_STATUS_CARD_INT_STAT_ENABLE) { + /* disable bogus interrupt */ + EnableDisableSDIOIRQ(pHcInstance,FALSE,TRUE); + } + + if (NULL == pReq) { + break; + } + + if (ints & HOST_REG_NORMAL_INT_STATUS_CMD_COMPLETE) { + /* clear interrupt */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_NORMAL_INT_STATUS_CMD_COMPLETE); + /* disable this interrupt */ + MaskIrqFromIsr(pHcInstance, HOST_REG_INT_STATUS_CMD_COMPLETE_ENABLE); + status = ProcessCommandDone(pHcInstance,pReq,TRUE); + if (status != SDIO_STATUS_PENDING) { + break; + } + continue; + } + + if (ints & HOST_REG_NORMAL_INT_STATUS_DMA_INT) { + /* we should NOT get these, the descriptors should not have the INTERRUPT bit set */ + DBG_ASSERT(FALSE); + break; + } + + /* check TX buffer ready */ + if (ints & HOST_REG_NORMAL_INT_STATUS_BUFFER_WRITE_RDY) { + DBG_ASSERT(IS_SDREQ_WRITE_DATA(pReq->Flags)); + /* clear interrupt */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_NORMAL_INT_STATUS_BUFFER_WRITE_RDY); + if (pReq->DataRemaining > 0) { + /* transfer data */ + if (!HcdTransferTxData(pHcInstance, pReq)) { + /* still more data to go... we'll get more write RDY interrupts */ + continue; + } + + /* fall through if this is the last block */ + } + /* transfer is done */ + /* disable write rdy */ + MaskIrqFromIsr(pHcInstance, + HOST_REG_INT_STATUS_BUFFER_WRITE_RDY_ENABLE); + /* all data transfered, wait for transfer complete */ + UnmaskIrqFromIsr(pHcInstance, + HOST_REG_INT_STATUS_TRANSFER_COMPLETE_ENABLE); + continue; + } + + /* check RX buffer ready */ + if (ints & (HOST_REG_NORMAL_INT_STATUS_BUFFER_READ_RDY)) { + DBG_ASSERT(!IS_SDREQ_WRITE_DATA(pReq->Flags)); + /* clear interrupt */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_NORMAL_INT_STATUS_BUFFER_READ_RDY ); + /* unload fifo */ + HcdTransferRxData(pHcInstance, pReq); + if (pReq->DataRemaining > 0) { + /* more to do.. */ + } else { + /* turn off read ready interrupts */ + MaskIrqFromIsr(pHcInstance, + HOST_REG_INT_STATUS_BUFFER_READ_RDY_ENABLE); + /* all data transfered, wait for transfer complete */ + UnmaskIrqFromIsr(pHcInstance, + HOST_REG_INT_STATUS_TRANSFER_COMPLETE_ENABLE); + } + continue; + } + + if (ints & HOST_REG_NORMAL_INT_STATUS_TRANSFER_COMPLETE) { + DBG_ASSERT(IS_SDREQ_DATA_TRANS(pReq->Flags)); + DBG_PRINT(STD_HOST_TRACE_INT, ("SDIO STD HOST HcdSDInterrupt Transfer done \n")); + + /* clear interrupt */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_NORMAL_INT_STATUS_TRANSFER_COMPLETE); + /* if we get here without an error, we are done with the data + * data operation */ + status = SDIO_STATUS_SUCCESS; + break; + } + + } + + if (status != SDIO_STATUS_PENDING) { + /* turn off interrupts and clock */ + MaskIrqFromIsr(pHcInstance, + ~(HOST_REG_INT_STATUS_ALLOW_INSERT_REMOVE_ONLY | + HOST_REG_INT_STATUS_CARD_INT_STAT_ENABLE) ); + + if (errors) { + /* reset statemachine */ + ResetCmdDatLine(pHcInstance); + } + + if (!pHcInstance->KeepClockOn) { + ClockStartStop(pHcInstance, CLOCK_OFF); + } + + if (pReq != NULL) { + if (IS_SDREQ_DATA_TRANS(pReq->Flags)) { + if (IS_SDREQ_WRITE_DATA(pReq->Flags)) { + TRACE_SIGNAL_DATA_WRITE(pHcInstance, FALSE); + } else { + TRACE_SIGNAL_DATA_READ(pHcInstance, FALSE); + } + } + /* set the status */ + pReq->Status = status; + + /* cleanup DMA if used */ + if (pReq->Flags & SDREQ_FLAGS_DATA_DMA) { + HcdTransferDataDMAEnd(pHcInstance,pReq); + } + + if (IS_SDREQ_DATA_TRANS(pReq->Flags)) { + DBG_PRINT(STD_HOST_TRACE_DATA, ("SDIO STD HOST - %s Data Transfer Complete with status:%d\n", + IS_SDREQ_WRITE_DATA(pReq->Flags) ? "TX":"RX", + pReq->Status)); + } + + if ((DBG_GET_DEBUG_LEVEL() >= STD_HOST_TRACE_DATA_DUMP) && SDIO_SUCCESS(status) && + IS_SDREQ_DATA_TRANS(pReq->Flags) && !IS_SDREQ_WRITE_DATA(pReq->Flags) && + !(pReq->Flags & SDREQ_FLAGS_DATA_DMA)) { + SDLIB_PrintBuffer(pReq->pDataBuffer,(pReq->BlockLen*pReq->BlockCount),"SDIO STD HOST - RX DataDump"); + } + /* queue work item to notify bus driver of I/O completion */ + QueueEventResponse(pHcInstance, WORK_ITEM_IO_COMPLETE); + } + } + + DBG_PRINT(STD_HOST_TRACE_INT, ("-SDIO STD HOST HcdSDInterrupt Int handler \n")); + return TRUE; +} + + +/* card detect callback from a deferred (non-ISR) context */ +void ProcessDeferredCardDetect(PSDHCD_INSTANCE pHcInstance) +{ + + HCD_EVENT event; + volatile UINT32 temp; + + event = EVENT_HCD_NOP; + + DBG_PRINT(SDDBG_TRACE, ("+SDIO STD HOST Card Detect Processing \n")); + if (pHcInstance->ShuttingDown) { + return; + } + + DBG_PRINT(SDDBG_TRACE, ("SDIO STD Host Card Detect Delaying to debounce card... \n")); + + OSSleep(SD_SLOT_DEBOUNCE_MS); + + /* wait for stable */ + while(!(temp = READ_HOST_REG32(pHcInstance, HOST_REG_PRESENT_STATE))& + HOST_REG_PRESENT_STATE_CARD_STATE_STABLE) { + ; + } + + /* look for removal */ + if (!(temp & HOST_REG_PRESENT_STATE_CARD_INSERTED)) { + pHcInstance->CardInserted = FALSE; + pHcInstance->KeepClockOn = FALSE; + /* turn the power off */ + SetPowerOn(pHcInstance, FALSE); + if (pHcInstance->StartUpCardCheckDone) { + DBG_PRINT(STD_HOST_TRACE_CARD_INSERT, ("SDIO STD HOST Card Detect REMOVE\n")); + /* card not present */ + event = EVENT_HCD_DETACH; + } + } else { + /* card present */ + event = EVENT_HCD_ATTACH; + pHcInstance->CardInserted = TRUE; + DBG_PRINT(STD_HOST_TRACE_CARD_INSERT, ("SDIO STD HOST Card Detect INSERT\n")); + } + + if (!pHcInstance->StartUpCardCheckDone) { + /* startup check is now done */ + pHcInstance->StartUpCardCheckDone = TRUE; + } + /* clear interrupt */ + WRITE_HOST_REG16(pHcInstance, + HOST_REG_NORMAL_INT_STATUS, + HOST_REG_INT_STATUS_CARD_INSERT_ENABLE | + HOST_REG_INT_STATUS_CARD_REMOVAL_ENABLE); + /* re-enable insertion/removal */ + UnmaskIrq(pHcInstance, HOST_REG_INT_STATUS_ALLOW_INSERT_REMOVE_ONLY,FALSE); + + if (event != EVENT_HCD_NOP) { + SDIO_HandleHcdEvent(&pHcInstance->Hcd, event); + } + + if (!pHcInstance->CardInserted && !pHcInstance->RequestCompleteQueued) { + /* check for a stuck request */ + PSDREQUEST pReq = GET_CURRENT_REQUEST(&pHcInstance->Hcd); + if (pReq != NULL) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST , Stuck Request! 0x%X\n",(UINT)pReq)); + DumpStdHcdRegisters(pHcInstance); + DumpCurrentRequestInfo(pHcInstance); + } + } + + DBG_PRINT(STD_HOST_TRACE_CARD_INSERT, ("- SDIO STD HOST Card Detect Processing \n")); +} + + +void DumpStdHcdRegisters(PSDHCD_INSTANCE pHcInstance) +{ + DBG_PRINT(SDDBG_TRACE, ("---------------- SDIO STD HOST, Register Dump ----------------- \n")); + + DBG_PRINT(SDDBG_TRACE,(" NORMAL INT STATUS : 0x%X \n", + READ_HOST_REG16(pHcInstance, HOST_REG_NORMAL_INT_STATUS))); + DBG_PRINT(SDDBG_TRACE,(" ERROR INT STATUS : 0x%X \n", + READ_HOST_REG16(pHcInstance, HOST_REG_ERROR_INT_STATUS))); + DBG_PRINT(SDDBG_TRACE,(" INT SIGNAL ENABLE : 0x%X \n", + READ_HOST_REG16(pHcInstance, HOST_REG_INT_SIGNAL_ENABLE))); + DBG_PRINT(SDDBG_TRACE,(" ERROR SIGNAL ENABLES : 0x%X \n", + READ_HOST_REG16(pHcInstance, HOST_REG_INT_ERR_SIGNAL_ENABLE))); + DBG_PRINT(SDDBG_TRACE,(" STATUS ENABLES : 0x%X \n", + READ_HOST_REG16(pHcInstance, HOST_REG_INT_STATUS_ENABLE))); + DBG_PRINT(SDDBG_TRACE,(" ERROR STATUS ENABLES : 0x%X \n", + READ_HOST_REG16(pHcInstance, HOST_REG_ERR_STATUS_ENABLE))); + + DBG_PRINT(SDDBG_TRACE,(" HOST PRESENT_STATE : 0x%X \n", + READ_HOST_REG32(pHcInstance, HOST_REG_PRESENT_STATE))); + + + DBG_PRINT(SDDBG_TRACE, ("------------------------------------------------------------------")); +} + + Index: linux-2.6.22/drivers/sdio/hcd/stdhost/sdio_std_hcd.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/hcd/stdhost/sdio_std_hcd.h 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,330 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_std_hcd.h + +@abstract: OS Independent standard host header file + +@notice: Copyright (c), 2006 Atheros Communications, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __SDIO_STD_HCD_H___ +#define __SDIO_STD_HCD_H___ + +#include +#include +#include +#include "sdio_std_hcd_linux.h" + +enum STD_HOST_TRACE_ENUM { + STD_HOST_TRACE_CARD_INSERT = (SDDBG_TRACE + 1), + STD_HOST_TRACE_DATA = (SDDBG_TRACE + 2), + STD_HOST_TRACE_REQUESTS, + STD_HOST_TRACE_DATA_DUMP, + STD_HOST_TRACE_CONFIG, + STD_HOST_TRACE_INT, + STD_HOST_TRACE_CLOCK, + STD_HOST_TRACE_SDIO_INT, + STD_HOST_TRACE_LAST +}; + + /* Host Controller register definitions */ +#define HOST_REG_SYSTEM_ADDRESS 0x00 + +#define HOST_REG_BLOCK_SIZE 0x04 +#define HOST_REG_BLOCK_SIZE_LEN_MASK 0x0FFF +#define HOST_REG_BLOCK_SIZE_DMA_MASK 0x7000 +#define HOST_REG_BLOCK_SIZE_DMA_SHIFT 12 +#define HOST_REG_BLOCK_SIZE_DMA_512K_BOUNDARY (7 << HOST_REG_BLOCK_SIZE_DMA_SHIFT) + +#define HOST_REG_BLOCK_COUNT 0x06 + +#define HOST_REG_ARGUMENT 0x08 + +#define HOST_REG_TRANSFER_MODE 0x0C +#define HOST_REG_TRANSFER_MODE_MULTI_BLOCK (1 << 5) +#define HOST_REG_TRANSFER_MODE_READ (1 << 4) +#define HOST_REG_TRANSFER_MODE_AUTOCMD12 (1 << 2) +#define HOST_REG_TRANSFER_MODE_BLOCKCOUNT_ENABLE (1 << 1) +#define HOST_REG_TRANSFER_MODE_DMA_ENABLE (1 << 0) + +#define HOST_REG_COMMAND_REGISTER 0x0E +#define HOST_REG_COMMAND_REGISTER_CMD_SHIFT 8 +#define HOST_REG_COMMAND_REGISTER_DATA_PRESENT (1 << 5) +#define HOST_REG_COMMAND_REGISTER_CMD_INDEX_CHECK_ENABLE (1 << 4) +#define HOST_REG_COMMAND_REGISTER_CRC_CHECK_ENABLE (1 << 3) + + +#define HOST_REG_RESPONSE 0x10 /* 32-bit reguisters 0x10 through 0x1C */ + +#define HOST_REG_BUFFER_DATA_PORT 0x20 + +#define HOST_REG_PRESENT_STATE 0x24 +#define HOST_REG_PRESENT_STATE_WRITE_ENABLED (1 << 19) +#define HOST_REG_PRESENT_STATE_CARD_DETECT (1 << 18) +#define HOST_REG_PRESENT_STATE_CARD_STATE_STABLE (1 << 17) +#define HOST_REG_PRESENT_STATE_CARD_INSERTED (1 << 16) +#define HOST_REG_PRESENT_STATE_BUFFER_READ_ENABLE (1 << 11) +#define HOST_REG_PRESENT_STATE_BUFFER_WRITE_ENABLE (1 << 10) +#define HOST_REG_PRESENT_STATE_BUFFER_READ_TRANSFER_ACTIVE (1 << 9) +#define HOST_REG_PRESENT_STATE_BUFFER_WRITE_TRANSFER_ACTIVE (1 << 8) +#define HOST_REG_PRESENT_STATE_BUFFER_DAT_LINE_ACTIVE (1 << 2) +#define HOST_REG_PRESENT_STATE_BUFFER_COMMAND_INHIBIT_DAT (1 << 1) +#define HOST_REG_PRESENT_STATE_BUFFER_COMMAND_INHIBIT_CMD (1 << 0) + + +#define HOST_REG_CONTROL 0x28 +#define HOST_REG_CONTROL_LED_ON (1 << 0) +#define HOST_REG_CONTROL_1BIT_WIDTH 0x00 +#define HOST_REG_CONTROL_4BIT_WIDTH (1 << 1) +#define HOST_REG_CONTROL_HI_SPEED (1 << 2) +#define HOST_REG_CONTROL_DMA_NONE (0 << 3) +#define HOST_REG_CONTROL_DMA_32BIT (1 << 3) +#define HOST_REG_CONTROL_DMA_64BIT (2 << 3) +#define HOST_REG_CONTROL_DMA_MASK (3 << 3) +#define HOST_REG_CONTROL_EXTENDED_DATA (1 << 5) +#define HOST_REG_CONTROL_CARD_DETECT_TEST (1 << 6) +#define HOST_REG_CONTROL_CARD_DETECT_SELECT (1 << 7) +#define HOST_REG_CONTROL_BUSWIDTH_BITS \ + (HOST_REG_CONTROL_1BIT_WIDTH | HOST_REG_CONTROL_4BIT_WIDTH | HOST_REG_CONTROL_EXTENDED_DATA) + + +#define HOST_REG_POWER_CONTROL 0x29 +#define HOST_REG_POWER_CONTROL_ON (1 << 0) +#define HOST_REG_POWER_CONTROL_VOLT_3_3 (7 << 1) +#define HOST_REG_POWER_CONTROL_VOLT_3_0 (6 << 1) +#define HOST_REG_POWER_CONTROL_VOLT_1_8 (5 << 1) + +#define HOST_REG_BLOCK_GAP 0x2A +#define HOST_REG_INT_DETECT_AT_BLOCK_GAP (1 << 3) + +#define HOST_REG_CLOCK_CONTROL 0x2C +#define HOST_REG_CLOCK_CONTROL_CLOCK_ENABLE (1 << 0) +#define HOST_REG_CLOCK_CONTROL_CLOCK_STABLE (1 << 1) +#define HOST_REG_CLOCK_CONTROL_SD_ENABLE (1 << 2) + +#define HOST_REG_TIMEOUT_CONTROL 0x2E +#define HOST_REG_TIMEOUT_CONTROL_DEFAULT 0x0C + +#define HOST_REG_SW_RESET 0x2F +#define HOST_REG_SW_RESET_ALL (1 << 0) +#define HOST_REG_SW_RST_CMD_LINE (1 << 1) +#define HOST_REG_SW_RST_DAT_LINE (1 << 2) + +#define HOST_REG_NORMAL_INT_STATUS 0x30 +#define HOST_REG_NORMAL_INT_STATUS_ERROR (1 << 15) +#define HOST_REG_NORMAL_INT_STATUS_CARD_INTERRUPT (1 << 8) +#define HOST_REG_NORMAL_INT_STATUS_CARD_REMOVAL (1 << 7) +#define HOST_REG_NORMAL_INT_STATUS_CARD_INSERT (1 << 6) +#define HOST_REG_NORMAL_INT_STATUS_BUFFER_READ_RDY (1 << 5) +#define HOST_REG_NORMAL_INT_STATUS_BUFFER_WRITE_RDY (1 << 4) +#define HOST_REG_NORMAL_INT_STATUS_DMA_INT (1 << 3) +#define HOST_REG_NORMAL_INT_STATUS_BLOCK_GAP (1 << 2) +#define HOST_REG_NORMAL_INT_STATUS_TRANSFER_COMPLETE (1 << 1) +#define HOST_REG_NORMAL_INT_STATUS_CMD_COMPLETE (1 << 0) +#define HOST_REG_NORMAL_INT_STATUS_CLEAR_ALL 0xFFFF + +#define HOST_REG_ERROR_INT_STATUS 0x32 +#define HOST_REG_ERROR_INT_STATUS_VENDOR_MASK 0xE000 +#define HOST_REG_ERROR_INT_STATUS_VENDOR_SHIFT 13 +#define HOST_REG_ERROR_INT_STATUS_SDMAERR (1 << 12) +#define HOST_REG_ERROR_INT_STATUS_ADMAERR (1 << 9) +#define HOST_REG_ERROR_INT_STATUS_AUTOCMD12ERR (1 << 8) +#define HOST_REG_ERROR_INT_STATUS_CURRENTLIMITERR (1 << 7) +#define HOST_REG_ERROR_INT_STATUS_DATAENDBITERR (1 << 6) +#define HOST_REG_ERROR_INT_STATUS_DATACRCERR (1 << 5) +#define HOST_REG_ERROR_INT_STATUS_DATATIMEOUTERR (1 << 4) +#define HOST_REG_ERROR_INT_STATUS_CMDINDEXERR (1 << 3) +#define HOST_REG_ERROR_INT_STATUS_CMDENDBITERR (1 << 2) +#define HOST_REG_ERROR_INT_STATUS_CRCERR (1 << 1) +#define HOST_REG_ERROR_INT_STATUS_CMDTIMEOUTERR (1 << 0) +#define HOST_REG_ERROR_INT_STATUS_ALL_ERR 0x7FF + +#define HOST_REG_INT_STATUS_ENABLE 0x34 +#define HOST_REG_INT_STATUS_CARD_INT_STAT_ENABLE (1 << 8) +#define HOST_REG_INT_STATUS_CARD_REMOVAL_ENABLE (1 << 7) +#define HOST_REG_INT_STATUS_CARD_INSERT_ENABLE (1 << 6) +#define HOST_REG_INT_STATUS_BUFFER_READ_RDY_ENABLE (1 << 5) +#define HOST_REG_INT_STATUS_BUFFER_WRITE_RDY_ENABLE (1 << 4) +#define HOST_REG_INT_STATUS_DMA_ENABLE (1 << 3) +#define HOST_REG_INT_STATUS_BLOCK_GAP_ENABLE (1 << 2) +#define HOST_REG_INT_STATUS_TRANSFER_COMPLETE_ENABLE (1 << 1) +#define HOST_REG_INT_STATUS_CMD_COMPLETE_ENABLE (1 << 0) +#define HOST_REG_INT_STATUS_ALL 0x00FB +#define HOST_REG_INT_STATUS_ALLOW_INSERT_REMOVE_ONLY 0x00C0 + +#define HOST_REG_ERR_STATUS_ENABLE 0x36 +/* same bits as HOST_REG_ERROR_INT_STATUS */ + +#define HOST_REG_INT_SIGNAL_ENABLE 0x38 +/* same bits as HOST_REG_INT_STATUS_ENABLE */ + +#define HOST_REG_INT_ERR_SIGNAL_ENABLE 0x3A +/* same bits as HOST_REG_ERR_STATUS_ENABLE */ + +#define HOST_REG_CAPABILITIES 0x40 +#define HOST_REG_CAPABILITIES_VOLT_1_8 (1 << 26) +#define HOST_REG_CAPABILITIES_VOLT_3_0 (1 << 25) +#define HOST_REG_CAPABILITIES_VOLT_3_3 (1 << 24) +#define HOST_REG_CAPABILITIES_SUSPEND_RESUME (1 << 23) +#define HOST_REG_CAPABILITIES_DMA (1 << 22) +#define HOST_REG_CAPABILITIES_HIGH_SPEED (1 << 21) +#define HOST_REG_CAPABILITIES_ADMA (1 << 20) +#define HOST_REG_CAPABILITIES_64 (1 << 19) +#define HOST_REG_CAPABILITIES_MMC8 (1 << 18) + +#define HOST_REG_CAPABILITIES_MAX_BLOCK_LEN_MASK 0x30000 +#define HOST_REG_CAPABILITIES_MAX_BLOCK_LEN_SHIFT 16 +#define HOST_REG_CAPABILITIES_CLOCK_MASK 0x3F00 +#define HOST_REG_CAPABILITIES_CLOCK_SHIFT 8 +#define HOST_REG_CAPABILITIES_TIMEOUT_CLOCK_UNITS (1 << 7) +#define HOST_REG_CAPABILITIES_TIMEOUT_FREQ_MASK 0x3F +#define HOST_REG_CAPABILITIES_TIMEOUT_FREQ_SHIFT 0 + +#define HOST_REG_MAX_CURRENT_CAPABILITIES 0x48 +#define HOST_REG_MAX_CURRENT_CAPABILITIES_1_8_MASK 0xFF0000 +#define HOST_REG_MAX_CURRENT_CAPABILITIES_1_8_SHIFT 16 +#define HOST_REG_MAX_CURRENT_CAPABILITIES_3_0_MASK 0x00FF00 +#define HOST_REG_MAX_CURRENT_CAPABILITIES_3_0_SHIFT 8 +#define HOST_REG_MAX_CURRENT_CAPABILITIES_3_3_MASK 0x0000FF +#define HOST_REG_MAX_CURRENT_CAPABILITIES_3_3_SHIFT 0 +#define HOST_REG_MAX_CURRENT_CAPABILITIES_SCALER 4 + +#define HOST_REG_ADMA_ERR_STATUS 0x54 +#define HOST_REG_ADMA_ERR_LEN_MISMATCH (1 << 2) +#define HOST_REG_ADMA_STATE_MASK 0x03 +#define HOST_REG_ADMA_STATE_SHIFT 0 +#define HOST_REG_ADMA_STATE_STOP 0x0 +#define HOST_REG_ADMA_STATE_FDS 0x1 +#define HOST_REG_ADMA_STATE_CADR 0x2 +#define HOST_REG_ADMA_STATE_TFR 0x3 + +#define HOST_REG_ADMA_ADDRESS 0x58 + +#define HOST_REG_SLOT_INT_STATUS 0xFC +#define HOST_REG_SLOT_INT_MASK 0xFF +#define HOST_REG_MAX_INT_SLOTS 8 + +#define HOST_REG_VERSION 0xFE +#define HOST_REG_VERSION_SPEC_VERSION_MASK 0xFF +#define HOST_REG_VERSION_VENDOR_VERSION_MASK 0xFF00 +#define HOST_REG_VERSION_VENDOR_VERSION_SHIFT 8 + +#define SDIO_BD_MAX_SLOTS 24 +#define SDIO_SD_MAX_BLOCKS ((UINT)0xFFFF) + +#define SD_DEFAULT_RESPONSE_BYTES 6 +#define SD_R2_RESPONSE_BYTES 16 +#define STD_HOST_SHORT_TRANSFER_THRESHOLD 32 +#define SD_CLOCK_MAX_ENTRIES 9 + +typedef struct _SD_CLOCK_TBL_ENTRY { + INT ClockRateDivisor; /* divisor */ + UINT16 RegisterValue; /* register value for clock divisor */ +}SD_CLOCK_TBL_ENTRY; + + /* standard host controller instance */ +typedef struct _SDHCD_INSTANCE { + SDLIST List; /* list */ + SDHCD Hcd; /* HCD structure for registration */ + SDDMA_DESCRIPTION DmaDescription; /* dma description for this HCD if used*/ + UINT32 Caps; /* host controller capabilities */ +#define SDHC_HW_INIT 0x01 +#define SDHC_REGISTERED 0x02 + UINT8 InitStateMask; /* init state for hardware independent layer */ + BOOL CardInserted; /* card inserted flag */ + BOOL Cancel; /* cancel flag */ + BOOL ShuttingDown; /* indicates shut down of HCD */ + BOOL StartUpCardCheckDone; + UINT32 BaseClock; /* base clock in hz */ + UINT32 TimeOut; /* timeout setting */ + UINT32 ClockSpinLimit; /* clock limit for command spin loops */ + BOOL KeepClockOn; + UINT32 BufferReadyWaitLimit; + UINT32 TransferCompleteWaitLimit; + UINT32 PresentStateWaitLimit; + UINT32 ResetWaitLimit; + BOOL RequestCompleteQueued; + PVOID pRegs; /* a more direct pointer to the registers */ + SDHCD_OS_SPECIFIC OsSpecific; +}SDHCD_INSTANCE, *PSDHCD_INSTANCE; + + +/* scatter-gather tables, as we use it in 32-bit mode */ +struct _SDHCD_SGDMA_DESCRIPTOR { + UINT32 Length; + UINT32 Address; +}CT_PACK_STRUCT; + + +typedef struct _SDHCD_SGDMA_DESCRIPTOR SDHCD_SGDMA_DESCRIPTOR; +typedef struct _SDHCD_SGDMA_DESCRIPTOR *PSDHCD_SGDMA_DESCRIPTOR; + +#define SDDMA_VALID 0x1 +#define SDDMA_END 0x2 +#define SDDMA_INT 0x4 +#define SDDMA_LENGTH 0x10 +#define SDDMA_TRANSFER 0x20 +#define SDDMA_DESCRIP_LINK 0x30 + +#define SET_DMA_LENGTH(d, l)\ + ((d)->Length = ((l) << 12) | SDDMA_LENGTH | SDDMA_VALID) +#define SET_DMA_ADDRESS(d, l)\ + ((d)->Address = ((l) & 0xFFFFF000) | SDDMA_TRANSFER | SDDMA_VALID) +#define SET_DMA_END_OF_TRANSFER(d)\ + ((d)->Address |= SDDMA_END); + +/* prototypes */ +SDIO_STATUS HcdRequest(PSDHCD pHcd); +SDIO_STATUS HcdConfig(PSDHCD pHcd, PSDCONFIG pReq); +SDIO_STATUS HcdInitialize(PSDHCD_INSTANCE pHcInstance); +void HcdDeinitialize(PSDHCD_INSTANCE pHcInstance); +BOOL HcdSDInterrupt(PSDHCD_INSTANCE pHcInstance); +void ProcessDeferredCardDetect(PSDHCD_INSTANCE pHcInstance); +SDIO_STATUS QueueEventResponse(PSDHCD_INSTANCE pHcInstance, INT WorkItemID); +BOOL HcdTransferTxData(PSDHCD_INSTANCE pHcInstance, PSDREQUEST pReq); +void HcdTransferRxData(PSDHCD_INSTANCE pHcInstance, PSDREQUEST pReq); +void SetPowerOn(PSDHCD_INSTANCE pHcInstance, BOOL On); +UINT16 MaskIrq(PSDHCD_INSTANCE pHcInstance, UINT32 Mask, BOOL FromIsr); +UINT16 UnmaskIrq(PSDHCD_INSTANCE pHcInstance, UINT32 Mask, BOOL FromIsr); +#define MaskIrqFromIsr(p,m) MaskIrq((p),(m),TRUE) +#define UnmaskIrqFromIsr(p,m) UnmaskIrq((p),(m),TRUE) + +void EnableDisableSDIOIRQ(PSDHCD_INSTANCE pHcInstance, BOOL Enable, BOOL FromIsr); +SDIO_STATUS SetUpHCDDMA(PSDHCD_INSTANCE pHcInstance, PSDREQUEST pReq); +void HcdTransferDataDMAEnd(PSDHCD_INSTANCE pHcInstance, PSDREQUEST pReq); +void DumpStdHcdRegisters(PSDHCD_INSTANCE pHcInstance); +void DumpDMADescriptorsInfo(PSDHCD_INSTANCE pHcInstance); +void DumpCurrentRequestInfo(PSDHCD_INSTANCE pHcInstance); + +#endif Index: linux-2.6.22/drivers/sdio/hcd/stdhost/sdio_std_hcd_linux.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/hcd/stdhost/sdio_std_hcd_linux.h 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,132 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_std_hcd_linux.h + +@abstract: include file for linux dependent code + +@notice: Copyright (c), 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 o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef __SDIO_STD_HCD_LINUX_H___ +#define __SDIO_STD_HCD_LINUX_H___ + +#include +#include +#include +#include +#include +#include + +#define SDHCD_MAX_DEVICE_NAME 64 + +/* Advance DMA parameters */ +#define SDHCD_MAX_ADMA_DESCRIPTOR 32 +#define SDHCD_ADMA_DESCRIPTOR_SIZE (SDHCD_MAX_ADMA_DESCRIPTOR * sizeof(SDHCD_SGDMA_DESCRIPTOR)) +#define SDHCD_MAX_ADMA_LENGTH 0x8000 /* up to 32KB per descriptor */ +#define SDHCD_ADMA_ADDRESS_MASK 0xFFFFE000 /* 4KB boundaries */ +#define SDHCD_ADMA_ALIGNMENT 0xFFF /* illegal alignment bits*/ +#define SDHCD_ADMA_LENGTH_ALIGNMENT 0x0 /* any length up to the max */ + +/* simple DMA */ +#define SDHCD_MAX_SDMA_DESCRIPTOR 1 +#define SDHCD_MAX_SDMA_LENGTH 0x80000 /* up to 512KB for a single descriptor*/ +#define SDHCD_SDMA_ADDRESS_MASK 0xFFFFFFFF /* any 32 bit address */ +#define SDHCD_SDMA_ALIGNMENT 0x0 /* any 32 bit address */ +#define SDHCD_SDMA_LENGTH_ALIGNMENT 0x0 /* any length up to the max */ + +#define HCD_COMMAND_MIN_POLLING_CLOCK 5000000 + +/* debounce delay for slot */ +#define SD_SLOT_DEBOUNCE_MS 1000 + +/* mapped memory address */ +typedef struct _SDHCD_MEMORY { + ULONG Raw; /* start of address range */ + ULONG Length; /* length of range */ + PVOID pMapped; /* the mapped address */ +}SDHCD_MEMORY, *PSDHCD_MEMORY; + +typedef struct _SDHCD_OS_SPECIFIC { + SDHCD_MEMORY Address; /* memory address of this device */ + spinlock_t RegAccessLock; /* use to protect registers when needed */ + struct work_struct iocomplete_work; /* work item definitions */ + struct work_struct carddetect_work; /* work item definintions */ + struct work_struct sdioirq_work; /* work item definintions */ + spinlock_t Lock; /* general purpose lock against the ISR */ + DMA_ADDRESS hDmaBuffer; /* handle for data buffer */ + PUINT8 pDmaBuffer; /* virtual address of DMA command buffer */ + PSDDMA_DESCRIPTOR pDmaList; /* in use scatter-gather list */ + UINT SGcount; /* count of in-use scatter gather list */ + UINT SlotNumber; /* the STD-host defined slot number assigned to this instance */ +/* everything below this line is used by the implementation that uses this STD core */ + UINT16 InitMask; /* implementation specific portion init mask */ + UINT32 ImpSpecific0; /* implementation specific storage */ + UINT32 ImpSpecific1; /* implementation specific storage */ +} SDHCD_OS_SPECIFIC, *PSDHCD_OS_SPECIFIC; + + +#define WORK_ITEM_IO_COMPLETE 0 +#define WORK_ITEM_CARD_DETECT 1 +#define WORK_ITEM_SDIO_IRQ 2 + +#define READ_HOST_REG32(pHcInstance, OFFSET) \ + _READ_DWORD_REG((((UINT32)((pHcInstance)->pRegs))) + (OFFSET)) +#define WRITE_HOST_REG32(pHcInstance, OFFSET, VALUE) \ + _WRITE_DWORD_REG((((UINT32)((pHcInstance)->pRegs))) + (OFFSET),(VALUE)) +#define READ_HOST_REG16(pHcInstance, OFFSET) \ + _READ_WORD_REG((((UINT32)((pHcInstance)->pRegs))) + (OFFSET)) +#define WRITE_HOST_REG16(pHcInstance, OFFSET, VALUE) \ + _WRITE_WORD_REG((((UINT32)((pHcInstance)->pRegs))) + (OFFSET),(VALUE)) +#define READ_HOST_REG8(pHcInstance, OFFSET) \ + _READ_BYTE_REG((((UINT32)((pHcInstance)->pRegs))) + (OFFSET)) +#define WRITE_HOST_REG8(pHcInstance, OFFSET, VALUE) \ + _WRITE_BYTE_REG((((UINT32)((pHcInstance)->pRegs))) + (OFFSET),(VALUE)) + +#define TRACE_SIGNAL_DATA_WRITE(pHcInstance, on) +#define TRACE_SIGNAL_DATA_READ(pHcInstance, on) +#define TRACE_SIGNAL_DATA_ISR(pHcInstance, on) +#define TRACE_SIGNAL_DATA_IOCOMP(pHcInstance, on) +#define TRACE_SIGNAL_DATA_TIMEOUT(pHcInstance, on) + + + +#define IS_HCD_ADMA(pHc) ((pHc)->Hcd.pDmaDescription != NULL) && \ + ((pHc)->Hcd.pDmaDescription->Flags & SDDMA_DESCRIPTION_FLAG_SGDMA) + +#define IS_HCD_SDMA(pHc) (((pHc)->Hcd.pDmaDescription != NULL) && \ + (((pHc)->Hcd.pDmaDescription->Flags & \ + (SDDMA_DESCRIPTION_FLAG_SGDMA | SDDMA_DESCRIPTION_FLAG_DMA)) == \ + SDDMA_DESCRIPTION_FLAG_DMA)) + +#endif Index: linux-2.6.22/drivers/sdio/hcd/stdhost/sdio_std_hcd_linux_lib.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/hcd/stdhost/sdio_std_hcd_linux_lib.h 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,79 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_std_hcd_linux_lib.h + +@abstract: include file for linux std host core APIs + +@notice: Copyright (c), 2006 Atheros Communications, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ +#ifndef SDIO_STD_HCD_LINUX_LIB_H_ +#define SDIO_STD_HCD_LINUX_LIB_H_ + +typedef struct _SDHCD_CORE_CONTEXT { + SDLIST List; + PVOID pBusContext; /* bus context this one belongs to */ + SDLIST SlotList; /* the list of current slots handled by this driver */ + spinlock_t SlotListLock; /* protection for the slot List */ + UINT SlotCount; /* number of slots currently installed */ + /* everything below this line is reserved for the user of this library */ + UINT32 CoreReserved1; + UINT32 CoreReserved2; +}SDHCD_CORE_CONTEXT, *PSDHCD_CORE_CONTEXT; + +void InitStdHostLib(void); +void DeinitStdHostLib(void); +PSDHCD_CORE_CONTEXT CreateStdHostCore(PVOID pBusContext); +void DeleteStdHostCore(PSDHCD_CORE_CONTEXT pStdCore); +PSDHCD_CORE_CONTEXT GetStdHostCore(PVOID pBusContext); + +INT GetCurrentHcdInstanceCount(PSDHCD_CORE_CONTEXT pStdCore); +PSDHCD_INSTANCE CreateStdHcdInstance(POS_DEVICE pOSDevice, + UINT SlotNumber, + PTEXT pName); +void DeleteStdHcdInstance(PSDHCD_INSTANCE pHcInstance); +#define START_HCD_FLAGS_FORCE_NO_DMA 0x01 /* don't use DMA even though capabilities indicate it can */ +#define START_HCD_FLAGS_FORCE_SDMA 0x02 /* force SDMA even though the capabilities show advance DMA support */ + +typedef SDIO_STATUS (*PPLAT_OVERRIDE_CALLBACK)(PSDHCD_INSTANCE); +SDIO_STATUS AddStdHcdInstance(PSDHCD_CORE_CONTEXT pStdCore, + PSDHCD_INSTANCE pHcInstance, + UINT Flags, + PPLAT_OVERRIDE_CALLBACK pCallBack, + SDDMA_DESCRIPTION *pSDMADescrip, + SDDMA_DESCRIPTION *pADMADescrip); +SDIO_STATUS StartStdHostCore(PSDHCD_CORE_CONTEXT pStdCore); +PSDHCD_INSTANCE RemoveStdHcdInstance(PSDHCD_CORE_CONTEXT pStdCore); +BOOL HandleSharedStdHostInterrupt(PSDHCD_CORE_CONTEXT pStdCore); +#endif /*SDIO_STD_HCD_LINUX_LIB_H_*/ Index: linux-2.6.22/drivers/sdio/hcd/stdhost/sdio_std_hcd_os.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.22/drivers/sdio/hcd/stdhost/sdio_std_hcd_os.c 2007-11-29 20:54:57.000000000 +0100 @@ -0,0 +1,826 @@ +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +@file: sdio_std_hcd_os.c + +@abstract: Generic Linux implementation for the Standard SDIO Host Controller Driver + +#notes: + +@notice: Copyright (c), 2006 Atheros Communications, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * Portions o this code were developed with information supplied from the + * SD Card Association Simplified Specifications. The following conditions and disclaimers may apply: + * + * The following conditions apply to the release of the SD simplified specification (“Simplified + * Specification”) by the SD Card Association. The Simplified Specification is a subset of the complete + * SD Specification which is owned by the SD Card Association. This Simplified Specification is provided + * on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified + * Specification may require a license from the SD Card Association or other third parties. + * Disclaimers: + * The information contained in the Simplified Specification is presented only as a standard + * specification for SD Cards and SD Host/Ancillary products and is provided "AS-IS" without any + * representations or warranties of any kind. No responsibility is assumed by the SD Card Association for + * any damages, any infringements of patents or other right of the SD Card Association or any third + * parties, which may result from its use. No license is granted by implication, estoppel or otherwise + * under any patent or other rights of the SD Card Association or any third party. Nothing herein shall + * be construed as an obligation by the SD Card Association to disclose or distribute any technical + * information, know-how or other confidential information to any third party. + * + * + * The initial developers of the original code are Seung Yi and Paul Lever + * + * sdio@atheros.com + * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/* debug level for this module*/ +#define DBG_DECLARE 4; +#include +#include "sdio_std_hcd.h" +#include "sdio_std_hcd_linux_lib.h" +#include +#include +#include + +static void hcd_iocomplete_wqueue_handler(void *context); +static void hcd_carddetect_wqueue_handler(void *context); +static void hcd_sdioirq_wqueue_handler(void *context); +static SDIO_STATUS SetupDmaBuffers(PSDHCD_INSTANCE pHcInstance); +static void DeinitializeStdHcdInstance(PSDHCD_INSTANCE pHcInstance); + +/* debug print parameter */ +module_param(debuglevel, int, 0644); +MODULE_PARM_DESC(debuglevel, "debuglevel 0-7, controls debug prints"); + + /* defaults for all std hosts, various attributes will be cleared based + * on values from the capabilities register */ +#define DEFAULT_ATTRIBUTES (SDHCD_ATTRIB_BUS_1BIT | \ + SDHCD_ATTRIB_BUS_4BIT | \ + SDHCD_ATTRIB_MULTI_BLK_IRQ | \ + SDHCD_ATTRIB_AUTO_CMD12 | \ + SDHCD_ATTRIB_POWER_SWITCH | \ + SDHCD_ATTRIB_BUS_MMC8BIT | \ + SDHCD_ATTRIB_SD_HIGH_SPEED | \ + SDHCD_ATTRIB_MMC_HIGH_SPEED) + +static UINT32 hcdattributes = DEFAULT_ATTRIBUTES; +module_param(hcdattributes, int, 0644); +MODULE_PARM_DESC(hcdattributes, "STD Host Attributes"); +static INT BaseClock = 0; +module_param(BaseClock, int, 0444); +MODULE_PARM_DESC(BaseClock, "BaseClock Hz when not present in configuration"); +static UINT32 timeout = HOST_REG_TIMEOUT_CONTROL_DEFAULT; +module_param(timeout, int, 0644); +MODULE_PARM_DESC(timeout, "STD Host data timeout control"); +static UINT32 ClockSpinLimit = HCD_COMMAND_MIN_POLLING_CLOCK; +module_param(ClockSpinLimit, int, 0644); +MODULE_PARM_DESC(ClockSpinLimit, "STD Host command clock spin time"); + +typedef struct _STDHCD_DEV { + SDLIST CoreList; /* the list of core contexts */ + spinlock_t CoreListLock; /* protection for the list */ +}STDHCD_DEV, *PSTDHCD_DEV; + +STDHCD_DEV StdDevices; + +void InitStdHostLib() +{ + ZERO_POBJECT(&StdDevices); + SDLIST_INIT(&StdDevices.CoreList); + spin_lock_init(&StdDevices.CoreListLock); +} + +void DeinitStdHostLib() +{ + + +} + +PSDHCD_CORE_CONTEXT CreateStdHostCore(PVOID pBusContext) +{ + PSDHCD_CORE_CONTEXT pStdCore = NULL; + + do { + pStdCore = KernelAlloc(sizeof(SDHCD_CORE_CONTEXT)); + if (NULL == pStdCore) { + break; + } + ZERO_POBJECT(pStdCore); + SDLIST_INIT(&pStdCore->SlotList); + spin_lock_init(&pStdCore->SlotListLock); + pStdCore->pBusContext = pBusContext; + + /* add it */ + spin_lock_irq(&StdDevices.CoreListLock); + SDListInsertHead(&StdDevices.CoreList, &pStdCore->List); + spin_unlock_irq(&StdDevices.CoreListLock); + } while (FALSE); + + return pStdCore; +} + +void DeleteStdHostCore(PSDHCD_CORE_CONTEXT pStdCore) +{ + spin_lock_irq(&StdDevices.CoreListLock); + /* remove */ + SDListRemove(&pStdCore->List); + spin_unlock_irq(&StdDevices.CoreListLock); + + KernelFree(pStdCore); +} + +/* find the std core associated with this bus context */ +PSDHCD_CORE_CONTEXT GetStdHostCore(PVOID pBusContext) +{ + PSDLIST pListItem; + PSDHCD_CORE_CONTEXT pStdCore = NULL; + + spin_lock_irq(&StdDevices.CoreListLock); + + do { + if (SDLIST_IS_EMPTY(&StdDevices.CoreList)) { + break; + } + + SDITERATE_OVER_LIST(&StdDevices.CoreList, pListItem) { + pStdCore = CONTAINING_STRUCT(pListItem, SDHCD_CORE_CONTEXT, List); + if (pStdCore->pBusContext == pBusContext) { + /* found it */ + break; + } + pStdCore = NULL; + } + + } while (FALSE); + + spin_unlock_irq(&StdDevices.CoreListLock); + return pStdCore; +} + +/* create a standard host memory instance */ +PSDHCD_INSTANCE CreateStdHcdInstance(POS_DEVICE pOSDevice, + UINT SlotNumber, + PTEXT pName) +{ + PSDHCD_INSTANCE pHcInstance = NULL; + BOOL success = FALSE; + + do { + /* allocate an instance for this new device */ + pHcInstance = (PSDHCD_INSTANCE)KernelAlloc(sizeof(SDHCD_INSTANCE)); + + if (pHcInstance == NULL) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD Host: CreateStdHcdInstance - no memory for instance\n")); + break; + } + + ZERO_POBJECT(pHcInstance); + SET_SDIO_STACK_VERSION(&pHcInstance->Hcd); + + pHcInstance->OsSpecific.SlotNumber = SlotNumber; + spin_lock_init(&pHcInstance->OsSpecific.Lock); + spin_lock_init(&pHcInstance->OsSpecific.RegAccessLock); + /* initialize work items */ + INIT_WORK(&(pHcInstance->OsSpecific.iocomplete_work), hcd_iocomplete_wqueue_handler, pHcInstance); + INIT_WORK(&(pHcInstance->OsSpecific.carddetect_work), hcd_carddetect_wqueue_handler, pHcInstance); + INIT_WORK(&(pHcInstance->OsSpecific.sdioirq_work), hcd_sdioirq_wqueue_handler, pHcInstance); + /* allocate space for the name */ + pHcInstance->Hcd.pName = (PTEXT)KernelAlloc(strlen(pName)+1); + if (NULL == pHcInstance->Hcd.pName) { + break; + } + strcpy(pHcInstance->Hcd.pName,pName); + /* set OS device for DMA allocations and mapping */ + pHcInstance->Hcd.pDevice = pOSDevice; + pHcInstance->Hcd.Attributes = hcdattributes; + pHcInstance->Hcd.MaxBlocksPerTrans = SDIO_SD_MAX_BLOCKS; + pHcInstance->Hcd.pContext = pHcInstance; + pHcInstance->Hcd.pRequest = HcdRequest; + pHcInstance->Hcd.pConfigure = HcdConfig; + pHcInstance->Hcd.pModule = THIS_MODULE; + pHcInstance->BaseClock = BaseClock; + pHcInstance->TimeOut = timeout; + pHcInstance->ClockSpinLimit = ClockSpinLimit; + + success = TRUE; + } while (FALSE); + + if (!success && (pHcInstance != NULL)) { + DeleteStdHcdInstance(pHcInstance); + } + + return pHcInstance; +} + +/* + * AddStdHcdInstance - add the std host controller instance +*/ +SDIO_STATUS AddStdHcdInstance(PSDHCD_CORE_CONTEXT pStdCore, + PSDHCD_INSTANCE pHcInstance, + UINT Flags, + PPLAT_OVERRIDE_CALLBACK pCallBack, + SDDMA_DESCRIPTION *pSDMADescrip, + SDDMA_DESCRIPTION *pADMADescrip) +{ + + SDIO_STATUS status = SDIO_STATUS_SUCCESS; + + do { + + if (!SDIO_SUCCESS((status = HcdInitialize(pHcInstance)))) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD HOST: StartStdHcdInstance - failed to init HW, status =%d\n",status)); + break; + } + /* mark that the hardware was initialized */ + pHcInstance->InitStateMask |= SDHC_HW_INIT; + + pHcInstance->Hcd.pDmaDescription = NULL; + + if (!(Flags & START_HCD_FLAGS_FORCE_NO_DMA)) { + /* check DMA parameters discovered by HcdInitialize */ + if (!(Flags & START_HCD_FLAGS_FORCE_SDMA) && + (pHcInstance->Caps & HOST_REG_CAPABILITIES_ADMA) && + (pADMADescrip != NULL)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST: StartStdHcdInstance - using Advanced DMA\n")); + /* copy the DMA description for advanced DMA */ + memcpy(&pHcInstance->DmaDescription, pADMADescrip, sizeof(SDDMA_DESCRIPTION)); + /* set DMA description */ + pHcInstance->Hcd.pDmaDescription = &pHcInstance->DmaDescription; + } else if ((pHcInstance->Caps & HOST_REG_CAPABILITIES_DMA) && + (pSDMADescrip != NULL)) { + DBG_PRINT(SDDBG_TRACE, ("SDIO STD HOST: StartStdHcdInstance - using Simple DMA\n")); + /* copy the DMA description for advanced DMA */ + memcpy(&pHcInstance->DmaDescription, pSDMADescrip, sizeof(SDDMA_DESCRIPTION)); + /* set DMA description */ + pHcInstance->Hcd.pDmaDescription = &pHcInstance->DmaDescription; + } + } + + if (IS_HCD_ADMA(pHcInstance)) { + /* setup DMA buffers for scatter-gather descriptor tables used in advanced DMA */ + status = SetupDmaBuffers(pHcInstance); + if (!SDIO_SUCCESS(status)) { + DBG_PRINT(SDDBG_ERROR, ("SDIO STD Host : StartStdHcdInstance - failed to setup DMA buffer\n")); + break; + } + } + + if (pCallBack != NULL) { + /* allow the platform to override any settings */ + status = pCallBack(pHcInstance); + if (!SDIO_SUCCESS(status)) { + break; + } + } + /* add this instance to our list, we will start the HCDs later */ + /* protect the devicelist */ + spin_lock_irq(&pStdCore->SlotListLock); + SDListInsertHead(&pStdCore->SlotList, &pHcInstance->List); + pStdCore->SlotCount++; + spin_unlock_irq(&pStdCore->SlotListLock); + + } while (FALSE); + + + if (SDIO_SUCCESS(status))