Add mvsdio driver to Kirkwood SoC Add SDIO support to SHEEVAPLUG Fix environments for SHEEVAPLUG
Signed-off-by: Gérald Kerma <gera...@gmail.com> --- Changes in v1: * Fix errors from SD/SDHC detect * Minor fixes in boot env arch/arm/cpu/arm926ejs/kirkwood/timer.c | 23 + arch/arm/include/asm/arch-kirkwood/kirkwood.h | 1 + drivers/mmc/Makefile | 1 + drivers/mmc/mv_sdio.c | 747 +++++++++++++++++++++++++ drivers/mmc/mv_sdio.h | 296 ++++++++++ include/configs/sheevaplug.h | 65 ++- 6 files changed, 1124 insertions(+), 9 deletions(-) diff --git a/arch/arm/cpu/arm926ejs/kirkwood/timer.c b/arch/arm/cpu/arm926ejs/kirkwood/timer.c index 2ec6a93..c8f932b 100644 --- a/arch/arm/cpu/arm926ejs/kirkwood/timer.c +++ b/arch/arm/cpu/arm926ejs/kirkwood/timer.c @@ -166,3 +166,26 @@ int timer_init(void) return 0; } + + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +ulong get_tbclk (void) +{ + ulong tbclk; + + tbclk = CONFIG_SYS_HZ; + return tbclk; +} + 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/drivers/mmc/Makefile b/drivers/mmc/Makefile index 8dfd8a3..f1e7db6 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -31,6 +31,7 @@ COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o +COBJS-$(CONFIG_MV_SDIO) += mv_sdio.o COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o diff --git a/drivers/mmc/mv_sdio.c b/drivers/mmc/mv_sdio.c new file mode 100644 index 0000000..aa8df10 --- /dev/null +++ b/drivers/mmc/mv_sdio.c @@ -0,0 +1,747 @@ +/* + * (C) Copyright 2003 + * Kyle Harris, Nexus Technologies, Inc. khar...@nexus-tech.net + * Copyright (C) 2010 Gérald Kerma <gera...@gmail.com> + * + * 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 <config.h> +#include <common.h> +#include <malloc.h> +#include <mmc.h> +#include "mv_sdio.h" +#include <asm/errno.h> +#include <part.h> +#include <asm/io.h> +#include <asm/arch/kirkwood.h> + +#ifdef CONFIG_MMC + +#define DRIVER_NAME "mv-sdio" + +//#define DEBUG + +//static int maxfreq = MVSD_CLOCKRATE_MAX; +//static int nodma; + +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; + +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; + + debug("mvsdio_set_clock: dividor = 0x%x clock=%d\n", + m, clock); + + + SDIO_REG_WRITE32(SDIO_CLK_DIV, m & 0x7ff); + + 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; + + 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 + SDIO_REG_WRITE16(SDIO_NOR_INTR_STATUS, 0xffff); + SDIO_REG_WRITE16(SDIO_ERR_INTR_STATUS, 0xffff); + + start = get_ticks(); + hz = get_tbclk(); + + while((SDIO_REG_READ16(SDIO_PRESENT_STATE0) & CARD_BUSY)) { + curr = get_ticks(); + 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; + } + } + + SDIO_REG_WRITE16(SDIO_ARG_LOW, (ushort)(arg&0xffff) ); + SDIO_REG_WRITE16(SDIO_ARG_HI, (ushort)(arg>>16) ); + SDIO_REG_WRITE16(SDIO_XFER_MODE, xfermode); + if( (cmd == MMC_CMD_READ_BLOCK) || (cmd == 25) ) + { + SDIO_REG_WRITE16(SDIO_CMD, ((cmd << 8) | resptype | 0x3c ) ); + debug("cmd reg : %x\n", SDIO_REG_READ16( SDIO_CMD )) ; + + } + else + { + SDIO_REG_WRITE16(SDIO_CMD, ((cmd << 8) | resptype ) ); + } + + done = SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & waittype; + start = get_ticks(); + + while( done!=waittype) + { + done = SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & waittype; + + if( SDIO_REG_READ16(SDIO_NOR_INTR_STATUS) & 0x8000 ) + { + debug("Error! cmd : %d, err : %04x\n", (unsigned int)cmd, SDIO_REG_READ16(SDIO_ERR_INTR_STATUS) ) ; + + return 0 ; // error happen + } + + curr = get_ticks(); + diff = (long) curr - (long) start; + if (diff > (3*hz)) + { + debug("cmd timeout, status : %04x\n", SDIO_REG_READ16(SDIO_NOR_INTR_STATUS)); + debug("xfer mode : %04x\n", SDIO_REG_READ16(SDIO_XFER_MODE)); + + err = 1 ; + break; + } + } + + for (resp_indx = 0 ; resp_indx < 8; resp_indx++) + response[resp_indx] = SDIO_REG_READ16(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; + debug("MMC resp :"); + for (i=0; i<4; ++i ) { + debug(" %08x", (unsigned int)resp[i]); + } + debug("\n"); + if( err ) + return NULL ; + else + return resp; +} + +/****************************************************/ +static int mv_mmc_block_read(uchar *dst, ulong src, ulong len) +/****************************************************/ +{ + ulong *resp; + //ushort argh, argl; + //ulong status; + + if (len == 0) { + return 0; + } + + if (is_sdhc) { + /* SDHC: use block address */ + src >>= 9; + } + + debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong)dst, src, (int)len); + + // prepare for dma transfer + SDIO_REG_WRITE16(SDIO_SYS_ADDR_LOW,((ulong)(dst))&0xffff); + SDIO_REG_WRITE16(SDIO_SYS_ADDR_HI,(((ulong)dst)>>16)&0xffff); + SDIO_REG_WRITE16(SDIO_BLK_SIZE,len); + SDIO_REG_WRITE16(SDIO_BLK_COUNT,1); + + /* send read command */ + resp = mv_mmc_cmd(MMC_CMD_READ_BLOCK, src, 0x10 , // 0x12, + SDIO_CMD_RSP_48, SDIO_NOR_XFER_DONE); + if (!resp) { + debug("mv_mmc_block_read: mmc read block cmd fails\n"); + return -EIO; + } + + return 0; +} + +/****************************************************/ +static int mv_mmc_block_write(ulong dst, uchar *src, int len) +/****************************************************/ +{ + return -1 ; +} + +/****************************************************/ +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 */ + 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; + 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; + } + 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) { + 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; + } + //printf("mem copy from %x to %x, size %d\n", (ulong)mmc_buf, (ulong)dst, mmc_block_size ); + memcpy(dst, mmc_buf, mmc_block_size); + } + 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) { + 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 int mv_mmc_write(uchar *src, ulong 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 = dst + size; + part_start = ~mmc_block_address & dst; + part_end = ~mmc_block_address & end; + aligned_start = mmc_block_address & dst; + aligned_end = mmc_block_address & end; + + /* all block aligned accesses */ + 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; + debug("ps src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", + (ulong)src, 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(mmc_buf+part_start, src, part_len); + if ((mv_mmc_block_write(aligned_start, mmc_buf, mmc_block_size)) < 0) { + return -1; + } + dst += part_len; + src += part_len; + } + 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 (; dst < aligned_end; src += mmc_block_size, dst += mmc_block_size) { + 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_write(dst, (uchar *)src, mmc_block_size)) < 0) { + return -1; + } + } + 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 && dst < end) { + 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(mmc_buf, src, part_end); + if ((mv_mmc_block_write(aligned_end, mmc_buf, mmc_block_size)) < 0) { + return -1; + } + } + 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 + SDIO_REG_WRITE16(SDIO_HOST_CTRL, + 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_WRITE16(SDIO_CLK_CTRL, 0); + + //enable status + SDIO_REG_WRITE16(SDIO_NOR_STATUS_EN, 0xffff); + SDIO_REG_WRITE16(SDIO_ERR_STATUS_EN, 0xffff); + + //disable interrupts + SDIO_REG_WRITE16(SDIO_NOR_INTR_EN, 0); + SDIO_REG_WRITE16(SDIO_ERR_INTR_EN, 0); + + SDIO_REG_WRITE16(SDIO_SW_RESET,0x100); + udelay(10000); + + mv_mmc_csd.c_size = 0; + + /* reset */ + retries = 10; + //mv_mmc_cmd(ulong cmd, ulong arg, ushort xfermode, ushort resptype, ushort waittype); + resp = mv_mmc_cmd(0, 0, 0, SDIO_CMD_RSP_NONE, SDIO_NOR_CMD_DONE ); + 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] ); + + + debug ("trying to detect SD card version\n"); + + resp = mv_mmc_cmd(8, 0x000001aa, 0, SDIO_CMD_RSP_48, SDIO_NOR_CMD_DONE ); + 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) { + 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 ); + 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 ); + + 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)) { + debug ("detected SD card\n"); + + is_sd = 1; + break; + } + + udelay(100*1000); + } + + if (retries <= 0 && !is_sd) { + 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 ); + 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 = SDIO_REG_READ16(SDIO_HOST_CTRL); + reg &= ~(0x3<<1); + reg |= SDIO_HOST_CTRL_CARD_TYPE_IO_MMC; + SDIO_REG_WRITE16(SDIO_HOST_CTRL, reg); + break; + } + + udelay(100*1000); + } + } + + if (retries <= 0) { + 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 ); + 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) { + 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 = 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) { + debug ("set rca fails\n"); + + return -ENODEV; + } + 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 ); + 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) { + 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 */ + 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) { + debug ("select card fails\n"); + + return -ENODEV; + } + 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) { + debug ("cmd55 fails\n"); + + return -ENODEV; + } + 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) { + debug ("cmd55 fails\n"); + + return -ENODEV; + } + 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; + 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) { + 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..bb7d39c --- /dev/null +++ b/drivers/mmc/mv_sdio.h @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2008 Marvell Semiconductors, All Rights Reserved. + * Copyright (C) 2010 Gérald Kerma <gerald.ke...@gk2.net> + * + * 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 SDIO_REG_WRITE32(offset,value) writel(value,SDIO_REG(offset)) +#define SDIO_REG_READ32(offset) readl(SDIO_REG(offset)) + +#define SDIO_REG_WRITE16(offset,value) writew(value,SDIO_REG(offset)) +#define SDIO_REG_READ16(offset) readw(SDIO_REG(offset)) + +#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_LOW 0x000 +#define SDIO_SYS_ADDR_HI 0x004 +#define SDIO_BLK_SIZE 0x008 +#define SDIO_BLK_COUNT 0x00c +#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/include/configs/sheevaplug.h b/include/configs/sheevaplug.h index c5de86e..7b7e5e8 100644 --- a/include/configs/sheevaplug.h +++ b/include/configs/sheevaplug.h @@ -25,10 +25,12 @@ #ifndef _CONFIG_SHEEVAPLUG_H #define _CONFIG_SHEEVAPLUG_H +//#define DEBUG 1 + /* * Version number information */ -#define CONFIG_IDENT_STRING "\nMarvell-Sheevaplug" +#define CONFIG_IDENT_STRING "\nMarvell-Sheevaplug - eSATA - SD/MMC" /* * High Level Configuration Options (easy to change) @@ -94,7 +96,9 @@ #define CONFIG_CMD_AUTOSCRIPT #define CONFIG_CMD_DHCP #define CONFIG_CMD_ENV #define CONFIG_CMD_MII +#define CONFIG_CMD_MMC #define CONFIG_CMD_NAND #define CONFIG_CMD_PING #define CONFIG_CMD_USB @@ -132,16 +136,23 @@ */ #define CONFIG_BOOTCOMMAND "${x_bootcmd_kernel}; " \ "setenv bootargs ${x_bootargs} ${x_bootargs_root}; " \ - "${x_bootcmd_usb}; bootm 0x6400000;" + "${x_bootcmd_usb}; ${x_bootcmd_sata}; bootm 0x6400000;" + +#define CONFIG_MTDPARTS "orion_nand:" \ + "512k(uboot),4...@1m(kernel),5...@5m(rootfs) rw\0" -#define CONFIG_MTDPARTS "orion_nand:512k(uboot)," \ - "3...@1m(kernel),1...@4m(psm),1...@5m(rootfs) rw\0" -#define CONFIG_EXTRA_ENV_SETTINGS "x_bootargs=console" \ - "=ttyS0,115200 mtdparts="CONFIG_MTDPARTS \ - "x_bootcmd_kernel=nand read 0x6400000 0x100000 0x300000\0" \ - "x_bootcmd_usb=usb start\0" \ - "x_bootargs_root=root=/dev/mtdblock3 rw rootfstype=jffs2\0" +#define CONFIG_EXTRA_ENV_SETTINGS "x_bootargs=console" \ + "=ttyS0,115200 mtdparts="CONFIG_MTDPARTS \ + "x_bootcmd_kernel=nand read 0x6400000 0x100000 0x400000\0" \ + "x_bootcmd_usb=usb start;\0" \ + "x_bootcmd_sata=ide reset;\0" \ + "x_bootargs_root=ubi.mtd=2 root=ubi0:rootfs rootfstype=ubifs\0" /* * Size of malloc() pool @@ -183,6 +194,42 @@ #endif /* CONFIG_CMD_NET */ /* + * SDIO/MMC Card Configuration + */ +#ifdef CONFIG_CMD_MMC +#define CONFIG_MMC +#define CONFIG_MV_SDIO +#endif /* CONFIG_CMD_MMC */ + +/* * USB/EHCI */ #ifdef CONFIG_CMD_USB _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot