From 3438ba21bb9449925f7a32e100e8fa485c8e243d Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sat, 11 Apr 2009 18:20:16 +0200 Subject: [PATCH] port of the BSD sdhc driver. breaks sd support atm --- bsdtypes.h | 29 + diskio.c | 20 +- errno.h | 175 +++++ hollywood.h | 4 +- ipc.c | 4 +- irq.c | 7 + irq.h | 3 +- main.c | 7 +- sdhc.c | 2080 ++++++++++++++++++++++++--------------------------- sdhcreg.h | 189 +++++ sdhcvar.h | 48 ++ sdmmcchip.h | 88 +++ sdmmcreg.h | 231 ++++++ sdmmcvar.h | 280 +++++++ 14 files changed, 2039 insertions(+), 1126 deletions(-) create mode 100644 bsdtypes.h create mode 100644 errno.h create mode 100644 sdhcreg.h create mode 100644 sdhcvar.h create mode 100644 sdmmcchip.h create mode 100644 sdmmcreg.h create mode 100644 sdmmcvar.h diff --git a/bsdtypes.h b/bsdtypes.h new file mode 100644 index 0000000..b752b2e --- /dev/null +++ b/bsdtypes.h @@ -0,0 +1,29 @@ +#ifndef __BSDTYPES_H__ +#define __BSDTYPES_H__ + +#include "types.h" +#include "errno.h" + +typedef u32 u_int; +typedef u32 u_int32_t; +typedef u16 u_int16_t; +typedef u8 u_int8_t; +typedef u8 u_char; + +typedef u32 bus_space_tag_t; +typedef u32 bus_space_handle_t; + +struct device { + char dv_xname[255]; + void *dummy; +}; + +#define MIN(a, b) (((a)>(b))?(b):(a)) + +#define wakeup(...) + +#define bzero(mem, size) memset(mem, 0, size) + +#define ISSET(var, mask) (((var) & (mask)) == (mask) ? 1 : 0) +#define SET(var, mask) ((var) |= (mask)) +#endif diff --git a/diskio.c b/diskio.c index 3dcb975..9bd78eb 100644 --- a/diskio.c +++ b/diskio.c @@ -7,13 +7,13 @@ #include "diskio.h" #include "string.h" -#include "sdhc.h" +//#include "sdhc.h" #ifndef MEM2_BSS #define MEM2_BSS #endif -static sdhci_t sdhci; +//static sdhci_t sdhci; static u8 buffer[512] MEM2_BSS ALIGNED(32); /*-----------------------------------------------------------------------*/ @@ -25,8 +25,8 @@ DSTATUS disk_initialize ( { s32 ret; - sd_init(&sdhci, 0); - ret = sd_mount(&sdhci); +// sd_init(&sdhci, 0); +// ret = sd_mount(&sdhci); if (ret < 0) return STA_NOINIT; @@ -43,8 +43,8 @@ DSTATUS disk_status ( BYTE drv /* Physical drive nmuber (0..) */ ) { - if (sd_inserted(&sdhci) == 0) - return STA_NODISK; +// if (sd_inserted(&sdhci) == 0) +// return STA_NODISK; return 0; } @@ -66,10 +66,10 @@ DRESULT disk_read ( res = RES_OK; for (i = 0; i < count; i++) { - if (sd_read(&sdhci, sector + i, 1, buffer) != 0) { +/* if (sd_read(&sdhci, sector + i, 1, buffer) != 0) { res = RES_ERROR; break; - } + }*/ memcpy(buff + i * 512, buffer, 512); } @@ -97,10 +97,10 @@ DRESULT disk_write ( for (i = 0; i < count; i++) { memcpy(buffer, buff + i * 512, 512); - if(sd_write(&sdhci, sector + i, 1, buffer) != 0) { +/* if(sd_write(&sdhci, sector + i, 1, buffer) != 0) { res = RES_ERROR; break; - } + }*/ } return res; diff --git a/errno.h b/errno.h new file mode 100644 index 0000000..c13ef29 --- /dev/null +++ b/errno.h @@ -0,0 +1,175 @@ +/* $OpenBSD: errno.h,v 1.20 2007/09/03 14:37:52 millert Exp $ */ +/* $NetBSD: errno.h,v 1.10 1996/01/20 01:33:53 jtc Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)errno.h 8.5 (Berkeley) 1/21/94 + */ + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* Input/output error */ +#define ENXIO 6 /* Device not configured */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file descriptor */ +#define ECHILD 10 /* No child processes */ +#define EDEADLK 11 /* Resource deadlock avoided */ + /* 11 was EAGAIN */ +#define ENOMEM 12 /* Cannot allocate memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#if __BSD_VISIBLE +#define ENOTBLK 15 /* Block device required */ +#endif +#define EBUSY 16 /* Device busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* Operation not supported by device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* Too many open files in system */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Inappropriate ioctl for device */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ + +/* math software */ +#define EDOM 33 /* Numerical argument out of domain */ +#define ERANGE 34 /* Result too large */ + +/* non-blocking and interrupt i/o */ +#define EAGAIN 35 /* Resource temporarily unavailable */ +#define EWOULDBLOCK EAGAIN /* Operation would block */ +#define EINPROGRESS 36 /* Operation now in progress */ +#define EALREADY 37 /* Operation already in progress */ + +/* ipc/network software -- argument errors */ +#define ENOTSOCK 38 /* Socket operation on non-socket */ +#define EDESTADDRREQ 39 /* Destination address required */ +#define EMSGSIZE 40 /* Message too long */ +#define EPROTOTYPE 41 /* Protocol wrong type for socket */ +#define ENOPROTOOPT 42 /* Protocol not available */ +#define EPROTONOSUPPORT 43 /* Protocol not supported */ +#if __BSD_VISIBLE +#define ESOCKTNOSUPPORT 44 /* Socket type not supported */ +#endif +#define EOPNOTSUPP 45 /* Operation not supported */ +#if __BSD_VISIBLE +#define EPFNOSUPPORT 46 /* Protocol family not supported */ +#endif +#define EAFNOSUPPORT 47 /* Address family not supported by protocol family */ +#define EADDRINUSE 48 /* Address already in use */ +#define EADDRNOTAVAIL 49 /* Can't assign requested address */ + +/* ipc/network software -- operational errors */ +#define ENETDOWN 50 /* Network is down */ +#define ENETUNREACH 51 /* Network is unreachable */ +#define ENETRESET 52 /* Network dropped connection on reset */ +#define ECONNABORTED 53 /* Software caused connection abort */ +#define ECONNRESET 54 /* Connection reset by peer */ +#define ENOBUFS 55 /* No buffer space available */ +#define EISCONN 56 /* Socket is already connected */ +#define ENOTCONN 57 /* Socket is not connected */ +#if __BSD_VISIBLE +#define ESHUTDOWN 58 /* Can't send after socket shutdown */ +#define ETOOMANYREFS 59 /* Too many references: can't splice */ +#endif /* __BSD_VISIBLE */ +#define ETIMEDOUT 60 /* Operation timed out */ +#define ECONNREFUSED 61 /* Connection refused */ + +#define ELOOP 62 /* Too many levels of symbolic links */ +#define ENAMETOOLONG 63 /* File name too long */ + +/* should be rearranged */ +#if __BSD_VISIBLE +#define EHOSTDOWN 64 /* Host is down */ +#define EHOSTUNREACH 65 /* No route to host */ +#endif /* __BSD_VISIBLE */ +#define ENOTEMPTY 66 /* Directory not empty */ + +/* quotas & mush */ +#if __BSD_VISIBLE +#define EPROCLIM 67 /* Too many processes */ +#define EUSERS 68 /* Too many users */ +#endif /* __BSD_VISIBLE */ +#define EDQUOT 69 /* Disk quota exceeded */ + +/* Network File System */ +#define ESTALE 70 /* Stale NFS file handle */ +#if __BSD_VISIBLE +#define EREMOTE 71 /* Too many levels of remote in path */ +#define EBADRPC 72 /* RPC struct is bad */ +#define ERPCMISMATCH 73 /* RPC version wrong */ +#define EPROGUNAVAIL 74 /* RPC prog. not avail */ +#define EPROGMISMATCH 75 /* Program version wrong */ +#define EPROCUNAVAIL 76 /* Bad procedure for program */ +#endif /* __BSD_VISIBLE */ + +#define ENOLCK 77 /* No locks available */ +#define ENOSYS 78 /* Function not implemented */ + +#if __BSD_VISIBLE +#define EFTYPE 79 /* Inappropriate file type or format */ +#define EAUTH 80 /* Authentication error */ +#define ENEEDAUTH 81 /* Need authenticator */ +#define EIPSEC 82 /* IPsec processing failure */ +#define ENOATTR 83 /* Attribute not found */ +#endif /* __BSD_VISIBLE */ +#define EILSEQ 84 /* Illegal byte sequence */ +#if __BSD_VISIBLE +#define ENOMEDIUM 85 /* No medium found */ +#define EMEDIUMTYPE 86 /* Wrong Medium Type */ +#define EOVERFLOW 87 /* Conversion overflow */ +#define ECANCELED 88 /* Operation canceled */ +#endif /* __BSD_VISIBLE */ +#define EIDRM 89 /* Identifier removed */ +#define ENOMSG 90 /* No message of desired type */ +#if __BSD_VISIBLE +#define ELAST 90 /* Must be equal largest errno */ +#endif /* __BSD_VISIBLE */ + +#ifdef _KERNEL +/* pseudo-errors returned inside kernel to modify return to process */ +#define ERESTART -1 /* restart syscall */ +#define EJUSTRETURN -2 /* don't modify regs, just return */ +#endif diff --git a/hollywood.h b/hollywood.h index 1be2ba9..8a42b02 100644 --- a/hollywood.h +++ b/hollywood.h @@ -154,8 +154,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA /* SD Host Controller Registers */ -#define SD_REG_BASE 0xd070000 +#define SDHC_REG_BASE 0xd070000 +#if 0 #define SDHC_SDMA_ADDR (0x000) #define SDHC_BLOCK_SIZE (0x004) #define SDHC_BLOCK_COUNT (0x006) @@ -187,6 +188,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #define SDHC_ADMA_SYSTEM_ADDR (0x058) #define SDHC_SLOT_INTERRUPT_STATUS (0x0fc) #define SDHC_VERSION (0x0fe) +#endif /* EXI Registers */ diff --git a/ipc.c b/ipc.c index 146c535..b3eafd4 100644 --- a/ipc.c +++ b/ipc.c @@ -30,7 +30,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "gecko.h" #include "ipc.h" #include "nand.h" -#include "sdhc.h" +//#include "sdhc.h" #include "crypto.h" #include "boot2.h" #include "powerpc.h" @@ -150,7 +150,7 @@ static u32 process_slow(volatile ipc_request *req) nand_ipc(req); break; case IPC_DEV_SD: - sd_ipc(req); +// sd_ipc(req); break; case IPC_DEV_KEYS: crypto_ipc(req); diff --git a/irq.c b/irq.c index bf7156c..a449496 100644 --- a/irq.c +++ b/irq.c @@ -26,6 +26,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "ipc.h" #include "crypto.h" #include "nand.h" +#include "sdhcvar.h" static u32 _alarm_frequency = 0; @@ -98,6 +99,12 @@ void irq_handler(void) gecko_printf("IRQ: AES\n"); write32(HW_ARMIRQFLAG, IRQF_AES); } + if (flags & IRQF_SDHC) { + gecko_printf("IRQ: SDHC\n"); + write32(HW_ARMIRQFLAG, IRQF_SDHC); + sdhc_irq(); + } + flags &= ~IRQF_ALL; if(flags) { gecko_printf("IRQ: unknown 0x%08x\n", flags); diff --git a/irq.h b/irq.h index 33ec460..ac80972 100644 --- a/irq.h +++ b/irq.h @@ -40,6 +40,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #define IRQF_TIMER (1< - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 2. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -*/ - -/* parts based on: - * * "SD Host Controller driver based on the SD Host Controller Standard" copyright (c) 2006 Uwe Stuehler - * * Simplified SD Host Controller Standard + * Copyright (c) 2006 Uwe Stuehler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include "hollywood.h" -#include "sdhc.h" -#include "utils.h" +/* + * SD Host Controller driver based on the SD Host Controller Standard + * Simplified Specification Version 1.00 (www.sdcard.com). + */ + + +#if 0 +#include +#include +#include +#include +#include +#include +#endif + +#include "bsdtypes.h" +#include "sdhcreg.h" +#include "sdhcvar.h" +#include "sdmmcchip.h" +#include "sdmmcreg.h" +#include "sdmmcvar.h" +#include "gecko.h" #include "string.h" -#include "start.h" -#include "memory.h" +#include "irq.h" +#include "utils.h" -#define _READONLY 1 +#define SDHC_DEBUG 1 -#define SDHC_PRINT_ERROR 1 -//#define SDHC_DEBUG 1 -//#define SDHC_DEBUG_V 1 +#define SDHC_COMMAND_TIMEOUT 0 +#define SDHC_BUFFER_TIMEOUT 0 +#define SDHC_TRANSFER_TIMEOUT 0 -#if defined(SDHC_DEBUG_V) && !defined(SDHC_DEBUG) -# define SDHC_DEBUG 1 -#endif +struct sdhc_host { + struct sdhc_softc *sc; /* host controller device */ + struct device *sdmmc; /* generic SD/MMC device */ + bus_space_tag_t iot; /* host register set tag */ + bus_space_handle_t ioh; /* host register set handle */ + u_int clkbase; /* base clock frequency in KHz */ + int maxblklen; /* maximum block length */ + int flags; /* flags for this host */ + u_int32_t ocr; /* OCR value from capabilities */ + u_int8_t regs[14]; /* host controller state */ + u_int16_t intr_status; /* soft interrupt status */ + u_int16_t intr_error_status; /* soft error status */ +}; -#ifdef SDHC_DEBUG -# include "gecko.h" -# define sdhc_debug(reg, f, arg...) do { gecko_printf("sdhc%d: " f "\n", ((reg - SD_REG_BASE) / 0x100), ##arg); } while(0) -#else -# define sdhc_debug(reg, f, arg...) udelay(100); -#endif +#define HDEVNAME(hp) ((hp)->sc->sc_dev.dv_xname) +#define sdmmc_delay(t) udelay(t) -#ifdef SDHC_PRINT_ERROR -# include "gecko.h" -# define sdhc_error(reg, f, arg...) do { gecko_printf("sdhc%d: " f "\n", ((reg - SD_REG_BASE) / 0x100), ##arg); } while(0) -#else -# define sdhc_error(reg, f, arg...) -#endif - -#define SDHC_SOFTWARE_RESET_DAT (1 << 2) -#define SDHC_SOFTWARE_RESET_CMD (1 << 1) -#define SDHC_SOFTWARE_RESET_ALL (7) - -#define SDHC_TIMEOUT_MAX (0x0e) - -#define SDHC_BFREQ_KHZ(c) ((((c) >> 8) & 0x3f) * 1000) - -#define SDHC_CAP_VOLTAGE_33 (1 << 24) -#define SDHC_CAP_VOLTAGE_30 (1 << 25) -#define SDHC_CAP_VOLTAGE_18 (1 << 26) - -#define SDHC_CAP_SDMA (1 << 22) - -#define SDHC_PCTRL_VOLTAGE_SHIFT 1 -#define SDHC_PCTRL_VOLTAGE_33 (0x07 << SDHC_PCTRL_VOLTAGE_SHIFT) -#define SDHC_PCTRL_VOLTAGE_30 (0x06 << SDHC_PCTRL_VOLTAGE_SHIFT) -#define SDHC_PCTRL_VOLTAGE_18 (0x05 << SDHC_PCTRL_VOLTAGE_SHIFT) -#define SDHC_PCTRL_ENABLE 1 - -#define SDHC_CLOCK_INTERNAL_ENABLE (1 << 0) -#define SDHC_CLOCK_INTERNAL_STABLE (1 << 1) -#define SDHC_CLOCK_SD_ENABLE (1 << 2) - -#define SDHC_CARD_INSERTED (1 << 16) -#define SDHC_WRITE_PROTECT (1 << 19) - -#define SDHC_BLOCKS_MAX 65535 - -#define SDHC_CMDMODE_MULTIBLOCK (1 << 5) -#define SDHC_CMDMODE_READ (1 << 4) -#define SDHC_CMDMODE_WRITE (0 << 4) -#define SDHC_CMDMODE_ACMD12_ENABLE (1 << 2) -#define SDHC_CMDMODE_BLOCKCNT_ENABLE (1 << 1) -#define SDHC_CMDMODE_DMA_ENABLE (1 << 0) - -#define SDHC_CMD_DATA (1 << 5) -#define SDHC_CMD_IDXCHECK (1 << 4) -#define SDHC_CMD_CRC (1 << 3) - -#define SDHC_CMDTEST_READ 0x40 - -#define SDHC_CMD_MASK 0xff -#define SDHC_CMD_SHIFT 8 - -#define SDHC_CMD_NORESP 0 -#define SDHC_CMD_RESP_136 1 -#define SDHC_CMD_RESP_48 2 -#define SDHC_CMD_RESP_BUSY 3 - -#define SDHC_PRESENT_CMD_INHIBIT_CMD 1 -#define SDHC_PRESENT_CMD_INHIBIT_DAT 2 -#define SDHC_PRESENT_CMD_INHIBIT_BOTH 3 - -#define SDHC_BFR_READ_ENABLE (1 << 11) -#define SDHC_BFR_WRITE_ENABLE (1 << 10) - -#define SDHC_WAIT_TIMEOUT 10000 -#define SDHC_WAIT_TIMEOUT_MULTIPLY 50 -#define SDHC_WAIT_TIMEOUT_OUTER 50000 -#define SDHC_WAIT_TIMEOUT_OUTER_MULTIPLY 5 - - -#define SDHC_INTERRUPT_DMA (1 << 3) -#define SDHC_INTERRUPT_TRANSF_COMPLETE (1 << 1) - -#define SD_RSP_BUSY 0x0100 -#define SD_RSP_136 0x0200 -#define SD_RSP_CRC 0x0400 -#define SD_RSP_IDX 0x0800 -#define SD_RSP_PRESENT 0x1000 - -#define SD_R0 0 -#define SD_R1 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX) -#define SD_R1B (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX | SD_RSP_BUSY) -#define SD_R2 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_136) -#define SD_R3 SD_RSP_PRESENT -#define SD_R4 SD_RSP_PRESENT -#define SD_R5 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX) -#define SD_R5B (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX | SD_RSP_BUSY) -#define SD_R6 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX) -#define SD_R7 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX) - -#define SD_READ 0x10000 - -#define SD_CMD_ACMD 0xC0 - -#define SD_CMD_RESET_CARD 0 -#define SD_CMD_ALL_SEND_CID 2 -#define SD_CMD_SEND_RELATIVE_ADDR 3 -#define SD_CMD_SELECT_CARD 7 -#define SD_CMD_SEND_IF_COND 8 -#define SD_CMD_SEND_CSD 9 -#define SD_CMD_SEND_STATUS 13 -#define SD_CMD_SET_BLOCKLEN 16 -#define SD_CMD_READ_MULTIPLE_BLOCK 18 -#define SD_CMD_WRITE_MULTIPLE_BLOCK 25 -#define SD_CMD_APP 55 -#define SD_CMD_READ_OCR 58 -#define SD_CMD_APP_SET_BUS_WIDTH (SD_CMD_ACMD + 6) -#define SD_CMD_APP_SEND_OP_COND (SD_CMD_ACMD + 41) - -#define SDHC_HCR_BUSWIDTH_4 2 - -#define BLOCK_SIZE 512 -#define BLOCK_SIZE_512K 7 /* SDMA block size */ - -#define SDMA_BLOCK_SIZE (512 * 1024) - -#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */ -#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */ -#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */ -#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */ -#define MMC_VDD_165_195 0x00000080 - -#define OCR_POWERUP_STATUS (1 << 31) -#define OCR_HCS (1 << 30) -#define OCR_CCS OCR_HCS - -#define INTERRUPT_ERROR (1 << 15) -#define INTERRUPT_CARD (1 << 8) -#define INTERRUPT_CARD_REMOVAL (1 << 7) -#define INTERRUPT_CARD_INSERTION (1 << 6) -#define INTERRUPT_BUFFER_READ_READY (1 << 5) -#define INTERRUPT_BUFFER_WRITE_READY (1 << 4) -#define INTERRUPT_DMA (1 << 3) -#define INTERRUPT_BLOCK_GAP_EVENT (1 << 2) -#define INTERRUPT_TRANSFER_COMPLETE (1 << 1) -#define INTERRUPT_COMMAND_COMPLETE (1 << 0) - -#define INTERRUPT_ALL 0x81ff - -#define EINTERRUPT_ADMA (1 << 9) -#define EINTERRUPT_ACMD12 (1 << 8) -#define EINTERRUPT_CURRENT_LIMIT (1 << 7) -#define EINTERRUPT_DATA_END_BIT (1 << 6) -#define EINTERRUPT_DATA_CRC (1 << 5) -#define EINTERRUPT_DATA_TIMEOUT (1 << 4) -#define EINTERRUPT_CMD_INDEX (1 << 3) -#define EINTERRUPT_CMD_END_BIT (1 << 2) -#define EINTERRUPT_CMD_CRC (1 << 1) -#define EINTERRUPT_CMD_TIMEOUT (1 << 0) - -#define EINTERRUPT_ALL 0x3ff - -static int ipc_code = 0; -static int ipc_tag = 0; -static sdhci_t sdhci; - -// not currently used :( -void sd_irq(void) +static inline u32 bus_space_read_4(bus_space_tag_t iot, bus_space_handle_t ioh, u32 reg) { - int code, tag; - if (ipc_code != 0) { - code = ipc_code; - tag = ipc_tag; - ipc_code = ipc_tag = 0; - ipc_post(code, tag, 0); - } + return read32(ioh + reg); } -unsigned int bswap32(unsigned int input) { - return ((input & 0x000000FF) << 24) | ((input & 0x0000FF00) << 8) | - ((input & 0x00FF0000) >> 8) | ((input & 0xFF000000) >> 24); +static inline u16 bus_space_read_2(bus_space_tag_t iot, bus_space_handle_t ioh, u32 reg) +{ + if(reg & 3) + return (read32((ioh + reg) & ~3) & 0xffff0000) >> 16; + else + return (read32(ioh + reg) & 0xffff); } -u8 __sd_read8(u32 addr) +static inline u8 bus_space_read_1(bus_space_tag_t iot, bus_space_handle_t ioh, u32 reg) { u32 mask; + u32 addr; u8 shift; - shift = (addr & 3) * 8; + shift = (reg & 3) * 8; mask = (0xFF << shift); + addr = ioh + reg; return (read32(addr & ~3) & mask) >> shift; } -u16 __sd_read16(u32 addr) +static inline void bus_space_write_4(bus_space_tag_t iot, bus_space_handle_t ioh, u32 r, u32 v) { - if(addr & 3) - return (read32(addr & ~3) & 0xffff0000) >> 16; + write32(ioh + r, v); +} + +static inline void bus_space_write_2(bus_space_tag_t iot, bus_space_handle_t ioh, u32 r, u16 v) +{ + if(r & 3) + mask32((ioh + r) & ~3, 0xffff0000, v << 16); else - return (read32(addr) & 0xffff); + mask32((ioh + r), 0xffff, ((u32)v)); } -inline u32 __sd_read32(u32 addr) -{ - return read32(addr); -} - -void __sd_write8(u32 addr, u8 data) +static inline void bus_space_write_1(bus_space_tag_t iot, bus_space_handle_t ioh, u32 r, u8 v) { u32 mask; + u32 addr; u8 shift; - shift = (addr & 3) * 8; + shift = (r & 3) * 8; mask = (0xFF << shift); + addr = ioh + r; - mask32(addr & ~3, mask, data << shift); + mask32(addr & ~3, mask, v << shift); } -void __sd_write16(u32 addr, u16 data) +u32 splbio() { - if(addr & 3) - mask32(addr & ~3, 0xffff0000, data << 16); - else - mask32(addr, 0xffff, ((u32)data)); -} - -inline void __sd_write32(u32 addr, u32 data) -{ - write32(addr, data); -} - - -#if 0 -static int __sd_wait8(u32 addr, u8 mask) -{ - u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; - do - { - if(__sd_read8(addr) & mask) - return 0; - udelay(SDHC_WAIT_TIMEOUT); - } - while(timeout--); - return SDHC_ETIMEDOUT; -} -#endif - -static int __sd_wait16(u32 addr, u16 mask) -{ - u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; - do - { - if(__sd_read16(addr) & mask) - return 0; - udelay(SDHC_WAIT_TIMEOUT); - } - while(timeout--); - return SDHC_ETIMEDOUT; -} - -static int __sd_wait32(u32 addr, u32 mask) -{ - u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; - do - { - if(__sd_read32(addr) & mask) - return 0; - udelay(SDHC_WAIT_TIMEOUT); - } - while(timeout--); - return SDHC_ETIMEDOUT; -} - -static int __sd_wait8_r(u32 addr, u8 mask) -{ - u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; - do - { - if(!(__sd_read8(addr) & mask)) - return 0; - udelay(SDHC_WAIT_TIMEOUT); - } - while(timeout--); - return SDHC_ETIMEDOUT; -} - -#if 0 -static int __sd_wait16_r(u32 addr, u16 mask) -{ - u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; - do - { - if(!(__sd_read16(addr) & mask)) - return 0; - udelay(SDHC_WAIT_TIMEOUT); - } - while(timeout--); - return SDHC_ETIMEDOUT; -} -#endif - -static int __sd_wait32_r(u32 addr, u32 mask) -{ - u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; - do - { - if(!(__sd_read32(addr) & mask)) - return 0; - udelay(SDHC_WAIT_TIMEOUT); - } - while(timeout--); - return SDHC_ETIMEDOUT; -} - -#ifdef SDHC_DEBUG_V -static void __sd_dumpregs(sdhci_t *sdhci) -{ - sdhc_debug(sdhci->reg_base, " register dump:"); - sdhc_debug(sdhci->reg_base, " sys addr: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_SDMA_ADDR)); - sdhc_debug(sdhci->reg_base, " version: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_VERSION)); - sdhc_debug(sdhci->reg_base, " bsize: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_BLOCK_SIZE)); - sdhc_debug(sdhci->reg_base, " bcount: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_BLOCK_COUNT)); - sdhc_debug(sdhci->reg_base, " argument: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_CMD_ARG)); - sdhc_debug(sdhci->reg_base, " trans mode: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_CMD_TRANSFER_MODE)); - sdhc_debug(sdhci->reg_base, " pres state: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE)); - sdhc_debug(sdhci->reg_base, " hc: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_HOST_CONTROL)); - sdhc_debug(sdhci->reg_base, " pwr ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_POWER_CONTROL)); - sdhc_debug(sdhci->reg_base, " gap ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_BLOCK_GAP_CONTROL)); - sdhc_debug(sdhci->reg_base, " wup ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_WAKEUP_CONTROL)); - sdhc_debug(sdhci->reg_base, " clk ctrl: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_CLOCK_CONTROL)); - sdhc_debug(sdhci->reg_base, " to ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_TIMEOUT_CONTROL)); - sdhc_debug(sdhci->reg_base, " int status: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS)); - sdhc_debug(sdhci->reg_base, " int enable: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE)); - sdhc_debug(sdhci->reg_base, " eint status: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_STATUS)); - sdhc_debug(sdhci->reg_base, " eint enable: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_ENABLE)); - sdhc_debug(sdhci->reg_base, " caps: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES)); - sdhc_debug(sdhci->reg_base, " max caps: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_MAX_CAPABILITIES)); - sdhc_debug(sdhci->reg_base, " soft reset: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_SOFTWARE_RESET)); - sdhc_debug(sdhci->reg_base, " command: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_CMD)); -} -#else -#define __sd_dumpregs(s) while(0) { } -#endif - -static int __sd_reset(sdhci_t *sdhci, int all) -{ - int retval; - u32 mask; - - sdhci->is_sdhc = 0; - sdhci->is_selected = 0; - sdhci->is_mounted = 0; - - __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE, 0); - __sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_ENABLE, 0); - __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_SIGNAL_ENABLE, 0); - __sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_SIGNAL_ENABLE, 0); - - if(all) - mask = SDHC_SOFTWARE_RESET_ALL; - else - mask = SDHC_SOFTWARE_RESET_CMD | SDHC_SOFTWARE_RESET_DAT; - - sdhc_error(sdhci->reg_base, "resetting card (mask = %x)", mask); - __sd_write8(sdhci->reg_base + SDHC_SOFTWARE_RESET, mask); - - retval = __sd_wait8_r(sdhci->reg_base + SDHC_SOFTWARE_RESET, mask); - if(retval < 0) - { - sdhc_debug(sdhci->reg_base, "reset failed, bits were never unset"); - sdhc_debug(sdhci->reg_base, "software reset register: %02x", __sd_read8(sdhci->reg_base + SDHC_SOFTWARE_RESET)); - return retval; - } - - sdhc_debug(sdhci->reg_base, "reset done"); - - __sd_dumpregs(sdhci); - __sd_write8(sdhci->reg_base + SDHC_TIMEOUT_CONTROL, SDHC_TIMEOUT_MAX); - + irq_disable(IRQ_SDHC); return 0; } -static int __sd_clock_div(u32 base, u32 target) +void splx(u32 dummy) { - int d; - for(d = 1; d <= 256; d *= 2) - { - if((base / d) <= target) - return (d/2); - } - return 256 / 2; + irq_enable(IRQ_SDHC); } -static int __sd_clock(sdhci_t *sdhci, u8 enable, u32 freq) -{ - u32 caps; - int d, retval; - - __sd_write16(sdhci->reg_base + SDHC_CLOCK_CONTROL, 0); +/* flag values */ +#define SHF_USE_DMA 0x0001 - if(!enable) - return 0; +#define HREAD1(hp, reg) \ + (bus_space_read_1((hp)->iot, (hp)->ioh, (reg))) +#define HREAD2(hp, reg) \ + (bus_space_read_2((hp)->iot, (hp)->ioh, (reg))) +#define HREAD4(hp, reg) \ + (bus_space_read_4((hp)->iot, (hp)->ioh, (reg))) +#define HWRITE1(hp, reg, val) \ + bus_space_write_1((hp)->iot, (hp)->ioh, (reg), (val)) +#define HWRITE2(hp, reg, val) \ + bus_space_write_2((hp)->iot, (hp)->ioh, (reg), (val)) +#define HWRITE4(hp, reg, val) \ + bus_space_write_4((hp)->iot, (hp)->ioh, (reg), (val)) +#define HCLR1(hp, reg, bits) \ + HWRITE1((hp), (reg), HREAD1((hp), (reg)) & ~(bits)) +#define HCLR2(hp, reg, bits) \ + HWRITE2((hp), (reg), HREAD2((hp), (reg)) & ~(bits)) +#define HSET1(hp, reg, bits) \ + HWRITE1((hp), (reg), HREAD1((hp), (reg)) | (bits)) +#define HSET2(hp, reg, bits) \ + HWRITE2((hp), (reg), HREAD2((hp), (reg)) | (bits)) - caps = read32(sdhci->reg_base + SDHC_CAPABILITIES); +int sdhc_host_reset(sdmmc_chipset_handle_t); +u_int32_t sdhc_host_ocr(sdmmc_chipset_handle_t); +int sdhc_host_maxblklen(sdmmc_chipset_handle_t); +int sdhc_card_detect(sdmmc_chipset_handle_t); +int sdhc_bus_power(sdmmc_chipset_handle_t, u_int32_t); +int sdhc_bus_clock(sdmmc_chipset_handle_t, int); +void sdhc_card_intr_mask(sdmmc_chipset_handle_t, int); +void sdhc_card_intr_ack(sdmmc_chipset_handle_t); +void sdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); +int sdhc_start_command(struct sdhc_host *, struct sdmmc_command *); +int sdhc_wait_state(struct sdhc_host *, u_int32_t, u_int32_t); +int sdhc_soft_reset(struct sdhc_host *, int); +int sdhc_wait_intr(struct sdhc_host *, int, int); +void sdhc_transfer_data(struct sdhc_host *, struct sdmmc_command *); +void sdhc_read_data(struct sdhc_host *, u_char *, int); +void sdhc_write_data(struct sdhc_host *, u_char *, int); - if(SDHC_BFREQ_KHZ(caps) != 0) - d = __sd_clock_div(SDHC_BFREQ_KHZ(caps), freq); - else - d = 256 / 2; - - __sd_write16(sdhci->reg_base + SDHC_CLOCK_CONTROL, (d << 8) | SDHC_CLOCK_INTERNAL_ENABLE); - - __sd_dumpregs(sdhci); - sdhc_debug(sdhci->reg_base, "waiting for internal clock to become stable"); - - retval = __sd_wait16(sdhci->reg_base + SDHC_CLOCK_CONTROL, SDHC_CLOCK_INTERNAL_STABLE); - if(retval < 0) - { - sdhc_debug(sdhci->reg_base, "clock didn't become stable :/"); - __sd_dumpregs(sdhci); - return retval; - } - sdhc_debug(sdhci->reg_base, "clock is stable; enabling sd clock"); - - __sd_write16(sdhci->reg_base + SDHC_CLOCK_CONTROL, (d << 8) | SDHC_CLOCK_SD_ENABLE | SDHC_CLOCK_INTERNAL_ENABLE); - return 0; -} - -static int __sd_power(sdhci_t *sdhci, int vdd) -{ - u32 caps; - u8 pwr; - - caps = __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES); - - if(vdd == -1) - { - if(caps & SDHC_CAP_VOLTAGE_33) - vdd = SDHC_CAP_VOLTAGE_33; - else if(caps & SDHC_CAP_VOLTAGE_30) - vdd = SDHC_CAP_VOLTAGE_30; - else if(caps & SDHC_CAP_VOLTAGE_18) - vdd = SDHC_CAP_VOLTAGE_18; - else - { - sdhc_error(sdhci->reg_base, "no voltage supported by the host? this should never happen..."); - return SDHC_ESTRANGE; - } - } - - if(!(caps & vdd)) - { - sdhc_error(sdhci->reg_base, "voltage %x not supported by the hc", vdd); - return SDHC_EINVAL; - } - - - pwr = 0; - switch(vdd) - { - case SDHC_CAP_VOLTAGE_33: - pwr |= SDHC_PCTRL_VOLTAGE_33; - break; - case SDHC_CAP_VOLTAGE_30: - pwr |= SDHC_PCTRL_VOLTAGE_30; - break; - case SDHC_CAP_VOLTAGE_18: - pwr |= SDHC_PCTRL_VOLTAGE_18; - break; - default: - sdhc_error(sdhci->reg_base, "invalid vdd: %x", vdd); - return SDHC_EINVAL; - } - - __sd_write8(sdhci->reg_base + SDHC_POWER_CONTROL, pwr); - pwr |= SDHC_PCTRL_ENABLE; - __sd_write8(sdhci->reg_base + SDHC_POWER_CONTROL, pwr); - - __sd_dumpregs(sdhci); - sdhc_debug(sdhci->reg_base, "card should get voltage now"); - - if(!(__sd_read8(sdhci->reg_base + SDHC_POWER_CONTROL) & SDHC_PCTRL_ENABLE)) - { - sdhc_error(sdhci->reg_base, "pctrl = 0 again"); - return SDHC_ESTRANGE; - } - - return 0; - -} - -static s32 __sd_cmd(sdhci_t *sdhci, u32 cmd, u32 type, u32 arg, u32 blk_cnt, void *buffer, u32 *response, u8 rlen) -{ - int mode; - int command; - int mask; - int use_dma; - int retval; - int i, imax; - u32 caps; - - if(cmd & SD_CMD_ACMD) - { - sdhc_debug(sdhci->reg_base, " cmd %X is ACMD%d, sending CMD55 first", cmd, cmd - SD_CMD_ACMD); - retval = __sd_cmd(sdhci, SD_CMD_APP, SD_R1, sdhci->rca << 16, 0, NULL, NULL, 0); - // TODO: also check the response here? - if(retval < 0) - return retval; - cmd -= SD_CMD_ACMD; - } - - __sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_STATUS, 0); - __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, 0); - - sdhc_debug(sdhci->reg_base, "__sd_cmd: cmd = %X, type = %X, arg = %X, blk_cnt = %d, rlen = %d", cmd, type, arg, blk_cnt, rlen); - - if(blk_cnt > SDHC_BLOCKS_MAX) - { - sdhc_error(sdhci->reg_base, "%d blocks are too much...", blk_cnt); - return SDHC_EOVERFLOW; - } - - caps = __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES); - if(caps & SDHC_CAP_SDMA && ((u32)buffer % 32 == 0) && blk_cnt > 0 && buffer != NULL) - use_dma = 1; - else - use_dma = 0; - - sdhc_debug(sdhci->reg_base, "enable DMA: %d (buffer mod 32: %d)", use_dma, (u32)buffer % 32); - - mode = mask = 0; - command = (cmd & SDHC_CMD_MASK) << SDHC_CMD_SHIFT; - if(blk_cnt > 0) - { - if(use_dma == 1) - mode |= SDHC_CMDMODE_DMA_ENABLE; - mode |= SDHC_CMDMODE_MULTIBLOCK; - mode |= SDHC_CMDMODE_ACMD12_ENABLE; - mode |= SDHC_CMDMODE_BLOCKCNT_ENABLE; - if(type & SD_READ) - { - mask = SDHC_BFR_READ_ENABLE; - mode |= SDHC_CMDMODE_READ; - } - else - { - mask = SDHC_BFR_WRITE_ENABLE; - mode |= SDHC_CMDMODE_WRITE; - } - - command |= SDHC_CMD_DATA; - } - - if(!(type & SD_RSP_PRESENT)) - command |= SDHC_CMD_NORESP; - else if(type & SD_RSP_136) - command |= SDHC_CMD_RESP_136; - else if(type & SD_RSP_BUSY) - command |= SDHC_CMD_RESP_BUSY; - else - command |= SDHC_CMD_RESP_48; - - if(type & SD_RSP_CRC) - command |= SDHC_CMD_CRC; - - if(type & SD_RSP_IDX) - command |= SDHC_CMD_IDXCHECK; - - - sdhc_debug(sdhci->reg_base, "waiting for command inhibit bits to be cleared.."); - - retval = __sd_wait32_r(sdhci->reg_base + SDHC_PRESENT_STATE, SDHC_PRESENT_CMD_INHIBIT_BOTH); - if(retval < 0) - { - sdhc_error(sdhci->reg_base, "command inhibit bits were never cleared"); - __sd_reset(sdhci, 0); - return retval; - } - - sdhc_debug(sdhci->reg_base, "command inhibit bits cleared, sending command"); - - if(use_dma == 1 && blk_cnt > 0) - { - sdhc_debug(sdhci->reg_base, "preparing buffer for SDMA transfer"); - if(mask == SDHC_BFR_WRITE_ENABLE) { - dc_flushrange(buffer, blk_cnt * BLOCK_SIZE); - ahb_flush_to(AHB_SDHC); - } else { - dc_invalidaterange(buffer, blk_cnt * BLOCK_SIZE); - } - - __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, 0); - __sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_STATUS, 0); - __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE, INTERRUPT_ALL); - __sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_ENABLE, EINTERRUPT_ALL); - __sd_write32(sdhci->reg_base + SDHC_SDMA_ADDR, dma_addr(buffer)); - } - - __sd_dumpregs(sdhci); - - if(blk_cnt > 0) - { - __sd_write16(sdhci->reg_base + SDHC_BLOCK_SIZE, (BLOCK_SIZE_512K << 12) | BLOCK_SIZE); - __sd_write16(sdhci->reg_base + SDHC_BLOCK_COUNT, blk_cnt); - } - - sdhc_debug(sdhci->reg_base, "writing mode = %x, arg = %x, cmd = %x", mode, arg, command); - __sd_write32(sdhci->reg_base + SDHC_CMD_ARG, arg); - - __sd_write32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE,__sd_read32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE) | INTERRUPT_COMMAND_COMPLETE); - __sd_write32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_COMMAND_COMPLETE); - __sd_write32(sdhci->reg_base + SDHC_CMD_TRANSFER_MODE, ((u32)command << 16) | mode); - - __sd_dumpregs(sdhci); - sdhc_debug(sdhci->reg_base, "waiting until command phase is done"); - retval = __sd_wait32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, - INTERRUPT_COMMAND_COMPLETE); -// retval = __sd_wait32_r(sdhci->reg_base + SDHC_PRESENT_STATE, SDHC_PRESENT_CMD_INHIBIT_CMD); - if(retval < 0) - { - __sd_write32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE,__sd_read32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE) & ~INTERRUPT_COMMAND_COMPLETE); - sdhc_error(sdhci->reg_base, "error: command phase not completed"); - __sd_dumpregs(sdhci); - __sd_reset(sdhci, 0); - return retval; - } - sdhc_debug(sdhci->reg_base, "command phase is done"); - __sd_write32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_COMMAND_COMPLETE); - __sd_write32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE,__sd_read32(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE) & ~INTERRUPT_COMMAND_COMPLETE); - - __sd_dumpregs(sdhci); - - for(i = 0; i < 4; i++) - sdhc_debug(sdhci->reg_base, "response %d: %08X", i, __sd_read32(sdhci->reg_base + SDHC_RESPONSE + 4*i)); - if(rlen < 4 && type & SD_RSP_PRESENT) - { - sdhc_debug(sdhci->reg_base, "response buffer not big enough for response..."); - } - else if(type & SD_RSP_PRESENT) - { - if(type & SD_RSP_136) - { - u8 *p = (u8 *)response; - - imax = 15 > rlen ? rlen : 15; - - for(i = 0; i < imax; i++) - *p++ = __sd_read8(sdhci->reg_base + SDHC_RESPONSE + i); - - for(i = 0; i < 4; i++) - sdhc_debug(sdhci->reg_base, "response %d: %X", i, __sd_read32(sdhci->reg_base + SDHC_RESPONSE + 4*i)); - } - else - { - response[0] = __sd_read32(sdhci->reg_base + SDHC_RESPONSE); - sdhc_debug(sdhci->reg_base, "response = %08X", response[0]); - } - } - - // FIXME: check response and abort on errors? - - if(blk_cnt > 0) - { - sdhc_debug(sdhci->reg_base, "starting transfer of %d %d byte blocks", blk_cnt, BLOCK_SIZE); - if(use_dma == 0) - { - gecko_printf("sdhci: PIO mode is broken, align your buffer and use DMA.\n"); - __sd_reset(sdhci, 0); - return SDHC_EIO; - } - else /* SDMA; transfer data in 512 KB blocks (i.e. 1024 512 byte blocks */ - { - u8 *ptr = (u8 *)buffer; - sdhc_debug(sdhci->reg_base, "using SDMA to transfer data"); - - while(1) - { - sdhc_debug(sdhci->reg_base, "waiting for interrupts..."); - retval = __sd_wait16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_DMA | INTERRUPT_TRANSFER_COMPLETE); - if(retval < 0) - { - sdhc_error(sdhci->reg_base, "failed while waiting for transfer complete or DMA interrupts..."); - __sd_reset(sdhci, 0); - return retval; - } - - retval = __sd_read16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS); - - if(retval & INTERRUPT_TRANSFER_COMPLETE) - { - __sd_dumpregs(sdhci); - sdhc_debug(sdhci->reg_base, "transfer completed. disabling interrupts again and returning."); - __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_TRANSFER_COMPLETE | INTERRUPT_DMA); - __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE, 0); - if(mask == SDHC_BFR_READ_ENABLE) - ahb_flush_from(AHB_SDHC); - return 0; - } - else if(retval & INTERRUPT_DMA) - { - u32 blk_left = __sd_read16(sdhci->reg_base + SDHC_BLOCK_COUNT); - blk_left = blk_cnt - blk_left; - ptr = buffer + blk_cnt * SDHC_BLOCK_SIZE; - __sd_dumpregs(sdhci); - sdhc_debug(sdhci->reg_base, "DMA interrupt set, updating next SDMA address"); - sdhc_debug(sdhci->reg_base, "sd blocks left: %d, addr: %p -> %p", blk_cnt, ptr, ptr + SDMA_BLOCK_SIZE); - - if(blk_cnt == 0) - { - sdhc_error(sdhci->reg_base, "FATAL ERROR: hc wants to transfer more DMA data but no more blocks are left."); - __sd_reset(sdhci, 0); - return SDHC_EIO; - } - - __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_DMA); - __sd_write32(sdhci->reg_base + SDHC_SDMA_ADDR, dma_addr(ptr)); - - sdhc_debug(sdhci->reg_base, "next DMA transfer started."); - __sd_dumpregs(sdhci); - } - } - } - } - - return 0; -} - -void __sd_print_status(sdhci_t *sdhci) -{ #ifdef SDHC_DEBUG - u32 status; - u32 state; - int retval; +int sdhcdebug = 0; +#define DPRINTF(n,s) do { if ((n) <= sdhcdebug) gecko_printf s; } while (0) +void sdhc_dump_regs(struct sdhc_host *); +#else +#define DPRINTF(n,s) do {} while(0) +#endif + +struct sdmmc_chip_functions sdhc_functions = { + /* host controller reset */ + sdhc_host_reset, + /* host controller capabilities */ + sdhc_host_ocr, + sdhc_host_maxblklen, + /* card detection */ + sdhc_card_detect, + /* bus power and clock frequency */ + sdhc_bus_power, + sdhc_bus_clock, + /* command execution */ + sdhc_exec_command, + /* card interrupt */ + sdhc_card_intr_mask, + sdhc_card_intr_ack +}; #if 0 - // FIXME: doesn't work for some reason ?!? - // sdhc0: Card status: 00000900 [|ò¦|ó¦L] - char *state_name[] = { - "IDLE", - "READY", - "IDENT", - "STANDBY", - "TRANSFER", - "DATA (SEND)", - "RECEIVE", - "PROGRAM", - "DISCONNECT" - }; - - sdhc_debug(sdhci->reg_base, "Card status %08x [%s]", status, state_name[state]); +struct cfdriver sdhc_cd = { + NULL, "sdhc", DV_DULL +}; #endif - - retval = __sd_cmd(sdhci, SD_CMD_SEND_STATUS, SD_R1, (u32)sdhci->rca << 16, 0, NULL, &status, sizeof(status)); - state = (status >> 9) & 0x0f; - gecko_printf("sdhc%d: Card status: %08x [", ((sdhci->reg_base - SD_REG_BASE) / 0x100), status); - - switch(state) - { - case 0: - gecko_printf("IDLE]\n"); - break; - case 1: - gecko_printf("READY]\n"); - break; - case 2: - gecko_printf("IDENT]\n"); - break; - case 3: - gecko_printf("STANDBY]\n"); - break; - case 4: - gecko_printf("TRANSFER]\n"); - break; - case 5: - gecko_printf("DATA (SEND)]\n"); - break; - case 6: - gecko_printf("DATA (RECEIVE)]\n"); - break; - case 7: - gecko_printf("PROGRAM]\n"); - break; - case 8: - gecko_printf("DISCONNECT]\n"); - break; - default: - gecko_printf("Reserved]\n"); - } - -#endif - return; -} - -static int __sd_getcid(sdhci_t *sdhci) { - u32 resp[4]; - u32 swapped[4]; - sdhc_debug(sdhci->reg_base, "sending ALL_SEND_CID command to get connected card"); - int retval = __sd_cmd(sdhci, SD_CMD_ALL_SEND_CID, SD_R2, 0, 0, NULL, resp, 16); - if(retval < 0) - { - sdhc_error(sdhci->reg_base, "__sd_cmd returned %d, resetting controller.", retval); - __sd_reset(sdhci, 1); - return SDHC_EIO; - } - swapped[0] = bswap32(resp[3]); - swapped[1] = bswap32(resp[2]); - swapped[2] = bswap32(resp[1]); - swapped[3] = bswap32(resp[0]); - - memcpy(&sdhci->cid, swapped, sizeof swapped); - - sdhc_error(sdhci->reg_base, "MID=%02x OID='%c%c' PNM='%c%c%c%c%c' PRV=%d.%d PSN=%08x MDT=200%d/%02d\n", - sdhci->cid.mid, sdhci->cid.oid[0], sdhci->cid.oid[1], - sdhci->cid.pnm[0], sdhci->cid.pnm[1], sdhci->cid.pnm[2], sdhci->cid.pnm[3], sdhci->cid.pnm[4], - sdhci->cid.prv >> 4, sdhci->cid.prv & 0xf, sdhci->cid.psn, sdhci->cid.mdt >> 4, sdhci->cid.mdt & 0xf); - return 0; -} - -static int __sd_getcsd(sdhci_t *sdhci) { - u8 resp[16]; - sdhc_debug(sdhci->reg_base, "requesting CSD"); - int retval = __sd_cmd(sdhci, SD_CMD_SEND_CSD, SD_R2, sdhci->rca << 16, 0, NULL, (u32 *)resp, 16); - if (retval < 0) { - sdhc_error(sdhci->reg_base, "failed to get CSD register (%d)", retval); - __sd_reset(sdhci, 1); - return SDHC_EIO; - } - sdhc_error(sdhci->reg_base, "CSD = %02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x", - resp[14], resp[13], resp[12], resp[11], resp[10], resp[9], resp[8], - resp[7], resp[6], resp[5], resp[4], resp[3], resp[2], resp[1], resp[0]); - - if (resp[13] == 0xe) { // sdhc - unsigned int c_size = resp[7] << 16 | resp[6] << 8 | resp[5]; - sdhc_error(sdhci->reg_base, "sdhc mode, c_size=%u, card size = %uk", c_size, (c_size + 1)* 512); - sdhci->timeout = 250 * 1000000; // spec says read timeout is 100ms and write/erase timeout is 250ms - sdhci->num_sectors = (c_size + 1) * 1024; // number of 512-byte sectors - } - else { - unsigned int taac, nsac, read_bl_len, c_size, c_size_mult; - taac = resp[13]; - nsac = resp[12]; - read_bl_len = resp[9] & 0xF; - - c_size = (resp[8] & 3) << 10; - c_size |= (resp[7] << 2); - c_size |= (resp[6] >> 6); - c_size_mult = (resp[5] & 3) << 1; - c_size_mult |= resp[4] >> 7; - sdhc_error(sdhci->reg_base, "taac=%u nsac=%u read_bl_len=%u c_size=%u c_size_mult=%u card size=%u bytes", - taac, nsac, read_bl_len, c_size, c_size_mult, (c_size + 1) * (4 << c_size_mult) * (1 << read_bl_len)); - static const unsigned int time_unit[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000}; - static const unsigned int time_value[] = {1, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80}; // must div by 10 - sdhci->timeout = time_unit[taac & 7] * time_value[(taac >> 3) & 0xf] / 10; - sdhc_error(sdhci->reg_base, "calculated timeout = %uns", sdhci->timeout); - sdhci->num_sectors = (c_size + 1) * (4 << c_size_mult) * (1 << read_bl_len) / 512; - } - sdhc_error(sdhci->reg_base, "num sectors = %u", sdhci->num_sectors); - - return 0; -} - -int sd_mount(sdhci_t *sdhci) +/* + * Called by attachment driver. For each SD card slot there is one SD + * host controller standard register set. (1.3) + */ +int +sdhc_host_found(struct sdhc_softc *sc, bus_space_tag_t iot, + bus_space_handle_t ioh, int usedma) { - u32 caps; - s32 retval; - u32 resp[4]; - int tries; + struct sdmmcbus_attach_args saa; + struct sdhc_host *hp; + u_int32_t caps; + int error = 1; +#ifdef SDHC_DEBUG + u_int16_t version; - __sd_dumpregs(sdhci); - retval = __sd_reset(sdhci, 1); - if(retval < 0) - return retval; + version = bus_space_read_2(iot, ioh, SDHC_HOST_CTL_VERSION); + gecko_printf("%s: SD Host Specification/Vendor Version ", + sc->sc_dev.dv_xname); + switch(SDHC_SPEC_VERSION(version)) { + case 0x00: + gecko_printf("1.0/%u\n", SDHC_VENDOR_VERSION(version)); + break; + default: + gecko_printf(">1.0/%u\n", SDHC_VENDOR_VERSION(version)); + break; + } +#endif - if(!sd_inserted(sdhci)) - return SDHC_ENOCARD; - - caps = __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES); - - retval = __sd_power(sdhci, -1); - if(retval < 0) - return retval; - - retval = __sd_clock(sdhci, 1, 25000); - if(retval < 0) - return retval; - - retval = __sd_cmd(sdhci, SD_CMD_RESET_CARD, SD_R0, 0x0, 0, NULL, NULL, 0); - if(retval < 0) - return retval; - - // check for SDHC - retval = __sd_cmd(sdhci, SD_CMD_SEND_IF_COND, SD_R7, 0x1AA, 0, NULL, resp, 6); - if(retval < 0 || (resp[0] & 0xff) != 0xAA) - { - // SDv1 low-capacity card# - sdhc_error(sdhci->reg_base, "SDv1 low-capacity card deteced. resetting controller and card again."); - - __sd_reset(sdhci, 1); - retval = __sd_power(sdhci, -1); - if(retval < 0) - return retval; - - retval = __sd_clock(sdhci, 1, 25000); - if(retval < 0) - return retval; - - retval = __sd_cmd(sdhci, SD_CMD_RESET_CARD, SD_R0, 0x0, 0, NULL, NULL, 0); - if(retval < 0) - return retval; - - sdhci->is_sdhc = 0; - sdhci->ocr = 0; + /* Allocate one more host structure. */ + if (sc->sc_nhosts < SDHC_MAX_HOSTS) { + sc->sc_nhosts++; + hp = sc->sc_host[sc->sc_nhosts - 1]; + memset(hp, 0, sizeof(*hp)); } else - { - // SDHC card deteced - sdhc_error(sdhci->reg_base, "SDv2 card detected."); - sdhci->is_sdhc = 1; - sdhci->ocr = OCR_HCS; + return -EINVAL; + + /* Fill in the new host structure. */ + hp->sc = sc; + hp->iot = iot; + hp->ioh = ioh; + + /* + * Reset the host controller and enable interrupts. + */ + (void)sdhc_host_reset(hp); + + /* Determine host capabilities. */ + caps = HREAD4(hp, SDHC_CAPABILITIES); + + /* Use DMA if the host system and the controller support it. */ + if (usedma && ISSET(caps, SDHC_DMA_SUPPORT)) + SET(hp->flags, SHF_USE_DMA); + + /* + * Determine the base clock frequency. (2.2.24) + */ + if (SDHC_BASE_FREQ_KHZ(caps) != 0) + hp->clkbase = SDHC_BASE_FREQ_KHZ(caps); + if (hp->clkbase == 0) { + /* The attachment driver must tell us. */ + gecko_printf("%s: base clock frequency unknown\n", + sc->sc_dev.dv_xname); + goto err; + } else if (hp->clkbase < 10000 || hp->clkbase > 63000) { + /* SDHC 1.0 supports only 10-63 MHz. */ + gecko_printf("%s: base clock frequency out of range: %u MHz\n", + sc->sc_dev.dv_xname, hp->clkbase / 1000); + goto err; } + /* + * XXX Set the data timeout counter value according to + * capabilities. (2.2.15) + */ + + /* + * Determine SD bus voltage levels supported by the controller. + */ + if (ISSET(caps, SDHC_VOLTAGE_SUPP_1_8V)) + SET(hp->ocr, MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V); + if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_0V)) + SET(hp->ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V); + if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_3V)) + SET(hp->ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V); + + /* + * Determine the maximum block length supported by the host + * controller. (2.2.24) + */ + switch((caps >> SDHC_MAX_BLK_LEN_SHIFT) & SDHC_MAX_BLK_LEN_MASK) { + case SDHC_MAX_BLK_LEN_512: + hp->maxblklen = 512; + break; + case SDHC_MAX_BLK_LEN_1024: + hp->maxblklen = 1024; + break; + case SDHC_MAX_BLK_LEN_2048: + hp->maxblklen = 2048; + break; + default: + hp->maxblklen = 1; + break; + } + + /* + * Attach the generic SD/MMC bus driver. (The bus driver must + * not invoke any chipset functions before it is attached.) + */ + bzero(&saa, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.sct = &sdhc_functions; + saa.sch = hp; + +/* hp->sdmmc = config_found(&sc->sc_dev, &saa, NULL); + if (hp->sdmmc == NULL) { + error = 0; + goto err; + }*/ - if(caps & SDHC_CAP_VOLTAGE_33) - sdhci->ocr |= MMC_VDD_32_33|MMC_VDD_33_34; - if(caps & SDHC_CAP_VOLTAGE_30) - sdhci->ocr |= MMC_VDD_29_30|MMC_VDD_30_31; - if(caps & SDHC_CAP_VOLTAGE_18) - sdhci->ocr |= MMC_VDD_165_195; - - sdhc_debug(sdhci->reg_base, "waiting for card to finalize power up"); - tries = 0; - resp[0] = 0; - while(tries++ <= SDHC_WAIT_TIMEOUT_OUTER_MULTIPLY) - { - retval = __sd_cmd(sdhci, SD_CMD_APP_SEND_OP_COND, SD_R3, sdhci->ocr, 0, NULL, resp, 6); - if(resp[0] & OCR_POWERUP_STATUS) - { - sdhc_error(sdhci->reg_base, "card power up is done."); - break; - } - udelay(SDHC_WAIT_TIMEOUT_OUTER); - } - - if(!(resp[0] & OCR_POWERUP_STATUS)) - { - sdhc_error(sdhci->reg_base, "powerup failed, resetting controller."); - __sd_reset(sdhci, 1); - return SDHC_EIO; - } - - sdhci->ocr = resp[0]; - - if(sdhci->ocr & OCR_CCS) - { - sdhc_debug(sdhci->reg_base, "SDHC card detected, using block instead of byte offset address mode"); - sdhci->is_sdhc = 1; - } - else - { - sdhc_debug(sdhci->reg_base, "low-capacity SD card detected. using byte offset address mode."); - sdhci->is_sdhc = 0; - } - - retval = __sd_getcid(sdhci); - if (retval) return retval; - - retval = __sd_cmd(sdhci, SD_CMD_SEND_RELATIVE_ADDR, SD_R6, 0, 0, NULL, resp, 6); - if(retval < 0) - { - sdhc_error(sdhci->reg_base, "failed at getting RCA (%d), resetting controller.", retval); - __sd_reset(sdhci, 1); - return SDHC_EIO; - } - - sdhci->rca = (resp[0] >> 16) & 0xffff; - sdhc_debug(sdhci->reg_base, "RCA: %04X", sdhci->rca); - retval = __sd_getcsd(sdhci); - if (retval) return retval; - - __sd_print_status(sdhci); - - sd_select(sdhci); - - __sd_print_status(sdhci); - - retval = __sd_cmd(sdhci, SD_CMD_SET_BLOCKLEN, SD_R1, BLOCK_SIZE, 0, NULL, NULL, 0); - if(retval < 0) - { - sdhc_debug(sdhci->reg_base, "failed to set the block length to 512bytes (%d)", retval); - return retval; - } - - __sd_print_status(sdhci); - - sd_select(sdhci); - - sdhc_debug(sdhci->reg_base, "setting bus width to 4"); - __sd_write8(sdhci->reg_base + SDHC_HOST_CONTROL, __sd_read8(sdhci->reg_base + SDHC_HOST_CONTROL) | SDHC_HCR_BUSWIDTH_4); - retval = __sd_cmd(sdhci, SD_CMD_APP_SET_BUS_WIDTH, SD_R1, 2, 0, NULL, NULL, 0); - if(retval < 0) - { - sdhc_debug(sdhci->reg_base, "failed to set bus width to 4: %d", retval); - return retval; - } - - sdhci->is_mounted = 1; - return 0; + +err: +// free(hp, M_DEVBUF); + sc->sc_host[sc->sc_nhosts - 1] = NULL; + sc->sc_nhosts--; + return (error); } -int sd_inserted(sdhci_t *sdhci) +#if 0 +/* + * Power hook established by or called from attachment driver. + */ +void +sdhc_power(int why, void *arg) { - return (__sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE) & SDHC_CARD_INSERTED) == SDHC_CARD_INSERTED; + struct sdhc_softc *sc = arg; + struct sdhc_host *hp; + int n, i; + + switch(why) { + case PWR_STANDBY: + case PWR_SUSPEND: + /* XXX poll for command completion or suspend command + * in progress */ + + /* Save the host controller state. */ + for (n = 0; n < sc->sc_nhosts; n++) { + hp = sc->sc_host[n]; + for (i = 0; i < sizeof hp->regs; i++) + hp->regs[i] = HREAD1(hp, i); + } + break; + + case PWR_RESUME: + /* Restore the host controller state. */ + for (n = 0; n < sc->sc_nhosts; n++) { + hp = sc->sc_host[n]; + (void)sdhc_host_reset(hp); + for (i = 0; i < sizeof hp->regs; i++) + HWRITE1(hp, i, hp->regs[i]); + } + break; + } } - -#ifndef _READONLY - -int sd_protected(sdhci_t *sdhci) -{ - return (__sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE) & SDHC_WRITE_PROTECT) == SDHC_WRITE_PROTECT; -} - #endif -int sd_init(sdhci_t *sdhci, int slot) +/* + * Shutdown hook established by or called from attachment driver. + */ +void +sdhc_shutdown(void *arg) { - memset(sdhci, 0, sizeof *sdhci); + struct sdhc_softc *sc = arg; + struct sdhc_host *hp; + int i; - if(slot > 1) - return SDHC_EINVAL; + /* XXX chip locks up if we don't disable it before reboot. */ + for (i = 0; i < sc->sc_nhosts; i++) { + hp = sc->sc_host[i]; + (void)sdhc_host_reset(hp); + } +} - sdhci->reg_base = SD_REG_BASE + slot * 0x100; +/* + * Reset the host controller. Called during initialization, when + * cards are removed, upon resume, and during error recovery. + */ +int +sdhc_host_reset(sdmmc_chipset_handle_t sch) +{ + struct sdhc_host *hp = sch; + u_int16_t imask; + int error; + int s; + + s = splsdmmc(); + + /* Disable all interrupts. */ + HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, 0); + + /* + * Reset the entire host controller and wait up to 100ms for + * the controller to clear the reset bit. + */ + if ((error = sdhc_soft_reset(hp, SDHC_RESET_ALL)) != 0) { + splx(s); + return (error); + } + + /* Set data timeout counter value to max for now. */ + HWRITE1(hp, SDHC_TIMEOUT_CTL, SDHC_TIMEOUT_MAX); + + /* Enable interrupts. */ + imask = SDHC_CARD_REMOVAL | SDHC_CARD_INSERTION | + SDHC_BUFFER_READ_READY | SDHC_BUFFER_WRITE_READY | + SDHC_DMA_INTERRUPT | SDHC_BLOCK_GAP_EVENT | + SDHC_TRANSFER_COMPLETE | SDHC_COMMAND_COMPLETE; + + HWRITE2(hp, SDHC_NINTR_STATUS_EN, imask); + HWRITE2(hp, SDHC_EINTR_STATUS_EN, SDHC_EINTR_STATUS_MASK); + HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, imask); + HWRITE2(hp, SDHC_EINTR_SIGNAL_EN, SDHC_EINTR_SIGNAL_MASK); + + splx(s); return 0; } -int sd_cmd(sdhci_t *sdhci, u32 cmd, u32 type, u32 arg, u32 blk_cnt, void *buffer, u32 *response, u8 rlen) +u_int32_t +sdhc_host_ocr(sdmmc_chipset_handle_t sch) { - return __sd_cmd(sdhci, cmd, type, arg, blk_cnt, buffer, response, rlen); + struct sdhc_host *hp = sch; + return hp->ocr; } -int sd_select(sdhci_t *sdhci) +int +sdhc_host_maxblklen(sdmmc_chipset_handle_t sch) { - int retval; + struct sdhc_host *hp = sch; + return hp->maxblklen; +} - if(sdhci->is_selected == 1) +/* + * Return non-zero if the card is currently inserted. + */ +int +sdhc_card_detect(sdmmc_chipset_handle_t sch) +{ + struct sdhc_host *hp = sch; + return ISSET(HREAD4(hp, SDHC_PRESENT_STATE), SDHC_CARD_INSERTED) ? + 1 : 0; +} + +/* + * Set or change SD bus voltage and enable or disable SD bus power. + * Return zero on success. + */ +int +sdhc_bus_power(sdmmc_chipset_handle_t sch, u_int32_t ocr) +{ + struct sdhc_host *hp = sch; + u_int8_t vdd; + int s; + + s = splsdmmc(); + + /* + * Disable bus power before voltage change. + */ + if (!(hp->sc->sc_flags & SDHC_F_NOPWR0)) + HWRITE1(hp, SDHC_POWER_CTL, 0); + + /* If power is disabled, reset the host and return now. */ + if (ocr == 0) { + splx(s); + (void)sdhc_host_reset(hp); return 0; - - retval = __sd_cmd(sdhci, SD_CMD_SELECT_CARD, SD_R1B, (u32)sdhci->rca << 16, 0, NULL, NULL, 0); - - if(retval < 0) - { - sdhc_debug(sdhci->reg_base, "selecting card failed with %d", retval); - return retval; } + /* + * Select the maximum voltage according to capabilities. + */ + ocr &= hp->ocr; + if (ISSET(ocr, MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V)) + vdd = SDHC_VOLTAGE_3_3V; + else if (ISSET(ocr, MMC_OCR_2_9V_3_0V|MMC_OCR_3_0V_3_1V)) + vdd = SDHC_VOLTAGE_3_0V; + else if (ISSET(ocr, MMC_OCR_1_7V_1_8V|MMC_OCR_1_8V_1_9V)) + vdd = SDHC_VOLTAGE_1_8V; + else { + /* Unsupported voltage level requested. */ + splx(s); + return EINVAL; + } - sdhci->is_selected = 1; - sdhc_debug(sdhci->reg_base, "card selected"); + /* + * Enable bus power. Wait at least 1 ms (or 74 clocks) plus + * voltage ramp until power rises. + */ + HWRITE1(hp, SDHC_POWER_CTL, (vdd << SDHC_VOLTAGE_SHIFT) | + SDHC_BUS_POWER); + sdmmc_delay(10000); + /* + * The host system may not power the bus due to battery low, + * etc. In that case, the host controller should clear the + * bus power bit. + */ + if (!ISSET(HREAD1(hp, SDHC_POWER_CTL), SDHC_BUS_POWER)) { + splx(s); + return ENXIO; + } + + splx(s); return 0; } -int sd_read(sdhci_t *sdhci, u32 start_block, u32 blk_cnt, void *buffer) +/* + * Return the smallest possible base clock frequency divisor value + * for the CLOCK_CTL register to produce `freq' (KHz). + */ +static int +sdhc_clock_divisor(struct sdhc_host *hp, u_int freq) { - int retval; - u32 response; + int div; - if(sdhci->is_mounted != 1) - return SDHC_EINVAL; - - retval = sd_select(sdhci); - if(retval < 0) - return retval; - - __sd_print_status(sdhci); - - if(sdhci->is_sdhc == 0) - start_block *= 512; - - dc_invalidaterange(buffer, blk_cnt * 512); - retval = __sd_cmd(sdhci, SD_CMD_READ_MULTIPLE_BLOCK, SD_R1 | SD_READ, start_block, blk_cnt, buffer, &response, sizeof(response)); - - if(retval < 0) - sdhc_debug(sdhci->reg_base, "reading blocks failed with %d.", retval); - __sd_print_status(sdhci); - - - return retval; + for (div = 1; div <= 256; div *= 2) + if ((hp->clkbase / div) <= freq) + return (div / 2); + /* No divisor found. */ + return -1; } -#if _READONLY == 1 - -int sd_write(sdhci_t *sdhci, u32 start_block, u32 blk_cnt, const void *buffer) +/* + * Set or change SDCLK frequency or disable the SD clock. + * Return zero on success. + */ +int +sdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq) { - int retval; - u32 response; + struct sdhc_host *hp = sch; + int s; + int div; + int timo; + int error = 0; - if(sdhci->is_mounted != 1) - return SDHC_EINVAL; - - retval = sd_select(sdhci); - if(retval < 0) - return retval; - - __sd_print_status(sdhci); - - if(sdhci->is_sdhc == 0) - start_block *= 512; - - dc_flushrange(buffer, blk_cnt * 512); - retval = __sd_cmd(sdhci, SD_CMD_WRITE_MULTIPLE_BLOCK, SD_R1, start_block, blk_cnt, (void *)buffer, &response, sizeof(response)); - - if(retval < 0) - sdhc_debug(sdhci->reg_base, "writing blocks failed with %d.", retval); - __sd_print_status(sdhci); - - return retval; -} + s = splsdmmc(); +#ifdef DIAGNOSTIC + /* Must not stop the clock if commands are in progress. */ + if (ISSET(HREAD4(hp, SDHC_PRESENT_STATE), SDHC_CMD_INHIBIT_MASK) && + sdhc_card_detect(hp)) + gecko_printf("sdhc_sdclk_frequency_select: command in progress\n"); #endif -void sd_initialize(void) -{ - ipc_code = ipc_tag = 0; - sd_init(&sdhci, 0); -// irq_enable(IRQ_NAND); ?? + /* + * Stop SD clock before changing the frequency. + */ + HWRITE2(hp, SDHC_CLOCK_CTL, 0); + if (freq == SDMMC_SDCLK_OFF) + goto ret; + + /* + * Set the minimum base clock frequency divisor. + */ + if ((div = sdhc_clock_divisor(hp, freq)) < 0) { + /* Invalid base clock frequency or `freq' value. */ + error = EINVAL; + goto ret; + } + HWRITE2(hp, SDHC_CLOCK_CTL, div << SDHC_SDCLK_DIV_SHIFT); + + /* + * Start internal clock. Wait 10ms for stabilization. + */ + HSET2(hp, SDHC_CLOCK_CTL, SDHC_INTCLK_ENABLE); + for (timo = 1000; timo > 0; timo--) { + if (ISSET(HREAD2(hp, SDHC_CLOCK_CTL), SDHC_INTCLK_STABLE)) + break; + sdmmc_delay(10); + } + if (timo == 0) { + error = ETIMEDOUT; + goto ret; + } + + /* + * Enable SD clock. + */ + HSET2(hp, SDHC_CLOCK_CTL, SDHC_SDCLK_ENABLE); + +ret: + splx(s); + return error; } -void sd_ipc(volatile ipc_request *req) +void +sdhc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable) { - int retval = 0; - if (ipc_code != 0 || ipc_tag != 0) { - gecko_printf("SDHC: previous IPC request is not done yet."); - ipc_post(req->code, req->tag, 1, -1); + struct sdhc_host *hp = sch; + + if (enable) { + HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); + HSET2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_CARD_INTERRUPT); + } else { + HCLR2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_CARD_INTERRUPT); + HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); + } +} + +void +sdhc_card_intr_ack(sdmmc_chipset_handle_t sch) +{ + struct sdhc_host *hp = sch; + + HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); +} + +int +sdhc_wait_state(struct sdhc_host *hp, u_int32_t mask, u_int32_t value) +{ + u_int32_t state; + int timeout; + + for (timeout = 10; timeout > 0; timeout--) { + if (((state = HREAD4(hp, SDHC_PRESENT_STATE)) & mask) + == value) + return 0; + sdmmc_delay(10000); + } + DPRINTF(0,("%s: timeout waiting for %x (state=%d)\n", + HDEVNAME(hp), value, state)); + return ETIMEDOUT; +} + +void +sdhc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct sdhc_host *hp = sch; + int error; + + /* + * Start the MMC command, or mark `cmd' as failed and return. + */ + error = sdhc_start_command(hp, cmd); + if (error != 0) { + cmd->c_error = error; + SET(cmd->c_flags, SCF_ITSDONE); return; } - switch (req->req) { - case IPC_SD_MOUNT: - retval = sd_mount(&sdhci); - ipc_post(req->code, req->tag, 1, retval); + /* + * Wait until the command phase is done, or until the command + * is marked done for any other reason. + */ + if (!sdhc_wait_intr(hp, SDHC_COMMAND_COMPLETE, + SDHC_COMMAND_TIMEOUT)) { + cmd->c_error = ETIMEDOUT; + SET(cmd->c_flags, SCF_ITSDONE); + return; + } + + /* + * The host controller removes bits [0:7] from the response + * data (CRC) and we pass the data up unchanged to the bus + * driver (without padding). + */ + if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { + if (ISSET(cmd->c_flags, SCF_RSP_136)) { + u_char *p = (u_char *)cmd->c_resp; + int i; + + for (i = 0; i < 15; i++) + *p++ = HREAD1(hp, SDHC_RESPONSE + i); + } else + cmd->c_resp[0] = HREAD4(hp, SDHC_RESPONSE); + } + + /* + * If the command has data to transfer in any direction, + * execute the transfer now. + */ + if (cmd->c_error == 0 && cmd->c_data != NULL) + sdhc_transfer_data(hp, cmd); + + /* Turn off the LED. */ + HCLR1(hp, SDHC_HOST_CTL, SDHC_LED_ON); + + DPRINTF(1,("%s: cmd %u done (flags=%#x error=%d)\n", + HDEVNAME(hp), cmd->c_opcode, cmd->c_flags, cmd->c_error)); + SET(cmd->c_flags, SCF_ITSDONE); +} + +int +sdhc_start_command(struct sdhc_host *hp, struct sdmmc_command *cmd) +{ + u_int16_t blksize = 0; + u_int16_t blkcount = 0; + u_int16_t mode; + u_int16_t command; + int error; + int s; + + DPRINTF(1,("%s: start cmd %u arg=%#x data=%p dlen=%d flags=%#x " + "proc=\"%s\"\n", HDEVNAME(hp), cmd->c_opcode, cmd->c_arg, + cmd->c_data, cmd->c_datalen, cmd->c_flags, "")); + + /* + * The maximum block length for commands should be the minimum + * of the host buffer size and the card buffer size. (1.7.2) + */ + + /* Fragment the data into proper blocks. */ + if (cmd->c_datalen > 0) { + blksize = MIN(cmd->c_datalen, cmd->c_blklen); + blkcount = cmd->c_datalen / blksize; + if (cmd->c_datalen % blksize > 0) { + /* XXX: Split this command. (1.7.4) */ + gecko_printf("%s: data not a multiple of %d bytes\n", + HDEVNAME(hp), blksize); + return EINVAL; + } + } + + /* Check limit imposed by 9-bit block count. (1.7.2) */ + if (blkcount > SDHC_BLOCK_COUNT_MAX) { + gecko_printf("%s: too much data\n", HDEVNAME(hp)); + return EINVAL; + } + + /* Prepare transfer mode register value. (2.2.5) */ + mode = 0; + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + mode |= SDHC_READ_MODE; + if (blkcount > 0) { + mode |= SDHC_BLOCK_COUNT_ENABLE; + if (blkcount > 1) { + mode |= SDHC_MULTI_BLOCK_MODE; + /* XXX only for memory commands? */ + mode |= SDHC_AUTO_CMD12_ENABLE; + } + } +#ifdef notyet + if (ISSET(hp->flags, SHF_USE_DMA)) + mode |= SDHC_DMA_ENABLE; +#endif + + /* + * Prepare command register value. (2.2.6) + */ + command = (cmd->c_opcode & SDHC_COMMAND_INDEX_MASK) << + SDHC_COMMAND_INDEX_SHIFT; + + if (ISSET(cmd->c_flags, SCF_RSP_CRC)) + command |= SDHC_CRC_CHECK_ENABLE; + if (ISSET(cmd->c_flags, SCF_RSP_IDX)) + command |= SDHC_INDEX_CHECK_ENABLE; + if (cmd->c_data != NULL) + command |= SDHC_DATA_PRESENT_SELECT; + + if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT)) + command |= SDHC_NO_RESPONSE; + else if (ISSET(cmd->c_flags, SCF_RSP_136)) + command |= SDHC_RESP_LEN_136; + else if (ISSET(cmd->c_flags, SCF_RSP_BSY)) + command |= SDHC_RESP_LEN_48_CHK_BUSY; + else + command |= SDHC_RESP_LEN_48; + + /* Wait until command and data inhibit bits are clear. (1.5) */ + if ((error = sdhc_wait_state(hp, SDHC_CMD_INHIBIT_MASK, 0)) != 0) + return error; + + s = splsdmmc(); + + /* Alert the user not to remove the card. */ + HSET1(hp, SDHC_HOST_CTL, SDHC_LED_ON); + + /* XXX: Set DMA start address if SHF_USE_DMA is set. */ + + DPRINTF(1,("%s: cmd=%#x mode=%#x blksize=%d blkcount=%d\n", + HDEVNAME(hp), command, mode, blksize, blkcount)); + + /* + * Start a CPU data transfer. Writing to the high order byte + * of the SDHC_COMMAND register triggers the SD command. (1.5) + */ + HWRITE2(hp, SDHC_TRANSFER_MODE, mode); + HWRITE2(hp, SDHC_BLOCK_SIZE, blksize); + if (blkcount > 1) + HWRITE2(hp, SDHC_BLOCK_COUNT, blkcount); + HWRITE4(hp, SDHC_ARGUMENT, cmd->c_arg); + HWRITE2(hp, SDHC_COMMAND, command); + + splx(s); + return 0; +} + +void +sdhc_transfer_data(struct sdhc_host *hp, struct sdmmc_command *cmd) +{ + u_char *datap = cmd->c_data; + int i, datalen; + int mask; + int error; + + mask = ISSET(cmd->c_flags, SCF_CMD_READ) ? + SDHC_BUFFER_READ_ENABLE : SDHC_BUFFER_WRITE_ENABLE; + error = 0; + datalen = cmd->c_datalen; + + DPRINTF(1,("%s: resp=%#x datalen=%d\n", HDEVNAME(hp), + MMC_R1(cmd->c_resp), datalen)); + +#ifdef SDHC_DEBUG + /* XXX I forgot why I wanted to know when this happens :-( */ + if ((cmd->c_opcode == 52 || cmd->c_opcode == 53) && + ISSET(MMC_R1(cmd->c_resp), 0xcb00)) + gecko_printf("%s: CMD52/53 error response flags %#x\n", + HDEVNAME(hp), MMC_R1(cmd->c_resp) & 0xff00); +#endif + + while (datalen > 0) { + if (!sdhc_wait_intr(hp, SDHC_BUFFER_READ_READY| + SDHC_BUFFER_WRITE_READY, SDHC_BUFFER_TIMEOUT)) { + error = ETIMEDOUT; + break; + } + + if ((error = sdhc_wait_state(hp, mask, mask)) != 0) break; - case IPC_SD_SELECT: - retval = sd_select(&sdhci); - ipc_post(req->code, req->tag, 1, retval); - break; + i = MIN(datalen, cmd->c_blklen); + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + sdhc_read_data(hp, datap, i); + else + sdhc_write_data(hp, datap, i); - case IPC_SD_GETSTATE: - retval = __sd_read32(sdhci.reg_base + SDHC_PRESENT_STATE); - ipc_post(req->code, req->tag, 1, retval); - break; - - case IPC_SD_READ: - retval = sd_read(&sdhci, req->args[0], req->args[1], - (void *)req->args[2]); - ipc_post(req->code, req->tag, 1, retval); - break; + datap += i; + datalen -= i; + } - case IPC_SD_WRITE: - retval = sd_write(&sdhci, req->args[0], req->args[1], - (void *)req->args[2]); - ipc_post(req->code, req->tag, 1, retval); - break; + if (error == 0 && !sdhc_wait_intr(hp, SDHC_TRANSFER_COMPLETE, + SDHC_TRANSFER_TIMEOUT)) + error = ETIMEDOUT; - case IPC_SD_GETSIZE: - ipc_post(req->code, req->tag, 1, sdhci.num_sectors); - break; + if (error != 0) + cmd->c_error = error; + SET(cmd->c_flags, SCF_ITSDONE); - default: - gecko_printf("IPC: unknown SLOW SDHC request %04x\n", - req->req); + DPRINTF(1,("%s: data transfer done (error=%d)\n", + HDEVNAME(hp), cmd->c_error)); +} +void +sdhc_read_data(struct sdhc_host *hp, u_char *datap, int datalen) +{ + while (datalen > 3) { + *(u_int32_t *)datap = HREAD4(hp, SDHC_DATA); + datap += 4; + datalen -= 4; + } + if (datalen > 0) { + u_int32_t rv = HREAD4(hp, SDHC_DATA); + do { + *datap++ = rv & 0xff; + rv = rv >> 8; + } while (--datalen > 0); } } + +void +sdhc_write_data(struct sdhc_host *hp, u_char *datap, int datalen) +{ + while (datalen > 3) { + DPRINTF(3,("%08x\n", *(u_int32_t *)datap)); + HWRITE4(hp, SDHC_DATA, *((u_int32_t *)datap)); + datap += 4; + datalen -= 4; + } + if (datalen > 0) { + u_int32_t rv = *datap++; + if (datalen > 1) + rv |= *datap++ << 8; + if (datalen > 2) + rv |= *datap++ << 16; + DPRINTF(3,("rv %08x\n", rv)); + HWRITE4(hp, SDHC_DATA, rv); + } +} + +/* Prepare for another command. */ +int +sdhc_soft_reset(struct sdhc_host *hp, int mask) +{ + int timo; + + DPRINTF(1,("%s: software reset reg=%#x\n", HDEVNAME(hp), mask)); + + HWRITE1(hp, SDHC_SOFTWARE_RESET, mask); + for (timo = 10; timo > 0; timo--) { + if (!ISSET(HREAD1(hp, SDHC_SOFTWARE_RESET), mask)) + break; + sdmmc_delay(10000); + HWRITE1(hp, SDHC_SOFTWARE_RESET, 0); + } + if (timo == 0) { + DPRINTF(1,("%s: timeout reg=%#x\n", HDEVNAME(hp), + HREAD1(hp, SDHC_SOFTWARE_RESET))); + HWRITE1(hp, SDHC_SOFTWARE_RESET, 0); + return (ETIMEDOUT); + } + + return (0); +} + +int +sdhc_wait_intr(struct sdhc_host *hp, int mask, int timo) +{ + int status; + int s; + + mask |= SDHC_ERROR_INTERRUPT; + + s = splsdmmc(); + status = hp->intr_status & mask; + + + for (timo = 1000; timo > 0; timo--) { + if (hp->intr_status != 0) { + status = hp->intr_status & mask; + break; + } + sdmmc_delay(10); + } + if (timo == 0) { + status |= SDHC_ERROR_INTERRUPT; + } + hp->intr_status &= ~status; + + DPRINTF(2,("%s: intr status %#x error %#x\n", HDEVNAME(hp), status, + hp->intr_error_status)); + + /* Command timeout has higher priority than command complete. */ + if (ISSET(status, SDHC_ERROR_INTERRUPT)) { + hp->intr_error_status = 0; + (void)sdhc_soft_reset(hp, SDHC_RESET_DAT|SDHC_RESET_CMD); + status = 0; + } + + splx(s); + return status; +} + +/* + * Established by attachment driver at interrupt priority IPL_SDMMC. + */ +int +sdhc_intr(void *arg) +{ + struct sdhc_softc *sc = arg; + int host; + int done = 0; + + /* We got an interrupt, but we don't know from which slot. */ + for (host = 0; host < sc->sc_nhosts; host++) { + struct sdhc_host *hp = sc->sc_host[host]; + u_int16_t status; + + if (hp == NULL) + continue; + + /* Find out which interrupts are pending. */ + status = HREAD2(hp, SDHC_NINTR_STATUS); + if (!ISSET(status, SDHC_NINTR_STATUS_MASK)) + continue; /* no interrupt for us */ + + /* Acknowledge the interrupts we are about to handle. */ + HWRITE2(hp, SDHC_NINTR_STATUS, status); + DPRINTF(2,("%s: interrupt status=%d\n", HDEVNAME(hp), + status)); + + /* Claim this interrupt. */ + done = 1; + + /* + * Service error interrupts. + */ + if (ISSET(status, SDHC_ERROR_INTERRUPT)) { + u_int16_t error; + + /* Acknowledge error interrupts. */ + error = HREAD2(hp, SDHC_EINTR_STATUS); + HWRITE2(hp, SDHC_EINTR_STATUS, error); + DPRINTF(2,("%s: error interrupt, status=%d\n", + HDEVNAME(hp), error)); + + if (ISSET(error, SDHC_CMD_TIMEOUT_ERROR| + SDHC_DATA_TIMEOUT_ERROR)) { + hp->intr_error_status |= error; + hp->intr_status |= status; + wakeup(&hp->intr_status); + } + } + + /* + * Wake up the sdmmc event thread to scan for cards. + */ +/* if (ISSET(status, SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION)) + sdmmc_needs_discover(hp->sdmmc);*/ + + /* + * Wake up the blocking process to service command + * related interrupt(s). + */ + if (ISSET(status, SDHC_BUFFER_READ_READY| + SDHC_BUFFER_WRITE_READY|SDHC_COMMAND_COMPLETE| + SDHC_TRANSFER_COMPLETE)) { + hp->intr_status |= status; + wakeup(&hp->intr_status); + } + + /* + * Service SD card interrupts. + */ + if (ISSET(status, SDHC_CARD_INTERRUPT)) { + DPRINTF(0,("%s: card interrupt\n", HDEVNAME(hp))); + HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); +// sdmmc_card_intr(hp->sdmmc); + } + } + return done; +} + +#ifdef SDHC_DEBUG +void +sdhc_dump_regs(struct sdhc_host *hp) +{ + gecko_printf("0x%02x PRESENT_STATE: %x\n", SDHC_PRESENT_STATE, + HREAD4(hp, SDHC_PRESENT_STATE)); + gecko_printf("0x%02x POWER_CTL: %x\n", SDHC_POWER_CTL, + HREAD1(hp, SDHC_POWER_CTL)); + gecko_printf("0x%02x NINTR_STATUS: %x\n", SDHC_NINTR_STATUS, + HREAD2(hp, SDHC_NINTR_STATUS)); + gecko_printf("0x%02x EINTR_STATUS: %x\n", SDHC_EINTR_STATUS, + HREAD2(hp, SDHC_EINTR_STATUS)); + gecko_printf("0x%02x NINTR_STATUS_EN: %x\n", SDHC_NINTR_STATUS_EN, + HREAD2(hp, SDHC_NINTR_STATUS_EN)); + gecko_printf("0x%02x EINTR_STATUS_EN: %x\n", SDHC_EINTR_STATUS_EN, + HREAD2(hp, SDHC_EINTR_STATUS_EN)); + gecko_printf("0x%02x NINTR_SIGNAL_EN: %x\n", SDHC_NINTR_SIGNAL_EN, + HREAD2(hp, SDHC_NINTR_SIGNAL_EN)); + gecko_printf("0x%02x EINTR_SIGNAL_EN: %x\n", SDHC_EINTR_SIGNAL_EN, + HREAD2(hp, SDHC_EINTR_SIGNAL_EN)); + gecko_printf("0x%02x CAPABILITIES: %x\n", SDHC_CAPABILITIES, + HREAD4(hp, SDHC_CAPABILITIES)); + gecko_printf("0x%02x MAX_CAPABILITIES: %x\n", SDHC_MAX_CAPABILITIES, + HREAD4(hp, SDHC_MAX_CAPABILITIES)); +} +#endif + +#include "hollywood.h" +static struct sdhc_softc __softc; + +void sdhc_irq(void) +{ + sdhc_intr(&__softc); +} + +void sdhc_init(void) +{ + memset(&__softc, 0, sizeof(__softc)); + sdhc_host_found(&__softc, 0, SDHC_REG_BASE, 1); +// sdhc_host_found(&__softc, 0, SDHC_REG_BASE + 0x100, 1); +// sdhc_host_found(&__softc, 0, 0x0d080000, 1); +} diff --git a/sdhcreg.h b/sdhcreg.h new file mode 100644 index 0000000..56d72dc --- /dev/null +++ b/sdhcreg.h @@ -0,0 +1,189 @@ +/* $OpenBSD: sdhcreg.h,v 1.4 2006/07/30 17:20:40 fgsch Exp $ */ + +/* + * Copyright (c) 2006 Uwe Stuehler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SDHCREG_H_ +#define _SDHCREG_H_ + +/* PCI base address registers */ +#define SDHC_PCI_BAR_START PCI_MAPREG_START +#define SDHC_PCI_BAR_END PCI_MAPREG_END + +/* PCI interface classes */ +#define SDHC_PCI_INTERFACE_NO_DMA 0x00 +#define SDHC_PCI_INTERFACE_DMA 0x01 +#define SDHC_PCI_INTERFACE_VENDOR 0x02 + +/* Host standard register set */ +#define SDHC_DMA_ADDR 0x00 +#define SDHC_BLOCK_SIZE 0x04 +#define SDHC_BLOCK_COUNT 0x06 +#define SDHC_BLOCK_COUNT_MAX 512 +#define SDHC_ARGUMENT 0x08 +#define SDHC_TRANSFER_MODE 0x0c +#define SDHC_MULTI_BLOCK_MODE (1<<5) +#define SDHC_READ_MODE (1<<4) +#define SDHC_AUTO_CMD12_ENABLE (1<<2) +#define SDHC_BLOCK_COUNT_ENABLE (1<<1) +#define SDHC_DMA_ENABLE (1<<0) +#define SDHC_COMMAND 0x0e +/* 14-15 reserved */ +#define SDHC_COMMAND_INDEX_SHIFT 8 +#define SDHC_COMMAND_INDEX_MASK 0x3f +#define SDHC_COMMAND_TYPE_ABORT (3<<6) +#define SDHC_COMMAND_TYPE_RESUME (2<<6) +#define SDHC_COMMAND_TYPE_SUSPEND (1<<6) +#define SDHC_COMMAND_TYPE_NORMAL (0<<6) +#define SDHC_DATA_PRESENT_SELECT (1<<5) +#define SDHC_INDEX_CHECK_ENABLE (1<<4) +#define SDHC_CRC_CHECK_ENABLE (1<<3) +/* 2 reserved */ +#define SDHC_RESP_LEN_48_CHK_BUSY (3<<0) +#define SDHC_RESP_LEN_48 (2<<0) +#define SDHC_RESP_LEN_136 (1<<0) +#define SDHC_NO_RESPONSE (0<<0) +#define SDHC_RESPONSE 0x10 /* - 0x1f */ +#define SDHC_DATA 0x20 +#define SDHC_PRESENT_STATE 0x24 +/* 25-31 reserved */ +#define SDHC_CMD_LINE_SIGNAL_LEVEL (1<<24) +#define SDHC_DAT3_LINE_LEVEL (1<<23) +#define SDHC_DAT2_LINE_LEVEL (1<<22) +#define SDHC_DAT1_LINE_LEVEL (1<<21) +#define SDHC_DAT0_LINE_LEVEL (1<<20) +#define SDHC_WRITE_PROTECT_SWITCH (1<<19) +#define SDHC_CARD_DETECT_PIN_LEVEL (1<<18) +#define SDHC_CARD_STATE_STABLE (1<<17) +#define SDHC_CARD_INSERTED (1<<16) +/* 12-15 reserved */ +#define SDHC_BUFFER_READ_ENABLE (1<<11) +#define SDHC_BUFFER_WRITE_ENABLE (1<<10) +#define SDHC_READ_TRANSFER_ACTIVE (1<<9) +#define SDHC_WRITE_TRANSFER_ACTIVE (1<<8) +/* 3-7 reserved */ +#define SDHC_DAT_ACTIVE (1<<2) +#define SDHC_CMD_INHIBIT_DAT (1<<1) +#define SDHC_CMD_INHIBIT_CMD (1<<0) +#define SDHC_CMD_INHIBIT_MASK 0x0003 +#define SDHC_HOST_CTL 0x28 +#define SDHC_HIGH_SPEED (1<<2) +#define SDHC_4BIT_MODE (1<<1) +#define SDHC_LED_ON (1<<0) +#define SDHC_POWER_CTL 0x29 +#define SDHC_VOLTAGE_SHIFT 1 +#define SDHC_VOLTAGE_MASK 0x07 +#define SDHC_VOLTAGE_3_3V 0x07 +#define SDHC_VOLTAGE_3_0V 0x06 +#define SDHC_VOLTAGE_1_8V 0x05 +#define SDHC_BUS_POWER (1<<0) +#define SDHC_BLOCK_GAP_CTL 0x2a +#define SDHC_WAKEUP_CTL 0x2b +#define SDHC_CLOCK_CTL 0x2c +#define SDHC_SDCLK_DIV_SHIFT 8 +#define SDHC_SDCLK_DIV_MASK 0xff +#define SDHC_SDCLK_ENABLE (1<<2) +#define SDHC_INTCLK_STABLE (1<<1) +#define SDHC_INTCLK_ENABLE (1<<0) +#define SDHC_TIMEOUT_CTL 0x2e +#define SDHC_TIMEOUT_MAX 0x0e +#define SDHC_SOFTWARE_RESET 0x2f +#define SDHC_RESET_MASK 0x5 +#define SDHC_RESET_DAT (1<<2) +#define SDHC_RESET_CMD (1<<1) +#define SDHC_RESET_ALL (1<<0) +#define SDHC_NINTR_STATUS 0x30 +#define SDHC_ERROR_INTERRUPT (1<<15) +#define SDHC_CARD_INTERRUPT (1<<8) +#define SDHC_CARD_REMOVAL (1<<7) +#define SDHC_CARD_INSERTION (1<<6) +#define SDHC_BUFFER_READ_READY (1<<5) +#define SDHC_BUFFER_WRITE_READY (1<<4) +#define SDHC_DMA_INTERRUPT (1<<3) +#define SDHC_BLOCK_GAP_EVENT (1<<2) +#define SDHC_TRANSFER_COMPLETE (1<<1) +#define SDHC_COMMAND_COMPLETE (1<<0) +#define SDHC_NINTR_STATUS_MASK 0x81ff +#define SDHC_EINTR_STATUS 0x32 +#define SDHC_AUTO_CMD12_ERROR (1<<8) +#define SDHC_CURRENT_LIMIT_ERROR (1<<7) +#define SDHC_DATA_END_BIT_ERROR (1<<6) +#define SDHC_DATA_CRC_ERROR (1<<5) +#define SDHC_DATA_TIMEOUT_ERROR (1<<4) +#define SDHC_CMD_INDEX_ERROR (1<<3) +#define SDHC_CMD_END_BIT_ERROR (1<<2) +#define SDHC_CMD_CRC_ERROR (1<<1) +#define SDHC_CMD_TIMEOUT_ERROR (1<<0) +#define SDHC_EINTR_STATUS_MASK 0x01ff /* excluding vendor signals */ +#define SDHC_NINTR_STATUS_EN 0x34 +#define SDHC_EINTR_STATUS_EN 0x36 +#define SDHC_NINTR_SIGNAL_EN 0x38 +#define SDHC_NINTR_SIGNAL_MASK 0x01ff +#define SDHC_EINTR_SIGNAL_EN 0x3a +#define SDHC_EINTR_SIGNAL_MASK 0x01ff /* excluding vendor signals */ +#define SDHC_CMD12_ERROR_STATUS 0x3c +#define SDHC_CAPABILITIES 0x40 +#define SDHC_VOLTAGE_SUPP_1_8V (1<<26) +#define SDHC_VOLTAGE_SUPP_3_0V (1<<25) +#define SDHC_VOLTAGE_SUPP_3_3V (1<<24) +#define SDHC_DMA_SUPPORT (1<<22) +#define SDHC_HIGH_SPEED_SUPP (1<<21) +#define SDHC_MAX_BLK_LEN_512 0 +#define SDHC_MAX_BLK_LEN_1024 1 +#define SDHC_MAX_BLK_LEN_2048 2 +#define SDHC_MAX_BLK_LEN_SHIFT 16 +#define SDHC_MAX_BLK_LEN_MASK 0x3 +#define SDHC_BASE_FREQ_SHIFT 8 +#define SDHC_BASE_FREQ_MASK 0x3f +#define SDHC_TIMEOUT_FREQ_UNIT (1<<7) /* 0=KHz, 1=MHz */ +#define SDHC_TIMEOUT_FREQ_SHIFT 0 +#define SDHC_TIMEOUT_FREQ_MASK 0x1f +#define SDHC_MAX_CAPABILITIES 0x48 +#define SDHC_SLOT_INTR_STATUS 0xfc +#define SDHC_HOST_CTL_VERSION 0xfe +#define SDHC_SPEC_VERS_SHIFT 0 +#define SDHC_SPEC_VERS_MASK 0xff +#define SDHC_VENDOR_VERS_SHIFT 8 +#define SDHC_VENDOR_VERS_MASK 0xff + +/* SDHC_CAPABILITIES decoding */ +#define SDHC_BASE_FREQ_KHZ(cap) \ + ((((cap) >> SDHC_BASE_FREQ_SHIFT) & SDHC_BASE_FREQ_MASK) * 1000) +#define SDHC_TIMEOUT_FREQ(cap) \ + (((cap) >> SDHC_TIMEOUT_FREQ_SHIFT) & SDHC_TIMEOUT_FREQ_MASK) +#define SDHC_TIMEOUT_FREQ_KHZ(cap) \ + (((cap) & SDHC_TIMEOUT_FREQ_UNIT) ? \ + SDHC_TIMEOUT_FREQ(cap) * 1000: \ + SDHC_TIMEOUT_FREQ(cap)) + +/* SDHC_HOST_CTL_VERSION decoding */ +#define SDHC_SPEC_VERSION(hcv) \ + (((hcv) >> SDHC_SPEC_VERS_SHIFT) & SDHC_SPEC_VERS_MASK) +#define SDHC_VENDOR_VERSION(hcv) \ + (((hcv) >> SDHC_VENDOR_VERS_SHIFT) & SDHC_VENDOR_VERS_MASK) + +#define SDHC_PRESENT_STATE_BITS \ + "\20\31CL\30D3L\27D2L\26D1L\25D0L\24WPS\23CD\22CSS\21CI" \ + "\14BRE\13BWE\12RTA\11WTA\3DLA\2CID\1CIC" +#define SDHC_NINTR_STATUS_BITS \ + "\20\20ERROR\11CARD\10REMOVAL\7INSERTION\6READ\5WRITE" \ + "\4DMA\3GAP\2XFER\1CMD" +#define SDHC_EINTR_STATUS_BITS \ + "\20\11ACMD12\10CL\7DEB\6DCRC\5DT\4CI\3CEB\2CCRC\1CT" +#define SDHC_CAPABILITIES_BITS \ + "\20\33Vdd1.8V\32Vdd3.0V\31Vdd3.3V\30SUSPEND\27DMA\26HIGHSPEED" + +#endif diff --git a/sdhcvar.h b/sdhcvar.h new file mode 100644 index 0000000..afdd444 --- /dev/null +++ b/sdhcvar.h @@ -0,0 +1,48 @@ +/* $OpenBSD: sdhcvar.h,v 1.3 2007/09/06 08:01:01 jsg Exp $ */ + +/* + * Copyright (c) 2006 Uwe Stuehler + * Copyright (c) 2009 Sven Peter + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SDHCVAR_H_ +#define _SDHCVAR_H_ + +#include "bsdtypes.h" +#define SDHC_MAX_HOSTS 4 + +struct sdhc_host; + +struct sdhc_softc { + struct device sc_dev; + struct sdhc_host *sc_host[SDHC_MAX_HOSTS]; + int sc_nhosts; + u_int sc_flags; +}; + + +/* Host controller functions called by the attachment driver. */ +int sdhc_host_found(struct sdhc_softc *, bus_space_tag_t, + bus_space_handle_t, int); +void sdhc_power(int, void *); +void sdhc_shutdown(void *); +int sdhc_intr(void *); +void sdhc_init(void); +void sdhc_irq(void); + +/* flag values */ +#define SDHC_F_NOPWR0 (1 << 0) + +#endif diff --git a/sdmmcchip.h b/sdmmcchip.h new file mode 100644 index 0000000..1846b1d --- /dev/null +++ b/sdmmcchip.h @@ -0,0 +1,88 @@ +/* $OpenBSD: sdmmcchip.h,v 1.4 2009/02/20 19:16:35 miod Exp $ */ + +/* + * Copyright (c) 2006 Uwe Stuehler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SDMMC_CHIP_H_ +#define _SDMMC_CHIP_H_ + +struct sdmmc_command; + +typedef struct sdmmc_chip_functions *sdmmc_chipset_tag_t; +typedef void *sdmmc_chipset_handle_t; + +struct sdmmc_chip_functions { + /* host controller reset */ + int (*host_reset)(sdmmc_chipset_handle_t); + /* host capabilities */ + u_int32_t (*host_ocr)(sdmmc_chipset_handle_t); + int (*host_maxblklen)(sdmmc_chipset_handle_t); + /* card detection */ + int (*card_detect)(sdmmc_chipset_handle_t); + /* bus power and clock frequency */ + int (*bus_power)(sdmmc_chipset_handle_t, u_int32_t); + int (*bus_clock)(sdmmc_chipset_handle_t, int); + /* command execution */ + void (*exec_command)(sdmmc_chipset_handle_t, + struct sdmmc_command *); + /* card interrupt */ + void (*card_intr_mask)(sdmmc_chipset_handle_t, int); + void (*card_intr_ack)(sdmmc_chipset_handle_t); +}; + +/* host controller reset */ +#define sdmmc_chip_host_reset(tag, handle) \ + ((tag)->host_reset((handle))) +/* host capabilities */ +#define sdmmc_chip_host_ocr(tag, handle) \ + ((tag)->host_ocr((handle))) +#define sdmmc_chip_host_maxblklen(tag, handle) \ + ((tag)->host_maxblklen((handle))) +/* card detection */ +#define sdmmc_chip_card_detect(tag, handle) \ + ((tag)->card_detect((handle))) +/* bus power and clock frequency */ +#define sdmmc_chip_bus_power(tag, handle, ocr) \ + ((tag)->bus_power((handle), (ocr))) +#define sdmmc_chip_bus_clock(tag, handle, freq) \ + ((tag)->bus_clock((handle), (freq))) +/* command execution */ +#define sdmmc_chip_exec_command(tag, handle, cmdp) \ + ((tag)->exec_command((handle), (cmdp))) +/* card interrupt */ +#define sdmmc_chip_card_intr_mask(tag, handle, enable) \ + ((tag)->card_intr_mask((handle), (enable))) +#define sdmmc_chip_card_intr_ack(tag, handle) \ + ((tag)->card_intr_ack((handle))) + +/* clock frequencies for sdmmc_chip_bus_clock() */ +#define SDMMC_SDCLK_OFF 0 +#define SDMMC_SDCLK_400KHZ 400 +#define SDMMC_SDCLK_25MHZ 25000 + +struct sdmmcbus_attach_args { + const char *saa_busname; + sdmmc_chipset_tag_t sct; + sdmmc_chipset_handle_t sch; + int flags; + long max_xfer; +}; + +void sdmmc_needs_discover(struct device *); +void sdmmc_card_intr(struct device *); +void sdmmc_delay(u_int); + +#endif diff --git a/sdmmcreg.h b/sdmmcreg.h new file mode 100644 index 0000000..9b182de --- /dev/null +++ b/sdmmcreg.h @@ -0,0 +1,231 @@ +/* $OpenBSD: sdmmcreg.h,v 1.4 2009/01/09 10:55:22 jsg Exp $ */ + +/* + * Copyright (c) 2006 Uwe Stuehler + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SDMMCREG_H_ +#define _SDMMCREG_H_ + +/* MMC commands */ /* response type */ +#define MMC_GO_IDLE_STATE 0 /* R0 */ +#define MMC_SEND_OP_COND 1 /* R3 */ +#define MMC_ALL_SEND_CID 2 /* R2 */ +#define MMC_SET_RELATIVE_ADDR 3 /* R1 */ +#define MMC_SELECT_CARD 7 /* R1 */ +#define MMC_SEND_CSD 9 /* R2 */ +#define MMC_STOP_TRANSMISSION 12 /* R1B */ +#define MMC_SEND_STATUS 13 /* R1 */ +#define MMC_SET_BLOCKLEN 16 /* R1 */ +#define MMC_READ_BLOCK_SINGLE 17 /* R1 */ +#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */ +#define MMC_SET_BLOCK_COUNT 23 /* R1 */ +#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */ +#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */ +#define MMC_APP_CMD 55 /* R1 */ + +/* SD commands */ /* response type */ +#define SD_SEND_RELATIVE_ADDR 3 /* R6 */ +#define SD_SEND_IF_COND 8 /* R7 */ + +/* SD application commands */ /* response type */ +#define SD_APP_SET_BUS_WIDTH 6 /* R1 */ +#define SD_APP_OP_COND 41 /* R3 */ + +/* OCR bits */ +#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */ +#define MMC_OCR_3_5V_3_6V (1<<23) +#define MMC_OCR_3_4V_3_5V (1<<22) +#define MMC_OCR_3_3V_3_4V (1<<21) +#define MMC_OCR_3_2V_3_3V (1<<20) +#define MMC_OCR_3_1V_3_2V (1<<19) +#define MMC_OCR_3_0V_3_1V (1<<18) +#define MMC_OCR_2_9V_3_0V (1<<17) +#define MMC_OCR_2_8V_2_9V (1<<16) +#define MMC_OCR_2_7V_2_8V (1<<15) +#define MMC_OCR_2_6V_2_7V (1<<14) +#define MMC_OCR_2_5V_2_6V (1<<13) +#define MMC_OCR_2_4V_2_5V (1<<12) +#define MMC_OCR_2_3V_2_4V (1<<11) +#define MMC_OCR_2_2V_2_3V (1<<10) +#define MMC_OCR_2_1V_2_2V (1<<9) +#define MMC_OCR_2_0V_2_1V (1<<8) +#define MMC_OCR_1_9V_2_0V (1<<7) +#define MMC_OCR_1_8V_1_9V (1<<6) +#define MMC_OCR_1_7V_1_8V (1<<5) +#define MMC_OCR_1_6V_1_7V (1<<4) + +#define SD_OCR_SDHC_CAP (1<<30) +#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */ + +/* R1 response type bits */ +#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */ +#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */ + +/* 48-bit response decoding (32 bits w/o CRC) */ +#define MMC_R1(resp) ((resp)[0]) +#define MMC_R3(resp) ((resp)[0]) +#define SD_R6(resp) ((resp)[0]) + +/* RCA argument and response */ +#define MMC_ARG_RCA(rca) ((rca) << 16) +#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16) + +/* bus width argument */ +#define SD_ARG_BUS_WIDTH_1 0 +#define SD_ARG_BUS_WIDTH_4 2 + +/* MMC R2 response (CSD) */ +#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) +#define MMC_CSD_CSDVER_1_0 1 +#define MMC_CSD_CSDVER_2_0 2 +#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4) +#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */ +#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */ +#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */ +#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */ +#define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */ +#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) +#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) +#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \ + (MMC_CSD_C_SIZE_MULT((resp))+2)) +#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) + +/* MMC v1 R2 response (CID) */ +#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24) +#define MMC_CID_PNM_V1_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \ + (pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \ + (pnm)[7] = '\0'; \ + } while (0) +#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8) +#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24) +#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8) + +/* MMC v2 R2 response (CID) */ +#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8) +#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16) +#define MMC_CID_PNM_V2_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \ + (pnm)[6] = '\0'; \ + } while (0) +#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32) + +/* SD R2 response (CSD) */ +#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) +#define SD_CSD_CSDVER_1_0 0 +#define SD_CSD_CSDVER_2_0 1 +#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8) +#define SD_CSD_TAAC_1_5_MSEC 0x26 +#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8) +#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8) +#define SD_CSD_SPEED_25_MHZ 0x32 +#define SD_CSD_SPEED_50_MHZ 0x5a +#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12) +#define SD_CSD_CCC_ALL 0x5f5 +#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) +#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1) +#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1) +#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1) +#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1) +#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) +#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \ + (SD_CSD_C_SIZE_MULT((resp))+2)) +#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22) +#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10) +#define SD_CSD_V2_BL_LEN 0x9 /* 512 */ +#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3) +#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3) +#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3) +#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3) +#define SD_CSD_VDD_RW_CURR_100mA 0x7 +#define SD_CSD_VDD_RW_CURR_80mA 0x6 +#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) +#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1) +#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */ +#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */ +#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1) +#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3) +#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4) +#define SD_CSD_RW_BL_LEN_2G 0xa +#define SD_CSD_RW_BL_LEN_1G 0x9 +#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1) +#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1) +#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1) +#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1) +#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1) +#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2) + +/* SD R2 response (CID) */ +#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8) +#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16) +#define SD_CID_PNM_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = '\0'; \ + } while (0) +#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8) +#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32) +#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12) + +/* Might be slow, but it should work on big and little endian systems. */ +#define MMC_RSP_BITS(resp, start, len) __bitfield((resp), (start)-8, (len)) +static __inline int +__bitfield(u_int32_t *src, int start, int len) +{ + u_int8_t *sp; + u_int32_t dst, mask; + int shift, bs, bc; + + if (start < 0 || len < 0 || len > 32) + return 0; + + dst = 0; + mask = len % 32 ? UINT_MAX >> (32 - (len % 32)) : UINT_MAX; + shift = 0; + + while (len > 0) { + sp = (u_int8_t *)src + start / 8; + bs = start % 8; + bc = 8 - bs; + if (bc > len) + bc = len; + dst |= (*sp++ >> bs) << shift; + shift += bc; + start += bc; + len -= bc; + } + + dst &= mask; + return (int)dst; +} + +#endif diff --git a/sdmmcvar.h b/sdmmcvar.h new file mode 100644 index 0000000..0504860 --- /dev/null +++ b/sdmmcvar.h @@ -0,0 +1,280 @@ +/* $OpenBSD: sdmmcvar.h,v 1.16 2009/04/07 16:35:52 blambert Exp $ */ + +/* + * Copyright (c) 2006 Uwe Stuehler + * Copyright (c) 2009 Sven Peter + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SDMMCVAR_H_ +#define _SDMMCVAR_H_ + +#if 0 +#include +#include + +#include +#include + +#include +#include +#endif + +struct sdmmc_csd { + int csdver; /* CSD structure format */ + int mmcver; /* MMC version (for CID format) */ + int capacity; /* total number of sectors */ + int sector_size; /* sector size in bytes */ + int read_bl_len; /* block length for reads */ + /* ... */ +}; + +struct sdmmc_cid { + int mid; /* manufacturer identification number */ + int oid; /* OEM/product identification number */ + char pnm[8]; /* product name (MMC v1 has the longest) */ + int rev; /* product revision */ + int psn; /* product serial number */ + int mdt; /* manufacturing date */ +}; + +typedef u_int32_t sdmmc_response[4]; + +struct sdmmc_softc; + +struct sdmmc_task { + void (*func)(void *arg); + void *arg; + int onqueue; + struct sdmmc_softc *sc; +#if 0 + TAILQ_ENTRY(sdmmc_task) next; +#endif +}; + +#define sdmmc_init_task(xtask, xfunc, xarg) do { \ + (xtask)->func = (xfunc); \ + (xtask)->arg = (xarg); \ + (xtask)->onqueue = 0; \ + (xtask)->sc = NULL; \ +} while (0) + +#define sdmmc_task_pending(xtask) ((xtask)->onqueue) + +struct sdmmc_command { + struct sdmmc_task c_task; /* task queue entry */ + u_int16_t c_opcode; /* SD or MMC command index */ + u_int32_t c_arg; /* SD/MMC command argument */ + sdmmc_response c_resp; /* response buffer */ + void *c_data; /* buffer to send or read into */ + int c_datalen; /* length of data buffer */ + int c_blklen; /* block length */ + int c_flags; /* see below */ +#define SCF_ITSDONE 0x0001 /* command is complete */ +#define SCF_CMD(flags) ((flags) & 0x00f0) +#define SCF_CMD_AC 0x0000 +#define SCF_CMD_ADTC 0x0010 +#define SCF_CMD_BC 0x0020 +#define SCF_CMD_BCR 0x0030 +#define SCF_CMD_READ 0x0040 /* read command (data expected) */ +#define SCF_RSP_BSY 0x0100 +#define SCF_RSP_136 0x0200 +#define SCF_RSP_CRC 0x0400 +#define SCF_RSP_IDX 0x0800 +#define SCF_RSP_PRESENT 0x1000 +/* response types */ +#define SCF_RSP_R0 0 /* none */ +#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R1B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY) +#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136) +#define SCF_RSP_R3 (SCF_RSP_PRESENT) +#define SCF_RSP_R4 (SCF_RSP_PRESENT) +#define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY) +#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) + int c_error; /* errno value on completion */ + + /* Host controller owned fields for data xfer in progress */ + int c_resid; /* remaining I/O */ + u_char *c_buf; /* remaining data */ +}; + +/* + * Decoded PC Card 16 based Card Information Structure (CIS), + * per card (function 0) and per function (1 and greater). + */ +struct sdmmc_cis { + u_int16_t manufacturer; +#define SDMMC_VENDOR_INVALID 0xffff + u_int16_t product; +#define SDMMC_PRODUCT_INVALID 0xffff + u_int8_t function; +#define SDMMC_FUNCTION_INVALID 0xff + u_char cis1_major; + u_char cis1_minor; + char cis1_info_buf[256]; + char *cis1_info[4]; +}; + +/* + * Structure describing either an SD card I/O function or a SD/MMC + * memory card from a "stack of cards" that responded to CMD2. For a + * combo card with one I/O function and one memory card, there will be + * two of these structures allocated. Each card slot has such a list + * of sdmmc_function structures. + */ +struct sdmmc_function { + /* common members */ + struct sdmmc_softc *sc; /* card slot softc */ + u_int16_t rca; /* relative card address */ + int flags; +#define SFF_ERROR 0x0001 /* function is poo; ignore it */ +#define SFF_SDHC 0x0002 /* SD High Capacity card */ +#if 0 + SIMPLEQ_ENTRY(sdmmc_function) sf_list; +#endif + /* SD card I/O function members */ + int number; /* I/O function number or -1 */ + struct device *child; /* function driver */ + struct sdmmc_cis cis; /* decoded CIS */ + /* SD/MMC memory card members */ + struct sdmmc_csd csd; /* decoded CSD value */ + struct sdmmc_cid cid; /* decoded CID value */ + sdmmc_response raw_cid; /* temp. storage for decoding */ +}; + +/* + * Structure describing a single SD/MMC/SDIO card slot. + */ +struct sdmmc_softc { + struct device sc_dev; /* base device */ +#define SDMMCDEVNAME(sc) ((sc)->sc_dev.dv_xname) + sdmmc_chipset_tag_t sct; /* host controller chipset tag */ + sdmmc_chipset_handle_t sch; /* host controller chipset handle */ + int sc_flags; +#define SMF_SD_MODE 0x0001 /* host in SD mode (MMC otherwise) */ +#define SMF_IO_MODE 0x0002 /* host in I/O mode (SD mode only) */ +#define SMF_MEM_MODE 0x0004 /* host in memory mode (SD or MMC) */ +#define SMF_CARD_PRESENT 0x0010 /* card presence noticed */ +#define SMF_CARD_ATTACHED 0x0020 /* card driver(s) attached */ +#define SMF_STOP_AFTER_MULTIPLE 0x0040 /* send a stop after a multiple cmd */ + int sc_function_count; /* number of I/O functions (SDIO) */ + struct sdmmc_function *sc_card; /* selected card */ + struct sdmmc_function *sc_fn0; /* function 0, the card itself */ +#if 0 + SIMPLEQ_HEAD(, sdmmc_function) sf_head; /* list of card functions */ +#endif + int sc_dying; /* bus driver is shutting down */ + struct proc *sc_task_thread; /* asynchronous tasks */ +#if 0 + TAILQ_HEAD(, sdmmc_task) sc_tskq; /* task thread work queue */ +#endif + struct sdmmc_task sc_discover_task; /* card attach/detach task */ + struct sdmmc_task sc_intr_task; /* card interrupt task */ +#if 0 + struct lock sc_lock; /* lock around host controller */ +#endif + void *sc_scsibus; /* SCSI bus emulation softc */ +#if 0 + TAILQ_HEAD(, sdmmc_intr_handler) sc_intrq; /* interrupt handlers */ +#endif + long sc_max_xfer; /* maximum transfer size */ +}; + +/* + * Attach devices at the sdmmc bus. + */ +struct sdmmc_attach_args { + struct scsi_link *scsi_link; /* XXX */ + struct sdmmc_function *sf; +}; + +#define IPL_SDMMC IPL_BIO +#define splsdmmc() splbio() + +#define SDMMC_LOCK(sc) lockmgr(&(sc)->sc_lock, LK_EXCLUSIVE, NULL) +#define SDMMC_UNLOCK(sc) lockmgr(&(sc)->sc_lock, LK_RELEASE, NULL) +#define SDMMC_ASSERT_LOCKED(sc) \ + KASSERT(lockstatus(&((sc))->sc_lock) == LK_EXCLUSIVE) + +#if 0 +void sdmmc_add_task(struct sdmmc_softc *, struct sdmmc_task *); +void sdmmc_del_task(struct sdmmc_task *); + +struct sdmmc_function *sdmmc_function_alloc(struct sdmmc_softc *); +void sdmmc_function_free(struct sdmmc_function *); +int sdmmc_set_bus_power(struct sdmmc_softc *, u_int32_t, u_int32_t); +int sdmmc_mmc_command(struct sdmmc_softc *, struct sdmmc_command *); +int sdmmc_app_command(struct sdmmc_softc *, struct sdmmc_command *); +void sdmmc_go_idle_state(struct sdmmc_softc *); +int sdmmc_select_card(struct sdmmc_softc *, struct sdmmc_function *); +int sdmmc_set_relative_addr(struct sdmmc_softc *, + struct sdmmc_function *); +int sdmmc_send_if_cond(struct sdmmc_softc *, uint32_t); + +void sdmmc_intr_enable(struct sdmmc_function *); +void sdmmc_intr_disable(struct sdmmc_function *); +void *sdmmc_intr_establish(struct device *, int (*)(void *), + void *, const char *); +void sdmmc_intr_disestablish(void *); +void sdmmc_intr_task(void *); + +int sdmmc_io_enable(struct sdmmc_softc *); +void sdmmc_io_scan(struct sdmmc_softc *); +int sdmmc_io_init(struct sdmmc_softc *, struct sdmmc_function *); +void sdmmc_io_attach(struct sdmmc_softc *); +void sdmmc_io_detach(struct sdmmc_softc *); +u_int8_t sdmmc_io_read_1(struct sdmmc_function *, int); +u_int16_t sdmmc_io_read_2(struct sdmmc_function *, int); +u_int32_t sdmmc_io_read_4(struct sdmmc_function *, int); +int sdmmc_io_read_multi_1(struct sdmmc_function *, int, u_char *, int); +void sdmmc_io_write_1(struct sdmmc_function *, int, u_int8_t); +void sdmmc_io_write_2(struct sdmmc_function *, int, u_int16_t); +void sdmmc_io_write_4(struct sdmmc_function *, int, u_int32_t); +int sdmmc_io_write_multi_1(struct sdmmc_function *, int, u_char *, int); +int sdmmc_io_function_ready(struct sdmmc_function *); +int sdmmc_io_function_enable(struct sdmmc_function *); +void sdmmc_io_function_disable(struct sdmmc_function *); + +int sdmmc_read_cis(struct sdmmc_function *, struct sdmmc_cis *); +void sdmmc_print_cis(struct sdmmc_function *); +void sdmmc_check_cis_quirks(struct sdmmc_function *); + +int sdmmc_mem_enable(struct sdmmc_softc *); +void sdmmc_mem_scan(struct sdmmc_softc *); +int sdmmc_mem_init(struct sdmmc_softc *, struct sdmmc_function *); +int sdmmc_mem_read_block(struct sdmmc_function *, int, u_char *, size_t); +int sdmmc_mem_write_block(struct sdmmc_function *, int, u_char *, size_t); + +/* ioctls */ + +#include + +struct bio_sdmmc_command { + void *cookie; + struct sdmmc_command cmd; +}; + +struct bio_sdmmc_debug { + void *cookie; + int debug; +}; + +#define SDIOCEXECMMC _IOWR('S',0, struct bio_sdmmc_command) +#define SDIOCEXECAPP _IOWR('S',1, struct bio_sdmmc_command) +#define SDIOCSETDEBUG _IOWR('S',2, struct bio_sdmmc_debug) +#endif + +#endif