Hi Marek, On 08/29/11 01:02, Marek Vasut wrote: > Signed-off-by: Marek Vasut <marek.va...@gmail.com> > --- > arch/arm/include/asm/arch-pxa/regs-mmc.h | 155 +++++++++++ > drivers/mmc/Makefile | 1 + > drivers/mmc/pxa_mmc_gen.c | 442 > ++++++++++++++++++++++++++++++ > 3 files changed, 598 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/include/asm/arch-pxa/regs-mmc.h > create mode 100644 drivers/mmc/pxa_mmc_gen.c > > diff --git a/arch/arm/include/asm/arch-pxa/regs-mmc.h > b/arch/arm/include/asm/arch-pxa/regs-mmc.h > new file mode 100644 > index 0000000..fd1eb1e > --- /dev/null > +++ b/arch/arm/include/asm/arch-pxa/regs-mmc.h > @@ -0,0 +1,155 @@ > +/* > + * Copyright (C) 2011 Marek Vasut <marek.va...@gmail.com> > + * > + * 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 __REGS_MMC_H__ > +#define __REGS_MMC_H__ > + > +#define MMC0_BASE 0x41100000 > +#define MMC1_BASE 0x42000000 > + > +int pxa_mmc_register(int card_index); > + > +struct pxa_mmc_regs { > + uint32_t strpcl; > + uint32_t stat; > + uint32_t clkrt; > + uint32_t spi; > + uint32_t cmdat; > + uint32_t resto; > + uint32_t rdto; > + uint32_t blklen; > + uint32_t nob; > + uint32_t prtbuf; > + uint32_t i_mask; > + uint32_t i_reg; > + uint32_t cmd; > + uint32_t argh; > + uint32_t argl; > + uint32_t res; > + uint32_t rxfifo; > + uint32_t txfifo;
Isn't space would be enough? I mean, they all the same type, so no alignment issues... > +}; > + > +/* MMC_STRPCL */ > +#define MMC_STRPCL_STOP_CLK (1 << 0) > +#define MMC_STRPCL_START_CLK (1 << 1) > + > +/* MMC_STAT */ > +#define MMC_STAT_END_CMD_RES (1 << 13) > +#define MMC_STAT_PRG_DONE (1 << 12) > +#define MMC_STAT_DATA_TRAN_DONE (1 << 11) > +#define MMC_STAT_CLK_EN (1 << 8) > +#define MMC_STAT_RECV_FIFO_FULL (1 << 7) > +#define MMC_STAT_XMIT_FIFO_EMPTY (1 << 6) > +#define MMC_STAT_RES_CRC_ERROR (1 << 5) > +#define MMC_STAT_SPI_READ_ERROR_TOKEN (1 << 4) > +#define MMC_STAT_CRC_READ_ERROR (1 << 3) > +#define MMC_STAT_CRC_WRITE_ERROR (1 << 2) > +#define MMC_STAT_TIME_OUT_RESPONSE (1 << 1) > +#define MMC_STAT_READ_TIME_OUT (1 << 0) > + > +/* MMC_CLKRT */ > +#define MMC_CLKRT_20MHZ 0 > +#define MMC_CLKRT_10MHZ 1 > +#define MMC_CLKRT_5MHZ 2 > +#define MMC_CLKRT_2_5MHZ 3 > +#define MMC_CLKRT_1_25MHZ 4 > +#define MMC_CLKRT_0_625MHZ 5 > +#define MMC_CLKRT_0_3125MHZ 6 > + > +/* MMC_SPI */ > +#define MMC_SPI_EN (1 << 0) > +#define MMC_SPI_CS_EN (1 << 2) > +#define MMC_SPI_CS_ADDRESS (1 << 3) > +#define MMC_SPI_CRC_ON (1 << 1) > + > +/* MMC_CMDAT */ > +#define MMC_CMDAT_SD_4DAT (1 << 8) > +#define MMC_CMDAT_MMC_DMA_EN (1 << 7) > +#define MMC_CMDAT_INIT (1 << 6) > +#define MMC_CMDAT_BUSY (1 << 5) > +#define MMC_CMDAT_BCR (MMC_CMDAT_BUSY | > MMC_CMDAT_INIT) > +#define MMC_CMDAT_STREAM (1 << 4) > +#define MMC_CMDAT_WRITE (1 << 3) > +#define MMC_CMDAT_DATA_EN (1 << 2) > +#define MMC_CMDAT_R0 0 > +#define MMC_CMDAT_R1 1 > +#define MMC_CMDAT_R2 2 > +#define MMC_CMDAT_R3 3 > + > +/* MMC_RESTO */ > +#define MMC_RES_TO_MAX_MASK 0x7f > + > +/* MMC_RDTO */ > +#define MMC_READ_TO_MAX_MASK 0xffff > + > +/* MMC_BLKLEN */ > +#define MMC_BLK_LEN_MAX_MASK 0x3ff > + > +/* MMC_PRTBUF */ > +#define MMC_PRTBUF_BUF_PART_FULL (1 << 0) > + > +/* MMC_I_MASK */ > +#define MMC_I_MASK_TXFIFO_WR_REQ (1 << 6) > +#define MMC_I_MASK_RXFIFO_RD_REQ (1 << 5) > +#define MMC_I_MASK_CLK_IS_OFF (1 << 4) > +#define MMC_I_MASK_STOP_CMD (1 << 3) > +#define MMC_I_MASK_END_CMD_RES (1 << 2) > +#define MMC_I_MASK_PRG_DONE (1 << 1) > +#define MMC_I_MASK_DATA_TRAN_DONE (1 << 0) > +#define MMC_I_MASK_ALL 0x7f > + > + > +/* MMC_I_REG */ > +#define MMC_I_REG_TXFIFO_WR_REQ (1 << 6) > +#define MMC_I_REG_RXFIFO_RD_REQ (1 << 5) > +#define MMC_I_REG_CLK_IS_OFF (1 << 4) > +#define MMC_I_REG_STOP_CMD (1 << 3) > +#define MMC_I_REG_END_CMD_RES (1 << 2) > +#define MMC_I_REG_PRG_DONE (1 << 1) > +#define MMC_I_REG_DATA_TRAN_DONE (1 << 0) > + > +/* MMC_CMD */ > +#define MMC_CMD_INDEX_MAX 0x6f > +#define CMD(x) (x) You've missed this one > + > +#define MMC_R1_IDLE_STATE 0x01 > +#define MMC_R1_ERASE_STATE 0x02 > +#define MMC_R1_ILLEGAL_CMD 0x04 > +#define MMC_R1_COM_CRC_ERR 0x08 > +#define MMC_R1_ERASE_SEQ_ERR 0x01 > +#define MMC_R1_ADDR_ERR 0x02 > +#define MMC_R1_PARAM_ERR 0x04 > + > +#define MMC_R1B_WP_ERASE_SKIP 0x0002 > +#define MMC_R1B_ERR 0x0004 > +#define MMC_R1B_CC_ERR 0x0008 > +#define MMC_R1B_CARD_ECC_ERR 0x0010 > +#define MMC_R1B_WP_VIOLATION 0x0020 > +#define MMC_R1B_ERASE_PARAM 0x0040 > +#define MMC_R1B_OOR 0x0080 > +#define MMC_R1B_IDLE_STATE 0x0100 > +#define MMC_R1B_ERASE_RESET 0x0200 > +#define MMC_R1B_ILLEGAL_CMD 0x0400 > +#define MMC_R1B_COM_CRC_ERR 0x0800 > +#define MMC_R1B_ERASE_SEQ_ERR 0x1000 > +#define MMC_R1B_ADDR_ERR 0x2000 > +#define MMC_R1B_PARAM_ERR 0x4000 All the above defines are really unnecessarily "tabbed"... The values are fine, but why the names? > + > +#endif /* __REGS_MMC_H__ */ > diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile > index 3968c14..59bda49 100644 > --- a/drivers/mmc/Makefile > +++ b/drivers/mmc/Makefile > @@ -38,6 +38,7 @@ COBJS-$(CONFIG_MXC_MMC) += mxcmmc.o > COBJS-$(CONFIG_OMAP3_MMC) += omap3_mmc.o > COBJS-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o > COBJS-$(CONFIG_PXA_MMC) += pxa_mmc.o > +COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o > COBJS-$(CONFIG_S5P_MMC) += s5p_mmc.o > COBJS-$(CONFIG_SDHCI) += sdhci.o > COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o > diff --git a/drivers/mmc/pxa_mmc_gen.c b/drivers/mmc/pxa_mmc_gen.c > new file mode 100644 > index 0000000..aa475e5 > --- /dev/null > +++ b/drivers/mmc/pxa_mmc_gen.c > @@ -0,0 +1,442 @@ > +/* > + * 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 <asm/arch/regs-mmc.h> > +#include <asm/io.h> > + > +/* 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) Same here... and below... why? > +#else > +#error "This CPU isn't supported by PXA MMC!" > +#endif > + > +#define MMC_STAT_ERRORS > \ > + (MMC_STAT_RES_CRC_ERROR | MMC_STAT_SPI_READ_ERROR_TOKEN | \ > + MMC_STAT_CRC_READ_ERROR | MMC_STAT_TIME_OUT_RESPONSE | \ > + MMC_STAT_READ_TIME_OUT | MMC_STAT_CRC_WRITE_ERROR) > + > +/* 1 millisecond (in wait cycles below it's 100 x 10uS waits) */ > +#define PXA_MMC_TIMEOUT 100 > + > +struct pxa_mmc_priv { > + struct pxa_mmc_regs *regs; > +}; > + > +/* Wait for bit to be set */ > +static int pxa_mmc_wait(struct mmc *mmc, uint32_t mask) > +{ > + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; > + struct pxa_mmc_regs *regs = priv->regs; > + unsigned int timeout = PXA_MMC_TIMEOUT; > + > + /* Wait for bit to be set */ > + while (--timeout) { > + if (readl(®s->stat) & mask) > + break; > + udelay(10); > + } > + > + if (!timeout) > + return -ETIMEDOUT; > + > + return 0; > +} > + > +static int pxa_mmc_stop_clock(struct mmc *mmc) > +{ > + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; > + struct pxa_mmc_regs *regs = priv->regs; > + unsigned int timeout = PXA_MMC_TIMEOUT; > + > + /* If the clock aren't running, exit */ > + if (!(readl(®s->stat) & MMC_STAT_CLK_EN)) > + return 0; > + > + /* Tell the controller to turn off the clock */ > + writel(MMC_STRPCL_STOP_CLK, ®s->strpcl); > + > + /* Wait until the clock are off */ > + while (--timeout) { > + if (!(readl(®s->stat) & MMC_STAT_CLK_EN)) > + break; > + 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 *mmc, struct mmc_cmd *cmd, > + uint32_t cmdat) > +{ > + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; > + struct pxa_mmc_regs *regs = priv->regs; > + 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 */ > + writel(cmd->cmdidx, ®s->cmd); > + writel(cmd->cmdarg >> 16, ®s->argh); > + writel(cmd->cmdarg & 0xffff, ®s->argl); > + writel(cmdat, ®s->cmdat); > + > + /* Start the controller clock and wait until they are started */ > + writel(MMC_STRPCL_START_CLK, ®s->strpcl); > + > + ret = pxa_mmc_wait(mmc, MMC_STAT_CLK_EN); > + if (ret) > + return ret; > + > + /* Correct and happy end */ > + return 0; > +} > + > +static int pxa_mmc_cmd_done(struct mmc *mmc, struct mmc_cmd *cmd) > +{ > + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; > + struct pxa_mmc_regs *regs = priv->regs; > + uint32_t a, b, c; > + int i; > + int stat; > + > + /* Read the controller status */ > + stat = readl(®s->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 = readl(®s->res) & 0xffff; > + for (i = 0; i < 4; i++) { > + b = readl(®s->res) & 0xffff; > + c = readl(®s->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] & (1 << 31)) > + 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 *mmc, struct mmc_data *data) > +{ > + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; > + struct pxa_mmc_regs *regs = priv->regs; > + uint32_t len; > + uint32_t *buf = (uint32_t *)data->dest; > + int size; > + int ret; > + > + len = data->blocks * data->blocksize; > + > + while (len) { > + /* The controller has data ready */ > + if (readl(®s->i_reg) & MMC_I_REG_RXFIFO_RD_REQ) { > + size = min(len, PXAMMC_FIFO_SIZE); > + len -= size; > + size /= 4; > + > + /* Read data into the buffer */ > + while (size--) > + *buf++ = readl(®s->rxfifo); > + > + } > + > + if (readl(®s->stat) & MMC_STAT_ERRORS) > + return -EIO; > + } > + > + /* Wait for the transmission-done interrupt */ > + ret = pxa_mmc_wait(mmc, MMC_STAT_DATA_TRAN_DONE); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int pxa_mmc_do_write_xfer(struct mmc *mmc, struct mmc_data *data) > +{ > + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; > + struct pxa_mmc_regs *regs = priv->regs; > + uint32_t len; > + uint32_t *buf = (uint32_t *)data->src; > + int size; > + int ret; > + > + len = data->blocks * data->blocksize; > + > + while (len) { > + /* The controller is ready to receive data */ > + if (readl(®s->i_reg) & MMC_I_REG_TXFIFO_WR_REQ) { > + size = min(len, PXAMMC_FIFO_SIZE); > + len -= size; > + size /= 4; > + > + while (size--) > + writel(*buf++, ®s->txfifo); > + > + if (min(len, PXAMMC_FIFO_SIZE) < 32) > + writel(MMC_PRTBUF_BUF_PART_FULL, ®s->prtbuf); > + } > + > + if (readl(®s->stat) & MMC_STAT_ERRORS) > + return -EIO; > + } > + > + /* Wait for the transmission-done interrupt */ > + ret = pxa_mmc_wait(mmc, MMC_STAT_DATA_TRAN_DONE); > + if (ret) > + return ret; > + > + /* Wait until the data are really written to the card */ > + ret = pxa_mmc_wait(mmc, 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) > +{ > + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; > + struct pxa_mmc_regs *regs = priv->regs; > + uint32_t cmdat = 0; > + int ret; > + > + /* Stop the controller */ > + ret = pxa_mmc_stop_clock(mmc); > + if (ret) > + return ret; > + > + /* If we're doing data transfer, configure the controller accordingly */ > + if (data) { > + writel(data->blocks, ®s->nob); > + writel(data->blocksize, ®s->blklen); > + /* This delay can be optimized, but stick with max value */ > + writel(0xffff, ®s->rdto); > + 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(mmc, cmd, cmdat); > + if (ret) > + return ret; > + > + /* Wait until the command completes */ > + ret = pxa_mmc_wait(mmc, MMC_STAT_END_CMD_RES); > + if (ret) > + return ret; > + > + /* Read back the result */ > + ret = pxa_mmc_cmd_done(mmc, 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(mmc, data); > + else > + pxa_mmc_do_read_xfer(mmc, data); > + } > + > + return 0; > +} > + > +static void pxa_mmc_set_ios(struct mmc *mmc) > +{ > + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; > + struct pxa_mmc_regs *regs = priv->regs; > + uint32_t tmp; > + uint32_t pxa_mmc_clock; > + > + if (!mmc->clock) { > + pxa_mmc_stop_clock(mmc); > + return; > + } > + > + /* PXA3xx can do 26MHz with special settings. */ > + if (mmc->clock == 26000000) { > + writel(0x7, ®s->clkrt); > + return; > + } > + > + /* Set clock to the card the usual way. */ > + pxa_mmc_clock = 0; > + tmp = mmc->f_max / mmc->clock; > + tmp += tmp % 2; > + > + while (tmp > 1) { > + pxa_mmc_clock++; > + tmp >>= 1; > + } > + > + writel(pxa_mmc_clock, ®s->clkrt); > +} > + > +static int pxa_mmc_init(struct mmc *mmc) > +{ > + struct pxa_mmc_priv *priv = (struct pxa_mmc_priv *)mmc->priv; > + struct pxa_mmc_regs *regs = priv->regs; > + > + /* Make sure the clock are stopped */ > + pxa_mmc_stop_clock(mmc); > + > + /* Turn off SPI mode */ > + writel(0, ®s->spi); > + > + /* Set up maximum timeout to wait for command response */ > + writel(MMC_RES_TO_MAX_MASK, ®s->resto); > + > + /* Mask all interrupts */ > + writel(~(MMC_I_MASK_TXFIFO_WR_REQ | MMC_I_MASK_RXFIFO_RD_REQ), > + ®s->i_mask); > + return 0; > +} > + > +int pxa_mmc_register(int card_index) > +{ > + struct mmc *mmc; > + struct pxa_mmc_priv *priv; > + uint32_t reg; > + int ret = -ENOMEM; > + > + mmc = malloc(sizeof(struct mmc)); > + if (!mmc) > + goto err0; > + > + priv = malloc(sizeof(struct pxa_mmc_priv)); > + if (!priv) > + goto err1; > + > + switch (card_index) { > + case 0: > + priv->regs = (struct pxa_mmc_regs *)MMC0_BASE; > + break; > + case 1: > + priv->regs = (struct pxa_mmc_regs *)MMC1_BASE; > + break; > + default: > + printf("PXA MMC: Invalid MMC controller ID (card_index = %d)\n", > + card_index); > + goto err2; > + } > + > + mmc->priv = priv; > + > + sprintf(mmc->name, "PXA MMC"); > + mmc->send_cmd = pxa_mmc_request; > + mmc->set_ios = pxa_mmc_set_ios; > + mmc->init = pxa_mmc_init; > + > + 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->b_max = 0; > + > +#ifndef CONFIG_CPU_MONAHANS /* PXA2xx */ > + reg = readl(CKEN); > + reg |= CKEN12_MMC; > + writel(reg, CKEN); > +#else /* PXA3xx */ > + reg = readl(CKENA); > + reg |= CKENA_12_MMC0 | CKENA_13_MMC1; > + writel(reg, CKENA); > +#endif > + > + mmc_register(mmc); > + > + return 0; > + > +err2: > + free(priv); > +err1: > + free(mmc); > +err0: > + return ret; > +} -- Regards, Igor. _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot