From patchwork Sun Nov 21 11:07:56 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: 72438 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 675BEB7160 for ; Sun, 21 Nov 2010 22:08:10 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id C823928234; Sun, 21 Nov 2010 12:08:08 +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 RbohrE9ZN9GT; Sun, 21 Nov 2010 12:08:08 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 5FB5228146; Sun, 21 Nov 2010 12:08:06 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 165522824E for ; Sun, 21 Nov 2010 12:08:04 +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 qaKPX9gdpMIh for ; Sun, 21 Nov 2010 12:08:02 +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-wy0-f172.google.com (mail-wy0-f172.google.com [74.125.82.172]) by theia.denx.de (Postfix) with ESMTP id 0BB552824C for ; Sun, 21 Nov 2010 12:08:00 +0100 (CET) Received: by wyb29 with SMTP id 29so6272062wyb.3 for ; Sun, 21 Nov 2010 03:07:59 -0800 (PST) Received: by 10.216.20.141 with SMTP id p13mr6502072wep.102.1290337678449; Sun, 21 Nov 2010 03:07:58 -0800 (PST) Received: from [192.168.0.152] (louche.kerma.com.fr [88.178.80.34]) by mx.google.com with ESMTPS id a2sm1709995wer.41.2010.11.21.03.07.56 (version=SSLv3 cipher=RC4-MD5); Sun, 21 Nov 2010 03:07:57 -0800 (PST) Message-ID: <4CE8FD8C.2090905@gmail.com> Date: Sun, 21 Nov 2010 12:07:56 +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: "u-boot@lists.denx.de" References: <4CE8FD0D.3020405@gmail.com> In-Reply-To: <4CE8FD0D.3020405@gmail.com> X-Enigmail-Version: 1.1.1 OpenPGP: id=DED5B136 Cc: Prabhanjan Sarnaik , Ashish Karkare Subject: [U-Boot] [PATCH v3 2/3] 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 Add SDIO legacy driver for Marvell SoCs (Kirkwood) Signed-off-by: Gérald Kerma --- v3: * GNU Software License compliance * Patch more readable v2: * Code cleaning v1: * Fix errors from SD/SDHC detect --- drivers/mmc/Makefile | 1 + drivers/mmc/mv_sdio.c | 675 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/mv_sdio.h | 313 +++++++++++++++++++++++ 3 files changed, 989 insertions(+), 0 deletions(-) U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot 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..35969d3 --- /dev/null +++ b/drivers/mmc/mv_sdio.c @@ -0,0 +1,675 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: Gérald Kerma + * + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_KIRKWOOD +#include +#endif +#include "mv_sdio.h" + +#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 mv_sdio_t *mvsd = (mv_sdio_t *)mmc->priv; +static mv_sdio_t *mvsd = (mv_sdio_t *)MV_SDIO_BASE; + +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, &mvsd->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]; + + 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, &mvsd->NOR_INTR_STATUS); + writew(0xffff, &mvsd->ERR_INTR_STATUS); + + start = get_timer(0); + hz = CONFIG_SYS_HZ; + + while((readw(&mvsd->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), &mvsd->ARG_LOW); + writew((ushort)(arg>>16), &mvsd->ARG_HI); + writew(xfermode, &mvsd->XFER_MODE); + if( (cmd == MMC_CMD_READ_BLOCK) || (cmd == 25) ) + { + writew(((cmd << 8) | resptype | 0x3c ) , &mvsd->CMD); + pr_debug("cmd reg : %x\n", readw(&mvsd->CMD)) ; + + } + else + { + writew(((cmd << 8) | resptype ), &mvsd->CMD); + } + + done = readw(&mvsd->NOR_INTR_STATUS) & waittype; + start = get_timer(0); + + while( done!=waittype) + { + done = readw(&mvsd->NOR_INTR_STATUS) & waittype; + + if( readw(&mvsd->NOR_INTR_STATUS) & 0x8000 ) + { + pr_debug("Error! cmd : %d, err : %04x\n", (unsigned int)cmd, readw(&mvsd->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(&mvsd->NOR_INTR_STATUS)); + pr_debug("xfer mode : %04x\n", readw(&mvsd->XFER_MODE)); + + err = 1 ; + break; + } + } + + response[0] = readw(&mvsd->RSP0); + response[1] = readw(&mvsd->RSP1); + response[2] = readw(&mvsd->RSP2); + response[3] = readw(&mvsd->RSP3); + response[4] = readw(&mvsd->RSP4); + response[5] = readw(&mvsd->RSP5); + response[6] = readw(&mvsd->RSP6); + response[7] = readw(&mvsd->RSP7); + + 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,&mvsd->SYS_ADDR_LOW); + writew(((ulong)(dst)>>16)&0xffff,&mvsd->SYS_ADDR_HI); + writew(len,&mvsd->BLK_SIZE); + writew(1,&mvsd->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 , + &mvsd->HOST_CTRL); + + writew( 0, &mvsd->CLK_CTRL); + + /* enable status */ + writew( 0xffff, &mvsd->NOR_STATUS_EN); + writew( 0xffff, &mvsd->ERR_STATUS_EN); + + /* disable interrupts */ + writew( 0, &mvsd->NOR_INTR_EN); + writew( 0, &mvsd->ERR_INTR_EN); + + writew( 0x100, &mvsd->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(&mvsd->HOST_CTRL); + reg &= ~(0x3<<1); + reg |= SDIO_HOST_CTRL_CARD_TYPE_IO_MMC; + writew( reg, &mvsd->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; + } + 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 */ diff --git a/drivers/mmc/mv_sdio.h b/drivers/mmc/mv_sdio.h new file mode 100644 index 0000000..3383c3d --- /dev/null +++ b/drivers/mmc/mv_sdio.h @@ -0,0 +1,313 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor + * Written-by: 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef _MVSDIO_INCLUDE +#define _MVSDIO_INCLUDE + +//#define SDIO_REG(x) (MV_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 + */ +#ifndef __ASSEMBLY__ + +/* + * Structure for struct SoC access. + * Names starting with '_' are fillers. + */ +typedef struct mv_sdio { + /* reg Offset */ + u32 SYS_ADDR_LOW; /* 0x00 */ + u32 SYS_ADDR_HI; /* 0x04 */ + u32 BLK_SIZE; /* 0x08 */ + u32 BLK_COUNT; /* 0x0c */ + u32 ARG_LOW; /* 0x10 */ + u32 ARG_HI; /* 0x14 */ + u32 XFER_MODE; /* 0x18 */ + u32 CMD; /* 0x1c */ + u32 RSP0; /* 0x20 */ + u32 RSP1; /* 0x24 */ + u32 RSP2; /* 0x28 */ + u32 RSP3; /* 0x2c */ + u32 RSP4; /* 0x30 */ + u32 RSP5; /* 0x34 */ + u32 RSP6; /* 0x38 */ + u32 RSP7; /* 0x3c */ + u32 BUF_DATA_PORT; /* 0x40 */ + u32 RSVED; /* 0x44 */ + u32 PRESENT_STATE0; /* 0x48 */ + u32 PRESENT_STATE1; /* 0x4c */ + u32 HOST_CTRL; /* 0x50 */ + u32 BLK_GAP_CTRL; /* 0x54 */ + u32 CLK_CTRL; /* 0x58 */ + u32 SW_RESET; /* 0x5c */ + u32 NOR_INTR_STATUS; /* 0x60 */ + u32 ERR_INTR_STATUS; /* 0x64 */ + u32 NOR_STATUS_EN; /* 0x68 */ + u32 ERR_STATUS_EN; /* 0x6c */ + u32 NOR_INTR_EN; /* 0x70 */ + u32 ERR_INTR_EN; /* 0x74 */ + u32 AUTOCMD12_ERR_STATUS; /* 0x78 */ + u32 CURR_BYTE_LEFT; /* 0x7c */ + u32 CURR_BLK_LEFT; /* 0x80 */ + u32 AUTOCMD12_ARG_LOW; /* 0x84 */ + u32 AUTOCMD12_ARG_HI; /* 0x88 */ + u32 AUTOCMD12_INDEX; /* 0x8c */ + u32 AUTO_RSP0; /* 0x90 */ + u32 AUTO_RSP1; /* 0x94 */ + u32 AUTO_RSP2; /* 0x98 */ + u32 _9c; /* 0x9c */ + u32 _a0[0x78]; /* 0xa0 */ + u32 CLK_DIV; /* 0x128 */ + +} mv_sdio_t; + +#endif /* __ASSEMBLY__ */ + +/* + * 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 */ -- 1.7.1 _______________________________________________ U-Boot mailing list