Add support for the Freescale eSDHC found on 8379 and 8536 SoCs
authorAndy Fleming <afleming@freescale.com>
Thu, 30 Oct 2008 21:47:16 +0000 (16:47 -0500)
committerAndy Fleming <afleming@freescale.com>
Tue, 17 Feb 2009 00:07:42 +0000 (18:07 -0600)
This uses the new MMC framework

Some contributions by Dave Liu <daveliu@freescale.com>

Signed-off-by: Andy Fleming <afleming@freescale.com>
drivers/mmc/Makefile
drivers/mmc/fsl_esdhc.c [new file with mode: 0644]
include/fsl_esdhc.h [new file with mode: 0644]

index 35aef314e5c0fd5664430259c370b22a2ab7e342..6aa24f5d8c01d7eb1a5bb1e36b71262913cc63ad 100644 (file)
@@ -29,6 +29,7 @@ COBJS-$(CONFIG_GENERIC_MMC) += mmc.o
 COBJS-$(CONFIG_ATMEL_MCI) += atmel_mci.o
 COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o
 COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o
+COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o
 
 COBJS  := $(COBJS-y)
 SRCS   := $(COBJS:.o=.c)
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
new file mode 100644 (file)
index 0000000..0ba45cd
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2007, Freescale Semiconductor, Inc
+ * Andy Fleming
+ *
+ * Based vaguely on the pxa mmc code:
+ * (C) Copyright 2003
+ * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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 <command.h>
+#include <mmc.h>
+#include <part.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <fsl_esdhc.h>
+#include <asm/io.h>
+
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct fsl_esdhc {
+       uint    dsaddr;
+       uint    blkattr;
+       uint    cmdarg;
+       uint    xfertyp;
+       uint    cmdrsp0;
+       uint    cmdrsp1;
+       uint    cmdrsp2;
+       uint    cmdrsp3;
+       uint    datport;
+       uint    prsstat;
+       uint    proctl;
+       uint    sysctl;
+       uint    irqstat;
+       uint    irqstaten;
+       uint    irqsigen;
+       uint    autoc12err;
+       uint    hostcapblt;
+       uint    wml;
+       char    reserved1[8];
+       uint    fevt;
+       char    reserved2[168];
+       uint    hostver;
+       char    reserved3[780];
+       uint    scr;
+};
+
+/* Return the XFERTYP flags for a given command and data packet */
+uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
+{
+       uint xfertyp = 0;
+
+       if (data) {
+               xfertyp |= XFERTYP_DPSEL | XFERTYP_DMAEN;
+
+               if (data->blocks > 1) {
+                       xfertyp |= XFERTYP_MSBSEL;
+                       xfertyp |= XFERTYP_BCEN;
+               }
+
+               if (data->flags & MMC_DATA_READ)
+                       xfertyp |= XFERTYP_DTDSEL;
+       }
+
+       if (cmd->resp_type & MMC_RSP_CRC)
+               xfertyp |= XFERTYP_CCCEN;
+       if (cmd->resp_type & MMC_RSP_OPCODE)
+               xfertyp |= XFERTYP_CICEN;
+       if (cmd->resp_type & MMC_RSP_136)
+               xfertyp |= XFERTYP_RSPTYP_136;
+       else if (cmd->resp_type & MMC_RSP_BUSY)
+               xfertyp |= XFERTYP_RSPTYP_48_BUSY;
+       else if (cmd->resp_type & MMC_RSP_PRESENT)
+               xfertyp |= XFERTYP_RSPTYP_48;
+
+       return XFERTYP_CMD(cmd->cmdidx) | xfertyp;
+}
+
+static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
+{
+       uint wml_value;
+       int timeout;
+       struct fsl_esdhc *regs = mmc->priv;
+
+       wml_value = data->blocksize/4;
+
+       if (data->flags & MMC_DATA_READ) {
+               if (wml_value > 0x10)
+                       wml_value = 0x10;
+
+               wml_value = 0x100000 | wml_value;
+
+               out_be32(&regs->dsaddr, (u32)data->dest);
+       } else {
+               if (wml_value > 0x80)
+                       wml_value = 0x80;
+               if ((in_be32(&regs->prsstat) & PRSSTAT_WPSPL) == 0) {
+                       printf("\nThe SD card is locked. Can not write to a locked card.\n\n");
+                       return TIMEOUT;
+               }
+               wml_value = wml_value << 16 | 0x10;
+               out_be32(&regs->dsaddr, (u32)data->src);
+       }
+
+       out_be32(&regs->wml, wml_value);
+
+       out_be32(&regs->blkattr, data->blocks << 16 | data->blocksize);
+
+       /* Calculate the timeout period for data transactions */
+       timeout = __ilog2(mmc->tran_speed/10);
+       timeout -= 13;
+
+       if (timeout > 14)
+               timeout = 14;
+
+       if (timeout < 0)
+               timeout = 0;
+
+       clrsetbits_be32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16);
+
+       return 0;
+}
+
+
+/*
+ * Sends a command out on the bus.  Takes the mmc pointer,
+ * a command pointer, and an optional data pointer.
+ */
+static int
+esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
+{
+       uint    xfertyp;
+       uint    irqstat;
+       volatile struct fsl_esdhc *regs = mmc->priv;
+
+       out_be32(&regs->irqstat, -1);
+
+       sync();
+
+       /* Wait for the bus to be idle */
+       while ((in_be32(&regs->prsstat) & PRSSTAT_CICHB) ||
+                       (in_be32(&regs->prsstat) & PRSSTAT_CIDHB));
+
+       while (in_be32(&regs->prsstat) & PRSSTAT_DLA);
+
+       /* Wait at least 8 SD clock cycles before the next command */
+       /*
+        * Note: This is way more than 8 cycles, but 1ms seems to
+        * resolve timing issues with some cards
+        */
+       udelay(1000);
+
+       /* Set up for a data transfer if we have one */
+       if (data) {
+               int err;
+
+               err = esdhc_setup_data(mmc, data);
+               if(err)
+                       return err;
+       }
+
+       /* Figure out the transfer arguments */
+       xfertyp = esdhc_xfertyp(cmd, data);
+
+       /* Send the command */
+       out_be32(&regs->cmdarg, cmd->cmdarg);
+       out_be32(&regs->xfertyp, xfertyp);
+
+       /* Wait for the command to complete */
+       while (!(in_be32(&regs->irqstat) & IRQSTAT_CC));
+
+       irqstat = in_be32(&regs->irqstat);
+       out_be32(&regs->irqstat, irqstat);
+
+       if (irqstat & CMD_ERR)
+               return COMM_ERR;
+
+       if (irqstat & IRQSTAT_CTOE)
+               return TIMEOUT;
+
+       /* Copy the response to the response buffer */
+       if (cmd->resp_type & MMC_RSP_136) {
+               u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0;
+
+               cmdrsp3 = in_be32(&regs->cmdrsp3);
+               cmdrsp2 = in_be32(&regs->cmdrsp2);
+               cmdrsp1 = in_be32(&regs->cmdrsp1);
+               cmdrsp0 = in_be32(&regs->cmdrsp0);
+               ((uint *)(cmd->response))[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24);
+               ((uint *)(cmd->response))[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24);
+               ((uint *)(cmd->response))[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24);
+               ((uint *)(cmd->response))[3] = (cmdrsp0 << 8);
+       } else
+               ((uint *)(cmd->response))[0] = in_be32(&regs->cmdrsp0);
+
+       /* Wait until all of the blocks are transferred */
+       if (data) {
+               do {
+                       irqstat = in_be32(&regs->irqstat);
+
+                       if (irqstat & DATA_ERR)
+                               return COMM_ERR;
+
+                       if (irqstat & IRQSTAT_DTOE)
+                               return TIMEOUT;
+               } while (!(irqstat & IRQSTAT_TC) &&
+                               (in_be32(&regs->prsstat) & PRSSTAT_DLA));
+       }
+
+       out_be32(&regs->irqstat, -1);
+
+       return 0;
+}
+
+void set_sysctl(struct mmc *mmc, uint clock)
+{
+       int sdhc_clk = gd->sdhc_clk;
+       int div, pre_div;
+       volatile struct fsl_esdhc *regs = mmc->priv;
+       uint clk;
+
+       if (sdhc_clk / 16 > clock) {
+               for (pre_div = 2; pre_div < 256; pre_div *= 2)
+                       if ((sdhc_clk / pre_div) <= (clock * 16))
+                               break;
+       } else
+               pre_div = 2;
+
+       for (div = 1; div <= 16; div++)
+               if ((sdhc_clk / (div * pre_div)) <= clock)
+                       break;
+
+       pre_div >>= 1;
+       div -= 1;
+
+       clk = (pre_div << 8) | (div << 4);
+
+       clrsetbits_be32(&regs->sysctl, SYSCTL_CLOCK_MASK, clk);
+
+       udelay(10000);
+
+       setbits_be32(&regs->sysctl, SYSCTL_PEREN);
+}
+
+static void esdhc_set_ios(struct mmc *mmc)
+{
+       struct fsl_esdhc *regs = mmc->priv;
+
+       /* Set the clock speed */
+       set_sysctl(mmc, mmc->clock);
+
+       /* Set the bus width */
+       clrbits_be32(&regs->proctl, PROCTL_DTW_4 | PROCTL_DTW_8);
+
+       if (mmc->bus_width == 4)
+               setbits_be32(&regs->proctl, PROCTL_DTW_4);
+       else if (mmc->bus_width == 8)
+               setbits_be32(&regs->proctl, PROCTL_DTW_8);
+}
+
+static int esdhc_init(struct mmc *mmc)
+{
+       struct fsl_esdhc *regs = mmc->priv;
+       int timeout = 1000;
+
+       /* Enable cache snooping */
+       out_be32(&regs->scr, 0x00000040);
+
+       out_be32(&regs->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN);
+
+       /* Set the initial clock speed */
+       set_sysctl(mmc, 400000);
+
+       /* Disable the BRR and BWR bits in IRQSTAT */
+       clrbits_be32(&regs->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR);
+
+       /* Put the PROCTL reg back to the default */
+       out_be32(&regs->proctl, PROCTL_INIT);
+
+       while (!(in_be32(&regs->prsstat) & PRSSTAT_CINS) && --timeout)
+               udelay(1000);
+
+       if (timeout <= 0)
+               return NO_CARD_ERR;
+
+       return 0;
+}
+
+static int esdhc_initialize(bd_t *bis)
+{
+       struct fsl_esdhc *regs = (struct fsl_esdhc *)CONFIG_SYS_FSL_ESDHC_ADDR;
+       struct mmc *mmc;
+       u32 caps;
+
+       mmc = malloc(sizeof(struct mmc));
+
+       sprintf(mmc->name, "FSL_ESDHC");
+       mmc->priv = regs;
+       mmc->send_cmd = esdhc_send_cmd;
+       mmc->set_ios = esdhc_set_ios;
+       mmc->init = esdhc_init;
+
+       caps = regs->hostcapblt;
+
+       if (caps & ESDHC_HOSTCAPBLT_VS18)
+               mmc->voltages |= MMC_VDD_165_195;
+       if (caps & ESDHC_HOSTCAPBLT_VS30)
+               mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+       if (caps & ESDHC_HOSTCAPBLT_VS33)
+               mmc->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+
+       mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
+
+       if (caps & ESDHC_HOSTCAPBLT_HSS)
+               mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+
+       mmc->f_min = 400000;
+       mmc->f_max = MIN(gd->sdhc_clk, 50000000);
+
+       mmc_register(mmc);
+
+       return 0;
+}
+
+int fsl_esdhc_mmc_init(bd_t *bis)
+{
+       return esdhc_initialize(bis);
+}
diff --git a/include/fsl_esdhc.h b/include/fsl_esdhc.h
new file mode 100644 (file)
index 0000000..0a5c5d6
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * FSL SD/MMC Defines
+ *-------------------------------------------------------------------
+ *
+ * Copyright 2007-2008, Freescale Semiconductor, Inc
+ *
+ * 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
+ *
+ *-------------------------------------------------------------------
+ *
+ */
+
+#ifndef  __FSL_ESDHC_H__
+#define        __FSL_ESDHC_H__
+
+/* FSL eSDHC-specific constants */
+#define SYSCTL                 0x0002e02c
+#define SYSCTL_INITA           0x08000000
+#define SYSCTL_TIMEOUT_MASK    0x000f0000
+#define SYSCTL_CLOCK_MASK      0x00000fff
+#define SYSCTL_PEREN           0x00000004
+#define SYSCTL_HCKEN           0x00000002
+#define SYSCTL_IPGEN           0x00000001
+
+#define IRQSTAT                        0x0002e030
+#define IRQSTAT_DMAE           (0x10000000)
+#define IRQSTAT_AC12E          (0x01000000)
+#define IRQSTAT_DEBE           (0x00400000)
+#define IRQSTAT_DCE            (0x00200000)
+#define IRQSTAT_DTOE           (0x00100000)
+#define IRQSTAT_CIE            (0x00080000)
+#define IRQSTAT_CEBE           (0x00040000)
+#define IRQSTAT_CCE            (0x00020000)
+#define IRQSTAT_CTOE           (0x00010000)
+#define IRQSTAT_CINT           (0x00000100)
+#define IRQSTAT_CRM            (0x00000080)
+#define IRQSTAT_CINS           (0x00000040)
+#define IRQSTAT_BRR            (0x00000020)
+#define IRQSTAT_BWR            (0x00000010)
+#define IRQSTAT_DINT           (0x00000008)
+#define IRQSTAT_BGE            (0x00000004)
+#define IRQSTAT_TC             (0x00000002)
+#define IRQSTAT_CC             (0x00000001)
+
+#define CMD_ERR                (IRQSTAT_CIE | IRQSTAT_CEBE | IRQSTAT_CCE)
+#define DATA_ERR       (IRQSTAT_DEBE | IRQSTAT_DCE | IRQSTAT_DTOE)
+
+#define IRQSTATEN              0x0002e034
+#define IRQSTATEN_DMAE         (0x10000000)
+#define IRQSTATEN_AC12E                (0x01000000)
+#define IRQSTATEN_DEBE         (0x00400000)
+#define IRQSTATEN_DCE          (0x00200000)
+#define IRQSTATEN_DTOE         (0x00100000)
+#define IRQSTATEN_CIE          (0x00080000)
+#define IRQSTATEN_CEBE         (0x00040000)
+#define IRQSTATEN_CCE          (0x00020000)
+#define IRQSTATEN_CTOE         (0x00010000)
+#define IRQSTATEN_CINT         (0x00000100)
+#define IRQSTATEN_CRM          (0x00000080)
+#define IRQSTATEN_CINS         (0x00000040)
+#define IRQSTATEN_BRR          (0x00000020)
+#define IRQSTATEN_BWR          (0x00000010)
+#define IRQSTATEN_DINT         (0x00000008)
+#define IRQSTATEN_BGE          (0x00000004)
+#define IRQSTATEN_TC           (0x00000002)
+#define IRQSTATEN_CC           (0x00000001)
+
+#define PRSSTAT                        0x0002e024
+#define PRSSTAT_CLSL           (0x00800000)
+#define PRSSTAT_WPSPL          (0x00080000)
+#define PRSSTAT_CDPL           (0x00040000)
+#define PRSSTAT_CINS           (0x00010000)
+#define PRSSTAT_BREN           (0x00000800)
+#define PRSSTAT_DLA            (0x00000004)
+#define PRSSTAT_CICHB          (0x00000002)
+#define PRSSTAT_CIDHB          (0x00000001)
+
+#define PROCTL                 0x0002e028
+#define PROCTL_INIT            0x00000020
+#define PROCTL_DTW_4           0x00000002
+#define PROCTL_DTW_8           0x00000004
+
+#define CMDARG                 0x0002e008
+
+#define XFERTYP                        0x0002e00c
+#define XFERTYP_CMD(x)         ((x & 0x3f) << 24)
+#define XFERTYP_CMDTYP_NORMAL  0x0
+#define XFERTYP_CMDTYP_SUSPEND 0x00400000
+#define XFERTYP_CMDTYP_RESUME  0x00800000
+#define XFERTYP_CMDTYP_ABORT   0x00c00000
+#define XFERTYP_DPSEL          0x00200000
+#define XFERTYP_CICEN          0x00100000
+#define XFERTYP_CCCEN          0x00080000
+#define XFERTYP_RSPTYP_NONE    0
+#define XFERTYP_RSPTYP_136     0x00010000
+#define XFERTYP_RSPTYP_48      0x00020000
+#define XFERTYP_RSPTYP_48_BUSY 0x00030000
+#define XFERTYP_MSBSEL         0x00000020
+#define XFERTYP_DTDSEL         0x00000010
+#define XFERTYP_AC12EN         0x00000004
+#define XFERTYP_BCEN           0x00000002
+#define XFERTYP_DMAEN          0x00000001
+
+#define CINS_TIMEOUT           1000
+
+#define DSADDR         0x2e004
+
+#define CMDRSP0                0x2e010
+#define CMDRSP1                0x2e014
+#define CMDRSP2                0x2e018
+#define CMDRSP3                0x2e01c
+
+#define DATPORT                0x2e020
+
+#define WML            0x2e044
+#define WML_WRITE      0x00010000
+
+#define BLKATTR                0x2e004
+#define BLKATTR_CNT(x) ((x & 0xffff) << 16)
+#define BLKATTR_SIZE(x)        (x & 0x1fff)
+#define MAX_BLK_CNT    0x7fff  /* so malloc will have enough room with 32M */
+
+#define ESDHC_HOSTCAPBLT_VS18  0x04000000
+#define ESDHC_HOSTCAPBLT_VS30  0x02000000
+#define ESDHC_HOSTCAPBLT_VS33  0x01000000
+#define ESDHC_HOSTCAPBLT_SRS   0x00800000
+#define ESDHC_HOSTCAPBLT_DMAS  0x00400000
+#define ESDHC_HOSTCAPBLT_HSS   0x00200000
+
+int fsl_esdhc_mmc_init(bd_t *bis);
+
+#endif  /* __FSL_ESDHC_H__ */