Patchwork [U-Boot,10/11] mmc: add an alternative FTSDC010 driver support

login
register
mail settings
Submitter Kuo-Jung Su
Date March 29, 2013, 7:06 a.m.
Message ID <1364540788-13943-11-git-send-email-dantesu@gmail.com>
Download mbox | patch
Permalink /patch/232380/
State Superseded
Delegated to: Albert ARIBAUD
Headers show

Comments

Kuo-Jung Su - March 29, 2013, 7:06 a.m.
From: Kuo-Jung Su <dantesu@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 <dantesu@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

--
1.7.9.5

Patch

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 <dantesu@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 <dantesu@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