Patchwork [U-Boot,v2,06/12] mmc: add an alternative driver to Faraday FTSDC010

login
register
mail settings
Submitter Kuo-Jung Su
Date April 18, 2013, 9:25 a.m.
Message ID <1366277139-29728-7-git-send-email-dantesu@gmail.com>
Download mbox | patch
Permalink /patch/237576/
State Superseded
Delegated to: Albert ARIBAUD
Headers show

Comments

Kuo-Jung Su - April 18, 2013, 9:25 a.m.
From: Kuo-Jung Su <dantesu@faraday-tech.com>

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 <dantesu@faraday-tech.com>
CC: Andy Fleming <afleming@gmail.com>
---
 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

--
1.7.9.5
Wolfgang Denk - April 18, 2013, 10:57 a.m.
Dear Kuo-Jung Su,

In message <1366277139-29728-7-git-send-email-dantesu@gmail.com> you wrote:
> From: Kuo-Jung Su <dantesu@faraday-tech.com>
> 
> 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.

NAK.  Instead of adding another driver, please fix the problems of the
original one, or replace the old one with your new, better one.  We do
not want to have several drivers for the same piece of hardware.

Best regards,

Wolfgang Denk
Kuo-Jung Su - April 22, 2013, 2:51 a.m.
2013/4/18 Wolfgang Denk <wd@denx.de>:
> Dear Kuo-Jung Su,
>
> In message <1366277139-29728-7-git-send-email-dantesu@gmail.com> you wrote:
>> From: Kuo-Jung Su <dantesu@faraday-tech.com>
>>
>> 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.
>
> NAK.  Instead of adding another driver, please fix the problems of the
> original one, or replace the old one with your new, better one.  We do
> not want to have several drivers for the same piece of hardware.
>

Got it, thanks.
I plan to replace it with my own new one at next patch.
And I'll make a CC to Andes to see if they has any issue.

> Best regards,
>
> Wolfgang Denk
>
> --
> DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
> "The greatest warriors are the ones who fight for peace."
> - Holly Near



--
Best wishes,
Kuo-Jung Su

Patch

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 <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 <faraday/ftsdc010.h>
+
+#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,
+		&regs->clr);
+	SD_WRITE(arg, &regs->argu);
+	SD_WRITE(cmd, &regs->cmd);
+
+	if (!(flags & (MMC_RSP_PRESENT | MMC_RSP_136))) {
+		for (ts = get_timer(0); get_timer(ts) < 100; ) {
+			if (SD_READ(&regs->status) & FTSDC010_STATUS_CMD_SEND) {
+				SD_WRITE(FTSDC010_STATUS_CMD_SEND, &regs->clr);
+				ret = 0;
+				break;
+			}
+		}
+	} else {
+		st = 0;
+		for (ts = get_timer(0); get_timer(ts) < 100; ) {
+			st = SD_READ(&regs->status);
+			SD_WRITE(st & FTSDC010_STATUS_RSP_MASK, &regs->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(&regs->rsp3);
+				mmc_cmd->response[1] = SD_READ(&regs->rsp2);
+				mmc_cmd->response[2] = SD_READ(&regs->rsp1);
+				mmc_cmd->response[3] = SD_READ(&regs->rsp0);
+			} else {
+				mmc_cmd->response[0] = SD_READ(&regs->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(&regs->status);
+		SD_WRITE(st & FTSDC010_STATUS_DATA_MASK, &regs->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), &regs->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, &regs->dcr);
+
+		/* 2. clear status register */
+		SD_WRITE(FTSDC010_STATUS_DATA_MASK | FTSDC010_STATUS_FIFO_URUN
+			| FTSDC010_STATUS_FIFO_ORUN, &regs->clr);
+
+		/* 3. data timeout (1 sec) */
+		SD_WRITE(chip->rate, &regs->dtr);
+
+		/* 4. data length (bytes) */
+		SD_WRITE(len, &regs->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, &regs->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(&regs->status) & mask))
+				;
+			SD_WRITE(mask, &regs->clr);
+
+			/* write bytes to ftsdc010 */
+			for (wlen = 0; wlen < len && wlen < chip->fifo; ) {
+				SD_WRITE(*(uint32_t *)buf, &regs->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(&regs->status) & mask))
+				;
+			SD_WRITE(mask, &regs->clr);
+
+			/* fetch bytes from ftsdc010 */
+			for (rlen = 0; rlen < len && rlen < chip->fifo; ) {
+				*(uint32_t *)buf = SD_READ(&regs->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, &regs->ccr);
+	else
+		SD_CLRBITS(FTSDC010_CCR_CLK_HISPD, &regs->ccr);
+
+	SD_CLRBITS(0x07, &regs->bwr);
+	switch (mmc->bus_width) {
+	case 4:
+		SD_SETBITS(FTSDC010_BWR_WIDE_4_BUS, &regs->bwr);
+		break;
+	case 8:
+		SD_SETBITS(FTSDC010_BWR_WIDE_8_BUS, &regs->bwr);
+		break;
+	default:
+		SD_SETBITS(FTSDC010_BWR_SINGLE_BUS, &regs->bwr);
+		break;
+	}
+}
+
+static int ftsdc010_init(struct mmc *mmc)
+{
+	struct ftsdc010_chip *chip = mmc->priv;
+	struct ftsdc010_mmc *regs = chip->iobase;
+
+	if (SD_READ(&regs->status) & FTSDC010_STATUS_CARD_DETECT)
+		return NO_CARD_ERR;
+
+	if (SD_READ(&regs->status) & FTSDC010_STATUS_WRITE_PROT) {
+		printf("ftsdc010: write protected\n");
+		chip->wprot = 1;
+	}
+
+	chip->fifo = (SD_READ(&regs->feature) & 0xff) << 2;
+
+	/* 1. chip reset */
+	SD_WRITE(FTSDC010_CMD_SDC_RST, &regs->cmd);
+	while (SD_READ(&regs->cmd) & FTSDC010_CMD_SDC_RST)
+		;
+
+	/* 2. enter low speed mode (400k card detection) */
+	ftsdc010_clkset(chip, 400000);
+
+	/* 3. interrupt disabled */
+	SD_WRITE(0, &regs->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(&regs->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 <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 _FARADAY_MMC_H
+#define _FARADAY_MMC_H
+
+int ftsdc010_mmc_init(int dev_index);
+
+#endif /* _FARADAY_MMC_H */