From: Kuo-Jung Su <dant...@faraday-tech.com>

Faraday FTSDC010 is a MMC/SD host controller.
There is already a driver in u-boot, which is
modified from eSHDC and contributed by Andes Tech.

However it works extreamly slow in Faraday A36x SoC
Platforms, so I turn to implement this new version
of driver with 10 times faster speed, and improved
stability.

Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com>
---
 drivers/mmc/Makefile       |    1 +
 drivers/mmc/ftsdc010_mci.c |  362 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/ftsdc010_mci.h |   91 +++++++++++
 3 files changed, 454 insertions(+)
 create mode 100644 drivers/mmc/ftsdc010_mci.c
 create mode 100644 drivers/mmc/ftsdc010_mci.h

diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 65791aa..dfe1b8c 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..d07e4ff
--- /dev/null
+++ b/drivers/mmc/ftsdc010_mci.c
@@ -0,0 +1,362 @@
+/*
+ * Faraday MMC/SD Host Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dant...@faraday-tech.com>
+ *
+ * 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 <common.h>
+#include <malloc.h>
+#include <part.h>
+#include <mmc.h>
+
+#include <asm/io.h>
+#include <asm/errno.h>
+#include <asm/byteorder.h>
+
+#include "ftsdc010_mci.h"
+
+#define SD_REG32(chip, off) \
+       *(volatile uint32_t *)((uint8_t *)(chip)->iobase + (off))
+
+struct ftsdc010_chip {
+       uint32_t 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;
+       uint32_t timeout;
+
+       uint32_t cmd   = mmc_cmd->cmdidx;
+       uint32_t arg   = mmc_cmd->cmdarg;
+       uint32_t flags = mmc_cmd->resp_type;
+
+       cmd |= CMD_EN;
+
+       if (chip->acmd) {
+               cmd |= CMD_APP;
+               chip->acmd = 0;
+       }
+
+       if (flags & MMC_RSP_PRESENT)
+               cmd |= CMD_WAIT_RSP;
+
+       if (flags & MMC_RSP_136)
+               cmd |= CMD_LONG_RSP;
+
+       SD_REG32(chip, REG_SCR) = SR_RSP_ERR | SR_RSP | SR_CMD;
+
+       SD_REG32(chip, REG_ARG) = arg;
+
+       SD_REG32(chip, REG_CMD) = cmd;
+
+       if ((flags & (MMC_RSP_PRESENT | MMC_RSP_136)) == 0) {
+               for (timeout = 250000; timeout > 0; --timeout) {
+                       if (SD_REG32(chip, REG_SR) & SR_CMD) {
+                               SD_REG32(chip, REG_SCR) = SR_CMD;
+                               break;
+                       }
+                       udelay(1);
+               }
+       } else {
+               for (timeout = 250000; timeout > 0; --timeout) {
+                       uint32_t st = SD_REG32(chip, REG_SR);
+                       if (st & SR_RSP) {
+                               SD_REG32(chip, REG_SCR) = SR_RSP;
+                               if (flags & MMC_RSP_136) {
+                                       mmc_cmd->response[0] = SD_REG32(chip, 
REG_RSP3);
+                                       mmc_cmd->response[1] = SD_REG32(chip, 
REG_RSP2);
+                                       mmc_cmd->response[2] = SD_REG32(chip, 
REG_RSP1);
+                                       mmc_cmd->response[3] = SD_REG32(chip, 
REG_RSP0);
+                               } else {
+                                       mmc_cmd->response[0] = SD_REG32(chip, 
REG_RSP0);
+                               }
+                               break;
+                       } else if (st & SR_RSP_ERR) {
+                               SD_REG32(chip, REG_SCR) = SR_RSP_ERR;
+                               debug("ftsdc010: rsp err (cmd=%d, st=0x%x)\n", 
mmc_cmd->cmdidx, st);
+                               return TIMEOUT;
+                       }
+                       udelay(1);
+               }
+       }
+
+       if (timeout == 0) {
+               debug("ftsdc010: cmd timeout (op code=%d)\n", mmc_cmd->cmdidx);
+               return TIMEOUT;
+       }
+
+       if (mmc_cmd->cmdidx == MMC_CMD_APP_CMD)
+               chip->acmd = 1;
+
+       return 0;
+}
+
+static int
+ftsdc010_wait(struct mmc *mmc)
+{
+       struct ftsdc010_chip *chip = mmc->priv;
+       uint32_t mask = SR_DAT | SR_DAT_END | SR_DAT_ERR;
+       uint32_t timeout;
+
+       for (timeout = 250000; timeout; --timeout) {
+               uint32_t st = SD_REG32(chip, REG_SR);
+               SD_REG32(chip, REG_SCR) = (st & mask);
+
+               if (st & SR_DAT_ERR) {
+                       printf("ftsdc010: data error!(st=0x%x)\n", st);
+                       return TIMEOUT;
+               } else if (st & SR_DAT_END) {
+                       break;
+               }
+               udelay(1);
+       }
+
+       if (timeout == 0) {
+               debug("ftsdc010: wait timeout\n");
+               return TIMEOUT;
+       }
+
+       return 0;
+}
+
+static void
+ftsdc010_clkset(struct ftsdc010_chip *chip, uint32_t rate)
+{
+       uint32_t div;
+       uint32_t clk = chip->sclk;
+
+       for (div = 0; div < 0x7F; ++div) {
+               if (rate >= clk / (2 * (div + 1)))
+                       break;
+       }
+       SD_REG32(chip, REG_CLK) = CLK_SD | div;
+
+       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 rc;
+       uint32_t len = 0;
+       struct ftsdc010_chip *chip = mmc->priv;
+
+       if (data && (data->flags & MMC_DATA_WRITE) && chip->wprot) {
+               printf("ftsdc010: the card is write protected!\n");
+               return UNUSABLE_ERR;
+       }
+
+       if (data) {
+               uint32_t dcr;
+
+               len = data->blocksize * data->blocks;
+
+               /* 1. data disable + fifo reset */
+               SD_REG32(chip, REG_DCR) = DCR_FIFO_RESET;
+
+               /* 2. clear status register */
+               SD_REG32(chip, REG_SCR) = SR_DAT_END | SR_DAT | SR_DAT_ERR | 
SR_TXRDY | SR_RXRDY;
+
+               /* 3. data timeout (1 sec) */
+               SD_REG32(chip, REG_DTR) = chip->rate;
+
+               /* 4. data length (bytes) */
+               SD_REG32(chip, REG_DLR) = len;
+
+               /* 5. data enable */
+               dcr = (ffs(data->blocksize) - 1) | DCR_EN;
+               if (data->flags & MMC_DATA_WRITE)
+                       dcr |= DCR_WR;
+               SD_REG32(chip, REG_DCR) = dcr;
+       }
+
+       rc = ftsdc010_send_cmd(mmc, cmd);
+       if (rc) {
+               printf("ftsdc010: sending CMD%d failed\n", cmd->cmdidx);
+               return rc;
+       }
+
+       if (!data)
+               return rc;
+
+       if (data->flags & MMC_DATA_WRITE) {
+               const uint8_t *buf = (const uint8_t *)data->src;
+
+               while (len > 0) {
+                       int wlen;
+
+                       /* wait data ready */
+                       while (!(SD_REG32(chip, REG_SR) & SR_TXRDY))
+                               ;
+                       SD_REG32(chip, REG_SCR) = SR_TXRDY;
+
+                       /* write bytes to ftsdc010 */
+                       for (wlen = 0; wlen < len && wlen < chip->fifo; ) {
+                               SD_REG32(chip, REG_DR) = *(uint32_t *)buf;
+                               buf  += 4;
+                               wlen += 4;
+                       }
+
+                       len -= wlen;
+               }
+
+       } else {
+               uint8_t *buf = (uint8_t *)data->dest;
+
+               while (len > 0) {
+                       int rlen;
+
+                       /* wait data ready */
+                       while (!(SD_REG32(chip, REG_SR) & SR_RXRDY))
+                               ;
+                       SD_REG32(chip, REG_SCR) = SR_RXRDY;
+
+                       /* fetch bytes from ftsdc010 */
+                       for (rlen = 0; rlen < len && rlen < chip->fifo; ) {
+                               *(uint32_t *)buf = SD_REG32(chip, REG_DR);
+                               buf  += 4;
+                               rlen += 4;
+                       }
+
+                       len -= rlen;
+               }
+
+       }
+
+       rc = ftsdc010_wait(mmc);
+       return rc;
+}
+
+static void ftsdc010_set_ios(struct mmc *mmc)
+{
+       struct ftsdc010_chip *chip = mmc->priv;
+
+       ftsdc010_clkset(chip, mmc->clock);
+
+       if (mmc->clock > 25000000)
+               SD_REG32(chip, REG_CLK) |= CLK_HISPD;
+       else
+               SD_REG32(chip, REG_CLK) &= ~CLK_HISPD;
+
+       SD_REG32(chip, REG_BUS) &= 0xFFFFFFF8;
+       switch (mmc->bus_width) {
+       case 4:
+               SD_REG32(chip, REG_BUS) |= 0x04;
+               break;
+       case 8:
+               SD_REG32(chip, REG_BUS) |= 0x02;
+               break;
+       default:
+               SD_REG32(chip, REG_BUS) |= 0x01;
+               break;
+       }
+}
+
+static int ftsdc010_init(struct mmc *mmc)
+{
+       struct ftsdc010_chip *chip = mmc->priv;
+
+       if (SD_REG32(chip, REG_SR) & SR_CARD_REMOVED)
+               return NO_CARD_ERR;
+
+       if (SD_REG32(chip, REG_SR) & SR_WPROT) {
+               printf("ftsdc010: write protected\n");
+               chip->wprot = 1;
+       }
+
+       chip->fifo = (SD_REG32(chip, REG_FEAR) & 0xFF) << 2;
+
+       /* 1. chip reset */
+       SD_REG32(chip, REG_CMD) = CMD_RST;
+       while (SD_REG32(chip, REG_CMD) & CMD_RST)
+               ;
+
+       /* 2. enter low speed mode (400k card detection) */
+       ftsdc010_clkset(chip, 400000);
+
+       /* 3. interrupt disabled */
+       SD_REG32(chip, REG_IER) = 0;
+
+       return 0;
+}
+
+int ftsdc010_mmc_init(int devid)
+{
+       struct mmc *mmc = NULL;
+       struct ftsdc010_chip *chip = 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 = CONFIG_FTSDC010_BASE + (devid << 20);
+       mmc->priv    = chip;
+
+       sprintf(mmc->name, "ftsdc010");
+       mmc->send_cmd = ftsdc010_request;
+       mmc->set_ios  = ftsdc010_set_ios;
+       mmc->init     = ftsdc010_init;
+
+       switch ((SD_REG32(chip, REG_BUS) >> 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/drivers/mmc/ftsdc010_mci.h b/drivers/mmc/ftsdc010_mci.h
new file mode 100644
index 0000000..86d8c9f
--- /dev/null
+++ b/drivers/mmc/ftsdc010_mci.h
@@ -0,0 +1,91 @@
+/*
+ * Faraday MMC/SD Host Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dant...@faraday-tech.com>
+ *
+ * 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 __FTSDC010_MCI_H
+#define __FTSDC010_MCI_H
+
+#include <faraday/ftsdc010.h>
+
+/* sd controller register */
+#define REG_CMD                0x0000
+#define REG_ARG                0x0004
+#define REG_RSP0               0x0008    /* response */
+#define REG_RSP1               0x000C
+#define REG_RSP2               0x0010
+#define REG_RSP3               0x0014
+#define REG_RSPCMD             0x0018    /* responsed command */
+#define REG_DCR                0x001C    /* data control */
+#define REG_DTR                0x0020    /* data timeout */
+#define REG_DLR                0x0024    /* data length */
+#define REG_SR                 0x0028    /* status register */
+#define REG_SCR                0x002C    /* status clear register */
+#define REG_IER                0x0030    /* interrupt mask/enable register */
+#define REG_PWR                0x0034    /* power control */
+#define REG_CLK                0x0038    /* clock control */
+#define REG_BUS                0x003C    /* bus width */
+#define REG_DR                 0x0040    /* data register */
+#define REG_GPOR               0x0048    /* general purpose output register */
+#define REG_FEAR               0x009C    /* feature register */
+#define REG_REVR               0x00A0    /* revision register */
+
+/* bit mapping of command register */
+#define CMD_IDX                0x0000003F
+#define CMD_WAIT_RSP           0x00000040
+#define CMD_LONG_RSP           0x00000080
+#define CMD_APP                0x00000100
+#define CMD_EN                 0x00000200
+#define CMD_RST                0x00000400
+
+/* bit mapping of response command register */
+#define RSP_CMDIDX             0x0000003F
+#define RSP_CMDAPP             0x00000040
+
+/* bit mapping of data control register */
+#define DCR_BKSZ               0x0000000F
+#define DCR_WR                 0x00000010
+#define DCR_RD                 0x00000000
+#define DCR_DMA                0x00000020
+#define DCR_EN                 0x00000040
+#define DCR_THRES              0x00000080
+#define DCR_BURST1             0x00000000
+#define DCR_BURST4             0x00000100
+#define DCR_BURST8             0x00000200
+#define DCR_FIFO_RESET         0x00000400
+
+/* bit mapping of status register */
+#define SR_RSP_CRC            0x00000001
+#define SR_DAT_CRC            0x00000002
+#define SR_RSP_TIMEOUT        0x00000004
+#define SR_DAT_TIMEOUT        0x00000008
+#define SR_RSP_ERR            (SR_RSP_CRC | SR_RSP_TIMEOUT)
+#define SR_DAT_ERR            (SR_DAT_CRC | SR_DAT_TIMEOUT)
+#define SR_RSP                0x00000010
+#define SR_DAT                0x00000020
+#define SR_CMD                0x00000040
+#define SR_DAT_END            0x00000080
+#define SR_TXRDY              0x00000100
+#define SR_RXRDY              0x00000200
+#define SR_CARD_CHANGE        0x00000400
+#define SR_CARD_REMOVED       0x00000800
+#define SR_WPROT              0x00001000
+#define SR_SDIO               0x00010000
+#define SR_DAT0               0x00020000
+
+/* bit mapping of clock register */
+#define CLK_HISPD              0x00000200
+#define CLK_OFF                0x00000100
+#define CLK_SD                 0x00000080
+
+/* bit mapping of bus register */
+#define BUS_CARD_DATA3         0x00000020
+#define BUS_4BITS_SUPP         0x00000008
+#define BUS_8BITS_SUPP         0x00000010
+
+#endif
--
1.7.9.5

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to