2009-03-16 21:13:24

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH v3 0/11] FSL eSDHC support

Hi all,

Here comes another version, let's hope that one is final. ;-)

Changes since v2:
- Introduced MMC_SDHCI_IO_ACCESSORS Kconfig symbol. If not selected,
SDHCI will just use PCI memory accessors unconditionally.
- Added MAINTAINERS entry.

Changes since v1:
- "Add support for bus-specific IO memory accessors" patch no longer
touches sdhci-pci. The changes were no longer needed since I dropped
"Add type checking ..." patch back in RFCv2;
- Patch "Add support for hosts with strict 32 bit addressing" dropped.
Now we handle the 32 bit magic in eSDHC's writew() accessor;
- Patch "Add quirk for controllers with max. block size up to 4096 bytes"
replaced by "Add quirk for forcing maximum block size to 2048 bytes";
- SDHCI_INT_ALL_MASK changed to (unsigned int)-1;
- Addressed Pierre's comments in "Add support for card-detection polling"
patch.

Changes since the third RFC:
- Use uninitialized_var() (suggested by Laurent Pinchart);
- Fixed a bug in eSDHC SDCLK prescaler calculations because of which
we were over-clocking the SDCLK, and that caused CRC errors using some
SD cards;
- Not a change, but some status: SDHS (50 MHz) cards were tested to
NOT work (at least on MPC837x boards) -- this is to be investigated
further. SDHC (> 4 GB) cards were not tested, yet.

Changes since the second RFC:
- Addressed all comments that were raised by Pierre Ossman.
There were too many to mention them all, so here is the link:
http://lkml.org/lkml/2009/2/6/320

Changes since the first RFC:
- Use of_iomap() in sdhci-of.c (suggested by Arnd Bergmann). Also added
Arnd's Acked-by: line for the sdhci-of patch.
- Kconfig help text improved (thanks to Matt Sealey and M. Warner Losh).
- In "sdhci: Add quirk to suppress PIO interrupts during DMA transfers"
patch: sdhci_init() now clears SDHCI_PIO_DISABLED flag, otherwise we
won't disable PIO interrupts after suspend.
- New patch: "sdhci: Add type checking for IO memory accessors"

--
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2


2009-03-16 21:14:02

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 01/11] sdhci: Add support for bus-specific IO memory accessors

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 <[email protected]>
---
drivers/mmc/host/Kconfig | 7 ++
drivers/mmc/host/sdhci.c | 159 ++++++++++++++++++++++-----------------------
drivers/mmc/host/sdhci.h | 95 +++++++++++++++++++++++++++
3 files changed, 180 insertions(+), 81 deletions(-)

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 99d4b28..e032a42 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -37,6 +37,13 @@ config MMC_SDHCI

If unsure, say N.

+config MMC_SDHCI_IO_ACCESSORS
+ bool
+ depends on MMC_SDHCI
+ help
+ This is silent Kconfig symbol that is selected by the drivers that
+ need to overwrite SDHCI IO memory accessors.
+
config MMC_SDHCI_PCI
tristate "SDHCI support on PCI bus"
depends on MMC_SDHCI && PCI
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index accb592..fd36b82 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -48,35 +48,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");
}
@@ -92,12 +92,12 @@ 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 +106,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 +132,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 +205,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 +257,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 +292,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 +581,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 +661,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 +681,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 +693,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 +710,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 +732,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 +801,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 +820,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 +848,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 +861,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 +893,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 +906,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 +923,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 +937,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 +946,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 +971,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 +1002,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 +1031,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 +1042,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 +1054,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 +1082,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 +1102,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 +1139,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 +1343,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 +1370,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 +1381,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 +1407,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 +1422,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 +1534,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 +1543,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..d9733f8 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -10,6 +10,9 @@
*/

#include <linux/scatterlist.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/io.h>

/*
* Controller registers
@@ -267,9 +270,101 @@ struct sdhci_host {


struct sdhci_ops {
+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+ 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);
+#endif
+
int (*enable_dma)(struct sdhci_host *host);
};

+#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
+
+static inline 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);
+}
+
+static inline 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);
+}
+
+static inline 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);
+}
+
+static inline 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);
+}
+
+static inline 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);
+}
+
+static inline 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);
+}
+
+#else
+
+static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
+{
+ writel(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
+{
+ writew(val, host->ioaddr + reg);
+}
+
+static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
+{
+ writeb(val, host->ioaddr + reg);
+}
+
+static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
+{
+ return readl(host->ioaddr + reg);
+}
+
+static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
+{
+ return readw(host->ioaddr + reg);
+}
+
+static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
+{
+ return readb(host->ioaddr + reg);
+}
+
+#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */

extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
size_t priv_size);
--
1.5.6.5

2009-03-16 21:14:24

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 02/11] sdhci: Split card-detection IRQs management from sdhci_init()

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 <[email protected]>
---
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 fd36b82..bd5acfb 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -87,6 +87,47 @@ static void sdhci_dumpregs(struct sdhci_host *host)
* *
\*****************************************************************************/

+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;
@@ -120,20 +161,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)
@@ -1032,7 +1074,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);
@@ -1093,7 +1135,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);

@@ -1102,15 +1143,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();

@@ -1452,6 +1488,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;
@@ -1484,6 +1522,8 @@ int sdhci_resume_host(struct sdhci_host *host)
if (ret)
return ret;

+ sdhci_enable_card_detection(host);
+
return 0;
}

@@ -1743,6 +1783,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
@@ -1779,6 +1821,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 d9733f8..a9e25c6 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -126,6 +126,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

2009-03-16 21:14:40

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 03/11] sdhci: Enable only relevant (DMA/PIO) interrupts during transfers

Some hosts (that is, FSL eSDHC) throw PIO interrupts during DMA
transfers, this causes tons of unneeded interrupts, and thus highly
degraded speed.

This patch modifies the driver so that now we only enable relevant
(DMA or PIO) interrupts during transfers.

Signed-off-by: Anton Vorontsov <[email protected]>
---
drivers/mmc/host/sdhci.c | 17 ++++++++++++++---
1 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index bd5acfb..6fbbc00 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -167,9 +167,7 @@ static void sdhci_init(struct sdhci_host *host)
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_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL |
- SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE |
- SDHCI_INT_ADMA_ERROR);
+ SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE);
}

static void sdhci_reinit(struct sdhci_host *host)
@@ -603,6 +601,17 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data)
return count;
}

+static void sdhci_set_transfer_irqs(struct sdhci_host *host)
+{
+ u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL;
+ u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR;
+
+ if (host->flags & SDHCI_REQ_USE_DMA)
+ sdhci_clear_set_irqs(host, pio_irqs, dma_irqs);
+ else
+ sdhci_clear_set_irqs(host, dma_irqs, pio_irqs);
+}
+
static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
{
u8 count;
@@ -751,6 +760,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
host->blocks = data->blocks;
}

+ sdhci_set_transfer_irqs(host);
+
/* We do not handle DMA boundaries, so set it to max (512 KiB) */
sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, data->blksz), SDHCI_BLOCK_SIZE);
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
--
1.5.6.5

2009-03-16 21:14:53

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 04/11] sdhci: Add support for card-detection polling

This patch adds SDHCI_QUIRK_BROKEN_CARD_DETECTION quirk. When specified,
sdhci driver will set MMC_CAP_NEEDS_POLL MMC host capability, and won't
enable card insert/remove interrupts.

This is needed for hosts with unreliable card detection, such as FSL
eSDHC. The original eSDHC driver was tring to "debounce" card-detection
IRQs by reading present state and disabling particular interrupts. But
with this debouncing scheme I noticed that sometimes we miss card
insertion/removal events.

Signed-off-by: Anton Vorontsov <[email protected]>
---
drivers/mmc/host/sdhci.c | 17 +++++++++++++++--
drivers/mmc/host/sdhci.h | 2 ++
2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 6fbbc00..fc7cb48 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -112,6 +112,9 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
{
u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT;

+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ return;
+
if (enable)
sdhci_unmask_irqs(host, irqs);
else
@@ -1041,6 +1044,7 @@ out:
static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdhci_host *host;
+ bool present;
unsigned long flags;

host = mmc_priv(mmc);
@@ -1055,8 +1059,14 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)

host->mrq = mrq;

- if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
- || (host->flags & SDHCI_DEVICE_DEAD)) {
+ /* If polling, assume that the card is always present. */
+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ present = true;
+ else
+ present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
+ SDHCI_CARD_PRESENT;
+
+ if (!present || host->flags & SDHCI_DEVICE_DEAD) {
host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet);
} else
@@ -1690,6 +1700,9 @@ int sdhci_add_host(struct sdhci_host *host)
if (caps & SDHCI_CAN_DO_HISPD)
mmc->caps |= MMC_CAP_SD_HIGHSPEED;

+ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
mmc->ocr_avail = 0;
if (caps & SDHCI_CAN_VDD_330)
mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index a9e25c6..968d713 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -214,6 +214,8 @@ struct sdhci_host {
#define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13)
/* Controller does not provide transfer-complete interrupt when not busy */
#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14)
+/* Controller has unreliable card detection */
+#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)

int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
--
1.5.6.5

2009-03-16 21:15:17

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 05/11] sdhci: Add support for hosts reporting inverted write-protect state

This patch adds SDHCI_QUIRK_INVERTED_WRITE_PROTECT quirk. When
specified, the sdhci driver will invert WP state.

p.s. Actually, the quirk is more board-specific than
controller-specific.

Signed-off-by: Anton Vorontsov <[email protected]>
---
drivers/mmc/host/sdhci.c | 2 ++
drivers/mmc/host/sdhci.h | 2 ++
2 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index fc7cb48..c814220 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1149,6 +1149,8 @@ static int sdhci_get_ro(struct mmc_host *mmc)

spin_unlock_irqrestore(&host->lock, flags);

+ if (host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT)
+ return !!(present & SDHCI_WRITE_PROTECT);
return !(present & SDHCI_WRITE_PROTECT);
}

diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 968d713..6980f27 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -216,6 +216,8 @@ struct sdhci_host {
#define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14)
/* Controller has unreliable card detection */
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
+/* Controller reports inverted write-protect state */
+#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)

int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
--
1.5.6.5

2009-03-16 21:16:18

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 08/11] sdhci: Add quirk for controllers that need small delays for PIO

Small udelay is needed to make eSDHC work in PIO mode. Without
the delay reading causes endless interrupt storm, and writing
corrupts data. The first guess would be that we must wait for
some bit in some register, but I didn't find any reliable bits
that change before and after the delay.

Signed-off-by: Anton Vorontsov <[email protected]>
---
drivers/mmc/host/sdhci.c | 3 +++
drivers/mmc/host/sdhci.h | 2 ++
2 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 75d0ecb..cd6dab3 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -336,6 +336,9 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
mask = ~0;

while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
+ if (host->quirks & SDHCI_QUIRK_PIO_NEEDS_DELAY)
+ udelay(100);
+
if (host->data->flags & MMC_DATA_READ)
sdhci_read_block_pio(host);
else
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index b9bc622..c5ce9ee 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -220,6 +220,8 @@ struct sdhci_host {
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
/* Controller has nonstandard clock management */
#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17)
+/* Controller does not like fast PIO transfers */
+#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)

int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
--
1.5.6.5

2009-03-16 21:15:55

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 07/11] sdhci: Add set_clock callback and a quirk for nonstandard clocks

FSL eSDHC hosts have incompatible register map to manage the SDCLK.
This patch adds set_clock callback so that drivers could overwrite
set_clock behaviour.

Similar patch[1] was posted by Ben Dooks, though in Ben's version the
callback is named change_clock, plus the patch has some unrelated bits
that makes the patch difficult to reuse.

[1] http://lkml.org/lkml/2008/12/2/160

Signed-off-by: Anton Vorontsov <[email protected]>
---
drivers/mmc/host/sdhci.c | 6 ++++++
drivers/mmc/host/sdhci.h | 4 ++++
2 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 34ab77b..75d0ecb 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -949,6 +949,12 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
if (clock == host->clock)
return;

+ if (host->ops->set_clock) {
+ host->ops->set_clock(host, clock);
+ if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK)
+ return;
+ }
+
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);

if (clock == 0)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index aab0652..b9bc622 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -218,6 +218,8 @@ struct sdhci_host {
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
/* Controller reports inverted write-protect state */
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
+/* Controller has nonstandard clock management */
+#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17)

int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
@@ -284,6 +286,8 @@ struct sdhci_ops {
void (*writeb)(struct sdhci_host *host, u8 val, int reg);
#endif

+ void (*set_clock)(struct sdhci_host *host, unsigned int clock);
+
int (*enable_dma)(struct sdhci_host *host);
unsigned int (*get_max_clock)(struct sdhci_host *host);
unsigned int (*get_timeout_clock)(struct sdhci_host *host);
--
1.5.6.5

2009-03-16 21:15:40

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 06/11] sdhci: Add get_{max,timeout}_clock callbacks

From: Ben Dooks <[email protected]>

Some controllers do not provide clock information in their capabilities
(in the Samsung case, it is because there are multiple clock sources
available to the controller). Add hooks to allow the system to supply
clock information.

p.s.
In the original Ben's patch there was a bug that makes sdhci_add_host()
return -ENODEV even if callbacks were specified. This is fixed now.

Signed-off-by: Ben Dooks <[email protected]>
Signed-off-by: Anton Vorontsov <[email protected]>
---
drivers/mmc/host/sdhci.c | 22 +++++++++++++++-------
drivers/mmc/host/sdhci.h | 2 ++
2 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c814220..34ab77b 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1674,19 +1674,27 @@ int sdhci_add_host(struct sdhci_host *host)

host->max_clk =
(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
+ host->max_clk *= 1000000;
if (host->max_clk == 0) {
- printk(KERN_ERR "%s: Hardware doesn't specify base clock "
- "frequency.\n", mmc_hostname(mmc));
- return -ENODEV;
+ if (!host->ops->get_max_clock) {
+ printk(KERN_ERR
+ "%s: Hardware doesn't specify base clock "
+ "frequency.\n", mmc_hostname(mmc));
+ return -ENODEV;
+ }
+ host->max_clk = host->ops->get_max_clock(host);
}
- host->max_clk *= 1000000;

host->timeout_clk =
(caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
if (host->timeout_clk == 0) {
- printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
- "frequency.\n", mmc_hostname(mmc));
- return -ENODEV;
+ if (!host->ops->get_timeout_clock) {
+ printk(KERN_ERR
+ "%s: Hardware doesn't specify timeout clock "
+ "frequency.\n", mmc_hostname(mmc));
+ return -ENODEV;
+ }
+ host->timeout_clk = host->ops->get_timeout_clock(host);
}
if (caps & SDHCI_TIMEOUT_CLK_UNIT)
host->timeout_clk *= 1000;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 6980f27..aab0652 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -285,6 +285,8 @@ struct sdhci_ops {
#endif

int (*enable_dma)(struct sdhci_host *host);
+ unsigned int (*get_max_clock)(struct sdhci_host *host);
+ unsigned int (*get_timeout_clock)(struct sdhci_host *host);
};

#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
--
1.5.6.5

2009-03-16 21:16:34

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 09/11] sdhci: Add quirk for controllers that need IRQ re-init after reset

FSL eSDHC controllers losing signal/interrupt enable states after
reset, so we should re-enable them.

Signed-off-by: Anton Vorontsov <[email protected]>
---
drivers/mmc/host/sdhci.c | 7 +++++++
drivers/mmc/host/sdhci.h | 2 ++
2 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index cd6dab3..3a72fe2 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -134,6 +134,7 @@ static void sdhci_disable_card_detection(struct sdhci_host *host)
static void sdhci_reset(struct sdhci_host *host, u8 mask)
{
unsigned long timeout;
+ u32 uninitialized_var(ier);

if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
@@ -141,6 +142,9 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
return;
}

+ if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
+ ier = sdhci_readl(host, SDHCI_INT_ENABLE);
+
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);

if (mask & SDHCI_RESET_ALL)
@@ -160,6 +164,9 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
timeout--;
mdelay(1);
}
+
+ if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
+ sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
}

static void sdhci_init(struct sdhci_host *host)
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c5ce9ee..2962102 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -222,6 +222,8 @@ struct sdhci_host {
#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17)
/* Controller does not like fast PIO transfers */
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
+/* Controller losing signal/interrupt enable states after reset */
+#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19)

int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
--
1.5.6.5

2009-03-16 21:17:12

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver

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 <[email protected]>
Acked-by: Arnd Bergmann <[email protected]>
---
MAINTAINERS | 7 +
drivers/mmc/host/Kconfig | 11 ++
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-of.c | 309 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 328 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/host/sdhci-of.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 4a1cfca..77c3ec0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3837,6 +3837,13 @@ M: [email protected]
L: [email protected]
S: Maintained

+SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF)
+P: Anton Vorontsov
+M: [email protected]
+L: [email protected]
+L: [email protected]
+S: Maintained
+
SECURITY SUBSYSTEM
F: security/
P: James Morris
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index e032a42..69bd124 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -72,6 +72,17 @@ config MMC_RICOH_MMC

If unsure, say Y.

+config MMC_SDHCI_OF
+ tristate "SDHCI support on OpenFirmware platforms"
+ depends on MMC_SDHCI && PPC_OF
+ select MMC_SDHCI_IO_ACCESSORS
+ 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 <[email protected]>
+ * Anton Vorontsov <[email protected]>
+ *
+ * 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 <[email protected]>, "
+ "Anton Vorontsov <[email protected]>");
+MODULE_LICENSE("GPL");
--
1.5.6.5

2009-03-16 21:16:49

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 10/11] sdhci: Add quirk for forcing maximum block size to 2048 bytes

FSL eSDHC controllers can support maximum block size up to 4096 bytes,
the MBL (Maximum Block Length) field in the capabilities register
extended by one bit, and is set to 0x3.

But the SDHCI core doesn't support blocks of 4096 bytes, and thus
forces blksz to the lowest value -- 512 bytes. With this patch we can
pin up the blksz to the maximum supported block size, i.e. 2048 bytes.

Signed-off-by: Anton Vorontsov <[email protected]>
---
drivers/mmc/host/sdhci.c | 20 +++++++++++++-------
drivers/mmc/host/sdhci.h | 2 ++
2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 3a72fe2..30d8e3d 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1777,13 +1777,19 @@ int sdhci_add_host(struct sdhci_host *host)
* Maximum block size. This varies from controller to controller and
* is specified in the capabilities register.
*/
- mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;
- if (mmc->max_blk_size >= 3) {
- printk(KERN_WARNING "%s: Invalid maximum block size, "
- "assuming 512 bytes\n", mmc_hostname(mmc));
- mmc->max_blk_size = 512;
- } else
- mmc->max_blk_size = 512 << mmc->max_blk_size;
+ if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) {
+ mmc->max_blk_size = 2;
+ } else {
+ mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >>
+ SDHCI_MAX_BLOCK_SHIFT;
+ if (mmc->max_blk_size >= 3) {
+ printk(KERN_WARNING "%s: Invalid maximum block size, "
+ "assuming 512 bytes\n", mmc_hostname(mmc));
+ mmc->max_blk_size = 0;
+ }
+ }
+
+ mmc->max_blk_size = 512 << mmc->max_blk_size;

/*
* Maximum block count.
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 2962102..f20a834 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -224,6 +224,8 @@ struct sdhci_host {
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
/* Controller losing signal/interrupt enable states after reset */
#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19)
+/* Controller has to be forced to use block size of 2048 bytes */
+#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)

int irq; /* Device IRQ */
void __iomem * ioaddr; /* Mapped address */
--
1.5.6.5

2009-03-19 23:35:28

by yamazaki

[permalink] [raw]
Subject: Re: [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver

Hi all,

I am running the Linux kernel 2.6.28.7 on my PPC8347 BRD.
I have to write the driver of SDHCI driver(using R5C807 RICOH).
But I could not find it.
Then I found the good sample of this.
I tried to the patch to kernel 2.6.28.7,but I did not succeed.
Please tell me how to get the kernei for this patch.

-Seiji


>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 <[email protected]>
>Acked-by: Arnd Bergmann <[email protected]>
>---
> MAINTAINERS | 7 +
> drivers/mmc/host/Kconfig | 11 ++
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/sdhci-of.c | 309 +++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 328 insertions(+), 0 deletions(-)
> create mode 100644 drivers/mmc/host/sdhci-of.c
>
>diff --git a/MAINTAINERS b/MAINTAINERS
>index 4a1cfca..77c3ec0 100644
>--- a/MAINTAINERS
>+++ b/MAINTAINERS
>@@ -3837,6 +3837,13 @@ M: [email protected]
> L: [email protected]
> S: Maintained
>
>+SECURE DIGITAL HOST CONTROLLER INTERFACE, OPEN FIRMWARE BINDINGS (SDHCI-OF)
>+P: Anton Vorontsov
>+M: [email protected]
>+L: [email protected]
>+L: [email protected]
>+S: Maintained
>+
> SECURITY SUBSYSTEM
> F: security/
> P: James Morris
>diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
>index e032a42..69bd124 100644
>--- a/drivers/mmc/host/Kconfig
>+++ b/drivers/mmc/host/Kconfig
>@@ -72,6 +72,17 @@ config MMC_RICOH_MMC
>
> If unsure, say Y.
>
>+config MMC_SDHCI_OF
>+ tristate "SDHCI support on OpenFirmware platforms"
>+ depends on MMC_SDHCI && PPC_OF
>+ select MMC_SDHCI_IO_ACCESSORS
>+ 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 <[email protected]>
>+ * Anton Vorontsov <[email protected]>
>+ *
>+ * 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 <[email protected]>, "
>+ "Anton Vorontsov <[email protected]>");
>+MODULE_LICENSE("GPL");
>--
>1.5.6.5
>_______________________________________________
>Linuxppc-dev mailing list
>[email protected]
>https://ozlabs.org/mailman/listinfo/linuxppc-dev

----
[email protected]

2009-03-20 22:43:44

by Anton Vorontsov

[permalink] [raw]
Subject: Re: [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver

Hi!

On Fri, Mar 20, 2009 at 08:28:39AM +0900, yamazaki wrote:
> Hi all,
>
> I am running the Linux kernel 2.6.28.7 on my PPC8347 BRD.
> I have to write the driver of SDHCI driver(using R5C807 RICOH).

RICOH? It should be a PCI SD/MMC controller, so you even don't
need any patches to make it work in 2.6.28.7. Just make sure your
.config file has following symbols enabled:

CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_PCI=y
CONFIG_MMC_RICOH_MMC=y

--
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2

2009-03-21 00:13:57

by yamazaki

[permalink] [raw]
Subject: Re: [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver

Hi

Thank you for your reply.
I know RICOH has PCI SD/MMC controller. But R5C807 RICOH is not the PCI device
which is probably new product.

I made the "sdhci-of.c" from patch file,then I compiled it.
But is was not succeeded.
I thought the reason is the format of "the struct sdhci_of_data" is defferent from the
kernel 2.6.28.7,
That is why I sent this question.

>Hi!
>
>On Fri, Mar 20, 2009 at 08:28:39AM +0900, yamazaki wrote:
>> Hi all,
>>
>> I am running the Linux kernel 2.6.28.7 on my PPC8347 BRD.
>> I have to write the driver of SDHCI driver(using R5C807 RICOH).
>
>RICOH? It should be a PCI SD/MMC controller, so you even don't
>need any patches to make it work in 2.6.28.7. Just make sure your
>.config file has following symbols enabled:
>
>CONFIG_MMC_SDHCI=y
>CONFIG_MMC_SDHCI_PCI=y
>CONFIG_MMC_RICOH_MMC=y
>
>--
>Anton Vorontsov
>email: [email protected]
>irc://irc.freenode.net/bd2
>_______________________________________________
>Linuxppc-dev mailing list
>[email protected]
>https://ozlabs.org/mailman/listinfo/linuxppc-dev

----
[email protected]

2009-03-21 00:45:29

by Anton Vorontsov

[permalink] [raw]
Subject: Re: [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver

On Sat, Mar 21, 2009 at 09:15:25AM +0900, yamazaki wrote:
> Hi
>
> Thank you for your reply.
> I know RICOH has PCI SD/MMC controller. But R5C807 RICOH is not the PCI device
> which is probably new product.

Ah, then it must be connected via MPC8347's localbus.

Well, then you need 2.6.29-rcX kernels, for example
http://www.kernel.org/pub/linux/kernel/v2.6/testing/linux-2.6.29-rc8.tar.bz2
is suitable.

Untar it and apply the patches (they'll apply fine on that
kernel). Then you'll need some device tree additions for
your MPC8347 board, something like this:

localbus@e0005000 {
#address-cells = <2>;
#size-cells = <1>;
compatible = "fsl,mpc8347-localbus",
"fsl,pq2pro-localbus";
reg = <0xe0005000 0xd8>;
ranges = <0x1 0x0 0xf0000000 0x1000>;
// ^^ change the 0xf0000000 to the actual address
sdhci@1,0 {
compatible = "ricoh,r5c807", "generic-sdhci";
reg = <0x1 0x0 0x1000>;
interrupts = <ricoh-interrupt-here 0x8>;
interrupt-parent = <&ipic>;
// if needed, clock-frequency = <freq-in-HZ-here>;
};
};

Note that I'm not sure what endiannes you'll get when connecting
the ricoh chip to the big-endinan host...

--
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2

2009-03-21 07:46:19

by yamazaki

[permalink] [raw]
Subject: Re: [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver

Hi

Thank you for your reply again.

>Ah, then it must be connected via MPC8347's localbus.

That's right.

I will try it,and I inform them of the result.

>On Sat, Mar 21, 2009 at 09:15:25AM +0900, yamazaki wrote:
>> Hi
>>
>> Thank you for your reply.
>> I know RICOH has PCI SD/MMC controller. But R5C807 RICOH is not the PCI device
>> which is probably new product.
>
>Ah, then it must be connected via MPC8347's localbus.
>
>Well, then you need 2.6.29-rcX kernels, for example
>http://www.kernel.org/pub/linux/kernel/v2.6/testing/linux-2.6.29-rc8.tar.bz2
>is suitable.
>
>Untar it and apply the patches (they'll apply fine on that
>kernel). Then you'll need some device tree additions for
>your MPC8347 board, something like this:
>
> localbus@e0005000 {
> #address-cells = <2>;
> #size-cells = <1>;
> compatible = "fsl,mpc8347-localbus",
> "fsl,pq2pro-localbus";
> reg = <0xe0005000 0xd8>;
> ranges = <0x1 0x0 0xf0000000 0x1000>;
> // ^^ change the 0xf0000000 to the actual address
> sdhci@1,0 {
> compatible = "ricoh,r5c807", "generic-sdhci";
> reg = <0x1 0x0 0x1000>;
> interrupts = <ricoh-interrupt-here 0x8>;
> interrupt-parent = <&ipic>;
> // if needed, clock-frequency = <freq-in-HZ-here>;
> };
> };
>
>Note that I'm not sure what endiannes you'll get when connecting
>the ricoh chip to the big-endinan host...
>
>--
>Anton Vorontsov
>email: [email protected]
>irc://irc.freenode.net/bd2

----
[email protected]

2009-03-22 15:48:27

by Pierre Ossman

[permalink] [raw]
Subject: Re: [PATCH v3 0/11] FSL eSDHC support

On Tue, 17 Mar 2009 00:13:06 +0300
Anton Vorontsov <[email protected]> wrote:

> Hi all,
>
> Here comes another version, let's hope that one is final. ;-)
>

I think so. I've queued it all for 2.6.30. Thanks :)

Rgds
--
-- Pierre Ossman

WARNING: This correspondence is being monitored by the
Swedish government. Make sure your server uses encryption
for SMTP traffic and consider using PGP for end-to-end
encryption.


Attachments:
signature.asc (198.00 B)
Subject: Re: [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver

Hi

I downloaded linux-2.6.29-rc8.tar.bz2,and I patched .
I was succeeded. Thank you very match.
But I have the other problem.

Compile was not succeeded.

Message is ...

CALL arch/powerpc/kernel/prom_init_check.sh
CC drivers/mmc/host/sdhci-of.o
drivers/mmc/host/sdhci-of.c:163: error: 'SDHCI_QUIRK_FORCE_BLK_SZ_2048' undeclared here (not in a function)
drivers/mmc/host/sdhci-of.c:164: error: 'SDHCI_QUIRK_BROKEN_CARD_DETECTION' undeclared here (not in a function)
drivers/mmc/host/sdhci-of.c:165: error: 'SDHCI_QUIRK_INVERTED_WRITE_PROTECT' undeclared here (not in a function)
drivers/mmc/host/sdhci-of.c:167: error: 'SDHCI_QUIRK_NONSTANDARD_CLOCK' undeclared here (not in a function)
drivers/mmc/host/sdhci-of.c:168: error: 'SDHCI_QUIRK_PIO_NEEDS_DELAY' undeclared here (not in a function)
drivers/mmc/host/sdhci-of.c:169: error: 'SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET' undeclared here (not in a function)
drivers/mmc/host/sdhci-of.c:172: error: unknown field 'readl' specified in initializer
drivers/mmc/host/sdhci-of.c:172: warning: initialization from incompatible pointer type
drivers/mmc/host/sdhci-of.c:173: error: unknown field 'readw' specified in initializer
drivers/mmc/host/sdhci-of.c:173: warning: excess elements in struct initializer
drivers/mmc/host/sdhci-of.c:173: warning: (near initialization for 'sdhci_esdhc.ops')
drivers/mmc/host/sdhci-of.c:174: error: unknown field 'readb' specified in initializer
drivers/mmc/host/sdhci-of.c:174: warning: excess elements in struct initializer
drivers/mmc/host/sdhci-of.c:174: warning: (near initialization for 'sdhci_esdhc.ops')
drivers/mmc/host/sdhci-of.c:175: error: unknown field 'writel' specified in initializer
drivers/mmc/host/sdhci-of.c:175: warning: excess elements in struct initializer
drivers/mmc/host/sdhci-of.c:175: warning: (near initialization for 'sdhci_esdhc.ops')
drivers/mmc/host/sdhci-of.c:176: error: unknown field 'writew' specified in initializer
drivers/mmc/host/sdhci-of.c:176: warning: excess elements in struct initializer
drivers/mmc/host/sdhci-of.c:176: warning: (near initialization for 'sdhci_esdhc.ops')
drivers/mmc/host/sdhci-of.c:177: error: unknown field 'writeb' specified in initializer
drivers/mmc/host/sdhci-of.c:177: warning: excess elements in struct initializer
drivers/mmc/host/sdhci-of.c:177: warning: (near initialization for 'sdhci_esdhc.ops')
drivers/mmc/host/sdhci-of.c:178: error: unknown field 'set_clock' specified in initializer
drivers/mmc/host/sdhci-of.c:178: warning: excess elements in struct initializer
drivers/mmc/host/sdhci-of.c:178: warning: (near initialization for 'sdhci_esdhc.ops')
drivers/mmc/host/sdhci-of.c:180: error: unknown field 'get_max_clock' specified in initializer
drivers/mmc/host/sdhci-of.c:180: warning: excess elements in struct initializer
drivers/mmc/host/sdhci-of.c:180: warning: (near initialization for 'sdhci_esdhc.ops')
drivers/mmc/host/sdhci-of.c:181: error: unknown field 'get_timeout_clock' specified in initializer
drivers/mmc/host/sdhci-of.c:181: warning: excess elements in struct initializer
drivers/mmc/host/sdhci-of.c:181: warning: (near initialization for 'sdhci_esdhc.ops')
make[3]: *** [drivers/mmc/host/sdhci-of.o] Error 1
make[2]: *** [drivers/mmc/host] Error 2
make[1]: *** [drivers/mmc] Error 2
make: *** [drivers] Error 2

I do not know why. Could you please tell us why.
Thanks.

On Sat, 21 Mar 2009 03:45:06 +0300
Anton Vorontsov <[email protected]> wrote:

> On Sat, Mar 21, 2009 at 09:15:25AM +0900, yamazaki wrote:
> > Hi
> >
> > Thank you for your reply.
> > I know RICOH has PCI SD/MMC controller. But R5C807 RICOH is not the PCI device
> > which is probably new product.
>
> Ah, then it must be connected via MPC8347's localbus.
>
> Well, then you need 2.6.29-rcX kernels, for example
> http://www.kernel.org/pub/linux/kernel/v2.6/testing/linux-2.6.29-rc8.tar.bz2
> is suitable.
>
> Untar it and apply the patches (they'll apply fine on that
> kernel). Then you'll need some device tree additions for
> your MPC8347 board, something like this:
>
> localbus@e0005000 {
> #address-cells = <2>;
> #size-cells = <1>;
> compatible = "fsl,mpc8347-localbus",
> "fsl,pq2pro-localbus";
> reg = <0xe0005000 0xd8>;
> ranges = <0x1 0x0 0xf0000000 0x1000>;
> // ^^ change the 0xf0000000 to the actual address
> sdhci@1,0 {
> compatible = "ricoh,r5c807", "generic-sdhci";
> reg = <0x1 0x0 0x1000>;
> interrupts = <ricoh-interrupt-here 0x8>;
> interrupt-parent = <&ipic>;
> // if needed, clock-frequency = <freq-in-HZ-here>;
> };
> };
>
> Note that I'm not sure what endiannes you'll get when connecting
> the ricoh chip to the big-endinan host...
>
> --
> Anton Vorontsov
> email: [email protected]
> irc://irc.freenode.net/bd2

--
$B;3:j!!@:Fs(B <[email protected]>

Subject: Re: [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver

Hi

> The patch might be fine, but are you receiving this without the patch?

Befor patch,no sdhci-of.c found.
After patch,found sdhci-of.c.

> if you receive this with the patch then well there you go, but if this
> happens as a result with a clean fresh .tar.ball(vanilla kernel) then this
> is something
> else.
>

I got the kernel from
Well, then you need 2.6.29-rcX kernels, for example
http://www.kernel.org/pub/linux/kernel/v2.6/testing/linux-2.6.29-rc8.tar.bz2

I do not know why so many errors when compile.

regards,

-- Seiji Yamazaki


On Mon, 23 Mar 2009 01:43:38 -0700 (PDT)
[email protected] wrote:

> On Mon, 23 Mar 2009, ????? wrote:
>
> > Hi
> >
> > I tried again form first.
> >
> > I patched like this.
> >
> > [yama@localhost Ippf]$ cd linux-2.6.29-rc8
> > [yama@localhost linux-2.6.29-rc8]$ patch -p 1 <patch.txt
> > (Stripping trailing CRs from patch.)
> > patching file MAINTAINERS
> > Hunk #1 succeeded at 3839 (offset 2 lines).
> > (Stripping trailing CRs from patch.)
> > patching file drivers/mmc/host/Kconfig
> > Hunk #1 succeeded at 65 (offset -7 lines).
> > (Stripping trailing CRs from patch.)
> > patching file drivers/mmc/host/Makefile
> > (Stripping trailing CRs from patch.)
> > patching file drivers/mmc/host/sdhci-of.c
> > [yama@localhost linux-2.6.29-rc8]$
>
> The patch might be fine, but are you receiving this without the patch?
>
> >
> > I Believe patch is OK.
> > And I got linux-2.6.29-rc8 kernel from kernel.org
> >
> >> What are the outcomes when not applying a patch?
> >>
> > Before applying a patch,sdhci-of.c was not exist in it.
> >
> > I attach the patch file?
> >
> > regards,
> >
> > -- Seiji Yamazaki
> >
> > On Mon, 23 Mar 2009 01:05:07 -0700 (PDT)
> > [email protected] wrote:
> >
> >> On Mon, 23 Mar 2009, ????? wrote:
> >>
> >>> Hi
> >>>
> >>> Sorry about it.
> >>>
> >>>> On Mon, 23 Mar 2009, ????? wrote:
> >>>
> >>> ????? is Japanese language.
> >>>
> >>>> my guess is one of the header files has a changed definition
> >>>> and/or you're missing the file completely.
> >>>> (but could be wrong);
> >>>
> >>> I tried to grep SDHCI_QUIRK_FORCE_BLK_SZ_2048 on linux-2.6.29-rc8.
> >>> But no found execpt sdhci-of.c.
> >>> Does anybody know why?
> >>
> >> well I guess this is where you have to work from.
> >> (but then after contemplating you did add a patch right?)
> >>
> >>
> >>>
> >>> regards,
> >>>
> >>> -- Seiji Yamazaki
> >>>
> >>> On Mon, 23 Mar 2009 00:14:23 -0700 (PDT)
> >>> [email protected] wrote:
> >>>
> >>>> On Mon, 23 Mar 2009, ????? wrote:
> >>>>
> >>>>> Hi
> >>>>>
> >>>>> I downloaded linux-2.6.29-rc8.tar.bz2,and I patched .
> >>>>> I was succeeded. Thank you very match.
> >>>>> But I have the other problem.
> >>>>>
> >>>>> Compile was not succeeded.
> >>>>>
> >>>>> Message is ...
> >>>>>
> >>>>> CALL arch/powerpc/kernel/prom_init_check.sh
> >>>>> CC drivers/mmc/host/sdhci-of.o
> >>>>> drivers/mmc/host/sdhci-of.c:163: error: 'SDHCI_QUIRK_FORCE_BLK_SZ_2048' undeclared here (not in a function)
> >>>>> drivers/mmc/host/sdhci-of.c:164: error: 'SDHCI_QUIRK_BROKEN_CARD_DETECTION' undeclared here (not in a function)
> >>>>> drivers/mmc/host/sdhci-of.c:165: error: 'SDHCI_QUIRK_INVERTED_WRITE_PROTECT' undeclared here (not in a function)
> >>>>> drivers/mmc/host/sdhci-of.c:167: error: 'SDHCI_QUIRK_NONSTANDARD_CLOCK' undeclared here (not in a function)
> >>>>> drivers/mmc/host/sdhci-of.c:168: error: 'SDHCI_QUIRK_PIO_NEEDS_DELAY' undeclared here (not in a function)
> >>>>> drivers/mmc/host/sdhci-of.c:169: error: 'SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET' undeclared here (not in a function)
> >>>>> drivers/mmc/host/sdhci-of.c:172: error: unknown field 'readl' specified in initializer
> >>>>> drivers/mmc/host/sdhci-of.c:172: warning: initialization from incompatible pointer type
> >>>>> drivers/mmc/host/sdhci-of.c:173: error: unknown field 'readw' specified in initializer
> >>>>> drivers/mmc/host/sdhci-of.c:173: warning: excess elements in struct initializer
> >>>>> drivers/mmc/host/sdhci-of.c:173: warning: (near initialization for 'sdhci_esdhc.ops')
> >>>>> drivers/mmc/host/sdhci-of.c:174: error: unknown field 'readb' specified in initializer
> >>>>> drivers/mmc/host/sdhci-of.c:174: warning: excess elements in struct initializer
> >>>>> drivers/mmc/host/sdhci-of.c:174: warning: (near initialization for 'sdhci_esdhc.ops')
> >>>>> drivers/mmc/host/sdhci-of.c:175: error: unknown field 'writel' specified in initializer
> >>>>> drivers/mmc/host/sdhci-of.c:175: warning: excess elements in struct initializer
> >>>>> drivers/mmc/host/sdhci-of.c:175: warning: (near initialization for 'sdhci_esdhc.ops')
> >>>>> drivers/mmc/host/sdhci-of.c:176: error: unknown field 'writew' specified in initializer
> >>>>> drivers/mmc/host/sdhci-of.c:176: warning: excess elements in struct initializer
> >>>>> drivers/mmc/host/sdhci-of.c:176: warning: (near initialization for 'sdhci_esdhc.ops')
> >>>>> drivers/mmc/host/sdhci-of.c:177: error: unknown field 'writeb' specified in initializer
> >>>>> drivers/mmc/host/sdhci-of.c:177: warning: excess elements in struct initializer
> >>>>> drivers/mmc/host/sdhci-of.c:177: warning: (near initialization for 'sdhci_esdhc.ops')
> >>>>> drivers/mmc/host/sdhci-of.c:178: error: unknown field 'set_clock' specified in initializer
> >>>>> drivers/mmc/host/sdhci-of.c:178: warning: excess elements in struct initializer
> >>>>> drivers/mmc/host/sdhci-of.c:178: warning: (near initialization for 'sdhci_esdhc.ops')
> >>>>> drivers/mmc/host/sdhci-of.c:180: error: unknown field 'get_max_clock' specified in initializer
> >>>>> drivers/mmc/host/sdhci-of.c:180: warning: excess elements in struct initializer
> >>>>> drivers/mmc/host/sdhci-of.c:180: warning: (near initialization for 'sdhci_esdhc.ops')
> >>>>> drivers/mmc/host/sdhci-of.c:181: error: unknown field 'get_timeout_clock' specified in initializer
> >>>>> drivers/mmc/host/sdhci-of.c:181: warning: excess elements in struct initializer
> >>>>> drivers/mmc/host/sdhci-of.c:181: warning: (near initialization for 'sdhci_esdhc.ops')
> >>>>> make[3]: *** [drivers/mmc/host/sdhci-of.o] Error 1
> >>>>> make[2]: *** [drivers/mmc/host] Error 2
> >>>>> make[1]: *** [drivers/mmc] Error 2
> >>>>> make: *** [drivers] Error 2
> >>>>>
> >>>>> I do not know why. Could you please tell us why.
> >>>>> Thanks.
> >>>>>
> >>>>> On Sat, 21 Mar 2009 03:45:06 +0300
> >>>>> Anton Vorontsov <[email protected]> wrote:
> >>>>>
> >>>>>> On Sat, Mar 21, 2009 at 09:15:25AM +0900, yamazaki wrote:
> >>>>>>> Hi
> >>>>>>>
> >>>>>>> Thank you for your reply.
> >>>>>>> I know RICOH has PCI SD/MMC controller. But R5C807 RICOH is not the PCI device
> >>>>>>> which is probably new product.
> >>>>>>
> >>>>>> Ah, then it must be connected via MPC8347's localbus.
> >>>>>>
> >>>>>> Well, then you need 2.6.29-rcX kernels, for example
> >>>>>> http://www.kernel.org/pub/linux/kernel/v2.6/testing/linux-2.6.29-rc8.tar.bz2
> >>>>>> is suitable.
> >>>>>>
> >>>>>> Untar it and apply the patches (they'll apply fine on that
> >>>>>> kernel). Then you'll need some device tree additions for
> >>>>>> your MPC8347 board, something like this:
> >>>>>>
> >>>>>> localbus@e0005000 {
> >>>>>> #address-cells = <2>;
> >>>>>> #size-cells = <1>;
> >>>>>> compatible = "fsl,mpc8347-localbus",
> >>>>>> "fsl,pq2pro-localbus";
> >>>>>> reg = <0xe0005000 0xd8>;
> >>>>>> ranges = <0x1 0x0 0xf0000000 0x1000>;
> >>>>>> // ^^ change the 0xf0000000 to the actual address
> >>>>>> sdhci@1,0 {
> >>>>>> compatible = "ricoh,r5c807", "generic-sdhci";
> >>>>>> reg = <0x1 0x0 0x1000>;
> >>>>>> interrupts = <ricoh-interrupt-here 0x8>;
> >>>>>> interrupt-parent = <&ipic>;
> >>>>>> // if needed, clock-frequency = <freq-in-HZ-here>;
> >>>>>> };
> >>>>>> };
> >>>>>>
> >>>>>> Note that I'm not sure what endiannes you'll get when connecting
> >>>>>> the ricoh chip to the big-endinan host...
> >>>>>>
> >>>>>> --
> >>>>>> Anton Vorontsov
> >>>>>> email: [email protected]
> >>>>>> irc://irc.freenode.net/bd2
> >>>>>
> >>>>> --
> >>>>> ????? <[email protected]>
> >>>>>
> >>>>> --
> >>>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> >>>>> the body of a message to [email protected]
> >>>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
> >>>>> Please read the FAQ at http://www.tux.org/lkml/
> >>>>>
> >>>>
> >>>> my guess is one of the header files has a changed definition
> >>>> and/or you're missing the file completely.
> >>>> (but could be wrong);
> >>>>
> >>>> regards,
> >>>>
> >>>> -- Justin P. Mattock
> >>>
> >>> --
> >>> ????? <[email protected]>
> >>>
> >>>
> >>
> >> What are the outcomes when not applying a patch?
> >>
> >> regards,
> >>
> >>
> >> -- Justin P. Mattock
> >
> > --
> > ????? <[email protected]>
> >
>
> if you receive this with the patch then well there you go, but if this
> happens as a result with a clean fresh .tar.ball(vanilla kernel) then this
> is something
> else.
>
> regards,
>
> -- Justin P. Mattock

--
<[email protected]>

2009-03-23 16:17:16

by Anton Vorontsov

[permalink] [raw]
Subject: Re: [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver

On Mon, Mar 23, 2009 at 03:34:45PM +0900, 山崎 精二 wrote:
> Hi
>
> I downloaded linux-2.6.29-rc8.tar.bz2,and I patched .
> I was succeeded. Thank you very match.
> But I have the other problem.
>
> Compile was not succeeded.
>
> Message is ...
>
> CALL arch/powerpc/kernel/prom_init_check.sh
> CC drivers/mmc/host/sdhci-of.o
> drivers/mmc/host/sdhci-of.c:163: error: 'SDHCI_QUIRK_FORCE_BLK_SZ_2048' undeclared here (not in a function)
> drivers/mmc/host/sdhci-of.c:164: error: 'SDHCI_QUIRK_BROKEN_CARD_DETECTION' undeclared here (not in a function)
> drivers/mmc/host/sdhci-of.c:165: error: 'SDHCI_QUIRK_INVERTED_WRITE_PROTECT' undeclared here (not in a function)
> drivers/mmc/host/sdhci-of.c:167: error: 'SDHCI_QUIRK_NONSTANDARD_CLOCK' undeclared here (not in a function)
> drivers/mmc/host/sdhci-of.c:168: error: 'SDHCI_QUIRK_PIO_NEEDS_DELAY' undeclared here (not in a function)
> drivers/mmc/host/sdhci-of.c:169: error: 'SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET' undeclared here (not in a function)
[...]
> drivers/mmc/host/sdhci-of.c:181: warning: excess elements in struct initializer
> drivers/mmc/host/sdhci-of.c:181: warning: (near initialization for 'sdhci_esdhc.ops')
> make[3]: *** [drivers/mmc/host/sdhci-of.o] Error 1
> make[2]: *** [drivers/mmc/host] Error 2
> make[1]: *** [drivers/mmc] Error 2

All these errors indicate that you didn't apply all the patches.
You'll need all 11 patches from this series, not only the last
one.

--
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2

Subject: Re: [PATCH 11/11] mmc: Add OpenFirmware bindings for SDHCI driver

Hi

>
> All these errors indicate that you didn't apply all the patches.
> You'll need all 11 patches from this series, not only the last
> one.
>

I applyed all the patches,then Compile was succeeded.

Thank you for your advice.

regards,

-- Seiji Yamazaki


On Mon, 23 Mar 2009 19:16:55 +0300
Anton Vorontsov <[email protected]> wrote:

> On Mon, Mar 23, 2009 at 03:34:45PM +0900, $Bj3%"j6<e(B??$Bn4%;h<7(Bwrote:
> > Hi
> >
> > I downloaded linux-2.6.29-rc8.tar.bz2,and I patched .
> > I was succeeded. Thank you very match.
> > But I have the other problem.
> >
> > Compile was not succeeded.
> >
> > Message is ...
> >
> > CALL arch/powerpc/kernel/prom_init_check.sh
> > CC drivers/mmc/host/sdhci-of.o
> > drivers/mmc/host/sdhci-of.c:163: error: 'SDHCI_QUIRK_FORCE_BLK_SZ_2048' undeclared here (not in a function)
> > drivers/mmc/host/sdhci-of.c:164: error: 'SDHCI_QUIRK_BROKEN_CARD_DETECTION' undeclared here (not in a function)
> > drivers/mmc/host/sdhci-of.c:165: error: 'SDHCI_QUIRK_INVERTED_WRITE_PROTECT' undeclared here (not in a function)
> > drivers/mmc/host/sdhci-of.c:167: error: 'SDHCI_QUIRK_NONSTANDARD_CLOCK' undeclared here (not in a function)
> > drivers/mmc/host/sdhci-of.c:168: error: 'SDHCI_QUIRK_PIO_NEEDS_DELAY' undeclared here (not in a function)
> > drivers/mmc/host/sdhci-of.c:169: error: 'SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET' undeclared here (not in a function)
> [...]
> > drivers/mmc/host/sdhci-of.c:181: warning: excess elements in struct initializer
> > drivers/mmc/host/sdhci-of.c:181: warning: (near initialization for 'sdhci_esdhc.ops')
> > make[3]: *** [drivers/mmc/host/sdhci-of.o] Error 1
> > make[2]: *** [drivers/mmc/host] Error 2
> > make[1]: *** [drivers/mmc] Error 2
>
> All these errors indicate that you didn't apply all the patches.
> You'll need all 11 patches from this series, not only the last
> one.
>
> --
> Anton Vorontsov
> email: [email protected]
> irc://irc.freenode.net/bd2

--
yamazaki seiji <[email protected]>