The new driver is a complete rewrite. It uses the MMC framework and should
support both pxa2xx and pxa3xx.

NOTE: This driver needs testing, that's why it's a totally separate thing for
now. I'll run it through some betatesters, though I'd like it in the tree.

Signed-off-by: Marek Vasut <marek.va...@gmail.com>
---
 drivers/mmc/Makefile      |    1 +
 drivers/mmc/pxa_mmc_gen.c |  344 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 345 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..24e0e4a
--- /dev/null
+++ b/drivers/mmc/pxa_mmc_gen.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2010 Marek Vasut <marek.va...@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;
+
+       /* 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

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

Reply via email to