]> git.ipfire.org Git - people/ms/u-boot.git/commitdiff
fsl_esdhc: add support for mx51 processor
authorStefano Babic <sbabic@denx.de>
Fri, 5 Feb 2010 14:11:27 +0000 (15:11 +0100)
committerTom Rix <Tom.Rix@windriver.com>
Sun, 7 Mar 2010 18:36:36 +0000 (12:36 -0600)
The esdhc controller in the mx51 processor is quite
the same as the one in some powerpc processors
(MPC83xx, MPC85xx). This patches adapts the driver
to support the arm mx51.

Signed-off-by: Stefano Babic <sbabic@denx.de>
cpu/arm_cortexa8/mx51/Makefile
cpu/arm_cortexa8/mx51/speed.c [new file with mode: 0644]
drivers/mmc/fsl_esdhc.c
include/asm-arm/global_data.h
include/fsl_esdhc.h
lib_arm/board.c

index 4d82293fa07918392eda440b0a48b0a0404d1fe6..7cfaa2c1305e639b10e2f9eb16c6fb79225c61a9 100644 (file)
@@ -27,7 +27,7 @@ include $(TOPDIR)/config.mk
 
 LIB    = $(obj)lib$(SOC).a
 
-COBJS  = soc.o clock.o iomux.o timer.o
+COBJS  = soc.o clock.o iomux.o timer.o speed.o
 SOBJS = lowlevel_init.o
 
 SRCS   := $(SOBJS:.o=.S) $(COBJS:.o=.c)
diff --git a/cpu/arm_cortexa8/mx51/speed.c b/cpu/arm_cortexa8/mx51/speed.c
new file mode 100644 (file)
index 0000000..10acbbf
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * (C) Copyright 2000-2003
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
+ * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
+ *
+ * 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 <common.h>
+#include <asm/arch/imx-regs.h>
+
+int get_clocks(void)
+{
+       DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef CONFIG_FSL_ESDHC
+       gd->sdhc_clk = mxc_get_clock(MXC_IPG_PERCLK);
+#endif
+       return 0;
+}
index c6e9e6e7822d886d947c7a0d3690a6584f8c6ddc..e665b5ebd8a7f10d62877c5e97482ceabc07e095 100644 (file)
@@ -37,7 +37,6 @@
 #include <fdt_support.h>
 #include <asm/io.h>
 
-
 DECLARE_GLOBAL_DATA_PTR;
 
 struct fsl_esdhc {
@@ -102,7 +101,8 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
 {
        uint wml_value;
        int timeout;
-       struct fsl_esdhc *regs = mmc->priv;
+       struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
+       struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
 
        wml_value = data->blocksize/4;
 
@@ -112,24 +112,24 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
 
                wml_value = 0x100000 | wml_value;
 
-               out_be32(&regs->dsaddr, (u32)data->dest);
+               esdhc_write32(&regs->dsaddr, (u32)data->dest);
        } else {
                if (wml_value > 0x80)
                        wml_value = 0x80;
-               if ((in_be32(&regs->prsstat) & PRSSTAT_WPSPL) == 0) {
+               if ((esdhc_read32(&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);
+               esdhc_write32(&regs->dsaddr, (u32)data->src);
        }
 
-       out_be32(&regs->wml, wml_value);
+       esdhc_write32(&regs->wml, wml_value);
 
-       out_be32(&regs->blkattr, data->blocks << 16 | data->blocksize);
+       esdhc_write32(&regs->blkattr, data->blocks << 16 | data->blocksize);
 
        /* Calculate the timeout period for data transactions */
-       timeout = __ilog2(mmc->tran_speed/10);
+       timeout = fls(mmc->tran_speed/10) - 1;
        timeout -= 13;
 
        if (timeout > 14)
@@ -138,7 +138,7 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
        if (timeout < 0)
                timeout = 0;
 
-       clrsetbits_be32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16);
+       esdhc_clrsetbits32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16);
 
        return 0;
 }
@@ -153,17 +153,20 @@ 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;
+       struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
+       volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
 
-       out_be32(&regs->irqstat, -1);
+       esdhc_write32(&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 ((esdhc_read32(&regs->prsstat) & PRSSTAT_CICHB) ||
+                       (esdhc_read32(&regs->prsstat) & PRSSTAT_CIDHB))
+               ;
 
-       while (in_be32(&regs->prsstat) & PRSSTAT_DLA);
+       while (esdhc_read32(&regs->prsstat) & PRSSTAT_DLA)
+               ;
 
        /* Wait at least 8 SD clock cycles before the next command */
        /*
@@ -185,14 +188,15 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
        xfertyp = esdhc_xfertyp(cmd, data);
 
        /* Send the command */
-       out_be32(&regs->cmdarg, cmd->cmdarg);
-       out_be32(&regs->xfertyp, xfertyp);
+       esdhc_write32(&regs->cmdarg, cmd->cmdarg);
+       esdhc_write32(&regs->xfertyp, xfertyp);
 
        /* Wait for the command to complete */
-       while (!(in_be32(&regs->irqstat) & IRQSTAT_CC));
+       while (!(esdhc_read32(&regs->irqstat) & IRQSTAT_CC))
+               ;
 
-       irqstat = in_be32(&regs->irqstat);
-       out_be32(&regs->irqstat, irqstat);
+       irqstat = esdhc_read32(&regs->irqstat);
+       esdhc_write32(&regs->irqstat, irqstat);
 
        if (irqstat & CMD_ERR)
                return COMM_ERR;
@@ -204,21 +208,21 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
        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);
+               cmdrsp3 = esdhc_read32(&regs->cmdrsp3);
+               cmdrsp2 = esdhc_read32(&regs->cmdrsp2);
+               cmdrsp1 = esdhc_read32(&regs->cmdrsp1);
+               cmdrsp0 = esdhc_read32(&regs->cmdrsp0);
                cmd->response[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24);
                cmd->response[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24);
                cmd->response[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24);
                cmd->response[3] = (cmdrsp0 << 8);
        } else
-               cmd->response[0] = in_be32(&regs->cmdrsp0);
+               cmd->response[0] = esdhc_read32(&regs->cmdrsp0);
 
        /* Wait until all of the blocks are transferred */
        if (data) {
                do {
-                       irqstat = in_be32(&regs->irqstat);
+                       irqstat = esdhc_read32(&regs->irqstat);
 
                        if (irqstat & DATA_ERR)
                                return COMM_ERR;
@@ -226,10 +230,10 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
                        if (irqstat & IRQSTAT_DTOE)
                                return TIMEOUT;
                } while (!(irqstat & IRQSTAT_TC) &&
-                               (in_be32(&regs->prsstat) & PRSSTAT_DLA));
+                               (esdhc_read32(&regs->prsstat) & PRSSTAT_DLA));
        }
 
-       out_be32(&regs->irqstat, -1);
+       esdhc_write32(&regs->irqstat, -1);
 
        return 0;
 }
@@ -238,9 +242,13 @@ 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;
+       struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
+       volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
        uint clk;
 
+       if (clock < mmc->f_min)
+               clock = mmc->f_min;
+
        if (sdhc_clk / 16 > clock) {
                for (pre_div = 2; pre_div < 256; pre_div *= 2)
                        if ((sdhc_clk / pre_div) <= (clock * 16))
@@ -257,67 +265,105 @@ void set_sysctl(struct mmc *mmc, uint clock)
 
        clk = (pre_div << 8) | (div << 4);
 
-       clrsetbits_be32(&regs->sysctl, SYSCTL_CLOCK_MASK, clk);
+       /* On imx the clock must be stopped before changing frequency */
+       if (cfg->clk_enable)
+               esdhc_clrbits32(&regs->sysctl, SYSCTL_CKEN);
+
+       esdhc_clrsetbits32(&regs->sysctl, SYSCTL_CLOCK_MASK, clk);
 
        udelay(10000);
 
-       setbits_be32(&regs->sysctl, SYSCTL_PEREN);
+       clk = SYSCTL_PEREN;
+       /* On imx systems the clock must be explicitely enabled */
+       if (cfg->clk_enable)
+               clk |= SYSCTL_CKEN;
+
+       esdhc_setbits32(&regs->sysctl, clk);
 }
 
 static void esdhc_set_ios(struct mmc *mmc)
 {
-       struct fsl_esdhc *regs = mmc->priv;
+       struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
+       struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
 
        /* Set the clock speed */
        set_sysctl(mmc, mmc->clock);
 
        /* Set the bus width */
-       clrbits_be32(&regs->proctl, PROCTL_DTW_4 | PROCTL_DTW_8);
+       esdhc_clrbits32(&regs->proctl, PROCTL_DTW_4 | PROCTL_DTW_8);
 
        if (mmc->bus_width == 4)
-               setbits_be32(&regs->proctl, PROCTL_DTW_4);
+               esdhc_setbits32(&regs->proctl, PROCTL_DTW_4);
        else if (mmc->bus_width == 8)
-               setbits_be32(&regs->proctl, PROCTL_DTW_8);
+               esdhc_setbits32(&regs->proctl, PROCTL_DTW_8);
+
 }
 
 static int esdhc_init(struct mmc *mmc)
 {
-       struct fsl_esdhc *regs = mmc->priv;
+       struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv;
+       struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
        int timeout = 1000;
+       int ret = 0;
+       u8 card_absent;
 
        /* Enable cache snooping */
-       out_be32(&regs->scr, 0x00000040);
+       if (cfg && !cfg->no_snoop)
+               esdhc_write32(&regs->scr, 0x00000040);
+
+       /* Reset the entire host controller */
+       esdhc_write32(&regs->sysctl, SYSCTL_RSTA);
+
+       /* Wait until the controller is available */
+       while ((esdhc_read32(&regs->sysctl) & SYSCTL_RSTA) && --timeout)
+               udelay(1000);
 
-       out_be32(&regs->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN);
+       esdhc_write32(&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);
+       esdhc_clrbits32(&regs->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR);
 
        /* Put the PROCTL reg back to the default */
-       out_be32(&regs->proctl, PROCTL_INIT);
+       esdhc_write32(&regs->proctl, PROCTL_INIT);
 
-       while (!(in_be32(&regs->prsstat) & PRSSTAT_CINS) && --timeout)
-               udelay(1000);
+       /* Set timout to the maximum value */
+       esdhc_clrsetbits32(&regs->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16);
 
-       if (timeout <= 0)
-               return NO_CARD_ERR;
+       /* Check if there is a callback for detecting the card */
+       if (board_mmc_getcd(&card_absent, mmc)) {
+               timeout = 1000;
+               while (!(esdhc_read32(&regs->prsstat) & PRSSTAT_CINS) &&
+                               --timeout)
+                       udelay(1000);
 
-       return 0;
+               if (timeout <= 0)
+                       ret = NO_CARD_ERR;
+       } else {
+               if (card_absent)
+                       ret = NO_CARD_ERR;
+       }
+
+       return ret;
 }
 
-static int esdhc_initialize(bd_t *bis)
+int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
 {
-       struct fsl_esdhc *regs = (struct fsl_esdhc *)CONFIG_SYS_FSL_ESDHC_ADDR;
+       struct fsl_esdhc *regs;
        struct mmc *mmc;
        u32 caps;
 
+       if (!cfg)
+               return -1;
+
        mmc = malloc(sizeof(struct mmc));
 
        sprintf(mmc->name, "FSL_ESDHC");
-       mmc->priv = regs;
+       regs = (struct fsl_esdhc *)cfg->esdhc_base;
+
+       mmc->priv = cfg;
        mmc->send_cmd = esdhc_send_cmd;
        mmc->set_ios = esdhc_set_ios;
        mmc->init = esdhc_init;
@@ -346,9 +392,15 @@ static int esdhc_initialize(bd_t *bis)
 
 int fsl_esdhc_mmc_init(bd_t *bis)
 {
-       return esdhc_initialize(bis);
+       struct fsl_esdhc_cfg *cfg;
+
+       cfg = malloc(sizeof(struct fsl_esdhc_cfg));
+       memset(cfg, 0, sizeof(struct fsl_esdhc_cfg));
+       cfg->esdhc_base = CONFIG_SYS_FSL_ESDHC_ADDR;
+       return fsl_esdhc_initialize(bis, cfg);
 }
 
+#ifdef CONFIG_OF_LIBFDT
 void fdt_fixup_esdhc(void *blob, bd_t *bd)
 {
        const char *compat = "fsl,esdhc";
@@ -365,3 +417,4 @@ out:
        do_fixup_by_compat(blob, compat, "status", status,
                           strlen(status) + 1, 1);
 }
+#endif
index 8115a24b78bba0f54ec1adc056bd8b1a83cb9753..02cfe4584a0e3163a69b1933eff4f767f3f441e2 100644 (file)
@@ -44,6 +44,9 @@ typedef       struct  global_data {
 #ifdef CONFIG_VFD
        unsigned char   vfd_type;       /* display type */
 #endif
+#ifdef CONFIG_FSL_ESDHC
+       unsigned long   sdhc_clk;
+#endif
 #if 0
        unsigned long   cpu_clk;        /* CPU clock in Hz!             */
        unsigned long   bus_clk;
index eac6a2bd4843821204e006bd393e3f0a46a4d128..01b7dec18875696bd3911a8ff54836bb6a2b3800 100644 (file)
 #define        __FSL_ESDHC_H__
 
 #include <asm/errno.h>
+#include <asm/byteorder.h>
 
 /* FSL eSDHC-specific constants */
 #define SYSCTL                 0x0002e02c
 #define SYSCTL_INITA           0x08000000
 #define SYSCTL_TIMEOUT_MASK    0x000f0000
 #define SYSCTL_CLOCK_MASK      0x0000fff0
+#define SYSCTL_RSTA            0x01000000
+#define SYSCTL_CKEN            0x00000008
 #define SYSCTL_PEREN           0x00000004
 #define SYSCTL_HCKEN           0x00000002
 #define SYSCTL_IPGEN           0x00000001
 #define ESDHC_HOSTCAPBLT_DMAS  0x00400000
 #define ESDHC_HOSTCAPBLT_HSS   0x00200000
 
+struct fsl_esdhc_cfg {
+       u32     esdhc_base;
+       u32     no_snoop;
+       u32     clk_enable;
+};
+
+/* Select the correct accessors depending on endianess */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define esdhc_read32           in_le32
+#define esdhc_write32          out_le32
+#define esdhc_clrsetbits32     clrsetbits_le32
+#define esdhc_clrbits32                clrbits_le32
+#define esdhc_setbits32                setbits_le32
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define esdhc_read32           in_be32
+#define esdhc_write32          out_be32
+#define esdhc_clrsetbits32     clrsetbits_be32
+#define esdhc_clrbits32                clrbits_be32
+#define esdhc_setbits32                setbits_be32
+#else
+#error "Endianess is not defined: please fix to continue"
+#endif
+
 #ifdef CONFIG_FSL_ESDHC
 int fsl_esdhc_mmc_init(bd_t *bis);
+int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg);
 void fdt_fixup_esdhc(void *blob, bd_t *bd);
 #else
 static inline int fsl_esdhc_mmc_init(bd_t *bis) { return -ENOSYS; }
index e148739152c0826130ab70f249c74aa010165d67..f5660a985b7a5eab87000bec9c24dc380aaa807e 100644 (file)
@@ -243,6 +243,9 @@ init_fnc_t *init_sequence[] = {
        interrupt_init,         /* set up exceptions */
 #endif
        timer_init,             /* initialize timer */
+#ifdef CONFIG_FSL_ESDHC
+       get_clocks,
+#endif
        env_init,               /* initialize environment */
        init_baudrate,          /* initialze baudrate settings */
        serial_init,            /* serial communications setup */