2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2011-11-02 00:29:27 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
|
|
|
|
*
|
2019-05-20 00:44:59 +00:00
|
|
|
* Modified to add driver model (DM) support
|
|
|
|
* Copyright (C) 2019 Marcel Ziswiler <marcel@ziswiler.com>
|
|
|
|
*
|
2011-11-02 00:29:27 +00:00
|
|
|
* 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>
|
2020-05-10 17:40:11 +00:00
|
|
|
#include <linux/delay.h>
|
2016-09-21 02:28:55 +00:00
|
|
|
#include <linux/errno.h>
|
2011-11-02 00:29:27 +00:00
|
|
|
#include <asm/io.h>
|
2019-05-20 00:44:59 +00:00
|
|
|
#include <dm.h>
|
|
|
|
#include <dm/platform_data/pxa_mmc_gen.h>
|
2015-08-16 02:16:27 +00:00
|
|
|
#include <malloc.h>
|
|
|
|
#include <mmc.h>
|
2011-11-02 00:29:27 +00:00
|
|
|
|
|
|
|
/* PXAMMC Generic default config for various CPUs */
|
2022-05-25 20:13:48 +00:00
|
|
|
#if defined(CONFIG_CPU_PXA27X)
|
2011-11-02 00:29:27 +00:00
|
|
|
#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)
|
|
|
|
{
|
2014-03-11 17:34:20 +00:00
|
|
|
struct pxa_mmc_priv *priv = mmc->priv;
|
2011-11-02 00:29:27 +00:00
|
|
|
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)
|
|
|
|
{
|
2014-03-11 17:34:20 +00:00
|
|
|
struct pxa_mmc_priv *priv = mmc->priv;
|
2011-11-02 00:29:27 +00:00
|
|
|
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,
|
2019-05-20 00:44:59 +00:00
|
|
|
uint32_t cmdat)
|
2011-11-02 00:29:27 +00:00
|
|
|
{
|
2014-03-11 17:34:20 +00:00
|
|
|
struct pxa_mmc_priv *priv = mmc->priv;
|
2011-11-02 00:29:27 +00:00
|
|
|
struct pxa_mmc_regs *regs = priv->regs;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* The card can send a "busy" response */
|
2012-09-06 20:23:13 +00:00
|
|
|
if (cmd->resp_type & MMC_RSP_BUSY)
|
2011-11-02 00:29:27 +00:00
|
|
|
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)
|
|
|
|
{
|
2014-03-11 17:34:20 +00:00
|
|
|
struct pxa_mmc_priv *priv = mmc->priv;
|
2011-11-02 00:29:27 +00:00
|
|
|
struct pxa_mmc_regs *regs = priv->regs;
|
2019-05-20 00:44:59 +00:00
|
|
|
u32 a, b, c;
|
2011-11-02 00:29:27 +00:00
|
|
|
int i;
|
|
|
|
int stat;
|
|
|
|
|
|
|
|
/* Read the controller status */
|
|
|
|
stat = readl(®s->stat);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Linux says:
|
2019-05-20 00:44:59 +00:00
|
|
|
* Did I mention this is Sick. We always need to
|
2011-11-02 00:29:27 +00:00
|
|
|
* 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 */
|
2019-05-20 00:44:59 +00:00
|
|
|
if (stat & MMC_STAT_TIME_OUT_RESPONSE) {
|
2011-11-02 00:29:27 +00:00
|
|
|
return -ETIMEDOUT;
|
2019-05-20 00:44:59 +00:00
|
|
|
} 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))
|
2011-11-02 00:29:27 +00:00
|
|
|
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)
|
|
|
|
{
|
2014-03-11 17:34:20 +00:00
|
|
|
struct pxa_mmc_priv *priv = mmc->priv;
|
2011-11-02 00:29:27 +00:00
|
|
|
struct pxa_mmc_regs *regs = priv->regs;
|
2019-05-20 00:44:59 +00:00
|
|
|
u32 len;
|
|
|
|
u32 *buf = (uint32_t *)data->dest;
|
2011-11-02 00:29:27 +00:00
|
|
|
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) {
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
size = min(len, (uint32_t)PXAMMC_FIFO_SIZE);
|
2011-11-02 00:29:27 +00:00
|
|
|
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)
|
|
|
|
{
|
2014-03-11 17:34:20 +00:00
|
|
|
struct pxa_mmc_priv *priv = mmc->priv;
|
2011-11-02 00:29:27 +00:00
|
|
|
struct pxa_mmc_regs *regs = priv->regs;
|
2019-05-20 00:44:59 +00:00
|
|
|
u32 len;
|
|
|
|
u32 *buf = (uint32_t *)data->src;
|
2011-11-02 00:29:27 +00:00
|
|
|
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) {
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
size = min(len, (uint32_t)PXAMMC_FIFO_SIZE);
|
2011-11-02 00:29:27 +00:00
|
|
|
len -= size;
|
|
|
|
size /= 4;
|
|
|
|
|
|
|
|
while (size--)
|
|
|
|
writel(*buf++, ®s->txfifo);
|
|
|
|
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
if (min(len, (uint32_t)PXAMMC_FIFO_SIZE) < 32)
|
2011-11-02 00:29:27 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-05-20 00:44:59 +00:00
|
|
|
static int pxa_mmc_send_cmd_common(struct pxa_mmc_priv *priv, struct mmc *mmc,
|
|
|
|
struct mmc_cmd *cmd, struct mmc_data *data)
|
2011-11-02 00:29:27 +00:00
|
|
|
{
|
|
|
|
struct pxa_mmc_regs *regs = priv->regs;
|
2019-05-20 00:44:59 +00:00
|
|
|
u32 cmdat = 0;
|
2011-11-02 00:29:27 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-05-20 00:44:59 +00:00
|
|
|
static int pxa_mmc_set_ios_common(struct pxa_mmc_priv *priv, struct mmc *mmc)
|
2011-11-02 00:29:27 +00:00
|
|
|
{
|
|
|
|
struct pxa_mmc_regs *regs = priv->regs;
|
2019-05-20 00:44:59 +00:00
|
|
|
u32 tmp;
|
|
|
|
u32 pxa_mmc_clock;
|
2011-11-02 00:29:27 +00:00
|
|
|
|
|
|
|
if (!mmc->clock) {
|
|
|
|
pxa_mmc_stop_clock(mmc);
|
2016-12-30 06:30:16 +00:00
|
|
|
return 0;
|
2011-11-02 00:29:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* PXA3xx can do 26MHz with special settings. */
|
|
|
|
if (mmc->clock == 26000000) {
|
|
|
|
writel(0x7, ®s->clkrt);
|
2016-12-30 06:30:16 +00:00
|
|
|
return 0;
|
2011-11-02 00:29:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set clock to the card the usual way. */
|
|
|
|
pxa_mmc_clock = 0;
|
2014-03-11 17:34:20 +00:00
|
|
|
tmp = mmc->cfg->f_max / mmc->clock;
|
2011-11-02 00:29:27 +00:00
|
|
|
tmp += tmp % 2;
|
|
|
|
|
|
|
|
while (tmp > 1) {
|
|
|
|
pxa_mmc_clock++;
|
|
|
|
tmp >>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
writel(pxa_mmc_clock, ®s->clkrt);
|
2016-12-30 06:30:16 +00:00
|
|
|
|
|
|
|
return 0;
|
2011-11-02 00:29:27 +00:00
|
|
|
}
|
|
|
|
|
2019-05-20 00:44:59 +00:00
|
|
|
static int pxa_mmc_init_common(struct pxa_mmc_priv *priv, struct mmc *mmc)
|
2011-11-02 00:29:27 +00:00
|
|
|
{
|
|
|
|
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),
|
2019-05-20 00:44:59 +00:00
|
|
|
®s->i_mask);
|
|
|
|
|
2011-11-02 00:29:27 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-05-20 00:44:59 +00:00
|
|
|
#if !CONFIG_IS_ENABLED(DM_MMC)
|
|
|
|
static int pxa_mmc_init(struct mmc *mmc)
|
|
|
|
{
|
|
|
|
struct pxa_mmc_priv *priv = mmc->priv;
|
|
|
|
|
|
|
|
return pxa_mmc_init_common(priv, mmc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pxa_mmc_request(struct mmc *mmc, struct mmc_cmd *cmd,
|
|
|
|
struct mmc_data *data)
|
|
|
|
{
|
|
|
|
struct pxa_mmc_priv *priv = mmc->priv;
|
|
|
|
|
|
|
|
return pxa_mmc_send_cmd_common(priv, mmc, cmd, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pxa_mmc_set_ios(struct mmc *mmc)
|
|
|
|
{
|
|
|
|
struct pxa_mmc_priv *priv = mmc->priv;
|
|
|
|
|
|
|
|
return pxa_mmc_set_ios_common(priv, mmc);
|
|
|
|
}
|
|
|
|
|
2014-02-26 17:28:45 +00:00
|
|
|
static const struct mmc_ops pxa_mmc_ops = {
|
|
|
|
.send_cmd = pxa_mmc_request,
|
|
|
|
.set_ios = pxa_mmc_set_ios,
|
|
|
|
.init = pxa_mmc_init,
|
|
|
|
};
|
|
|
|
|
2014-03-11 17:34:20 +00:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2011-11-02 00:29:27 +00:00
|
|
|
int pxa_mmc_register(int card_index)
|
|
|
|
{
|
|
|
|
struct mmc *mmc;
|
|
|
|
struct pxa_mmc_priv *priv;
|
2019-05-20 00:44:59 +00:00
|
|
|
u32 reg;
|
2011-11-02 00:29:27 +00:00
|
|
|
int ret = -ENOMEM;
|
|
|
|
|
|
|
|
priv = malloc(sizeof(struct pxa_mmc_priv));
|
|
|
|
if (!priv)
|
2014-03-11 17:34:20 +00:00
|
|
|
goto err0;
|
|
|
|
|
|
|
|
memset(priv, 0, sizeof(*priv));
|
2011-11-02 00:29:27 +00:00
|
|
|
|
|
|
|
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:
|
2014-03-11 17:34:20 +00:00
|
|
|
ret = -EINVAL;
|
2011-11-02 00:29:27 +00:00
|
|
|
printf("PXA MMC: Invalid MMC controller ID (card_index = %d)\n",
|
2019-05-20 00:44:59 +00:00
|
|
|
card_index);
|
2014-03-11 17:34:20 +00:00
|
|
|
goto err1;
|
2011-11-02 00:29:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
2014-03-11 17:34:20 +00:00
|
|
|
mmc = mmc_create(&pxa_mmc_cfg, priv);
|
2019-05-20 00:44:59 +00:00
|
|
|
if (!mmc)
|
2014-03-11 17:34:20 +00:00
|
|
|
goto err1;
|
2011-11-02 00:29:27 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err1:
|
2014-03-11 17:34:20 +00:00
|
|
|
free(priv);
|
2011-11-02 00:29:27 +00:00
|
|
|
err0:
|
|
|
|
return ret;
|
|
|
|
}
|
2019-05-20 00:44:59 +00:00
|
|
|
#else /* !CONFIG_IS_ENABLED(DM_MMC) */
|
|
|
|
static int pxa_mmc_probe(struct udevice *dev)
|
|
|
|
{
|
|
|
|
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
|
2020-12-03 23:55:20 +00:00
|
|
|
struct pxa_mmc_plat *plat = dev_get_plat(dev);
|
2019-05-20 00:44:59 +00:00
|
|
|
struct mmc_config *cfg = &plat->cfg;
|
|
|
|
struct mmc *mmc = &plat->mmc;
|
|
|
|
struct pxa_mmc_priv *priv = dev_get_priv(dev);
|
|
|
|
u32 reg;
|
|
|
|
|
|
|
|
upriv->mmc = mmc;
|
|
|
|
|
|
|
|
cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
|
|
|
|
cfg->f_max = PXAMMC_MAX_SPEED;
|
|
|
|
cfg->f_min = PXAMMC_MIN_SPEED;
|
|
|
|
cfg->host_caps = PXAMMC_HOST_CAPS;
|
|
|
|
cfg->name = dev->name;
|
|
|
|
cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
|
|
|
|
|
|
|
|
mmc->priv = priv;
|
|
|
|
|
|
|
|
priv->regs = plat->base;
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
return pxa_mmc_init_common(priv, mmc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pxa_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
|
|
|
|
struct mmc_data *data)
|
|
|
|
{
|
2020-12-03 23:55:20 +00:00
|
|
|
struct pxa_mmc_plat *plat = dev_get_plat(dev);
|
2019-05-20 00:44:59 +00:00
|
|
|
struct pxa_mmc_priv *priv = dev_get_priv(dev);
|
|
|
|
|
|
|
|
return pxa_mmc_send_cmd_common(priv, &plat->mmc, cmd, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pxa_mmc_set_ios(struct udevice *dev)
|
|
|
|
{
|
2020-12-03 23:55:20 +00:00
|
|
|
struct pxa_mmc_plat *plat = dev_get_plat(dev);
|
2019-05-20 00:44:59 +00:00
|
|
|
struct pxa_mmc_priv *priv = dev_get_priv(dev);
|
|
|
|
|
|
|
|
return pxa_mmc_set_ios_common(priv, &plat->mmc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dm_mmc_ops pxa_mmc_ops = {
|
|
|
|
.get_cd = NULL,
|
|
|
|
.send_cmd = pxa_mmc_send_cmd,
|
|
|
|
.set_ios = pxa_mmc_set_ios,
|
|
|
|
};
|
|
|
|
|
|
|
|
#if CONFIG_IS_ENABLED(BLK)
|
|
|
|
static int pxa_mmc_bind(struct udevice *dev)
|
|
|
|
{
|
2020-12-03 23:55:20 +00:00
|
|
|
struct pxa_mmc_plat *plat = dev_get_plat(dev);
|
2019-05-20 00:44:59 +00:00
|
|
|
|
|
|
|
return mmc_bind(dev, &plat->mmc, &plat->cfg);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
U_BOOT_DRIVER(pxa_mmc) = {
|
|
|
|
#if CONFIG_IS_ENABLED(BLK)
|
|
|
|
.bind = pxa_mmc_bind,
|
|
|
|
#endif
|
|
|
|
.id = UCLASS_MMC,
|
|
|
|
.name = "pxa_mmc",
|
|
|
|
.ops = &pxa_mmc_ops,
|
2020-12-03 23:55:17 +00:00
|
|
|
.priv_auto = sizeof(struct pxa_mmc_priv),
|
2019-05-20 00:44:59 +00:00
|
|
|
.probe = pxa_mmc_probe,
|
|
|
|
};
|
|
|
|
#endif /* !CONFIG_IS_ENABLED(DM_MMC) */
|