diff options
Diffstat (limited to 'roms/u-boot-sam460ex/drivers/mmc')
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mmc/Makefile | 52 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mmc/atmel_mci.c | 533 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mmc/atmel_mci.h | 201 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mmc/bfin_sdh.c | 257 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mmc/fsl_esdhc.c | 514 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mmc/mmc.c | 945 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mmc/mxcmmc.c | 522 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mmc/omap3_mmc.c | 534 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mmc/pxa_mmc.c | 646 | ||||
-rw-r--r-- | roms/u-boot-sam460ex/drivers/mmc/pxa_mmc.h | 138 |
10 files changed, 4342 insertions, 0 deletions
diff --git a/roms/u-boot-sam460ex/drivers/mmc/Makefile b/roms/u-boot-sam460ex/drivers/mmc/Makefile new file mode 100644 index 000000000..6fa04b84f --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mmc/Makefile @@ -0,0 +1,52 @@ +# +# (C) Copyright 2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# 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 $(TOPDIR)/config.mk + +LIB := $(obj)libmmc.a + +COBJS-$(CONFIG_GENERIC_MMC) += mmc.o +COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o +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_PXA_MMC) += pxa_mmc.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/roms/u-boot-sam460ex/drivers/mmc/atmel_mci.c b/roms/u-boot-sam460ex/drivers/mmc/atmel_mci.c new file mode 100644 index 000000000..3946ffeeb --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mmc/atmel_mci.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2004-2006 Atmel Corporation + * + * 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 <common.h> + +#include <part.h> +#include <mmc.h> + +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/byteorder.h> +#include <asm/arch/clk.h> +#include <asm/arch/memory-map.h> + +#include "atmel_mci.h" + +#ifdef DEBUG +#define pr_debug(fmt, args...) printf(fmt, ##args) +#else +#define pr_debug(...) do { } while(0) +#endif + +#ifndef CONFIG_SYS_MMC_CLK_OD +#define CONFIG_SYS_MMC_CLK_OD 150000 +#endif + +#ifndef CONFIG_SYS_MMC_CLK_PP +#define CONFIG_SYS_MMC_CLK_PP 5000000 +#endif + +#ifndef CONFIG_SYS_MMC_OP_COND +#define CONFIG_SYS_MMC_OP_COND 0x00100000 +#endif + +#define MMC_DEFAULT_BLKLEN 512 +#define MMC_DEFAULT_RCA 1 + +static unsigned int mmc_rca; +static int mmc_card_is_sd; +static block_dev_desc_t mmc_blkdev; + +block_dev_desc_t *mmc_get_dev(int dev) +{ + return &mmc_blkdev; +} + +static void mci_set_mode(unsigned long hz, unsigned long blklen) +{ + unsigned long bus_hz; + unsigned long clkdiv; + + bus_hz = get_mci_clk_rate(); + clkdiv = (bus_hz / hz) / 2 - 1; + + pr_debug("mmc: setting clock %lu Hz, block size %lu\n", + hz, blklen); + + if (clkdiv & ~255UL) { + clkdiv = 255; + printf("mmc: clock %lu too low; setting CLKDIV to 255\n", + hz); + } + + blklen &= 0xfffc; + mmci_writel(MR, (MMCI_BF(CLKDIV, clkdiv) + | MMCI_BF(BLKLEN, blklen) + | MMCI_BIT(RDPROOF) + | MMCI_BIT(WRPROOF))); +} + +#define RESP_NO_CRC 1 +#define R1 MMCI_BF(RSPTYP, 1) +#define R2 MMCI_BF(RSPTYP, 2) +#define R3 (R1 | RESP_NO_CRC) +#define R6 R1 +#define NID MMCI_BF(MAXLAT, 0) +#define NCR MMCI_BF(MAXLAT, 1) +#define TRCMD_START MMCI_BF(TRCMD, 1) +#define TRDIR_READ MMCI_BF(TRDIR, 1) +#define TRTYP_BLOCK MMCI_BF(TRTYP, 0) +#define INIT_CMD MMCI_BF(SPCMD, 1) +#define OPEN_DRAIN MMCI_BF(OPDCMD, 1) + +#define ERROR_FLAGS (MMCI_BIT(DTOE) \ + | MMCI_BIT(RDIRE) \ + | MMCI_BIT(RENDE) \ + | MMCI_BIT(RINDE) \ + | MMCI_BIT(RTOE)) + +static int +mmc_cmd(unsigned long cmd, unsigned long arg, + void *resp, unsigned long flags) +{ + unsigned long *response = resp; + int i, response_words = 0; + unsigned long error_flags; + u32 status; + + pr_debug("mmc: CMD%lu 0x%lx (flags 0x%lx)\n", + cmd, arg, flags); + + error_flags = ERROR_FLAGS; + if (!(flags & RESP_NO_CRC)) + error_flags |= MMCI_BIT(RCRCE); + + flags &= ~MMCI_BF(CMDNB, ~0UL); + + if (MMCI_BFEXT(RSPTYP, flags) == MMCI_RSPTYP_48_BIT_RESP) + response_words = 1; + else if (MMCI_BFEXT(RSPTYP, flags) == MMCI_RSPTYP_136_BIT_RESP) + response_words = 4; + + mmci_writel(ARGR, arg); + mmci_writel(CMDR, cmd | flags); + do { + udelay(40); + status = mmci_readl(SR); + } while (!(status & MMCI_BIT(CMDRDY))); + + pr_debug("mmc: status 0x%08x\n", status); + + if (status & error_flags) { + printf("mmc: command %lu failed (status: 0x%08x)\n", + cmd, status); + return -EIO; + } + + if (response_words) + pr_debug("mmc: response:"); + + for (i = 0; i < response_words; i++) { + response[i] = mmci_readl(RSPR); + pr_debug(" %08lx", response[i]); + } + pr_debug("\n"); + + return 0; +} + +static int mmc_acmd(unsigned long cmd, unsigned long arg, + void *resp, unsigned long flags) +{ + unsigned long aresp[4]; + int ret; + + /* + * Seems like the APP_CMD part of an ACMD has 64 cycles max + * latency even though the ACMD part doesn't. This isn't + * entirely clear in the SD Card spec, but some cards refuse + * to work if we attempt to use 5 cycles max latency here... + */ + ret = mmc_cmd(MMC_CMD_APP_CMD, 0, aresp, + R1 | NCR | (flags & OPEN_DRAIN)); + if (ret) + return ret; + if ((aresp[0] & (R1_ILLEGAL_COMMAND | R1_APP_CMD)) != R1_APP_CMD) + return -ENODEV; + + ret = mmc_cmd(cmd, arg, resp, flags); + return ret; +} + +static unsigned long +mmc_bread(int dev, unsigned long start, lbaint_t blkcnt, + void *buffer) +{ + int ret, i = 0; + unsigned long resp[4]; + unsigned long card_status, data; + unsigned long wordcount; + u32 *p = buffer; + u32 status; + + if (blkcnt == 0) + return 0; + + pr_debug("mmc_bread: dev %d, start %lx, blkcnt %lx\n", + dev, start, blkcnt); + + /* Put the device into Transfer state */ + ret = mmc_cmd(MMC_CMD_SELECT_CARD, mmc_rca << 16, resp, R1 | NCR); + if (ret) goto out; + + /* Set block length */ + ret = mmc_cmd(MMC_CMD_SET_BLOCKLEN, mmc_blkdev.blksz, resp, R1 | NCR); + if (ret) goto out; + + pr_debug("MCI_DTOR = %08lx\n", mmci_readl(DTOR)); + + for (i = 0; i < blkcnt; i++, start++) { + ret = mmc_cmd(MMC_CMD_READ_SINGLE_BLOCK, + start * mmc_blkdev.blksz, resp, + (R1 | NCR | TRCMD_START | TRDIR_READ + | TRTYP_BLOCK)); + if (ret) goto out; + + ret = -EIO; + wordcount = 0; + do { + do { + status = mmci_readl(SR); + if (status & (ERROR_FLAGS | MMCI_BIT(OVRE))) + goto read_error; + } while (!(status & MMCI_BIT(RXRDY))); + + if (status & MMCI_BIT(RXRDY)) { + data = mmci_readl(RDR); + /* pr_debug("%x\n", data); */ + *p++ = data; + wordcount++; + } + } while(wordcount < (mmc_blkdev.blksz / 4)); + + pr_debug("mmc: read %u words, waiting for BLKE\n", wordcount); + + do { + status = mmci_readl(SR); + } while (!(status & MMCI_BIT(BLKE))); + + putc('.'); + } + +out: + /* Put the device back into Standby state */ + mmc_cmd(MMC_CMD_SELECT_CARD, 0, resp, NCR); + return i; + +read_error: + mmc_cmd(MMC_CMD_SEND_STATUS, mmc_rca << 16, &card_status, R1 | NCR); + printf("mmc: bread failed, status = %08x, card status = %08lx\n", + status, card_status); + goto out; +} + +static void mmc_parse_cid(struct mmc_cid *cid, unsigned long *resp) +{ + cid->mid = resp[0] >> 24; + cid->oid = (resp[0] >> 8) & 0xffff; + cid->pnm[0] = resp[0]; + cid->pnm[1] = resp[1] >> 24; + cid->pnm[2] = resp[1] >> 16; + cid->pnm[3] = resp[1] >> 8; + cid->pnm[4] = resp[1]; + cid->pnm[5] = resp[2] >> 24; + cid->pnm[6] = 0; + cid->prv = resp[2] >> 16; + cid->psn = (resp[2] << 16) | (resp[3] >> 16); + cid->mdt = resp[3] >> 8; +} + +static void sd_parse_cid(struct mmc_cid *cid, unsigned long *resp) +{ + cid->mid = resp[0] >> 24; + cid->oid = (resp[0] >> 8) & 0xffff; + cid->pnm[0] = resp[0]; + cid->pnm[1] = resp[1] >> 24; + cid->pnm[2] = resp[1] >> 16; + cid->pnm[3] = resp[1] >> 8; + cid->pnm[4] = resp[1]; + cid->pnm[5] = 0; + cid->pnm[6] = 0; + cid->prv = resp[2] >> 24; + cid->psn = (resp[2] << 8) | (resp[3] >> 24); + cid->mdt = (resp[3] >> 8) & 0x0fff; +} + +static void mmc_dump_cid(const struct mmc_cid *cid) +{ + printf("Manufacturer ID: %02X\n", cid->mid); + printf("OEM/Application ID: %04X\n", cid->oid); + printf("Product name: %s\n", cid->pnm); + printf("Product Revision: %u.%u\n", + cid->prv >> 4, cid->prv & 0x0f); + printf("Product Serial Number: %lu\n", cid->psn); + printf("Manufacturing Date: %02u/%02u\n", + cid->mdt >> 4, cid->mdt & 0x0f); +} + +static void mmc_dump_csd(const struct mmc_csd *csd) +{ + unsigned long *csd_raw = (unsigned long *)csd; + printf("CSD data: %08lx %08lx %08lx %08lx\n", + csd_raw[0], csd_raw[1], csd_raw[2], csd_raw[3]); + printf("CSD structure version: 1.%u\n", csd->csd_structure); + printf("MMC System Spec version: %u\n", csd->spec_vers); + printf("Card command classes: %03x\n", csd->ccc); + printf("Read block length: %u\n", 1 << csd->read_bl_len); + if (csd->read_bl_partial) + puts("Supports partial reads\n"); + else + puts("Does not support partial reads\n"); + printf("Write block length: %u\n", 1 << csd->write_bl_len); + if (csd->write_bl_partial) + puts("Supports partial writes\n"); + else + puts("Does not support partial writes\n"); + if (csd->wp_grp_enable) + printf("Supports group WP: %u\n", csd->wp_grp_size + 1); + else + puts("Does not support group WP\n"); + printf("Card capacity: %u bytes\n", + (csd->c_size + 1) * (1 << (csd->c_size_mult + 2)) * + (1 << csd->read_bl_len)); + printf("File format: %u/%u\n", + csd->file_format_grp, csd->file_format); + puts("Write protection: "); + if (csd->perm_write_protect) + puts(" permanent"); + if (csd->tmp_write_protect) + puts(" temporary"); + putc('\n'); +} + +static int mmc_idle_cards(void) +{ + int ret; + + /* Reset and initialize all cards */ + ret = mmc_cmd(MMC_CMD_GO_IDLE_STATE, 0, NULL, 0); + if (ret) + return ret; + + /* Keep the bus idle for 74 clock cycles */ + return mmc_cmd(0, 0, NULL, INIT_CMD); +} + +static int sd_init_card(struct mmc_cid *cid, int verbose) +{ + unsigned long resp[4]; + int i, ret = 0; + + mmc_idle_cards(); + for (i = 0; i < 1000; i++) { + ret = mmc_acmd(SD_CMD_APP_SEND_OP_COND, CONFIG_SYS_MMC_OP_COND, + resp, R3 | NID); + if (ret || (resp[0] & 0x80000000)) + break; + ret = -ETIMEDOUT; + } + + if (ret) + return ret; + + ret = mmc_cmd(MMC_CMD_ALL_SEND_CID, 0, resp, R2 | NID); + if (ret) + return ret; + sd_parse_cid(cid, resp); + if (verbose) + mmc_dump_cid(cid); + + /* Get RCA of the card that responded */ + ret = mmc_cmd(SD_CMD_SEND_RELATIVE_ADDR, 0, resp, R6 | NCR); + if (ret) + return ret; + + mmc_rca = resp[0] >> 16; + if (verbose) + printf("SD Card detected (RCA %u)\n", mmc_rca); + mmc_card_is_sd = 1; + return 0; +} + +static int mmc_init_card(struct mmc_cid *cid, int verbose) +{ + unsigned long resp[4]; + int i, ret = 0; + + mmc_idle_cards(); + for (i = 0; i < 1000; i++) { + ret = mmc_cmd(MMC_CMD_SEND_OP_COND, CONFIG_SYS_MMC_OP_COND, resp, + R3 | NID | OPEN_DRAIN); + if (ret || (resp[0] & 0x80000000)) + break; + ret = -ETIMEDOUT; + } + + if (ret) + return ret; + + /* Get CID of all cards. FIXME: Support more than one card */ + ret = mmc_cmd(MMC_CMD_ALL_SEND_CID, 0, resp, R2 | NID | OPEN_DRAIN); + if (ret) + return ret; + mmc_parse_cid(cid, resp); + if (verbose) + mmc_dump_cid(cid); + + /* Set Relative Address of the card that responded */ + ret = mmc_cmd(MMC_CMD_SET_RELATIVE_ADDR, mmc_rca << 16, resp, + R1 | NCR | OPEN_DRAIN); + return ret; +} + +static void mci_set_data_timeout(struct mmc_csd *csd) +{ + static const unsigned int dtomul_to_shift[] = { + 0, 4, 7, 8, 10, 12, 16, 20, + }; + static const unsigned int taac_exp[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, + }; + static const unsigned int taac_mant[] = { + 0, 10, 12, 13, 15, 60, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, + }; + unsigned int timeout_ns, timeout_clks; + unsigned int e, m; + unsigned int dtocyc, dtomul; + unsigned int shift; + u32 dtor; + + e = csd->taac & 0x07; + m = (csd->taac >> 3) & 0x0f; + + timeout_ns = (taac_exp[e] * taac_mant[m] + 9) / 10; + timeout_clks = csd->nsac * 100; + + timeout_clks += (((timeout_ns + 9) / 10) + * ((CONFIG_SYS_MMC_CLK_PP + 99999) / 100000) + 9999) / 10000; + if (!mmc_card_is_sd) + timeout_clks *= 10; + else + timeout_clks *= 100; + + dtocyc = timeout_clks; + dtomul = 0; + shift = 0; + while (dtocyc > 15 && dtomul < 8) { + dtomul++; + shift = dtomul_to_shift[dtomul]; + dtocyc = (timeout_clks + (1 << shift) - 1) >> shift; + } + + if (dtomul >= 8) { + dtomul = 7; + dtocyc = 15; + puts("Warning: Using maximum data timeout\n"); + } + + dtor = (MMCI_BF(DTOMUL, dtomul) + | MMCI_BF(DTOCYC, dtocyc)); + mmci_writel(DTOR, dtor); + + printf("mmc: Using %u cycles data timeout (DTOR=0x%x)\n", + dtocyc << shift, dtor); +} + +int mmc_legacy_init(int verbose) +{ + struct mmc_cid cid; + struct mmc_csd csd; + unsigned int max_blksz; + int ret; + + /* Initialize controller */ + mmci_writel(CR, MMCI_BIT(SWRST)); + mmci_writel(CR, MMCI_BIT(MCIEN)); + mmci_writel(DTOR, 0x5f); + mmci_writel(IDR, ~0UL); + mci_set_mode(CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN); + + mmc_card_is_sd = 0; + + ret = sd_init_card(&cid, verbose); + if (ret) { + mmc_rca = MMC_DEFAULT_RCA; + ret = mmc_init_card(&cid, verbose); + } + if (ret) + return ret; + + /* Get CSD from the card */ + ret = mmc_cmd(MMC_CMD_SEND_CSD, mmc_rca << 16, &csd, R2 | NCR); + if (ret) + return ret; + if (verbose) + mmc_dump_csd(&csd); + + mci_set_data_timeout(&csd); + + /* Initialize the blockdev structure */ + mmc_blkdev.if_type = IF_TYPE_MMC; + mmc_blkdev.part_type = PART_TYPE_DOS; + mmc_blkdev.block_read = mmc_bread; + sprintf((char *)mmc_blkdev.vendor, + "Man %02x%04x Snr %08lx", + cid.mid, cid.oid, cid.psn); + strncpy((char *)mmc_blkdev.product, cid.pnm, + sizeof(mmc_blkdev.product)); + sprintf((char *)mmc_blkdev.revision, "%x %x", + cid.prv >> 4, cid.prv & 0x0f); + + /* + * If we can't use 512 byte blocks, refuse to deal with the + * card. Tons of code elsewhere seems to depend on this. + */ + max_blksz = 1 << csd.read_bl_len; + if (max_blksz < 512 || (max_blksz > 512 && !csd.read_bl_partial)) { + printf("Card does not support 512 byte reads, aborting.\n"); + return -ENODEV; + } + mmc_blkdev.blksz = 512; + mmc_blkdev.lba = (csd.c_size + 1) * (1 << (csd.c_size_mult + 2)); + + mci_set_mode(CONFIG_SYS_MMC_CLK_PP, mmc_blkdev.blksz); + +#if 0 + if (fat_register_device(&mmc_blkdev, 1)) + printf("Could not register MMC fat device\n"); +#else + init_part(&mmc_blkdev); +#endif + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mmc/atmel_mci.h b/roms/u-boot-sam460ex/drivers/mmc/atmel_mci.h new file mode 100644 index 000000000..5b4f5c99b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mmc/atmel_mci.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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 + */ +#ifndef __CPU_AT32AP_ATMEL_MCI_H__ +#define __CPU_AT32AP_ATMEL_MCI_H__ + +/* Atmel MultiMedia Card Interface (MCI) registers */ +#define MMCI_CR 0x0000 +#define MMCI_MR 0x0004 +#define MMCI_DTOR 0x0008 +#define MMCI_SDCR 0x000c +#define MMCI_ARGR 0x0010 +#define MMCI_CMDR 0x0014 +#define MMCI_RSPR 0x0020 +#define MMCI_RSPR1 0x0024 +#define MMCI_RSPR2 0x0028 +#define MMCI_RSPR3 0x002c +#define MMCI_RDR 0x0030 +#define MMCI_TDR 0x0034 +#define MMCI_SR 0x0040 +#define MMCI_IER 0x0044 +#define MMCI_IDR 0x0048 +#define MMCI_IMR 0x004c + +/* Bitfields in CR */ +#define MMCI_MCIEN_OFFSET 0 +#define MMCI_MCIEN_SIZE 1 +#define MMCI_MCIDIS_OFFSET 1 +#define MMCI_MCIDIS_SIZE 1 +#define MMCI_PWSEN_OFFSET 2 +#define MMCI_PWSEN_SIZE 1 +#define MMCI_PWSDIS_OFFSET 3 +#define MMCI_PWSDIS_SIZE 1 +#define MMCI_SWRST_OFFSET 7 +#define MMCI_SWRST_SIZE 1 + +/* Bitfields in MR */ +#define MMCI_CLKDIV_OFFSET 0 +#define MMCI_CLKDIV_SIZE 8 +#define MMCI_PWSDIV_OFFSET 8 +#define MMCI_PWSDIV_SIZE 3 +#define MMCI_RDPROOF_OFFSET 11 +#define MMCI_RDPROOF_SIZE 1 +#define MMCI_WRPROOF_OFFSET 12 +#define MMCI_WRPROOF_SIZE 1 +#define MMCI_PDCPADV_OFFSET 14 +#define MMCI_PDCPADV_SIZE 1 +#define MMCI_PDCMODE_OFFSET 15 +#define MMCI_PDCMODE_SIZE 1 +#define MMCI_BLKLEN_OFFSET 16 +#define MMCI_BLKLEN_SIZE 16 + +/* Bitfields in DTOR */ +#define MMCI_DTOCYC_OFFSET 0 +#define MMCI_DTOCYC_SIZE 4 +#define MMCI_DTOMUL_OFFSET 4 +#define MMCI_DTOMUL_SIZE 3 + +/* Bitfields in SDCR */ +#define MMCI_SCDSEL_OFFSET 0 +#define MMCI_SCDSEL_SIZE 4 +#define MMCI_SCDBUS_OFFSET 7 +#define MMCI_SCDBUS_SIZE 1 + +/* Bitfields in ARGR */ +#define MMCI_ARG_OFFSET 0 +#define MMCI_ARG_SIZE 32 + +/* Bitfields in CMDR */ +#define MMCI_CMDNB_OFFSET 0 +#define MMCI_CMDNB_SIZE 6 +#define MMCI_RSPTYP_OFFSET 6 +#define MMCI_RSPTYP_SIZE 2 +#define MMCI_SPCMD_OFFSET 8 +#define MMCI_SPCMD_SIZE 3 +#define MMCI_OPDCMD_OFFSET 11 +#define MMCI_OPDCMD_SIZE 1 +#define MMCI_MAXLAT_OFFSET 12 +#define MMCI_MAXLAT_SIZE 1 +#define MMCI_TRCMD_OFFSET 16 +#define MMCI_TRCMD_SIZE 2 +#define MMCI_TRDIR_OFFSET 18 +#define MMCI_TRDIR_SIZE 1 +#define MMCI_TRTYP_OFFSET 19 +#define MMCI_TRTYP_SIZE 2 + +/* Bitfields in RSPRx */ +#define MMCI_RSP_OFFSET 0 +#define MMCI_RSP_SIZE 32 + +/* Bitfields in SR/IER/IDR/IMR */ +#define MMCI_CMDRDY_OFFSET 0 +#define MMCI_CMDRDY_SIZE 1 +#define MMCI_RXRDY_OFFSET 1 +#define MMCI_RXRDY_SIZE 1 +#define MMCI_TXRDY_OFFSET 2 +#define MMCI_TXRDY_SIZE 1 +#define MMCI_BLKE_OFFSET 3 +#define MMCI_BLKE_SIZE 1 +#define MMCI_DTIP_OFFSET 4 +#define MMCI_DTIP_SIZE 1 +#define MMCI_NOTBUSY_OFFSET 5 +#define MMCI_NOTBUSY_SIZE 1 +#define MMCI_ENDRX_OFFSET 6 +#define MMCI_ENDRX_SIZE 1 +#define MMCI_ENDTX_OFFSET 7 +#define MMCI_ENDTX_SIZE 1 +#define MMCI_RXBUFF_OFFSET 14 +#define MMCI_RXBUFF_SIZE 1 +#define MMCI_TXBUFE_OFFSET 15 +#define MMCI_TXBUFE_SIZE 1 +#define MMCI_RINDE_OFFSET 16 +#define MMCI_RINDE_SIZE 1 +#define MMCI_RDIRE_OFFSET 17 +#define MMCI_RDIRE_SIZE 1 +#define MMCI_RCRCE_OFFSET 18 +#define MMCI_RCRCE_SIZE 1 +#define MMCI_RENDE_OFFSET 19 +#define MMCI_RENDE_SIZE 1 +#define MMCI_RTOE_OFFSET 20 +#define MMCI_RTOE_SIZE 1 +#define MMCI_DCRCE_OFFSET 21 +#define MMCI_DCRCE_SIZE 1 +#define MMCI_DTOE_OFFSET 22 +#define MMCI_DTOE_SIZE 1 +#define MMCI_OVRE_OFFSET 30 +#define MMCI_OVRE_SIZE 1 +#define MMCI_UNRE_OFFSET 31 +#define MMCI_UNRE_SIZE 1 + +/* Constants for DTOMUL */ +#define MMCI_DTOMUL_1_CYCLE 0 +#define MMCI_DTOMUL_16_CYCLES 1 +#define MMCI_DTOMUL_128_CYCLES 2 +#define MMCI_DTOMUL_256_CYCLES 3 +#define MMCI_DTOMUL_1024_CYCLES 4 +#define MMCI_DTOMUL_4096_CYCLES 5 +#define MMCI_DTOMUL_65536_CYCLES 6 +#define MMCI_DTOMUL_1048576_CYCLES 7 + +/* Constants for RSPTYP */ +#define MMCI_RSPTYP_NO_RESP 0 +#define MMCI_RSPTYP_48_BIT_RESP 1 +#define MMCI_RSPTYP_136_BIT_RESP 2 + +/* Constants for SPCMD */ +#define MMCI_SPCMD_NO_SPEC_CMD 0 +#define MMCI_SPCMD_INIT_CMD 1 +#define MMCI_SPCMD_SYNC_CMD 2 +#define MMCI_SPCMD_INT_CMD 4 +#define MMCI_SPCMD_INT_RESP 5 + +/* Constants for TRCMD */ +#define MMCI_TRCMD_NO_TRANS 0 +#define MMCI_TRCMD_START_TRANS 1 +#define MMCI_TRCMD_STOP_TRANS 2 + +/* Constants for TRTYP */ +#define MMCI_TRTYP_BLOCK 0 +#define MMCI_TRTYP_MULTI_BLOCK 1 +#define MMCI_TRTYP_STREAM 2 + +/* Bit manipulation macros */ +#define MMCI_BIT(name) \ + (1 << MMCI_##name##_OFFSET) +#define MMCI_BF(name,value) \ + (((value) & ((1 << MMCI_##name##_SIZE) - 1)) \ + << MMCI_##name##_OFFSET) +#define MMCI_BFEXT(name,value) \ + (((value) >> MMCI_##name##_OFFSET)\ + & ((1 << MMCI_##name##_SIZE) - 1)) +#define MMCI_BFINS(name,value,old) \ + (((old) & ~(((1 << MMCI_##name##_SIZE) - 1) \ + << MMCI_##name##_OFFSET)) \ + | MMCI_BF(name,value)) + +/* Register access macros */ +#define mmci_readl(reg) \ + readl((void *)MMCI_BASE + MMCI_##reg) +#define mmci_writel(reg,value) \ + writel((value), (void *)MMCI_BASE + MMCI_##reg) + +#endif /* __CPU_AT32AP_ATMEL_MCI_H__ */ diff --git a/roms/u-boot-sam460ex/drivers/mmc/bfin_sdh.c b/roms/u-boot-sam460ex/drivers/mmc/bfin_sdh.c new file mode 100644 index 000000000..f9d560a71 --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mmc/bfin_sdh.c @@ -0,0 +1,257 @@ +/* + * Driver for Blackfin on-chip SDH controller + * + * Copyright (c) 2008-2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <malloc.h> +#include <part.h> +#include <mmc.h> + +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/byteorder.h> +#include <asm/blackfin.h> +#include <asm/mach-common/bits/sdh.h> +#include <asm/mach-common/bits/dma.h> + +#if defined(__ADSPBF51x__) +# define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CONTROL +# define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CONTROL +# define bfin_read_SDH_CLK_CTL bfin_read_RSI_CLK_CONTROL +# define bfin_write_SDH_CLK_CTL bfin_write_RSI_CLK_CONTROL +# define bfin_write_SDH_ARGUMENT bfin_write_RSI_ARGUMENT +# define bfin_write_SDH_COMMAND bfin_write_RSI_COMMAND +# define bfin_read_SDH_RESPONSE0 bfin_read_RSI_RESPONSE0 +# define bfin_read_SDH_RESPONSE1 bfin_read_RSI_RESPONSE1 +# define bfin_read_SDH_RESPONSE2 bfin_read_RSI_RESPONSE2 +# define bfin_read_SDH_RESPONSE3 bfin_read_RSI_RESPONSE3 +# define bfin_write_SDH_DATA_TIMER bfin_write_RSI_DATA_TIMER +# define bfin_write_SDH_DATA_LGTH bfin_write_RSI_DATA_LGTH +# define bfin_read_SDH_DATA_CTL bfin_read_RSI_DATA_CONTROL +# define bfin_write_SDH_DATA_CTL bfin_write_RSI_DATA_CONTROL +# define bfin_read_SDH_STATUS bfin_read_RSI_STATUS +# define bfin_write_SDH_STATUS_CLR bfin_write_RSI_STATUSCL +# define bfin_read_SDH_CFG bfin_read_RSI_CONFIG +# define bfin_write_SDH_CFG bfin_write_RSI_CONFIG +# define bfin_write_DMA_START_ADDR bfin_write_DMA4_START_ADDR +# define bfin_write_DMA_X_COUNT bfin_write_DMA4_X_COUNT +# define bfin_write_DMA_X_MODIFY bfin_write_DMA4_X_MODIFY +# define bfin_write_DMA_CONFIG bfin_write_DMA4_CONFIG +#elif defined(__ADSPBF54x__) +# define bfin_write_DMA_START_ADDR bfin_write_DMA22_START_ADDR +# define bfin_write_DMA_X_COUNT bfin_write_DMA22_X_COUNT +# define bfin_write_DMA_X_MODIFY bfin_write_DMA22_X_MODIFY +# define bfin_write_DMA_CONFIG bfin_write_DMA22_CONFIG +#else +# error no support for this proc yet +#endif + +static int +sdh_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd) +{ + unsigned int sdh_cmd; + unsigned int status; + int cmd = mmc_cmd->cmdidx; + int flags = mmc_cmd->resp_type; + int arg = mmc_cmd->cmdarg; + int ret = 0; + sdh_cmd = 0; + + sdh_cmd |= cmd; + + if (flags & MMC_RSP_PRESENT) + sdh_cmd |= CMD_RSP; + + if (flags & MMC_RSP_136) + sdh_cmd |= CMD_L_RSP; + + bfin_write_SDH_ARGUMENT(arg); + bfin_write_SDH_COMMAND(sdh_cmd | CMD_E); + + /* wait for a while */ + do { + udelay(1); + status = bfin_read_SDH_STATUS(); + } while (!(status & (CMD_SENT | CMD_RESP_END | CMD_TIME_OUT | + CMD_CRC_FAIL))); + + if (flags & MMC_RSP_PRESENT) { + mmc_cmd->response[0] = bfin_read_SDH_RESPONSE0(); + if (flags & MMC_RSP_136) { + mmc_cmd->response[1] = bfin_read_SDH_RESPONSE1(); + mmc_cmd->response[2] = bfin_read_SDH_RESPONSE2(); + mmc_cmd->response[3] = bfin_read_SDH_RESPONSE3(); + } + } + + if (status & CMD_TIME_OUT) + ret |= TIMEOUT; + else if (status & CMD_CRC_FAIL && flags & MMC_RSP_CRC) + ret |= COMM_ERR; + + bfin_write_SDH_STATUS_CLR(CMD_SENT_STAT | CMD_RESP_END_STAT | + CMD_TIMEOUT_STAT | CMD_CRC_FAIL_STAT); + return ret; +} + +/* set data for single block transfer */ +static int sdh_setup_data(struct mmc *mmc, struct mmc_data *data) +{ + u16 data_ctl = 0; + u16 dma_cfg = 0; + int ret = 0; + + /* Don't support write yet. */ + if (data->flags & MMC_DATA_WRITE) + return UNUSABLE_ERR; + data_ctl |= ((ffs(data->blocksize) - 1) << 4); + data_ctl |= DTX_DIR; + bfin_write_SDH_DATA_CTL(data_ctl); + dma_cfg = WDSIZE_32 | RESTART | WNR | DMAEN; + + bfin_write_SDH_DATA_TIMER(0xFFFF); + + blackfin_dcache_flush_invalidate_range(data->dest, + data->dest + data->blocksize); + /* configure DMA */ + bfin_write_DMA_START_ADDR(data->dest); + bfin_write_DMA_X_COUNT(data->blocksize / 4); + bfin_write_DMA_X_MODIFY(4); + bfin_write_DMA_CONFIG(dma_cfg); + bfin_write_SDH_DATA_LGTH(data->blocksize); + /* kick off transfer */ + bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E); + + return ret; +} + + +static int bfin_sdh_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + u32 status; + int ret = 0; + + ret = sdh_send_cmd(mmc, cmd); + if (ret) { + printf("sending CMD%d failed\n", cmd->cmdidx); + return ret; + } + if (data) { + ret = sdh_setup_data(mmc, data); + do { + udelay(1); + status = bfin_read_SDH_STATUS(); + } while (!(status & (DAT_BLK_END | DAT_END | DAT_TIME_OUT | DAT_CRC_FAIL | RX_OVERRUN))); + + if (status & DAT_TIME_OUT) { + bfin_write_SDH_STATUS_CLR(DAT_TIMEOUT_STAT); + ret |= TIMEOUT; + } else if (status & (DAT_CRC_FAIL | RX_OVERRUN)) { + bfin_write_SDH_STATUS_CLR(DAT_CRC_FAIL_STAT | RX_OVERRUN_STAT); + ret |= COMM_ERR; + } else + bfin_write_SDH_STATUS_CLR(DAT_BLK_END_STAT | DAT_END_STAT); + + if (ret) { + printf("tranfering data failed\n"); + return ret; + } + } + return 0; +} + +static void sdh_set_clk(unsigned long clk) +{ + unsigned long sys_clk; + unsigned long clk_div; + u16 clk_ctl = 0; + + clk_ctl = bfin_read_SDH_CLK_CTL(); + if (clk) { + /* setting SD_CLK */ + sys_clk = get_sclk(); + bfin_write_SDH_CLK_CTL(clk_ctl & ~CLK_E); + if (sys_clk % (2 * clk) == 0) + clk_div = sys_clk / (2 * clk) - 1; + else + clk_div = sys_clk / (2 * clk); + + if (clk_div > 0xff) + clk_div = 0xff; + clk_ctl |= (clk_div & 0xff); + clk_ctl |= CLK_E; + bfin_write_SDH_CLK_CTL(clk_ctl); + } else + bfin_write_SDH_CLK_CTL(clk_ctl & ~CLK_E); +} + +static void bfin_sdh_set_ios(struct mmc *mmc) +{ + u16 cfg = 0; + u16 clk_ctl = 0; + + if (mmc->bus_width == 4) { + cfg = bfin_read_SDH_CFG(); + cfg &= ~0x80; + cfg |= 0x40; + bfin_write_SDH_CFG(cfg); + clk_ctl |= WIDE_BUS; + } + bfin_write_SDH_CLK_CTL(clk_ctl); + sdh_set_clk(mmc->clock); +} + +static int bfin_sdh_init(struct mmc *mmc) +{ + + u16 pwr_ctl = 0; +/* Initialize sdh controller */ +#if defined(__ADSPBF54x__) + bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() | 0x1); + bfin_write_PORTC_FER(bfin_read_PORTC_FER() | 0x3F00); + bfin_write_PORTC_MUX(bfin_read_PORTC_MUX() & ~0xFFF0000); +#elif defined(__ADSPBF51x__) + bfin_write_PORTG_FER(bfin_read_PORTG_FER() | 0x01F8); + bfin_write_PORTG_MUX((bfin_read_PORTG_MUX() & ~0x3FC) | 0x154); +#else +# error no portmux for this proc yet +#endif + bfin_write_SDH_CFG(bfin_read_SDH_CFG() | CLKS_EN); + /* Disable card detect pin */ + bfin_write_SDH_CFG((bfin_read_SDH_CFG() & 0x1F) | 0x60); + + pwr_ctl |= ROD_CTL; + pwr_ctl |= PWR_ON; + bfin_write_SDH_PWR_CTL(pwr_ctl); + return 0; +} + + +int bfin_mmc_init(bd_t *bis) +{ + struct mmc *mmc = NULL; + + mmc = malloc(sizeof(struct mmc)); + + if (!mmc) + return -ENOMEM; + sprintf(mmc->name, "Blackfin SDH"); + mmc->send_cmd = bfin_sdh_request; + mmc->set_ios = bfin_sdh_set_ios; + mmc->init = bfin_sdh_init; + mmc->host_caps = MMC_MODE_4BIT; + + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->f_max = get_sclk(); + mmc->f_min = mmc->f_max >> 9; + mmc->block_dev.part_type = PART_TYPE_DOS; + + mmc_register(mmc); + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mmc/fsl_esdhc.c b/roms/u-boot-sam460ex/drivers/mmc/fsl_esdhc.c new file mode 100644 index 000000000..a368fe60d --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mmc/fsl_esdhc.c @@ -0,0 +1,514 @@ +/* + * Copyright 2007,2010 Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the pxa mmc code: + * (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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <common.h> +#include <command.h> +#include <hwconfig.h> +#include <mmc.h> +#include <part.h> +#include <malloc.h> +#include <mmc.h> +#include <fsl_esdhc.h> +#include <fdt_support.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct fsl_esdhc { + uint dsaddr; + uint blkattr; + uint cmdarg; + uint xfertyp; + uint cmdrsp0; + uint cmdrsp1; + uint cmdrsp2; + uint cmdrsp3; + uint datport; + uint prsstat; + uint proctl; + uint sysctl; + uint irqstat; + uint irqstaten; + uint irqsigen; + uint autoc12err; + uint hostcapblt; + uint wml; + char reserved1[8]; + uint fevt; + char reserved2[168]; + uint hostver; + char reserved3[780]; + uint scr; +}; + +/* Return the XFERTYP flags for a given command and data packet */ +uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) +{ + uint xfertyp = 0; + + if (data) { + xfertyp |= XFERTYP_DPSEL; +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO + xfertyp |= XFERTYP_DMAEN; +#endif + if (data->blocks > 1) { + xfertyp |= XFERTYP_MSBSEL; + xfertyp |= XFERTYP_BCEN; + } + + if (data->flags & MMC_DATA_READ) + xfertyp |= XFERTYP_DTDSEL; + } + + if (cmd->resp_type & MMC_RSP_CRC) + xfertyp |= XFERTYP_CCCEN; + if (cmd->resp_type & MMC_RSP_OPCODE) + xfertyp |= XFERTYP_CICEN; + if (cmd->resp_type & MMC_RSP_136) + xfertyp |= XFERTYP_RSPTYP_136; + else if (cmd->resp_type & MMC_RSP_BUSY) + xfertyp |= XFERTYP_RSPTYP_48_BUSY; + else if (cmd->resp_type & MMC_RSP_PRESENT) + xfertyp |= XFERTYP_RSPTYP_48; + + return XFERTYP_CMD(cmd->cmdidx) | xfertyp; +} + +#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO +/* + * PIO Read/Write Mode reduce the performace as DMA is not used in this mode. + */ +static void +esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data) +{ + struct fsl_esdhc *regs = mmc->priv; + uint blocks; + char *buffer; + uint databuf; + uint size; + uint irqstat; + uint timeout; + + if (data->flags & MMC_DATA_READ) { + blocks = data->blocks; + buffer = data->dest; + while (blocks) { + timeout = PIO_TIMEOUT; + size = data->blocksize; + irqstat = esdhc_read32(®s->irqstat); + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_BREN) + && --timeout); + if (timeout <= 0) { + printf("\nData Read Failed in PIO Mode."); + return; + } + while (size && (!(irqstat & IRQSTAT_TC))) { + udelay(100); /* Wait before last byte transfer complete */ + irqstat = esdhc_read32(®s->irqstat); + databuf = in_le32(®s->datport); + *((uint *)buffer) = databuf; + buffer += 4; + size -= 4; + } + blocks--; + } + } else { + blocks = data->blocks; + buffer = (char *)data->src; + while (blocks) { + timeout = PIO_TIMEOUT; + size = data->blocksize; + irqstat = esdhc_read32(®s->irqstat); + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_BWEN) + && --timeout); + if (timeout <= 0) { + printf("\nData Write Failed in PIO Mode."); + return; + } + while (size && (!(irqstat & IRQSTAT_TC))) { + udelay(100); /* Wait before last byte transfer complete */ + databuf = *((uint *)buffer); + buffer += 4; + size -= 4; + irqstat = esdhc_read32(®s->irqstat); + out_le32(®s->datport, databuf); + } + blocks--; + } + } +} +#endif + +static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) +{ + int timeout; + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO + uint wml_value; + + wml_value = data->blocksize/4; + + if (data->flags & MMC_DATA_READ) { + if (wml_value > 0x10) + wml_value = 0x10; + + esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value); + esdhc_write32(®s->dsaddr, (u32)data->dest); + } else { + if (wml_value > 0x80) + wml_value = 0x80; + if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { + printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); + return TIMEOUT; + } + + esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK, + wml_value << 16); + esdhc_write32(®s->dsaddr, (u32)data->src); + } +#else /* CONFIG_SYS_FSL_ESDHC_USE_PIO */ + if (!(data->flags & MMC_DATA_READ)) { + if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { + printf("\nThe SD card is locked. " + "Can not write to a locked card.\n\n"); + return TIMEOUT; + } + esdhc_write32(®s->dsaddr, (u32)data->src); + } else + esdhc_write32(®s->dsaddr, (u32)data->dest); +#endif /* CONFIG_SYS_FSL_ESDHC_USE_PIO */ + + esdhc_write32(®s->blkattr, data->blocks << 16 | data->blocksize); + + /* Calculate the timeout period for data transactions */ + timeout = fls(mmc->tran_speed/10) - 1; + timeout -= 13; + + if (timeout > 14) + timeout = 14; + + if (timeout < 0) + timeout = 0; + + esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16); + + return 0; +} + + +/* + * Sends a command out on the bus. Takes the mmc pointer, + * a command pointer, and an optional data pointer. + */ +static int +esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + uint xfertyp; + uint irqstat; + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + + esdhc_write32(®s->irqstat, -1); + + sync(); + + /* Wait for the bus to be idle */ + while ((esdhc_read32(®s->prsstat) & PRSSTAT_CICHB) || + (esdhc_read32(®s->prsstat) & PRSSTAT_CIDHB)) + ; + + while (esdhc_read32(®s->prsstat) & PRSSTAT_DLA) + ; + + /* Wait at least 8 SD clock cycles before the next command */ + /* + * Note: This is way more than 8 cycles, but 1ms seems to + * resolve timing issues with some cards + */ + udelay(1000); + + /* Set up for a data transfer if we have one */ + if (data) { + int err; + + err = esdhc_setup_data(mmc, data); + if(err) + return err; + } + + /* Figure out the transfer arguments */ + xfertyp = esdhc_xfertyp(cmd, data); + + /* Send the command */ + esdhc_write32(®s->cmdarg, cmd->cmdarg); + esdhc_write32(®s->xfertyp, xfertyp); + + /* Wait for the command to complete */ + while (!(esdhc_read32(®s->irqstat) & IRQSTAT_CC)) + ; + + irqstat = esdhc_read32(®s->irqstat); + esdhc_write32(®s->irqstat, irqstat); + + if (irqstat & CMD_ERR) + return COMM_ERR; + + if (irqstat & IRQSTAT_CTOE) + return TIMEOUT; + + /* Copy the response to the response buffer */ + if (cmd->resp_type & MMC_RSP_136) { + u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; + + cmdrsp3 = esdhc_read32(®s->cmdrsp3); + cmdrsp2 = esdhc_read32(®s->cmdrsp2); + cmdrsp1 = esdhc_read32(®s->cmdrsp1); + cmdrsp0 = esdhc_read32(®s->cmdrsp0); + cmd->response[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24); + cmd->response[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24); + cmd->response[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24); + cmd->response[3] = (cmdrsp0 << 8); + } else + cmd->response[0] = esdhc_read32(®s->cmdrsp0); + + /* Wait until all of the blocks are transferred */ + if (data) { +#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO + esdhc_pio_read_write(mmc, data); +#else + do { + irqstat = esdhc_read32(®s->irqstat); + + if (irqstat & DATA_ERR) + return COMM_ERR; + + if (irqstat & IRQSTAT_DTOE) + return TIMEOUT; + } while (!(irqstat & IRQSTAT_TC) && + (esdhc_read32(®s->prsstat) & PRSSTAT_DLA)); +#endif + } + + esdhc_write32(®s->irqstat, -1); + + return 0; +} + +void set_sysctl(struct mmc *mmc, uint clock) +{ + int sdhc_clk = gd->sdhc_clk; + int div, pre_div; + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + uint clk; + + if (clock < mmc->f_min) + clock = mmc->f_min; + + if (sdhc_clk / 16 > clock) { + for (pre_div = 2; pre_div < 256; pre_div *= 2) + if ((sdhc_clk / pre_div) <= (clock * 16)) + break; + } else + pre_div = 2; + + for (div = 1; div <= 16; div++) + if ((sdhc_clk / (div * pre_div)) <= clock) + break; + + pre_div >>= 1; + div -= 1; + + clk = (pre_div << 8) | (div << 4); + + esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN); + + esdhc_clrsetbits32(®s->sysctl, SYSCTL_CLOCK_MASK, clk); + + udelay(10000); + + clk = SYSCTL_PEREN | SYSCTL_CKEN; + + esdhc_setbits32(®s->sysctl, clk); +} + +static void esdhc_set_ios(struct mmc *mmc) +{ + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + + /* Set the clock speed */ + set_sysctl(mmc, mmc->clock); + + /* Set the bus width */ + esdhc_clrbits32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); + + if (mmc->bus_width == 4) + esdhc_setbits32(®s->proctl, PROCTL_DTW_4); + else if (mmc->bus_width == 8) + esdhc_setbits32(®s->proctl, PROCTL_DTW_8); + +} + +static int esdhc_init(struct mmc *mmc) +{ + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + int timeout = 1000; + int ret = 0; + u8 card_absent; + + /* Enable cache snooping */ + if (cfg && !cfg->no_snoop) + esdhc_write32(®s->scr, 0x00000040); + + /* Reset the entire host controller */ + esdhc_write32(®s->sysctl, SYSCTL_RSTA); + + /* Wait until the controller is available */ + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) + udelay(1000); + + esdhc_write32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); + + /* Set the initial clock speed */ + set_sysctl(mmc, 400000); + + /* Disable the BRR and BWR bits in IRQSTAT */ + esdhc_clrbits32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); + + /* Put the PROCTL reg back to the default */ + esdhc_write32(®s->proctl, PROCTL_INIT); + + /* Set timout to the maximum value */ + esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16); + + /* Check if there is a callback for detecting the card */ + if (board_mmc_getcd(&card_absent, mmc)) { + timeout = 1000; + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_CINS) && + --timeout) + udelay(1000); + + if (timeout <= 0) + ret = NO_CARD_ERR; + } else { + if (card_absent) + ret = NO_CARD_ERR; + } + + return ret; +} + +static void esdhc_reset(struct fsl_esdhc *regs) +{ + unsigned long timeout = 100; /* wait max 100 ms */ + + /* reset the controller */ + esdhc_write32(®s->sysctl, SYSCTL_RSTA); + + /* hardware clears the bit when it is done */ + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) + udelay(1000); + if (!timeout) + printf("MMC/SD: Reset never completed.\n"); +} + +int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) +{ + struct fsl_esdhc *regs; + struct mmc *mmc; + u32 caps; + + if (!cfg) + return -1; + + mmc = malloc(sizeof(struct mmc)); + + sprintf(mmc->name, "FSL_ESDHC"); + regs = (struct fsl_esdhc *)cfg->esdhc_base; + + /* First reset the eSDHC controller */ + esdhc_reset(regs); + + mmc->priv = cfg; + mmc->send_cmd = esdhc_send_cmd; + mmc->set_ios = esdhc_set_ios; + mmc->init = esdhc_init; + + caps = regs->hostcapblt; + + if (caps & ESDHC_HOSTCAPBLT_VS18) + mmc->voltages |= MMC_VDD_165_195; + if (caps & ESDHC_HOSTCAPBLT_VS30) + mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; + if (caps & ESDHC_HOSTCAPBLT_VS33) + mmc->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34; + + mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; + + if (caps & ESDHC_HOSTCAPBLT_HSS) + mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + + mmc->f_min = 400000; + mmc->f_max = MIN(gd->sdhc_clk, 50000000); + + mmc_register(mmc); + + return 0; +} + +int fsl_esdhc_mmc_init(bd_t *bis) +{ + struct fsl_esdhc_cfg *cfg; + + cfg = malloc(sizeof(struct fsl_esdhc_cfg)); + memset(cfg, 0, sizeof(struct fsl_esdhc_cfg)); + cfg->esdhc_base = CONFIG_SYS_FSL_ESDHC_ADDR; + return fsl_esdhc_initialize(bis, cfg); +} + +#ifdef CONFIG_OF_LIBFDT +void fdt_fixup_esdhc(void *blob, bd_t *bd) +{ + const char *compat = "fsl,esdhc"; + const char *status = "okay"; + + if (!hwconfig("esdhc")) { + status = "disabled"; + goto out; + } + + do_fixup_by_compat_u32(blob, compat, "clock-frequency", + gd->sdhc_clk, 1); +out: + do_fixup_by_compat(blob, compat, "status", status, + strlen(status) + 1, 1); +} +#endif diff --git a/roms/u-boot-sam460ex/drivers/mmc/mmc.c b/roms/u-boot-sam460ex/drivers/mmc/mmc.c new file mode 100644 index 000000000..cf4ea161b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mmc/mmc.c @@ -0,0 +1,945 @@ +/* + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the Linux code + * + * 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 <command.h> +#include <mmc.h> +#include <part.h> +#include <malloc.h> +#include <linux/list.h> +#include <mmc.h> +#include <div64.h> + +static struct list_head mmc_devices; +static int cur_dev_num = -1; + +int __board_mmc_getcd(u8 *cd, struct mmc *mmc) { + return -1; +} + +int board_mmc_getcd(u8 *cd, struct mmc *mmc)__attribute__((weak, + alias("__board_mmc_getcd"))); + +int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +{ + return mmc->send_cmd(mmc, cmd, data); +} + +int mmc_set_blocklen(struct mmc *mmc, int len) +{ + struct mmc_cmd cmd; + + cmd.cmdidx = MMC_CMD_SET_BLOCKLEN; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = len; + cmd.flags = 0; + + return mmc_send_cmd(mmc, &cmd, NULL); +} + +struct mmc *find_mmc_device(int dev_num) +{ + struct mmc *m; + struct list_head *entry; + + list_for_each(entry, &mmc_devices) { + m = list_entry(entry, struct mmc, link); + + if (m->block_dev.dev == dev_num) + return m; + } + + printf("MMC Device %d not found\n", dev_num); + + return NULL; +} + +static ulong +mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int err; + int stoperr = 0; + struct mmc *mmc = find_mmc_device(dev_num); + int blklen; + + if (!mmc) + return -1; + + blklen = mmc->write_bl_len; + + err = mmc_set_blocklen(mmc, mmc->write_bl_len); + + if (err) { + printf("set write bl len failed\n\r"); + return err; + } + + if (blkcnt > 1) + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = start; + else + cmd.cmdarg = start * blklen; + + cmd.resp_type = MMC_RSP_R1; + cmd.flags = 0; + + data.src = src; + data.blocks = blkcnt; + data.blocksize = blklen; + data.flags = MMC_DATA_WRITE; + + err = mmc_send_cmd(mmc, &cmd, &data); + + if (err) { + printf("mmc write failed\n\r"); + return err; + } + + if (blkcnt > 1) { + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1b; + cmd.flags = 0; + stoperr = mmc_send_cmd(mmc, &cmd, NULL); + } + + return blkcnt; +} + +int mmc_read_block(struct mmc *mmc, void *dst, uint blocknum) +{ + struct mmc_cmd cmd; + struct mmc_data data; + + cmd.cmdidx = MMC_CMD_READ_SINGLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = blocknum; + else + cmd.cmdarg = blocknum * mmc->read_bl_len; + + cmd.resp_type = MMC_RSP_R1; + cmd.flags = 0; + + data.dest = dst; + data.blocks = 1; + data.blocksize = mmc->read_bl_len; + data.flags = MMC_DATA_READ; + + return mmc_send_cmd(mmc, &cmd, &data); +} + +int mmc_read(struct mmc *mmc, u64 src, uchar *dst, int size) +{ + char *buffer; + int i; + int blklen = mmc->read_bl_len; + int startblock = lldiv(src, mmc->read_bl_len); + int endblock = lldiv(src + size - 1, mmc->read_bl_len); + int err = 0; + + /* Make a buffer big enough to hold all the blocks we might read */ + buffer = malloc(blklen); + + if (!buffer) { + printf("Could not allocate buffer for MMC read!\n"); + return -1; + } + + /* We always do full block reads from the card */ + err = mmc_set_blocklen(mmc, mmc->read_bl_len); + + if (err) + goto free_buffer; + + for (i = startblock; i <= endblock; i++) { + int segment_size; + int offset; + + err = mmc_read_block(mmc, buffer, i); + + if (err) + goto free_buffer; + + /* + * The first block may not be aligned, so we + * copy from the desired point in the block + */ + offset = (src & (blklen - 1)); + segment_size = MIN(blklen - offset, size); + + memcpy(dst, buffer + offset, segment_size); + + dst += segment_size; + src += segment_size; + size -= segment_size; + } + +free_buffer: + free(buffer); + + return err; +} + +static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) +{ + int err; + int i; + struct mmc *mmc = find_mmc_device(dev_num); + + if (!mmc) + return 0; + + /* We always do full block reads from the card */ + err = mmc_set_blocklen(mmc, mmc->read_bl_len); + + if (err) { + return 0; + } + + for (i = start; i < start + blkcnt; i++, dst += mmc->read_bl_len) { + err = mmc_read_block(mmc, dst, i); + + if (err) { + printf("block read failed: %d\n", err); + return i - start; + } + } + + return blkcnt; +} + +int mmc_go_idle(struct mmc* mmc) +{ + struct mmc_cmd cmd; + int err; + + udelay(1000); + + cmd.cmdidx = MMC_CMD_GO_IDLE_STATE; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_NONE; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + udelay(2000); + + return 0; +} + +int +sd_send_op_cond(struct mmc *mmc) +{ + int timeout = 1000; + int err; + struct mmc_cmd cmd; + + do { + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SEND_OP_COND; + cmd.resp_type = MMC_RSP_R3; + + /* + * Most cards do not answer if some reserved bits + * in the ocr are set. However, Some controller + * can set bit 7 (reserved for low voltages), but + * how to manage low voltages SD card is not yet + * specified. + */ + cmd.cmdarg = mmc->voltages & 0xff8000; + + if (mmc->version == SD_VERSION_2) + cmd.cmdarg |= OCR_HCS; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + udelay(1000); + } while ((!(cmd.response[0] & OCR_BUSY)) && timeout--); + + if (timeout <= 0) + return UNUSABLE_ERR; + + if (mmc->version != SD_VERSION_2) + mmc->version = SD_VERSION_1_0; + + mmc->ocr = cmd.response[0]; + + mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); + mmc->rca = 0; + + return 0; +} + +int mmc_send_op_cond(struct mmc *mmc) +{ + int timeout = 1000; + struct mmc_cmd cmd; + int err; + + /* Some cards seem to need this */ + mmc_go_idle(mmc); + + do { + cmd.cmdidx = MMC_CMD_SEND_OP_COND; + cmd.resp_type = MMC_RSP_R3; + cmd.cmdarg = OCR_HCS | mmc->voltages; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + udelay(1000); + } while (!(cmd.response[0] & OCR_BUSY) && timeout--); + + if (timeout <= 0) + return UNUSABLE_ERR; + + mmc->version = MMC_VERSION_UNKNOWN; + mmc->ocr = cmd.response[0]; + + mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); + mmc->rca = 0; + + return 0; +} + + +int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int err; + + /* Get the Card Status Register */ + cmd.cmdidx = MMC_CMD_SEND_EXT_CSD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + cmd.flags = 0; + + data.dest = ext_csd; + data.blocks = 1; + data.blocksize = 512; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + + return err; +} + + +int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) +{ + struct mmc_cmd cmd; + + cmd.cmdidx = MMC_CMD_SWITCH; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (index << 16) | + (value << 8); + cmd.flags = 0; + + return mmc_send_cmd(mmc, &cmd, NULL); +} + +int mmc_change_freq(struct mmc *mmc) +{ + char ext_csd[512]; + char cardtype; + int err; + + mmc->card_caps = 0; + + /* Only version 4 supports high-speed */ + if (mmc->version < MMC_VERSION_4) + return 0; + + mmc->card_caps |= MMC_MODE_4BIT; + + err = mmc_send_ext_csd(mmc, ext_csd); + + if (err) + return err; + + if (ext_csd[212] || ext_csd[213] || ext_csd[214] || ext_csd[215]) + mmc->high_capacity = 1; + + cardtype = ext_csd[196] & 0xf; + + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); + + if (err) + return err; + + /* Now check to see that it worked */ + err = mmc_send_ext_csd(mmc, ext_csd); + + if (err) + return err; + + /* No high-speed support */ + if (!ext_csd[185]) + return 0; + + /* High Speed is set, there are two types: 52MHz and 26MHz */ + if (cardtype & MMC_HS_52MHZ) + mmc->card_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + else + mmc->card_caps |= MMC_MODE_HS; + + return 0; +} + +int sd_switch(struct mmc *mmc, int mode, int group, u8 value, u8 *resp) +{ + struct mmc_cmd cmd; + struct mmc_data data; + + /* Switch the frequency */ + cmd.cmdidx = SD_CMD_SWITCH_FUNC; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = (mode << 31) | 0xffffff; + cmd.cmdarg &= ~(0xf << (group * 4)); + cmd.cmdarg |= value << (group * 4); + cmd.flags = 0; + + data.dest = (char *)resp; + data.blocksize = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + return mmc_send_cmd(mmc, &cmd, &data); +} + + +int sd_change_freq(struct mmc *mmc) +{ + int err; + struct mmc_cmd cmd; + uint scr[2]; + uint switch_status[16]; + struct mmc_data data; + int timeout; + + mmc->card_caps = 0; + + /* Read the SCR to find out if this card supports higher speeds */ + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SEND_SCR; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + cmd.flags = 0; + + timeout = 3; + +retry_scr: + data.dest = (char *)&scr; + data.blocksize = 8; + data.blocks = 1; + data.flags = MMC_DATA_READ; + + err = mmc_send_cmd(mmc, &cmd, &data); + + if (err) { + if (timeout--) + goto retry_scr; + + return err; + } + + mmc->scr[0] = __be32_to_cpu(scr[0]); + mmc->scr[1] = __be32_to_cpu(scr[1]); + + switch ((mmc->scr[0] >> 24) & 0xf) { + case 0: + mmc->version = SD_VERSION_1_0; + break; + case 1: + mmc->version = SD_VERSION_1_10; + break; + case 2: + mmc->version = SD_VERSION_2; + break; + default: + mmc->version = SD_VERSION_1_0; + break; + } + + /* Version 1.0 doesn't support switching */ + if (mmc->version == SD_VERSION_1_0) + return 0; + + timeout = 4; + while (timeout--) { + err = sd_switch(mmc, SD_SWITCH_CHECK, 0, 1, + (u8 *)&switch_status); + + if (err) + return err; + + /* The high-speed function is busy. Try again */ + if (!(__be32_to_cpu(switch_status[7]) & SD_HIGHSPEED_BUSY)) + break; + } + + if (mmc->scr[0] & SD_DATA_4BIT) + mmc->card_caps |= MMC_MODE_4BIT; + + /* If high-speed isn't supported, we return */ + if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) + return 0; + + err = sd_switch(mmc, SD_SWITCH_SWITCH, 0, 1, (u8 *)&switch_status); + + if (err) + return err; + + if ((__be32_to_cpu(switch_status[4]) & 0x0f000000) == 0x01000000) + mmc->card_caps |= MMC_MODE_HS; + + return 0; +} + +/* frequency bases */ +/* divided by 10 to be nice to platforms without floating point */ +int fbase[] = { + 10000, + 100000, + 1000000, + 10000000, +}; + +/* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice + * to platforms without floating point. + */ +int multipliers[] = { + 0, /* reserved */ + 10, + 12, + 13, + 15, + 20, + 25, + 30, + 35, + 40, + 45, + 50, + 55, + 60, + 70, + 80, +}; + +void mmc_set_ios(struct mmc *mmc) +{ + mmc->set_ios(mmc); +} + +void mmc_set_clock(struct mmc *mmc, uint clock) +{ + if (clock > mmc->f_max) + clock = mmc->f_max; + + if (clock < mmc->f_min) + clock = mmc->f_min; + + mmc->clock = clock; + + mmc_set_ios(mmc); +} + +void mmc_set_bus_width(struct mmc *mmc, uint width) +{ + mmc->bus_width = width; + + mmc_set_ios(mmc); +} + +int mmc_startup(struct mmc *mmc) +{ + int err; + uint mult, freq; + u64 cmult, csize; + struct mmc_cmd cmd; + + /* Put the Card in Identify Mode */ + cmd.cmdidx = MMC_CMD_ALL_SEND_CID; + cmd.resp_type = MMC_RSP_R2; + cmd.cmdarg = 0; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + memcpy(mmc->cid, cmd.response, 16); + + /* + * For MMC cards, set the Relative Address. + * For SD cards, get the Relatvie Address. + * This also puts the cards into Standby State + */ + cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; + cmd.cmdarg = mmc->rca << 16; + cmd.resp_type = MMC_RSP_R6; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + if (IS_SD(mmc)) + mmc->rca = (cmd.response[0] >> 16) & 0xffff; + + /* Get the Card-Specific Data */ + cmd.cmdidx = MMC_CMD_SEND_CSD; + cmd.resp_type = MMC_RSP_R2; + cmd.cmdarg = mmc->rca << 16; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + mmc->csd[0] = cmd.response[0]; + mmc->csd[1] = cmd.response[1]; + mmc->csd[2] = cmd.response[2]; + mmc->csd[3] = cmd.response[3]; + + if (mmc->version == MMC_VERSION_UNKNOWN) { + int version = (cmd.response[0] >> 26) & 0xf; + + switch (version) { + case 0: + mmc->version = MMC_VERSION_1_2; + break; + case 1: + mmc->version = MMC_VERSION_1_4; + break; + case 2: + mmc->version = MMC_VERSION_2_2; + break; + case 3: + mmc->version = MMC_VERSION_3; + break; + case 4: + mmc->version = MMC_VERSION_4; + break; + default: + mmc->version = MMC_VERSION_1_2; + break; + } + } + + /* divide frequency by 10, since the mults are 10x bigger */ + freq = fbase[(cmd.response[0] & 0x7)]; + mult = multipliers[((cmd.response[0] >> 3) & 0xf)]; + + mmc->tran_speed = freq * mult; + + mmc->read_bl_len = 1 << ((cmd.response[1] >> 16) & 0xf); + + if (IS_SD(mmc)) + mmc->write_bl_len = mmc->read_bl_len; + else + mmc->write_bl_len = 1 << ((cmd.response[3] >> 22) & 0xf); + + if (mmc->high_capacity) { + csize = (mmc->csd[1] & 0x3f) << 16 + | (mmc->csd[2] & 0xffff0000) >> 16; + cmult = 8; + } else { + csize = (mmc->csd[1] & 0x3ff) << 2 + | (mmc->csd[2] & 0xc0000000) >> 30; + cmult = (mmc->csd[2] & 0x00038000) >> 15; + } + + mmc->capacity = (csize + 1) << (cmult + 2); + mmc->capacity *= mmc->read_bl_len; + + if (mmc->read_bl_len > 512) + mmc->read_bl_len = 512; + + if (mmc->write_bl_len > 512) + mmc->write_bl_len = 512; + + /* Select the card, and put it into Transfer Mode */ + cmd.cmdidx = MMC_CMD_SELECT_CARD; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = mmc->rca << 16; + cmd.flags = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + if (IS_SD(mmc)) + err = sd_change_freq(mmc); + else + err = mmc_change_freq(mmc); + + if (err) + return err; + + /* Restrict card's capabilities by what the host can do */ + mmc->card_caps &= mmc->host_caps; + + if (IS_SD(mmc)) { + if (mmc->card_caps & MMC_MODE_4BIT) { + cmd.cmdidx = MMC_CMD_APP_CMD; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = mmc->rca << 16; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + cmd.cmdidx = SD_CMD_APP_SET_BUS_WIDTH; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 2; + cmd.flags = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + + mmc_set_bus_width(mmc, 4); + } + + if (mmc->card_caps & MMC_MODE_HS) + mmc_set_clock(mmc, 50000000); + else + mmc_set_clock(mmc, 25000000); + } else { + if (mmc->card_caps & MMC_MODE_4BIT) { + /* Set the card to use 4 bit*/ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + EXT_CSD_BUS_WIDTH_4); + + if (err) + return err; + + mmc_set_bus_width(mmc, 4); + } else if (mmc->card_caps & MMC_MODE_8BIT) { + /* Set the card to use 8 bit*/ + err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + EXT_CSD_BUS_WIDTH_8); + + if (err) + return err; + + mmc_set_bus_width(mmc, 8); + } + + if (mmc->card_caps & MMC_MODE_HS) { + if (mmc->card_caps & MMC_MODE_HS_52MHz) + mmc_set_clock(mmc, 52000000); + else + mmc_set_clock(mmc, 26000000); + } else + mmc_set_clock(mmc, 20000000); + } + + /* fill in device description */ + mmc->block_dev.lun = 0; + mmc->block_dev.type = 0; + mmc->block_dev.blksz = mmc->read_bl_len; + mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); + sprintf(mmc->block_dev.vendor, "Man %06x Snr %08x", mmc->cid[0] >> 8, + (mmc->cid[2] << 8) | (mmc->cid[3] >> 24)); + sprintf(mmc->block_dev.product, "%c%c%c%c%c", mmc->cid[0] & 0xff, + (mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff, + (mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff); + sprintf(mmc->block_dev.revision, "%d.%d", mmc->cid[2] >> 28, + (mmc->cid[2] >> 24) & 0xf); + init_part(&mmc->block_dev); + + return 0; +} + +int mmc_send_if_cond(struct mmc *mmc) +{ + struct mmc_cmd cmd; + int err; + + cmd.cmdidx = SD_CMD_SEND_IF_COND; + /* We set the bit if the host supports voltages between 2.7 and 3.6 V */ + cmd.cmdarg = ((mmc->voltages & 0xff8000) != 0) << 8 | 0xaa; + cmd.resp_type = MMC_RSP_R7; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + if ((cmd.response[0] & 0xff) != 0xaa) + return UNUSABLE_ERR; + else + mmc->version = SD_VERSION_2; + + return 0; +} + +int mmc_register(struct mmc *mmc) +{ + /* Setup the universal parts of the block interface just once */ + mmc->block_dev.if_type = IF_TYPE_MMC; + mmc->block_dev.dev = cur_dev_num++; + mmc->block_dev.removable = 1; + mmc->block_dev.block_read = mmc_bread; + mmc->block_dev.block_write = mmc_bwrite; + + INIT_LIST_HEAD (&mmc->link); + + list_add_tail (&mmc->link, &mmc_devices); + + return 0; +} + +block_dev_desc_t *mmc_get_dev(int dev) +{ + struct mmc *mmc = find_mmc_device(dev); + + return mmc ? &mmc->block_dev : NULL; +} + +int mmc_init(struct mmc *mmc) +{ + int err; + + err = mmc->init(mmc); + + if (err) + return err; + + mmc_set_bus_width(mmc, 1); + mmc_set_clock(mmc, 1); + + /* Reset the Card */ + err = mmc_go_idle(mmc); + + if (err) + return err; + + /* Test for SD version 2 */ + err = mmc_send_if_cond(mmc); + + /* Now try to get the SD card's operating condition */ + err = sd_send_op_cond(mmc); + + /* If the command timed out, we check for an MMC card */ + if (err == TIMEOUT) { + err = mmc_send_op_cond(mmc); + + if (err) { + printf("Card did not respond to voltage select!\n"); + return UNUSABLE_ERR; + } + } + + return mmc_startup(mmc); +} + +/* + * CPU and board-specific MMC initializations. Aliased function + * signals caller to move on + */ +static int __def_mmc_init(bd_t *bis) +{ + return -1; +} + +int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); +int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); + +void print_mmc_devices(char separator) +{ + struct mmc *m; + struct list_head *entry; + + list_for_each(entry, &mmc_devices) { + m = list_entry(entry, struct mmc, link); + + printf("%s: %d", m->name, m->block_dev.dev); + + if (entry->next != &mmc_devices) + printf("%c ", separator); + } + + printf("\n"); +} + +int mmc_initialize(bd_t *bis) +{ + INIT_LIST_HEAD (&mmc_devices); + cur_dev_num = 0; + + if (board_mmc_init(bis) < 0) + cpu_mmc_init(bis); + + print_mmc_devices(','); + + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mmc/mxcmmc.c b/roms/u-boot-sam460ex/drivers/mmc/mxcmmc.c new file mode 100644 index 000000000..59639539f --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mmc/mxcmmc.c @@ -0,0 +1,522 @@ +/* + * This is a driver for the SDHC controller found in Freescale MX2/MX3 + * SoCs. It is basically the same hardware as found on MX1 (imxmmc.c). + * Unlike the hardware found on MX1, this hardware just works and does + * not need all the quirks found in imxmmc.c, hence the seperate driver. + * + * Copyright (C) 2009 Ilya Yanok, <yanok@emcraft.com> + * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> + * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com> + * + * derived from pxamci.c by Russell King + * + * 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. + * + */ + +#include <config.h> +#include <common.h> +#include <command.h> +#include <mmc.h> +#include <part.h> +#include <malloc.h> +#include <mmc.h> +#include <asm/errno.h> +#include <asm/io.h> +#ifdef CONFIG_MX27 +#include <asm/arch/clock.h> +#endif + +#define DRIVER_NAME "mxc-mmc" + +struct mxcmci_regs { + u32 str_stp_clk; + u32 status; + u32 clk_rate; + u32 cmd_dat_cont; + u32 res_to; + u32 read_to; + u32 blk_len; + u32 nob; + u32 rev_no; + u32 int_cntr; + u32 cmd; + u32 arg; + u32 pad; + u32 res_fifo; + u32 buffer_access; +}; + +#define STR_STP_CLK_RESET (1 << 3) +#define STR_STP_CLK_START_CLK (1 << 1) +#define STR_STP_CLK_STOP_CLK (1 << 0) + +#define STATUS_CARD_INSERTION (1 << 31) +#define STATUS_CARD_REMOVAL (1 << 30) +#define STATUS_YBUF_EMPTY (1 << 29) +#define STATUS_XBUF_EMPTY (1 << 28) +#define STATUS_YBUF_FULL (1 << 27) +#define STATUS_XBUF_FULL (1 << 26) +#define STATUS_BUF_UND_RUN (1 << 25) +#define STATUS_BUF_OVFL (1 << 24) +#define STATUS_SDIO_INT_ACTIVE (1 << 14) +#define STATUS_END_CMD_RESP (1 << 13) +#define STATUS_WRITE_OP_DONE (1 << 12) +#define STATUS_DATA_TRANS_DONE (1 << 11) +#define STATUS_READ_OP_DONE (1 << 11) +#define STATUS_WR_CRC_ERROR_CODE_MASK (3 << 10) +#define STATUS_CARD_BUS_CLK_RUN (1 << 8) +#define STATUS_BUF_READ_RDY (1 << 7) +#define STATUS_BUF_WRITE_RDY (1 << 6) +#define STATUS_RESP_CRC_ERR (1 << 5) +#define STATUS_CRC_READ_ERR (1 << 3) +#define STATUS_CRC_WRITE_ERR (1 << 2) +#define STATUS_TIME_OUT_RESP (1 << 1) +#define STATUS_TIME_OUT_READ (1 << 0) +#define STATUS_ERR_MASK 0x2f + +#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1 << 12) +#define CMD_DAT_CONT_STOP_READWAIT (1 << 11) +#define CMD_DAT_CONT_START_READWAIT (1 << 10) +#define CMD_DAT_CONT_BUS_WIDTH_4 (2 << 8) +#define CMD_DAT_CONT_INIT (1 << 7) +#define CMD_DAT_CONT_WRITE (1 << 4) +#define CMD_DAT_CONT_DATA_ENABLE (1 << 3) +#define CMD_DAT_CONT_RESPONSE_48BIT_CRC (1 << 0) +#define CMD_DAT_CONT_RESPONSE_136BIT (2 << 0) +#define CMD_DAT_CONT_RESPONSE_48BIT (3 << 0) + +#define INT_SDIO_INT_WKP_EN (1 << 18) +#define INT_CARD_INSERTION_WKP_EN (1 << 17) +#define INT_CARD_REMOVAL_WKP_EN (1 << 16) +#define INT_CARD_INSERTION_EN (1 << 15) +#define INT_CARD_REMOVAL_EN (1 << 14) +#define INT_SDIO_IRQ_EN (1 << 13) +#define INT_DAT0_EN (1 << 12) +#define INT_BUF_READ_EN (1 << 4) +#define INT_BUF_WRITE_EN (1 << 3) +#define INT_END_CMD_RES_EN (1 << 2) +#define INT_WRITE_OP_DONE_EN (1 << 1) +#define INT_READ_OP_EN (1 << 0) + +struct mxcmci_host { + struct mmc *mmc; + struct mxcmci_regs *base; + int irq; + int detect_irq; + int dma; + int do_dma; + unsigned int power_mode; + + struct mmc_cmd *cmd; + struct mmc_data *data; + + unsigned int dma_nents; + unsigned int datasize; + unsigned int dma_dir; + + u16 rev_no; + unsigned int cmdat; + + int clock; +}; + +static struct mxcmci_host mxcmci_host; +static struct mxcmci_host *host = &mxcmci_host; + +static inline int mxcmci_use_dma(struct mxcmci_host *host) +{ + return host->do_dma; +} + +static void mxcmci_softreset(struct mxcmci_host *host) +{ + int i; + + /* reset sequence */ + writel(STR_STP_CLK_RESET, &host->base->str_stp_clk); + writel(STR_STP_CLK_RESET | STR_STP_CLK_START_CLK, + &host->base->str_stp_clk); + + for (i = 0; i < 8; i++) + writel(STR_STP_CLK_START_CLK, &host->base->str_stp_clk); + + writel(0xff, &host->base->res_to); +} + +static void mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) +{ + unsigned int nob = data->blocks; + unsigned int blksz = data->blocksize; + unsigned int datasize = nob * blksz; + + host->data = data; + + writel(nob, &host->base->nob); + writel(blksz, &host->base->blk_len); + host->datasize = datasize; +} + +static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_cmd *cmd, + unsigned int cmdat) +{ + if (host->cmd != NULL) + printf("mxcmci: error!\n"); + host->cmd = cmd; + + switch (cmd->resp_type) { + case MMC_RSP_R1: /* short CRC, OPCODE */ + case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */ + cmdat |= CMD_DAT_CONT_RESPONSE_48BIT_CRC; + break; + case MMC_RSP_R2: /* long 136 bit + CRC */ + cmdat |= CMD_DAT_CONT_RESPONSE_136BIT; + break; + case MMC_RSP_R3: /* short */ + cmdat |= CMD_DAT_CONT_RESPONSE_48BIT; + break; + case MMC_RSP_NONE: + break; + default: + printf("mxcmci: unhandled response type 0x%x\n", + cmd->resp_type); + return -EINVAL; + } + + writel(cmd->cmdidx, &host->base->cmd); + writel(cmd->cmdarg, &host->base->arg); + writel(cmdat, &host->base->cmd_dat_cont); + + return 0; +} + +static void mxcmci_finish_request(struct mxcmci_host *host, + struct mmc_cmd *cmd, struct mmc_data *data) +{ + host->cmd = NULL; + host->data = NULL; +} + +static int mxcmci_finish_data(struct mxcmci_host *host, unsigned int stat) +{ + int data_error = 0; + + if (stat & STATUS_ERR_MASK) { + printf("request failed. status: 0x%08x\n", + stat); + if (stat & STATUS_CRC_READ_ERR) { + data_error = -EILSEQ; + } else if (stat & STATUS_CRC_WRITE_ERR) { + u32 err_code = (stat >> 9) & 0x3; + if (err_code == 2) /* No CRC response */ + data_error = TIMEOUT; + else + data_error = -EILSEQ; + } else if (stat & STATUS_TIME_OUT_READ) { + data_error = TIMEOUT; + } else { + data_error = -EIO; + } + } + + host->data = NULL; + + return data_error; +} + +static int mxcmci_read_response(struct mxcmci_host *host, unsigned int stat) +{ + struct mmc_cmd *cmd = host->cmd; + int i; + u32 a, b, c; + u32 *resp = (u32 *)cmd->response; + + if (!cmd) + return 0; + + if (stat & STATUS_TIME_OUT_RESP) { + printf("CMD TIMEOUT\n"); + return TIMEOUT; + } else if (stat & STATUS_RESP_CRC_ERR && cmd->resp_type & MMC_RSP_CRC) { + printf("cmd crc error\n"); + return -EILSEQ; + } + + if (cmd->resp_type & MMC_RSP_PRESENT) { + if (cmd->resp_type & MMC_RSP_136) { + for (i = 0; i < 4; i++) { + a = readl(&host->base->res_fifo) & 0xFFFF; + b = readl(&host->base->res_fifo) & 0xFFFF; + resp[i] = a << 16 | b; + } + } else { + a = readl(&host->base->res_fifo) & 0xFFFF; + b = readl(&host->base->res_fifo) & 0xFFFF; + c = readl(&host->base->res_fifo) & 0xFFFF; + resp[0] = a << 24 | b << 8 | c >> 8; + } + } + return 0; +} + +static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask) +{ + u32 stat; + unsigned long timeout = get_ticks() + CONFIG_SYS_HZ; + + do { + stat = readl(&host->base->status); + if (stat & STATUS_ERR_MASK) + return stat; + if (timeout < get_ticks()) + return STATUS_TIME_OUT_READ; + if (stat & mask) + return 0; + } while (1); +} + +static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes) +{ + unsigned int stat; + u32 *buf = _buf; + + while (bytes > 3) { + stat = mxcmci_poll_status(host, + STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE); + if (stat) + return stat; + *buf++ = readl(&host->base->buffer_access); + bytes -= 4; + } + + if (bytes) { + u8 *b = (u8 *)buf; + u32 tmp; + + stat = mxcmci_poll_status(host, + STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE); + if (stat) + return stat; + tmp = readl(&host->base->buffer_access); + memcpy(b, &tmp, bytes); + } + + return 0; +} + +static int mxcmci_push(struct mxcmci_host *host, const void *_buf, int bytes) +{ + unsigned int stat; + const u32 *buf = _buf; + + while (bytes > 3) { + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); + if (stat) + return stat; + writel(*buf++, &host->base->buffer_access); + bytes -= 4; + } + + if (bytes) { + const u8 *b = (u8 *)buf; + u32 tmp; + + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); + if (stat) + return stat; + + memcpy(&tmp, b, bytes); + writel(tmp, &host->base->buffer_access); + } + + stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); + if (stat) + return stat; + + return 0; +} + +static int mxcmci_transfer_data(struct mxcmci_host *host) +{ + struct mmc_data *data = host->data; + int stat; + unsigned long length; + + length = data->blocks * data->blocksize; + host->datasize = 0; + + if (data->flags & MMC_DATA_READ) { + stat = mxcmci_pull(host, data->dest, length); + if (stat) + return stat; + host->datasize += length; + } else { + stat = mxcmci_push(host, (const void *)(data->src), length); + if (stat) + return stat; + host->datasize += length; + stat = mxcmci_poll_status(host, STATUS_WRITE_OP_DONE); + if (stat) + return stat; + } + return 0; +} + +static int mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat) +{ + int datastat; + int ret; + + ret = mxcmci_read_response(host, stat); + + if (ret) { + mxcmci_finish_request(host, host->cmd, host->data); + return ret; + } + + if (!host->data) { + mxcmci_finish_request(host, host->cmd, host->data); + return 0; + } + + datastat = mxcmci_transfer_data(host); + ret = mxcmci_finish_data(host, datastat); + mxcmci_finish_request(host, host->cmd, host->data); + return ret; +} + +static int mxcmci_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct mxcmci_host *host = mmc->priv; + unsigned int cmdat = host->cmdat; + u32 stat; + int ret; + + host->cmdat &= ~CMD_DAT_CONT_INIT; + if (data) { + mxcmci_setup_data(host, data); + + cmdat |= CMD_DAT_CONT_DATA_ENABLE; + + if (data->flags & MMC_DATA_WRITE) + cmdat |= CMD_DAT_CONT_WRITE; + } + + if ((ret = mxcmci_start_cmd(host, cmd, cmdat))) { + mxcmci_finish_request(host, cmd, data); + return ret; + } + + do { + stat = readl(&host->base->status); + writel(stat, &host->base->status); + } while (!(stat & STATUS_END_CMD_RESP)); + + return mxcmci_cmd_done(host, stat); +} + +static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios) +{ + unsigned int divider; + int prescaler = 0; + unsigned long clk_in = imx_get_perclk2(); + + while (prescaler <= 0x800) { + for (divider = 1; divider <= 0xF; divider++) { + int x; + + x = (clk_in / (divider + 1)); + + if (prescaler) + x /= (prescaler * 2); + + if (x <= clk_ios) + break; + } + if (divider < 0x10) + break; + + if (prescaler == 0) + prescaler = 1; + else + prescaler <<= 1; + } + + writel((prescaler << 4) | divider, &host->base->clk_rate); +} + +static void mxcmci_set_ios(struct mmc *mmc) +{ + struct mxcmci_host *host = mmc->priv; + if (mmc->bus_width == 4) + host->cmdat |= CMD_DAT_CONT_BUS_WIDTH_4; + else + host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4; + + if (mmc->clock) { + mxcmci_set_clk_rate(host, mmc->clock); + writel(STR_STP_CLK_START_CLK, &host->base->str_stp_clk); + } else { + writel(STR_STP_CLK_STOP_CLK, &host->base->str_stp_clk); + } + + host->clock = mmc->clock; +} + +static int mxcmci_init(struct mmc *mmc) +{ + struct mxcmci_host *host = mmc->priv; + + mxcmci_softreset(host); + + host->rev_no = readl(&host->base->rev_no); + if (host->rev_no != 0x400) { + printf("wrong rev.no. 0x%08x. aborting.\n", + host->rev_no); + return -ENODEV; + } + + /* recommended in data sheet */ + writel(0x2db4, &host->base->read_to); + + writel(0, &host->base->int_cntr); + + return 0; +} + +static int mxcmci_initialize(bd_t *bis) +{ + struct mmc *mmc = NULL; + + mmc = malloc(sizeof(struct mmc)); + + if (!mmc) + return -ENOMEM; + + sprintf(mmc->name, "MXC MCI"); + mmc->send_cmd = mxcmci_request; + mmc->set_ios = mxcmci_set_ios; + mmc->init = mxcmci_init; + mmc->host_caps = MMC_MODE_4BIT; + + host->base = (struct mxcmci_regs *)CONFIG_MXC_MCI_REGS_BASE; + mmc->priv = host; + host->mmc = mmc; + + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + + mmc->f_min = imx_get_perclk2() >> 7; + mmc->f_max = imx_get_perclk2() >> 1; + + mmc_register(mmc); + + return 0; +} + +int mxc_mmc_init(bd_t *bis) +{ + return mxcmci_initialize(bis); +} diff --git a/roms/u-boot-sam460ex/drivers/mmc/omap3_mmc.c b/roms/u-boot-sam460ex/drivers/mmc/omap3_mmc.c new file mode 100644 index 000000000..96c0e653b --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mmc/omap3_mmc.c @@ -0,0 +1,534 @@ +/* + * (C) Copyright 2008 + * Texas Instruments, <www.ti.com> + * Syed Mohammed Khasim <khasim@ti.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's version 2 of + * the License. + * + * 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 <fat.h> +#include <mmc.h> +#include <part.h> +#include <i2c.h> +#include <twl4030.h> +#include <asm/io.h> +#include <asm/arch/mmc.h> + +const unsigned short mmc_transspeed_val[15][4] = { + {CLKD(10, 1), CLKD(10, 10), CLKD(10, 100), CLKD(10, 1000)}, + {CLKD(12, 1), CLKD(12, 10), CLKD(12, 100), CLKD(12, 1000)}, + {CLKD(13, 1), CLKD(13, 10), CLKD(13, 100), CLKD(13, 1000)}, + {CLKD(15, 1), CLKD(15, 10), CLKD(15, 100), CLKD(15, 1000)}, + {CLKD(20, 1), CLKD(20, 10), CLKD(20, 100), CLKD(20, 1000)}, + {CLKD(26, 1), CLKD(26, 10), CLKD(26, 100), CLKD(26, 1000)}, + {CLKD(30, 1), CLKD(30, 10), CLKD(30, 100), CLKD(30, 1000)}, + {CLKD(35, 1), CLKD(35, 10), CLKD(35, 100), CLKD(35, 1000)}, + {CLKD(40, 1), CLKD(40, 10), CLKD(40, 100), CLKD(40, 1000)}, + {CLKD(45, 1), CLKD(45, 10), CLKD(45, 100), CLKD(45, 1000)}, + {CLKD(52, 1), CLKD(52, 10), CLKD(52, 100), CLKD(52, 1000)}, + {CLKD(55, 1), CLKD(55, 10), CLKD(55, 100), CLKD(55, 1000)}, + {CLKD(60, 1), CLKD(60, 10), CLKD(60, 100), CLKD(60, 1000)}, + {CLKD(70, 1), CLKD(70, 10), CLKD(70, 100), CLKD(70, 1000)}, + {CLKD(80, 1), CLKD(80, 10), CLKD(80, 100), CLKD(80, 1000)} +}; + +mmc_card_data cur_card_data; +static block_dev_desc_t mmc_blk_dev; +static hsmmc_t *mmc_base = (hsmmc_t *)OMAP_HSMMC_BASE; + +block_dev_desc_t *mmc_get_dev(int dev) +{ + return (block_dev_desc_t *) &mmc_blk_dev; +} + +unsigned char mmc_board_init(void) +{ + t2_t *t2_base = (t2_t *)T2_BASE; + +#if defined(CONFIG_TWL4030_POWER) + twl4030_power_mmc_init(); +#endif + + writel(readl(&t2_base->pbias_lite) | PBIASLITEPWRDNZ1 | + PBIASSPEEDCTRL0 | PBIASLITEPWRDNZ0, + &t2_base->pbias_lite); + + writel(readl(&t2_base->devconf0) | MMCSDIO1ADPCLKISEL, + &t2_base->devconf0); + + return 1; +} + +void mmc_init_stream(void) +{ + writel(readl(&mmc_base->con) | INIT_INITSTREAM, &mmc_base->con); + + writel(MMC_CMD0, &mmc_base->cmd); + while (!(readl(&mmc_base->stat) & CC_MASK)); + + writel(CC_MASK, &mmc_base->stat); + + writel(MMC_CMD0, &mmc_base->cmd); + while (!(readl(&mmc_base->stat) & CC_MASK)); + + writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con); +} + +unsigned char mmc_clock_config(unsigned int iclk, unsigned short clk_div) +{ + unsigned int val; + + mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK), + (ICE_STOP | DTO_15THDTO | CEN_DISABLE)); + + switch (iclk) { + case CLK_INITSEQ: + val = MMC_INIT_SEQ_CLK / 2; + break; + case CLK_400KHZ: + val = MMC_400kHz_CLK; + break; + case CLK_MISC: + val = clk_div; + break; + default: + return 0; + } + mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK, + (val << CLKD_OFFSET) | ICE_OSCILLATE); + + while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY); + + writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl); + return 1; +} + +unsigned char mmc_init_setup(void) +{ + unsigned int reg_val; + + mmc_board_init(); + + writel(readl(&mmc_base->sysconfig) | MMC_SOFTRESET, + &mmc_base->sysconfig); + while ((readl(&mmc_base->sysstatus) & RESETDONE) == 0); + + writel(readl(&mmc_base->sysctl) | SOFTRESETALL, &mmc_base->sysctl); + while ((readl(&mmc_base->sysctl) & SOFTRESETALL) != 0x0); + + writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl); + writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP, + &mmc_base->capa); + + reg_val = readl(&mmc_base->con) & RESERVED_MASK; + + writel(CTPL_MMC_SD | reg_val | WPP_ACTIVEHIGH | CDP_ACTIVEHIGH | + MIT_CTO | DW8_1_4BITMODE | MODE_FUNC | STR_BLOCK | + HR_NOHOSTRESP | INIT_NOINIT | NOOPENDRAIN, &mmc_base->con); + + mmc_clock_config(CLK_INITSEQ, 0); + writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl); + + writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE | + IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC, + &mmc_base->ie); + + mmc_init_stream(); + return 1; +} + +unsigned char mmc_send_cmd(unsigned int cmd, unsigned int arg, + unsigned int *response) +{ + unsigned int mmc_stat; + + while ((readl(&mmc_base->pstate) & DATI_MASK) == DATI_CMDDIS); + + writel(BLEN_512BYTESLEN | NBLK_STPCNT, &mmc_base->blk); + writel(0xFFFFFFFF, &mmc_base->stat); + writel(arg, &mmc_base->arg); + writel(cmd | CMD_TYPE_NORMAL | CICE_NOCHECK | CCCE_NOCHECK | + MSBS_SGLEBLK | ACEN_DISABLE | BCE_DISABLE | DE_DISABLE, + &mmc_base->cmd); + + while (1) { + do { + mmc_stat = readl(&mmc_base->stat); + } while (mmc_stat == 0); + + if ((mmc_stat & ERRI_MASK) != 0) + return (unsigned char) mmc_stat; + + if (mmc_stat & CC_MASK) { + writel(CC_MASK, &mmc_base->stat); + response[0] = readl(&mmc_base->rsp10); + if ((cmd & RSP_TYPE_MASK) == RSP_TYPE_LGHT136) { + response[1] = readl(&mmc_base->rsp32); + response[2] = readl(&mmc_base->rsp54); + response[3] = readl(&mmc_base->rsp76); + } + break; + } + } + return 1; +} + +unsigned char mmc_read_data(unsigned int *output_buf) +{ + unsigned int mmc_stat; + unsigned int read_count = 0; + + /* + * Start Polled Read + */ + while (1) { + do { + mmc_stat = readl(&mmc_base->stat); + } while (mmc_stat == 0); + + if ((mmc_stat & ERRI_MASK) != 0) + return (unsigned char) mmc_stat; + + if (mmc_stat & BRR_MASK) { + unsigned int k; + + writel(readl(&mmc_base->stat) | BRR_MASK, + &mmc_base->stat); + for (k = 0; k < MMCSD_SECTOR_SIZE / 4; k++) { + *output_buf = readl(&mmc_base->data); + output_buf++; + read_count += 4; + } + } + + if (mmc_stat & BWR_MASK) + writel(readl(&mmc_base->stat) | BWR_MASK, + &mmc_base->stat); + + if (mmc_stat & TC_MASK) { + writel(readl(&mmc_base->stat) | TC_MASK, + &mmc_base->stat); + break; + } + } + return 1; +} + +unsigned char mmc_detect_card(mmc_card_data *mmc_card_cur) +{ + unsigned char err; + unsigned int argument = 0; + unsigned int ocr_value, ocr_recvd, ret_cmd41, hcs_val; + unsigned short retry_cnt = 2000; + mmc_resp_t mmc_resp; + + /* Set to Initialization Clock */ + err = mmc_clock_config(CLK_400KHZ, 0); + if (err != 1) + return err; + + mmc_card_cur->RCA = MMC_RELATIVE_CARD_ADDRESS; + argument = 0x00000000; + + ocr_value = (0x1FF << 15); + err = mmc_send_cmd(MMC_CMD0, argument, mmc_resp.resp); + if (err != 1) + return err; + + argument = SD_CMD8_CHECK_PATTERN | SD_CMD8_2_7_3_6_V_RANGE; + err = mmc_send_cmd(MMC_SDCMD8, argument, mmc_resp.resp); + hcs_val = (err == 1) ? + MMC_OCR_REG_HOST_CAPACITY_SUPPORT_SECTOR : + MMC_OCR_REG_HOST_CAPACITY_SUPPORT_BYTE; + + argument = 0x0000 << 16; + err = mmc_send_cmd(MMC_CMD55, argument, mmc_resp.resp); + if (err == 1) { + mmc_card_cur->card_type = SD_CARD; + ocr_value |= hcs_val; + ret_cmd41 = MMC_ACMD41; + } else { + mmc_card_cur->card_type = MMC_CARD; + ocr_value |= MMC_OCR_REG_ACCESS_MODE_SECTOR; + ret_cmd41 = MMC_CMD1; + writel(readl(&mmc_base->con) & ~OD, &mmc_base->con); + writel(readl(&mmc_base->con) | OPENDRAIN, &mmc_base->con); + } + + argument = ocr_value; + err = mmc_send_cmd(ret_cmd41, argument, mmc_resp.resp); + if (err != 1) + return err; + + ocr_recvd = mmc_resp.r3.ocr; + + while (!(ocr_recvd & (0x1 << 31)) && (retry_cnt > 0)) { + retry_cnt--; + if (mmc_card_cur->card_type == SD_CARD) { + argument = 0x0000 << 16; + err = mmc_send_cmd(MMC_CMD55, argument, mmc_resp.resp); + } + + argument = ocr_value; + err = mmc_send_cmd(ret_cmd41, argument, mmc_resp.resp); + if (err != 1) + return err; + ocr_recvd = mmc_resp.r3.ocr; + } + + if (!(ocr_recvd & (0x1 << 31))) + return 0; + + if (mmc_card_cur->card_type == MMC_CARD) { + if ((ocr_recvd & MMC_OCR_REG_ACCESS_MODE_MASK) == + MMC_OCR_REG_ACCESS_MODE_SECTOR) { + mmc_card_cur->mode = SECTOR_MODE; + } else { + mmc_card_cur->mode = BYTE_MODE; + } + + ocr_recvd &= ~MMC_OCR_REG_ACCESS_MODE_MASK; + } else { + if ((ocr_recvd & MMC_OCR_REG_HOST_CAPACITY_SUPPORT_MASK) + == MMC_OCR_REG_HOST_CAPACITY_SUPPORT_SECTOR) { + mmc_card_cur->mode = SECTOR_MODE; + } else { + mmc_card_cur->mode = BYTE_MODE; + } + ocr_recvd &= ~MMC_OCR_REG_HOST_CAPACITY_SUPPORT_MASK; + } + + ocr_recvd &= ~(0x1 << 31); + if (!(ocr_recvd & ocr_value)) + return 0; + + err = mmc_send_cmd(MMC_CMD2, argument, mmc_resp.resp); + if (err != 1) + return err; + + if (mmc_card_cur->card_type == MMC_CARD) { + argument = mmc_card_cur->RCA << 16; + err = mmc_send_cmd(MMC_CMD3, argument, mmc_resp.resp); + if (err != 1) + return err; + } else { + argument = 0x00000000; + err = mmc_send_cmd(MMC_SDCMD3, argument, mmc_resp.resp); + if (err != 1) + return err; + + mmc_card_cur->RCA = mmc_resp.r6.newpublishedrca; + } + + writel(readl(&mmc_base->con) & ~OD, &mmc_base->con); + writel(readl(&mmc_base->con) | NOOPENDRAIN, &mmc_base->con); + return 1; +} + +unsigned char mmc_read_cardsize(mmc_card_data *mmc_dev_data, + mmc_csd_reg_t *cur_csd) +{ + mmc_extended_csd_reg_t ext_csd; + unsigned int size, count, blk_len, blk_no, card_size, argument; + unsigned char err; + unsigned int resp[4]; + + if (mmc_dev_data->mode == SECTOR_MODE) { + if (mmc_dev_data->card_type == SD_CARD) { + card_size = + (((mmc_sd2_csd_reg_t *) cur_csd)-> + c_size_lsb & MMC_SD2_CSD_C_SIZE_LSB_MASK) | + ((((mmc_sd2_csd_reg_t *) cur_csd)-> + c_size_msb & MMC_SD2_CSD_C_SIZE_MSB_MASK) + << MMC_SD2_CSD_C_SIZE_MSB_OFFSET); + mmc_dev_data->size = card_size * 1024; + if (mmc_dev_data->size == 0) + return 0; + } else { + argument = 0x00000000; + err = mmc_send_cmd(MMC_CMD8, argument, resp); + if (err != 1) + return err; + err = mmc_read_data((unsigned int *) &ext_csd); + if (err != 1) + return err; + mmc_dev_data->size = ext_csd.sectorcount; + + if (mmc_dev_data->size == 0) + mmc_dev_data->size = 8388608; + } + } else { + if (cur_csd->c_size_mult >= 8) + return 0; + + if (cur_csd->read_bl_len >= 12) + return 0; + + /* Compute size */ + count = 1 << (cur_csd->c_size_mult + 2); + card_size = (cur_csd->c_size_lsb & MMC_CSD_C_SIZE_LSB_MASK) | + ((cur_csd->c_size_msb & MMC_CSD_C_SIZE_MSB_MASK) + << MMC_CSD_C_SIZE_MSB_OFFSET); + blk_no = (card_size + 1) * count; + blk_len = 1 << cur_csd->read_bl_len; + size = blk_no * blk_len; + mmc_dev_data->size = size / MMCSD_SECTOR_SIZE; + if (mmc_dev_data->size == 0) + return 0; + } + return 1; +} + +unsigned char omap_mmc_read_sect(unsigned int start_sec, unsigned int num_bytes, + mmc_card_data *mmc_c, + unsigned long *output_buf) +{ + unsigned char err; + unsigned int argument; + unsigned int resp[4]; + unsigned int num_sec_val = + (num_bytes + (MMCSD_SECTOR_SIZE - 1)) / MMCSD_SECTOR_SIZE; + unsigned int sec_inc_val; + + if (num_sec_val == 0) + return 1; + + if (mmc_c->mode == SECTOR_MODE) { + argument = start_sec; + sec_inc_val = 1; + } else { + argument = start_sec * MMCSD_SECTOR_SIZE; + sec_inc_val = MMCSD_SECTOR_SIZE; + } + + while (num_sec_val) { + err = mmc_send_cmd(MMC_CMD17, argument, resp); + if (err != 1) + return err; + + err = mmc_read_data((unsigned int *) output_buf); + if (err != 1) + return err; + + output_buf += (MMCSD_SECTOR_SIZE / 4); + argument += sec_inc_val; + num_sec_val--; + } + return 1; +} + +unsigned char configure_mmc(mmc_card_data *mmc_card_cur) +{ + unsigned char ret_val; + unsigned int argument; + unsigned int trans_clk, trans_fact, trans_unit, retries = 2; + unsigned char trans_speed; + mmc_resp_t mmc_resp; + + ret_val = mmc_init_setup(); + + if (ret_val != 1) + return ret_val; + + do { + ret_val = mmc_detect_card(mmc_card_cur); + retries--; + } while ((retries > 0) && (ret_val != 1)); + + argument = mmc_card_cur->RCA << 16; + ret_val = mmc_send_cmd(MMC_CMD9, argument, mmc_resp.resp); + if (ret_val != 1) + return ret_val; + + if (mmc_card_cur->card_type == MMC_CARD) + mmc_card_cur->version = mmc_resp.Card_CSD.spec_vers; + + trans_speed = mmc_resp.Card_CSD.tran_speed; + + ret_val = mmc_send_cmd(MMC_CMD4, MMC_DSR_DEFAULT << 16, mmc_resp.resp); + if (ret_val != 1) + return ret_val; + + trans_unit = trans_speed & MMC_CSD_TRAN_SPEED_UNIT_MASK; + trans_fact = trans_speed & MMC_CSD_TRAN_SPEED_FACTOR_MASK; + + if (trans_unit > MMC_CSD_TRAN_SPEED_UNIT_100MHZ) + return 0; + + if ((trans_fact < MMC_CSD_TRAN_SPEED_FACTOR_1_0) || + (trans_fact > MMC_CSD_TRAN_SPEED_FACTOR_8_0)) + return 0; + + trans_unit >>= 0; + trans_fact >>= 3; + + trans_clk = mmc_transspeed_val[trans_fact - 1][trans_unit] * 2; + ret_val = mmc_clock_config(CLK_MISC, trans_clk); + + if (ret_val != 1) + return ret_val; + + argument = mmc_card_cur->RCA << 16; + ret_val = mmc_send_cmd(MMC_CMD7_SELECT, argument, mmc_resp.resp); + if (ret_val != 1) + return ret_val; + + /* Configure the block length to 512 bytes */ + argument = MMCSD_SECTOR_SIZE; + ret_val = mmc_send_cmd(MMC_CMD16, argument, mmc_resp.resp); + if (ret_val != 1) + return ret_val; + + /* get the card size in sectors */ + ret_val = mmc_read_cardsize(mmc_card_cur, &mmc_resp.Card_CSD); + if (ret_val != 1) + return ret_val; + + return 1; +} +unsigned long mmc_bread(int dev_num, unsigned long blknr, lbaint_t blkcnt, + void *dst) +{ + omap_mmc_read_sect(blknr, (blkcnt * MMCSD_SECTOR_SIZE), &cur_card_data, + (unsigned long *) dst); + return 1; +} + +int mmc_legacy_init(int verbose) +{ + if (configure_mmc(&cur_card_data) != 1) + return 1; + + mmc_blk_dev.if_type = IF_TYPE_MMC; + mmc_blk_dev.part_type = PART_TYPE_DOS; + mmc_blk_dev.dev = 0; + mmc_blk_dev.lun = 0; + mmc_blk_dev.type = 0; + + /* FIXME fill in the correct size (is set to 32MByte) */ + mmc_blk_dev.blksz = MMCSD_SECTOR_SIZE; + mmc_blk_dev.lba = 0x10000; + mmc_blk_dev.removable = 0; + mmc_blk_dev.block_read = mmc_bread; + + fat_register_device(&mmc_blk_dev, 1); + return 0; +} diff --git a/roms/u-boot-sam460ex/drivers/mmc/pxa_mmc.c b/roms/u-boot-sam460ex/drivers/mmc/pxa_mmc.c new file mode 100644 index 000000000..8225235bf --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mmc/pxa_mmc.c @@ -0,0 +1,646 @@ +/* + * (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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <common.h> +#include <mmc.h> +#include <asm/errno.h> +#include <asm/arch/hardware.h> +#include <part.h> + +#include "pxa_mmc.h" + +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 uchar spec_ver; +static int mmc_ready = 0; +static int wide = 0; + +static uint32_t * +/****************************************************/ +mmc_cmd(ushort cmd, ushort argh, ushort argl, ushort cmdat) +/****************************************************/ +{ + static uint32_t resp[4], a, b, c; + ulong status; + int i; + + debug("mmc_cmd %u 0x%04x 0x%04x 0x%04x\n", cmd, argh, argl, + cmdat | wide); + MMC_STRPCL = MMC_STRPCL_STOP_CLK; + MMC_I_MASK = ~MMC_I_MASK_CLK_IS_OFF; + while (!(MMC_I_REG & MMC_I_REG_CLK_IS_OFF)) ; + MMC_CMD = cmd; + MMC_ARGH = argh; + MMC_ARGL = argl; + MMC_CMDAT = cmdat | wide; + MMC_I_MASK = ~MMC_I_MASK_END_CMD_RES; + MMC_STRPCL = MMC_STRPCL_START_CLK; + while (!(MMC_I_REG & MMC_I_REG_END_CMD_RES)) ; + + status = MMC_STAT; + debug("MMC status 0x%08x\n", status); + if (status & MMC_STAT_TIME_OUT_RESPONSE) { + return 0; + } + + /* Linux says: + * Did I mention this is Sick. We always need to + * discard the upper 8 bits of the first 16-bit word. + */ + a = (MMC_RES & 0xffff); + for (i = 0; i < 4; i++) { + b = (MMC_RES & 0xffff); + c = (MMC_RES & 0xffff); + resp[i] = (a << 24) | (b << 8) | (c >> 8); + a = c; + debug("MMC resp[%d] = %#08x\n", i, resp[i]); + } + + return resp; +} + +int +/****************************************************/ +mmc_block_read(uchar * dst, ulong src, ulong len) +/****************************************************/ +{ + ushort argh, argl; + ulong status; + + if (len == 0) { + return 0; + } + + debug("mmc_block_rd dst %lx src %lx len %d\n", (ulong) dst, src, len); + + argh = len >> 16; + argl = len & 0xffff; + + /* set block len */ + mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1); + + /* send read command */ + argh = src >> 16; + argl = src & 0xffff; + MMC_STRPCL = MMC_STRPCL_STOP_CLK; + MMC_RDTO = 0xffff; + MMC_NOB = 1; + MMC_BLKLEN = len; + mmc_cmd(MMC_CMD_READ_SINGLE_BLOCK, argh, argl, + MMC_CMDAT_R1 | MMC_CMDAT_READ | MMC_CMDAT_BLOCK | + MMC_CMDAT_DATA_EN); + + MMC_I_MASK = ~MMC_I_MASK_RXFIFO_RD_REQ; + while (len) { + if (MMC_I_REG & MMC_I_REG_RXFIFO_RD_REQ) { +#ifdef CONFIG_PXA27X + int i; + for (i = min(len, 32); i; i--) { + *dst++ = *((volatile uchar *)&MMC_RXFIFO); + len--; + } +#else + *dst++ = MMC_RXFIFO; + len--; +#endif + } + status = MMC_STAT; + if (status & MMC_STAT_ERRORS) { + printf("MMC_STAT error %lx\n", status); + return -1; + } + } + MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE; + while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE)) ; + status = MMC_STAT; + if (status & MMC_STAT_ERRORS) { + printf("MMC_STAT error %lx\n", status); + return -1; + } + return 0; +} + +int +/****************************************************/ +mmc_block_write(ulong dst, uchar * src, int len) +/****************************************************/ +{ + ushort argh, argl; + ulong status; + + if (len == 0) { + return 0; + } + + debug("mmc_block_wr dst %lx src %lx len %d\n", dst, (ulong) src, len); + + argh = len >> 16; + argl = len & 0xffff; + + /* set block len */ + mmc_cmd(MMC_CMD_SET_BLOCKLEN, argh, argl, MMC_CMDAT_R1); + + /* send write command */ + argh = dst >> 16; + argl = dst & 0xffff; + MMC_STRPCL = MMC_STRPCL_STOP_CLK; + MMC_NOB = 1; + MMC_BLKLEN = len; + mmc_cmd(MMC_CMD_WRITE_SINGLE_BLOCK, argh, argl, + MMC_CMDAT_R1 | MMC_CMDAT_WRITE | MMC_CMDAT_BLOCK | + MMC_CMDAT_DATA_EN); + + MMC_I_MASK = ~MMC_I_MASK_TXFIFO_WR_REQ; + while (len) { + if (MMC_I_REG & MMC_I_REG_TXFIFO_WR_REQ) { + int i, bytes = min(32, len); + + for (i = 0; i < bytes; i++) { + MMC_TXFIFO = *src++; + } + if (bytes < 32) { + MMC_PRTBUF = MMC_PRTBUF_BUF_PART_FULL; + } + len -= bytes; + } + status = MMC_STAT; + if (status & MMC_STAT_ERRORS) { + printf("MMC_STAT error %lx\n", status); + return -1; + } + } + MMC_I_MASK = ~MMC_I_MASK_DATA_TRAN_DONE; + while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE)) ; + MMC_I_MASK = ~MMC_I_MASK_PRG_DONE; + while (!(MMC_I_REG & MMC_I_REG_PRG_DONE)) ; + status = MMC_STAT; + if (status & MMC_STAT_ERRORS) { + printf("MMC_STAT error %lx\n", status); + return -1; + } + return 0; +} + +int +/****************************************************/ +pxa_mmc_read(long 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); + + src -= CONFIG_SYS_MMC_BASE; + 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", + 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", + src, (ulong) dst, end, part_start, part_end, aligned_start, + aligned_end); + if ((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", + src, (ulong) dst, end, part_start, part_end, aligned_start, + aligned_end); + for (; src < 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", + src, (ulong) dst, end, part_start, part_end, aligned_start, + aligned_end); + if ((mmc_block_read((uchar *) (dst), src, mmc_block_size)) < 0) { + return -1; + } + } + debug + ("src %lx dst %lx end %lx pstart %lx pend %lx astart %lx aend %lx\n", + 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", + src, (ulong) dst, end, part_start, part_end, aligned_start, + aligned_end); + if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) { + return -1; + } + memcpy(dst, mmc_buf, part_end); + } + return 0; +} + +int +/****************************************************/ +pxa_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); + + dst -= CONFIG_SYS_MMC_BASE; + 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", + 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 ((mmc_block_read(mmc_buf, aligned_start, mmc_block_size)) < + 0) { + return -1; + } + memcpy(mmc_buf + part_start, src, part_len); + if ((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", + 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", + src, (ulong) dst, end, part_start, part_end, aligned_start, + aligned_end); + if ((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", + 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", + src, (ulong) dst, end, part_start, part_end, aligned_start, + aligned_end); + if ((mmc_block_read(mmc_buf, aligned_end, mmc_block_size)) < 0) { + return -1; + } + memcpy(mmc_buf, src, part_end); + if ((mmc_block_write(aligned_end, mmc_buf, mmc_block_size)) < 0) { + return -1; + } + } + return 0; +} + +static ulong +/****************************************************/ +mmc_bread(int dev_num, ulong blknr, lbaint_t blkcnt, void *dst) +/****************************************************/ +{ + int mmc_block_size = MMC_BLOCK_SIZE; + ulong src = blknr * mmc_block_size + CONFIG_SYS_MMC_BASE; + + pxa_mmc_read(src, (uchar *) dst, blkcnt * mmc_block_size); + return blkcnt; +} + +#ifdef __GNUC__ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#define UNSTUFF_BITS(resp,start,size) \ + ({ \ + const int __size = size; \ + const uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1; \ + const int32_t __off = 3 - ((start) / 32); \ + const int32_t __shft = (start) & 31; \ + uint32_t __res; \ + \ + __res = resp[__off] >> __shft; \ + if (__size + __shft > 32) \ + __res |= resp[__off-1] << ((32 - __shft) % 32); \ + __res & __mask; \ + }) + +/* + * Given the decoded CSD structure, decode the raw CID to our CID structure. + */ +static void mmc_decode_cid(uint32_t * resp) +{ + if (IF_TYPE_SD == mmc_dev.if_type) { + /* + * SD doesn't currently have a version field so we will + * have to assume we can parse this. + */ + sprintf((char *)mmc_dev.vendor, + "Man %02x OEM %c%c \"%c%c%c%c%c\" Date %02u/%04u", + UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp, 112, 8), + UNSTUFF_BITS(resp, 104, 8), UNSTUFF_BITS(resp, 96, 8), + UNSTUFF_BITS(resp, 88, 8), UNSTUFF_BITS(resp, 80, 8), + UNSTUFF_BITS(resp, 72, 8), UNSTUFF_BITS(resp, 64, 8), + UNSTUFF_BITS(resp, 8, 4), UNSTUFF_BITS(resp, 12, + 8) + 2000); + sprintf((char *)mmc_dev.revision, "%d.%d", + UNSTUFF_BITS(resp, 60, 4), UNSTUFF_BITS(resp, 56, 4)); + sprintf((char *)mmc_dev.product, "%u", + UNSTUFF_BITS(resp, 24, 32)); + } else { + /* + * The selection of the format here is based upon published + * specs from sandisk and from what people have reported. + */ + switch (spec_ver) { + case 0: /* MMC v1.0 - v1.2 */ + case 1: /* MMC v1.4 */ + sprintf((char *)mmc_dev.vendor, + "Man %02x%02x%02x \"%c%c%c%c%c%c%c\" Date %02u/%04u", + UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp, + 112, + 8), + UNSTUFF_BITS(resp, 104, 8), UNSTUFF_BITS(resp, + 96, 8), + UNSTUFF_BITS(resp, 88, 8), UNSTUFF_BITS(resp, + 80, 8), + UNSTUFF_BITS(resp, 72, 8), UNSTUFF_BITS(resp, + 64, 8), + UNSTUFF_BITS(resp, 56, 8), UNSTUFF_BITS(resp, + 48, 8), + UNSTUFF_BITS(resp, 12, 4), UNSTUFF_BITS(resp, 8, + 4) + + 1997); + sprintf((char *)mmc_dev.revision, "%d.%d", + UNSTUFF_BITS(resp, 44, 4), UNSTUFF_BITS(resp, + 40, 4)); + sprintf((char *)mmc_dev.product, "%u", + UNSTUFF_BITS(resp, 16, 24)); + break; + + case 2: /* MMC v2.0 - v2.2 */ + case 3: /* MMC v3.1 - v3.3 */ + case 4: /* MMC v4 */ + sprintf((char *)mmc_dev.vendor, + "Man %02x OEM %04x \"%c%c%c%c%c%c\" Date %02u/%04u", + UNSTUFF_BITS(resp, 120, 8), UNSTUFF_BITS(resp, + 104, + 16), + UNSTUFF_BITS(resp, 96, 8), UNSTUFF_BITS(resp, + 88, 8), + UNSTUFF_BITS(resp, 80, 8), UNSTUFF_BITS(resp, + 72, 8), + UNSTUFF_BITS(resp, 64, 8), UNSTUFF_BITS(resp, + 56, 8), + UNSTUFF_BITS(resp, 12, 4), UNSTUFF_BITS(resp, 8, + 4) + + 1997); + sprintf((char *)mmc_dev.product, "%u", + UNSTUFF_BITS(resp, 16, 32)); + sprintf((char *)mmc_dev.revision, "N/A"); + break; + + default: + printf("MMC card has unknown MMCA version %d\n", + spec_ver); + break; + } + } + printf("%s card.\nVendor: %s\nProduct: %s\nRevision: %s\n", + (IF_TYPE_SD == mmc_dev.if_type) ? "SD" : "MMC", mmc_dev.vendor, + mmc_dev.product, mmc_dev.revision); +} + +/* + * Given a 128-bit response, decode to our card CSD structure. + */ +static void mmc_decode_csd(uint32_t * resp) +{ + unsigned int mult, csd_struct; + + if (IF_TYPE_SD == mmc_dev.if_type) { + csd_struct = UNSTUFF_BITS(resp, 126, 2); + if (csd_struct != 0) { + printf("SD: unrecognised CSD structure version %d\n", + csd_struct); + return; + } + } else { + /* + * We only understand CSD structure v1.1 and v1.2. + * v1.2 has extra information in bits 15, 11 and 10. + */ + csd_struct = UNSTUFF_BITS(resp, 126, 2); + if (csd_struct != 1 && csd_struct != 2) { + printf("MMC: unrecognised CSD structure version %d\n", + csd_struct); + return; + } + + spec_ver = UNSTUFF_BITS(resp, 122, 4); + mmc_dev.if_type = IF_TYPE_MMC; + } + + mult = 1 << (UNSTUFF_BITS(resp, 47, 3) + 2); + mmc_dev.lba = (1 + UNSTUFF_BITS(resp, 62, 12)) * mult; + mmc_dev.blksz = 1 << UNSTUFF_BITS(resp, 80, 4); + + /* FIXME: The following just makes assumes that's the partition type -- should really read it */ + mmc_dev.part_type = PART_TYPE_DOS; + mmc_dev.dev = 0; + mmc_dev.lun = 0; + mmc_dev.type = DEV_TYPE_HARDDISK; + mmc_dev.removable = 0; + mmc_dev.block_read = mmc_bread; + + printf("Detected: %lu blocks of %lu bytes (%luMB) ", + mmc_dev.lba, + mmc_dev.blksz, + mmc_dev.lba * mmc_dev.blksz / (1024 * 1024)); +} + +int +/****************************************************/ +mmc_legacy_init(int verbose) +/****************************************************/ +{ + int retries, rc = -ENODEV; + uint32_t cid_resp[4]; + uint32_t *resp; + uint16_t rca = 0; + + /* Reset device interface type */ + mmc_dev.if_type = IF_TYPE_UNKNOWN; + +#if defined (CONFIG_LUBBOCK) || (defined (CONFIG_GUMSTIX) && !defined(CONFIG_PXA27X)) + set_GPIO_mode(GPIO6_MMCCLK_MD); + set_GPIO_mode(GPIO8_MMCCS0_MD); +#endif + CKEN |= CKEN12_MMC; /* enable MMC unit clock */ + + MMC_CLKRT = MMC_CLKRT_0_3125MHZ; + MMC_RESTO = MMC_RES_TO_MAX; + MMC_SPI = MMC_SPI_DISABLE; + + /* reset */ + mmc_cmd(MMC_CMD_GO_IDLE_STATE, 0, 0, MMC_CMDAT_INIT | MMC_CMDAT_R0); + udelay(200000); + retries = 3; + while (retries--) { + resp = mmc_cmd(MMC_CMD_APP_CMD, 0, 0, MMC_CMDAT_R1); + if (!(resp[0] & 0x00000020)) { /* Card does not support APP_CMD */ + debug("Card does not support APP_CMD\n"); + break; + } + + /* Select 3.2-3.3V and 3.3-3.4V */ + resp = mmc_cmd(SD_CMD_APP_SEND_OP_COND, 0x0030, 0x0000, + MMC_CMDAT_R3 | (retries < 2 ? 0 + : MMC_CMDAT_INIT)); + if (resp[0] & 0x80000000) { + mmc_dev.if_type = IF_TYPE_SD; + debug("Detected SD card\n"); + break; + } +#ifdef CONFIG_PXA27X + udelay(10000); +#else + udelay(200000); +#endif + } + + if (retries <= 0 || !(IF_TYPE_SD == mmc_dev.if_type)) { + debug("Failed to detect SD Card, trying MMC\n"); + resp = + mmc_cmd(MMC_CMD_SEND_OP_COND, 0x00ff, 0x8000, MMC_CMDAT_R3); + + retries = 10; + while (retries-- && resp && !(resp[0] & 0x80000000)) { +#ifdef CONFIG_PXA27X + udelay(10000); +#else + udelay(200000); +#endif + resp = + mmc_cmd(MMC_CMD_SEND_OP_COND, 0x00ff, 0x8000, + MMC_CMDAT_R3); + } + } + + /* try to get card id */ + resp = + mmc_cmd(MMC_CMD_ALL_SEND_CID, 0, 0, MMC_CMDAT_R2 | MMC_CMDAT_BUSY); + if (resp) { + memcpy(cid_resp, resp, sizeof(cid_resp)); + + /* MMC exists, get CSD too */ + resp = mmc_cmd(MMC_CMD_SET_RELATIVE_ADDR, 0, 0, MMC_CMDAT_R1); + if (IF_TYPE_SD == mmc_dev.if_type) + rca = ((resp[0] & 0xffff0000) >> 16); + resp = mmc_cmd(MMC_CMD_SEND_CSD, rca, 0, MMC_CMDAT_R2); + if (resp) { + mmc_decode_csd(resp); + rc = 0; + mmc_ready = 1; + } + + mmc_decode_cid(cid_resp); + } + + MMC_CLKRT = 0; /* 20 MHz */ + resp = mmc_cmd(MMC_CMD_SELECT_CARD, rca, 0, MMC_CMDAT_R1); + +#ifdef CONFIG_PXA27X + if (IF_TYPE_SD == mmc_dev.if_type) { + resp = mmc_cmd(MMC_CMD_APP_CMD, rca, 0, MMC_CMDAT_R1); + resp = mmc_cmd(SD_CMD_APP_SET_BUS_WIDTH, 0, 2, MMC_CMDAT_R1); + wide = MMC_CMDAT_SD_4DAT; + } +#endif + + fat_register_device(&mmc_dev, 1); /* partitions start counting with 1 */ + + return rc; +} diff --git a/roms/u-boot-sam460ex/drivers/mmc/pxa_mmc.h b/roms/u-boot-sam460ex/drivers/mmc/pxa_mmc.h new file mode 100644 index 000000000..6fa42686e --- /dev/null +++ b/roms/u-boot-sam460ex/drivers/mmc/pxa_mmc.h @@ -0,0 +1,138 @@ +/* + * linux/drivers/mmc/mmc_pxa.h + * + * Author: Vladimir Shebordaev, Igor Oblakov + * Copyright: MontaVista Software Inc. + * + * $Id: mmc_pxa.h,v 0.3.1.6 2002/09/25 19:25:48 ted Exp ted $ + * + * 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 __MMC_PXA_P_H__ +#define __MMC_PXA_P_H__ + +/* PXA-250 MMC controller registers */ + +/* MMC_STRPCL */ +#define MMC_STRPCL_STOP_CLK (0x0001UL) +#define MMC_STRPCL_START_CLK (0x0002UL) + +/* MMC_STAT */ +#define MMC_STAT_END_CMD_RES (0x0001UL << 13) +#define MMC_STAT_PRG_DONE (0x0001UL << 12) +#define MMC_STAT_DATA_TRAN_DONE (0x0001UL << 11) +#define MMC_STAT_CLK_EN (0x0001UL << 8) +#define MMC_STAT_RECV_FIFO_FULL (0x0001UL << 7) +#define MMC_STAT_XMIT_FIFO_EMPTY (0x0001UL << 6) +#define MMC_STAT_RES_CRC_ERROR (0x0001UL << 5) +#define MMC_STAT_SPI_READ_ERROR_TOKEN (0x0001UL << 4) +#define MMC_STAT_CRC_READ_ERROR (0x0001UL << 3) +#define MMC_STAT_CRC_WRITE_ERROR (0x0001UL << 2) +#define MMC_STAT_TIME_OUT_RESPONSE (0x0001UL << 1) +#define MMC_STAT_READ_TIME_OUT (0x0001UL) + +#define MMC_STAT_ERRORS (MMC_STAT_RES_CRC_ERROR|MMC_STAT_SPI_READ_ERROR_TOKEN\ + |MMC_STAT_CRC_READ_ERROR|MMC_STAT_TIME_OUT_RESPONSE\ + |MMC_STAT_READ_TIME_OUT|MMC_STAT_CRC_WRITE_ERROR) + +/* MMC_CLKRT */ +#define MMC_CLKRT_20MHZ (0x0000UL) +#define MMC_CLKRT_10MHZ (0x0001UL) +#define MMC_CLKRT_5MHZ (0x0002UL) +#define MMC_CLKRT_2_5MHZ (0x0003UL) +#define MMC_CLKRT_1_25MHZ (0x0004UL) +#define MMC_CLKRT_0_625MHZ (0x0005UL) +#define MMC_CLKRT_0_3125MHZ (0x0006UL) + +/* MMC_SPI */ +#define MMC_SPI_DISABLE (0x00UL) +#define MMC_SPI_EN (0x01UL) +#define MMC_SPI_CS_EN (0x01UL << 2) +#define MMC_SPI_CS_ADDRESS (0x01UL << 3) +#define MMC_SPI_CRC_ON (0x01UL << 1) + +/* MMC_CMDAT */ +#define MMC_CMDAT_SD_4DAT (0x0001UL << 8) +#define MMC_CMDAT_MMC_DMA_EN (0x0001UL << 7) +#define MMC_CMDAT_INIT (0x0001UL << 6) +#define MMC_CMDAT_BUSY (0x0001UL << 5) +#define MMC_CMDAT_BCR (0x0003UL << 5) +#define MMC_CMDAT_STREAM (0x0001UL << 4) +#define MMC_CMDAT_BLOCK (0x0000UL << 4) +#define MMC_CMDAT_WRITE (0x0001UL << 3) +#define MMC_CMDAT_READ (0x0000UL << 3) +#define MMC_CMDAT_DATA_EN (0x0001UL << 2) +#define MMC_CMDAT_R0 (0) +#define MMC_CMDAT_R1 (0x0001UL) +#define MMC_CMDAT_R2 (0x0002UL) +#define MMC_CMDAT_R3 (0x0003UL) + +/* MMC_RESTO */ +#define MMC_RES_TO_MAX (0x007fUL) /* [6:0] */ + +/* MMC_RDTO */ +#define MMC_READ_TO_MAX (0x0ffffUL) /* [15:0] */ + +/* MMC_BLKLEN */ +#define MMC_BLK_LEN_MAX (0x03ffUL) /* [9:0] */ + +/* MMC_PRTBUF */ +#define MMC_PRTBUF_BUF_PART_FULL (0x01UL) +#define MMC_PRTBUF_BUF_FULL (0x00UL ) + +/* MMC_I_MASK */ +#define MMC_I_MASK_TXFIFO_WR_REQ (0x01UL << 6) +#define MMC_I_MASK_RXFIFO_RD_REQ (0x01UL << 5) +#define MMC_I_MASK_CLK_IS_OFF (0x01UL << 4) +#define MMC_I_MASK_STOP_CMD (0x01UL << 3) +#define MMC_I_MASK_END_CMD_RES (0x01UL << 2) +#define MMC_I_MASK_PRG_DONE (0x01UL << 1) +#define MMC_I_MASK_DATA_TRAN_DONE (0x01UL) +#define MMC_I_MASK_ALL (0x07fUL) + + +/* MMC_I_REG */ +#define MMC_I_REG_TXFIFO_WR_REQ (0x01UL << 6) +#define MMC_I_REG_RXFIFO_RD_REQ (0x01UL << 5) +#define MMC_I_REG_CLK_IS_OFF (0x01UL << 4) +#define MMC_I_REG_STOP_CMD (0x01UL << 3) +#define MMC_I_REG_END_CMD_RES (0x01UL << 2) +#define MMC_I_REG_PRG_DONE (0x01UL << 1) +#define MMC_I_REG_DATA_TRAN_DONE (0x01UL) +#define MMC_I_REG_ALL (0x007fUL) + +/* MMC_CMD */ +#define MMC_CMD_INDEX_MAX (0x006fUL) /* [5:0] */ +#define CMD(x) (x) + +#define MMC_DEFAULT_RCA 1 + +#define MMC_BLOCK_SIZE 512 +#define MMC_MAX_BLOCK_SIZE 512 + +#define MMC_R1_IDLE_STATE 0x01 +#define MMC_R1_ERASE_STATE 0x02 +#define MMC_R1_ILLEGAL_CMD 0x04 +#define MMC_R1_COM_CRC_ERR 0x08 +#define MMC_R1_ERASE_SEQ_ERR 0x01 +#define MMC_R1_ADDR_ERR 0x02 +#define MMC_R1_PARAM_ERR 0x04 + +#define MMC_R1B_WP_ERASE_SKIP 0x0002 +#define MMC_R1B_ERR 0x0004 +#define MMC_R1B_CC_ERR 0x0008 +#define MMC_R1B_CARD_ECC_ERR 0x0010 +#define MMC_R1B_WP_VIOLATION 0x0020 +#define MMC_R1B_ERASE_PARAM 0x0040 +#define MMC_R1B_OOR 0x0080 +#define MMC_R1B_IDLE_STATE 0x0100 +#define MMC_R1B_ERASE_RESET 0x0200 +#define MMC_R1B_ILLEGAL_CMD 0x0400 +#define MMC_R1B_COM_CRC_ERR 0x0800 +#define MMC_R1B_ERASE_SEQ_ERR 0x1000 +#define MMC_R1B_ADDR_ERR 0x2000 +#define MMC_R1B_PARAM_ERR 0x4000 + +#endif /* __MMC_PXA_P_H__ */ |