From 9c14306697190baa70987d4cac2886b68fd8ad2d Mon Sep 17 00:00:00 2001 From: John Rigby Date: Wed, 7 Mar 2012 18:24:05 -0700 Subject: MMC: arm_pl180_mmci: factor out plat specific info TODO: add the v2 stuff in a separate patch Signed-off-by: John Rigby --- board/armltd/vexpress/vexpress_common.c | 21 +++- board/st-ericsson/u8500/u8500_href.c | 20 +++- drivers/mmc/arm_pl180_mmci.c | 172 ++++++++++++++------------------ drivers/mmc/arm_pl180_mmci.h | 26 ++++- include/configs/vexpress_common.h | 2 +- 5 files changed, 138 insertions(+), 103 deletions(-) diff --git a/board/armltd/vexpress/vexpress_common.c b/board/armltd/vexpress/vexpress_common.c index 62815bdcf..95b5e7103 100644 --- a/board/armltd/vexpress/vexpress_common.c +++ b/board/armltd/vexpress/vexpress_common.c @@ -33,6 +33,8 @@ * MA 02111-1307 USA */ #include +#include +#include #include #include #include @@ -89,8 +91,25 @@ int board_eth_init(bd_t *bis) int cpu_mmc_init(bd_t *bis) { int rc = 0; + (void) bis; #ifdef CONFIG_ARM_PL180_MMCI - rc = arm_pl180_mmci_init(0); + struct pl180_mmc_host *host; + + host = malloc(sizeof(struct pl180_mmc_host)); + if (!host) + return -ENOMEM; + memset(host, 0, sizeof(*host)); + + strcpy(host->name, "MMC"); + host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE; + host->pwr_init = INIT_PWR; + host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN; + host->voltages = VOLTAGE_WINDOW_MMC; + host->caps = 0; + host->clock_in = ARM_MCLK; + host->clock_min = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT + 1)); + host->clock_max = CONFIG_ARM_PL180_MMCI_CLOCK_FREQ; + rc = arm_pl180_mmci_init(host); #endif return rc; } diff --git a/board/st-ericsson/u8500/u8500_href.c b/board/st-ericsson/u8500/u8500_href.c index 8170a8773..d53b226fd 100644 --- a/board/st-ericsson/u8500/u8500_href.c +++ b/board/st-ericsson/u8500/u8500_href.c @@ -448,12 +448,26 @@ static int u8500_mmci_board_init(void) int board_mmc_init(bd_t *bd) { + struct pl180_mmc_host *host; + if (u8500_mmci_board_init()) return -ENODEV; - if (arm_pl180_mmci_init(CONFIG_MMC_DEV_NUM)) - return -ENODEV; - return 0; + host = malloc(sizeof(struct pl180_mmc_host)); + if (!host) + return -ENOMEM; + memset(host, 0, sizeof(*host)); + + strcpy(host->name, "MMC"); + host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE; + host->pwr_init = INIT_PWR; + host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN; + host->voltages = VOLTAGE_WINDOW_MMC; + dev->caps = 0; + host->clock_in = ARM_MCLK; + host->clock_min = ARM_MCLK / (2 * (SDI_CLKCR_CLKDIV_INIT + 1)); + host->clock_max = CONFIG_ARM_PL180_MMCI_CLOCK_FREQ; + return arm_pl180_mmci_init(host); } #endif diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index 877fd3011..516de052b 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -32,14 +32,11 @@ #include "arm_pl180_mmci.h" #include -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; + struct pl180_mmc_host *host = dev->priv; statusmask = SDI_STA_CTIMEOUT | SDI_STA_CCRCFAIL; if ((cmd->resp_type & MMC_RSP_PRESENT)) @@ -51,10 +48,12 @@ static int wait_for_command_end(struct mmc *dev, struct mmc_cmd *cmd) hoststatus = readl(&host->base->status) & statusmask; while (!hoststatus); + debug("SDI_ICR <= 0x%08X\n", statusmask); + debug("status <= 0x%08X\n", hoststatus); writel(statusmask, &host->base->status_clear); if (hoststatus & SDI_STA_CTIMEOUT) { - printf("CMD%d time out\n", cmd->cmdidx); - return -ETIMEDOUT; + debug("CMD%d time out\n", cmd->cmdidx); + return TIMEOUT; } else if ((hoststatus & SDI_STA_CCRCFAIL) && (cmd->flags & MMC_RSP_CRC)) { printf("CMD%d CRC error\n", cmd->cmdidx); @@ -80,7 +79,11 @@ static int do_command(struct mmc *dev, struct mmc_cmd *cmd) { int result; u32 sdi_cmd = 0; - struct mmc_host *host = dev->priv; + struct pl180_mmc_host *host = dev->priv; + u32 lap = 0; + + debug("Request to do CMD%d on %s\n", cmd->cmdidx, + dev->name); sdi_cmd = ((cmd->cmdidx & SDI_CMD_CMDINDEX_MASK) | SDI_CMD_CPSMEN); @@ -90,10 +93,24 @@ static int do_command(struct mmc *dev, struct mmc_cmd *cmd) sdi_cmd |= SDI_CMD_LONGRESP; } + debug("SDI_ARG <= 0x%08X\n", cmd->cmdarg); writel((u32)cmd->cmdarg, &host->base->argument); udelay(COMMAND_REG_DELAY); - writel(sdi_cmd, &host->base->command); - result = wait_for_command_end(dev, cmd); + debug("SDI_CMD <= 0x%08X\n", sdi_cmd); + + /* + * It has been noticed that after a write operation some cards do + * not respond to a new command for a few milliseconds. So here we + * retry the command a couple of times if we get a timeout. + */ + do { + writel(sdi_cmd, &host->base->command); + result = wait_for_command_end(dev, cmd); + if ((result != TIMEOUT) || (lap >= 10)) + break; + udelay(1000); + lap++; + } while (1); /* After CMD2 set RCA to a none zero value. */ if ((result == 0) && (cmd->cmdidx == MMC_CMD_ALL_SEND_CID)) @@ -112,7 +129,7 @@ static int read_bytes(struct mmc *dev, u32 *dest, u32 blkcount, u32 blksize) { u32 *tempbuff = dest; u64 xfercount = blkcount * blksize; - struct mmc_host *host = dev->priv; + struct pl180_mmc_host *host = dev->priv; u32 status, status_err; debug("read_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); @@ -168,7 +185,7 @@ 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; + struct pl180_mmc_host *host = dev->priv; u32 status, status_err; debug("write_bytes: blkcount=%u blksize=%u\n", blkcount, blksize); @@ -227,14 +244,24 @@ static int do_data_transfer(struct mmc *dev, struct mmc_data *data) { int error = -ETIMEDOUT; - struct mmc_host *host = dev->priv; + struct pl180_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; + if (!host->version2) { + blksz = (ffs(data->blocksize) - 1); + data_ctrl |= ((blksz << 4) & SDI_DCTRL_DBLKSIZE_MASK); + } else { + blksz = data->blocksize; + data_ctrl |= (blksz << SDI_DCTRL_DBLOCKSIZE_V2_SHIFT); + } + data_ctrl |= SDI_DCTRL_DTEN | SDI_DCTRL_BUSYMODE; + +#ifdef DEBUG + if (data_ctrl & SDI_DCTRL_DDR_MODE) + printf("SDI_DCTRL_DDR_MODE\n"); +#endif writel(SDI_DTIMER_DEFAULT, &host->base->datatimer); writel(data_len, &host->base->datalength); @@ -280,17 +307,15 @@ static int host_request(struct mmc *dev, /* 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); + struct pl180_mmc_host *host = dev->priv; + writel(host->pwr_init, &host->base->power); return 0; } static void host_set_ios(struct mmc *dev) { - struct mmc_host *host = dev->priv; + struct pl180_mmc_host *host = dev->priv; u32 sdi_clkcr; sdi_clkcr = readl(&host->base->clock); @@ -298,15 +323,24 @@ static void host_set_ios(struct mmc *dev) /* Ramp up the clock rate */ if (dev->clock) { u32 clkdiv = 0; + u32 tmp_clock; - if (dev->clock >= dev->f_max) + debug("setting clock and bus width in the host:"); + if (dev->clock >= dev->f_max) { + clkdiv = 0; dev->clock = dev->f_max; - - clkdiv = ((ARM_MCLK / dev->clock) / 2) - 1; - + } else { + clkdiv = (host->clock_in / dev->clock) - 2; + } + tmp_clock = host->clock_in / (clkdiv + 2); + while (tmp_clock > dev->clock) { + clkdiv++; + tmp_clock = host->clock_in / (clkdiv + 2); + } if (clkdiv > SDI_CLKCR_CLKDIV_MASK) clkdiv = SDI_CLKCR_CLKDIV_MASK; - + tmp_clock = host->clock_in / (clkdiv + 2); + dev->clock = tmp_clock; sdi_clkcr &= ~(SDI_CLKCR_CLKDIV_MASK); sdi_clkcr |= clkdiv; } @@ -322,6 +356,9 @@ static void host_set_ios(struct mmc *dev) case 4: buswidth |= SDI_CLKCR_WIDBUS_4; break; + case 8: + buswidth |= SDI_CLKCR_WIDBUS_8; + break; default: printf("Invalid bus width\n"); break; @@ -334,96 +371,41 @@ static void host_set_ios(struct mmc *dev) 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(int index, struct mmc *dev) +int arm_pl180_mmci_init(struct pl180_mmc_host *host) { - struct mmc_host *host = dev->priv; + struct mmc *dev; u32 sdi_u32; - switch (index) { -#ifdef CONFIG_ARM_PL180_MMCI_BASE0 - case 0: - host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE0; - break; -#endif -#ifdef CONFIG_ARM_PL180_MMCI_BASE1 - case 1: - host->base = (struct sdi_registers *)CONFIG_ARM_PL180_MMCI_BASE1; - break; -#endif - default: - printf("Bad index in %s\n", __FUNCTION__); - return 1; - } + dev = malloc(sizeof(struct mmc)); + if (!dev) + return -ENOMEM; - /* Initially set power-on, full voltage & MMCI read */ - sdi_u32 = INIT_PWR; - writel(sdi_u32, &host->base->power); + memset(dev, 0, sizeof(struct mmc)); + dev->priv = host; - /* setting clk freq 505KHz */ - sdi_u32 = SDI_CLKCR_CLKDIV_INIT | SDI_CLKCR_CLKEN; - writel(sdi_u32, &host->base->clock); + writel(host->pwr_init, &host->base->power); + writel(host->clkdiv_init, &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)); + strncpy(dev->name, host->name, sizeof(dev->name)); dev->send_cmd = host_request; dev->set_ios = host_set_ios; dev->init = mmc_host_reset; dev->getcd = NULL; - 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(int index) -{ - int error; - struct mmc *dev; - - dev = alloc_mmc_struct(); - if (!dev) - return -1; - - error = arm_pl180_mmci_host_init(index, dev); - if (error) { - printf("mmci_host_init index %d error - %d\n", index, error); - return -1; - } - - dev->b_max = 0; + dev->host_caps = host->caps; + dev->voltages = host->voltages; + dev->f_min = host->clock_min; + dev->f_max = host->clock_max; + dev->b_max = host->b_max; mmc_register(dev); debug("registered mmc interface number is:%d\n", dev->block_dev.dev); diff --git a/drivers/mmc/arm_pl180_mmci.h b/drivers/mmc/arm_pl180_mmci.h index 43aef3481..1cbd24d27 100644 --- a/drivers/mmc/arm_pl180_mmci.h +++ b/drivers/mmc/arm_pl180_mmci.h @@ -26,8 +26,6 @@ #ifndef __ARM_PL180_MMCI_H__ #define __ARM_PL180_MMCI_H__ -int arm_pl180_mmci_init(int); - #define COMMAND_REG_DELAY 300 #define DATA_REG_DELAY 1000 #define CLK_CHANGE_DELAY 2000 @@ -59,8 +57,12 @@ int arm_pl180_mmci_init(int); #define SDI_CLKCR_WIDBUS_MASK 0x00001800 #define SDI_CLKCR_WIDBUS_1 0x00000000 #define SDI_CLKCR_WIDBUS_4 0x00000800 +/* V2 only */ +#define SDI_CLKCR_WIDBUS_8 0x00001000 +#define SDI_CLKCR_NEDGE 0x00002000 +#define SDI_CLKCR_HWFC_EN 0x00004000 -#define SDI_CLKCR_CLKDIV_INIT 0x000000C6 /* MCLK/(2*(0xC6+1)) => 505KHz */ +#define SDI_CLKCR_CLKDIV_INIT 0x000000FD /* SDI command register bits */ #define SDI_CMD_CMDINDEX_MASK 0x000000FF @@ -144,6 +146,8 @@ int arm_pl180_mmci_init(int); #define SDI_DCTRL_DBOOTMODEEN 0x00002000 #define SDI_DCTRL_BUSYMODE 0x00004000 #define SDI_DCTRL_DDR_MODE 0x00008000 +#define SDI_DCTRL_DBLOCKSIZE_V2_MASK 0x7fff0000 +#define SDI_DCTRL_DBLOCKSIZE_V2_SHIFT 16 #define SDI_FIFO_BURST_SIZE 8 @@ -180,4 +184,20 @@ struct sdi_registers { u32 pcell_id3; /* 0xFFC*/ }; +struct pl180_mmc_host { + struct sdi_registers *base; + char name[32]; + unsigned int b_max; + unsigned int voltages; + unsigned int caps; + unsigned int clock_in; + unsigned int clock_min; + unsigned int clock_max; + unsigned int clkdiv_init; + unsigned int pwr_init; + int version2; +}; + +int arm_pl180_mmci_init(struct pl180_mmc_host *); + #endif diff --git a/include/configs/vexpress_common.h b/include/configs/vexpress_common.h index 096929cfb..e81a379a3 100644 --- a/include/configs/vexpress_common.h +++ b/include/configs/vexpress_common.h @@ -185,7 +185,7 @@ #define CONFIG_CMD_MMC #define CONFIG_GENERIC_MMC #define CONFIG_ARM_PL180_MMCI -#define CONFIG_ARM_PL180_MMCI_BASE0 V2M_MMCI +#define CONFIG_ARM_PL180_MMCI_BASE V2M_MMCI #define CONFIG_SYS_MMC_MAX_BLK_COUNT 127 #define CONFIG_ARM_PL180_MMCI_CLOCK_FREQ 6250000 -- cgit v1.2.3