mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-20 08:58:52 +09:00
![Tom Rini](/assets/img/avatar_default.png)
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
432 lines
9.4 KiB
C
432 lines
9.4 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
|
|
*
|
|
* Loosely based on the old code and Linux's PXA MMC driver
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/arch/hardware.h>
|
|
#include <asm/arch/regs-mmc.h>
|
|
#include <linux/errno.h>
|
|
#include <asm/io.h>
|
|
#include <malloc.h>
|
|
#include <mmc.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 = 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 = 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 = 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, ®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 = 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->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 = 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, (uint32_t)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 = 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, (uint32_t)PXAMMC_FIFO_SIZE);
|
|
len -= size;
|
|
size /= 4;
|
|
|
|
while (size--)
|
|
writel(*buf++, ®s->txfifo);
|
|
|
|
if (min(len, (uint32_t)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 = 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 int pxa_mmc_set_ios(struct mmc *mmc)
|
|
{
|
|
struct pxa_mmc_priv *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 0;
|
|
}
|
|
|
|
/* PXA3xx can do 26MHz with special settings. */
|
|
if (mmc->clock == 26000000) {
|
|
writel(0x7, ®s->clkrt);
|
|
return 0;
|
|
}
|
|
|
|
/* Set clock to the card the usual way. */
|
|
pxa_mmc_clock = 0;
|
|
tmp = mmc->cfg->f_max / mmc->clock;
|
|
tmp += tmp % 2;
|
|
|
|
while (tmp > 1) {
|
|
pxa_mmc_clock++;
|
|
tmp >>= 1;
|
|
}
|
|
|
|
writel(pxa_mmc_clock, ®s->clkrt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pxa_mmc_init(struct mmc *mmc)
|
|
{
|
|
struct pxa_mmc_priv *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;
|
|
}
|
|
|
|
static const struct mmc_ops pxa_mmc_ops = {
|
|
.send_cmd = pxa_mmc_request,
|
|
.set_ios = pxa_mmc_set_ios,
|
|
.init = pxa_mmc_init,
|
|
};
|
|
|
|
static struct mmc_config pxa_mmc_cfg = {
|
|
.name = "PXA MMC",
|
|
.ops = &pxa_mmc_ops,
|
|
.voltages = MMC_VDD_32_33 | MMC_VDD_33_34,
|
|
.f_max = PXAMMC_MAX_SPEED,
|
|
.f_min = PXAMMC_MIN_SPEED,
|
|
.host_caps = PXAMMC_HOST_CAPS,
|
|
.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT,
|
|
};
|
|
|
|
int pxa_mmc_register(int card_index)
|
|
{
|
|
struct mmc *mmc;
|
|
struct pxa_mmc_priv *priv;
|
|
uint32_t reg;
|
|
int ret = -ENOMEM;
|
|
|
|
priv = malloc(sizeof(struct pxa_mmc_priv));
|
|
if (!priv)
|
|
goto err0;
|
|
|
|
memset(priv, 0, sizeof(*priv));
|
|
|
|
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:
|
|
ret = -EINVAL;
|
|
printf("PXA MMC: Invalid MMC controller ID (card_index = %d)\n",
|
|
card_index);
|
|
goto err1;
|
|
}
|
|
|
|
#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 = mmc_create(&pxa_mmc_cfg, priv);
|
|
if (mmc == NULL)
|
|
goto err1;
|
|
|
|
return 0;
|
|
|
|
err1:
|
|
free(priv);
|
|
err0:
|
|
return ret;
|
|
}
|