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