[U-Boot] [PATCH 1/2] PXA: Add MMC driver using the generic MMC framework

Signed-off-by: Marek Vasut marek.vasut@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.vasut@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; +}; + +/* 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) + +#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 + +#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.vasut@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) +#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; +}

Signed-off-by: Marek Vasut marek.vasut@gmail.com --- board/vpac270/vpac270.c | 8 ++++++++ include/configs/vpac270.h | 3 ++- 2 files changed, 10 insertions(+), 1 deletions(-)
diff --git a/board/vpac270/vpac270.c b/board/vpac270/vpac270.c index 43bbdff..d3f03c0 100644 --- a/board/vpac270/vpac270.c +++ b/board/vpac270/vpac270.c @@ -21,6 +21,7 @@
#include <common.h> #include <asm/arch/hardware.h> +#include <asm/arch/regs-mmc.h> #include <netdev.h> #include <serial.h> #include <asm/io.h> @@ -72,6 +73,13 @@ void dram_init_banksize(void) #endif }
+#ifdef CONFIG_CMD_MMC +int board_mmc_init(bd_t *bis) +{ + pxa_mmc_register(0); +} +#endif + #ifdef CONFIG_CMD_USB int usb_board_init(void) { diff --git a/include/configs/vpac270.h b/include/configs/vpac270.h index ea77971..9c3980d 100644 --- a/include/configs/vpac270.h +++ b/include/configs/vpac270.h @@ -108,7 +108,8 @@ */ #ifdef CONFIG_CMD_MMC #define CONFIG_MMC -#define CONFIG_PXA_MMC +#define CONFIG_GENERIC_MMC +#define CONFIG_PXA_MMC_GENERIC #define CONFIG_SYS_MMC_BASE 0xF0000000 #define CONFIG_CMD_FAT #define CONFIG_CMD_EXT2

Signed-off-by: Marek Vasut marek.vasut@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
Hey Andy,
maybe you can pick this up now?
Cheers

Hi Marek,
On 08/29/11 01:02, Marek Vasut wrote:
Signed-off-by: Marek Vasut marek.vasut@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.vasut@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.vasut@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;
+}

Hi Marek,
Hi Igor,
On 08/29/11 01:02, Marek Vasut wrote:
Signed-off-by: Marek Vasut marek.vasut@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.vasut@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...
It's more readable like this, though that's likely an issue of personal taste.
+};
+/* 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
Thanks
+#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?
I'm used to put tabs there ... is there actually any rule against that ? Checkpatch doesn't complain.
+#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
[...]
Cheers

On 11/01/11 14:20, Marek Vasut wrote:
Hi Marek,
Hi Igor,
On 08/29/11 01:02, Marek Vasut wrote:
Signed-off-by: Marek Vasut marek.vasut@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.vasut@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...
It's more readable like this, though that's likely an issue of personal taste.
Agreed, it is personal taste.
+};
+/* 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
Thanks
+#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?
I'm used to put tabs there ... is there actually any rule against that ? Checkpatch doesn't complain.
There is no rule, but it seems redundant, especially with the 80 characters limit (though it is not a hard limit).
+#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
[...]
Cheers

Signed-off-by: Marek Vasut marek.vasut@gmail.com --- arch/arm/include/asm/arch-pxa/regs-mmc.h | 154 +++++++++++ drivers/mmc/Makefile | 1 + drivers/mmc/pxa_mmc_gen.c | 442 ++++++++++++++++++++++++++++++ 3 files changed, 597 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-pxa/regs-mmc.h create mode 100644 drivers/mmc/pxa_mmc_gen.c
V2: Squash CMD(x) and use #define[space]
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..6b7dfd3 --- /dev/null +++ b/arch/arm/include/asm/arch-pxa/regs-mmc.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2011 Marek Vasut marek.vasut@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; +}; + +/* 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 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 + +#endif /* __REGS_MMC_H__ */ diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 6e94860..9f9db75 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -38,6 +38,7 @@ COBJS-$(CONFIG_MV_SDHCI) += mv_sdhci.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.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..28e37b4 --- /dev/null +++ b/drivers/mmc/pxa_mmc_gen.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2010 Marek Vasut marek.vasut@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) +#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; +}

On 11/02/11 00:58, Marek Vasut wrote:
Signed-off-by: Marek Vasut marek.vasut@gmail.com
arch/arm/include/asm/arch-pxa/regs-mmc.h | 154 +++++++++++ drivers/mmc/Makefile | 1 + drivers/mmc/pxa_mmc_gen.c | 442 ++++++++++++++++++++++++++++++ 3 files changed, 597 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-pxa/regs-mmc.h create mode 100644 drivers/mmc/pxa_mmc_gen.c
V2: Squash CMD(x) and use #define[space]
[...]
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..6b7dfd3 --- /dev/null +++ b/arch/arm/include/asm/arch-pxa/regs-mmc.h
[...]
+/* 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)
You have 3 spaces here while all other lines use tab.
+#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)
spaces instead before the parenthesis
[...]

On 11/02/11 00:58, Marek Vasut wrote:
Signed-off-by: Marek Vasut marek.vasut@gmail.com
arch/arm/include/asm/arch-pxa/regs-mmc.h | 154 +++++++++++ drivers/mmc/Makefile | 1 + drivers/mmc/pxa_mmc_gen.c | 442 ++++++++++++++++++++++++++++++ 3 files changed, 597 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-pxa/regs-mmc.h create mode 100644 drivers/mmc/pxa_mmc_gen.c
V2: Squash CMD(x) and use #define[space]
[...]
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..6b7dfd3 --- /dev/null +++ b/arch/arm/include/asm/arch-pxa/regs-mmc.h
[...]
+/* 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)
You have 3 spaces here while all other lines use tab.
+#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)
spaces instead before the parenthesis
Thanks! I need better old-crap-linting tool now :)
[...]

Signed-off-by: Marek Vasut marek.vasut@gmail.com --- arch/arm/include/asm/arch-pxa/regs-mmc.h | 154 +++++++++++ drivers/mmc/Makefile | 1 + drivers/mmc/pxa_mmc_gen.c | 442 ++++++++++++++++++++++++++++++ 3 files changed, 597 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-pxa/regs-mmc.h create mode 100644 drivers/mmc/pxa_mmc_gen.c
V2: Squash CMD(x) and use #define[space] V3: Fix whitespace issues
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..b5c9b3b --- /dev/null +++ b/arch/arm/include/asm/arch-pxa/regs-mmc.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2011 Marek Vasut marek.vasut@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; +}; + +/* 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 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 + +#endif /* __REGS_MMC_H__ */ diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 6e94860..9f9db75 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -38,6 +38,7 @@ COBJS-$(CONFIG_MV_SDHCI) += mv_sdhci.o COBJS-$(CONFIG_MXC_MMC) += mxcmmc.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..28e37b4 --- /dev/null +++ b/drivers/mmc/pxa_mmc_gen.c @@ -0,0 +1,442 @@ +/* + * Copyright (C) 2010 Marek Vasut marek.vasut@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) +#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; +}
participants (2)
-
Igor Grinberg
-
Marek Vasut