From 50586ef24ed5caf6ce5591df76f355009da2cd79 Mon Sep 17 00:00:00 2001 From: Andy Fleming Date: Thu, 30 Oct 2008 16:47:16 -0500 Subject: [PATCH] Add support for the Freescale eSDHC found on 8379 and 8536 SoCs This uses the new MMC framework Some contributions by Dave Liu Signed-off-by: Andy Fleming --- drivers/mmc/Makefile | 1 + drivers/mmc/fsl_esdhc.c | 348 ++++++++++++++++++++++++++++++++++++++++ include/fsl_esdhc.h | 145 +++++++++++++++++ 3 files changed, 494 insertions(+) create mode 100644 drivers/mmc/fsl_esdhc.c create mode 100644 include/fsl_esdhc.h diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 35aef314e..6aa24f5d8 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -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 index 000000000..0ba45cd03 --- /dev/null +++ b/drivers/mmc/fsl_esdhc.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + + +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(®s->dsaddr, (u32)data->dest); + } else { + if (wml_value > 0x80) + wml_value = 0x80; + if ((in_be32(®s->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(®s->dsaddr, (u32)data->src); + } + + out_be32(®s->wml, wml_value); + + out_be32(®s->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(®s->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(®s->irqstat, -1); + + sync(); + + /* Wait for the bus to be idle */ + while ((in_be32(®s->prsstat) & PRSSTAT_CICHB) || + (in_be32(®s->prsstat) & PRSSTAT_CIDHB)); + + while (in_be32(®s->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(®s->cmdarg, cmd->cmdarg); + out_be32(®s->xfertyp, xfertyp); + + /* Wait for the command to complete */ + while (!(in_be32(®s->irqstat) & IRQSTAT_CC)); + + irqstat = in_be32(®s->irqstat); + out_be32(®s->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(®s->cmdrsp3); + cmdrsp2 = in_be32(®s->cmdrsp2); + cmdrsp1 = in_be32(®s->cmdrsp1); + cmdrsp0 = in_be32(®s->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(®s->cmdrsp0); + + /* Wait until all of the blocks are transferred */ + if (data) { + do { + irqstat = in_be32(®s->irqstat); + + if (irqstat & DATA_ERR) + return COMM_ERR; + + if (irqstat & IRQSTAT_DTOE) + return TIMEOUT; + } while (!(irqstat & IRQSTAT_TC) && + (in_be32(®s->prsstat) & PRSSTAT_DLA)); + } + + out_be32(®s->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(®s->sysctl, SYSCTL_CLOCK_MASK, clk); + + udelay(10000); + + setbits_be32(®s->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(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); + + if (mmc->bus_width == 4) + setbits_be32(®s->proctl, PROCTL_DTW_4); + else if (mmc->bus_width == 8) + setbits_be32(®s->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(®s->scr, 0x00000040); + + out_be32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); + + /* Set the initial clock speed */ + set_sysctl(mmc, 400000); + + /* Disable the BRR and BWR bits in IRQSTAT */ + clrbits_be32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); + + /* Put the PROCTL reg back to the default */ + out_be32(®s->proctl, PROCTL_INIT); + + while (!(in_be32(®s->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 index 000000000..0a5c5d626 --- /dev/null +++ b/include/fsl_esdhc.h @@ -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__ */