mirror of
https://github.com/torvalds/linux.git
synced 2024-11-12 07:01:57 +00:00
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:
parent
70aa610959
commit
c7ceab0254
@ -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.
|
||||
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user