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