diff options
Diffstat (limited to 'drivers/mmc')
| -rw-r--r-- | drivers/mmc/Makefile | 2 | ||||
| -rw-r--r-- | drivers/mmc/arm_pl180_mmci.c | 443 | ||||
| -rw-r--r-- | drivers/mmc/arm_pl180_mmci.h | 183 | ||||
| -rw-r--r-- | drivers/mmc/bfin_sdh.c | 15 | ||||
| -rw-r--r-- | drivers/mmc/davinci_mmc.c | 3 | ||||
| -rw-r--r-- | drivers/mmc/fsl_esdhc.c | 18 | ||||
| -rw-r--r-- | drivers/mmc/gen_atmel_mci.c | 2 | ||||
| -rw-r--r-- | drivers/mmc/mmc.c | 262 | ||||
| -rw-r--r-- | drivers/mmc/mmc_spi.c | 280 | ||||
| -rw-r--r-- | drivers/mmc/mxcmmc.c | 2 | ||||
| -rw-r--r-- | drivers/mmc/omap_hsmmc.c | 10 | ||||
| -rw-r--r-- | drivers/mmc/s5p_mmc.c | 1 |
12 files changed, 1163 insertions, 58 deletions
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 3496f0aa0..a8fe17a6f 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -31,6 +31,8 @@ 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_MMC_SPI) += mmc_spi.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 000000000..ed296ee02 --- /dev/null +++ b/drivers/mmc/arm_pl180_mmci.c @@ -0,0 +1,443 @@ +/* + * 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; + } + + dev->b_max = 0; + + 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 000000000..42fbe3e38 --- /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 diff --git a/drivers/mmc/bfin_sdh.c b/drivers/mmc/bfin_sdh.c index 27d9bf6cb..bc9057fa9 100644 --- a/drivers/mmc/bfin_sdh.c +++ b/drivers/mmc/bfin_sdh.c @@ -19,7 +19,7 @@ #include <asm/mach-common/bits/sdh.h> #include <asm/mach-common/bits/dma.h> -#if defined(__ADSPBF51x__) +#if defined(__ADSPBF50x__) || defined(__ADSPBF51x__) # define bfin_read_SDH_PWR_CTL bfin_read_RSI_PWR_CONTROL # define bfin_write_SDH_PWR_CTL bfin_write_RSI_PWR_CONTROL # define bfin_read_SDH_CLK_CTL bfin_read_RSI_CLK_CONTROL @@ -114,25 +114,26 @@ static int sdh_setup_data(struct mmc *mmc, struct mmc_data *data) u16 data_ctl = 0; u16 dma_cfg = 0; int ret = 0; + unsigned long data_size = data->blocksize * data->blocks; /* Don't support write yet. */ if (data->flags & MMC_DATA_WRITE) return UNUSABLE_ERR; - data_ctl |= ((ffs(data->blocksize) - 1) << 4); + data_ctl |= ((ffs(data_size) - 1) << 4); data_ctl |= DTX_DIR; bfin_write_SDH_DATA_CTL(data_ctl); dma_cfg = WDSIZE_32 | RESTART | WNR | DMAEN; - bfin_write_SDH_DATA_TIMER(0xFFFF); + bfin_write_SDH_DATA_TIMER(-1); blackfin_dcache_flush_invalidate_range(data->dest, - data->dest + data->blocksize); + data->dest + data_size); /* configure DMA */ bfin_write_DMA_START_ADDR(data->dest); - bfin_write_DMA_X_COUNT(data->blocksize / 4); + bfin_write_DMA_X_COUNT(data_size / 4); bfin_write_DMA_X_MODIFY(4); bfin_write_DMA_CONFIG(dma_cfg); - bfin_write_SDH_DATA_LGTH(data->blocksize); + bfin_write_SDH_DATA_LGTH(data_size); /* kick off transfer */ bfin_write_SDH_DATA_CTL(bfin_read_SDH_DATA_CTL() | DTX_DMA_E | DTX_E); @@ -256,6 +257,8 @@ int bfin_mmc_init(bd_t *bis) mmc->f_min = mmc->f_max >> 9; mmc->block_dev.part_type = PART_TYPE_DOS; + mmc->b_max = 0; + mmc_register(mmc); return 0; diff --git a/drivers/mmc/davinci_mmc.c b/drivers/mmc/davinci_mmc.c index d5d19ebee..5d918e6ff 100644 --- a/drivers/mmc/davinci_mmc.c +++ b/drivers/mmc/davinci_mmc.c @@ -394,9 +394,8 @@ int davinci_mmc_init(bd_t *bis, struct davinci_mmc *host) mmc->voltages = host->voltages; mmc->host_caps = host->host_caps; -#ifdef CONFIG_MMC_MBLOCK mmc->b_max = DAVINCI_MAX_BLOCKS; -#endif + mmc_register(mmc); return 0; diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 0962ac447..d2355be6b 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -99,6 +99,10 @@ uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) else if (cmd->resp_type & MMC_RSP_PRESENT) xfertyp |= XFERTYP_RSPTYP_48; +#ifdef CONFIG_MX53 + if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) + xfertyp |= XFERTYP_CMDTYP_ABORT; +#endif return XFERTYP_CMD(cmd->cmdidx) | xfertyp; } @@ -178,14 +182,14 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) wml_value = data->blocksize/4; if (data->flags & MMC_DATA_READ) { - if (wml_value > 0x10) - wml_value = 0x10; + if (wml_value > WML_RD_WML_MAX) + wml_value = WML_RD_WML_MAX_VAL; esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value); esdhc_write32(®s->dsaddr, (u32)data->dest); } else { - if (wml_value > 0x80) - wml_value = 0x80; + if (wml_value > WML_WR_WML_MAX) + wml_value = WML_WR_WML_MAX_VAL; if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); return TIMEOUT; @@ -332,11 +336,11 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) do { irqstat = esdhc_read32(®s->irqstat); - if (irqstat & DATA_ERR) - return COMM_ERR; - if (irqstat & IRQSTAT_DTOE) return TIMEOUT; + + if (irqstat & DATA_ERR) + return COMM_ERR; } while (!(irqstat & IRQSTAT_TC) && (esdhc_read32(®s->prsstat) & PRSSTAT_DLA)); #endif diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index 2984d645c..6577925b8 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -348,6 +348,8 @@ int atmel_mci_init(void *regs) mmc->f_min = get_mci_clk_rate() / (2*256); mmc->f_max = get_mci_clk_rate() / (2*1); + mmc->b_max = 0; + mmc_register(mmc); return 0; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 6805b33f7..f6d31f584 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -30,9 +30,13 @@ #include <part.h> #include <malloc.h> #include <linux/list.h> -#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; @@ -45,7 +49,100 @@ int board_mmc_getcd(u8 *cd, struct mmc *mmc)__attribute__((weak, int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { +#ifdef CONFIG_MMC_TRACE + int ret; + int i; + u8 *ptr; + + printf("CMD_SEND:%d\n", cmd->cmdidx); + printf("\t\tARG\t\t\t 0x%08X\n", cmd->cmdarg); + printf("\t\tFLAG\t\t\t %d\n", cmd->flags); + ret = mmc->send_cmd(mmc, cmd, data); + switch (cmd->resp_type) { + case MMC_RSP_NONE: + printf("\t\tMMC_RSP_NONE\n"); + break; + case MMC_RSP_R1: + printf("\t\tMMC_RSP_R1,5,6,7 \t 0x%08X \n", + cmd->response[0]); + break; + case MMC_RSP_R1b: + printf("\t\tMMC_RSP_R1b\t\t 0x%08X \n", + cmd->response[0]); + break; + case MMC_RSP_R2: + printf("\t\tMMC_RSP_R2\t\t 0x%08X \n", + cmd->response[0]); + printf("\t\t \t\t 0x%08X \n", + cmd->response[1]); + printf("\t\t \t\t 0x%08X \n", + cmd->response[2]); + printf("\t\t \t\t 0x%08X \n", + cmd->response[3]); + printf("\n"); + printf("\t\t\t\t\tDUMPING DATA\n"); + for (i = 0; i < 4; i++) { + int j; + printf("\t\t\t\t\t%03d - ", i*4); + ptr = &cmd->response[i]; + ptr += 3; + for (j = 0; j < 4; j++) + printf("%02X ", *ptr--); + printf("\n"); + } + break; + case MMC_RSP_R3: + printf("\t\tMMC_RSP_R3,4\t\t 0x%08X \n", + cmd->response[0]); + break; + default: + printf("\t\tERROR MMC rsp not supported\n"); + break; + } + return ret; +#else return mmc->send_cmd(mmc, cmd, data); +#endif +} + +int mmc_send_status(struct mmc *mmc, int timeout) +{ + struct mmc_cmd cmd; + int err; +#ifdef CONFIG_MMC_TRACE + int status; +#endif + + cmd.cmdidx = MMC_CMD_SEND_STATUS; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 0; + cmd.flags = 0; + + do { + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + return err; + else if (cmd.response[0] & MMC_STATUS_RDY_FOR_DATA) + break; + + udelay(1000); + + if (cmd.response[0] & MMC_STATUS_MASK) { + printf("Status Error: 0x%08X\n", cmd.response[0]); + return COMM_ERR; + } + } while (timeout--); + +#ifdef CONFIG_MMC_TRACE + status = (cmd.response[0] & MMC_STATUS_CURR_STATE) >> 9; + printf("CURR STATE:%d\n", status); +#endif + if (!timeout) { + printf("Timeout waiting card ready\n"); + return TIMEOUT; + } + + return 0; } int mmc_set_blocklen(struct mmc *mmc, int len) @@ -82,6 +179,7 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) { struct mmc_cmd cmd; struct mmc_data data; + int timeout = 1000; if ((start + blkcnt) > mmc->block_dev.lba) { printf("MMC: block number 0x%lx exceeds max(0x%lx)\n", @@ -112,7 +210,10 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) return 0; } - if (blkcnt > 1) { + /* SPI multiblock writes terminate using a special + * token, not a STOP_TRANSMISSION request. + */ + if (!mmc_host_is_spi(mmc) && blkcnt > 1) { cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1b; @@ -121,6 +222,9 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) printf("mmc fail to send stop cmd\n"); return 0; } + + /* Waiting for the ready status */ + mmc_send_status(mmc, timeout); } return blkcnt; @@ -139,11 +243,7 @@ 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 > mmc->b_max) ? mmc->b_max : blocks_todo; if(mmc_write_blocks(mmc, start, cur, src) != cur) return 0; blocks_todo -= cur; @@ -158,6 +258,7 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt) { struct mmc_cmd cmd; struct mmc_data data; + int timeout = 1000; if (blkcnt > 1) cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK; @@ -189,6 +290,9 @@ int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, lbaint_t blkcnt) printf("mmc fail to send stop cmd\n"); return 0; } + + /* Waiting for the ready status */ + mmc_send_status(mmc, timeout); } return blkcnt; @@ -215,11 +319,7 @@ 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 > mmc->b_max) ? mmc->b_max : blocks_todo; if(mmc_read_blocks(mmc, dst, start, cur) != cur) return 0; blocks_todo -= cur; @@ -280,7 +380,8 @@ sd_send_op_cond(struct mmc *mmc) * how to manage low voltages SD card is not yet * specified. */ - cmd.cmdarg = mmc->voltages & 0xff8000; + cmd.cmdarg = mmc_host_is_spi(mmc) ? 0 : + (mmc->voltages & 0xff8000); if (mmc->version == SD_VERSION_2) cmd.cmdarg |= OCR_HCS; @@ -299,6 +400,18 @@ sd_send_op_cond(struct mmc *mmc) if (mmc->version != SD_VERSION_2) mmc->version = SD_VERSION_1_0; + if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ + cmd.cmdidx = MMC_CMD_SPI_READ_OCR; + cmd.resp_type = MMC_RSP_R3; + cmd.cmdarg = 0; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } + mmc->ocr = cmd.response[0]; mmc->high_capacity = ((mmc->ocr & OCR_HCS) == OCR_HCS); @@ -309,17 +422,33 @@ sd_send_op_cond(struct mmc *mmc) int mmc_send_op_cond(struct mmc *mmc) { - int timeout = 1000; + int timeout = 10000; struct mmc_cmd cmd; int err; /* Some cards seem to need this */ mmc_go_idle(mmc); + /* Asking to the card its capabilities */ + cmd.cmdidx = MMC_CMD_SEND_OP_COND; + cmd.resp_type = MMC_RSP_R3; + cmd.cmdarg = 0; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + + udelay(1000); + do { cmd.cmdidx = MMC_CMD_SEND_OP_COND; cmd.resp_type = MMC_RSP_R3; - cmd.cmdarg = OCR_HCS | mmc->voltages; + cmd.cmdarg = (mmc_host_is_spi(mmc) ? 0 : + (mmc->voltages & + (cmd.response[0] & OCR_VOLTAGE_MASK)) | + (cmd.response[0] & OCR_ACCESS_MODE)); cmd.flags = 0; err = mmc_send_cmd(mmc, &cmd, NULL); @@ -333,6 +462,18 @@ int mmc_send_op_cond(struct mmc *mmc) if (timeout <= 0) return UNUSABLE_ERR; + if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ + cmd.cmdidx = MMC_CMD_SPI_READ_OCR; + cmd.resp_type = MMC_RSP_R3; + cmd.cmdarg = 0; + cmd.flags = 0; + + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } + mmc->version = MMC_VERSION_UNKNOWN; mmc->ocr = cmd.response[0]; @@ -369,15 +510,23 @@ int mmc_send_ext_csd(struct mmc *mmc, char *ext_csd) int mmc_switch(struct mmc *mmc, u8 set, u8 index, u8 value) { struct mmc_cmd cmd; + int timeout = 1000; + int ret; cmd.cmdidx = MMC_CMD_SWITCH; cmd.resp_type = MMC_RSP_R1b; cmd.cmdarg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (index << 16) | - (value << 8); + (index << 16) | + (value << 8); cmd.flags = 0; - return mmc_send_cmd(mmc, &cmd, NULL); + ret = mmc_send_cmd(mmc, &cmd, NULL); + + /* Waiting for the ready status */ + mmc_send_status(mmc, timeout); + + return ret; + } int mmc_change_freq(struct mmc *mmc) @@ -388,6 +537,9 @@ int mmc_change_freq(struct mmc *mmc) mmc->card_caps = 0; + if (mmc_host_is_spi(mmc)) + return 0; + /* Only version 4 supports high-speed */ if (mmc->version < MMC_VERSION_4) return 0; @@ -399,9 +551,6 @@ int mmc_change_freq(struct mmc *mmc) if (err) return err; - if (ext_csd[212] || ext_csd[213] || ext_csd[214] || ext_csd[215]) - mmc->high_capacity = 1; - cardtype = ext_csd[196] & 0xf; err = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, 1); @@ -461,6 +610,9 @@ int sd_change_freq(struct mmc *mmc) mmc->card_caps = 0; + if (mmc_host_is_spi(mmc)) + return 0; + /* Read the SCR to find out if this card supports higher speeds */ cmd.cmdidx = MMC_CMD_APP_CMD; cmd.resp_type = MMC_RSP_R1; @@ -512,6 +664,9 @@ retry_scr: break; } + if (mmc->scr[0] & SD_DATA_4BIT) + mmc->card_caps |= MMC_MODE_4BIT; + /* Version 1.0 doesn't support switching */ if (mmc->version == SD_VERSION_1_0) return 0; @@ -529,9 +684,6 @@ retry_scr: break; } - if (mmc->scr[0] & SD_DATA_4BIT) - mmc->card_caps |= MMC_MODE_4BIT; - /* If high-speed isn't supported, we return */ if (!(__be32_to_cpu(switch_status[3]) & SD_HIGHSPEED_SUPPORTED)) return 0; @@ -549,7 +701,7 @@ retry_scr: /* frequency bases */ /* divided by 10 to be nice to platforms without floating point */ -int fbase[] = { +static const int fbase[] = { 10000, 100000, 1000000, @@ -559,7 +711,7 @@ int fbase[] = { /* Multiplier values for TRAN_SPEED. Multiplied by 10 to be nice * to platforms without floating point. */ -int multipliers[] = { +static const int multipliers[] = { 0, /* reserved */ 10, 12, @@ -610,9 +762,24 @@ int mmc_startup(struct mmc *mmc) u64 cmult, csize; struct mmc_cmd cmd; char ext_csd[512]; + int timeout = 1000; + +#ifdef CONFIG_MMC_SPI_CRC_ON + if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */ + cmd.cmdidx = MMC_CMD_SPI_CRC_ON_OFF; + cmd.resp_type = MMC_RSP_R1; + cmd.cmdarg = 1; + cmd.flags = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); + + if (err) + return err; + } +#endif /* Put the Card in Identify Mode */ - cmd.cmdidx = MMC_CMD_ALL_SEND_CID; + cmd.cmdidx = mmc_host_is_spi(mmc) ? MMC_CMD_SEND_CID : + MMC_CMD_ALL_SEND_CID; /* cmd not supported in spi */ cmd.resp_type = MMC_RSP_R2; cmd.cmdarg = 0; cmd.flags = 0; @@ -629,18 +796,20 @@ int mmc_startup(struct mmc *mmc) * For SD cards, get the Relatvie Address. * This also puts the cards into Standby State */ - cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; - cmd.cmdarg = mmc->rca << 16; - cmd.resp_type = MMC_RSP_R6; - cmd.flags = 0; + if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ + cmd.cmdidx = SD_CMD_SEND_RELATIVE_ADDR; + cmd.cmdarg = mmc->rca << 16; + cmd.resp_type = MMC_RSP_R6; + cmd.flags = 0; - err = mmc_send_cmd(mmc, &cmd, NULL); + err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; + if (err) + return err; - if (IS_SD(mmc)) - mmc->rca = (cmd.response[0] >> 16) & 0xffff; + if (IS_SD(mmc)) + mmc->rca = (cmd.response[0] >> 16) & 0xffff; + } /* Get the Card-Specific Data */ cmd.cmdidx = MMC_CMD_SEND_CSD; @@ -650,6 +819,9 @@ int mmc_startup(struct mmc *mmc) err = mmc_send_cmd(mmc, &cmd, NULL); + /* Waiting for the ready status */ + mmc_send_status(mmc, timeout); + if (err) return err; @@ -716,14 +888,16 @@ int mmc_startup(struct mmc *mmc) mmc->write_bl_len = 512; /* Select the card, and put it into Transfer Mode */ - cmd.cmdidx = MMC_CMD_SELECT_CARD; - cmd.resp_type = MMC_RSP_R1b; - cmd.cmdarg = mmc->rca << 16; - cmd.flags = 0; - err = mmc_send_cmd(mmc, &cmd, NULL); + if (!mmc_host_is_spi(mmc)) { /* cmd not supported in spi */ + cmd.cmdidx = MMC_CMD_SELECT_CARD; + cmd.resp_type = MMC_RSP_R1b; + cmd.cmdarg = mmc->rca << 16; + cmd.flags = 0; + err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - return err; + if (err) + return err; + } if (!IS_SD(mmc) && (mmc->version >= MMC_VERSION_4)) { /* check ext_csd version and capacity */ @@ -853,6 +1027,8 @@ int mmc_register(struct mmc *mmc) mmc->block_dev.removable = 1; mmc->block_dev.block_read = mmc_bread; mmc->block_dev.block_write = mmc_bwrite; + if (!mmc->b_max) + mmc->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; INIT_LIST_HEAD (&mmc->link); diff --git a/drivers/mmc/mmc_spi.c b/drivers/mmc/mmc_spi.c new file mode 100644 index 000000000..dc7574cbe --- /dev/null +++ b/drivers/mmc/mmc_spi.c @@ -0,0 +1,280 @@ +/* + * generic mmc spi driver + * + * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> + * Licensed under the GPL-2 or later. + */ +#include <common.h> +#include <malloc.h> +#include <part.h> +#include <mmc.h> +#include <spi.h> +#include <crc.h> +#include <linux/crc7.h> +#include <linux/byteorder/swab.h> + +/* MMC/SD in SPI mode reports R1 status always */ +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) +/* R1 bit 7 is always zero, reuse this bit for error */ +#define R1_SPI_ERROR (1 << 7) + +/* Response tokens used to ack each block written: */ +#define SPI_MMC_RESPONSE_CODE(x) ((x) & 0x1f) +#define SPI_RESPONSE_ACCEPTED ((2 << 1)|1) +#define SPI_RESPONSE_CRC_ERR ((5 << 1)|1) +#define SPI_RESPONSE_WRITE_ERR ((6 << 1)|1) + +/* Read and write blocks start with these tokens and end with crc; + * on error, read tokens act like a subset of R2_SPI_* values. + */ +#define SPI_TOKEN_SINGLE 0xfe /* single block r/w, multiblock read */ +#define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock write */ +#define SPI_TOKEN_STOP_TRAN 0xfd /* terminate multiblock write */ + +/* MMC SPI commands start with a start bit "0" and a transmit bit "1" */ +#define MMC_SPI_CMD(x) (0x40 | (x & 0x3f)) + +/* bus capability */ +#define MMC_SPI_VOLTAGE (MMC_VDD_32_33 | MMC_VDD_33_34) +#define MMC_SPI_MIN_CLOCK 400000 /* 400KHz to meet MMC spec */ + +/* timeout value */ +#define CTOUT 8 +#define RTOUT 3000000 /* 1 sec */ +#define WTOUT 3000000 /* 1 sec */ + +static uint mmc_spi_sendcmd(struct mmc *mmc, ushort cmdidx, u32 cmdarg) +{ + struct spi_slave *spi = mmc->priv; + u8 cmdo[7]; + u8 r1; + int i; + cmdo[0] = 0xff; + cmdo[1] = MMC_SPI_CMD(cmdidx); + cmdo[2] = cmdarg >> 24; + cmdo[3] = cmdarg >> 16; + cmdo[4] = cmdarg >> 8; + cmdo[5] = cmdarg; + cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01; + spi_xfer(spi, sizeof(cmdo) * 8, cmdo, NULL, 0); + for (i = 0; i < CTOUT; i++) { + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (i && (r1 & 0x80) == 0) /* r1 response */ + break; + } + debug("%s:cmd%d resp%d %x\n", __func__, cmdidx, i, r1); + return r1; +} + +static uint mmc_spi_readdata(struct mmc *mmc, void *xbuf, + u32 bcnt, u32 bsize) +{ + struct spi_slave *spi = mmc->priv; + u8 *buf = xbuf; + u8 r1; + u16 crc; + int i; + while (bcnt--) { + for (i = 0; i < RTOUT; i++) { + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (r1 != 0xff) /* data token */ + break; + } + debug("%s:tok%d %x\n", __func__, i, r1); + if (r1 == SPI_TOKEN_SINGLE) { + spi_xfer(spi, bsize * 8, NULL, buf, 0); + spi_xfer(spi, 2 * 8, NULL, &crc, 0); +#ifdef CONFIG_MMC_SPI_CRC_ON + if (swab16(cyg_crc16(buf, bsize)) != crc) { + debug("%s: CRC error\n", mmc->name); + r1 = R1_SPI_COM_CRC; + break; + } +#endif + r1 = 0; + } else { + r1 = R1_SPI_ERROR; + break; + } + buf += bsize; + } + return r1; +} + +static uint mmc_spi_writedata(struct mmc *mmc, const void *xbuf, + u32 bcnt, u32 bsize, int multi) +{ + struct spi_slave *spi = mmc->priv; + const u8 *buf = xbuf; + u8 r1; + u16 crc; + u8 tok[2]; + int i; + tok[0] = 0xff; + tok[1] = multi ? SPI_TOKEN_MULTI_WRITE : SPI_TOKEN_SINGLE; + while (bcnt--) { +#ifdef CONFIG_MMC_SPI_CRC_ON + crc = swab16(cyg_crc16((u8 *)buf, bsize)); +#endif + spi_xfer(spi, 2 * 8, tok, NULL, 0); + spi_xfer(spi, bsize * 8, buf, NULL, 0); + spi_xfer(spi, 2 * 8, &crc, NULL, 0); + for (i = 0; i < CTOUT; i++) { + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if ((r1 & 0x10) == 0) /* response token */ + break; + } + debug("%s:tok%d %x\n", __func__, i, r1); + if (SPI_MMC_RESPONSE_CODE(r1) == SPI_RESPONSE_ACCEPTED) { + for (i = 0; i < WTOUT; i++) { /* wait busy */ + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (i && r1 == 0xff) { + r1 = 0; + break; + } + } + if (i == WTOUT) { + debug("%s:wtout %x\n", __func__, r1); + r1 = R1_SPI_ERROR; + break; + } + } else { + debug("%s: err %x\n", __func__, r1); + r1 = R1_SPI_COM_CRC; + break; + } + buf += bsize; + } + if (multi && bcnt == -1) { /* stop multi write */ + tok[1] = SPI_TOKEN_STOP_TRAN; + spi_xfer(spi, 2 * 8, tok, NULL, 0); + for (i = 0; i < WTOUT; i++) { /* wait busy */ + spi_xfer(spi, 1 * 8, NULL, &r1, 0); + if (i && r1 == 0xff) { + r1 = 0; + break; + } + } + if (i == WTOUT) { + debug("%s:wstop %x\n", __func__, r1); + r1 = R1_SPI_ERROR; + } + } + return r1; +} + +static int mmc_spi_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct spi_slave *spi = mmc->priv; + u8 r1; + int i; + int ret = 0; + debug("%s:cmd%d %x %x %x\n", __func__, + cmd->cmdidx, cmd->resp_type, cmd->cmdarg, cmd->flags); + spi_claim_bus(spi); + spi_cs_activate(spi); + r1 = mmc_spi_sendcmd(mmc, cmd->cmdidx, cmd->cmdarg); + if (r1 == 0xff) { /* no response */ + ret = NO_CARD_ERR; + goto done; + } else if (r1 & R1_SPI_COM_CRC) { + ret = COMM_ERR; + goto done; + } else if (r1 & ~R1_SPI_IDLE) { /* other errors */ + ret = TIMEOUT; + goto done; + } else if (cmd->resp_type == MMC_RSP_R2) { + r1 = mmc_spi_readdata(mmc, cmd->response, 1, 16); + for (i = 0; i < 4; i++) + cmd->response[i] = swab32(cmd->response[i]); + debug("r128 %x %x %x %x\n", cmd->response[0], cmd->response[1], + cmd->response[2], cmd->response[3]); + } else if (!data) { + switch (cmd->cmdidx) { + case SD_CMD_APP_SEND_OP_COND: + case MMC_CMD_SEND_OP_COND: + cmd->response[0] = (r1 & R1_SPI_IDLE) ? 0 : OCR_BUSY; + break; + case SD_CMD_SEND_IF_COND: + case MMC_CMD_SPI_READ_OCR: + spi_xfer(spi, 4 * 8, NULL, cmd->response, 0); + cmd->response[0] = swab32(cmd->response[0]); + debug("r32 %x\n", cmd->response[0]); + break; + } + } else { + debug("%s:data %x %x %x\n", __func__, + data->flags, data->blocks, data->blocksize); + if (data->flags == MMC_DATA_READ) + r1 = mmc_spi_readdata(mmc, data->dest, + data->blocks, data->blocksize); + else if (data->flags == MMC_DATA_WRITE) + r1 = mmc_spi_writedata(mmc, data->src, + data->blocks, data->blocksize, + (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)); + if (r1 & R1_SPI_COM_CRC) + ret = COMM_ERR; + else if (r1) /* other errors */ + ret = TIMEOUT; + } +done: + spi_cs_deactivate(spi); + spi_release_bus(spi); + return ret; +} + +static void mmc_spi_set_ios(struct mmc *mmc) +{ + struct spi_slave *spi = mmc->priv; + debug("%s: clock %u\n", __func__, mmc->clock); + if (mmc->clock) + spi_set_speed(spi, mmc->clock); +} + +static int mmc_spi_init_p(struct mmc *mmc) +{ + struct spi_slave *spi = mmc->priv; + mmc->clock = 0; + spi_set_speed(spi, MMC_SPI_MIN_CLOCK); + spi_claim_bus(spi); + /* cs deactivated for 100+ clock */ + spi_xfer(spi, 18 * 8, NULL, NULL, 0); + spi_release_bus(spi); + return 0; +} + +struct mmc *mmc_spi_init(uint bus, uint cs, uint speed, uint mode) +{ + struct mmc *mmc; + + mmc = malloc(sizeof(*mmc)); + if (!mmc) + return NULL; + memset(mmc, 0, sizeof(*mmc)); + mmc->priv = spi_setup_slave(bus, cs, speed, mode); + if (!mmc->priv) { + free(mmc); + return NULL; + } + sprintf(mmc->name, "MMC_SPI"); + mmc->send_cmd = mmc_spi_request; + mmc->set_ios = mmc_spi_set_ios; + mmc->init = mmc_spi_init_p; + mmc->host_caps = MMC_MODE_SPI; + + mmc->voltages = MMC_SPI_VOLTAGE; + mmc->f_max = speed; + mmc->f_min = MMC_SPI_MIN_CLOCK; + mmc->block_dev.part_type = PART_TYPE_DOS; + + mmc_register(mmc); + + return mmc; +} diff --git a/drivers/mmc/mxcmmc.c b/drivers/mmc/mxcmmc.c index 59639539f..ab1fc82fb 100644 --- a/drivers/mmc/mxcmmc.c +++ b/drivers/mmc/mxcmmc.c @@ -511,6 +511,8 @@ static int mxcmci_initialize(bd_t *bis) mmc->f_min = imx_get_perclk2() >> 7; mmc->f_max = imx_get_perclk2() >> 1; + mmc->b_max = 0; + mmc_register(mmc); return 0; diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 6f2280abf..957b9877a 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -465,6 +465,16 @@ int omap_mmc_init(int dev_index) mmc->f_min = 400000; mmc->f_max = 52000000; + mmc->b_max = 0; + +#if defined(CONFIG_OMAP34XX) + /* + * Silicon revs 2.1 and older do not support multiblock transfers. + */ + if ((get_cpu_family() == CPU_OMAP34XX) && (get_cpu_rev() <= CPU_3XX_ES21)) + mmc->b_max = 1; +#endif + mmc_register(mmc); return 0; diff --git a/drivers/mmc/s5p_mmc.c b/drivers/mmc/s5p_mmc.c index 032380071..668c28bde 100644 --- a/drivers/mmc/s5p_mmc.c +++ b/drivers/mmc/s5p_mmc.c @@ -466,6 +466,7 @@ static int s5p_mmc_initialize(int dev_index, int bus_width) mmc_host[dev_index].clock = 0; mmc_host[dev_index].reg = s5p_get_base_mmc(dev_index); + mmc->m_bmax = 0; mmc_register(mmc); return 0; |