
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;
+}