Skip to content
Snippets Groups Projects
Select Git revision
  • 3f9315c04f3be076b81c04260c351e26adb47f30
  • master default protected
  • early-display
  • variant-emmc-nvme-boot
  • 2023-01-25
  • v3
  • variant-emmc-nvme-boot
  • 2020-06-01
8 results

pxa_mmc_gen.c

Blame
  • Forked from Reform / reform-boundary-uboot
    Source project has a limited visibility.
    pxa_mmc_gen.c 10.14 KiB
    /*
     * 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_CPU_PXA25X)
    #define PXAMMC_FIFO_SIZE	1
    #define PXAMMC_MIN_SPEED	312500
    #define PXAMMC_MAX_SPEED	20000000
    #define PXAMMC_HOST_CAPS	(0)
    #elif defined(CONFIG_CPU_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(&regs->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(&regs->stat) & MMC_STAT_CLK_EN))
    		return 0;
    
    	/* Tell the controller to turn off the clock */
    	writel(MMC_STRPCL_STOP_CLK, &regs->strpcl);
    
    	/* Wait until the clock are off */
    	while (--timeout) {
    		if (!(readl(&regs->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->resp_type & 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, &regs->cmd);
    	writel(cmd->cmdarg >> 16, &regs->argh);
    	writel(cmd->cmdarg & 0xffff, &regs->argl);
    	writel(cmdat, &regs->cmdat);
    
    	/* Start the controller clock and wait until they are started */
    	writel(MMC_STRPCL_START_CLK, &regs->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(&regs->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(&regs->res) & 0xffff;
    	for (i = 0; i < 4; i++) {
    		b = readl(&regs->res) & 0xffff;
    		c = readl(&regs->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->resp_type & MMC_RSP_CRC) {
    #ifdef	PXAMMC_CRC_SKIP
    		if (cmd->resp_type & 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(&regs->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(&regs->rxfifo);
    
    		}
    
    		if (readl(&regs->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(&regs->i_reg) & MMC_I_REG_TXFIFO_WR_REQ) {
    			size = min(len, PXAMMC_FIFO_SIZE);
    			len -= size;
    			size /= 4;
    
    			while (size--)
    				writel(*buf++, &regs->txfifo);
    
    			if (min(len, PXAMMC_FIFO_SIZE) < 32)
    				writel(MMC_PRTBUF_BUF_PART_FULL, &regs->prtbuf);
    		}
    
    		if (readl(&regs->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, &regs->nob);
    		writel(data->blocksize, &regs->blklen);
    		/* This delay can be optimized, but stick with max value */
    		writel(0xffff, &regs->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, &regs->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, &regs->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, &regs->spi);
    
    	/* Set up maximum timeout to wait for command response */
    	writel(MMC_RES_TO_MAX_MASK, &regs->resto);
    
    	/* Mask all interrupts */
    	writel(~(MMC_I_MASK_TXFIFO_WR_REQ | MMC_I_MASK_RXFIFO_RD_REQ),
    		&regs->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->getcd	= NULL;
    
    	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;
    }