From patchwork Fri Nov 19 16:10:55 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?G=C3=A9rald_Kerma?= X-Patchwork-Id: 72264 X-Patchwork-Delegate: afleming@freescale.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 2E63BB712B for ; Sat, 20 Nov 2010 03:11:16 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id DD9332831A; Fri, 19 Nov 2010 17:11:13 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id LdZEsdWjd5by; Fri, 19 Nov 2010 17:11:13 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id CD7CF2831B; Fri, 19 Nov 2010 17:11:10 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 9CDBA2831B for ; Fri, 19 Nov 2010 17:11:05 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id T6ZSaGVE-Ulf for ; Fri, 19 Nov 2010 17:11:03 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-yx0-f172.google.com (mail-yx0-f172.google.com [209.85.213.172]) by theia.denx.de (Postfix) with ESMTP id 0B2AE2831A for ; Fri, 19 Nov 2010 17:11:01 +0100 (CET) Received: by yxd39 with SMTP id 39so2938731yxd.3 for ; Fri, 19 Nov 2010 08:11:00 -0800 (PST) Received: by 10.216.240.198 with SMTP id e48mr1071319wer.0.1290183057040; Fri, 19 Nov 2010 08:10:57 -0800 (PST) Received: from [192.168.0.168] (louche.kerma.com.fr [88.178.80.34]) by mx.google.com with ESMTPS id x15sm872263weq.7.2010.11.19.08.10.55 (version=SSLv3 cipher=RC4-MD5); Fri, 19 Nov 2010 08:10:56 -0800 (PST) Message-ID: <4CE6A18F.90601@gmail.com> Date: Fri, 19 Nov 2010 17:10:55 +0100 From: =?ISO-8859-1?Q?G=E9rald_Kerma?= User-Agent: Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.9.2.12) Gecko/20101027 Lightning/1.0b2 Thunderbird/3.1.6 MIME-Version: 1.0 To: Prafulla Wadaskar References: In-Reply-To: X-Enigmail-Version: 1.1.1 OpenPGP: id=DED5B136 Cc: "u-boot@lists.denx.de" , Clint Adams Subject: [U-Boot] [PATCH v2 1/2] mmc : Add SDIO driver for Marvell SoCs (Kirkwood) X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.9 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de --- arch/arm/include/asm/arch-kirkwood/kirkwood.h | 1 + arch/arm/include/asm/arch-kirkwood/mmc_host_def.h | 292 +++++++++ drivers/mmc/Makefile | 1 + drivers/mmc/mv_sdio.c | 663 +++++++++++++++++++++ 4 files changed, 957 insertions(+), 0 deletions(-) + pr_debug("cmd6 resp : 0x%08x 0x%08x 0x%08x 0x%08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3]); + + } + + resp = (ulong *) &mv_mmc_csd; + pr_debug("csd: 0x%08x 0x%08x 0x%08x 0x%08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3]); + + + /* check SDHC */ + if ((resp[0]&0xf0000000)==0x40000000) + is_sdhc = 1; + + /* set block len */ + resp = mv_mmc_cmd(MMC_CMD_SET_BLOCKLEN, MMC_BLOCK_SIZE, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); + if (!resp) { + pr_debug("mv_mmc_block_read: set blk len fails\n"); + return -ENODEV; + } + + if (verbose) { + if (is_sd) + print_sd_cid((mv_sd_cid_t *) cidbuf); + else + print_mmc_cid((mv_mmc_cid_t *) cidbuf); + } + + mvsdio_set_clock(CONFIG_SYS_MMC_CLK_PP); + + fat_register_device(&mmc_dev,1); /* partitions start counting with 1 */ + + return 0; +} + +#endif /* CONFIG_MMC */ Signed-off-by: Gérald Kerma diff --git a/arch/arm/include/asm/arch-kirkwood/kirkwood.h b/arch/arm/include/asm/arch-kirkwood/kirkwood.h index 0104418..4f9fe7e 100644 --- a/arch/arm/include/asm/arch-kirkwood/kirkwood.h +++ b/arch/arm/include/asm/arch-kirkwood/kirkwood.h @@ -60,6 +60,7 @@ #define KW_EGIGA0_BASE (KW_REGISTER(0x72000)) #define KW_EGIGA1_BASE (KW_REGISTER(0x76000)) #define KW_SATA_BASE (KW_REGISTER(0x80000)) +#define KW_SDIO_BASE (KW_REGISTER(0x90000)) /* Kirkwood Sata controller has two ports */ #define KW_SATA_PORT0_OFFSET 0x2000 diff --git a/arch/arm/include/asm/arch-kirkwood/mmc_host_def.h b/arch/arm/include/asm/arch-kirkwood/mmc_host_def.h new file mode 100644 index 0000000..4327c93 --- /dev/null +++ b/arch/arm/include/asm/arch-kirkwood/mmc_host_def.h @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2008 Marvell Semiconductors, All Rights Reserved. + * Copyright (C) 2010 Gérald Kerma + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _MVSDIO_INCLUDE +#define _MVSDIO_INCLUDE + +#define SDIO_REG(x) (KW_SDIO_BASE + (x)) + +#define MVSDMMC_DMA_SIZE 65536 +#define MVSDMMC_CMD_TIMEOUT 2 /* 100 usec*/ + +/* + * Clock rates + */ + +#define MVSD_CLOCKRATE_MAX 50000000 +#define MVSD_BASE_DIV_MAX 0x7ff + +#define CONFIG_SYS_MMC_CLK_PP 25000000 + +/* + * The base MMC clock rate + */ + +#define MVSDMMC_CLOCKRATE_MIN 100000 +#define MVSDMMC_CLOCKRATE_MAX MVSD_CLOCKRATE_MAX +#define MVSDMMC_BASE_FAST_CLOCK CONFIG_SYS_TCLK + + +/* + * SDIO register + */ + +#define SDIO_SYS_ADDR 0x000 +#define SDIO_SYS_ADDR_LOW 0x000 +#define SDIO_SYS_ADDR_HI 0x004 +#define SDIO_BLK_SIZE 0x008 +#define SDIO_BLK_COUNT 0x00c +#define SDIO_ARG 0x010 +#define SDIO_ARG_LOW 0x010 +#define SDIO_ARG_HI 0x014 +#define SDIO_XFER_MODE 0x018 +#define SDIO_CMD 0x01c +#define SDIO_RSP(i) (0x020 + ((i)<<2)) +#define SDIO_RSP0 0x020 +#define SDIO_RSP1 0x024 +#define SDIO_RSP2 0x028 +#define SDIO_RSP3 0x02c +#define SDIO_RSP4 0x030 +#define SDIO_RSP5 0x034 +#define SDIO_RSP6 0x038 +#define SDIO_RSP7 0x03c +#define SDIO_BUF_DATA_PORT 0x040 +#define SDIO_RSVED 0x044 + +#define SDIO_PRESENT_STATE0 0x048 +#define SDIO_PRESENT_STATE1 0x04c +#define SDIO_HOST_CTRL 0x050 +#define SDIO_BLK_GAP_CTRL 0x054 +#define SDIO_CLK_CTRL 0x058 +#define SDIO_SW_RESET 0x05c +#define SDIO_NOR_INTR_STATUS 0x060 +#define SDIO_ERR_INTR_STATUS 0x064 +#define SDIO_NOR_STATUS_EN 0x068 +#define SDIO_ERR_STATUS_EN 0x06c +#define SDIO_NOR_INTR_EN 0x070 +#define SDIO_ERR_INTR_EN 0x074 +#define SDIO_AUTOCMD12_ERR_STATUS 0x078 +#define SDIO_CURR_BYTE_LEFT 0x07c +#define SDIO_CURR_BLK_LEFT 0x080 +#define SDIO_AUTOCMD12_ARG_LOW 0x084 +#define SDIO_AUTOCMD12_ARG_HI 0x088 +#define SDIO_AUTOCMD12_INDEX 0x08c +#define SDIO_AUTO_RSP(i) (0x090 + ((i)<<2)) +#define SDIO_AUTO_RSP0 0x090 +#define SDIO_AUTO_RSP1 0x094 +#define SDIO_AUTO_RSP2 0x098 +#define SDIO_CLK_DIV 0x128 + +#define WINDOW_CTRL(i) (0x108 + ((i) << 3)) +#define WINDOW_BASE(i) (0x10c + ((i) << 3)) + + +/* + * SDIO_PRESENT_STATE + */ + +#define CARD_BUSY (1 << 1) +#define CMD_INHIBIT (1 << 0) +#define CMD_TXACTIVE (1 << 8) +#define CMD_RXACTIVE (1 << 9) +#define CMD_AUTOCMD12ACTIVE (1 << 14) + +#define CMD_BUS_BUSY (CMD_AUTOCMD12ACTIVE| \ + CMD_RXACTIVE| \ + CMD_TXACTIVE| \ + CMD_INHIBIT| \ + CARD_BUSY) + +/* + * SDIO_CMD + */ + +#define SDIO_CMD_RSP_NONE (0 << 0) +#define SDIO_CMD_RSP_136 (1 << 0) +#define SDIO_CMD_RSP_48 (2 << 0) +#define SDIO_CMD_RSP_48BUSY (3 << 0) + +#define SDIO_CMD_CHECK_DATACRC16 (1 << 2) +#define SDIO_CMD_CHECK_CMDCRC (1 << 3) +#define SDIO_CMD_INDX_CHECK (1 << 4) +#define SDIO_CMD_DATA_PRESENT (1 << 5) +#define SDIO_UNEXPECTED_RESP (1 << 7) + + +/* + * SDIO_XFER_MODE + */ + +#define SDIO_XFER_MODE_STOP_CLK (1 << 5) +#define SDIO_XFER_MODE_HW_WR_DATA_EN (1 << 1) +#define SDIO_XFER_MODE_AUTO_CMD12 (1 << 2) +#define SDIO_XFER_MODE_INT_CHK_EN (1 << 3) +#define SDIO_XFER_MODE_TO_HOST (1 << 4) + + +/* + * SDIO_HOST_CTRL + */ + +#define SDIO_HOST_CTRL_PUSH_PULL_EN (1 << 0) + +#define SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY (0 << 1) +#define SDIO_HOST_CTRL_CARD_TYPE_IO_ONLY (1 << 1) +#define SDIO_HOST_CTRL_CARD_TYPE_IO_MEM_COMBO (2 << 1) +#define SDIO_HOST_CTRL_CARD_TYPE_IO_MMC (3 << 1) +#define SDIO_HOST_CTRL_CARD_TYPE_MASK (3 << 1) + +#define SDIO_HOST_CTRL_BIG_ENDIAN (1 << 3) +#define SDIO_HOST_CTRL_LSB_FIRST (1 << 4) +#define SDIO_HOST_CTRL_ID_MODE_LOW_FREQ (1 << 5) +#define SDIO_HOST_CTRL_HALF_SPEED (1 << 6) +#define SDIO_HOST_CTRL_DATA_WIDTH_4_BITS (1 << 9) +#define SDIO_HOST_CTRL_HI_SPEED_EN (1 << 10) + + +#define SDIO_HOST_CTRL_TMOUT_MASK (0xf << 11) +#define SDIO_HOST_CTRL_TMOUT_MAX (0xf << 11) +#define SDIO_HOST_CTRL_TMOUT(x) ((x) << 11) +#define SDIO_HOST_CTRL_TMOUT_EN (1 << 15) + +#define SDIO_HOST_CTRL_DFAULT_OPEN_DRAIN \ + (SDIO_HOST_CTRL_TMOUT(x)(0xf)) +#define SDIO_HOST_CTRL_DFAULT_PUSH_PULL \ + (SDIO_HOST_CTRL_TMOUT(x)(0xf) | SDIO_HOST_CTRL_PUSH_PULL_EN) + + +/* + * NOR status bits + */ + +#define SDIO_NOR_ERROR (1 << 15) +#define SDIO_NOR_UNEXP_RSP (1 << 14) +#define SDIO_NOR_AUTOCMD12_DONE (1 << 13) +#define SDIO_NOR_SUSPEND_ON (1 << 12) +#define SDIO_NOR_LMB_FF_8W_AVAIL (1 << 11) +#define SDIO_NOR_LMB_FF_8W_FILLED (1 << 10) +#define SDIO_NOR_READ_WAIT_ON (1 << 9) +#define SDIO_NOR_CARD_INT (1 << 8) +#define SDIO_NOR_READ_READY (1 << 5) +#define SDIO_NOR_WRITE_READY (1 << 4) +#define SDIO_NOR_DMA_INI (1 << 3) +#define SDIO_NOR_BLK_GAP_EVT (1 << 2) +#define SDIO_NOR_XFER_DONE (1 << 1) +#define SDIO_NOR_CMD_DONE (1 << 0) + + +/* + * ERR status bits + */ + +#define SDIO_ERR_CRC_STATUS (1 << 14) +#define SDIO_ERR_CRC_STARTBIT (1 << 13) +#define SDIO_ERR_CRC_ENDBIT (1 << 12) +#define SDIO_ERR_RESP_TBIT (1 << 11) +#define SDIO_ERR_SIZE (1 << 10) +#define SDIO_ERR_CMD_STARTBIT (1 << 9) +#define SDIO_ERR_AUTOCMD12 (1 << 8) +#define SDIO_ERR_DATA_ENDBIT (1 << 6) +#define SDIO_ERR_DATA_CRC (1 << 5) +#define SDIO_ERR_DATA_TIMEOUT (1 << 4) +#define SDIO_ERR_CMD_INDEX (1 << 3) +#define SDIO_ERR_CMD_ENDBIT (1 << 2) +#define SDIO_ERR_CMD_CRC (1 << 1) +#define SDIO_ERR_CMD_TIMEOUT (1 << 0) + +#define SDIO_ERR_INTR_MASK 0xFFFF + + +#define MMC_BLOCK_SIZE 512 +#define MMC_CMD_RESET 0 +#define MMC_CMD_SEND_OP_COND 1 +#define MMC_CMD_ALL_SEND_CID 2 +#define MMC_CMD_SET_RCA 3 +#define MMC_CMD_SELECT_CARD 7 +#define MMC_CMD_SEND_CSD 9 +#define MMC_CMD_SEND_CID 10 +#define MMC_CMD_SEND_STATUS 13 +#define MMC_CMD_SET_BLOCKLEN 16 +#define MMC_CMD_READ_BLOCK 17 +#define MMC_CMD_RD_BLK_MULTI 18 +#define MMC_CMD_WRITE_BLOCK 24 +#define MMC_MAX_BLOCK_SIZE 512 + +typedef struct mv_mmc_cid +{ + /* FIXME: BYTE_ORDER */ + uchar year:4, + month:4; + uchar sn[3]; + uchar fwrev:4, + hwrev:4; + uchar name[6]; + uchar id[3]; +} mv_mmc_cid_t; + +typedef struct mv_mmc_csd +{ + uchar ecc:2, + file_format:2, + tmp_write_protect:1, + perm_write_protect:1, + copy:1, + file_format_grp:1; + uint64_t content_prot_app:1, + rsvd3:4, + write_bl_partial:1, + write_bl_len:4, + r2w_factor:3, + default_ecc:2, + wp_grp_enable:1, + wp_grp_size:5, + erase_grp_mult:5, + erase_grp_size:5, + c_size_mult1:3, + vdd_w_curr_max:3, + vdd_w_curr_min:3, + vdd_r_curr_max:3, + vdd_r_curr_min:3, + c_size:12, + rsvd2:2, + dsr_imp:1, + read_blk_misalign:1, + write_blk_misalign:1, + read_bl_partial:1; + ushort read_bl_len:4, + ccc:12; + uchar tran_speed; + uchar nsac; + uchar taac; + uchar rsvd1:2, + spec_vers:4, + csd_structure:2; +} mv_mmc_csd_t; + +typedef struct { + char pnm_0; /* product name */ + char oid_1; /* OEM/application ID */ + char oid_0; + uint8_t mid; /* manufacturer ID */ + char pnm_4; + char pnm_3; + char pnm_2; + char pnm_1; + uint8_t psn_2; /* product serial number */ + uint8_t psn_1; + uint8_t psn_0; /* MSB */ + uint8_t prv; /* product revision */ + uint8_t crc; /* CRC7 checksum, b0 is unused and set to 1 */ + uint8_t mdt_1; /* manufacturing date, LSB, RRRRyyyy yyyymmmm */ + uint8_t mdt_0; /* MSB */ + uint8_t psn_3; /* LSB */ +} mv_sd_cid_t; + +#endif /* _MVSDIO_INCLUDE */ diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 68afd30..2a4e1d4 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -30,6 +30,7 @@ COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o +COBJS-$(CONFIG_MV_SDIO) += mv_sdio.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o diff --git a/drivers/mmc/mv_sdio.c b/drivers/mmc/mv_sdio.c new file mode 100644 index 0000000..593250f --- /dev/null +++ b/drivers/mmc/mv_sdio.c @@ -0,0 +1,663 @@ +/* + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * Copyright (C) 2010 Gérald Kerma + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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; either version 2 of + * the License, or (at your option) any later version. + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KIRKWOOD +#include +#endif +#include + +#ifdef CONFIG_MMC + +#define DRIVER_NAME "mv-sdio" + +#ifdef DEBUG +#define pr_debug(fmt, args...) printf(fmt, ##args) +#else +#define pr_debug(...) do { } while(0) +#endif + +static int is_sdhc; +extern int fat_register_device(block_dev_desc_t *dev_desc, int part_no); +static block_dev_desc_t mmc_dev; +block_dev_desc_t * mmc_get_dev(int dev) +{ + return ((block_dev_desc_t *)&mmc_dev); +} + +/* + * FIXME needs to read cid and csd info to determine block size + * and other parameters + */ +static uchar mmc_buf[MMC_BLOCK_SIZE]; +static mv_mmc_csd_t mv_mmc_csd; +static int mmc_ready = 0; + +/* MMC_DEFAULT_RCA should probably be just 1, but this may break other code + that expects it to be shifted. */ +static u_int16_t rca = 0; + +/* used for debug */ +static u_int32_t mv_mmc_size(const struct mv_mmc_csd *csd) +{ + u_int32_t block_len, mult, blocknr; + + block_len = csd->read_bl_len << 12; + mult = csd->c_size_mult1 << 8; + blocknr = (csd->c_size+1) * mult; + + return blocknr * block_len; +} + +static int isprint (unsigned char ch) +{ + if (ch >= 32 && ch < 127) + return (1); + + return (0); +} + +static int toprint(char *dst, char c) +{ + if (isprint(c)) { + *dst = c; + return 1; + } + + return sprintf(dst,"\\x%02x", c); + +} + +static void print_mmc_cid(mv_mmc_cid_t *cid) +{ + printf("MMC found. Card desciption is:\n"); + printf("Manufacturer ID = %02x%02x%02x\n", + cid->id[0], cid->id[1], cid->id[2]); + printf("HW/FW Revision = %x %x\n",cid->hwrev, cid->fwrev); + cid->hwrev = cid->fwrev = 0; /* null terminate string */ + printf("Product Name = %s\n",cid->name); + printf("Serial Number = %02x%02x%02x\n", + cid->sn[0], cid->sn[1], cid->sn[2]); + printf("Month = %d\n",cid->month); + printf("Year = %d\n",1997 + cid->year); +} + +static void print_sd_cid(mv_sd_cid_t *cid) +{ + int len; + char tbuf[64]; + + printf("SD%s found. Card desciption is:\n", is_sdhc?"HC":""); + + len = 0; + len += toprint(&tbuf[len], cid->oid_0); + len += toprint(&tbuf[len], cid->oid_1); + tbuf[len] = 0; + + printf("Manufacturer: 0x%02x, OEM \"%s\"\n", + cid->mid, tbuf); + + len = 0; + len += toprint(&tbuf[len], cid->pnm_0); + len += toprint(&tbuf[len], cid->pnm_1); + len += toprint(&tbuf[len], cid->pnm_2); + len += toprint(&tbuf[len], cid->pnm_3); + len += toprint(&tbuf[len], cid->pnm_4); + tbuf[len] = 0; + + printf("Product name: \"%s\", revision %d.%d\n", + tbuf, + cid->prv >> 4, cid->prv & 15); + + printf("Serial number: %u\n", + cid->psn_0 << 24 | cid->psn_1 << 16 | cid->psn_2 << 8 | + cid->psn_3); + printf("Manufacturing date: %d/%d\n", + cid->mdt_1 & 15, + 2000+((cid->mdt_0 & 15) << 4)+((cid->mdt_1 & 0xf0) >> 4)); + + printf("CRC: 0x%02x, b0 = %d\n", + cid->crc >> 1, cid->crc & 1); +} + +static void mvsdio_set_clock(unsigned int clock) +{ + unsigned int m; + + m = MVSDMMC_BASE_FAST_CLOCK/(2*clock) - 1; + + pr_debug("mvsdio_set_clock: dividor = 0x%x clock=%d\n", + m, clock); + + + writew(m & 0x7ff, SDIO_REG(SDIO_CLK_DIV)); + + if (isprint(1)) + udelay(10*1000); +} + +/****************************************************/ +static ulong * mv_mmc_cmd(ulong cmd, ulong arg, ushort xfermode, ushort resptype, ushort waittype) +/****************************************************/ +{ + static ulong resp[4]; + ushort done ; + int err = 0 ; + ulong curr, start, diff, hz; + ushort response[8], resp_indx = 0; + + pr_debug("mv_mmc_cmd %x, arg: %x,xfer: %x,resp: %x, wait : %x\n" + , (unsigned int)cmd, (unsigned int)arg, xfermode, resptype, waittype); + + + /* clear status */ + writew(0xffff, SDIO_REG(SDIO_NOR_INTR_STATUS)); + writew(0xffff, SDIO_REG(SDIO_ERR_INTR_STATUS)); + + start = get_timer(0); + hz = CONFIG_SYS_HZ; + + while((readw(SDIO_REG(SDIO_PRESENT_STATE0)) & CARD_BUSY)) { + curr = get_timer(0); + diff = (long) curr - (long) start; + if (diff > (3*hz)) + { + /* 3 seconds timeout, card busy, can't sent cmd */ + printf("card too busy \n"); + return 0; + } + } + + writew((ushort)(arg&0xffff), SDIO_REG(SDIO_ARG_LOW)); + writew((ushort)(arg>>16), SDIO_REG(SDIO_ARG_HI)); + writew(xfermode, SDIO_REG(SDIO_XFER_MODE)); + if( (cmd == MMC_CMD_READ_BLOCK) || (cmd == 25) ) + { + writew(((cmd << 8) | resptype | 0x3c ) , SDIO_REG(SDIO_CMD)); + pr_debug("cmd reg : %x\n", readw(SDIO_REG(SDIO_CMD) )) ; + + } + else + { + writew(((cmd << 8) | resptype ), SDIO_REG(SDIO_CMD)); + } + + done = readw(SDIO_REG(SDIO_NOR_INTR_STATUS)) & waittype; + start = get_timer(0); + + while( done!=waittype) + { + done = readw(SDIO_REG(SDIO_NOR_INTR_STATUS)) & waittype; + + if( readw(SDIO_REG(SDIO_NOR_INTR_STATUS)) & 0x8000 ) + { + pr_debug("Error! cmd : %d, err : %04x\n", (unsigned int)cmd, readw(SDIO_REG(SDIO_ERR_INTR_STATUS))) ; + + return 0 ; /* error happen */ + } + + curr = get_timer(0); + diff = (long) curr - (long) start; + if (diff > (3*hz)) + { + pr_debug("cmd timeout, status : %04x\n", readw(SDIO_REG(SDIO_NOR_INTR_STATUS))); + pr_debug("xfer mode : %04x\n", readw(SDIO_REG(SDIO_XFER_MODE))); + + err = 1 ; + break; + } + } + + for (resp_indx = 0 ; resp_indx < 8; resp_indx++) + response[resp_indx] = readw(SDIO_REG(SDIO_RSP(resp_indx))); + + memset(resp, 0, sizeof(resp)); + + switch (resptype & 0x3) { + case SDIO_CMD_RSP_48: + case SDIO_CMD_RSP_48BUSY: + resp[0] = ((response[2] & 0x3f) << (8 - 8)) | + ((response[1] & 0xffff) << (14 - 8)) | + ((response[0] & 0x3ff) << (30 - 8)); + resp[1] = ((response[0] & 0xfc00) >> 10); + break; + + case SDIO_CMD_RSP_136: + resp[3] = ((response[7] & 0x3fff) << 8) | + ((response[6] & 0x3ff) << 22); + resp[2] = ((response[6] & 0xfc00) >> 10) | + ((response[5] & 0xffff) << 6) | + ((response[4] & 0x3ff) << 22); + resp[1] = ((response[4] & 0xfc00) >> 10) | + ((response[3] & 0xffff) << 6) | + ((response[2] & 0x3ff) << 22); + resp[0] = ((response[2] & 0xfc00) >> 10) | + ((response[1] & 0xffff) << 6) | + ((response[0] & 0x3ff) << 22); + break; + default: + return 0; + } + int i; + pr_debug("MMC resp :"); + for (i=0; i<4; ++i ) { + pr_debug(" %08x", (unsigned int)resp[i]); + } + pr_debug("\n"); + if( err ) + return NULL ; + else + return resp; +} + +/****************************************************/ +static int mv_mmc_block_read(uchar *dst, ulong src, ulong len) +/****************************************************/ +{ + ulong *resp; + + if (len == 0) { + return 0; + } + + if (is_sdhc) { + /* SDHC: use block address */ + src >>= 9; + } + + pr_debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong)dst, src, (int)len); + + /* prepare for dma transfer */ + writew(((ulong)(dst))&0xffff,SDIO_REG(SDIO_SYS_ADDR_LOW)); + writew(((ulong)(dst)>>16)&0xffff,SDIO_REG(SDIO_SYS_ADDR_HI)); + writew(len,SDIO_REG(SDIO_BLK_SIZE)); + writew(1,SDIO_REG(SDIO_BLK_COUNT)); + + /* send read command */ + resp = mv_mmc_cmd(MMC_CMD_READ_BLOCK, src, 0x10 , + SDIO_CMD_RSP_48, SDIO_NOR_XFER_DONE); + if (!resp) { + pr_debug("mv_mmc_block_read: mmc read block cmd fails\n"); + return -EIO; + } + + return 0; +} + +/****************************************************/ +int mv_mmc_read(ulong src, uchar *dst, int size) +/****************************************************/ +{ + ulong end, part_start, part_end, part_len, aligned_start, aligned_end; + ulong mmc_block_size, mmc_block_address; + + if (size == 0) { + return 0; + } + + if (!mmc_ready) { + printf("Please initial the MMC first\n"); + return -1; + } + + mmc_block_size = MMC_BLOCK_SIZE; + mmc_block_address = ~(mmc_block_size - 1); + + end = src + size; + part_start = ~mmc_block_address & src; + part_end = ~mmc_block_address & end; + aligned_start = mmc_block_address & src; + aligned_end = mmc_block_address & end; + + /* all block aligned accesses */ + pr_debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", + (long unsigned int)src,(ulong)dst, end, part_start, part_end, aligned_start, aligned_end); + + if (part_start) { + part_len = mmc_block_size - part_start; + pr_debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", + (long unsigned int)src,(ulong)dst, end, part_start, part_end, aligned_start, aligned_end); + + if ((mv_mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0) { + return -1; + } + memcpy(dst, mmc_buf+part_start, part_len); + dst += part_len; + src += part_len; + } + pr_debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", + (long unsigned int)src,(ulong)dst, end, part_start, part_end, aligned_start, aligned_end); + + for (; src < aligned_end; aligned_start +=mmc_block_size, src += mmc_block_size, dst += mmc_block_size) { + pr_debug("al src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", + (long unsigned int)src,(ulong)dst, end, part_start, part_end, aligned_start, aligned_end); + + if ((mv_mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < 0) { + printf("mmc block read error\n"); + return -1; + } + memcpy(dst, mmc_buf, mmc_block_size); + } + pr_debug("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", + (long unsigned int)src,(ulong)dst, end, part_start, part_end, aligned_start, aligned_end); + + if (part_end && src < end) { + pr_debug("pe src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", + (long unsigned int)src,(ulong)dst, end, part_start, part_end, aligned_start, aligned_end); + + if ((mv_mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) { + return -1; + } + memcpy(dst, mmc_buf, part_end); + } + return 0; +} + +/****************************************************/ +static ulong mv_mmc_bread(int dev_num, ulong blknr, ulong blkcnt, ulong *dst) +/****************************************************/ +{ + int mmc_block_size = MMC_BLOCK_SIZE; + ulong src = blknr * mmc_block_size; + + mv_mmc_read(src, (uchar *)dst, blkcnt*mmc_block_size); + return blkcnt; +} + +/****************************************************/ +int mmc_legacy_init(int verbose) +/****************************************************/ +{ + int retries, rc = -ENODEV; + ulong *resp; + int sd_ver20; + int is_sd; + ushort reg; + uchar cidbuf[64]; + + sd_ver20 = 0; + is_sdhc = 0; + is_sd = 0; + + /* Initial Host Ctrl : Timeout : max , Normal Speed mode, 4-bit data mode */ + /* Big Endian, SD memory Card, Push_pull CMD Line */ + writew( SDIO_HOST_CTRL_TMOUT(0xf) | + SDIO_HOST_CTRL_DATA_WIDTH_4_BITS | + SDIO_HOST_CTRL_BIG_ENDIAN | + SDIO_HOST_CTRL_PUSH_PULL_EN | + SDIO_HOST_CTRL_CARD_TYPE_MEM_ONLY , + SDIO_REG(SDIO_HOST_CTRL)); + + writew( 0, SDIO_REG(SDIO_CLK_CTRL)); + + /* enable status */ + writew( 0xffff, SDIO_REG(SDIO_NOR_STATUS_EN)); + writew( 0xffff, SDIO_REG(SDIO_ERR_STATUS_EN)); + + /* disable interrupts */ + writew( 0, SDIO_REG(SDIO_NOR_INTR_EN)); + writew( 0, SDIO_REG(SDIO_ERR_INTR_EN)); + + writew( 0x100, SDIO_REG(SDIO_SW_RESET)); + udelay(10000); + + mv_mmc_csd.c_size = 0; + + /* reset */ + retries = 10; + resp = mv_mmc_cmd(0, 0, 0, SDIO_CMD_RSP_NONE, SDIO_NOR_CMD_DONE ); + pr_debug("cmd 0 resp : %08x %08x %08x %08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3] ); + + + pr_debug ("trying to detect SD card version\n"); + + resp = mv_mmc_cmd(8, 0x000001aa, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); + pr_debug("cmd 8 resp : %08x %08x %08x %08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3] ); + + if (resp && (resp[0] & 0x1ff)==0x1aa) { + pr_debug ("SD version 2.0 card detected\n"); + + sd_ver20 = 1; + } + + if (sd_ver20) + retries = 50; + else + retries = 10; + + while (retries--) { + resp = mv_mmc_cmd(55, 0, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); + pr_debug("cmd 55 resp : %08x %08x %08x %08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3] ); + + + if (sd_ver20) + resp = mv_mmc_cmd(41, 0x40300000, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); + else + resp = mv_mmc_cmd(41, 0x00300000, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); + + pr_debug("cmd 41 resp : %08x %08x %08x %08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3] ); + + + if (resp && (resp[0] & 0x80000000)) { + pr_debug ("detected SD card\n"); + + is_sd = 1; + break; + } + + udelay(100*1000); + } + + if (retries <= 0 && !is_sd) { + pr_debug ("failed to detect SD card, trying MMC\n"); + + retries = 10; + while (retries--) { + resp = mv_mmc_cmd(1, 0, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); + pr_debug("cmd 01 resp : %08x %08x %08x %08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3] ); + + + if (resp && (resp[0] & 0x80000000)) { + printf ("detected MMC card\n"); + reg = readw(SDIO_REG(SDIO_HOST_CTRL)); + reg &= ~(0x3<<1); + reg |= SDIO_HOST_CTRL_CARD_TYPE_IO_MMC; + writew( reg, SDIO_REG(SDIO_HOST_CTRL)); + break; + } + + udelay(100*1000); + } + } + + if (retries <= 0) { + pr_debug ("detect fails\n"); + + return -ENODEV; + } + + /* try to get card id */ + resp = mv_mmc_cmd(2, 0, 0, SDIO_CMD_RSP_136, SDIO_NOR_CMD_DONE ); + pr_debug("cmd 2 resp : %08x %08x %08x %08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3] ); + + + if (resp == NULL) { + pr_debug ("read cid fails\n"); + + return -ENODEV; + } + + if (is_sd) { + mv_sd_cid_t *cid = (mv_sd_cid_t *) resp; + + memcpy(cidbuf, resp, sizeof(mv_sd_cid_t)); + + sprintf((char *) mmc_dev.vendor, + "Man %02x OEM %c%c \"%c%c%c%c%c\"", + cid->mid, cid->oid_0, cid->oid_1, + cid->pnm_0, cid->pnm_1, cid->pnm_2, cid->pnm_3, cid->pnm_4); + + sprintf((char *) mmc_dev.product, "%d", + (cid->psn_0 << 24) | (cid->psn_1 <<16) | (cid->psn_2 << 8) | (cid->psn_3 << 8)); + + sprintf((char *) mmc_dev.revision, "%d.%d", cid->prv>>4, cid->prv & 0xff); + + } else { + /* TODO configure mmc driver depending on card attributes */ + mv_mmc_cid_t *cid = (mv_mmc_cid_t *) resp; + + memcpy(cidbuf, resp, sizeof(mv_sd_cid_t)); + + + sprintf((char *) mmc_dev.vendor, + "Man %02x%02x%02x Snr %02x%02x%02x", + cid->id[0], cid->id[1], cid->id[2], + cid->sn[0], cid->sn[1], cid->sn[2]); + sprintf((char *) mmc_dev.product, "%s", cid->name); + sprintf((char *) mmc_dev.revision, "%x %x", cid->hwrev, cid->fwrev); + } + + /* fill in device description */ + mmc_dev.if_type = IF_TYPE_MMC; + mmc_dev.part_type = PART_TYPE_DOS; + mmc_dev.dev = 0; + mmc_dev.lun = 0; + mmc_dev.type = 0; + + /* FIXME fill in the correct size (is set to 128MByte) */ + mmc_dev.blksz = MMC_BLOCK_SIZE; + mmc_dev.lba = 0x10000; + + mmc_dev.removable = 0; + mmc_dev.block_read = (unsigned long) mv_mmc_bread; + + /* MMC exists, get CSD too */ + resp = mv_mmc_cmd(MMC_CMD_SET_RCA, 0, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); + if (resp == NULL) { + pr_debug ("set rca fails\n"); + + return -ENODEV; + } + pr_debug("cmd3 resp : 0x%08x 0x%08x 0x%08x 0x%08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3]); + + + if (is_sd) + rca = resp[0] >> 16; + else + rca = 0; + + resp = mv_mmc_cmd(MMC_CMD_SEND_CSD, rca<<16, 0, SDIO_CMD_RSP_136,SDIO_NOR_CMD_DONE ); + pr_debug("cmd 9 resp : %08x %08x %08x %08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3] ); + + if (resp == NULL) { + pr_debug ("read csd fails\n"); + + return -ENODEV; + } + + memcpy(&mv_mmc_csd, (mv_mmc_csd_t *) resp, sizeof(mv_mmc_csd_t)); + rc = 0; + mmc_ready = 1; + + /* FIXME add verbose printout for csd */ + pr_debug ("size = %u\n", mv_mmc_size(&mv_mmc_csd)); + + + resp = mv_mmc_cmd(7, rca<<16, 0, SDIO_CMD_RSP_48BUSY, SDIO_NOR_CMD_DONE); + if (resp == NULL) { + pr_debug ("select card fails\n"); + + return -ENODEV; + } + pr_debug("cmd 7 resp : %08x %08x %08x %08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3] ); + + + if (is_sd) { + resp = mv_mmc_cmd(55, rca<<16, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); + if (resp == NULL) { + pr_debug ("cmd55 fails\n"); + + return -ENODEV; + } + pr_debug("cmd55 resp : 0x%08x 0x%08x 0x%08x 0x%08x\n", + (unsigned int)resp[0], (unsigned int)resp[1], (unsigned int)resp[2], (unsigned int)resp[3]); + + + resp = mv_mmc_cmd(6, (rca<<16) | 0x2 , 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); + if (resp == NULL) { + pr_debug ("cmd55 fails\n"); + + return -ENODEV; + }