From mboxrd@z Thu Jan 1 00:00:00 1970 From: Kuo-Jung Su Date: Thu, 18 Apr 2013 17:25:33 +0800 Subject: [U-Boot] [PATCH v2 06/12] mmc: add an alternative driver to Faraday FTSDC010 In-Reply-To: <1366277139-29728-1-git-send-email-dantesu@gmail.com> References: <1366277139-29728-1-git-send-email-dantesu@gmail.com> Message-ID: <1366277139-29728-7-git-send-email-dantesu@gmail.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de From: Kuo-Jung Su Faraday FTSDC010 is a MMC/SD host controller. Although there is already a driver in current u-boot release, which is modified from eSHDC and contributed by Andes Tech. Its performance is too terrible on Faraday A36x SoC platforms, so I turn to implement this new version of driver which is 10+ times faster than the old one. If the hardware platform is avaiable, you may modify the 'include/configs/a369_defaults.h' for performance evaluation. Here is the code snapshot of the 'a369_defaults.h' #if 1 #define CONFIG_FTSDC010_MCI 1 #define CONFIG_FTSDC010_SDIO 1 /* The hardware core supports SDIO */ #else #define CONFIG_FTSDC010 1 #define CONFIG_FTSDC010_SDIO 1 /* The hardware core supports SDIO */ #define CONFIG_FTSDC010_NUMBER 1 #define CONFIG_SYS_CLK_FREQ 133000000 /* AHB clock */ #endif #define CONFIG_MMC 1 #define CONFIG_CMD_MMC 1 #define CONFIG_GENERIC_MMC 1 Signed-off-by: Kuo-Jung Su CC: Andy Fleming --- drivers/mmc/Makefile | 1 + drivers/mmc/ftsdc010_mci.c | 373 ++++++++++++++++++++++++++++++++++++++++++++ include/faraday/ftsdc010.h | 16 +- include/faraday/mmc.h | 16 ++ 4 files changed, 403 insertions(+), 3 deletions(-) create mode 100644 drivers/mmc/ftsdc010_mci.c create mode 100644 include/faraday/mmc.h diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 1d6faa2..52908bd 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -33,6 +33,7 @@ COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o COBJS-$(CONFIG_FTSDC010) += ftsdc010_esdhc.o +COBJS-$(CONFIG_FTSDC010_MCI) += ftsdc010_mci.o COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o diff --git a/drivers/mmc/ftsdc010_mci.c b/drivers/mmc/ftsdc010_mci.c new file mode 100644 index 0000000..52c5a71 --- /dev/null +++ b/drivers/mmc/ftsdc010_mci.c @@ -0,0 +1,373 @@ +/* + * Faraday MMC/SD Host Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define SD_READ(r) le32_to_cpu(readl(r)) +#define SD_WRITE(v, r) writel(cpu_to_le32(v), r) +#define SD_SETBITS(m, r) setbits_le32(r, m) +#define SD_CLRBITS(m, r) clrbits_le32(r, m) + +struct ftsdc010_chip { + void *iobase; + uint32_t wprot; /* write protected (locked) */ + uint32_t rate; /* actual SD clock in Hz */ + uint32_t sclk; /* FTSDC010 source clock in Hz */ + uint32_t fifo; /* fifo depth in bytes */ + uint32_t acmd; +}; + +static inline int +ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc *regs = chip->iobase; + int ret = -TIMEOUT; + uint32_t ts, st; + uint32_t cmd = FTSDC010_CMD_IDX(mmc_cmd->cmdidx); + uint32_t arg = mmc_cmd->cmdarg; + uint32_t flags = mmc_cmd->resp_type; + + cmd |= FTSDC010_CMD_CMD_EN; + + if (chip->acmd) { + cmd |= FTSDC010_CMD_APP_CMD; + chip->acmd = 0; + } + + if (flags & MMC_RSP_PRESENT) + cmd |= FTSDC010_CMD_NEED_RSP; + + if (flags & MMC_RSP_136) + cmd |= FTSDC010_CMD_LONG_RSP; + + SD_WRITE(FTSDC010_STATUS_RSP_MASK | FTSDC010_STATUS_CMD_SEND, + ®s->clr); + SD_WRITE(arg, ®s->argu); + SD_WRITE(cmd, ®s->cmd); + + if (!(flags & (MMC_RSP_PRESENT | MMC_RSP_136))) { + for (ts = get_timer(0); get_timer(ts) < 100; ) { + if (SD_READ(®s->status) & FTSDC010_STATUS_CMD_SEND) { + SD_WRITE(FTSDC010_STATUS_CMD_SEND, ®s->clr); + ret = 0; + break; + } + } + } else { + st = 0; + for (ts = get_timer(0); get_timer(ts) < 100; ) { + st = SD_READ(®s->status); + SD_WRITE(st & FTSDC010_STATUS_RSP_MASK, ®s->clr); + if (st & FTSDC010_STATUS_RSP_MASK) + break; + } + if (st & FTSDC010_STATUS_RSP_CRC_OK) { + if (flags & MMC_RSP_136) { + mmc_cmd->response[0] = SD_READ(®s->rsp3); + mmc_cmd->response[1] = SD_READ(®s->rsp2); + mmc_cmd->response[2] = SD_READ(®s->rsp1); + mmc_cmd->response[3] = SD_READ(®s->rsp0); + } else { + mmc_cmd->response[0] = SD_READ(®s->rsp0); + } + ret = 0; + } else { + debug("ftsdc010: rsp err (cmd=%d, st=0x%x)\n", + mmc_cmd->cmdidx, st); + } + } + + if (ret) { + debug("ftsdc010: cmd timeout (op code=%d)\n", + mmc_cmd->cmdidx); + } else if (mmc_cmd->cmdidx == MMC_CMD_APP_CMD) { + chip->acmd = 1; + } + + return ret; +} + +static int +ftsdc010_wait(struct mmc *mmc) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc *regs = chip->iobase; + int ret = -TIMEOUT; + ulong ts; + + for (ts = get_timer(0); get_timer(ts) < 100; ) { + uint32_t st = SD_READ(®s->status); + SD_WRITE(st & FTSDC010_STATUS_DATA_MASK, ®s->clr); + + if (st & FTSDC010_STATUS_DATA_ERROR) { + debug("ftsdc010: data error!(st=0x%x)\n", st); + break; + } else if (st & FTSDC010_STATUS_DATA_END) { + ret = 0; + break; + } + } + + if (ret) + debug("ftsdc010: wait timeout\n"); + + return ret; +} + +static void +ftsdc010_clkset(struct ftsdc010_chip *chip, uint32_t rate) +{ + uint32_t div; + uint32_t clk = chip->sclk; + struct ftsdc010_mmc *regs = chip->iobase; + + for (div = 0; div < 0x7f; ++div) { + if (rate >= clk / (2 * (div + 1))) + break; + } + SD_WRITE(FTSDC010_CCR_CLK_SD | FTSDC010_CCR_CLK_DIV(div), ®s->ccr); + + chip->rate = clk / (2 * (div + 1)); +} + +static inline int +ftsdc010_is_ro(struct mmc *mmc) +{ + struct ftsdc010_chip *chip = mmc->priv; + const uint8_t *csd = (const uint8_t *)mmc->csd; + + if (chip->wprot || (csd[1] & 0x30)) + return 1; + + return 0; +} + +/* + * u-boot mmc api + */ + +static int ftsdc010_request(struct mmc *mmc, + struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int ret = -UNUSABLE_ERR; + uint32_t len = 0; + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc *regs = chip->iobase; + + if (data && (data->flags & MMC_DATA_WRITE) && chip->wprot) { + printf("ftsdc010: the card is write protected!\n"); + return ret; + } + + if (data) { + uint32_t dcr; + + len = data->blocksize * data->blocks; + + /* 1. data disable + fifo reset */ + SD_WRITE(FTSDC010_DCR_FIFO_RST, ®s->dcr); + + /* 2. clear status register */ + SD_WRITE(FTSDC010_STATUS_DATA_MASK | FTSDC010_STATUS_FIFO_URUN + | FTSDC010_STATUS_FIFO_ORUN, ®s->clr); + + /* 3. data timeout (1 sec) */ + SD_WRITE(chip->rate, ®s->dtr); + + /* 4. data length (bytes) */ + SD_WRITE(len, ®s->dlr); + + /* 5. data enable */ + dcr = (ffs(data->blocksize) - 1) | FTSDC010_DCR_DATA_EN; + if (data->flags & MMC_DATA_WRITE) + dcr |= FTSDC010_DCR_DATA_WRITE; + SD_WRITE(dcr, ®s->dcr); + } + + ret = ftsdc010_send_cmd(mmc, cmd); + if (ret) { + printf("ftsdc010: sending CMD%d failed\n", cmd->cmdidx); + return ret; + } + + if (!data) + return ret; + + if (data->flags & MMC_DATA_WRITE) { + const uint8_t *buf = (const uint8_t *)data->src; + + while (len > 0) { + int wlen; + uint32_t mask = FTSDC010_STATUS_FIFO_URUN; + + /* wait data ready */ + while (!(SD_READ(®s->status) & mask)) + ; + SD_WRITE(mask, ®s->clr); + + /* write bytes to ftsdc010 */ + for (wlen = 0; wlen < len && wlen < chip->fifo; ) { + SD_WRITE(*(uint32_t *)buf, ®s->dwr); + buf += 4; + wlen += 4; + } + + len -= wlen; + } + + } else { + uint8_t *buf = (uint8_t *)data->dest; + + while (len > 0) { + int rlen; + uint32_t mask = FTSDC010_STATUS_FIFO_ORUN; + + /* wait data ready */ + while (!(SD_READ(®s->status) & mask)) + ; + SD_WRITE(mask, ®s->clr); + + /* fetch bytes from ftsdc010 */ + for (rlen = 0; rlen < len && rlen < chip->fifo; ) { + *(uint32_t *)buf = SD_READ(®s->dwr); + buf += 4; + rlen += 4; + } + + len -= rlen; + } + + } + + return ftsdc010_wait(mmc); +} + +static void ftsdc010_set_ios(struct mmc *mmc) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc *regs = chip->iobase; + + ftsdc010_clkset(chip, mmc->clock); + + if (mmc->clock > 25000000) + SD_SETBITS(FTSDC010_CCR_CLK_HISPD, ®s->ccr); + else + SD_CLRBITS(FTSDC010_CCR_CLK_HISPD, ®s->ccr); + + SD_CLRBITS(0x07, ®s->bwr); + switch (mmc->bus_width) { + case 4: + SD_SETBITS(FTSDC010_BWR_WIDE_4_BUS, ®s->bwr); + break; + case 8: + SD_SETBITS(FTSDC010_BWR_WIDE_8_BUS, ®s->bwr); + break; + default: + SD_SETBITS(FTSDC010_BWR_SINGLE_BUS, ®s->bwr); + break; + } +} + +static int ftsdc010_init(struct mmc *mmc) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc *regs = chip->iobase; + + if (SD_READ(®s->status) & FTSDC010_STATUS_CARD_DETECT) + return NO_CARD_ERR; + + if (SD_READ(®s->status) & FTSDC010_STATUS_WRITE_PROT) { + printf("ftsdc010: write protected\n"); + chip->wprot = 1; + } + + chip->fifo = (SD_READ(®s->feature) & 0xff) << 2; + + /* 1. chip reset */ + SD_WRITE(FTSDC010_CMD_SDC_RST, ®s->cmd); + while (SD_READ(®s->cmd) & FTSDC010_CMD_SDC_RST) + ; + + /* 2. enter low speed mode (400k card detection) */ + ftsdc010_clkset(chip, 400000); + + /* 3. interrupt disabled */ + SD_WRITE(0, ®s->int_mask); + + return 0; +} + +int ftsdc010_mmc_init(int devid) +{ + struct mmc *mmc = NULL; + struct ftsdc010_chip *chip = NULL; + struct ftsdc010_mmc *regs = NULL; + + mmc = malloc(sizeof(struct mmc)); + if (!mmc) + return -ENOMEM; + memset(mmc, 0, sizeof(struct mmc)); + + chip = malloc(sizeof(struct ftsdc010_chip)); + if (!chip) { + free(mmc); + return -ENOMEM; + } + memset(chip, 0, sizeof(struct ftsdc010_chip)); + + chip->iobase = (void *)(CONFIG_FTSDC010_BASE + (devid << 20)); + mmc->priv = chip; + + regs = chip->iobase; + + sprintf(mmc->name, "ftsdc010"); + mmc->send_cmd = ftsdc010_request; + mmc->set_ios = ftsdc010_set_ios; + mmc->init = ftsdc010_init; + + switch ((SD_READ(®s->bwr) >> 3) & 3) { + case 1: + mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz + | MMC_MODE_4BIT; + break; + case 2: + mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz + | MMC_MODE_4BIT | MMC_MODE_8BIT; + break; + default: + mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz; + break; + } + +#ifdef CONFIG_SYS_CLK_FREQ + chip->sclk = CONFIG_SYS_CLK_FREQ; +#else + chip->sclk = clk_get_rate("SDC"); +#endif + + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->f_max = chip->sclk / 2; + mmc->f_min = chip->sclk / 0x100; + mmc->block_dev.part_type = PART_TYPE_DOS; + + mmc_register(mmc); + + return 0; +} diff --git a/include/faraday/ftsdc010.h b/include/faraday/ftsdc010.h index c34dde7..d8c289c 100644 --- a/include/faraday/ftsdc010.h +++ b/include/faraday/ftsdc010.h @@ -23,6 +23,9 @@ #define __FTSDC010_H #ifndef __ASSEMBLY__ + +#include "mmc.h" + /* sd controller register */ struct ftsdc010_mmc { unsigned int cmd; /* 0x00 - command reg */ @@ -67,9 +70,6 @@ struct mmc_host { unsigned int card_type; /* Card type */ }; -/* functions */ -int ftsdc010_mmc_init(int dev_index); - #endif /* __ASSEMBLY__ */ /* global defines */ @@ -143,6 +143,15 @@ int ftsdc010_mmc_init(int dev_index); #define FTSDC010_STATUS_SDIO_IRPT (1 << 16) /* SDIO card intr */ #define FTSDC010_STATUS_DATA0_STATUS (1 << 17) #endif /* CONFIG_FTSDC010_SDIO */ +#define FTSDC010_STATUS_RSP_ERROR \ + (FTSDC010_STATUS_RSP_CRC_FAIL | FTSDC010_STATUS_RSP_TIMEOUT) +#define FTSDC010_STATUS_RSP_MASK \ + (FTSDC010_STATUS_RSP_ERROR | FTSDC010_STATUS_RSP_CRC_OK) +#define FTSDC010_STATUS_DATA_ERROR \ + (FTSDC010_STATUS_DATA_CRC_FAIL | FTSDC010_STATUS_DATA_TIMEOUT) +#define FTSDC010_STATUS_DATA_MASK \ + (FTSDC010_STATUS_DATA_ERROR | FTSDC010_STATUS_DATA_CRC_OK \ + | FTSDC010_STATUS_DATA_END) /* 0x2c - clear register */ #define FTSDC010_CLR_RSP_CRC_FAIL (1 << 0) @@ -192,6 +201,7 @@ int ftsdc010_mmc_init(int dev_index); #define FTSDC010_CCR_CLK_DIV(x) (((x) & 0x7f) << 0) #define FTSDC010_CCR_CLK_SD (1 << 7) /* 0: MMC, 1: SD */ #define FTSDC010_CCR_CLK_DIS (1 << 8) +#define FTSDC010_CCR_CLK_HISPD (1 << 9) /* high speed */ /* card type */ #define FTSDC010_CARD_TYPE_SD FTSDC010_CLOCK_REG_CARD_TYPE diff --git a/include/faraday/mmc.h b/include/faraday/mmc.h new file mode 100644 index 0000000..ed09292 --- /dev/null +++ b/include/faraday/mmc.h @@ -0,0 +1,16 @@ +/* + * Faraday MMC/SD Host Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#ifndef _FARADAY_MMC_H +#define _FARADAY_MMC_H + +int ftsdc010_mmc_init(int dev_index); + +#endif /* _FARADAY_MMC_H */ -- 1.7.9.5