* [PATCH 01/11] sdhci: Add support for bus-specific IO memory accessors
2009-03-05 20:27 [PATCH v2 0/11] FSL eSDHC support Anton Vorontsov
@ 2009-03-05 20:28 ` Anton Vorontsov
2009-03-05 20:28 ` [PATCH 02/11] sdhci: Split card-detection IRQs management from sdhci_init() Anton Vorontsov
` (9 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Anton Vorontsov @ 2009-03-05 20:28 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ben Dooks, Arnd Bergmann, Liu Dave, linux-kernel, linuxppc-dev,
sdhci-devel
Currently the SDHCI driver works with PCI accessors (write{l,b,w} and
read{l,b,w}).
With this patch drivers may change memory accessors, so that we can
support hosts with "weird" IO memory access requirments.
For example, in "FSL eSDHC" SDHCI hardware all registers are 32 bit
width, with big-endian addressing. That is, readb(0x2f) should turn
into readb(0x2c), and readw(0x2c) should be translated to
le16_to_cpu(readw(0x2e)).
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/mmc/host/sdhci.c | 214 ++++++++++++++++++++++++++++-----------------
drivers/mmc/host/sdhci.h | 14 +++
2 files changed, 147 insertions(+), 81 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index accb592..9887820 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
+#include <linux/compiler.h>
#include <linux/leds.h>
@@ -48,35 +49,35 @@ static void sdhci_dumpregs(struct sdhci_host *host)
printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n");
printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n",
- readl(host->ioaddr + SDHCI_DMA_ADDRESS),
- readw(host->ioaddr + SDHCI_HOST_VERSION));
+ sdhci_readl(host, SDHCI_DMA_ADDRESS),
+ sdhci_readw(host, SDHCI_HOST_VERSION));
printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n",
- readw(host->ioaddr + SDHCI_BLOCK_SIZE),
- readw(host->ioaddr + SDHCI_BLOCK_COUNT));
+ sdhci_readw(host, SDHCI_BLOCK_SIZE),
+ sdhci_readw(host, SDHCI_BLOCK_COUNT));
printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n",
- readl(host->ioaddr + SDHCI_ARGUMENT),
- readw(host->ioaddr + SDHCI_TRANSFER_MODE));
+ sdhci_readl(host, SDHCI_ARGUMENT),
+ sdhci_readw(host, SDHCI_TRANSFER_MODE));
printk(KERN_DEBUG DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n",
- readl(host->ioaddr + SDHCI_PRESENT_STATE),
- readb(host->ioaddr + SDHCI_HOST_CONTROL));
+ sdhci_readl(host, SDHCI_PRESENT_STATE),
+ sdhci_readb(host, SDHCI_HOST_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n",
- readb(host->ioaddr + SDHCI_POWER_CONTROL),
- readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL));
+ sdhci_readb(host, SDHCI_POWER_CONTROL),
+ sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
- readb(host->ioaddr + SDHCI_WAKE_UP_CONTROL),
- readw(host->ioaddr + SDHCI_CLOCK_CONTROL));
+ sdhci_readb(host, SDHCI_WAKE_UP_CONTROL),
+ sdhci_readw(host, SDHCI_CLOCK_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
- readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL),
- readl(host->ioaddr + SDHCI_INT_STATUS));
+ sdhci_readb(host, SDHCI_TIMEOUT_CONTROL),
+ sdhci_readl(host, SDHCI_INT_STATUS));
printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n",
- readl(host->ioaddr + SDHCI_INT_ENABLE),
- readl(host->ioaddr + SDHCI_SIGNAL_ENABLE));
+ sdhci_readl(host, SDHCI_INT_ENABLE),
+ sdhci_readl(host, SDHCI_SIGNAL_ENABLE));
printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n",
- readw(host->ioaddr + SDHCI_ACMD12_ERR),
- readw(host->ioaddr + SDHCI_SLOT_INT_STATUS));
+ sdhci_readw(host, SDHCI_ACMD12_ERR),
+ sdhci_readw(host, SDHCI_SLOT_INT_STATUS));
printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Max curr: 0x%08x\n",
- readl(host->ioaddr + SDHCI_CAPABILITIES),
- readl(host->ioaddr + SDHCI_MAX_CURRENT));
+ sdhci_readl(host, SDHCI_CAPABILITIES),
+ sdhci_readl(host, SDHCI_MAX_CURRENT));
printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n");
}
@@ -87,17 +88,71 @@ static void sdhci_dumpregs(struct sdhci_host *host)
* *
\*****************************************************************************/
+void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+ if (unlikely(host->ops->writel))
+ host->ops->writel(host, val, reg);
+ else
+ writel(val, host->ioaddr + reg);
+}
+EXPORT_SYMBOL_GPL(sdhci_writel);
+
+void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+ if (unlikely(host->ops->writew))
+ host->ops->writew(host, val, reg);
+ else
+ writew(val, host->ioaddr + reg);
+}
+EXPORT_SYMBOL_GPL(sdhci_writew);
+
+void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ if (unlikely(host->ops->writeb))
+ host->ops->writeb(host, val, reg);
+ else
+ writeb(val, host->ioaddr + reg);
+}
+EXPORT_SYMBOL_GPL(sdhci_writeb);
+
+u32 sdhci_readl(struct sdhci_host *host, int reg)
+{
+ if (unlikely(host->ops->readl))
+ return host->ops->readl(host, reg);
+ else
+ return readl(host->ioaddr + reg);
+}
+EXPORT_SYMBOL_GPL(sdhci_readl);
+
+u16 sdhci_readw(struct sdhci_host *host, int reg)
+{
+ if (unlikely(host->ops->readw))
+ return host->ops->readw(host, reg);
+ else
+ return readw(host->ioaddr + reg);
+}
+EXPORT_SYMBOL_GPL(sdhci_readw);
+
+u8 sdhci_readb(struct sdhci_host *host, int reg)
+{
+ if (unlikely(host->ops->readb))
+ return host->ops->readb(host, reg);
+ else
+ return readb(host->ioaddr + reg);
+}
+EXPORT_SYMBOL_GPL(sdhci_readb);
+
static void sdhci_reset(struct sdhci_host *host, u8 mask)
{
unsigned long timeout;
if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
- if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) &
+ if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
SDHCI_CARD_PRESENT))
return;
}
- writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET);
+ sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
if (mask & SDHCI_RESET_ALL)
host->clock = 0;
@@ -106,7 +161,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
timeout = 100;
/* hw clears the bit when it's done */
- while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) {
+ while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) {
if (timeout == 0) {
printk(KERN_ERR "%s: Reset 0x%x never completed.\n",
mmc_hostname(host->mmc), (int)mask);
@@ -132,26 +187,26 @@ static void sdhci_init(struct sdhci_host *host)
SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
SDHCI_INT_ADMA_ERROR;
- writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
- writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ sdhci_writel(host, intmask, SDHCI_INT_ENABLE);
+ sdhci_writel(host, intmask, SDHCI_SIGNAL_ENABLE);
}
static void sdhci_activate_led(struct sdhci_host *host)
{
u8 ctrl;
- ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
ctrl |= SDHCI_CTRL_LED;
- writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
static void sdhci_deactivate_led(struct sdhci_host *host)
{
u8 ctrl;
- ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
ctrl &= ~SDHCI_CTRL_LED;
- writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
#ifdef SDHCI_USE_LEDS_CLASS
@@ -205,7 +260,7 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
while (len) {
if (chunk == 0) {
- scratch = readl(host->ioaddr + SDHCI_BUFFER);
+ scratch = sdhci_readl(host, SDHCI_BUFFER);
chunk = 4;
}
@@ -257,7 +312,7 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
len--;
if ((chunk == 4) || ((len == 0) && (blksize == 0))) {
- writel(scratch, host->ioaddr + SDHCI_BUFFER);
+ sdhci_writel(host, scratch, SDHCI_BUFFER);
chunk = 0;
scratch = 0;
}
@@ -292,7 +347,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
(host->data->blocks == 1))
mask = ~0;
- while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+ while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (host->data->flags & MMC_DATA_READ)
sdhci_read_block_pio(host);
else
@@ -581,7 +636,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
host->data_early = 0;
count = sdhci_calc_timeout(host, data);
- writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
+ sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
if (host->flags & SDHCI_USE_DMA)
host->flags |= SDHCI_REQ_USE_DMA;
@@ -661,8 +716,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
WARN_ON(1);
host->flags &= ~SDHCI_REQ_USE_DMA;
} else {
- writel(host->adma_addr,
- host->ioaddr + SDHCI_ADMA_ADDRESS);
+ sdhci_writel(host, host->adma_addr,
+ SDHCI_ADMA_ADDRESS);
}
} else {
int sg_cnt;
@@ -681,8 +736,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
host->flags &= ~SDHCI_REQ_USE_DMA;
} else {
WARN_ON(sg_cnt != 1);
- writel(sg_dma_address(data->sg),
- host->ioaddr + SDHCI_DMA_ADDRESS);
+ sdhci_writel(host, sg_dma_address(data->sg),
+ SDHCI_DMA_ADDRESS);
}
}
}
@@ -693,14 +748,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
* is ADMA.
*/
if (host->version >= SDHCI_SPEC_200) {
- ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
ctrl &= ~SDHCI_CTRL_DMA_MASK;
if ((host->flags & SDHCI_REQ_USE_DMA) &&
(host->flags & SDHCI_USE_ADMA))
ctrl |= SDHCI_CTRL_ADMA32;
else
ctrl |= SDHCI_CTRL_SDMA;
- writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
if (!(host->flags & SDHCI_REQ_USE_DMA)) {
@@ -710,9 +765,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
}
/* We do not handle DMA boundaries, so set it to max (512 KiB) */
- writew(SDHCI_MAKE_BLKSZ(7, data->blksz),
- host->ioaddr + SDHCI_BLOCK_SIZE);
- writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT);
+ sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, data->blksz), SDHCI_BLOCK_SIZE);
+ sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
}
static void sdhci_set_transfer_mode(struct sdhci_host *host,
@@ -733,7 +787,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
if (host->flags & SDHCI_REQ_USE_DMA)
mode |= SDHCI_TRNS_DMA;
- writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE);
+ sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
}
static void sdhci_finish_data(struct sdhci_host *host)
@@ -802,7 +856,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
if (host->mrq->data && (cmd == host->mrq->data->stop))
mask &= ~SDHCI_DATA_INHIBIT;
- while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) {
+ while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (timeout == 0) {
printk(KERN_ERR "%s: Controller never released "
"inhibit bit(s).\n", mmc_hostname(host->mmc));
@@ -821,7 +875,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_prepare_data(host, cmd->data);
- writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT);
+ sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
sdhci_set_transfer_mode(host, cmd->data);
@@ -849,8 +903,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
if (cmd->data)
flags |= SDHCI_CMD_DATA;
- writew(SDHCI_MAKE_CMD(cmd->opcode, flags),
- host->ioaddr + SDHCI_COMMAND);
+ sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
}
static void sdhci_finish_command(struct sdhci_host *host)
@@ -863,15 +916,15 @@ static void sdhci_finish_command(struct sdhci_host *host)
if (host->cmd->flags & MMC_RSP_136) {
/* CRC is stripped so we need to do some shifting. */
for (i = 0;i < 4;i++) {
- host->cmd->resp[i] = readl(host->ioaddr +
+ host->cmd->resp[i] = sdhci_readl(host,
SDHCI_RESPONSE + (3-i)*4) << 8;
if (i != 3)
host->cmd->resp[i] |=
- readb(host->ioaddr +
+ sdhci_readb(host,
SDHCI_RESPONSE + (3-i)*4-1);
}
} else {
- host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE);
+ host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE);
}
}
@@ -895,7 +948,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
if (clock == host->clock)
return;
- writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
if (clock == 0)
goto out;
@@ -908,11 +961,11 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
clk = div << SDHCI_DIVIDER_SHIFT;
clk |= SDHCI_CLOCK_INT_EN;
- writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
/* Wait max 10 ms */
timeout = 10;
- while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL))
+ while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
& SDHCI_CLOCK_INT_STABLE)) {
if (timeout == 0) {
printk(KERN_ERR "%s: Internal clock never "
@@ -925,7 +978,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
}
clk |= SDHCI_CLOCK_CARD_EN;
- writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL);
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
out:
host->clock = clock;
@@ -939,7 +992,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
return;
if (power == (unsigned short)-1) {
- writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
goto out;
}
@@ -948,7 +1001,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
* a new value. Some controllers don't seem to like this though.
*/
if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE))
- writeb(0, host->ioaddr + SDHCI_POWER_CONTROL);
+ sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
pwr = SDHCI_POWER_ON;
@@ -973,10 +1026,9 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
* and set turn on power at the same time, so set the voltage first.
*/
if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER))
- writeb(pwr & ~SDHCI_POWER_ON,
- host->ioaddr + SDHCI_POWER_CONTROL);
+ sdhci_writeb(host, pwr & ~SDHCI_POWER_ON, SDHCI_POWER_CONTROL);
- writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL);
+ sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
out:
host->power = power;
@@ -1005,7 +1057,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq;
- if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
+ if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
|| (host->flags & SDHCI_DEVICE_DEAD)) {
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
@@ -1034,7 +1086,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* Should clear out any weird states.
*/
if (ios->power_mode == MMC_POWER_OFF) {
- writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
sdhci_init(host);
}
@@ -1045,7 +1097,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
sdhci_set_power(host, ios->vdd);
- ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
+ ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (ios->bus_width == MMC_BUS_WIDTH_4)
ctrl |= SDHCI_CTRL_4BITBUS;
@@ -1057,7 +1109,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
ctrl &= ~SDHCI_CTRL_HISPD;
- writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
/*
* Some (ENE) controllers go apeshit on some ios operation,
@@ -1085,7 +1137,7 @@ static int sdhci_get_ro(struct mmc_host *mmc)
if (host->flags & SDHCI_DEVICE_DEAD)
present = 0;
else
- present = readl(host->ioaddr + SDHCI_PRESENT_STATE);
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE);
spin_unlock_irqrestore(&host->lock, flags);
@@ -1105,14 +1157,14 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
if (host->flags & SDHCI_DEVICE_DEAD)
goto out;
- ier = readl(host->ioaddr + SDHCI_INT_ENABLE);
+ ier = sdhci_readl(host, SDHCI_INT_ENABLE);
ier &= ~SDHCI_INT_CARD_INT;
if (enable)
ier |= SDHCI_INT_CARD_INT;
- writel(ier, host->ioaddr + SDHCI_INT_ENABLE);
- writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+ sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
out:
mmiowb();
@@ -1142,7 +1194,7 @@ static void sdhci_tasklet_card(unsigned long param)
spin_lock_irqsave(&host->lock, flags);
- if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+ if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
if (host->mrq) {
printk(KERN_ERR "%s: Card removed during transfer!\n",
mmc_hostname(host->mmc));
@@ -1346,8 +1398,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
* we need to at least restart the transfer.
*/
if (intmask & SDHCI_INT_DMA_END)
- writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS),
- host->ioaddr + SDHCI_DMA_ADDRESS);
+ sdhci_writel(host, sdhci_readl(host, SDHCI_DMA_ADDRESS),
+ SDHCI_DMA_ADDRESS);
if (intmask & SDHCI_INT_DATA_END) {
if (host->cmd) {
@@ -1373,7 +1425,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
spin_lock(&host->lock);
- intmask = readl(host->ioaddr + SDHCI_INT_STATUS);
+ intmask = sdhci_readl(host, SDHCI_INT_STATUS);
if (!intmask || intmask == 0xffffffff) {
result = IRQ_NONE;
@@ -1384,22 +1436,22 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
mmc_hostname(host->mmc), intmask);
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
- writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
- host->ioaddr + SDHCI_INT_STATUS);
+ sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
+ SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
tasklet_schedule(&host->card_tasklet);
}
intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);
if (intmask & SDHCI_INT_CMD_MASK) {
- writel(intmask & SDHCI_INT_CMD_MASK,
- host->ioaddr + SDHCI_INT_STATUS);
+ sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK,
+ SDHCI_INT_STATUS);
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
}
if (intmask & SDHCI_INT_DATA_MASK) {
- writel(intmask & SDHCI_INT_DATA_MASK,
- host->ioaddr + SDHCI_INT_STATUS);
+ sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK,
+ SDHCI_INT_STATUS);
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
}
@@ -1410,7 +1462,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
if (intmask & SDHCI_INT_BUS_POWER) {
printk(KERN_ERR "%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc));
- writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);
+ sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS);
}
intmask &= ~SDHCI_INT_BUS_POWER;
@@ -1425,7 +1477,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
mmc_hostname(host->mmc), intmask);
sdhci_dumpregs(host);
- writel(intmask, host->ioaddr + SDHCI_INT_STATUS);
+ sdhci_writel(host, intmask, SDHCI_INT_STATUS);
}
result = IRQ_HANDLED;
@@ -1537,7 +1589,7 @@ int sdhci_add_host(struct sdhci_host *host)
sdhci_reset(host, SDHCI_RESET_ALL);
- host->version = readw(host->ioaddr + SDHCI_HOST_VERSION);
+ host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
host->version = (host->version & SDHCI_SPEC_VER_MASK)
>> SDHCI_SPEC_VER_SHIFT;
if (host->version > SDHCI_SPEC_200) {
@@ -1546,7 +1598,7 @@ int sdhci_add_host(struct sdhci_host *host)
host->version);
}
- caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
+ caps = sdhci_readl(host, SDHCI_CAPABILITIES);
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_DMA;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 43c37c6..8c88482 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -9,6 +9,7 @@
* your option) any later version.
*/
+#include <linux/types.h>
#include <linux/scatterlist.h>
/*
@@ -267,9 +268,22 @@ struct sdhci_host {
struct sdhci_ops {
+ u32 (*readl)(struct sdhci_host *host, int reg);
+ u16 (*readw)(struct sdhci_host *host, int reg);
+ u8 (*readb)(struct sdhci_host *host, int reg);
+ void (*writel)(struct sdhci_host *host, u32 val, int reg);
+ void (*writew)(struct sdhci_host *host, u16 val, int reg);
+ void (*writeb)(struct sdhci_host *host, u8 val, int reg);
+
int (*enable_dma)(struct sdhci_host *host);
};
+extern void sdhci_writel(struct sdhci_host *host, u32 val, int reg);
+extern void sdhci_writew(struct sdhci_host *host, u16 val, int reg);
+extern void sdhci_writeb(struct sdhci_host *host, u8 val, int reg);
+extern u32 sdhci_readl(struct sdhci_host *host, int reg);
+extern u16 sdhci_readw(struct sdhci_host *host, int reg);
+extern u8 sdhci_readb(struct sdhci_host *host, int reg);
extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
size_t priv_size);
--
1.5.6.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 02/11] sdhci: Split card-detection IRQs management from sdhci_init()
2009-03-05 20:27 [PATCH v2 0/11] FSL eSDHC support Anton Vorontsov
2009-03-05 20:28 ` [PATCH 01/11] sdhci: Add support for bus-specific IO memory accessors Anton Vorontsov
@ 2009-03-05 20:28 ` Anton Vorontsov
2009-03-05 20:28 ` [PATCH 03/11] sdhci: Enable only relevant (DMA/PIO) interrupts during transfers Anton Vorontsov
` (8 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Anton Vorontsov @ 2009-03-05 20:28 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ben Dooks, Arnd Bergmann, Liu Dave, linux-kernel, linuxppc-dev,
sdhci-devel
Card detection interrupts should be handled separately as they should
not be enabled before mmc_add_host() returns and should be disabled
before calling mmc_remove_host(). The same is for suspend and resume
routines.
sdhci_init() no longer enables card-detection irqs. Instead, two new
functions implemented: sdhci_enable_card_detection() and
sdhci_disable_card_detection().
New sdhci_reinit() call implemented to behave the same way as the old
sdhci_init().
Also, this patch implements and uses few new helpers to manage IRQs in
a more conveinient way, that is:
- sdhci_clear_set_irqs()
- sdhci_unmask_irqs()
- sdhci_mask_irqs()
- SDHCI_INT_ALL_MASK constant
sdhci_enable_sdio_irq() converted to these new helpers, plus the
helpers will be used by the subsequent patches.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
drivers/mmc/host/sdhci.c | 78 ++++++++++++++++++++++++++++++++++++----------
drivers/mmc/host/sdhci.h | 1 +
2 files changed, 62 insertions(+), 17 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9887820..ba9f95f 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -142,6 +142,47 @@ u8 sdhci_readb(struct sdhci_host *host, int reg)
}
EXPORT_SYMBOL_GPL(sdhci_readb);
+static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
+{
+ u32 ier;
+
+ ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+ ier &= ~clear;
+ ier |= set;
+ sdhci_writel(host, ier, SDHCI_INT_ENABLE);
+ sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
+}
+
+static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs)
+{
+ sdhci_clear_set_irqs(host, 0, irqs);
+}
+
+static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs)
+{
+ sdhci_clear_set_irqs(host, irqs, 0);
+}
+
+static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
+{
+ u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;
+
+ if (enable)
+ sdhci_unmask_irqs(host, irqs);
+ else
+ sdhci_mask_irqs(host, irqs);
+}
+
+static void sdhci_enable_card_detection(struct sdhci_host *host)
+{
+ sdhci_set_card_detection(host, true);
+}
+
+static void sdhci_disable_card_detection(struct sdhci_host *host)
+{
+ sdhci_set_card_detection(host, false);
+}
+
static void sdhci_reset(struct sdhci_host *host, u8 mask)
{
unsigned long timeout;
@@ -175,20 +216,21 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
static void sdhci_init(struct sdhci_host *host)
{
- u32 intmask;
-
sdhci_reset(host, SDHCI_RESET_ALL);
- intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
+ sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
+ SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
- SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT |
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
- SDHCI_INT_ADMA_ERROR;
+ SDHCI_INT_ADMA_ERROR);
+}
- sdhci_writel(host, intmask, SDHCI_INT_ENABLE);
- sdhci_writel(host, intmask, SDHCI_SIGNAL_ENABLE);
+static void sdhci_reinit(struct sdhci_host *host)
+{
+ sdhci_init(host);
+ sdhci_enable_card_detection(host);
}
static void sdhci_activate_led(struct sdhci_host *host)
@@ -1087,7 +1129,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
*/
if (ios->power_mode == MMC_POWER_OFF) {
sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
- sdhci_init(host);
+ sdhci_reinit(host);
}
sdhci_set_clock(host, ios->clock);
@@ -1148,7 +1190,6 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct sdhci_host *host;
unsigned long flags;
- u32 ier;
host = mmc_priv(mmc);
@@ -1157,15 +1198,10 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
if (host->flags & SDHCI_DEVICE_DEAD)
goto out;
- ier = sdhci_readl(host, SDHCI_INT_ENABLE);
-
- ier &= ~SDHCI_INT_CARD_INT;
if (enable)
- ier |= SDHCI_INT_CARD_INT;
-
- sdhci_writel(host, ier, SDHCI_INT_ENABLE);
- sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
-
+ sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
+ else
+ sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
out:
mmiowb();
@@ -1507,6 +1543,8 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state)
{
int ret;
+ sdhci_disable_card_detection(host);
+
ret = mmc_suspend_host(host->mmc, state);
if (ret)
return ret;
@@ -1539,6 +1577,8 @@ int sdhci_resume_host(struct sdhci_host *host)
if (ret)
return ret;
+ sdhci_enable_card_detection(host);
+
return 0;
}
@@ -1798,6 +1838,8 @@ int sdhci_add_host(struct sdhci_host *host)
(host->flags & SDHCI_USE_ADMA)?"A":"",
(host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
+ sdhci_enable_card_detection(host);
+
return 0;
#ifdef SDHCI_USE_LEDS_CLASS
@@ -1834,6 +1876,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
spin_unlock_irqrestore(&host->lock, flags);
}
+ sdhci_disable_card_detection(host);
+
mmc_remove_host(host->mmc);
#ifdef SDHCI_USE_LEDS_CLASS
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 8c88482..1c29895 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -124,6 +124,7 @@
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
SDHCI_INT_DATA_END_BIT)
+#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
#define SDHCI_ACMD12_ERR 0x3C
--
1.5.6.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver
2009-03-05 20:27 [PATCH v2 0/11] FSL eSDHC support Anton Vorontsov
` (9 preceding siblings ...)
2009-03-05 20:28 ` [PATCH 10/11] sdhci: Add quirk for forcing maximum block size to 2048 bytes Anton Vorontsov
@ 2009-03-05 20:28 ` Anton Vorontsov
2009-03-08 14:44 ` Pierre Ossman
10 siblings, 1 reply; 14+ messages in thread
From: Anton Vorontsov @ 2009-03-05 20:28 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ben Dooks, Arnd Bergmann, Liu Dave, linux-kernel, linuxppc-dev,
sdhci-devel
This patch adds a new driver: sdhci-of. The driver is similar to
the sdhci-pci, it contains common probe code, and controller-specific
ops and quirks.
So far there are only Freescale eSDHC ops and quirks.
Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
drivers/mmc/host/Kconfig | 10 ++
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-of.c | 309 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 320 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/host/sdhci-of.c
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 99d4b28..73b79e1 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -65,6 +65,16 @@ config MMC_RICOH_MMC
If unsure, say Y.
+config MMC_SDHCI_OF
+ tristate "SDHCI support on OpenFirmware platforms"
+ depends on MMC_SDHCI && PPC_OF
+ help
+ This selects the OF support for Secure Digital Host Controller
+ Interfaces. So far, only the Freescale eSDHC controller is known
+ to exist on OF platforms.
+
+ If unsure, say N.
+
config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index dedec55..dd512d9 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o
+obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c
new file mode 100644
index 0000000..3ff4ac3
--- /dev/null
+++ b/drivers/mmc/host/sdhci-of.c
@@ -0,0 +1,309 @@
+/*
+ * OpenFirmware bindings for Secure Digital Host Controller Interface.
+ *
+ * Copyright (c) 2007 Freescale Semiconductor, Inc.
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ *
+ * Authors: Xiaobo Xie <X.Xie@freescale.com>
+ * Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mmc/host.h>
+#include "sdhci.h"
+
+struct sdhci_of_data {
+ unsigned int quirks;
+ struct sdhci_ops ops;
+};
+
+struct sdhci_of_host {
+ unsigned int clock;
+ u16 xfer_mode_shadow;
+};
+
+/*
+ * Ops and quirks for the Freescale eSDHC controller.
+ */
+
+#define ESDHC_DMA_SYSCTL 0x40c
+#define ESDHC_DMA_SNOOP 0x00000040
+
+#define ESDHC_SYSTEM_CONTROL 0x2c
+#define ESDHC_CLOCK_MASK 0x0000fff0
+#define ESDHC_PREDIV_SHIFT 8
+#define ESDHC_DIVIDER_SHIFT 4
+#define ESDHC_CLOCK_PEREN 0x00000004
+#define ESDHC_CLOCK_HCKEN 0x00000002
+#define ESDHC_CLOCK_IPGEN 0x00000001
+
+static u32 esdhc_readl(struct sdhci_host *host, int reg)
+{
+ return in_be32(host->ioaddr + reg);
+}
+
+static u16 esdhc_readw(struct sdhci_host *host, int reg)
+{
+ return in_be16(host->ioaddr + (reg ^ 0x2));
+}
+
+static u8 esdhc_readb(struct sdhci_host *host, int reg)
+{
+ return in_8(host->ioaddr + (reg ^ 0x3));
+}
+
+static void esdhc_writel(struct sdhci_host *host, u32 val, int reg)
+{
+ out_be32(host->ioaddr + reg, val);
+}
+
+static void esdhc_writew(struct sdhci_host *host, u16 val, int reg)
+{
+ struct sdhci_of_host *of_host = sdhci_priv(host);
+ int base = reg & ~0x3;
+ int shift = (reg & 0x2) * 8;
+
+ switch (reg) {
+ case SDHCI_TRANSFER_MODE:
+ /*
+ * Postpone this write, we must do it together with a
+ * command write that is down below.
+ */
+ of_host->xfer_mode_shadow = val;
+ return;
+ case SDHCI_COMMAND:
+ esdhc_writel(host, val << 16 | of_host->xfer_mode_shadow,
+ SDHCI_TRANSFER_MODE);
+ return;
+ case SDHCI_BLOCK_SIZE:
+ /*
+ * Two last DMA bits are reserved, and first one is used for
+ * non-standard blksz of 4096 bytes that we don't support
+ * yet. So clear the DMA boundary bits.
+ */
+ val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
+ /* fall through */
+ }
+ clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
+}
+
+static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ int base = reg & ~0x3;
+ int shift = (reg & 0x3) * 8;
+
+ clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
+}
+
+static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ int div;
+ int pre_div = 2;
+
+ clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
+ ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
+
+ if (clock == 0)
+ goto out;
+
+ if (host->max_clk / 16 > clock) {
+ for (; pre_div < 256; pre_div *= 2) {
+ if (host->max_clk / pre_div < clock * 16)
+ break;
+ }
+ }
+
+ for (div = 1; div <= 16; div++) {
+ if (host->max_clk / (div * pre_div) <= clock)
+ break;
+ }
+
+ pre_div >>= 1;
+
+ setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN |
+ ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN |
+ div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT);
+ mdelay(100);
+out:
+ host->clock = clock;
+}
+
+static int esdhc_enable_dma(struct sdhci_host *host)
+{
+ setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP);
+ return 0;
+}
+
+static unsigned int esdhc_get_max_clock(struct sdhci_host *host)
+{
+ struct sdhci_of_host *of_host = sdhci_priv(host);
+
+ return of_host->clock;
+}
+
+static unsigned int esdhc_get_timeout_clock(struct sdhci_host *host)
+{
+ struct sdhci_of_host *of_host = sdhci_priv(host);
+
+ return of_host->clock / 1000;
+}
+
+static struct sdhci_of_data sdhci_esdhc = {
+ .quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
+ SDHCI_QUIRK_BROKEN_CARD_DETECTION |
+ SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
+ SDHCI_QUIRK_NO_BUSY_IRQ |
+ SDHCI_QUIRK_NONSTANDARD_CLOCK |
+ SDHCI_QUIRK_PIO_NEEDS_DELAY |
+ SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET |
+ SDHCI_QUIRK_NO_CARD_NO_RESET,
+ .ops = {
+ .readl = esdhc_readl,
+ .readw = esdhc_readw,
+ .readb = esdhc_readb,
+ .writel = esdhc_writel,
+ .writew = esdhc_writew,
+ .writeb = esdhc_writeb,
+ .set_clock = esdhc_set_clock,
+ .enable_dma = esdhc_enable_dma,
+ .get_max_clock = esdhc_get_max_clock,
+ .get_timeout_clock = esdhc_get_timeout_clock,
+ },
+};
+
+#ifdef CONFIG_PM
+
+static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state)
+{
+ struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+ return mmc_suspend_host(host->mmc, state);
+}
+
+static int sdhci_of_resume(struct of_device *ofdev)
+{
+ struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+ return mmc_resume_host(host->mmc);
+}
+
+#else
+
+#define sdhci_of_suspend NULL
+#define sdhci_of_resume NULL
+
+#endif
+
+static int __devinit sdhci_of_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ struct device_node *np = ofdev->node;
+ struct sdhci_of_data *sdhci_of_data = match->data;
+ struct sdhci_host *host;
+ struct sdhci_of_host *of_host;
+ const u32 *clk;
+ int size;
+ int ret;
+
+ if (!of_device_is_available(np))
+ return -ENODEV;
+
+ host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host));
+ if (!host)
+ return -ENOMEM;
+
+ of_host = sdhci_priv(host);
+ dev_set_drvdata(&ofdev->dev, host);
+
+ host->ioaddr = of_iomap(np, 0);
+ if (!host->ioaddr) {
+ ret = -ENOMEM;
+ goto err_addr_map;
+ }
+
+ host->irq = irq_of_parse_and_map(np, 0);
+ if (!host->irq) {
+ ret = -EINVAL;
+ goto err_no_irq;
+ }
+
+ host->hw_name = dev_name(&ofdev->dev);
+ if (sdhci_of_data) {
+ host->quirks = sdhci_of_data->quirks;
+ host->ops = &sdhci_of_data->ops;
+ }
+
+ clk = of_get_property(np, "clock-frequency", &size);
+ if (clk && size == sizeof(*clk) && *clk)
+ of_host->clock = *clk;
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto err_add_host;
+
+ return 0;
+
+err_add_host:
+ irq_dispose_mapping(host->irq);
+err_no_irq:
+ iounmap(host->ioaddr);
+err_addr_map:
+ sdhci_free_host(host);
+ return ret;
+}
+
+static int __devexit sdhci_of_remove(struct of_device *ofdev)
+{
+ struct sdhci_host *host = dev_get_drvdata(&ofdev->dev);
+
+ sdhci_remove_host(host, 0);
+ sdhci_free_host(host);
+ irq_dispose_mapping(host->irq);
+ iounmap(host->ioaddr);
+ return 0;
+}
+
+static const struct of_device_id sdhci_of_match[] = {
+ { .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, },
+ { .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, },
+ { .compatible = "generic-sdhci", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sdhci_of_match);
+
+static struct of_platform_driver sdhci_of_driver = {
+ .driver.name = "sdhci-of",
+ .match_table = sdhci_of_match,
+ .probe = sdhci_of_probe,
+ .remove = __devexit_p(sdhci_of_remove),
+ .suspend = sdhci_of_suspend,
+ .resume = sdhci_of_resume,
+};
+
+static int __init sdhci_of_init(void)
+{
+ return of_register_platform_driver(&sdhci_of_driver);
+}
+module_init(sdhci_of_init);
+
+static void __exit sdhci_of_exit(void)
+{
+ of_unregister_platform_driver(&sdhci_of_driver);
+}
+module_exit(sdhci_of_exit);
+
+MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver");
+MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
+ "Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_LICENSE("GPL");
--
1.5.6.5
^ permalink raw reply related [flat|nested] 14+ messages in thread