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

Tested on:
- Palm Tungsten|C       PXA255
- Aeronix ZipitZ2       PXA270
- Marvell Zylonite 300  PXA300

This driver needs testing though.

Signed-off-by: Marek Vasut <[email protected]>
---
 drivers/mmc/Makefile      |    1 +
 drivers/mmc/pxa_mmc.h     |   21 +++
 drivers/mmc/pxa_mmc_gen.c |  335 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 357 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.h b/drivers/mmc/pxa_mmc.h
index 6fa4268..a5b7351 100644
--- a/drivers/mmc/pxa_mmc.h
+++ b/drivers/mmc/pxa_mmc.h
@@ -135,4 +135,25 @@
 #define MMC_R1B_ADDR_ERR               0x2000
 #define MMC_R1B_PARAM_ERR              0x4000
 
+/* PXAMMC Generic default config for various CPUs */
+#if defined(CONFIG_PXA250)
+#define        PXAMMC_FIFO_SIZE        1
+#define        PXAMMC_MIN_SPEED        312500
+#define        PXAMMC_MAX_SPEED        20000000
+#define        PXAMMC_HOST_CAPS        (0)
+#elif defined(CONFIG_PXA27X)
+#define        PXAMMC_CRC_SKIP
+#define        PXAMMC_FIFO_SIZE        32
+#define        PXAMMC_MIN_SPEED        304000
+#define        PXAMMC_MAX_SPEED        19500000
+#define        PXAMMC_HOST_CAPS        (MMC_MODE_4BIT)
+#elif defined(CONFIG_CPU_MONAHANS)
+#define        PXAMMC_FIFO_SIZE        32
+#define        PXAMMC_MIN_SPEED        304000
+#define        PXAMMC_MAX_SPEED        26000000
+#define        PXAMMC_HOST_CAPS        (MMC_MODE_4BIT | MMC_MODE_HS)
+#else
+#error "Unknown CPU for PXAMMC"
+#endif
+
 #endif /* __MMC_PXA_P_H__ */
diff --git a/drivers/mmc/pxa_mmc_gen.c b/drivers/mmc/pxa_mmc_gen.c
new file mode 100644
index 0000000..ea04e0a
--- /dev/null
+++ b/drivers/mmc/pxa_mmc_gen.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2010 Marek Vasut <[email protected]>
+ *
+ * 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"
+
+/* 1000uS (in wait cycles below it's 100 x 10uS waits) */
+#define        PXA_MMC_TIMEOUT 100
+
+static int pxa_mmc_wait(int mask)
+{
+       int timeout = PXA_MMC_TIMEOUT;
+
+       /* Wait until the clock are off */
+       while (!(MMC_STAT & mask) && --timeout)
+               udelay(10);
+
+       /* The clock refused to stop, scream and die a painful death */
+       if (!timeout)
+               return -ETIMEDOUT;
+
+}
+
+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 ret;
+
+       /* 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;
+
+       ret = pxa_mmc_wait(MMC_STAT_CLK_EN)
+       if (ret)
+               return ret;
+
+       /* 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) {
+#ifdef PXAMMC_CRC_SKIP
+               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 ret;
+
+       len = data->blocks * data->blocksize;
+
+       while (len) {
+               /* The controller has data ready */
+               if (MMC_I_REG & MMC_I_REG_RXFIFO_RD_REQ) {
+                       i = min(len, PXAMMC_FIFO_SIZE);
+
+                       while (i--) {
+                               *data->dest++ = *((volatile uchar 
*)&MMC_RXFIFO);
+                               len--;
+                       }
+               }
+
+               if (MMC_STAT & MMC_STAT_ERRORS)
+                       return -EIO;
+       }
+
+       /* Wait for the transmission-done interrupt */
+       ret = pxa_mmc_wait(MMC_STAT_DATA_TRAN_DONE)
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int pxa_mmc_do_write_xfer(struct mmc_data *data)
+{
+       unsigned long len;
+       int i, bytes;
+       int ret;
+
+       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(len, PXAMMC_FIFO_SIZE);
+
+                       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 */
+       ret = pxa_mmc_wait(MMC_STAT_DATA_TRAN_DONE)
+       if (ret)
+               return ret;
+
+       /* Wait until the data are really written to the card */
+       ret = pxa_mmc_wait(MMC_STAT_PRG_DONE)
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int pxa_mmc_request(struct mmc *mmc, struct mmc_cmd *cmd,
+                               struct mmc_data *data)
+{
+       unsigned long cmdat = 0;
+       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 */
+       ret = pxa_mmc_wait(MMC_STAT_END_CMD_RES)
+       if (ret)
+               return ret;
+
+       /* 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)
+{
+       /* 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_TXFIFO_WR_REQ | MMC_I_MASK_RXFIFO_RD_REQ);
+       return 0;
+}
+
+int pxa_mmc_init(bd_t *bis)
+{
+       struct mmc *mmc;
+
+       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->voltages   = MMC_VDD_32_33 | MMC_VDD_33_34;
+       mmc->f_max      = PXAMMC_MAX_SPEED;
+       mmc->f_min      = PXAMMC_MIN_SPEED;
+       mmc->host_caps  = PXAMMC_HOST_CAPS;
+       mmc_register(mmc);
+
+       return 0;
+}
+
+int cpu_mmc_init(bd_t *bis)
+{
+       return pxa_mmc_init(bis);
+}
-- 
1.7.1

_______________________________________________
U-Boot mailing list
[email protected]
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to