mmc: mxcmmc: add mpc512x SDHC support

The SDHC controller on mpc512x is compatible with i.MX31 SDHC,
so the mxcmmc driver can be used on mpc512x, too. Extend the
driver to support mpc512x as well.

Signed-off-by: Anatolij Gustschin <agust@denx.de>
Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Chris Ball <cjb@laptop.org>
This commit is contained in:
Anatolij Gustschin 2013-04-08 23:28:06 +02:00 committed by Chris Ball
parent 70aa610959
commit c7ceab0254
2 changed files with 94 additions and 49 deletions

View File

@ -320,12 +320,12 @@ config MMC_MSM
support for SDIO devices.
config MMC_MXC
tristate "Freescale i.MX21/27/31 Multimedia Card Interface support"
depends on ARCH_MXC
tristate "Freescale i.MX21/27/31 or MPC512x Multimedia Card support"
depends on ARCH_MXC || PPC_MPC512x
help
This selects the Freescale i.MX21, i.MX27 and i.MX31 Multimedia card
Interface. If you have a i.MX platform with a Multimedia Card slot,
say Y or M here.
This selects the Freescale i.MX21, i.MX27, i.MX31 or MPC512x
Multimedia Card Interface. If you have an i.MX or MPC512x platform
with a Multimedia Card slot, say Y or M here.
If unsure, say N.

View File

@ -41,7 +41,6 @@
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/sizes.h>
#include <linux/platform_data/mmc-mxcmmc.h>
#include <linux/platform_data/dma-imx.h>
@ -119,6 +118,7 @@
enum mxcmci_type {
IMX21_MMC,
IMX31_MMC,
MPC512X_MMC,
};
struct mxcmci_host {
@ -171,6 +171,9 @@ static struct platform_device_id mxcmci_devtype[] = {
}, {
.name = "imx31-mmc",
.driver_data = IMX31_MMC,
}, {
.name = "mpc512x-sdhc",
.driver_data = MPC512X_MMC,
}, {
/* sentinel */
}
@ -184,6 +187,9 @@ static const struct of_device_id mxcmci_of_match[] = {
}, {
.compatible = "fsl,imx31-mmc",
.data = &mxcmci_devtype[IMX31_MMC],
}, {
.compatible = "fsl,mpc5121-sdhc",
.data = &mxcmci_devtype[MPC512X_MMC],
}, {
/* sentinel */
}
@ -195,6 +201,43 @@ static inline int is_imx31_mmc(struct mxcmci_host *host)
return host->devtype == IMX31_MMC;
}
static inline int is_mpc512x_mmc(struct mxcmci_host *host)
{
return host->devtype == MPC512X_MMC;
}
static inline u32 mxcmci_readl(struct mxcmci_host *host, int reg)
{
if (IS_ENABLED(CONFIG_PPC_MPC512x))
return ioread32be(host->base + reg);
else
return readl(host->base + reg);
}
static inline void mxcmci_writel(struct mxcmci_host *host, u32 val, int reg)
{
if (IS_ENABLED(CONFIG_PPC_MPC512x))
iowrite32be(val, host->base + reg);
else
writel(val, host->base + reg);
}
static inline u16 mxcmci_readw(struct mxcmci_host *host, int reg)
{
if (IS_ENABLED(CONFIG_PPC_MPC512x))
return ioread32be(host->base + reg);
else
return readw(host->base + reg);
}
static inline void mxcmci_writew(struct mxcmci_host *host, u16 val, int reg)
{
if (IS_ENABLED(CONFIG_PPC_MPC512x))
iowrite32be(val, host->base + reg);
else
writew(val, host->base + reg);
}
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
static inline void mxcmci_init_ocr(struct mxcmci_host *host)
@ -246,14 +289,14 @@ static void mxcmci_softreset(struct mxcmci_host *host)
dev_dbg(mmc_dev(host->mmc), "mxcmci_softreset\n");
/* reset sequence */
writew(STR_STP_CLK_RESET, host->base + MMC_REG_STR_STP_CLK);
writew(STR_STP_CLK_RESET | STR_STP_CLK_START_CLK,
host->base + MMC_REG_STR_STP_CLK);
mxcmci_writew(host, STR_STP_CLK_RESET, MMC_REG_STR_STP_CLK);
mxcmci_writew(host, STR_STP_CLK_RESET | STR_STP_CLK_START_CLK,
MMC_REG_STR_STP_CLK);
for (i = 0; i < 8; i++)
writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
mxcmci_writew(host, STR_STP_CLK_START_CLK, MMC_REG_STR_STP_CLK);
writew(0xff, host->base + MMC_REG_RES_TO);
mxcmci_writew(host, 0xff, MMC_REG_RES_TO);
}
static int mxcmci_setup_dma(struct mmc_host *mmc);
@ -272,8 +315,8 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data)
host->data = data;
data->bytes_xfered = 0;
writew(nob, host->base + MMC_REG_NOB);
writew(blksz, host->base + MMC_REG_BLK_LEN);
mxcmci_writew(host, nob, MMC_REG_NOB);
mxcmci_writew(host, blksz, MMC_REG_BLK_LEN);
host->datasize = datasize;
if (!mxcmci_use_dma(host))
@ -329,13 +372,13 @@ static void mxcmci_dma_callback(void *data)
del_timer(&host->watchdog);
stat = readl(host->base + MMC_REG_STATUS);
writel(stat & ~STATUS_DATA_TRANS_DONE, host->base + MMC_REG_STATUS);
stat = mxcmci_readl(host, MMC_REG_STATUS);
mxcmci_writel(host, stat & ~STATUS_DATA_TRANS_DONE, MMC_REG_STATUS);
dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
if (stat & STATUS_READ_OP_DONE)
writel(STATUS_READ_OP_DONE, host->base + MMC_REG_STATUS);
mxcmci_writel(host, STATUS_READ_OP_DONE, MMC_REG_STATUS);
mxcmci_data_done(host, stat);
}
@ -383,12 +426,12 @@ static int mxcmci_start_cmd(struct mxcmci_host *host, struct mmc_command *cmd,
spin_lock_irqsave(&host->lock, flags);
if (host->use_sdio)
int_cntr |= INT_SDIO_IRQ_EN;
writel(int_cntr, host->base + MMC_REG_INT_CNTR);
mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR);
spin_unlock_irqrestore(&host->lock, flags);
writew(cmd->opcode, host->base + MMC_REG_CMD);
writel(cmd->arg, host->base + MMC_REG_ARG);
writew(cmdat, host->base + MMC_REG_CMD_DAT_CONT);
mxcmci_writew(host, cmd->opcode, MMC_REG_CMD);
mxcmci_writel(host, cmd->arg, MMC_REG_ARG);
mxcmci_writew(host, cmdat, MMC_REG_CMD_DAT_CONT);
return 0;
}
@ -402,7 +445,7 @@ static void mxcmci_finish_request(struct mxcmci_host *host,
spin_lock_irqsave(&host->lock, flags);
if (host->use_sdio)
int_cntr |= INT_SDIO_IRQ_EN;
writel(int_cntr, host->base + MMC_REG_INT_CNTR);
mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR);
spin_unlock_irqrestore(&host->lock, flags);
host->req = NULL;
@ -477,14 +520,14 @@ static void mxcmci_read_response(struct mxcmci_host *host, unsigned int stat)
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
for (i = 0; i < 4; i++) {
a = readw(host->base + MMC_REG_RES_FIFO);
b = readw(host->base + MMC_REG_RES_FIFO);
a = mxcmci_readw(host, MMC_REG_RES_FIFO);
b = mxcmci_readw(host, MMC_REG_RES_FIFO);
cmd->resp[i] = a << 16 | b;
}
} else {
a = readw(host->base + MMC_REG_RES_FIFO);
b = readw(host->base + MMC_REG_RES_FIFO);
c = readw(host->base + MMC_REG_RES_FIFO);
a = mxcmci_readw(host, MMC_REG_RES_FIFO);
b = mxcmci_readw(host, MMC_REG_RES_FIFO);
c = mxcmci_readw(host, MMC_REG_RES_FIFO);
cmd->resp[0] = a << 24 | b << 8 | c >> 8;
}
}
@ -496,7 +539,7 @@ static int mxcmci_poll_status(struct mxcmci_host *host, u32 mask)
unsigned long timeout = jiffies + HZ;
do {
stat = readl(host->base + MMC_REG_STATUS);
stat = mxcmci_readl(host, MMC_REG_STATUS);
if (stat & STATUS_ERR_MASK)
return stat;
if (time_after(jiffies, timeout)) {
@ -520,7 +563,7 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
if (stat)
return stat;
*buf++ = readl(host->base + MMC_REG_BUFFER_ACCESS);
*buf++ = cpu_to_le32(mxcmci_readl(host, MMC_REG_BUFFER_ACCESS));
bytes -= 4;
}
@ -532,7 +575,7 @@ static int mxcmci_pull(struct mxcmci_host *host, void *_buf, int bytes)
STATUS_BUF_READ_RDY | STATUS_READ_OP_DONE);
if (stat)
return stat;
tmp = readl(host->base + MMC_REG_BUFFER_ACCESS);
tmp = cpu_to_le32(mxcmci_readl(host, MMC_REG_BUFFER_ACCESS));
memcpy(b, &tmp, bytes);
}
@ -548,7 +591,7 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
if (stat)
return stat;
writel(*buf++, host->base + MMC_REG_BUFFER_ACCESS);
mxcmci_writel(host, cpu_to_le32(*buf++), MMC_REG_BUFFER_ACCESS);
bytes -= 4;
}
@ -561,7 +604,7 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes)
return stat;
memcpy(&tmp, b, bytes);
writel(tmp, host->base + MMC_REG_BUFFER_ACCESS);
mxcmci_writel(host, cpu_to_le32(tmp), MMC_REG_BUFFER_ACCESS);
}
stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY);
@ -607,8 +650,8 @@ static void mxcmci_datawork(struct work_struct *work)
datawork);
int datastat = mxcmci_transfer_data(host);
writel(STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
host->base + MMC_REG_STATUS);
mxcmci_writel(host, STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
MMC_REG_STATUS);
mxcmci_finish_data(host, datastat);
if (host->req->stop) {
@ -686,9 +729,11 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
bool sdio_irq;
u32 stat;
stat = readl(host->base + MMC_REG_STATUS);
writel(stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
STATUS_WRITE_OP_DONE), host->base + MMC_REG_STATUS);
stat = mxcmci_readl(host, MMC_REG_STATUS);
mxcmci_writel(host,
stat & ~(STATUS_SDIO_INT_ACTIVE | STATUS_DATA_TRANS_DONE |
STATUS_WRITE_OP_DONE),
MMC_REG_STATUS);
dev_dbg(mmc_dev(host->mmc), "%s: 0x%08x\n", __func__, stat);
@ -698,11 +743,11 @@ static irqreturn_t mxcmci_irq(int irq, void *devid)
if (mxcmci_use_dma(host) &&
(stat & (STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE)))
writel(STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
host->base + MMC_REG_STATUS);
mxcmci_writel(host, STATUS_READ_OP_DONE | STATUS_WRITE_OP_DONE,
MMC_REG_STATUS);
if (sdio_irq) {
writel(STATUS_SDIO_INT_ACTIVE, host->base + MMC_REG_STATUS);
mxcmci_writel(host, STATUS_SDIO_INT_ACTIVE, MMC_REG_STATUS);
mmc_signal_sdio_irq(host->mmc);
}
@ -784,7 +829,7 @@ static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios)
prescaler <<= 1;
}
writew((prescaler << 4) | divider, host->base + MMC_REG_CLK_RATE);
mxcmci_writew(host, (prescaler << 4) | divider, MMC_REG_CLK_RATE);
dev_dbg(mmc_dev(host->mmc), "scaler: %d divider: %d in: %d out: %d\n",
prescaler, divider, clk_in, clk_ios);
@ -847,9 +892,9 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->clock) {
mxcmci_set_clk_rate(host, ios->clock);
writew(STR_STP_CLK_START_CLK, host->base + MMC_REG_STR_STP_CLK);
mxcmci_writew(host, STR_STP_CLK_START_CLK, MMC_REG_STR_STP_CLK);
} else {
writew(STR_STP_CLK_STOP_CLK, host->base + MMC_REG_STR_STP_CLK);
mxcmci_writew(host, STR_STP_CLK_STOP_CLK, MMC_REG_STR_STP_CLK);
}
host->clock = ios->clock;
@ -886,14 +931,14 @@ static void mxcmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_lock_irqsave(&host->lock, flags);
host->use_sdio = enable;
int_cntr = readl(host->base + MMC_REG_INT_CNTR);
int_cntr = mxcmci_readl(host, MMC_REG_INT_CNTR);
if (enable)
int_cntr |= INT_SDIO_IRQ_EN;
else
int_cntr &= ~INT_SDIO_IRQ_EN;
writel(int_cntr, host->base + MMC_REG_INT_CNTR);
mxcmci_writel(host, int_cntr, MMC_REG_INT_CNTR);
spin_unlock_irqrestore(&host->lock, flags);
}
@ -931,7 +976,7 @@ static void mxcmci_watchdog(unsigned long data)
struct mmc_host *mmc = (struct mmc_host *)data;
struct mxcmci_host *host = mmc_priv(mmc);
struct mmc_request *req = host->req;
unsigned int stat = readl(host->base + MMC_REG_STATUS);
unsigned int stat = mxcmci_readl(host, MMC_REG_STATUS);
if (host->dma_dir == DMA_FROM_DEVICE) {
dmaengine_terminate_all(host->dma);
@ -974,7 +1019,7 @@ static int mxcmci_probe(struct platform_device *pdev)
const struct of_device_id *of_id;
struct imxmmc_platform_data *pdata = pdev->dev.platform_data;
pr_info("i.MX SDHC driver\n");
pr_info("i.MX/MPC512x SDHC driver\n");
of_id = of_match_device(mxcmci_of_match, &pdev->dev);
@ -1060,7 +1105,7 @@ static int mxcmci_probe(struct platform_device *pdev)
mxcmci_softreset(host);
host->rev_no = readw(host->base + MMC_REG_REV_NO);
host->rev_no = mxcmci_readw(host, MMC_REG_REV_NO);
if (host->rev_no != 0x400) {
ret = -ENODEV;
dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n",
@ -1072,9 +1117,9 @@ static int mxcmci_probe(struct platform_device *pdev)
mmc->f_max = clk_get_rate(host->clk_per) >> 1;
/* recommended in data sheet */
writew(0x2db4, host->base + MMC_REG_READ_TO);
mxcmci_writew(host, 0x2db4, MMC_REG_READ_TO);
writel(host->default_irq_mask, host->base + MMC_REG_INT_CNTR);
mxcmci_writel(host, host->default_irq_mask, MMC_REG_INT_CNTR);
if (!host->pdata) {
host->dma = dma_request_slave_channel(&pdev->dev, "rx-tx");