[U-Boot] [PATCH v2] PXA: New MMC driver
Marek Vasut
marek.vasut at gmail.com
Wed Jun 30 01:39:36 CEST 2010
The new driver is a complete rewrite. It uses the MMC framework and should
support both pxa2xx and pxa3xx.
This driver needs testing though.
Change since v1:
- Handle PXA27x CRC bug (erratum #42)
Signed-off-by: Marek Vasut <marek.vasut at gmail.com>
---
drivers/mmc/Makefile | 1 +
drivers/mmc/pxa_mmc_gen.c | 352 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 353 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/pxa_mmc_gen.c
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 6fa04b8..01338a4 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -32,6 +32,7 @@ COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o
COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o
+COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
diff --git a/drivers/mmc/pxa_mmc_gen.c b/drivers/mmc/pxa_mmc_gen.c
new file mode 100644
index 0000000..a158000
--- /dev/null
+++ b/drivers/mmc/pxa_mmc_gen.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2010 Marek Vasut <marek.vasut at gmail.com>
+ *
+ * Loosely based on the old code and Linux's PXA MMC driver
+ *
+ * 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
+ */
+
+#include <config.h>
+#include <common.h>
+#include <malloc.h>
+
+#include <mmc.h>
+#include <asm/errno.h>
+#include <asm/arch/hardware.h>
+
+#include "pxa_mmc.h"
+
+#define PXA_MMC_TIMEOUT 100
+
+static int pxa_mmc_stop_clock(void)
+{
+ int timeout = PXA_MMC_TIMEOUT;
+
+ /* If the clock aren't running, exit */
+ if (!(MMC_STAT & MMC_STAT_CLK_EN))
+ return 0;
+
+ /* Tell the controller to turn off the clock */
+ MMC_STRPCL = MMC_STRPCL_STOP_CLK;
+
+ /* Wait until the clock are off */
+ while ((MMC_STAT & MMC_STAT_CLK_EN) && --timeout)
+ udelay(10);
+
+ /* The clock refused to stop, scream and die a painful death */
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ /* The clock stopped correctly */
+ return 0;
+}
+
+static int pxa_mmc_start_cmd(struct mmc_cmd *cmd, unsigned long cmdat)
+{
+ int timeout = PXA_MMC_TIMEOUT;
+
+ /* The card can send a "busy" response */
+ if (cmd->flags & MMC_RSP_BUSY)
+ cmdat |= MMC_CMDAT_BUSY;
+
+ /* Inform the controller about response type */
+ switch (cmd->resp_type) {
+ case MMC_RSP_R1:
+ case MMC_RSP_R1b:
+ cmdat |= MMC_CMDAT_R1;
+ break;
+ case MMC_RSP_R2:
+ cmdat |= MMC_CMDAT_R2;
+ break;
+ case MMC_RSP_R3:
+ cmdat |= MMC_CMDAT_R3;
+ break;
+ default:
+ break;
+ }
+
+ /* Load command and it's arguments into the controller */
+ MMC_CMD = cmd->cmdidx;
+ MMC_ARGH = cmd->cmdarg >> 16;
+ MMC_ARGL = cmd->cmdarg & 0xffff;
+ MMC_CMDAT = cmdat;
+
+ /* Start the controller clock and wait until they are started */
+ MMC_STRPCL = MMC_STRPCL_START_CLK;
+ while (!(MMC_STAT & MMC_STAT_CLK_EN) && --timeout)
+ udelay(10);
+
+ /* The clock didn't start, we have a problem */
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ /* Correct and happy end */
+ return 0;
+}
+
+static int pxa_mmc_cmd_done(struct mmc_cmd *cmd)
+{
+ unsigned long a, b, c;
+ int i;
+ int stat;
+
+ /* Read the controller status */
+ stat = MMC_STAT;
+
+ /*
+ * Linux says:
+ * Did I mention this is Sick. We always need to
+ * discard the upper 8 bits of the first 16-bit word.
+ */
+ a = MMC_RES & 0xffff;
+ for (i = 0; i < 4; i++) {
+ b = MMC_RES & 0xffff;
+ c = MMC_RES & 0xffff;
+ cmd->response[i] = (a << 24) | (b << 8) | (c >> 8);
+ a = c;
+ }
+
+ /* The command response didn't arrive */
+ if (stat & MMC_STAT_TIME_OUT_RESPONSE)
+ return -ETIMEDOUT;
+ else if (stat & MMC_STAT_RES_CRC_ERROR && cmd->flags & MMC_RSP_CRC) {
+#if defined(CONFIG_PXA27X)
+ if (cmd->flags & MMC_RSP_136 && cmd->response[0] & 0x80000000)
+ printf("Ignoring CRC, this may be dangerous!\n");
+ else
+#endif
+ return -EILSEQ;
+ }
+
+ /* The command response was successfully read */
+ return 0;
+}
+
+static int pxa_mmc_do_read_xfer(struct mmc_data *data)
+{
+ unsigned long len;
+ int i;
+ int timeout = PXA_MMC_TIMEOUT;
+
+ len = data->blocks * data->blocksize;
+
+ while (len) {
+ /* The controller has data ready */
+ if (MMC_I_REG & MMC_I_REG_RXFIFO_RD_REQ) {
+#if defined(CONFIG_PXA27X) || defined(CONFIG_CPU_MONAHANS)
+ i = min(len, 32);
+#else
+ i = 1;
+#endif
+ while (i--) {
+ *data->dest++ = *((volatile uchar *)&MMC_RXFIFO);
+ len--;
+ }
+ }
+
+ if (MMC_STAT & MMC_STAT_ERRORS)
+ return -EIO;
+ }
+
+ /* Wait for the transmission-done interrupt */
+ while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE) && --timeout)
+ udelay(10);
+
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int pxa_mmc_do_write_xfer(struct mmc_data *data)
+{
+ unsigned long len;
+ int i, bytes;
+ int timeout = PXA_MMC_TIMEOUT;
+
+ len = data->blocks * data->blocksize;
+
+ while (len) {
+ /* The controller is ready to receive data */
+ if (MMC_I_REG & MMC_I_REG_TXFIFO_WR_REQ) {
+ bytes = min(32, len);
+
+ for (i = 0; i < bytes; i++)
+ MMC_TXFIFO = *data->src++;
+
+ if (bytes < 32)
+ MMC_PRTBUF = MMC_PRTBUF_BUF_PART_FULL;
+
+ len -= bytes;
+ }
+
+ if (MMC_STAT & MMC_STAT_ERRORS)
+ return -EIO;
+ }
+
+ /* Wait for the transmission-done interrupt */
+ while (!(MMC_I_REG & MMC_I_REG_DATA_TRAN_DONE) && --timeout)
+ udelay(10);
+
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ /* Wait until the data are really written to the card */
+ timeout = PXA_MMC_TIMEOUT;
+ while (!(MMC_I_REG & MMC_I_REG_PRG_DONE) && --timeout)
+ udelay(10);
+
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int pxa_mmc_request(struct mmc *mmc, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ unsigned long cmdat = 0;
+ int timeout = PXA_MMC_TIMEOUT;
+ int ret;
+
+ /* Stop the controller */
+ ret = pxa_mmc_stop_clock();
+ if (ret)
+ return ret;
+
+ /* If we're doing data transfer, configure the controller accordingly */
+ if (data) {
+ MMC_NOB = data->blocks;
+ MMC_BLKLEN = data->blocksize;
+ /* This delay can be optimized, but stick with max value */
+ MMC_RDTO = 0xffff;
+ cmdat |= MMC_CMDAT_DATA_EN;
+ if (data->flags & MMC_DATA_WRITE)
+ cmdat |= MMC_CMDAT_WRITE;
+ }
+
+ /* Run in 4bit mode if the card can do it */
+ if (mmc->bus_width == 4)
+ cmdat |= MMC_CMDAT_SD_4DAT;
+
+ /* Execute the command */
+ ret = pxa_mmc_start_cmd(cmd, cmdat);
+ if (ret)
+ return ret;
+
+ /* Wait until the command completes */
+ while (!(MMC_I_REG & MMC_I_REG_END_CMD_RES) && --timeout)
+ udelay(10);
+
+ /* If the command doesn't complete, die */
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ /* Read back the result */
+ ret = pxa_mmc_cmd_done(cmd);
+ if (ret)
+ return ret;
+
+ /* In case there was a data transfer scheduled, do it */
+ if (data) {
+ if (data->flags & MMC_DATA_WRITE)
+ pxa_mmc_do_write_xfer(data);
+ else
+ pxa_mmc_do_read_xfer(data);
+ }
+
+ return 0;
+}
+
+static void pxa_mmc_set_ios(struct mmc *mmc)
+{
+ unsigned long tmp;
+ unsigned long pxa_mmc_clock;
+
+ /* Set clock to the card */
+ if (mmc->clock) {
+ /* PXA3xx can do 26MHz with special settings */
+ if (mmc->clock == 26000000)
+ MMC_CLKRT = 0x7;
+ else {
+ pxa_mmc_clock = 0;
+ tmp = mmc->f_max / mmc->clock;
+ tmp += tmp % 2;
+ while (tmp > 1) {
+ pxa_mmc_clock++;
+ tmp >>= 1;
+ }
+ MMC_CLKRT = pxa_mmc_clock;
+ }
+ } else
+ pxa_mmc_stop_clock();
+}
+
+static int pxa_mmc_setup(struct mmc *mmc)
+{
+
+ /* Enable MMC unit clock */
+#ifdef CONFIG_CPU_MONAHANS /* pxa3xx */
+ CKENA |= CKENA_12_MMC0 | CKENA_13_MMC1;
+#else /* pxa2xx */
+ CKEN |= CKEN12_MMC;
+#endif
+ /* Make sure the clock are stopped */
+ pxa_mmc_stop_clock();
+ /* Turn off SPI mode */
+ MMC_SPI = MMC_SPI_DISABLE;
+ /* Set up maximum timeout to wait for command response */
+ MMC_RESTO = MMC_RES_TO_MAX;
+ /* Mask all interrupts */
+ MMC_I_MASK = MMC_I_MASK_ALL;
+ return 0;
+}
+
+int pxa_mmc_init(bd_t *bis)
+{
+ struct mmc *mmc = NULL;
+
+ mmc = malloc(sizeof(struct mmc));
+
+ if (!mmc)
+ return -ENOMEM;
+ sprintf(mmc->name, "PXA MMC");
+ mmc->send_cmd = pxa_mmc_request;
+ mmc->set_ios = pxa_mmc_set_ios;
+ mmc->init = pxa_mmc_setup;
+ mmc->host_caps = MMC_MODE_4BIT;
+
+ mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+#if defined(CONFIG_CPU_MONAHANS)
+ mmc->f_max = 26000000;
+ mmc->f_min = 304000;
+ mmc->host_caps |= MMC_MODE_HS;
+#elif defined(CONFIG_PXA27X)
+ mmc->f_max = 19500000;
+ mmc->f_min = 304000;
+#else
+ mmc->f_max = 20000000;
+ mmc->f_min = 3125000;
+#endif
+ mmc_register(mmc);
+
+ return 0;
+}
+
+int cpu_mmc_init(bd_t *bis)
+{
+ return pxa_mmc_init(bis);
+}
--
1.7.1
More information about the U-Boot
mailing list