[U-Boot] [PATCH 0/3] Add support for the MMC device to the vexpress

From: Matt Waddel matt.waddel@linaro.org
These patches add support for the ARM PrimeCell PL180 MultiMedia Interface. The Versatile Express was the test platform for these changes.
Matt Waddel (3): MMC: Max blocks value adjustable MMC: Add support for PL180 ARM mmc device ARMV7: Vexpress: Add MMC support
board/armltd/vexpress/ca9x4_ct_vxp.c | 9 + drivers/mmc/Makefile | 1 + drivers/mmc/mmc.c | 19 +- drivers/mmc/mmci.c | 452 ++++++++++++++++++++++++++++++++++ drivers/mmc/mmci.h | 181 ++++++++++++++ include/configs/ca9x4_ct_vxp.h | 4 + 6 files changed, 656 insertions(+), 10 deletions(-) create mode 100644 drivers/mmc/mmci.c create mode 100644 drivers/mmc/mmci.h

From: Matt Waddel matt.waddel@linaro.org
The maximum blocks value was hardcoded to 65535 due to a 16 bit register length. The value can change for different platforms. This patch makes the default the current value of 65535, but it is configurable for other platforms.
Signed-off-by: Matt Waddel matt.waddel@linaro.org --- drivers/mmc/mmc.c | 19 +++++++++---------- 1 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 6805b33..d69eaa1 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -33,6 +33,11 @@ #include <mmc.h> #include <div64.h>
+/* Set block count limit because of 16 bit register limit on some hardware*/ +#ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT +#define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535 +#endif + static struct list_head mmc_devices; static int cur_dev_num = -1;
@@ -139,11 +144,8 @@ mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) return 0;
do { - /* - * The 65535 constraint comes from some hardware has - * only 16 bit width block number counter - */ - cur = (blocks_todo > 65535) ? 65535 : blocks_todo; + cur = (blocks_todo > CONFIG_SYS_MMC_MAX_BLK_COUNT) ? + CONFIG_SYS_MMC_MAX_BLK_COUNT : blocks_todo; if(mmc_write_blocks(mmc, start, cur, src) != cur) return 0; blocks_todo -= cur; @@ -215,11 +217,8 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) return 0;
do { - /* - * The 65535 constraint comes from some hardware has - * only 16 bit width block number counter - */ - cur = (blocks_todo > 65535) ? 65535 : blocks_todo; + cur = (blocks_todo > CONFIG_SYS_MMC_MAX_BLK_COUNT) ? + CONFIG_SYS_MMC_MAX_BLK_COUNT : blocks_todo; if(mmc_read_blocks(mmc, dst, start, cur) != cur) return 0; blocks_todo -= cur;

From: Matt Waddel matt.waddel@linaro.org
Add support for the ARM PrimeCell MultiMedia Interface - PL180. Ported from original device driver written by ST-Ericsson.
Signed-off-by: Matt Waddel matt.waddel@linaro.org --- drivers/mmc/Makefile | 1 + drivers/mmc/mmci.c | 452 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/mmci.h | 181 ++++++++++++++++++++ 3 files changed, 634 insertions(+), 0 deletions(-) create mode 100644 drivers/mmc/mmci.c create mode 100644 drivers/mmc/mmci.h
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 3496f0a..999970b 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -31,6 +31,7 @@ COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o +COBJS-$(CONFIG_MMCI) += mmci.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c new file mode 100644 index 0000000..ba2f335 --- /dev/null +++ b/drivers/mmc/mmci.c @@ -0,0 +1,452 @@ +/* + * ARM PrimeCell MultiMedia Card Interface - PL180 + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson ulf.hansson@stericsson.com + * Author: Martin Lundholm martin.xa.lundholm@stericsson.com + * Ported to drivers/mmc/ by: Matt Waddel matt.waddel@linaro.org + * + * 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 + */ + +/* #define DEBUG */ + +#include <asm/io.h> +#include "common.h" +#include <errno.h> +#include <mmc.h> +#include "mmci.h" +#include <malloc.h> + +struct mmc_host { + struct sdi_registers *base; +}; + +static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) +{ + u32 hoststatus, statusmask; + struct mmc_host *host = dev->priv; + + statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL; + if ((cmd->resp_type & MMC_RSP_PRESENT)) + statusmask |= SDI_STA_CMDREND; + else + statusmask |= SDI_STA_CMDSENT; + + do + hoststatus = readl(&host->base->status) & statusmask; + while (!hoststatus); + + writel(statusmask, &host->base->status_clear); + if (hoststatus & SDI_STA_CTIMEOUT) { + printf("CMD%d time out\n", cmd->cmdidx); + return -ETIMEDOUT; + } else if ((hoststatus & SDI_STA_CCRCFAIL) && + (cmd->flags & MMC_RSP_CRC)) { + printf("CMD%d CRC error\n", cmd->cmdidx); + return -EILSEQ; + } + + if (cmd->resp_type & MMC_RSP_PRESENT) { + cmd->response[0] = readl(&host->base->response0); + cmd->response[1] = readl(&host->base->response1); + cmd->response[2] = readl(&host->base->response2); + cmd->response[3] = readl(&host->base->response3); + debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, " + "response[2]:0x%08X, response[3]:0x%08X\n", + cmd->cmdidx, cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); + } + + return 0; +} + +/* send command to the mmc card and wait for results */ +static int do_command(struct mmc *dev, struct mmc_cmd *cmd) +{ + int result; + u32 sdi_cmd = 0; + struct mmc_host *host = dev->priv; + + sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); + + if (cmd->resp_type) { + sdi_cmd |= SDI_CMD_WAITRESP; + if (cmd->resp_type & MMC_RSP_136) + sdi_cmd |= SDI_CMD_LONGRESP; + } + + writel((u32)cmd->cmdarg, &host->base->argument); + udelay(COMMAND_REG_DELAY); + writel(sdi_cmd, &host->base->command); + result = wait_for_command_end(dev, cmd); + + /* After CMD2 set RCA to a none zero value. */ + if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)) + dev->rca = 10; + + /* After CMD3 open drain is switched off and push pull is used. */ + if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) { + u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD; + writel(sdi_pwr, &host->base->power); + } + + return result; +} + +static int convert_from_bytes_to_power_of_two(unsigned int x) +{ + int y = 0; + y = (x & 0xAAAA) ? 1 : 0; + y |= ((x & 0xCCCC) ? 1 : 0) << 1; + y |= ((x & 0xF0F0) ? 1 : 0) << 2; + y |= ((x & 0xFF00) ? 1 : 0) << 3; + + return y; +} + +static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) +{ + u32 *tempbuff = dest; + int i; + u64 xfercount = blkcount * blksize; + struct mmc_host *host = dev->priv; + u32 status, status_err; + + debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); + + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | + SDI_STA_RXOVERR); + while (!status_err && + (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32))) { + if (status & SDI_STA_RXFIFOBR) { + for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) + *(tempbuff + i) = readl(&host->base->fifo); + tempbuff += SDI_FIFO_BURST_SIZE; + xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); + } + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_RXOVERR); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Read data blk CRC error: 0x%x\n", status); + return -EILSEQ; + } else if (status & SDI_STA_RXOVERR) { + printf("Read data RX overflow error\n"); + return -EIO; + } + + while ((!status_err) && (xfercount >= sizeof(u32))) { + if (status & SDI_STA_RXDAVL) { + *(tempbuff) = readl(&host->base->fifo); + tempbuff++; + xfercount -= sizeof(u32); + } + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | + SDI_STA_RXOVERR); + } + + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | + SDI_STA_RXOVERR); + while (!status_err) { + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | + SDI_STA_RXOVERR); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Read data bytes CRC error: 0x%x\n", status); + return -EILSEQ; + } else if (status & SDI_STA_RXOVERR) { + printf("Read data RX overflow error\n"); + return -EIO; + } + + writel(SDI_ICR_MASK, &host->base->status_clear); + + if (xfercount) { + printf("Read data error, xfercount: %llu\n", xfercount); + return -ENOBUFS; + } + + return 0; +} + +static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize) +{ + u32 *tempbuff = src; + int i; + u64 xfercount = blkcount * blksize; + struct mmc_host *host = dev->priv; + u32 status, status_err; + + debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); + + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); + while (!status_err && xfercount) { + if (status & SDI_STA_TXFIFOBW) { + if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) { + for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) + writel(*(tempbuff + i), + &host->base->fifo); + tempbuff += SDI_FIFO_BURST_SIZE; + xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); + } else { + while (xfercount >= sizeof(u32)) { + writel(*(tempbuff), &host->base->fifo); + tempbuff++; + xfercount -= sizeof(u32); + } + } + } + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); + } + + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); + while (!status_err) { + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Write data timed out, xfercount:%llu,status:0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Write data CRC error\n"); + return -EILSEQ; + } + + writel(SDI_ICR_MASK, &host->base->status_clear); + + if (xfercount) { + printf("Write data error, xfercount:%llu", xfercount); + return -ENOBUFS; + } + + return 0; +} + +static int do_data_transfer(struct mmc *dev, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int error = -ETIMEDOUT; + struct mmc_host *host = dev->priv; + u32 blksz = 0; + u32 data_ctrl = 0; + u32 data_len = (u32) (data->blocks * data->blocksize); + + blksz = convert_from_bytes_to_power_of_two(data->blocksize); + data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); + data_ctrl |= SDI_DCTRL_DTEN; + + writel(SDI_DTIMER_DEFAULT, &host->base->datatimer); + writel(data_len, &host->base->datalength); + udelay(DATA_REG_DELAY); + + if (data->flags & MMC_DATA_READ) { + data_ctrl |= SDI_DCTRL_DTDIR_IN; + writel(data_ctrl, &host->base->datactrl); + + error = do_command(dev, cmd); + if (error) + return error; + + error = read_bytes(dev, (u32 *)data->dest, (u32)data->blocks, + (u32)data->blocksize); + } else if (data->flags & MMC_DATA_WRITE) { + error = do_command(dev, cmd); + if (error) + return error; + + writel(data_ctrl, &host->base->datactrl); + error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks, + (u32)data->blocksize); + } + + return error; +} + +static int host_request(struct mmc *dev, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int result; + + if (data) + result = do_data_transfer(dev, cmd, data); + else + result = do_command(dev, cmd); + + return result; +} + +/* MMC uses open drain drivers in the enumeration phase */ +static int mmc_host_reset(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_u32 = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON; + + writel(sdi_u32, &host->base->power); + + return 0; +} + +static void host_set_ios(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_clkcr; + + sdi_clkcr = readl(&host->base->clock); + + /* Ramp up the clock rate */ + if (dev->clock) { + u32 clkdiv = 0; + + if (dev->clock >= dev->f_max) + dev->clock = dev->f_max; + + clkdiv = ((MCLK / dev->clock) / 2) - 1; + + if (clkdiv > SDI_CLKCR_CLKDIV_MASK) + clkdiv = SDI_CLKCR_CLKDIV_MASK; + + sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); + sdi_clkcr |= clkdiv; + } + + /* Set the bus width */ + if (dev->bus_width) { + u32 buswidth = 0; + + switch (dev->bus_width) { + case 1: + buswidth |= SDI_CLKCR_WIDBUS_1; + break; + case 4: + buswidth |= SDI_CLKCR_WIDBUS_4; + break; + default: + printf("Invalid bus width\n"); + break; + } + sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); + sdi_clkcr |= buswidth; + } + + writel(sdi_clkcr, &host->base->clock); + udelay(CLK_CHANGE_DELAY); +} + +struct mmc *alloc_mmc_struct(void) +{ + struct mmc_host *host = NULL; + struct mmc *mmc_device = NULL; + + host = malloc(sizeof(struct mmc_host)); + if (!host) + return NULL; + + mmc_device = malloc(sizeof(struct mmc)); + if (!mmc_device) + goto err; + + mmc_device->priv = host; + return mmc_device; + +err: + free(host); + return NULL; +} + +/* + * mmc_host_init - initialize the mmc controller. + * Set initial clock and power for mmc slot. + * Initialize mmc struct and register with mmc framework. + */ +static int mmc_host_init(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_u32; + + host->base = (struct sdi_registers *)CONFIG_MMC_BASE; + + /* Initially set power-on, full voltage & MMCI read */ + sdi_u32 = INIT_PWR; + writel(sdi_u32, &host->base->power); + + /* setting clk freq 505KHz */ + sdi_u32 = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN; + writel(sdi_u32, &host->base->clock); + udelay(CLK_CHANGE_DELAY); + + /* Disable mmc interrupts */ + sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; + writel(sdi_u32, &host->base->mask0); + + sprintf(dev->name, "MMC"); + dev->clock = MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT + 1)); + dev->send_cmd = host_request; + dev->set_ios = host_set_ios; + dev->init = mmc_host_reset; + dev->host_caps = 0; + dev->voltages = VOLTAGE_WINDOW_MMC; + dev->f_min = dev->clock; + dev->f_max = CONFIG_MMCI_CLOCK_FREQ; + + return 0; +} + +int mmci_init(void) +{ + int error; + struct mmc *dev; + + dev = alloc_mmc_struct(); + if (!dev) + return -1; + + error = mmc_host_init(dev); + if (error) { + printf("mmci_host_init error - %d\n", error); + return -1; + } + + mmc_register(dev); + debug("registered mmc interface number is:%d\n", dev->block_dev.dev); + + return 0; +} diff --git a/drivers/mmc/mmci.h b/drivers/mmc/mmci.h new file mode 100644 index 0000000..ad82e61 --- /dev/null +++ b/drivers/mmc/mmci.h @@ -0,0 +1,181 @@ +/* + * ARM PrimeCell MultiMedia Card Interface - PL180 + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson ulf.hansson@stericsson.com + * Author: Martin Lundholm martin.xa.lundholm@stericsson.com + * Ported to drivers/mmc/ by: Matt Waddel matt.waddel@linaro.org + * + * 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 __MMC_NOMADIK_H__ +#define __MMC_NOMADIK_H__ + +#define COMMAND_REG_DELAY 300 +#define DATA_REG_DELAY 1000 +#define CLK_CHANGE_DELAY 2000 + +#define INIT_PWR 0xBF /* Power on, full power, not open drain */ +#define MCLK (100*1000*1000) + +/* SDI Power Control register bits */ +#define SDI_PWR_PWRCTRL_MASK 0x00000003 +#define SDI_PWR_PWRCTRL_ON 0x00000003 +#define SDI_PWR_PWRCTRL_OFF 0x00000000 +#define SDI_PWR_DAT2DIREN 0x00000004 +#define SDI_PWR_CMDDIREN 0x00000008 +#define SDI_PWR_DAT0DIREN 0x00000010 +#define SDI_PWR_DAT31DIREN 0x00000020 +#define SDI_PWR_OPD 0x00000040 +#define SDI_PWR_FBCLKEN 0x00000080 +#define SDI_PWR_DAT74DIREN 0x00000100 +#define SDI_PWR_RSTEN 0x00000200 + +#define VOLTAGE_WINDOW_MMC 0x00FF8080 +#define VOLTAGE_WINDOW_SD 0x80010000 + +/* SDI clock control register bits */ +#define SDI_CLKCR_CLKDIV_MASK 0x000000FF +#define SDI_CLKCR_CLKEN 0x00000100 +#define SDI_CLKCR_PWRSAV 0x00000200 +#define SDI_CLKCR_BYPASS 0x00000400 +#define SDI_CLKCR_WIDBUS_MASK 0x00001800 +#define SDI_CLKCR_WIDBUS_1 0x00000000 +#define SDI_CLKCR_WIDBUS_4 0x00000800 + +#define SDI_CLKCR_CLKDIV_INIT 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */ + +/* SDI command register bits */ +#define SDI_CMD_CMDINDEX_MASK 0x000000FF +#define SDI_CMD_WAITRESP 0x00000040 +#define SDI_CMD_LONGRESP 0x00000080 +#define SDI_CMD_WAITINT 0x00000100 +#define SDI_CMD_WAITPEND 0x00000200 +#define SDI_CMD_CPSMEN 0x00000400 +#define SDI_CMD_SDIOSUSPEND 0x00000800 +#define SDI_CMD_ENDCMDCOMPL 0x00001000 +#define SDI_CMD_NIEN 0x00002000 +#define SDI_CMD_CE_ATACMD 0x00004000 +#define SDI_CMD_CBOOTMODEEN 0x00008000 + +#define SDI_DTIMER_DEFAULT 0xFFFF0000 + +/* SDI Status register bits */ +#define SDI_STA_CCRCFAIL 0x00000001 +#define SDI_STA_DCRCFAIL 0x00000002 +#define SDI_STA_CTIMEOUT 0x00000004 +#define SDI_STA_DTIMEOUT 0x00000008 +#define SDI_STA_TXUNDERR 0x00000010 +#define SDI_STA_RXOVERR 0x00000020 +#define SDI_STA_CMDREND 0x00000040 +#define SDI_STA_CMDSENT 0x00000080 +#define SDI_STA_DATAEND 0x00000100 +#define SDI_STA_STBITERR 0x00000200 +#define SDI_STA_DBCKEND 0x00000400 +#define SDI_STA_CMDACT 0x00000800 +#define SDI_STA_TXACT 0x00001000 +#define SDI_STA_RXACT 0x00002000 +#define SDI_STA_TXFIFOBW 0x00004000 +#define SDI_STA_RXFIFOBR 0x00008000 +#define SDI_STA_TXFIFOF 0x00010000 +#define SDI_STA_RXFIFOF 0x00020000 +#define SDI_STA_TXFIFOE 0x00040000 +#define SDI_STA_RXFIFOE 0x00080000 +#define SDI_STA_TXDAVL 0x00100000 +#define SDI_STA_RXDAVL 0x00200000 +#define SDI_STA_SDIOIT 0x00400000 +#define SDI_STA_CEATAEND 0x00800000 +#define SDI_STA_CARDBUSY 0x01000000 +#define SDI_STA_BOOTMODE 0x02000000 +#define SDI_STA_BOOTACKERR 0x04000000 +#define SDI_STA_BOOTACKTIMEOUT 0x08000000 +#define SDI_STA_RSTNEND 0x10000000 + +/* SDI Interrupt Clear register bits */ +#define SDI_ICR_MASK 0x1DC007FF +#define SDI_ICR_CCRCFAILC 0x00000001 +#define SDI_ICR_DCRCFAILC 0x00000002 +#define SDI_ICR_CTIMEOUTC 0x00000004 +#define SDI_ICR_DTIMEOUTC 0x00000008 +#define SDI_ICR_TXUNDERRC 0x00000010 +#define SDI_ICR_RXOVERRC 0x00000020 +#define SDI_ICR_CMDRENDC 0x00000040 +#define SDI_ICR_CMDSENTC 0x00000080 +#define SDI_ICR_DATAENDC 0x00000100 +#define SDI_ICR_STBITERRC 0x00000200 +#define SDI_ICR_DBCKENDC 0x00000400 +#define SDI_ICR_SDIOITC 0x00400000 +#define SDI_ICR_CEATAENDC 0x00800000 +#define SDI_ICR_BUSYENDC 0x01000000 +#define SDI_ICR_BOOTACKERRC 0x04000000 +#define SDI_ICR_BOOTACKTIMEOUTC 0x08000000 +#define SDI_ICR_RSTNENDC 0x10000000 + +#define SDI_MASK0_MASK 0x1FFFFFFF + +/* SDI Data control register bits */ +#define SDI_DCTRL_DTEN 0x00000001 +#define SDI_DCTRL_DTDIR_IN 0x00000002 +#define SDI_DCTRL_DTMODE_STREAM 0x00000004 +#define SDI_DCTRL_DMAEN 0x00000008 +#define SDI_DCTRL_DBLKSIZE_MASK 0x000000F0 +#define SDI_DCTRL_RWSTART 0x00000100 +#define SDI_DCTRL_RWSTOP 0x00000200 +#define SDI_DCTRL_RWMOD 0x00000200 +#define SDI_DCTRL_SDIOEN 0x00000800 +#define SDI_DCTRL_DMAREQCTL 0x00001000 +#define SDI_DCTRL_DBOOTMODEEN 0x00002000 +#define SDI_DCTRL_BUSYMODE 0x00004000 +#define SDI_DCTRL_DDR_MODE 0x00008000 + +#define SDI_FIFO_BURST_SIZE 8 + +struct sdi_registers { + u32 power; /* 0x00*/ + u32 clock; /* 0x04*/ + u32 argument; /* 0x08*/ + u32 command; /* 0x0c*/ + u32 respcommand; /* 0x10*/ + u32 response0; /* 0x14*/ + u32 response1; /* 0x18*/ + u32 response2; /* 0x1c*/ + u32 response3; /* 0x20*/ + u32 datatimer; /* 0x24*/ + u32 datalength; /* 0x28*/ + u32 datactrl; /* 0x2c*/ + u32 datacount; /* 0x30*/ + u32 status; /* 0x34*/ + u32 status_clear; /* 0x38*/ + u32 mask0; /* 0x3c*/ + u32 mask1; /* 0x40*/ + u32 card_select; /* 0x44*/ + u32 fifo_count; /* 0x48*/ + u32 padding1[(0x80-0x4C)>>2]; + u32 fifo; /* 0x80*/ + u32 padding2[(0xFE0-0x84)>>2]; + u32 periph_id0; /* 0xFE0 mmc Peripheral Identifcation Register*/ + u32 periph_id1; /* 0xFE4*/ + u32 periph_id2; /* 0xFE8*/ + u32 periph_id3; /* 0xFEC*/ + u32 pcell_id0; /* 0xFF0*/ + u32 pcell_id1; /* 0xFF4*/ + u32 pcell_id2; /* 0xFF8*/ + u32 pcell_id3; /* 0xFFC*/ +}; + +#endif

From: Matt Waddel matt.waddel@linaro.org
Added the board specific definitions to use the MMCI device.
Signed-off-by: Matt Waddel matt.waddel@linaro.org --- board/armltd/vexpress/ca9x4_ct_vxp.c | 9 +++++++++ include/configs/ca9x4_ct_vxp.h | 4 ++++ 2 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/board/armltd/vexpress/ca9x4_ct_vxp.c b/board/armltd/vexpress/ca9x4_ct_vxp.c index ce1be1e..73cea1c 100644 --- a/board/armltd/vexpress/ca9x4_ct_vxp.c +++ b/board/armltd/vexpress/ca9x4_ct_vxp.c @@ -86,6 +86,15 @@ int board_eth_init(bd_t *bis) return rc; }
+int cpu_mmc_init(bd_t *bis) +{ + int rc = 0; +#ifdef CONFIG_MMCI + rc = mmci_init(bis); +#endif + return rc; +} + static void flash__init(void) { /* Setup the sytem control register to allow writing to flash */ diff --git a/include/configs/ca9x4_ct_vxp.h b/include/configs/ca9x4_ct_vxp.h index 63f003d..47a3bfc 100644 --- a/include/configs/ca9x4_ct_vxp.h +++ b/include/configs/ca9x4_ct_vxp.h @@ -86,6 +86,10 @@ #define CONFIG_MMC 1 #define CONFIG_CMD_MMC #define CONFIG_GENERIC_MMC +#define CONFIG_MMCI +#define CONFIG_MMC_BASE 0x10005000 +#define CONFIG_SYS_MMC_MAX_BLK_COUNT 127 +#define CONFIG_MMCI_CLOCK_FREQ 6250000
/* BOOTP options */ #define CONFIG_BOOTP_BOOTFILESIZE

Dear matt.waddel@linaro.org,
From: Matt Waddelmatt.waddel@linaro.org
These patches add support for the ARM PrimeCell PL180 MultiMedia Interface. The Versatile Express was the test platform for these changes.
Matt Waddel (3): MMC: Max blocks value adjustable MMC: Add support for PL180 ARM mmc device ARMV7: Vexpress: Add MMC support
board/armltd/vexpress/ca9x4_ct_vxp.c | 9 + drivers/mmc/Makefile | 1 + drivers/mmc/mmc.c | 19 +- drivers/mmc/mmci.c | 452 ++++++++++++++++++++++++++++++++++ drivers/mmc/mmci.h | 181 ++++++++++++++
I would expect something more descriptive like <hardware>_mmc.[ch] here. mmci.[ch] does not lead one to think its a hardware specific driver, even less for what specific hardware it is. The same is true for the exported function name "mmci_init", the macro CONFIG_MMCI etc.
Then, in patch 2/3:
+int mmci_init(void)
but in patch 3/3:
+ rc = mmci_init(bis);
Seems you should add a prototype to include/mmc.h to detect such mistakes ;)
Best Regards, Reinhard

Reinhard, On Thu, Feb 24, 2011 at 8:02 PM, Reinhard Meyer u-boot@emk-elektronik.de wrote:
Dear matt.waddel@linaro.org,
From: Matt Waddelmatt.waddel@linaro.org
These patches add support for the ARM PrimeCell PL180 MultiMedia Interface. The Versatile Express was the test platform for these changes.
Matt Waddel (3): MMC: Max blocks value adjustable MMC: Add support for PL180 ARM mmc device ARMV7: Vexpress: Add MMC support
board/armltd/vexpress/ca9x4_ct_vxp.c | 9 + drivers/mmc/Makefile | 1 + drivers/mmc/mmc.c | 19 +- drivers/mmc/mmci.c | 452 ++++++++++++++++++++++++++++++++++ drivers/mmc/mmci.h | 181 ++++++++++++++
I would expect something more descriptive like <hardware>_mmc.[ch] here. mmci.[ch] does not lead one to think its a hardware specific driver, even less for what specific hardware it is. The same is true for the exported function name "mmci_init", the macro CONFIG_MMCI etc.
The file in the linux kernel is mmci.c. One could argue that keeping it the same as the kernel is a good thing. If it needs to be changed, I would vote for arm_mmci.c.
The config option in the kernel is CONFIG_MMC_ARMMMCI so I would vote for that as the config option in u-boot.
br, John

From: Matt Waddel matt.waddel@linaro.org
These patches add support for the ARM PrimeCell PL180 MultiMedia Interface. The Versatile Express was the test platform for these changes.
--- Change log:
v2 - Rename patch items to a more descriptive name (ie. mmci -> arm_mmci) Fixed conflicting fuction call and added prototype to arm_mmci.h (Note: Patch 1/3 did not change with these corrections)
Matt Waddel (3): MMC: Max blocks value adjustable MMC: Add support for PL180 ARM mmc device ARMV7: Vexpress: Add MMC support
board/armltd/vexpress/ca9x4_ct_vxp.c | 9 + drivers/mmc/Makefile | 1 + drivers/mmc/arm_mmci.c | 452 ++++++++++++++++++++++++++++++++++ drivers/mmc/arm_mmci.h | 183 ++++++++++++++ drivers/mmc/mmc.c | 19 +- include/configs/ca9x4_ct_vxp.h | 4 + 6 files changed, 658 insertions(+), 10 deletions(-) create mode 100644 drivers/mmc/arm_mmci.c create mode 100644 drivers/mmc/arm_mmci.h

Dear matt.waddel,
From: Matt Waddelmatt.waddel@linaro.org
These patches add support for the ARM PrimeCell PL180 MultiMedia Interface. The Versatile Express was the test platform for these changes.
Change log:
v2 - Rename patch items to a more descriptive name (ie. mmci -> arm_mmci) Fixed conflicting fuction call and added prototype to arm_mmci.h
"function"
(Note: Patch 1/3 did not change with these corrections)
Matt Waddel (3): MMC: Max blocks value adjustable MMC: Add support for PL180 ARM mmc device ARMV7: Vexpress: Add MMC support
board/armltd/vexpress/ca9x4_ct_vxp.c | 9 + drivers/mmc/Makefile | 1 + drivers/mmc/arm_mmci.c | 452 ++++++++++++++++++++++++++++++++++ drivers/mmc/arm_mmci.h | 183 ++++++++++++++ drivers/mmc/mmc.c | 19 +- include/configs/ca9x4_ct_vxp.h | 4 + 6 files changed, 658 insertions(+), 10 deletions(-) create mode 100644 drivers/mmc/arm_mmci.c create mode 100644 drivers/mmc/arm_mmci.h
Sorry, my original comment still holds true for the naming, now the name suggests that this driver is generic to ARM devices, which clearly is not true.
Can you list which devices have a MMCI that is covered by this driver? PL180 only? Or a series of PLxxx? It should be possible to find a naming that is more descriptive than "arm".
(That such a generic name slipped into the kernel must not imply we have to make the same mistake in u-boot)
Best Regards, Reinhard

Hi Reinhard,
On 02/28/2011 10:58 PM, Reinhard Meyer wrote:
Dear matt.waddel,
From: Matt Waddelmatt.waddel@linaro.org
<snip>
Sorry, my original comment still holds true for the naming, now the name suggests that this driver is generic to ARM devices, which clearly is not true.
Correct. However, this hardware device was developed by ARM (the company). As far as I know the other ARM mmc drivers, like the omap3_mmc, are not. So that would be the difference between the naming of these drivers.
I don't feel like this driver should be named after a particular platform like vexpress, since this driver could be used in any ARM hardware.
Can you list which devices have a MMCI that is covered by this driver? PL180 only? Or a series of PLxxx? It should be possible to find a naming that is more descriptive than "arm".
The PL180 is the name assigned to the hardware in the SOC. Here is the description of device from the 1st paragraph in the manual:
1.1 About the ARM PrimeCell Multimedia Card Interface (PL180) The PrimeCell Multimedia Card Interface (MCI) is an Advanced Microcontroller Bus Architecture(AMBA) compliant, System-on-a-Chip (SoC) peripheral that is developed, tested, and licensed by ARM. ----
As far as other platforms that use this hardware, it is in some of the other ARM EVMs and some STEricsson platforms like the u300 and u8500.
(That such a generic name slipped into the kernel must not imply we have to make the same mistake in u-boot)
I agree. However, it would be nice to have a bit of a link between the device in the kernel and the one in u-boot and I didn't think having "_mmci" in the name would be too objectionable.
Would pl180_mmci.c work? or how about arm_pl180_mmc.c? I'm open to other suggestions.
Best regards, Matt
Best Regards, Reinhard

Hello Matt Waddel,
Sorry, my original comment still holds true for the naming, now the name suggests that this driver is generic to ARM devices, which clearly is not true.
Correct. However, this hardware device was developed by ARM (the company). As far as I know the other ARM mmc drivers, like the omap3_mmc, are not. So that would be the difference between the naming of these drivers.
I see. The "problem" is that ARM is a company, an architecture, and now even an IP manufacturer...
I don't feel like this driver should be named after a particular platform like vexpress, since this driver could be used in any ARM hardware.
Correct.
Can you list which devices have a MMCI that is covered by this driver? PL180 only? Or a series of PLxxx? It should be possible to find a naming that is more descriptive than "arm".
The PL180 is the name assigned to the hardware in the SOC. Here is the description of device from the 1st paragraph in the manual:
1.1 About the ARM PrimeCell Multimedia Card Interface (PL180) The PrimeCell Multimedia Card Interface (MCI) is an Advanced Microcontroller Bus Architecture(AMBA) compliant, System-on-a-Chip (SoC) peripheral that is developed, tested, and licensed by ARM.
As far as other platforms that use this hardware, it is in some of the other ARM EVMs and some STEricsson platforms like the u300 and u8500.
(That such a generic name slipped into the kernel must not imply we have to make the same mistake in u-boot)
I agree. However, it would be nice to have a bit of a link between the device in the kernel and the one in u-boot and I didn't think having "_mmci" in the name would be too objectionable.
Would pl180_mmci.c work? or how about arm_pl180_mmc.c? I'm open to other suggestions.
Yes, after what you have described, is it to be expected that it is always designated as PL180 when licensed and used by a SoC nanufacturer? "arm_pl180_mmci" would be best then, I think. That leaves the namespace open for other "arm_*_mmci" controllers that might appear in the future.
Best regards, Reinhard

From: Matt Waddel matt.waddel@linaro.org
Add support for the ARM PrimeCell MultiMedia Interface - PL180. Ported from original device driver written by ST-Ericsson.
Signed-off-by: Matt Waddel matt.waddel@linaro.org --- drivers/mmc/Makefile | 1 + drivers/mmc/arm_mmci.c | 452 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/arm_mmci.h | 183 +++++++++++++++++++ 3 files changed, 636 insertions(+), 0 deletions(-) create mode 100644 drivers/mmc/arm_mmci.c create mode 100644 drivers/mmc/arm_mmci.h
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 3496f0a..7f365b4 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -31,6 +31,7 @@ COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o +COBJS-$(CONFIG_ARM_MMCI) += arm_mmci.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o diff --git a/drivers/mmc/arm_mmci.c b/drivers/mmc/arm_mmci.c new file mode 100644 index 0000000..8380138 --- /dev/null +++ b/drivers/mmc/arm_mmci.c @@ -0,0 +1,452 @@ +/* + * ARM PrimeCell MultiMedia Card Interface - PL180 + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson ulf.hansson@stericsson.com + * Author: Martin Lundholm martin.xa.lundholm@stericsson.com + * Ported to drivers/mmc/ by: Matt Waddel matt.waddel@linaro.org + * + * 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 + */ + +/* #define DEBUG */ + +#include <asm/io.h> +#include "common.h" +#include <errno.h> +#include <mmc.h> +#include "arm_mmci.h" +#include <malloc.h> + +struct mmc_host { + struct sdi_registers *base; +}; + +static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) +{ + u32 hoststatus, statusmask; + struct mmc_host *host = dev->priv; + + statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL; + if ((cmd->resp_type & MMC_RSP_PRESENT)) + statusmask |= SDI_STA_CMDREND; + else + statusmask |= SDI_STA_CMDSENT; + + do + hoststatus = readl(&host->base->status) & statusmask; + while (!hoststatus); + + writel(statusmask, &host->base->status_clear); + if (hoststatus & SDI_STA_CTIMEOUT) { + printf("CMD%d time out\n", cmd->cmdidx); + return -ETIMEDOUT; + } else if ((hoststatus & SDI_STA_CCRCFAIL) && + (cmd->flags & MMC_RSP_CRC)) { + printf("CMD%d CRC error\n", cmd->cmdidx); + return -EILSEQ; + } + + if (cmd->resp_type & MMC_RSP_PRESENT) { + cmd->response[0] = readl(&host->base->response0); + cmd->response[1] = readl(&host->base->response1); + cmd->response[2] = readl(&host->base->response2); + cmd->response[3] = readl(&host->base->response3); + debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, " + "response[2]:0x%08X, response[3]:0x%08X\n", + cmd->cmdidx, cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); + } + + return 0; +} + +/* send command to the mmc card and wait for results */ +static int do_command(struct mmc *dev, struct mmc_cmd *cmd) +{ + int result; + u32 sdi_cmd = 0; + struct mmc_host *host = dev->priv; + + sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); + + if (cmd->resp_type) { + sdi_cmd |= SDI_CMD_WAITRESP; + if (cmd->resp_type & MMC_RSP_136) + sdi_cmd |= SDI_CMD_LONGRESP; + } + + writel((u32)cmd->cmdarg, &host->base->argument); + udelay(COMMAND_REG_DELAY); + writel(sdi_cmd, &host->base->command); + result = wait_for_command_end(dev, cmd); + + /* After CMD2 set RCA to a none zero value. */ + if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)) + dev->rca = 10; + + /* After CMD3 open drain is switched off and push pull is used. */ + if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) { + u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD; + writel(sdi_pwr, &host->base->power); + } + + return result; +} + +static int convert_from_bytes_to_power_of_two(unsigned int x) +{ + int y = 0; + y = (x & 0xAAAA) ? 1 : 0; + y |= ((x & 0xCCCC) ? 1 : 0) << 1; + y |= ((x & 0xF0F0) ? 1 : 0) << 2; + y |= ((x & 0xFF00) ? 1 : 0) << 3; + + return y; +} + +static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) +{ + u32 *tempbuff = dest; + int i; + u64 xfercount = blkcount * blksize; + struct mmc_host *host = dev->priv; + u32 status, status_err; + + debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); + + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | + SDI_STA_RXOVERR); + while (!status_err && + (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32))) { + if (status & SDI_STA_RXFIFOBR) { + for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) + *(tempbuff + i) = readl(&host->base->fifo); + tempbuff += SDI_FIFO_BURST_SIZE; + xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); + } + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_RXOVERR); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Read data blk CRC error: 0x%x\n", status); + return -EILSEQ; + } else if (status & SDI_STA_RXOVERR) { + printf("Read data RX overflow error\n"); + return -EIO; + } + + while ((!status_err) && (xfercount >= sizeof(u32))) { + if (status & SDI_STA_RXDAVL) { + *(tempbuff) = readl(&host->base->fifo); + tempbuff++; + xfercount -= sizeof(u32); + } + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | + SDI_STA_RXOVERR); + } + + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | + SDI_STA_RXOVERR); + while (!status_err) { + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | + SDI_STA_RXOVERR); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Read data bytes CRC error: 0x%x\n", status); + return -EILSEQ; + } else if (status & SDI_STA_RXOVERR) { + printf("Read data RX overflow error\n"); + return -EIO; + } + + writel(SDI_ICR_MASK, &host->base->status_clear); + + if (xfercount) { + printf("Read data error, xfercount: %llu\n", xfercount); + return -ENOBUFS; + } + + return 0; +} + +static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize) +{ + u32 *tempbuff = src; + int i; + u64 xfercount = blkcount * blksize; + struct mmc_host *host = dev->priv; + u32 status, status_err; + + debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); + + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); + while (!status_err && xfercount) { + if (status & SDI_STA_TXFIFOBW) { + if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) { + for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) + writel(*(tempbuff + i), + &host->base->fifo); + tempbuff += SDI_FIFO_BURST_SIZE; + xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); + } else { + while (xfercount >= sizeof(u32)) { + writel(*(tempbuff), &host->base->fifo); + tempbuff++; + xfercount -= sizeof(u32); + } + } + } + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); + } + + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); + while (!status_err) { + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Write data timed out, xfercount:%llu,status:0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Write data CRC error\n"); + return -EILSEQ; + } + + writel(SDI_ICR_MASK, &host->base->status_clear); + + if (xfercount) { + printf("Write data error, xfercount:%llu", xfercount); + return -ENOBUFS; + } + + return 0; +} + +static int do_data_transfer(struct mmc *dev, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int error = -ETIMEDOUT; + struct mmc_host *host = dev->priv; + u32 blksz = 0; + u32 data_ctrl = 0; + u32 data_len = (u32) (data->blocks * data->blocksize); + + blksz = convert_from_bytes_to_power_of_two(data->blocksize); + data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); + data_ctrl |= SDI_DCTRL_DTEN; + + writel(SDI_DTIMER_DEFAULT, &host->base->datatimer); + writel(data_len, &host->base->datalength); + udelay(DATA_REG_DELAY); + + if (data->flags & MMC_DATA_READ) { + data_ctrl |= SDI_DCTRL_DTDIR_IN; + writel(data_ctrl, &host->base->datactrl); + + error = do_command(dev, cmd); + if (error) + return error; + + error = read_bytes(dev, (u32 *)data->dest, (u32)data->blocks, + (u32)data->blocksize); + } else if (data->flags & MMC_DATA_WRITE) { + error = do_command(dev, cmd); + if (error) + return error; + + writel(data_ctrl, &host->base->datactrl); + error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks, + (u32)data->blocksize); + } + + return error; +} + +static int host_request(struct mmc *dev, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int result; + + if (data) + result = do_data_transfer(dev, cmd, data); + else + result = do_command(dev, cmd); + + return result; +} + +/* MMC uses open drain drivers in the enumeration phase */ +static int mmc_host_reset(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_u32 = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON; + + writel(sdi_u32, &host->base->power); + + return 0; +} + +static void host_set_ios(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_clkcr; + + sdi_clkcr = readl(&host->base->clock); + + /* Ramp up the clock rate */ + if (dev->clock) { + u32 clkdiv = 0; + + if (dev->clock >= dev->f_max) + dev->clock = dev->f_max; + + clkdiv = ((ARM_MCLK / dev->clock) / 2) - 1; + + if (clkdiv > SDI_CLKCR_CLKDIV_MASK) + clkdiv = SDI_CLKCR_CLKDIV_MASK; + + sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); + sdi_clkcr |= clkdiv; + } + + /* Set the bus width */ + if (dev->bus_width) { + u32 buswidth = 0; + + switch (dev->bus_width) { + case 1: + buswidth |= SDI_CLKCR_WIDBUS_1; + break; + case 4: + buswidth |= SDI_CLKCR_WIDBUS_4; + break; + default: + printf("Invalid bus width\n"); + break; + } + sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); + sdi_clkcr |= buswidth; + } + + writel(sdi_clkcr, &host->base->clock); + udelay(CLK_CHANGE_DELAY); +} + +struct mmc *alloc_mmc_struct(void) +{ + struct mmc_host *host = NULL; + struct mmc *mmc_device = NULL; + + host = malloc(sizeof(struct mmc_host)); + if (!host) + return NULL; + + mmc_device = malloc(sizeof(struct mmc)); + if (!mmc_device) + goto err; + + mmc_device->priv = host; + return mmc_device; + +err: + free(host); + return NULL; +} + +/* + * mmc_host_init - initialize the mmc controller. + * Set initial clock and power for mmc slot. + * Initialize mmc struct and register with mmc framework. + */ +static int arm_mmci_host_init(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_u32; + + host->base = (struct sdi_registers *)CONFIG_ARM_MMCI_BASE; + + /* Initially set power-on, full voltage & MMCI read */ + sdi_u32 = INIT_PWR; + writel(sdi_u32, &host->base->power); + + /* setting clk freq 505KHz */ + sdi_u32 = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN; + writel(sdi_u32, &host->base->clock); + udelay(CLK_CHANGE_DELAY); + + /* Disable mmc interrupts */ + sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; + writel(sdi_u32, &host->base->mask0); + + sprintf(dev->name, "MMC"); + dev->clock = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT + 1)); + dev->send_cmd = host_request; + dev->set_ios = host_set_ios; + dev->init = mmc_host_reset; + dev->host_caps = 0; + dev->voltages = VOLTAGE_WINDOW_MMC; + dev->f_min = dev->clock; + dev->f_max = CONFIG_ARM_MMCI_CLOCK_FREQ; + + return 0; +} + +int arm_mmci_init(void) +{ + int error; + struct mmc *dev; + + dev = alloc_mmc_struct(); + if (!dev) + return -1; + + error = arm_mmci_host_init(dev); + if (error) { + printf("mmci_host_init error - %d\n", error); + return -1; + } + + mmc_register(dev); + debug("registered mmc interface number is:%d\n", dev->block_dev.dev); + + return 0; +} diff --git a/drivers/mmc/arm_mmci.h b/drivers/mmc/arm_mmci.h new file mode 100644 index 0000000..b790f03 --- /dev/null +++ b/drivers/mmc/arm_mmci.h @@ -0,0 +1,183 @@ +/* + * ARM PrimeCell MultiMedia Card Interface - PL180 + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson ulf.hansson@stericsson.com + * Author: Martin Lundholm martin.xa.lundholm@stericsson.com + * Ported to drivers/mmc/ by: Matt Waddel matt.waddel@linaro.org + * + * 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 __MMC_NOMADIK_H__ +#define __MMC_NOMADIK_H__ + +int mmci_init(void); + +#define COMMAND_REG_DELAY 300 +#define DATA_REG_DELAY 1000 +#define CLK_CHANGE_DELAY 2000 + +#define INIT_PWR 0xBF /* Power on, full power, not open drain */ +#define ARM_MCLK (100*1000*1000) + +/* SDI Power Control register bits */ +#define SDI_PWR_PWRCTRL_MASK 0x00000003 +#define SDI_PWR_PWRCTRL_ON 0x00000003 +#define SDI_PWR_PWRCTRL_OFF 0x00000000 +#define SDI_PWR_DAT2DIREN 0x00000004 +#define SDI_PWR_CMDDIREN 0x00000008 +#define SDI_PWR_DAT0DIREN 0x00000010 +#define SDI_PWR_DAT31DIREN 0x00000020 +#define SDI_PWR_OPD 0x00000040 +#define SDI_PWR_FBCLKEN 0x00000080 +#define SDI_PWR_DAT74DIREN 0x00000100 +#define SDI_PWR_RSTEN 0x00000200 + +#define VOLTAGE_WINDOW_MMC 0x00FF8080 +#define VOLTAGE_WINDOW_SD 0x80010000 + +/* SDI clock control register bits */ +#define SDI_CLKCR_CLKDIV_MASK 0x000000FF +#define SDI_CLKCR_CLKEN 0x00000100 +#define SDI_CLKCR_PWRSAV 0x00000200 +#define SDI_CLKCR_BYPASS 0x00000400 +#define SDI_CLKCR_WIDBUS_MASK 0x00001800 +#define SDI_CLKCR_WIDBUS_1 0x00000000 +#define SDI_CLKCR_WIDBUS_4 0x00000800 + +#define SDI_CLKCR_CLKDIV_INIT 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */ + +/* SDI command register bits */ +#define SDI_CMD_CMDINDEX_MASK 0x000000FF +#define SDI_CMD_WAITRESP 0x00000040 +#define SDI_CMD_LONGRESP 0x00000080 +#define SDI_CMD_WAITINT 0x00000100 +#define SDI_CMD_WAITPEND 0x00000200 +#define SDI_CMD_CPSMEN 0x00000400 +#define SDI_CMD_SDIOSUSPEND 0x00000800 +#define SDI_CMD_ENDCMDCOMPL 0x00001000 +#define SDI_CMD_NIEN 0x00002000 +#define SDI_CMD_CE_ATACMD 0x00004000 +#define SDI_CMD_CBOOTMODEEN 0x00008000 + +#define SDI_DTIMER_DEFAULT 0xFFFF0000 + +/* SDI Status register bits */ +#define SDI_STA_CCRCFAIL 0x00000001 +#define SDI_STA_DCRCFAIL 0x00000002 +#define SDI_STA_CTIMEOUT 0x00000004 +#define SDI_STA_DTIMEOUT 0x00000008 +#define SDI_STA_TXUNDERR 0x00000010 +#define SDI_STA_RXOVERR 0x00000020 +#define SDI_STA_CMDREND 0x00000040 +#define SDI_STA_CMDSENT 0x00000080 +#define SDI_STA_DATAEND 0x00000100 +#define SDI_STA_STBITERR 0x00000200 +#define SDI_STA_DBCKEND 0x00000400 +#define SDI_STA_CMDACT 0x00000800 +#define SDI_STA_TXACT 0x00001000 +#define SDI_STA_RXACT 0x00002000 +#define SDI_STA_TXFIFOBW 0x00004000 +#define SDI_STA_RXFIFOBR 0x00008000 +#define SDI_STA_TXFIFOF 0x00010000 +#define SDI_STA_RXFIFOF 0x00020000 +#define SDI_STA_TXFIFOE 0x00040000 +#define SDI_STA_RXFIFOE 0x00080000 +#define SDI_STA_TXDAVL 0x00100000 +#define SDI_STA_RXDAVL 0x00200000 +#define SDI_STA_SDIOIT 0x00400000 +#define SDI_STA_CEATAEND 0x00800000 +#define SDI_STA_CARDBUSY 0x01000000 +#define SDI_STA_BOOTMODE 0x02000000 +#define SDI_STA_BOOTACKERR 0x04000000 +#define SDI_STA_BOOTACKTIMEOUT 0x08000000 +#define SDI_STA_RSTNEND 0x10000000 + +/* SDI Interrupt Clear register bits */ +#define SDI_ICR_MASK 0x1DC007FF +#define SDI_ICR_CCRCFAILC 0x00000001 +#define SDI_ICR_DCRCFAILC 0x00000002 +#define SDI_ICR_CTIMEOUTC 0x00000004 +#define SDI_ICR_DTIMEOUTC 0x00000008 +#define SDI_ICR_TXUNDERRC 0x00000010 +#define SDI_ICR_RXOVERRC 0x00000020 +#define SDI_ICR_CMDRENDC 0x00000040 +#define SDI_ICR_CMDSENTC 0x00000080 +#define SDI_ICR_DATAENDC 0x00000100 +#define SDI_ICR_STBITERRC 0x00000200 +#define SDI_ICR_DBCKENDC 0x00000400 +#define SDI_ICR_SDIOITC 0x00400000 +#define SDI_ICR_CEATAENDC 0x00800000 +#define SDI_ICR_BUSYENDC 0x01000000 +#define SDI_ICR_BOOTACKERRC 0x04000000 +#define SDI_ICR_BOOTACKTIMEOUTC 0x08000000 +#define SDI_ICR_RSTNENDC 0x10000000 + +#define SDI_MASK0_MASK 0x1FFFFFFF + +/* SDI Data control register bits */ +#define SDI_DCTRL_DTEN 0x00000001 +#define SDI_DCTRL_DTDIR_IN 0x00000002 +#define SDI_DCTRL_DTMODE_STREAM 0x00000004 +#define SDI_DCTRL_DMAEN 0x00000008 +#define SDI_DCTRL_DBLKSIZE_MASK 0x000000F0 +#define SDI_DCTRL_RWSTART 0x00000100 +#define SDI_DCTRL_RWSTOP 0x00000200 +#define SDI_DCTRL_RWMOD 0x00000200 +#define SDI_DCTRL_SDIOEN 0x00000800 +#define SDI_DCTRL_DMAREQCTL 0x00001000 +#define SDI_DCTRL_DBOOTMODEEN 0x00002000 +#define SDI_DCTRL_BUSYMODE 0x00004000 +#define SDI_DCTRL_DDR_MODE 0x00008000 + +#define SDI_FIFO_BURST_SIZE 8 + +struct sdi_registers { + u32 power; /* 0x00*/ + u32 clock; /* 0x04*/ + u32 argument; /* 0x08*/ + u32 command; /* 0x0c*/ + u32 respcommand; /* 0x10*/ + u32 response0; /* 0x14*/ + u32 response1; /* 0x18*/ + u32 response2; /* 0x1c*/ + u32 response3; /* 0x20*/ + u32 datatimer; /* 0x24*/ + u32 datalength; /* 0x28*/ + u32 datactrl; /* 0x2c*/ + u32 datacount; /* 0x30*/ + u32 status; /* 0x34*/ + u32 status_clear; /* 0x38*/ + u32 mask0; /* 0x3c*/ + u32 mask1; /* 0x40*/ + u32 card_select; /* 0x44*/ + u32 fifo_count; /* 0x48*/ + u32 padding1[(0x80-0x4C)>>2]; + u32 fifo; /* 0x80*/ + u32 padding2[(0xFE0-0x84)>>2]; + u32 periph_id0; /* 0xFE0 mmc Peripheral Identifcation Register*/ + u32 periph_id1; /* 0xFE4*/ + u32 periph_id2; /* 0xFE8*/ + u32 periph_id3; /* 0xFEC*/ + u32 pcell_id0; /* 0xFF0*/ + u32 pcell_id1; /* 0xFF4*/ + u32 pcell_id2; /* 0xFF8*/ + u32 pcell_id3; /* 0xFFC*/ +}; + +#endif

From: Matt Waddel matt.waddel@linaro.org
Added the board specific definitions to use the MMCI device.
Signed-off-by: Matt Waddel matt.waddel@linaro.org --- board/armltd/vexpress/ca9x4_ct_vxp.c | 9 +++++++++ include/configs/ca9x4_ct_vxp.h | 4 ++++ 2 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/board/armltd/vexpress/ca9x4_ct_vxp.c b/board/armltd/vexpress/ca9x4_ct_vxp.c index ce1be1e..92cb6be 100644 --- a/board/armltd/vexpress/ca9x4_ct_vxp.c +++ b/board/armltd/vexpress/ca9x4_ct_vxp.c @@ -86,6 +86,15 @@ int board_eth_init(bd_t *bis) return rc; }
+int cpu_mmc_init(bd_t *bis) +{ + int rc = 0; +#ifdef CONFIG_ARM_MMCI + rc = arm_mmci_init(); +#endif + return rc; +} + static void flash__init(void) { /* Setup the sytem control register to allow writing to flash */ diff --git a/include/configs/ca9x4_ct_vxp.h b/include/configs/ca9x4_ct_vxp.h index 63f003d..52fb5df 100644 --- a/include/configs/ca9x4_ct_vxp.h +++ b/include/configs/ca9x4_ct_vxp.h @@ -86,6 +86,10 @@ #define CONFIG_MMC 1 #define CONFIG_CMD_MMC #define CONFIG_GENERIC_MMC +#define CONFIG_ARM_MMCI +#define CONFIG_ARM_MMCI_BASE 0x10005000 +#define CONFIG_SYS_MMC_MAX_BLK_COUNT 127 +#define CONFIG_ARM_MMCI_CLOCK_FREQ 6250000
/* BOOTP options */ #define CONFIG_BOOTP_BOOTFILESIZE

From: Matt Waddel matt.waddel@linaro.org
These patches add support for the ARM PrimeCell PL180 MultiMedia Interface. The Versatile Express was the test platform for these changes.
--- Change log:
v2 - Rename patch items to a more descriptive name (ie. mmci -> arm_mmci) Fixed conflicting fuction call and added prototype to arm_mmci.h
v3 - Rename patch items (arm_mmci -> arm_pl180_mmci) (Note: Patch 1/3 still did not change with these corrections)
Matt Waddel (3): MMC: Max blocks value adjustable MMC: Add support for PL180 ARM mmc device ARMV7: Vexpress: Add MMC support
board/armltd/vexpress/ca9x4_ct_vxp.c | 9 + drivers/mmc/Makefile | 1 + drivers/mmc/arm_pl180_mmci.c | 452 ++++++++++++++++++++++++++++++++++ drivers/mmc/arm_pl180_mmci.h | 183 ++++++++++++++ drivers/mmc/mmc.c | 19 +- include/configs/ca9x4_ct_vxp.h | 4 + 6 files changed, 658 insertions(+), 10 deletions(-) create mode 100644 drivers/mmc/arm_pl180_mmci.c create mode 100644 drivers/mmc/arm_pl180_mmci.h

From: Matt Waddel matt.waddel@linaro.org
Add support for the ARM PrimeCell MultiMedia Interface - PL180. Ported from original device driver written by ST-Ericsson.
Signed-off-by: Matt Waddel matt.waddel@linaro.org --- drivers/mmc/Makefile | 1 + drivers/mmc/arm_pl180_mmci.c | 452 ++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/arm_pl180_mmci.h | 183 +++++++++++++++++ 3 files changed, 636 insertions(+), 0 deletions(-) create mode 100644 drivers/mmc/arm_pl180_mmci.c create mode 100644 drivers/mmc/arm_pl180_mmci.h
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 3496f0a..7b3c319 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -31,6 +31,7 @@ COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o +COBJS-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c new file mode 100644 index 0000000..9b60e4f --- /dev/null +++ b/drivers/mmc/arm_pl180_mmci.c @@ -0,0 +1,452 @@ +/* + * ARM PrimeCell MultiMedia Card Interface - PL180 + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson ulf.hansson@stericsson.com + * Author: Martin Lundholm martin.xa.lundholm@stericsson.com + * Ported to drivers/mmc/ by: Matt Waddel matt.waddel@linaro.org + * + * 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 + */ + +/* #define DEBUG */ + +#include <asm/io.h> +#include "common.h" +#include <errno.h> +#include <mmc.h> +#include "arm_pl180_mmci.h" +#include <malloc.h> + +struct mmc_host { + struct sdi_registers *base; +}; + +static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) +{ + u32 hoststatus, statusmask; + struct mmc_host *host = dev->priv; + + statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL; + if ((cmd->resp_type & MMC_RSP_PRESENT)) + statusmask |= SDI_STA_CMDREND; + else + statusmask |= SDI_STA_CMDSENT; + + do + hoststatus = readl(&host->base->status) & statusmask; + while (!hoststatus); + + writel(statusmask, &host->base->status_clear); + if (hoststatus & SDI_STA_CTIMEOUT) { + printf("CMD%d time out\n", cmd->cmdidx); + return -ETIMEDOUT; + } else if ((hoststatus & SDI_STA_CCRCFAIL) && + (cmd->flags & MMC_RSP_CRC)) { + printf("CMD%d CRC error\n", cmd->cmdidx); + return -EILSEQ; + } + + if (cmd->resp_type & MMC_RSP_PRESENT) { + cmd->response[0] = readl(&host->base->response0); + cmd->response[1] = readl(&host->base->response1); + cmd->response[2] = readl(&host->base->response2); + cmd->response[3] = readl(&host->base->response3); + debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, " + "response[2]:0x%08X, response[3]:0x%08X\n", + cmd->cmdidx, cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); + } + + return 0; +} + +/* send command to the mmc card and wait for results */ +static int do_command(struct mmc *dev, struct mmc_cmd *cmd) +{ + int result; + u32 sdi_cmd = 0; + struct mmc_host *host = dev->priv; + + sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); + + if (cmd->resp_type) { + sdi_cmd |= SDI_CMD_WAITRESP; + if (cmd->resp_type & MMC_RSP_136) + sdi_cmd |= SDI_CMD_LONGRESP; + } + + writel((u32)cmd->cmdarg, &host->base->argument); + udelay(COMMAND_REG_DELAY); + writel(sdi_cmd, &host->base->command); + result = wait_for_command_end(dev, cmd); + + /* After CMD2 set RCA to a none zero value. */ + if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)) + dev->rca = 10; + + /* After CMD3 open drain is switched off and push pull is used. */ + if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) { + u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD; + writel(sdi_pwr, &host->base->power); + } + + return result; +} + +static int convert_from_bytes_to_power_of_two(unsigned int x) +{ + int y = 0; + y = (x & 0xAAAA) ? 1 : 0; + y |= ((x & 0xCCCC) ? 1 : 0) << 1; + y |= ((x & 0xF0F0) ? 1 : 0) << 2; + y |= ((x & 0xFF00) ? 1 : 0) << 3; + + return y; +} + +static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) +{ + u32 *tempbuff = dest; + int i; + u64 xfercount = blkcount * blksize; + struct mmc_host *host = dev->priv; + u32 status, status_err; + + debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); + + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | + SDI_STA_RXOVERR); + while (!status_err && + (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32))) { + if (status & SDI_STA_RXFIFOBR) { + for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) + *(tempbuff + i) = readl(&host->base->fifo); + tempbuff += SDI_FIFO_BURST_SIZE; + xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); + } + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_RXOVERR); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Read data blk CRC error: 0x%x\n", status); + return -EILSEQ; + } else if (status & SDI_STA_RXOVERR) { + printf("Read data RX overflow error\n"); + return -EIO; + } + + while ((!status_err) && (xfercount >= sizeof(u32))) { + if (status & SDI_STA_RXDAVL) { + *(tempbuff) = readl(&host->base->fifo); + tempbuff++; + xfercount -= sizeof(u32); + } + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | + SDI_STA_RXOVERR); + } + + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | + SDI_STA_RXOVERR); + while (!status_err) { + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | + SDI_STA_RXOVERR); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Read data bytes CRC error: 0x%x\n", status); + return -EILSEQ; + } else if (status & SDI_STA_RXOVERR) { + printf("Read data RX overflow error\n"); + return -EIO; + } + + writel(SDI_ICR_MASK, &host->base->status_clear); + + if (xfercount) { + printf("Read data error, xfercount: %llu\n", xfercount); + return -ENOBUFS; + } + + return 0; +} + +static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize) +{ + u32 *tempbuff = src; + int i; + u64 xfercount = blkcount * blksize; + struct mmc_host *host = dev->priv; + u32 status, status_err; + + debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); + + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); + while (!status_err && xfercount) { + if (status & SDI_STA_TXFIFOBW) { + if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) { + for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) + writel(*(tempbuff + i), + &host->base->fifo); + tempbuff += SDI_FIFO_BURST_SIZE; + xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); + } else { + while (xfercount >= sizeof(u32)) { + writel(*(tempbuff), &host->base->fifo); + tempbuff++; + xfercount -= sizeof(u32); + } + } + } + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); + } + + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); + while (!status_err) { + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Write data timed out, xfercount:%llu,status:0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Write data CRC error\n"); + return -EILSEQ; + } + + writel(SDI_ICR_MASK, &host->base->status_clear); + + if (xfercount) { + printf("Write data error, xfercount:%llu", xfercount); + return -ENOBUFS; + } + + return 0; +} + +static int do_data_transfer(struct mmc *dev, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int error = -ETIMEDOUT; + struct mmc_host *host = dev->priv; + u32 blksz = 0; + u32 data_ctrl = 0; + u32 data_len = (u32) (data->blocks * data->blocksize); + + blksz = convert_from_bytes_to_power_of_two(data->blocksize); + data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); + data_ctrl |= SDI_DCTRL_DTEN; + + writel(SDI_DTIMER_DEFAULT, &host->base->datatimer); + writel(data_len, &host->base->datalength); + udelay(DATA_REG_DELAY); + + if (data->flags & MMC_DATA_READ) { + data_ctrl |= SDI_DCTRL_DTDIR_IN; + writel(data_ctrl, &host->base->datactrl); + + error = do_command(dev, cmd); + if (error) + return error; + + error = read_bytes(dev, (u32 *)data->dest, (u32)data->blocks, + (u32)data->blocksize); + } else if (data->flags & MMC_DATA_WRITE) { + error = do_command(dev, cmd); + if (error) + return error; + + writel(data_ctrl, &host->base->datactrl); + error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks, + (u32)data->blocksize); + } + + return error; +} + +static int host_request(struct mmc *dev, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int result; + + if (data) + result = do_data_transfer(dev, cmd, data); + else + result = do_command(dev, cmd); + + return result; +} + +/* MMC uses open drain drivers in the enumeration phase */ +static int mmc_host_reset(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_u32 = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON; + + writel(sdi_u32, &host->base->power); + + return 0; +} + +static void host_set_ios(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_clkcr; + + sdi_clkcr = readl(&host->base->clock); + + /* Ramp up the clock rate */ + if (dev->clock) { + u32 clkdiv = 0; + + if (dev->clock >= dev->f_max) + dev->clock = dev->f_max; + + clkdiv = ((ARM_MCLK / dev->clock) / 2) - 1; + + if (clkdiv > SDI_CLKCR_CLKDIV_MASK) + clkdiv = SDI_CLKCR_CLKDIV_MASK; + + sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); + sdi_clkcr |= clkdiv; + } + + /* Set the bus width */ + if (dev->bus_width) { + u32 buswidth = 0; + + switch (dev->bus_width) { + case 1: + buswidth |= SDI_CLKCR_WIDBUS_1; + break; + case 4: + buswidth |= SDI_CLKCR_WIDBUS_4; + break; + default: + printf("Invalid bus width\n"); + break; + } + sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); + sdi_clkcr |= buswidth; + } + + writel(sdi_clkcr, &host->base->clock); + udelay(CLK_CHANGE_DELAY); +} + +struct mmc *alloc_mmc_struct(void) +{ + struct mmc_host *host = NULL; + struct mmc *mmc_device = NULL; + + host = malloc(sizeof(struct mmc_host)); + if (!host) + return NULL; + + mmc_device = malloc(sizeof(struct mmc)); + if (!mmc_device) + goto err; + + mmc_device->priv = host; + return mmc_device; + +err: + free(host); + return NULL; +} + +/* + * mmc_host_init - initialize the mmc controller. + * Set initial clock and power for mmc slot. + * Initialize mmc struct and register with mmc framework. + */ +static int arm_pl180_mmci_host_init(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_u32; + + host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE; + + /* Initially set power-on, full voltage & MMCI read */ + sdi_u32 = INIT_PWR; + writel(sdi_u32, &host->base->power); + + /* setting clk freq 505KHz */ + sdi_u32 = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN; + writel(sdi_u32, &host->base->clock); + udelay(CLK_CHANGE_DELAY); + + /* Disable mmc interrupts */ + sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; + writel(sdi_u32, &host->base->mask0); + + sprintf(dev->name, "MMC"); + dev->clock = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT + 1)); + dev->send_cmd = host_request; + dev->set_ios = host_set_ios; + dev->init = mmc_host_reset; + dev->host_caps = 0; + dev->voltages = VOLTAGE_WINDOW_MMC; + dev->f_min = dev->clock; + dev->f_max = CONFIG_ARM_PL180_MMCI_CLOCK_FREQ; + + return 0; +} + +int arm_pl180_mmci_init(void) +{ + int error; + struct mmc *dev; + + dev = alloc_mmc_struct(); + if (!dev) + return -1; + + error = arm_pl180_mmci_host_init(dev); + if (error) { + printf("mmci_host_init error - %d\n", error); + return -1; + } + + mmc_register(dev); + debug("registered mmc interface number is:%d\n", dev->block_dev.dev); + + return 0; +} diff --git a/drivers/mmc/arm_pl180_mmci.h b/drivers/mmc/arm_pl180_mmci.h new file mode 100644 index 0000000..42fbe3e --- /dev/null +++ b/drivers/mmc/arm_pl180_mmci.h @@ -0,0 +1,183 @@ +/* + * ARM PrimeCell MultiMedia Card Interface - PL180 + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson ulf.hansson@stericsson.com + * Author: Martin Lundholm martin.xa.lundholm@stericsson.com + * Ported to drivers/mmc/ by: Matt Waddel matt.waddel@linaro.org + * + * 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 __ARM_PL180_MMCI_H__ +#define __ARM_PL180_MMCI_H__ + +int arm_pl180_mmci_init(void); + +#define COMMAND_REG_DELAY 300 +#define DATA_REG_DELAY 1000 +#define CLK_CHANGE_DELAY 2000 + +#define INIT_PWR 0xBF /* Power on, full power, not open drain */ +#define ARM_MCLK (100*1000*1000) + +/* SDI Power Control register bits */ +#define SDI_PWR_PWRCTRL_MASK 0x00000003 +#define SDI_PWR_PWRCTRL_ON 0x00000003 +#define SDI_PWR_PWRCTRL_OFF 0x00000000 +#define SDI_PWR_DAT2DIREN 0x00000004 +#define SDI_PWR_CMDDIREN 0x00000008 +#define SDI_PWR_DAT0DIREN 0x00000010 +#define SDI_PWR_DAT31DIREN 0x00000020 +#define SDI_PWR_OPD 0x00000040 +#define SDI_PWR_FBCLKEN 0x00000080 +#define SDI_PWR_DAT74DIREN 0x00000100 +#define SDI_PWR_RSTEN 0x00000200 + +#define VOLTAGE_WINDOW_MMC 0x00FF8080 +#define VOLTAGE_WINDOW_SD 0x80010000 + +/* SDI clock control register bits */ +#define SDI_CLKCR_CLKDIV_MASK 0x000000FF +#define SDI_CLKCR_CLKEN 0x00000100 +#define SDI_CLKCR_PWRSAV 0x00000200 +#define SDI_CLKCR_BYPASS 0x00000400 +#define SDI_CLKCR_WIDBUS_MASK 0x00001800 +#define SDI_CLKCR_WIDBUS_1 0x00000000 +#define SDI_CLKCR_WIDBUS_4 0x00000800 + +#define SDI_CLKCR_CLKDIV_INIT 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */ + +/* SDI command register bits */ +#define SDI_CMD_CMDINDEX_MASK 0x000000FF +#define SDI_CMD_WAITRESP 0x00000040 +#define SDI_CMD_LONGRESP 0x00000080 +#define SDI_CMD_WAITINT 0x00000100 +#define SDI_CMD_WAITPEND 0x00000200 +#define SDI_CMD_CPSMEN 0x00000400 +#define SDI_CMD_SDIOSUSPEND 0x00000800 +#define SDI_CMD_ENDCMDCOMPL 0x00001000 +#define SDI_CMD_NIEN 0x00002000 +#define SDI_CMD_CE_ATACMD 0x00004000 +#define SDI_CMD_CBOOTMODEEN 0x00008000 + +#define SDI_DTIMER_DEFAULT 0xFFFF0000 + +/* SDI Status register bits */ +#define SDI_STA_CCRCFAIL 0x00000001 +#define SDI_STA_DCRCFAIL 0x00000002 +#define SDI_STA_CTIMEOUT 0x00000004 +#define SDI_STA_DTIMEOUT 0x00000008 +#define SDI_STA_TXUNDERR 0x00000010 +#define SDI_STA_RXOVERR 0x00000020 +#define SDI_STA_CMDREND 0x00000040 +#define SDI_STA_CMDSENT 0x00000080 +#define SDI_STA_DATAEND 0x00000100 +#define SDI_STA_STBITERR 0x00000200 +#define SDI_STA_DBCKEND 0x00000400 +#define SDI_STA_CMDACT 0x00000800 +#define SDI_STA_TXACT 0x00001000 +#define SDI_STA_RXACT 0x00002000 +#define SDI_STA_TXFIFOBW 0x00004000 +#define SDI_STA_RXFIFOBR 0x00008000 +#define SDI_STA_TXFIFOF 0x00010000 +#define SDI_STA_RXFIFOF 0x00020000 +#define SDI_STA_TXFIFOE 0x00040000 +#define SDI_STA_RXFIFOE 0x00080000 +#define SDI_STA_TXDAVL 0x00100000 +#define SDI_STA_RXDAVL 0x00200000 +#define SDI_STA_SDIOIT 0x00400000 +#define SDI_STA_CEATAEND 0x00800000 +#define SDI_STA_CARDBUSY 0x01000000 +#define SDI_STA_BOOTMODE 0x02000000 +#define SDI_STA_BOOTACKERR 0x04000000 +#define SDI_STA_BOOTACKTIMEOUT 0x08000000 +#define SDI_STA_RSTNEND 0x10000000 + +/* SDI Interrupt Clear register bits */ +#define SDI_ICR_MASK 0x1DC007FF +#define SDI_ICR_CCRCFAILC 0x00000001 +#define SDI_ICR_DCRCFAILC 0x00000002 +#define SDI_ICR_CTIMEOUTC 0x00000004 +#define SDI_ICR_DTIMEOUTC 0x00000008 +#define SDI_ICR_TXUNDERRC 0x00000010 +#define SDI_ICR_RXOVERRC 0x00000020 +#define SDI_ICR_CMDRENDC 0x00000040 +#define SDI_ICR_CMDSENTC 0x00000080 +#define SDI_ICR_DATAENDC 0x00000100 +#define SDI_ICR_STBITERRC 0x00000200 +#define SDI_ICR_DBCKENDC 0x00000400 +#define SDI_ICR_SDIOITC 0x00400000 +#define SDI_ICR_CEATAENDC 0x00800000 +#define SDI_ICR_BUSYENDC 0x01000000 +#define SDI_ICR_BOOTACKERRC 0x04000000 +#define SDI_ICR_BOOTACKTIMEOUTC 0x08000000 +#define SDI_ICR_RSTNENDC 0x10000000 + +#define SDI_MASK0_MASK 0x1FFFFFFF + +/* SDI Data control register bits */ +#define SDI_DCTRL_DTEN 0x00000001 +#define SDI_DCTRL_DTDIR_IN 0x00000002 +#define SDI_DCTRL_DTMODE_STREAM 0x00000004 +#define SDI_DCTRL_DMAEN 0x00000008 +#define SDI_DCTRL_DBLKSIZE_MASK 0x000000F0 +#define SDI_DCTRL_RWSTART 0x00000100 +#define SDI_DCTRL_RWSTOP 0x00000200 +#define SDI_DCTRL_RWMOD 0x00000200 +#define SDI_DCTRL_SDIOEN 0x00000800 +#define SDI_DCTRL_DMAREQCTL 0x00001000 +#define SDI_DCTRL_DBOOTMODEEN 0x00002000 +#define SDI_DCTRL_BUSYMODE 0x00004000 +#define SDI_DCTRL_DDR_MODE 0x00008000 + +#define SDI_FIFO_BURST_SIZE 8 + +struct sdi_registers { + u32 power; /* 0x00*/ + u32 clock; /* 0x04*/ + u32 argument; /* 0x08*/ + u32 command; /* 0x0c*/ + u32 respcommand; /* 0x10*/ + u32 response0; /* 0x14*/ + u32 response1; /* 0x18*/ + u32 response2; /* 0x1c*/ + u32 response3; /* 0x20*/ + u32 datatimer; /* 0x24*/ + u32 datalength; /* 0x28*/ + u32 datactrl; /* 0x2c*/ + u32 datacount; /* 0x30*/ + u32 status; /* 0x34*/ + u32 status_clear; /* 0x38*/ + u32 mask0; /* 0x3c*/ + u32 mask1; /* 0x40*/ + u32 card_select; /* 0x44*/ + u32 fifo_count; /* 0x48*/ + u32 padding1[(0x80-0x4C)>>2]; + u32 fifo; /* 0x80*/ + u32 padding2[(0xFE0-0x84)>>2]; + u32 periph_id0; /* 0xFE0 mmc Peripheral Identifcation Register*/ + u32 periph_id1; /* 0xFE4*/ + u32 periph_id2; /* 0xFE8*/ + u32 periph_id3; /* 0xFEC*/ + u32 pcell_id0; /* 0xFF0*/ + u32 pcell_id1; /* 0xFF4*/ + u32 pcell_id2; /* 0xFF8*/ + u32 pcell_id3; /* 0xFFC*/ +}; + +#endif

On Wed, Mar 2, 2011 at 11:22 PM, matt.waddel@linaro.org wrote:
+static int convert_from_bytes_to_power_of_two(unsigned int x) +{
- int y = 0;
- y = (x & 0xAAAA) ? 1 : 0;
- y |= ((x & 0xCCCC) ? 1 : 0) << 1;
- y |= ((x & 0xF0F0) ? 1 : 0) << 2;
- y |= ((x & 0xFF00) ? 1 : 0) << 3;
- return y;
+}
Let's not try to be this clever. U-Boot already has a function that tells you which power of two a number corresponds to. Unless the caller of this will be highly sensitive to several cycles that you may gain from knowing that the input is already a power-of-two, there's just no cause to creating a separate implementation of ffs().
Other than that, it looks fine, and as soon as you respin, I'll add it to the mmc tree.
Andy

From: Matt Waddel matt.waddel@linaro.org
Added the board specific definitions to use the MMCI device.
Signed-off-by: Matt Waddel matt.waddel@linaro.org --- board/armltd/vexpress/ca9x4_ct_vxp.c | 9 +++++++++ include/configs/ca9x4_ct_vxp.h | 4 ++++ 2 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/board/armltd/vexpress/ca9x4_ct_vxp.c b/board/armltd/vexpress/ca9x4_ct_vxp.c index ce1be1e..3566b95 100644 --- a/board/armltd/vexpress/ca9x4_ct_vxp.c +++ b/board/armltd/vexpress/ca9x4_ct_vxp.c @@ -86,6 +86,15 @@ int board_eth_init(bd_t *bis) return rc; }
+int cpu_mmc_init(bd_t *bis) +{ + int rc = 0; +#ifdef CONFIG_ARM_PL180_MMCI + rc = arm_pl180_mmci_init(); +#endif + return rc; +} + static void flash__init(void) { /* Setup the sytem control register to allow writing to flash */ diff --git a/include/configs/ca9x4_ct_vxp.h b/include/configs/ca9x4_ct_vxp.h index 63f003d..4ab58c0 100644 --- a/include/configs/ca9x4_ct_vxp.h +++ b/include/configs/ca9x4_ct_vxp.h @@ -86,6 +86,10 @@ #define CONFIG_MMC 1 #define CONFIG_CMD_MMC #define CONFIG_GENERIC_MMC +#define CONFIG_ARM_PL180_MMCI +#define CONFIG_ARM_PL180_MMCI_BASE 0x10005000 +#define CONFIG_SYS_MMC_MAX_BLK_COUNT 127 +#define CONFIG_ARM_PL180_MMCI_CLOCK_FREQ 6250000
/* BOOTP options */ #define CONFIG_BOOTP_BOOTFILESIZE

On Wed, Mar 2, 2011 at 11:22 PM, matt.waddel@linaro.org wrote:
From: Matt Waddel matt.waddel@linaro.org
Added the board specific definitions to use the MMCI device.
Signed-off-by: Matt Waddel matt.waddel@linaro.org
Looks fine to me. I can apply this to my tree if that's fine with the maintainer. If not, I'm also OK if the maintainer wants to pull in the driver patch (with the previously-mentioned small change).
Either way: Acked-by: Andy Fleming afleming@freescale.com
Andy

On 04/13/2011 05:09 AM, Andy Fleming wrote:
On Wed, Mar 2, 2011 at 11:22 PM, matt.waddel@linaro.org wrote:
From: Matt Waddel matt.waddel@linaro.org
Added the board specific definitions to use the MMCI device.
Signed-off-by: Matt Waddel matt.waddel@linaro.org
Looks fine to me. I can apply this to my tree if that's fine with the maintainer. If not, I'm also OK if the maintainer wants to pull in the driver patch (with the previously-mentioned small change).
Hi Andy,
I made the ffs() change you suggested and have posted a 4th version of the 3 patches (even though the 2nd one was the only one that changed, I wanted to make sure they were all still in sync with the repository).
If you could apply these to your tree that would be great.
Either way: Acked-by: Andy Fleming afleming@freescale.com
Thanks, Matt
Andy _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

On Sat, Apr 16, 2011 at 3:53 PM, Matt Waddel matt.waddel@canonical.com wrote:
On 04/13/2011 05:09 AM, Andy Fleming wrote:
On Wed, Mar 2, 2011 at 11:22 PM, matt.waddel@linaro.org wrote:
From: Matt Waddel matt.waddel@linaro.org
Added the board specific definitions to use the MMCI device.
Signed-off-by: Matt Waddel matt.waddel@linaro.org
Looks fine to me. I can apply this to my tree if that's fine with the maintainer. If not, I'm also OK if the maintainer wants to pull in the driver patch (with the previously-mentioned small change).
Hi Andy,
I made the ffs() change you suggested and have posted a 4th version of the 3 patches (even though the 2nd one was the only one that changed, I wanted to make sure they were all still in sync with the repository).
If you could apply these to your tree that would be great.
Either way: Acked-by: Andy Fleming afleming@freescale.com
Thanks, Matt
Andy _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
Andy, Matt:
What is the plan for getting the pl180 mmc driver commited? I need it in before my U8500 board patches can be commited.
Thanks, John

It's on my todo list for today
On Apr 28, 2011, at 1:46 PM, John Rigby wrote:
On Sat, Apr 16, 2011 at 3:53 PM, Matt Waddel matt.waddel@canonical.com wrote:
On 04/13/2011 05:09 AM, Andy Fleming wrote:
On Wed, Mar 2, 2011 at 11:22 PM, matt.waddel@linaro.org wrote:
From: Matt Waddel matt.waddel@linaro.org
Added the board specific definitions to use the MMCI device.
Signed-off-by: Matt Waddel matt.waddel@linaro.org
Looks fine to me. I can apply this to my tree if that's fine with the maintainer. If not, I'm also OK if the maintainer wants to pull in the driver patch (with the previously-mentioned small change).
Hi Andy,
I made the ffs() change you suggested and have posted a 4th version of the 3 patches (even though the 2nd one was the only one that changed, I wanted to make sure they were all still in sync with the repository).
If you could apply these to your tree that would be great.
Either way: Acked-by: Andy Fleming afleming@freescale.com
Thanks, Matt
Andy _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot
Andy, Matt:
What is the plan for getting the pl180 mmc driver commited? I need it in before my U8500 board patches can be commited.
Thanks, John

On Thu, Apr 28, 2011 at 12:57 PM, Andy Fleming afleming@freescale.com wrote:
It's on my todo list for today
Thanks!

From: Matt Waddel matt.waddel@linaro.org
These patches add support for the ARM PrimeCell PL180 MultiMedia Interface. The Versatile Express was the test platform for these changes.
--- Change log:
v2 - Rename patch items to a more descriptive name (ie. mmci -> arm_mmci) Fixed conflicting fuction call and added prototype to arm_mmci.h
v3 - Rename patch items (arm_mmci -> arm_pl180_mmci) (Note: Patch 1/3 still did not change with these corrections)
v4 - Removed convert_from_bytes_to_power_of_two() routine and used generic ffs call instead. (Feedback from Andy Fleming.)
Matt Waddel (3): MMC: Max blocks value adjustable MMC: Add support for PL180 ARM mmc device ARMV7: Vexpress: Add MMC support
board/armltd/vexpress/ca9x4_ct_vxp.c | 9 + drivers/mmc/Makefile | 1 + drivers/mmc/arm_pl180_mmci.c | 441 ++++++++++++++++++++++++++++++++++ drivers/mmc/arm_pl180_mmci.h | 183 ++++++++++++++ drivers/mmc/mmc.c | 19 +- include/configs/ca9x4_ct_vxp.h | 4 + 6 files changed, 647 insertions(+), 10 deletions(-) create mode 100644 drivers/mmc/arm_pl180_mmci.c create mode 100644 drivers/mmc/arm_pl180_mmci.h

From: Matt Waddel matt.waddel@linaro.org
The maximum blocks value was hardcoded to 65535 due to a 16 bit register length. The value can change for different platforms. This patch makes the default the current value of 65535, but it is configurable for other platforms.
Signed-off-by: Matt Waddel matt.waddel@linaro.org --- drivers/mmc/mmc.c | 19 +++++++++---------- 1 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 6805b33..d69eaa1 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -33,6 +33,11 @@ #include <mmc.h> #include <div64.h>
+/* Set block count limit because of 16 bit register limit on some hardware*/ +#ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT +#define CONFIG_SYS_MMC_MAX_BLK_COUNT 65535 +#endif + static struct list_head mmc_devices; static int cur_dev_num = -1;
@@ -139,11 +144,8 @@ mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) return 0;
do { - /* - * The 65535 constraint comes from some hardware has - * only 16 bit width block number counter - */ - cur = (blocks_todo > 65535) ? 65535 : blocks_todo; + cur = (blocks_todo > CONFIG_SYS_MMC_MAX_BLK_COUNT) ? + CONFIG_SYS_MMC_MAX_BLK_COUNT : blocks_todo; if(mmc_write_blocks(mmc, start, cur, src) != cur) return 0; blocks_todo -= cur; @@ -215,11 +217,8 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) return 0;
do { - /* - * The 65535 constraint comes from some hardware has - * only 16 bit width block number counter - */ - cur = (blocks_todo > 65535) ? 65535 : blocks_todo; + cur = (blocks_todo > CONFIG_SYS_MMC_MAX_BLK_COUNT) ? + CONFIG_SYS_MMC_MAX_BLK_COUNT : blocks_todo; if(mmc_read_blocks(mmc, dst, start, cur) != cur) return 0; blocks_todo -= cur;

From: Matt Waddel matt.waddel@linaro.org
Add support for the ARM PrimeCell MultiMedia Interface - PL180. Ported from original device driver written by ST-Ericsson.
Signed-off-by: Matt Waddel matt.waddel@linaro.org --- drivers/mmc/Makefile | 1 + drivers/mmc/arm_pl180_mmci.c | 441 ++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/arm_pl180_mmci.h | 183 +++++++++++++++++ 3 files changed, 625 insertions(+), 0 deletions(-) create mode 100644 drivers/mmc/arm_pl180_mmci.c create mode 100644 drivers/mmc/arm_pl180_mmci.h
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 3496f0a..7b3c319 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -31,6 +31,7 @@ COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o +COBJS-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c new file mode 100644 index 0000000..245f482 --- /dev/null +++ b/drivers/mmc/arm_pl180_mmci.c @@ -0,0 +1,441 @@ +/* + * ARM PrimeCell MultiMedia Card Interface - PL180 + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson ulf.hansson@stericsson.com + * Author: Martin Lundholm martin.xa.lundholm@stericsson.com + * Ported to drivers/mmc/ by: Matt Waddel matt.waddel@linaro.org + * + * 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 + */ + +/* #define DEBUG */ + +#include <asm/io.h> +#include "common.h" +#include <errno.h> +#include <mmc.h> +#include "arm_pl180_mmci.h" +#include <malloc.h> + +struct mmc_host { + struct sdi_registers *base; +}; + +static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) +{ + u32 hoststatus, statusmask; + struct mmc_host *host = dev->priv; + + statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL; + if ((cmd->resp_type & MMC_RSP_PRESENT)) + statusmask |= SDI_STA_CMDREND; + else + statusmask |= SDI_STA_CMDSENT; + + do + hoststatus = readl(&host->base->status) & statusmask; + while (!hoststatus); + + writel(statusmask, &host->base->status_clear); + if (hoststatus & SDI_STA_CTIMEOUT) { + printf("CMD%d time out\n", cmd->cmdidx); + return -ETIMEDOUT; + } else if ((hoststatus & SDI_STA_CCRCFAIL) && + (cmd->flags & MMC_RSP_CRC)) { + printf("CMD%d CRC error\n", cmd->cmdidx); + return -EILSEQ; + } + + if (cmd->resp_type & MMC_RSP_PRESENT) { + cmd->response[0] = readl(&host->base->response0); + cmd->response[1] = readl(&host->base->response1); + cmd->response[2] = readl(&host->base->response2); + cmd->response[3] = readl(&host->base->response3); + debug("CMD%d response[0]:0x%08X, response[1]:0x%08X, " + "response[2]:0x%08X, response[3]:0x%08X\n", + cmd->cmdidx, cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); + } + + return 0; +} + +/* send command to the mmc card and wait for results */ +static int do_command(struct mmc *dev, struct mmc_cmd *cmd) +{ + int result; + u32 sdi_cmd = 0; + struct mmc_host *host = dev->priv; + + sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); + + if (cmd->resp_type) { + sdi_cmd |= SDI_CMD_WAITRESP; + if (cmd->resp_type & MMC_RSP_136) + sdi_cmd |= SDI_CMD_LONGRESP; + } + + writel((u32)cmd->cmdarg, &host->base->argument); + udelay(COMMAND_REG_DELAY); + writel(sdi_cmd, &host->base->command); + result = wait_for_command_end(dev, cmd); + + /* After CMD2 set RCA to a none zero value. */ + if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)) + dev->rca = 10; + + /* After CMD3 open drain is switched off and push pull is used. */ + if ((result == 0) && (cmd->cmdidx == MMC_CMD_SET_RELATIVE_ADDR)) { + u32 sdi_pwr = readl(&host->base->power) & ~SDI_PWR_OPD; + writel(sdi_pwr, &host->base->power); + } + + return result; +} + +static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) +{ + u32 *tempbuff = dest; + int i; + u64 xfercount = blkcount * blksize; + struct mmc_host *host = dev->priv; + u32 status, status_err; + + debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); + + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | + SDI_STA_RXOVERR); + while (!status_err && + (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32))) { + if (status & SDI_STA_RXFIFOBR) { + for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) + *(tempbuff + i) = readl(&host->base->fifo); + tempbuff += SDI_FIFO_BURST_SIZE; + xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); + } + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_RXOVERR); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Read data blk CRC error: 0x%x\n", status); + return -EILSEQ; + } else if (status & SDI_STA_RXOVERR) { + printf("Read data RX overflow error\n"); + return -EIO; + } + + while ((!status_err) && (xfercount >= sizeof(u32))) { + if (status & SDI_STA_RXDAVL) { + *(tempbuff) = readl(&host->base->fifo); + tempbuff++; + xfercount -= sizeof(u32); + } + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | + SDI_STA_RXOVERR); + } + + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | + SDI_STA_RXOVERR); + while (!status_err) { + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND | + SDI_STA_RXOVERR); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Read data timed out, xfercount: %llu, status: 0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Read data bytes CRC error: 0x%x\n", status); + return -EILSEQ; + } else if (status & SDI_STA_RXOVERR) { + printf("Read data RX overflow error\n"); + return -EIO; + } + + writel(SDI_ICR_MASK, &host->base->status_clear); + + if (xfercount) { + printf("Read data error, xfercount: %llu\n", xfercount); + return -ENOBUFS; + } + + return 0; +} + +static int write_bytes(struct mmc *dev, u32 *src, u32 blkcount, u32 blksize) +{ + u32 *tempbuff = src; + int i; + u64 xfercount = blkcount * blksize; + struct mmc_host *host = dev->priv; + u32 status, status_err; + + debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); + + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); + while (!status_err && xfercount) { + if (status & SDI_STA_TXFIFOBW) { + if (xfercount >= SDI_FIFO_BURST_SIZE * sizeof(u32)) { + for (i = 0; i < SDI_FIFO_BURST_SIZE; i++) + writel(*(tempbuff + i), + &host->base->fifo); + tempbuff += SDI_FIFO_BURST_SIZE; + xfercount -= SDI_FIFO_BURST_SIZE * sizeof(u32); + } else { + while (xfercount >= sizeof(u32)) { + writel(*(tempbuff), &host->base->fifo); + tempbuff++; + xfercount -= sizeof(u32); + } + } + } + status = readl(&host->base->status); + status_err = status & (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT); + } + + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); + while (!status_err) { + status = readl(&host->base->status); + status_err = status & + (SDI_STA_DCRCFAIL | SDI_STA_DTIMEOUT | SDI_STA_DBCKEND); + } + + if (status & SDI_STA_DTIMEOUT) { + printf("Write data timed out, xfercount:%llu,status:0x%08X\n", + xfercount, status); + return -ETIMEDOUT; + } else if (status & SDI_STA_DCRCFAIL) { + printf("Write data CRC error\n"); + return -EILSEQ; + } + + writel(SDI_ICR_MASK, &host->base->status_clear); + + if (xfercount) { + printf("Write data error, xfercount:%llu", xfercount); + return -ENOBUFS; + } + + return 0; +} + +static int do_data_transfer(struct mmc *dev, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int error = -ETIMEDOUT; + struct mmc_host *host = dev->priv; + u32 blksz = 0; + u32 data_ctrl = 0; + u32 data_len = (u32) (data->blocks * data->blocksize); + + blksz = (ffs(data->blocksize) - 1); + data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); + data_ctrl |= SDI_DCTRL_DTEN; + + writel(SDI_DTIMER_DEFAULT, &host->base->datatimer); + writel(data_len, &host->base->datalength); + udelay(DATA_REG_DELAY); + + if (data->flags & MMC_DATA_READ) { + data_ctrl |= SDI_DCTRL_DTDIR_IN; + writel(data_ctrl, &host->base->datactrl); + + error = do_command(dev, cmd); + if (error) + return error; + + error = read_bytes(dev, (u32 *)data->dest, (u32)data->blocks, + (u32)data->blocksize); + } else if (data->flags & MMC_DATA_WRITE) { + error = do_command(dev, cmd); + if (error) + return error; + + writel(data_ctrl, &host->base->datactrl); + error = write_bytes(dev, (u32 *)data->src, (u32)data->blocks, + (u32)data->blocksize); + } + + return error; +} + +static int host_request(struct mmc *dev, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int result; + + if (data) + result = do_data_transfer(dev, cmd, data); + else + result = do_command(dev, cmd); + + return result; +} + +/* MMC uses open drain drivers in the enumeration phase */ +static int mmc_host_reset(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_u32 = SDI_PWR_OPD | SDI_PWR_PWRCTRL_ON; + + writel(sdi_u32, &host->base->power); + + return 0; +} + +static void host_set_ios(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_clkcr; + + sdi_clkcr = readl(&host->base->clock); + + /* Ramp up the clock rate */ + if (dev->clock) { + u32 clkdiv = 0; + + if (dev->clock >= dev->f_max) + dev->clock = dev->f_max; + + clkdiv = ((ARM_MCLK / dev->clock) / 2) - 1; + + if (clkdiv > SDI_CLKCR_CLKDIV_MASK) + clkdiv = SDI_CLKCR_CLKDIV_MASK; + + sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); + sdi_clkcr |= clkdiv; + } + + /* Set the bus width */ + if (dev->bus_width) { + u32 buswidth = 0; + + switch (dev->bus_width) { + case 1: + buswidth |= SDI_CLKCR_WIDBUS_1; + break; + case 4: + buswidth |= SDI_CLKCR_WIDBUS_4; + break; + default: + printf("Invalid bus width\n"); + break; + } + sdi_clkcr &= ~(SDI_CLKCR_WIDBUS_MASK); + sdi_clkcr |= buswidth; + } + + writel(sdi_clkcr, &host->base->clock); + udelay(CLK_CHANGE_DELAY); +} + +struct mmc *alloc_mmc_struct(void) +{ + struct mmc_host *host = NULL; + struct mmc *mmc_device = NULL; + + host = malloc(sizeof(struct mmc_host)); + if (!host) + return NULL; + + mmc_device = malloc(sizeof(struct mmc)); + if (!mmc_device) + goto err; + + mmc_device->priv = host; + return mmc_device; + +err: + free(host); + return NULL; +} + +/* + * mmc_host_init - initialize the mmc controller. + * Set initial clock and power for mmc slot. + * Initialize mmc struct and register with mmc framework. + */ +static int arm_pl180_mmci_host_init(struct mmc *dev) +{ + struct mmc_host *host = dev->priv; + u32 sdi_u32; + + host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE; + + /* Initially set power-on, full voltage & MMCI read */ + sdi_u32 = INIT_PWR; + writel(sdi_u32, &host->base->power); + + /* setting clk freq 505KHz */ + sdi_u32 = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN; + writel(sdi_u32, &host->base->clock); + udelay(CLK_CHANGE_DELAY); + + /* Disable mmc interrupts */ + sdi_u32 = readl(&host->base->mask0) & ~SDI_MASK0_MASK; + writel(sdi_u32, &host->base->mask0); + + sprintf(dev->name, "MMC"); + dev->clock = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT + 1)); + dev->send_cmd = host_request; + dev->set_ios = host_set_ios; + dev->init = mmc_host_reset; + dev->host_caps = 0; + dev->voltages = VOLTAGE_WINDOW_MMC; + dev->f_min = dev->clock; + dev->f_max = CONFIG_ARM_PL180_MMCI_CLOCK_FREQ; + + return 0; +} + +int arm_pl180_mmci_init(void) +{ + int error; + struct mmc *dev; + + dev = alloc_mmc_struct(); + if (!dev) + return -1; + + error = arm_pl180_mmci_host_init(dev); + if (error) { + printf("mmci_host_init error - %d\n", error); + return -1; + } + + mmc_register(dev); + debug("registered mmc interface number is:%d\n", dev->block_dev.dev); + + return 0; +} diff --git a/drivers/mmc/arm_pl180_mmci.h b/drivers/mmc/arm_pl180_mmci.h new file mode 100644 index 0000000..42fbe3e --- /dev/null +++ b/drivers/mmc/arm_pl180_mmci.h @@ -0,0 +1,183 @@ +/* + * ARM PrimeCell MultiMedia Card Interface - PL180 + * + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Ulf Hansson ulf.hansson@stericsson.com + * Author: Martin Lundholm martin.xa.lundholm@stericsson.com + * Ported to drivers/mmc/ by: Matt Waddel matt.waddel@linaro.org + * + * 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 __ARM_PL180_MMCI_H__ +#define __ARM_PL180_MMCI_H__ + +int arm_pl180_mmci_init(void); + +#define COMMAND_REG_DELAY 300 +#define DATA_REG_DELAY 1000 +#define CLK_CHANGE_DELAY 2000 + +#define INIT_PWR 0xBF /* Power on, full power, not open drain */ +#define ARM_MCLK (100*1000*1000) + +/* SDI Power Control register bits */ +#define SDI_PWR_PWRCTRL_MASK 0x00000003 +#define SDI_PWR_PWRCTRL_ON 0x00000003 +#define SDI_PWR_PWRCTRL_OFF 0x00000000 +#define SDI_PWR_DAT2DIREN 0x00000004 +#define SDI_PWR_CMDDIREN 0x00000008 +#define SDI_PWR_DAT0DIREN 0x00000010 +#define SDI_PWR_DAT31DIREN 0x00000020 +#define SDI_PWR_OPD 0x00000040 +#define SDI_PWR_FBCLKEN 0x00000080 +#define SDI_PWR_DAT74DIREN 0x00000100 +#define SDI_PWR_RSTEN 0x00000200 + +#define VOLTAGE_WINDOW_MMC 0x00FF8080 +#define VOLTAGE_WINDOW_SD 0x80010000 + +/* SDI clock control register bits */ +#define SDI_CLKCR_CLKDIV_MASK 0x000000FF +#define SDI_CLKCR_CLKEN 0x00000100 +#define SDI_CLKCR_PWRSAV 0x00000200 +#define SDI_CLKCR_BYPASS 0x00000400 +#define SDI_CLKCR_WIDBUS_MASK 0x00001800 +#define SDI_CLKCR_WIDBUS_1 0x00000000 +#define SDI_CLKCR_WIDBUS_4 0x00000800 + +#define SDI_CLKCR_CLKDIV_INIT 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */ + +/* SDI command register bits */ +#define SDI_CMD_CMDINDEX_MASK 0x000000FF +#define SDI_CMD_WAITRESP 0x00000040 +#define SDI_CMD_LONGRESP 0x00000080 +#define SDI_CMD_WAITINT 0x00000100 +#define SDI_CMD_WAITPEND 0x00000200 +#define SDI_CMD_CPSMEN 0x00000400 +#define SDI_CMD_SDIOSUSPEND 0x00000800 +#define SDI_CMD_ENDCMDCOMPL 0x00001000 +#define SDI_CMD_NIEN 0x00002000 +#define SDI_CMD_CE_ATACMD 0x00004000 +#define SDI_CMD_CBOOTMODEEN 0x00008000 + +#define SDI_DTIMER_DEFAULT 0xFFFF0000 + +/* SDI Status register bits */ +#define SDI_STA_CCRCFAIL 0x00000001 +#define SDI_STA_DCRCFAIL 0x00000002 +#define SDI_STA_CTIMEOUT 0x00000004 +#define SDI_STA_DTIMEOUT 0x00000008 +#define SDI_STA_TXUNDERR 0x00000010 +#define SDI_STA_RXOVERR 0x00000020 +#define SDI_STA_CMDREND 0x00000040 +#define SDI_STA_CMDSENT 0x00000080 +#define SDI_STA_DATAEND 0x00000100 +#define SDI_STA_STBITERR 0x00000200 +#define SDI_STA_DBCKEND 0x00000400 +#define SDI_STA_CMDACT 0x00000800 +#define SDI_STA_TXACT 0x00001000 +#define SDI_STA_RXACT 0x00002000 +#define SDI_STA_TXFIFOBW 0x00004000 +#define SDI_STA_RXFIFOBR 0x00008000 +#define SDI_STA_TXFIFOF 0x00010000 +#define SDI_STA_RXFIFOF 0x00020000 +#define SDI_STA_TXFIFOE 0x00040000 +#define SDI_STA_RXFIFOE 0x00080000 +#define SDI_STA_TXDAVL 0x00100000 +#define SDI_STA_RXDAVL 0x00200000 +#define SDI_STA_SDIOIT 0x00400000 +#define SDI_STA_CEATAEND 0x00800000 +#define SDI_STA_CARDBUSY 0x01000000 +#define SDI_STA_BOOTMODE 0x02000000 +#define SDI_STA_BOOTACKERR 0x04000000 +#define SDI_STA_BOOTACKTIMEOUT 0x08000000 +#define SDI_STA_RSTNEND 0x10000000 + +/* SDI Interrupt Clear register bits */ +#define SDI_ICR_MASK 0x1DC007FF +#define SDI_ICR_CCRCFAILC 0x00000001 +#define SDI_ICR_DCRCFAILC 0x00000002 +#define SDI_ICR_CTIMEOUTC 0x00000004 +#define SDI_ICR_DTIMEOUTC 0x00000008 +#define SDI_ICR_TXUNDERRC 0x00000010 +#define SDI_ICR_RXOVERRC 0x00000020 +#define SDI_ICR_CMDRENDC 0x00000040 +#define SDI_ICR_CMDSENTC 0x00000080 +#define SDI_ICR_DATAENDC 0x00000100 +#define SDI_ICR_STBITERRC 0x00000200 +#define SDI_ICR_DBCKENDC 0x00000400 +#define SDI_ICR_SDIOITC 0x00400000 +#define SDI_ICR_CEATAENDC 0x00800000 +#define SDI_ICR_BUSYENDC 0x01000000 +#define SDI_ICR_BOOTACKERRC 0x04000000 +#define SDI_ICR_BOOTACKTIMEOUTC 0x08000000 +#define SDI_ICR_RSTNENDC 0x10000000 + +#define SDI_MASK0_MASK 0x1FFFFFFF + +/* SDI Data control register bits */ +#define SDI_DCTRL_DTEN 0x00000001 +#define SDI_DCTRL_DTDIR_IN 0x00000002 +#define SDI_DCTRL_DTMODE_STREAM 0x00000004 +#define SDI_DCTRL_DMAEN 0x00000008 +#define SDI_DCTRL_DBLKSIZE_MASK 0x000000F0 +#define SDI_DCTRL_RWSTART 0x00000100 +#define SDI_DCTRL_RWSTOP 0x00000200 +#define SDI_DCTRL_RWMOD 0x00000200 +#define SDI_DCTRL_SDIOEN 0x00000800 +#define SDI_DCTRL_DMAREQCTL 0x00001000 +#define SDI_DCTRL_DBOOTMODEEN 0x00002000 +#define SDI_DCTRL_BUSYMODE 0x00004000 +#define SDI_DCTRL_DDR_MODE 0x00008000 + +#define SDI_FIFO_BURST_SIZE 8 + +struct sdi_registers { + u32 power; /* 0x00*/ + u32 clock; /* 0x04*/ + u32 argument; /* 0x08*/ + u32 command; /* 0x0c*/ + u32 respcommand; /* 0x10*/ + u32 response0; /* 0x14*/ + u32 response1; /* 0x18*/ + u32 response2; /* 0x1c*/ + u32 response3; /* 0x20*/ + u32 datatimer; /* 0x24*/ + u32 datalength; /* 0x28*/ + u32 datactrl; /* 0x2c*/ + u32 datacount; /* 0x30*/ + u32 status; /* 0x34*/ + u32 status_clear; /* 0x38*/ + u32 mask0; /* 0x3c*/ + u32 mask1; /* 0x40*/ + u32 card_select; /* 0x44*/ + u32 fifo_count; /* 0x48*/ + u32 padding1[(0x80-0x4C)>>2]; + u32 fifo; /* 0x80*/ + u32 padding2[(0xFE0-0x84)>>2]; + u32 periph_id0; /* 0xFE0 mmc Peripheral Identifcation Register*/ + u32 periph_id1; /* 0xFE4*/ + u32 periph_id2; /* 0xFE8*/ + u32 periph_id3; /* 0xFEC*/ + u32 pcell_id0; /* 0xFF0*/ + u32 pcell_id1; /* 0xFF4*/ + u32 pcell_id2; /* 0xFF8*/ + u32 pcell_id3; /* 0xFFC*/ +}; + +#endif

From: Matt Waddel matt.waddel@linaro.org
Added the board specific definitions to use the MMCI device.
Signed-off-by: Matt Waddel matt.waddel@linaro.org --- board/armltd/vexpress/ca9x4_ct_vxp.c | 9 +++++++++ include/configs/ca9x4_ct_vxp.h | 4 ++++ 2 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/board/armltd/vexpress/ca9x4_ct_vxp.c b/board/armltd/vexpress/ca9x4_ct_vxp.c index ce1be1e..3566b95 100644 --- a/board/armltd/vexpress/ca9x4_ct_vxp.c +++ b/board/armltd/vexpress/ca9x4_ct_vxp.c @@ -86,6 +86,15 @@ int board_eth_init(bd_t *bis) return rc; }
+int cpu_mmc_init(bd_t *bis) +{ + int rc = 0; +#ifdef CONFIG_ARM_PL180_MMCI + rc = arm_pl180_mmci_init(); +#endif + return rc; +} + static void flash__init(void) { /* Setup the sytem control register to allow writing to flash */ diff --git a/include/configs/ca9x4_ct_vxp.h b/include/configs/ca9x4_ct_vxp.h index 63f003d..4ab58c0 100644 --- a/include/configs/ca9x4_ct_vxp.h +++ b/include/configs/ca9x4_ct_vxp.h @@ -86,6 +86,10 @@ #define CONFIG_MMC 1 #define CONFIG_CMD_MMC #define CONFIG_GENERIC_MMC +#define CONFIG_ARM_PL180_MMCI +#define CONFIG_ARM_PL180_MMCI_BASE 0x10005000 +#define CONFIG_SYS_MMC_MAX_BLK_COUNT 127 +#define CONFIG_ARM_PL180_MMCI_CLOCK_FREQ 6250000
/* BOOTP options */ #define CONFIG_BOOTP_BOOTFILESIZE
participants (6)
-
Andy Fleming
-
Andy Fleming
-
John Rigby
-
Matt Waddel
-
matt.waddel@linaro.org
-
Reinhard Meyer