2009-07-28 10:38:36

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 0/32] mmc and omap_hsmmc patches

Hi

Here is version 2 of our 32 patches for mmc and omap_hsmmc.

They include Matt Fleming's change for card caps, and 2 other fixes:
- use a spin lock rather than enable / diable irq
- make disable delay specified in milliseconds not jiffies because the
value is an int, and jiffies are unsigned long (also millisecs are
better anyway)

They split into 2 groups: the first 8 affect mmc core only
and the remaining ones affect omap_hsmmc only.

Adrian Hunter (19):
mmc: add 'enable' and 'disable' methods to mmc host
mmc: allow host claim / release nesting
mmc: add MMC_CAP_NONREMOVABLE host capability
mmc: add ability to save power by powering off cards
mmc: check status after MMC SWITCH command
omap_hsmmc: make use of new enable/disable interface
omap_hsmmc: keep track of power mode
omap_hsmmc: ensure workqueues are empty before suspend
omap_hsmmc: make use of new MMC_CAP_NONREMOVABLE host capability
ARM: OMAP: mmc-twl4030: add regulator sleep / wake function
omap_hsmmc: put MMC regulator to sleep
omap_hsmmc: clear interrupt status after init sequence
omap_hsmmc: cater for weird CMD6 behaviour
omap_hsmmc: prevent races with irq handler
omap_hsmmc: pass host capabilities for SD only and MMC only
omap_hsmmc: protect the card when the cover is open
omap_hsmmc: ensure all clock enables and disables are paired
omap_hsmmc: set a large data timeout for commands with busy signal
ARM: OMAP: RX51: set MMC capabilities and power-saving flag

Denis Karpov (8):
mmc: power off once at removal
omap_hsmmc: add debugfs entry (host registers)
ARM: OMAP: mmc-twl4030: add context loss counter support
omap_hsmmc: context save/restore support
omap_hsmmc: set open drain bit correctly
omap_hsmmc: support for deeper power saving states
omap_hsmmc: cleanup macro usage
omap_hsmmc: code refactoring

Jarkko Lavinen (4):
mmc: add mmc card sleep and awake support
omap_hsmmc: fix scatter-gather list sanity checking
omap_hsmmc: add mmc card sleep and awake support
omap_hsmmc: fix NULL pointer dereference

Matt Fleming (1):
mmc: add host capabilities for SD only and MMC only

arch/arm/mach-omap2/board-rx51-peripherals.c | 5 +
arch/arm/mach-omap2/mmc-twl4030.c | 77 ++
arch/arm/mach-omap2/mmc-twl4030.h | 2 +
arch/arm/plat-omap/include/mach/mmc.h | 11 +
drivers/mmc/core/core.c | 291 +++++++-
drivers/mmc/core/core.h | 4 +
drivers/mmc/core/host.c | 1 +
drivers/mmc/core/host.h | 2 +
drivers/mmc/core/mmc.c | 130 +++-
drivers/mmc/core/mmc_ops.c | 59 ++
drivers/mmc/core/mmc_ops.h | 1 +
drivers/mmc/core/sd.c | 52 ++-
drivers/mmc/host/at91_mci.c | 2 +-
drivers/mmc/host/atmel-mci.c | 1 +
drivers/mmc/host/au1xmmc.c | 3 +-
drivers/mmc/host/cb710-mmc.c | 3 +-
drivers/mmc/host/imxmmc.c | 3 +-
drivers/mmc/host/mmc_spi.c | 2 +-
drivers/mmc/host/mmci.c | 2 +
drivers/mmc/host/mvsdio.c | 3 +-
drivers/mmc/host/mxcmmc.c | 3 +-
drivers/mmc/host/omap.c | 2 +-
drivers/mmc/host/omap_hsmmc.c | 1038 +++++++++++++++++++++-----
drivers/mmc/host/pxamci.c | 2 +-
drivers/mmc/host/s3cmci.c | 3 +-
drivers/mmc/host/sdhci.c | 2 +-
drivers/mmc/host/sdricoh_cs.c | 3 +-
drivers/mmc/host/tifm_sd.c | 3 +-
drivers/mmc/host/tmio_mmc.c | 3 +-
drivers/mmc/host/via-sdmmc.c | 3 +-
drivers/mmc/host/wbsd.c | 3 +-
include/linux/mmc/card.h | 2 +
include/linux/mmc/core.h | 1 +
include/linux/mmc/host.h | 61 ++
include/linux/mmc/mmc.h | 3 +
35 files changed, 1554 insertions(+), 232 deletions(-)


Regards
Adrian


2009-07-28 10:38:37

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 2/32] mmc: allow host claim / release nesting

>From 93e60ec163a4ef242e88056c9c642c227f256628 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Mon, 27 Apr 2009 13:38:42 +0300
Subject: [PATCH] mmc: allow host claim / release nesting

This change allows the MMC host to be claimed in
situations where the host may or may not have
already been claimed. Also 'mmc_try_claim_host()'
is now exported.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/core/core.c | 34 +++++++++++++++++++++++++---------
include/linux/mmc/core.h | 1 +
include/linux/mmc/host.h | 2 ++
3 files changed, 28 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 9bc8d27..bab5015 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -461,16 +461,18 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
stop = abort ? atomic_read(abort) : 0;
- if (stop || !host->claimed)
+ if (stop || !host->claimed || host->claimer == current)
break;
spin_unlock_irqrestore(&host->lock, flags);
schedule();
spin_lock_irqsave(&host->lock, flags);
}
set_current_state(TASK_RUNNING);
- if (!stop)
+ if (!stop) {
host->claimed = 1;
- else
+ host->claimer = current;
+ host->claim_cnt += 1;
+ } else
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait);
@@ -481,29 +483,43 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)

EXPORT_SYMBOL(__mmc_claim_host);

-static int mmc_try_claim_host(struct mmc_host *host)
+/**
+ * mmc_try_claim_host - try exclusively to claim a host
+ * @host: mmc host to claim
+ *
+ * Returns %1 if the host is claimed, %0 otherwise.
+ */
+int mmc_try_claim_host(struct mmc_host *host)
{
int claimed_host = 0;
unsigned long flags;

spin_lock_irqsave(&host->lock, flags);
- if (!host->claimed) {
+ if (!host->claimed || host->claimer == current) {
host->claimed = 1;
+ host->claimer = current;
+ host->claim_cnt += 1;
claimed_host = 1;
}
spin_unlock_irqrestore(&host->lock, flags);
return claimed_host;
}
+EXPORT_SYMBOL(mmc_try_claim_host);

static void mmc_do_release_host(struct mmc_host *host)
{
unsigned long flags;

spin_lock_irqsave(&host->lock, flags);
- host->claimed = 0;
- spin_unlock_irqrestore(&host->lock, flags);
-
- wake_up(&host->wq);
+ if (--host->claim_cnt) {
+ /* Release for nested claim */
+ spin_unlock_irqrestore(&host->lock, flags);
+ } else {
+ host->claimed = 0;
+ host->claimer = NULL;
+ spin_unlock_irqrestore(&host->lock, flags);
+ wake_up(&host->wq);
+ }
}

void mmc_host_deeper_disable(struct work_struct *work)
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 7ac8b50..e4898e9 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -139,6 +139,7 @@ extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);

extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
extern void mmc_release_host(struct mmc_host *host);
+extern int mmc_try_claim_host(struct mmc_host *host);

/**
* mmc_claim_host - exclusively claim a host
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 338a9b3..631a2fe 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -182,6 +182,8 @@ struct mmc_host {
struct mmc_card *card; /* device attached to this host */

wait_queue_head_t wq;
+ struct task_struct *claimer; /* task that has host claimed */
+ int claim_cnt; /* "claim" nesting count */

struct delayed_work detect;

--
1.5.6.3

2009-07-28 10:38:48

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 3/32] mmc: add MMC_CAP_NONREMOVABLE host capability

>From 447937e379db91975b4e834f19dfb1805d813fc4 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Mon, 11 May 2009 10:03:41 +0300
Subject: [PATCH] mmc: add MMC_CAP_NONREMOVABLE host capability

eMMC's are not removable, so unsafe resume is OK always.

To permit this a new host capability MMC_CAP_NONREMOVABLE
has been added and suspend / resume updated accordingly.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/core/mmc.c | 41 ++++++++++++++++++++++++++++++++++-------
drivers/mmc/core/sd.c | 41 ++++++++++++++++++++++++++++++++++-------
include/linux/mmc/host.h | 1 +
3 files changed, 69 insertions(+), 14 deletions(-)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 06084db..3e35075 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -507,8 +507,6 @@ static void mmc_detect(struct mmc_host *host)
}
}

-#ifdef CONFIG_MMC_UNSAFE_RESUME
-
/*
* Suspend callback from host.
*/
@@ -551,20 +549,49 @@ static void mmc_resume(struct mmc_host *host)

}

-#else
+#ifdef CONFIG_MMC_UNSAFE_RESUME

-#define mmc_suspend NULL
-#define mmc_resume NULL
+static const struct mmc_bus_ops mmc_ops = {
+ .remove = mmc_remove,
+ .detect = mmc_detect,
+ .suspend = mmc_suspend,
+ .resume = mmc_resume,
+};

-#endif
+static void mmc_attach_bus_ops(struct mmc_host *host)
+{
+ mmc_attach_bus(host, &mmc_ops);
+}
+
+#else

static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static const struct mmc_bus_ops mmc_ops_unsafe = {
+ .remove = mmc_remove,
+ .detect = mmc_detect,
.suspend = mmc_suspend,
.resume = mmc_resume,
};

+static void mmc_attach_bus_ops(struct mmc_host *host)
+{
+ const struct mmc_bus_ops *bus_ops;
+
+ if (host->caps & MMC_CAP_NONREMOVABLE)
+ bus_ops = &mmc_ops_unsafe;
+ else
+ bus_ops = &mmc_ops;
+ mmc_attach_bus(host, bus_ops);
+}
+
+#endif
+
/*
* Starting point for MMC card init.
*/
@@ -575,7 +602,7 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
BUG_ON(!host);
WARN_ON(!host->claimed);

- mmc_attach_bus(host, &mmc_ops);
+ mmc_attach_bus_ops(host);

/*
* We need to get OCR a different way for SPI.
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index cd81c39..80cccd2 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -561,8 +561,6 @@ static void mmc_sd_detect(struct mmc_host *host)
}
}

-#ifdef CONFIG_MMC_UNSAFE_RESUME
-
/*
* Suspend callback from host.
*/
@@ -605,20 +603,49 @@ static void mmc_sd_resume(struct mmc_host *host)

}

-#else
+#ifdef CONFIG_MMC_UNSAFE_RESUME

-#define mmc_sd_suspend NULL
-#define mmc_sd_resume NULL
+static const struct mmc_bus_ops mmc_sd_ops = {
+ .remove = mmc_sd_remove,
+ .detect = mmc_sd_detect,
+ .suspend = mmc_sd_suspend,
+ .resume = mmc_sd_resume,
+};

-#endif
+static void mmc_sd_attach_bus_ops(struct mmc_host *host)
+{
+ mmc_attach_bus(host, &mmc_sd_ops);
+}
+
+#else

static const struct mmc_bus_ops mmc_sd_ops = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
+ .remove = mmc_sd_remove,
+ .detect = mmc_sd_detect,
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
};

+static void mmc_sd_attach_bus_ops(struct mmc_host *host)
+{
+ const struct mmc_bus_ops *bus_ops;
+
+ if (host->caps & MMC_CAP_NONREMOVABLE)
+ bus_ops = &mmc_sd_ops_unsafe;
+ else
+ bus_ops = &mmc_sd_ops;
+ mmc_attach_bus(host, bus_ops);
+}
+
+#endif
+
/*
* Starting point for SD card init.
*/
@@ -629,7 +656,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
BUG_ON(!host);
WARN_ON(!host->claimed);

- mmc_attach_bus(host, &mmc_sd_ops);
+ mmc_sd_attach_bus_ops(host);

/*
* We need to get OCR a different way for SPI.
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 631a2fe..bb867d2 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -148,6 +148,7 @@ struct mmc_host {
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
#define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */
+#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */

/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
--
1.5.6.3

2009-07-28 10:38:53

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 4/32] mmc: add ability to save power by powering off cards

>From e6355578d082d94707c8fda1e1342c478019b5c8 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Mon, 11 May 2009 12:20:57 +0300
Subject: [PATCH] mmc: add ability to save power by powering off cards

Power can be saved by powering off cards that are not
in use. This is similar to suspend / resume except
it is under the control of the driver, and does not
require any power management support. It can only
be used when the driver can monitor whether the card
is removed, otherwise it is unsafe. This is possible
because, unlike suspend, the driver still receives
card detect and / or cover switch interrupts.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/core/core.c | 34 ++++++++++++++++++++++++++++++++++
drivers/mmc/core/core.h | 2 ++
drivers/mmc/core/mmc.c | 11 +++++++++++
drivers/mmc/core/sd.c | 11 +++++++++++
include/linux/mmc/host.h | 3 +++
5 files changed, 61 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index bab5015..39f7bd1 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1145,6 +1145,40 @@ void mmc_stop_host(struct mmc_host *host)
mmc_power_off(host);
}

+void mmc_power_save_host(struct mmc_host *host)
+{
+ mmc_bus_get(host);
+
+ if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
+ mmc_bus_put(host);
+ return;
+ }
+
+ if (host->bus_ops->power_save)
+ host->bus_ops->power_save(host);
+
+ mmc_bus_put(host);
+
+ mmc_power_off(host);
+}
+EXPORT_SYMBOL(mmc_power_save_host);
+
+void mmc_power_restore_host(struct mmc_host *host)
+{
+ mmc_bus_get(host);
+
+ if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
+ mmc_bus_put(host);
+ return;
+ }
+
+ mmc_power_up(host);
+ host->bus_ops->power_restore(host);
+
+ mmc_bus_put(host);
+}
+EXPORT_SYMBOL(mmc_power_restore_host);
+
#ifdef CONFIG_PM

/**
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index c819eff..f7eb4c4 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -20,6 +20,8 @@ struct mmc_bus_ops {
void (*detect)(struct mmc_host *);
void (*suspend)(struct mmc_host *);
void (*resume)(struct mmc_host *);
+ void (*power_save)(struct mmc_host *);
+ void (*power_restore)(struct mmc_host *);
};

void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 3e35075..01f7226 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -549,6 +549,14 @@ static void mmc_resume(struct mmc_host *host)

}

+static void mmc_power_restore(struct mmc_host *host)
+{
+ host->card->state &= ~MMC_STATE_HIGHSPEED;
+ mmc_claim_host(host);
+ mmc_init_card(host, host->ocr, host->card);
+ mmc_release_host(host);
+}
+
#ifdef CONFIG_MMC_UNSAFE_RESUME

static const struct mmc_bus_ops mmc_ops = {
@@ -556,6 +564,7 @@ static const struct mmc_bus_ops mmc_ops = {
.detect = mmc_detect,
.suspend = mmc_suspend,
.resume = mmc_resume,
+ .power_restore = mmc_power_restore,
};

static void mmc_attach_bus_ops(struct mmc_host *host)
@@ -570,6 +579,7 @@ static const struct mmc_bus_ops mmc_ops = {
.detect = mmc_detect,
.suspend = NULL,
.resume = NULL,
+ .power_restore = mmc_power_restore,
};

static const struct mmc_bus_ops mmc_ops_unsafe = {
@@ -577,6 +587,7 @@ static const struct mmc_bus_ops mmc_ops_unsafe = {
.detect = mmc_detect,
.suspend = mmc_suspend,
.resume = mmc_resume,
+ .power_restore = mmc_power_restore,
};

static void mmc_attach_bus_ops(struct mmc_host *host)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 80cccd2..debe26e 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -603,6 +603,14 @@ static void mmc_sd_resume(struct mmc_host *host)

}

+static void mmc_sd_power_restore(struct mmc_host *host)
+{
+ host->card->state &= ~MMC_STATE_HIGHSPEED;
+ mmc_claim_host(host);
+ mmc_sd_init_card(host, host->ocr, host->card);
+ mmc_release_host(host);
+}
+
#ifdef CONFIG_MMC_UNSAFE_RESUME

static const struct mmc_bus_ops mmc_sd_ops = {
@@ -610,6 +618,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.detect = mmc_sd_detect,
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
+ .power_restore = mmc_sd_power_restore,
};

static void mmc_sd_attach_bus_ops(struct mmc_host *host)
@@ -624,6 +633,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.detect = mmc_sd_detect,
.suspend = NULL,
.resume = NULL,
+ .power_restore = mmc_sd_power_restore,
};

static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
@@ -631,6 +641,7 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
.detect = mmc_sd_detect,
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume,
+ .power_restore = mmc_sd_power_restore,
};

static void mmc_sd_attach_bus_ops(struct mmc_host *host)
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index bb867d2..c1cbe59 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -223,6 +223,9 @@ static inline void *mmc_priv(struct mmc_host *host)
extern int mmc_suspend_host(struct mmc_host *, pm_message_t);
extern int mmc_resume_host(struct mmc_host *);

+extern void mmc_power_save_host(struct mmc_host *host);
+extern void mmc_power_restore_host(struct mmc_host *host);
+
extern void mmc_detect_change(struct mmc_host *, unsigned long delay);
extern void mmc_request_done(struct mmc_host *, struct mmc_request *);

--
1.5.6.3

2009-07-28 10:39:06

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 5/32] mmc: add mmc card sleep and awake support

>From 44a818d58544b3f796e2e4a7e2331d4f1d14a3f1 Mon Sep 17 00:00:00 2001
From: Jarkko Lavinen <[email protected]>
Date: Tue, 12 May 2009 19:46:14 +0300
Subject: [PATCH] mmc: add mmc card sleep and awake support

Add support for the new MMC command SLEEP_AWAKE.

Signed-off-by: Jarkko Lavinen <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/core/core.c | 40 ++++++++++++++++++++++++++++++++
drivers/mmc/core/core.h | 2 +
drivers/mmc/core/mmc.c | 54 +++++++++++++++++++++++++++++++++++++++----
drivers/mmc/core/mmc_ops.c | 36 +++++++++++++++++++++++++++++
drivers/mmc/core/mmc_ops.h | 1 +
include/linux/mmc/card.h | 2 +
include/linux/mmc/host.h | 5 ++++
include/linux/mmc/mmc.h | 2 +
8 files changed, 137 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 39f7bd1..0611bf7 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1179,6 +1179,46 @@ void mmc_power_restore_host(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_power_restore_host);

+int mmc_card_awake(struct mmc_host *host)
+{
+ int err = -ENOSYS;
+
+ mmc_bus_get(host);
+
+ if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
+ err = host->bus_ops->awake(host);
+
+ mmc_bus_put(host);
+
+ return err;
+}
+EXPORT_SYMBOL(mmc_card_awake);
+
+int mmc_card_sleep(struct mmc_host *host)
+{
+ int err = -ENOSYS;
+
+ mmc_bus_get(host);
+
+ if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
+ err = host->bus_ops->sleep(host);
+
+ mmc_bus_put(host);
+
+ return err;
+}
+EXPORT_SYMBOL(mmc_card_sleep);
+
+int mmc_card_can_sleep(struct mmc_host *host)
+{
+ struct mmc_card *card = host->card;
+
+ if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_card_can_sleep);
+
#ifdef CONFIG_PM

/**
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index f7eb4c4..c386348 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -16,6 +16,8 @@
#define MMC_CMD_RETRIES 3

struct mmc_bus_ops {
+ int (*awake)(struct mmc_host *);
+ int (*sleep)(struct mmc_host *);
void (*remove)(struct mmc_host *);
void (*detect)(struct mmc_host *);
void (*suspend)(struct mmc_host *);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 01f7226..8e2e3d2 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -160,7 +160,6 @@ static int mmc_read_ext_csd(struct mmc_card *card)
{
int err;
u8 *ext_csd;
- unsigned int ext_csd_struct;

BUG_ON(!card);

@@ -207,16 +206,16 @@ static int mmc_read_ext_csd(struct mmc_card *card)
goto out;
}

- ext_csd_struct = ext_csd[EXT_CSD_REV];
- if (ext_csd_struct > 3) {
+ card->ext_csd.rev = ext_csd[EXT_CSD_REV];
+ if (card->ext_csd.rev > 3) {
printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
"version %d\n", mmc_hostname(card->host),
- ext_csd_struct);
+ card->ext_csd.rev);
err = -EINVAL;
goto out;
}

- if (ext_csd_struct >= 2) {
+ if (card->ext_csd.rev >= 2) {
card->ext_csd.sectors =
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
@@ -241,6 +240,15 @@ static int mmc_read_ext_csd(struct mmc_card *card)
goto out;
}

+ if (card->ext_csd.rev >= 3) {
+ u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT];
+
+ /* Sleep / awake timeout in 100ns units */
+ if (sa_shift > 0 && sa_shift <= 0x17)
+ card->ext_csd.sa_timeout =
+ 1 << ext_csd[EXT_CSD_S_A_TIMEOUT];
+ }
+
out:
kfree(ext_csd);

@@ -557,9 +565,41 @@ static void mmc_power_restore(struct mmc_host *host)
mmc_release_host(host);
}

+static int mmc_sleep(struct mmc_host *host)
+{
+ struct mmc_card *card = host->card;
+ int err = -ENOSYS;
+
+ if (card && card->ext_csd.rev >= 3) {
+ err = mmc_card_sleepawake(host, 1);
+ if (err < 0)
+ pr_debug("%s: Error %d while putting card into sleep",
+ mmc_hostname(host), err);
+ }
+
+ return err;
+}
+
+static int mmc_awake(struct mmc_host *host)
+{
+ struct mmc_card *card = host->card;
+ int err = -ENOSYS;
+
+ if (card && card->ext_csd.rev >= 3) {
+ err = mmc_card_sleepawake(host, 0);
+ if (err < 0)
+ pr_debug("%s: Error %d while awaking sleeping card",
+ mmc_hostname(host), err);
+ }
+
+ return err;
+}
+
#ifdef CONFIG_MMC_UNSAFE_RESUME

static const struct mmc_bus_ops mmc_ops = {
+ .awake = mmc_awake,
+ .sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = mmc_suspend,
@@ -575,6 +615,8 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
#else

static const struct mmc_bus_ops mmc_ops = {
+ .awake = mmc_awake,
+ .sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = NULL,
@@ -583,6 +625,8 @@ static const struct mmc_bus_ops mmc_ops = {
};

static const struct mmc_bus_ops mmc_ops_unsafe = {
+ .awake = mmc_awake,
+ .sleep = mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = mmc_suspend,
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 34ce270..355c604 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -57,6 +57,42 @@ int mmc_deselect_cards(struct mmc_host *host)
return _mmc_select_card(host, NULL);
}

+int mmc_card_sleepawake(struct mmc_host *host, int sleep)
+{
+ struct mmc_command cmd;
+ struct mmc_card *card = host->card;
+ int err;
+
+ if (sleep)
+ mmc_deselect_cards(host);
+
+ memset(&cmd, 0, sizeof(struct mmc_command));
+
+ cmd.opcode = MMC_SLEEP_AWAKE;
+ cmd.arg = card->rca << 16;
+ if (sleep)
+ cmd.arg |= 1 << 15;
+
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(host, &cmd, 0);
+ if (err)
+ return err;
+
+ /*
+ * If the host does not wait while the card signals busy, then we will
+ * will have to wait the sleep/awake timeout. Note, we cannot use the
+ * SEND_STATUS command to poll the status because that command (and most
+ * others) is invalid while the card sleeps.
+ */
+ if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
+ mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));
+
+ if (!sleep)
+ err = mmc_select_card(card);
+
+ return err;
+}
+
int mmc_go_idle(struct mmc_host *host)
{
int err;
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 17854bf..653eb8e 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -25,6 +25,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_cid(struct mmc_host *host, u32 *cid);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
+int mmc_card_sleepawake(struct mmc_host *host, int sleep);

#endif

diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 403aa50..58f5917 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -40,6 +40,8 @@ struct mmc_csd {
};

struct mmc_ext_csd {
+ u8 rev;
+ unsigned int sa_timeout; /* Units: 100ns */
unsigned int hs_max_dtr;
unsigned int sectors;
};
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index c1cbe59..81bb423 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -149,6 +149,7 @@ struct mmc_host {
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
#define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
+#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */

/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
@@ -240,6 +241,10 @@ struct regulator;
int mmc_regulator_get_ocrmask(struct regulator *supply);
int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit);

+int mmc_card_awake(struct mmc_host *host);
+int mmc_card_sleep(struct mmc_host *host);
+int mmc_card_can_sleep(struct mmc_host *host);
+
int mmc_host_enable(struct mmc_host *host);
int mmc_host_disable(struct mmc_host *host);
int mmc_host_lazy_disable(struct mmc_host *host);
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 14b81f3..b2b4095 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -31,6 +31,7 @@
#define MMC_ALL_SEND_CID 2 /* bcr R2 */
#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */
#define MMC_SET_DSR 4 /* bc [31:16] RCA */
+#define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */
#define MMC_SWITCH 6 /* ac [31:0] See below R1b */
#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */
#define MMC_SEND_EXT_CSD 8 /* adtc R1 */
@@ -254,6 +255,7 @@ struct _mmc_csd {
#define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_REV 192 /* RO */
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
+#define EXT_CSD_S_A_TIMEOUT 217

/*
* EXT_CSD field definitions
--
1.5.6.3

2009-07-28 10:39:13

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 6/32] mmc: power off once at removal

>From cf9505241b8ac5da1a3ebd0b77c1e0f5a149e461 Mon Sep 17 00:00:00 2001
From: Denis Karpov <[email protected]>
Date: Thu, 14 May 2009 09:11:38 +0200
Subject: [PATCH] mmc: power off once at removal

Fix MMC host stop sequence: power off once.

Signed-off-by: Denis Karpov <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/core/core.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 0611bf7..8d03da4 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1137,6 +1137,8 @@ void mmc_stop_host(struct mmc_host *host)
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
+ mmc_bus_put(host);
+ return;
}
mmc_bus_put(host);

--
1.5.6.3

2009-07-28 10:39:30

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 7/32] mmc: add host capabilities for SD only and MMC only

>From f22d0fb3d25771f4e380176e99d55068b78f0c17 Mon Sep 17 00:00:00 2001
From: Matt Fleming <[email protected]>
Date: Sat, 25 Jul 2009 23:52:37 +0000
Subject: [PATCH] mmc: add host capabilities for SD only and MMC only

Some hosts can accept only certain types of cards. For example, an eMMC
is MMC only and a uSD slot may be SD only. However the MMC card scanning
logic checks for all card types one by one.

Add host capabilities to specify which card types can be used, and amend
the card scanning logic to skip scanning for those types which cannot be
used.

Signed-off-by: Matt Fleming <[email protected]>
Cc: Adrian Hunter <[email protected]>
Cc: Ian Molton <[email protected]>
Cc: "Roberto A. Foglietta" <[email protected]>
Cc: Jarkko Lavinen <[email protected]>
Cc: Denis Karpov <[email protected]>
Cc: Pierre Ossman <[email protected]>
Cc: Andrew Morton <[email protected]>
---
drivers/mmc/core/core.c | 16 +++++++++++++++-
drivers/mmc/host/at91_mci.c | 2 +-
drivers/mmc/host/atmel-mci.c | 1 +
drivers/mmc/host/au1xmmc.c | 3 ++-
drivers/mmc/host/cb710-mmc.c | 3 ++-
drivers/mmc/host/imxmmc.c | 3 ++-
drivers/mmc/host/mmc_spi.c | 2 +-
drivers/mmc/host/mmci.c | 2 ++
drivers/mmc/host/mvsdio.c | 3 ++-
drivers/mmc/host/mxcmmc.c | 3 ++-
drivers/mmc/host/omap.c | 2 +-
drivers/mmc/host/omap_hsmmc.c | 2 ++
drivers/mmc/host/pxamci.c | 2 +-
drivers/mmc/host/s3cmci.c | 3 ++-
drivers/mmc/host/sdhci.c | 2 +-
drivers/mmc/host/sdricoh_cs.c | 3 ++-
drivers/mmc/host/tifm_sd.c | 3 ++-
drivers/mmc/host/tmio_mmc.c | 3 ++-
drivers/mmc/host/via-sdmmc.c | 3 ++-
drivers/mmc/host/wbsd.c | 3 ++-
include/linux/mmc/host.h | 3 +++
21 files changed, 50 insertions(+), 17 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 8d03da4..5fe51ff 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1069,7 +1069,11 @@ void mmc_rescan(struct work_struct *work)
mmc_power_up(host);
mmc_go_idle(host);

- mmc_send_if_cond(host, host->ocr_avail);
+ if ((host->caps & MMC_CAP_SDIO) || (host->caps & MMC_CAP_SD))
+ mmc_send_if_cond(host, host->ocr_avail);
+
+ if (!(host->caps & MMC_CAP_SDIO))
+ goto not_sdio;

/*
* First we search for SDIO...
@@ -1081,6 +1085,10 @@ void mmc_rescan(struct work_struct *work)
goto out;
}

+not_sdio:
+ if (!(host->caps & MMC_CAP_SD))
+ goto not_sd;
+
/*
* ...then normal SD...
*/
@@ -1091,6 +1099,10 @@ void mmc_rescan(struct work_struct *work)
goto out;
}

+not_sd:
+ if (!(host->caps & MMC_CAP_MMC))
+ goto not_mmc;
+
/*
* ...and finally MMC.
*/
@@ -1101,6 +1113,8 @@ void mmc_rescan(struct work_struct *work)
goto out;
}

+not_mmc:
+
mmc_release_host(host);
mmc_power_off(host);

diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index e556d42..aa541a5 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -1007,7 +1007,7 @@ static int __init at91_mci_probe(struct platform_device *pdev)
mmc->f_min = 375000;
mmc->f_max = 25000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_SDIO_IRQ;
+ mmc->caps = MMC_CAP_SDIO_IRQ | MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;

mmc->max_blk_size = 4095;
mmc->max_blk_count = mmc->max_req_size;
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 7b603e4..907841a 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -1467,6 +1467,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512);
mmc->f_max = host->bus_hz / 2;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;
if (slot_data->bus_width >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;

diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index d3f5561..4aae609 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -1003,7 +1003,8 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
mmc->max_blk_count = 512;

mmc->ocr_avail = AU1XMMC_OCR;
- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
+ MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;

host->status = HOST_S_IDLE;

diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c
index 11efefb..622b420 100644
--- a/drivers/mmc/host/cb710-mmc.c
+++ b/drivers/mmc/host/cb710-mmc.c
@@ -721,7 +721,8 @@ static int __devinit cb710_mmc_init(struct platform_device *pdev)
mmc->f_max = val;
mmc->f_min = val >> cb710_clock_divider_log2[CB710_MAX_DIVIDER_IDX];
mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO |
+ MMC_CAP_SD | MMC_CAP_MMC;

reader = mmc_priv(mmc);

diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c
index e0be21a..b6653ba 100644
--- a/drivers/mmc/host/imxmmc.c
+++ b/drivers/mmc/host/imxmmc.c
@@ -962,7 +962,8 @@ static int __init imxmci_probe(struct platform_device *pdev)
mmc->f_min = 150000;
mmc->f_max = CLK_RATE/2;
mmc->ocr_avail = MMC_VDD_32_33;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO |
+ MMC_CAP_SD | MMC_CAP_MMC;

/* MMC core transfer sizes tunable parameters */
mmc->max_hw_segs = 64;
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index a461017..e1a3c7f 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1386,7 +1386,7 @@ static int mmc_spi_probe(struct spi_device *spi)
mmc->max_req_size = MMC_SPI_BLOCKSATONCE * MMC_SPI_BLOCKSIZE;
mmc->max_blk_count = MMC_SPI_BLOCKSATONCE;

- mmc->caps = MMC_CAP_SPI;
+ mmc->caps = MMC_CAP_SPI | MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;

/* SPI doesn't need the lowspeed device identification thing for
* MMC or SD cards, since it never comes up in open drain mode.
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index e1aa847..55e882b 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -557,6 +557,8 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
mmc->f_max = min(host->mclk, fmax);
mmc->ocr_avail = plat->ocr_mask;

+ mmc->caps = MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;
+
/*
* We can do SGIO
*/
diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c
index 34e2348..1c3f4aa 100644
--- a/drivers/mmc/host/mvsdio.c
+++ b/drivers/mmc/host/mvsdio.c
@@ -734,7 +734,8 @@ static int __init mvsd_probe(struct platform_device *pdev)

mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
- MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
+ MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
+ MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;

mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX);
mmc->f_max = maxfreq;
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index bc14bb1..d67d639 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -700,7 +700,8 @@ static int mxcmci_probe(struct platform_device *pdev)
}

mmc->ops = &mxcmci_ops;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MMC |
+ MMC_CAP_SD | MMC_CAP_SDIO;

/* MMC core transfer sizes tunable parameters */
mmc->max_hw_segs = 64;
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index e7a331d..d6d23e5 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -1314,7 +1314,7 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)

host->slots[id] = slot;

- mmc->caps = 0;
+ mmc->caps = MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;
if (host->pdata->slots[id].wires >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 1cf9cfb..056bcaf 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1080,6 +1080,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
else if (pdata->slots[host->slot_id].wires >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;

+ mmc->caps |= MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;
+
omap_hsmmc_init(host);

/* Select DMA lines */
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index e55ac79..1a71da3 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -598,7 +598,7 @@ static int pxamci_probe(struct platform_device *pdev)

pxamci_init_ocr(host);

- mmc->caps = 0;
+ mmc->caps = MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;
host->cmdat = 0;
if (!cpu_is_pxa25x()) {
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 8c08cd7..9d73289 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -1376,7 +1376,8 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)

mmc->ops = &s3cmci_ops;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO |
+ MMC_CAP_SD | MMC_CAP_MMC;
mmc->f_min = host->clk_rate / (host->clk_div * 256);
mmc->f_max = host->clk_rate / host->clk_div;

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 6779b4e..6334938 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1768,7 +1768,7 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->ops = &sdhci_ops;
mmc->f_min = host->max_clk / 256;
mmc->f_max = host->max_clk;
- mmc->caps = MMC_CAP_SDIO_IRQ;
+ mmc->caps = MMC_CAP_SDIO_IRQ | MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;

if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA))
mmc->caps |= MMC_CAP_4_BIT_DATA;
diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c
index cb41e9c..7a24bb1 100644
--- a/drivers/mmc/host/sdricoh_cs.c
+++ b/drivers/mmc/host/sdricoh_cs.c
@@ -443,7 +443,8 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev,
mmc->f_min = 450000;
mmc->f_max = 24000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps |= MMC_CAP_4_BIT_DATA;
+ mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO |
+ MMC_CAP_SD | MMC_CAP_MMC;

mmc->max_seg_size = 1024 * 512;
mmc->max_blk_size = 512;
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index 82554dd..cb170d5 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -973,7 +973,8 @@ static int tifm_sd_probe(struct tifm_dev *sock)

mmc->ops = &tifm_sd_ops;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO |
+ MMC_CAP_SD | MMC_CAP_MMC;
mmc->f_min = 20000000 / 60;
mmc->f_max = 24000000;

diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 91991b4..a708bb6 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -553,7 +553,8 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
goto unmap_ctl;

mmc->ops = &tmio_mmc_ops;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO |
+ MMC_CAP_SD | MMC_CAP_MMC;
mmc->f_max = pdata->hclk;
mmc->f_min = mmc->f_max / 512;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index 632858a..c6c652a 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -1046,7 +1046,8 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
mmc->f_min = VIA_CRDR_MIN_CLOCK;
mmc->f_max = VIA_CRDR_MAX_CLOCK;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
+ MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;
mmc->ops = &via_sdc_ops;

/*Hardware cannot do scatter lists*/
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index 89bf8cd..16f0e5b 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -1219,7 +1219,8 @@ static int __devinit wbsd_alloc_mmc(struct device *dev)
mmc->f_min = 375000;
mmc->f_max = 24000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO |
+ MMC_CAP_SD | MMC_CAP_MMC;

spin_lock_init(&host->lock);

diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 81bb423..ca6d733 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -150,6 +150,9 @@ struct mmc_host {
#define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
+#define MMC_CAP_SDIO (1 << 10) /* Card can be SDIO */
+#define MMC_CAP_SD (1 << 11) /* Card can be SD */
+#define MMC_CAP_MMC (1 << 12) /* Card can be MMC */

/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
--
1.5.6.3

2009-07-28 10:40:28

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 17/32] omap_hsmmc: make use of new MMC_CAP_NONREMOVABLE host capability

>From a5af29d59b76a8c09c3d02e46ab25754c1bb2ec6 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Mon, 11 May 2009 10:06:38 +0300
Subject: [PATCH] omap_hsmmc: make use of new MMC_CAP_NONREMOVABLE host capability

Let the board specify that a card is nonremovable e.g. eMMC

Signed-off-by: Adrian Hunter <[email protected]>
---
arch/arm/mach-omap2/mmc-twl4030.c | 3 +++
arch/arm/mach-omap2/mmc-twl4030.h | 1 +
arch/arm/plat-omap/include/mach/mmc.h | 3 +++
drivers/mmc/host/omap_hsmmc.c | 3 +++
4 files changed, 10 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c
index 5be3111..67c22c6 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.c
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -409,6 +409,9 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
} else
mmc->slots[0].gpio_wp = -EINVAL;

+ if (c->nonremovable)
+ mmc->slots[0].nonremovable = 1;
+
/* NOTE: MMC slots should have a Vcc regulator set up.
* This may be from a TWL4030-family chip, another
* controllable regulator, or a fixed supply.
diff --git a/arch/arm/mach-omap2/mmc-twl4030.h b/arch/arm/mach-omap2/mmc-twl4030.h
index 3807c45..75b0c64 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.h
+++ b/arch/arm/mach-omap2/mmc-twl4030.h
@@ -12,6 +12,7 @@ struct twl4030_hsmmc_info {
bool transceiver; /* MMC-2 option */
bool ext_clock; /* use external pin for input clock */
bool cover_only; /* No card detect - just cover switch */
+ bool nonremovable; /* Nonremovable e.g. eMMC */
int gpio_cd; /* or -EINVAL */
int gpio_wp; /* or -EINVAL */
char *name; /* or NULL for default */
diff --git a/arch/arm/plat-omap/include/mach/mmc.h b/arch/arm/plat-omap/include/mach/mmc.h
index 2f7cf31..bab486c 100644
--- a/arch/arm/plat-omap/include/mach/mmc.h
+++ b/arch/arm/plat-omap/include/mach/mmc.h
@@ -83,6 +83,9 @@ struct omap_mmc_platform_data {
/* use the internal clock */
unsigned internal_clock:1;

+ /* nonremovable e.g. eMMC */
+ unsigned nonremovable:1;
+
int switch_pin; /* gpio (card detect) */
int gpio_wp; /* gpio (write protect) */

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 6222940..e618bc0 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1383,6 +1383,9 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
else if (pdata->slots[host->slot_id].wires >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;

+ if (pdata->slots[host->slot_id].nonremovable)
+ mmc->caps |= MMC_CAP_NONREMOVABLE;
+
mmc->caps |= MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;

omap_hsmmc_init(host);
--
1.5.6.3

2009-07-28 10:39:57

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 13/32] omap_hsmmc: context save/restore support

>From 43e9fa346d7e386328876a8535dc8619bd1f47ae Mon Sep 17 00:00:00 2001
From: Denis Karpov <[email protected]>
Date: Wed, 22 Apr 2009 16:04:25 +0200
Subject: [PATCH] omap_hsmmc: context save/restore support

Keep the context over PM dynamic OFF states.

Signed-off-by: Denis Karpov <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 194 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 184 insertions(+), 10 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index c28d055..ac1a3bf 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -37,6 +37,7 @@

/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSCONFIG 0x0010
+#define OMAP_HSMMC_SYSSTATUS 0x0014
#define OMAP_HSMMC_CON 0x002C
#define OMAP_HSMMC_BLK 0x0104
#define OMAP_HSMMC_ARG 0x0108
@@ -94,6 +95,8 @@
#define DUAL_VOLT_OCR_BIT 7
#define SRC (1 << 25)
#define SRD (1 << 26)
+#define SOFTRESET (1 << 1)
+#define RESETDONE (1 << 0)

/*
* FIXME: Most likely all the data using these _DEVID defines should come
@@ -152,6 +155,8 @@ struct mmc_omap_host {
int slot_id;
int dbclk_enabled;
int response_busy;
+ int context_loss;
+
struct omap_mmc_platform_data *pdata;
};

@@ -166,6 +171,166 @@ static void omap_mmc_stop_clock(struct mmc_omap_host *host)
dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
}

+#ifdef CONFIG_PM
+
+/*
+ * Restore the MMC host context, if it was lost as result of a
+ * power state change.
+ */
+static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
+{
+ struct mmc_ios *ios = &host->mmc->ios;
+ struct omap_mmc_platform_data *pdata = host->pdata;
+ int context_loss = 0;
+ u32 hctl, capa, con;
+ u16 dsor = 0;
+ unsigned long timeout;
+
+ if (pdata->get_context_loss_count) {
+ context_loss = pdata->get_context_loss_count(host->dev);
+ if (context_loss < 0)
+ return 1;
+ }
+
+ dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
+ context_loss == host->context_loss ? "not " : "");
+ if (host->context_loss == context_loss)
+ return 1;
+
+ /* Wait for hardware reset */
+ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+ while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE
+ && time_before(jiffies, timeout))
+ ;
+
+ /* Do software reset */
+ OMAP_HSMMC_WRITE(host->base, SYSCONFIG, SOFTRESET);
+ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+ while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE
+ && time_before(jiffies, timeout))
+ ;
+
+ OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
+ OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
+
+ if (host->id == OMAP_MMC1_DEVID) {
+ if (host->power_mode != MMC_POWER_OFF &&
+ (1 << ios->vdd) <= MMC_VDD_23_24)
+ hctl = SDVS18;
+ else
+ hctl = SDVS30;
+ capa = VS30 | VS18;
+ } else {
+ hctl = SDVS18;
+ capa = VS18;
+ }
+
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | hctl);
+
+ OMAP_HSMMC_WRITE(host->base, CAPA,
+ OMAP_HSMMC_READ(host->base, CAPA) | capa);
+
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+
+ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+ while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP
+ && time_before(jiffies, timeout))
+ ;
+
+ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+ OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
+ OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
+
+ /* Do not initialize card-specific things if the power is off */
+ if (host->power_mode == MMC_POWER_OFF)
+ goto out;
+
+ con = OMAP_HSMMC_READ(host->base, CON);
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_8:
+ OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
+ break;
+ case MMC_BUS_WIDTH_4:
+ OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
+ break;
+ case MMC_BUS_WIDTH_1:
+ OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
+ break;
+ }
+
+ if (ios->clock) {
+ dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
+ if (dsor < 1)
+ dsor = 1;
+
+ if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
+ dsor++;
+
+ if (dsor > 250)
+ dsor = 250;
+ }
+
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
+ OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
+ OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16));
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
+ OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
+
+ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+ while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
+ && time_before(jiffies, timeout))
+ ;
+
+ OMAP_HSMMC_WRITE(host->base, SYSCTL,
+ OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
+
+ con = OMAP_HSMMC_READ(host->base, CON);
+ if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ OMAP_HSMMC_WRITE(host->base, CON, con | OD);
+ else
+ OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
+out:
+ host->context_loss = context_loss;
+
+ dev_dbg(mmc_dev(host->mmc), "context is restored\n");
+ return 0;
+}
+
+/*
+ * Save the MMC host context (store the number of power state changes so far).
+ */
+static void omap_mmc_save_ctx(struct mmc_omap_host *host)
+{
+ struct omap_mmc_platform_data *pdata = host->pdata;
+ int context_loss;
+
+ if (pdata->get_context_loss_count) {
+ context_loss = pdata->get_context_loss_count(host->dev);
+ if (context_loss < 0)
+ return;
+ host->context_loss = context_loss;
+ }
+}
+
+#else
+
+static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
+{
+ return 0;
+}
+
+static void omap_mmc_save_ctx(struct mmc_omap_host *host)
+{
+}
+
+#endif
+
/*
* Send init stream sequence to card
* before sending IDLE command
@@ -823,6 +988,7 @@ static int omap_mmc_enable(struct mmc_host *mmc)
if (err)
return err;
dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
+ omap_mmc_restore_ctx(host);
return 0;
}

@@ -830,6 +996,7 @@ static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
{
struct mmc_omap_host *host = mmc_priv(mmc);

+ omap_mmc_save_ctx(host);
clk_disable(host->fclk);
dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
return 0;
@@ -934,7 +1101,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

/* Wait till the ICS bit is set */
timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
- while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2
+ while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
&& time_before(jiffies, timeout))
msleep(1);

@@ -1014,12 +1181,19 @@ static int mmc_regs_show(struct seq_file *s, void *data)
{
struct mmc_host *mmc = s->private;
struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_mmc_platform_data *pdata = host->pdata;
+ int context_loss = 0;
+
+ if (pdata->get_context_loss_count)
+ context_loss = pdata->get_context_loss_count(host->dev);

seq_printf(s, "mmc%d:\n"
" enabled:\t%d\n"
" nesting_cnt:\t%d\n"
+ " ctx_loss:\t%d:%d\n"
"\nregs:\n",
- mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt);
+ mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt,
+ host->context_loss, context_loss);

if (clk_enable(host->fclk) != 0) {
seq_printf(s, "can't read the regs\n");
@@ -1144,6 +1318,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
goto err1;
}

+ omap_mmc_save_ctx(host);
+
mmc->caps |= MMC_CAP_DISABLE;
mmc_set_disable_delay(mmc, 100);
if (mmc_host_enable(host->mmc) != 0) {
@@ -1380,21 +1556,19 @@ static int omap_mmc_resume(struct platform_device *pdev)
return 0;

if (host) {
-
- if (mmc_host_enable(host->mmc) != 0)
- goto clk_en_err;
-
ret = clk_enable(host->iclk);
- if (ret) {
- mmc_host_disable(host->mmc);
- clk_put(host->fclk);
+ if (ret)
goto clk_en_err;
- }

if (clk_enable(host->dbclk) != 0)
dev_dbg(mmc_dev(host->mmc),
"Enabling debounce clk failed\n");

+ if (mmc_host_enable(host->mmc) != 0) {
+ clk_disable(host->iclk);
+ goto clk_en_err;
+ }
+
omap_hsmmc_init(host);

if (host->pdata->resume) {
--
1.5.6.3

2009-07-28 10:39:40

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 9/32] omap_hsmmc: add debugfs entry (host registers)

>From 4ea575689bc8933cb7370871fb10696c3b226b2d Mon Sep 17 00:00:00 2001
From: Denis Karpov <[email protected]>
Date: Wed, 22 Apr 2009 14:21:34 +0200
Subject: [PATCH] omap_hsmmc: add debugfs entry (host registers)

Adds <debugfs_root>/kernel/debug/mmc<N>/regs entry,
contents show registers' state and some driver internal
state variables.

Signed-off-by: Denis Karpov <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 57 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 57 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 056bcaf..0444de1 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -17,6 +17,8 @@

#include <linux/module.h>
#include <linux/init.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -967,6 +969,59 @@ static struct mmc_host_ops mmc_omap_ops = {
/* NYET -- enable_sdio_irq */
};

+#ifdef CONFIG_DEBUG_FS
+
+static int mmc_regs_show(struct seq_file *s, void *data)
+{
+ struct mmc_host *mmc = s->private;
+ struct mmc_omap_host *host = mmc_priv(mmc);
+
+ seq_printf(s, "mmc%d regs:\n", mmc->index);
+
+ seq_printf(s, "SYSCONFIG:\t0x%08x\n",
+ OMAP_HSMMC_READ(host->base, SYSCONFIG));
+ seq_printf(s, "CON:\t\t0x%08x\n",
+ OMAP_HSMMC_READ(host->base, CON));
+ seq_printf(s, "HCTL:\t\t0x%08x\n",
+ OMAP_HSMMC_READ(host->base, HCTL));
+ seq_printf(s, "SYSCTL:\t\t0x%08x\n",
+ OMAP_HSMMC_READ(host->base, SYSCTL));
+ seq_printf(s, "IE:\t\t0x%08x\n",
+ OMAP_HSMMC_READ(host->base, IE));
+ seq_printf(s, "ISE:\t\t0x%08x\n",
+ OMAP_HSMMC_READ(host->base, ISE));
+ seq_printf(s, "CAPA:\t\t0x%08x\n",
+ OMAP_HSMMC_READ(host->base, CAPA));
+ return 0;
+}
+
+static int mmc_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mmc_regs_show, inode->i_private);
+}
+
+static const struct file_operations mmc_regs_fops = {
+ .open = mmc_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void omap_mmc_debugfs(struct mmc_host *mmc)
+{
+ if (mmc->debugfs_root)
+ debugfs_create_file("regs", S_IRUSR, mmc->debugfs_root,
+ mmc, &mmc_regs_fops);
+}
+
+#else
+
+static void omap_mmc_debugfs(struct mmc_host *mmc)
+{
+}
+
+#endif
+
static int __init omap_mmc_probe(struct platform_device *pdev)
{
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
@@ -1152,6 +1207,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
goto err_cover_switch;
}

+ omap_mmc_debugfs(mmc);
+
return 0;

err_cover_switch:
--
1.5.6.3

2009-07-28 10:40:37

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 19/32] ARM: OMAP: mmc-twl4030: add regulator sleep / wake function

>From b5f863cd167b6e3a1fb06be4e51513964fa83cb4 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Wed, 8 Jul 2009 13:20:30 +0300
Subject: [PATCH] ARM: OMAP: mmc-twl4030: add regulator sleep / wake function

Add the ability for the driver to put the card power
regulators to sleep and wake them up again.

Signed-off-by: Adrian Hunter <[email protected]>
---
arch/arm/mach-omap2/mmc-twl4030.c | 57 +++++++++++++++++++++++++++++++++
arch/arm/plat-omap/include/mach/mmc.h | 2 +
2 files changed, 59 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c
index 3678f0e..f95c702 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.c
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -334,6 +334,61 @@ static int twl_mmc23_set_power(struct device *dev, int slot, int power_on, int v
return ret;
}

+static int twl_mmc1_set_sleep(struct device *dev, int slot, int sleep, int vdd,
+ int cardsleep)
+{
+ struct twl_mmc_controller *c = &hsmmc[0];
+ int mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
+
+ return regulator_set_mode(c->vcc, mode);
+}
+
+static int twl_mmc23_set_sleep(struct device *dev, int slot, int sleep, int vdd,
+ int cardsleep)
+{
+ struct twl_mmc_controller *c = NULL;
+ struct omap_mmc_platform_data *mmc = dev->platform_data;
+ int i, err, mode;
+
+ for (i = 1; i < ARRAY_SIZE(hsmmc); i++) {
+ if (mmc == hsmmc[i].mmc) {
+ c = &hsmmc[i];
+ break;
+ }
+ }
+
+ if (c == NULL)
+ return -ENODEV;
+
+ /*
+ * If we don't see a Vcc regulator, assume it's a fixed
+ * voltage always-on regulator.
+ */
+ if (!c->vcc)
+ return 0;
+
+ mode = sleep ? REGULATOR_MODE_STANDBY : REGULATOR_MODE_NORMAL;
+
+ if (!c->vcc_aux)
+ return regulator_set_mode(c->vcc, mode);
+
+ if (cardsleep) {
+ /* VCC can be turned off if card is asleep */
+ struct regulator *vcc_aux = c->vcc_aux;
+
+ c->vcc_aux = NULL;
+ if (sleep)
+ err = twl_mmc23_set_power(dev, slot, 0, 0);
+ else
+ err = twl_mmc23_set_power(dev, slot, 1, vdd);
+ c->vcc_aux = vcc_aux;
+ } else
+ err = regulator_set_mode(c->vcc, mode);
+ if (err)
+ return err;
+ return regulator_set_mode(c->vcc_aux, mode);
+}
+
static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata;

void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
@@ -427,6 +482,7 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
case 1:
/* on-chip level shifting via PBIAS0/PBIAS1 */
mmc->slots[0].set_power = twl_mmc1_set_power;
+ mmc->slots[0].set_sleep = twl_mmc1_set_sleep;
break;
case 2:
if (c->ext_clock)
@@ -437,6 +493,7 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
case 3:
/* off-chip level shifting, or none */
mmc->slots[0].set_power = twl_mmc23_set_power;
+ mmc->slots[0].set_sleep = twl_mmc23_set_sleep;
break;
default:
pr_err("MMC%d configuration not supported!\n", c->mmc);
diff --git a/arch/arm/plat-omap/include/mach/mmc.h b/arch/arm/plat-omap/include/mach/mmc.h
index 82f1e29..9390297 100644
--- a/arch/arm/plat-omap/include/mach/mmc.h
+++ b/arch/arm/plat-omap/include/mach/mmc.h
@@ -95,6 +95,8 @@ struct omap_mmc_platform_data {
int (* set_bus_mode)(struct device *dev, int slot, int bus_mode);
int (* set_power)(struct device *dev, int slot, int power_on, int vdd);
int (* get_ro)(struct device *dev, int slot);
+ int (*set_sleep)(struct device *dev, int slot, int sleep,
+ int vdd, int cardsleep);

/* return MMC cover switch state, can be NULL if not supported.
*
--
1.5.6.3

2009-07-28 10:40:10

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 10/32] omap_hsmmc: make use of new enable/disable interface

>From 4ce5c79ed1c42938ee1d42d7cf172e6d1265106d Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Thu, 23 Apr 2009 10:01:29 +0300
Subject: [PATCH] omap_hsmmc: make use of new enable/disable interface

For the moment enable / disable just turns the fclk
on and off.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 68 +++++++++++++++++++++++++++++++++++------
1 files changed, 58 insertions(+), 10 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 0444de1..f9b7cfe 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -598,7 +598,9 @@ static void mmc_omap_detect(struct work_struct *work)
if (host->carddetect) {
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
} else {
+ mmc_host_enable(host->mmc);
mmc_omap_reset_controller_fsm(host, SRD);
+ mmc_host_lazy_disable(host->mmc);
mmc_detect_change(host->mmc, (HZ * 50) / 1000);
}
}
@@ -811,6 +813,27 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
return 0;
}

+static int omap_mmc_enable(struct mmc_host *mmc)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+ int err;
+
+ err = clk_enable(host->fclk);
+ if (err)
+ return err;
+ dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
+ return 0;
+}
+
+static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+
+ clk_disable(host->fclk);
+ dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
+ return 0;
+}
+
/*
* Request function. for read/write operation
*/
@@ -834,6 +857,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
unsigned long timeout;
u32 con;

+ mmc_host_enable(host->mmc);
+
switch (ios->power_mode) {
case MMC_POWER_OFF:
mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
@@ -912,6 +937,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
OMAP_HSMMC_WRITE(host->base, CON,
OMAP_HSMMC_READ(host->base, CON) | OD);
+
+ mmc_host_lazy_disable(host->mmc);
}

static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -962,6 +989,8 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)
}

static struct mmc_host_ops mmc_omap_ops = {
+ .enable = omap_mmc_enable,
+ .disable = omap_mmc_disable,
.request = omap_mmc_request,
.set_ios = omap_mmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
@@ -976,7 +1005,16 @@ static int mmc_regs_show(struct seq_file *s, void *data)
struct mmc_host *mmc = s->private;
struct mmc_omap_host *host = mmc_priv(mmc);

- seq_printf(s, "mmc%d regs:\n", mmc->index);
+ seq_printf(s, "mmc%d:\n"
+ " enabled:\t%d\n"
+ " nesting_cnt:\t%d\n"
+ "\nregs:\n",
+ mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt);
+
+ if (clk_enable(host->fclk) != 0) {
+ seq_printf(s, "can't read the regs\n");
+ goto err;
+ }

seq_printf(s, "SYSCONFIG:\t0x%08x\n",
OMAP_HSMMC_READ(host->base, SYSCONFIG));
@@ -992,6 +1030,9 @@ static int mmc_regs_show(struct seq_file *s, void *data)
OMAP_HSMMC_READ(host->base, ISE));
seq_printf(s, "CAPA:\t\t0x%08x\n",
OMAP_HSMMC_READ(host->base, CAPA));
+
+ clk_disable(host->fclk);
+err:
return 0;
}

@@ -1092,14 +1133,16 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
goto err1;
}

- if (clk_enable(host->fclk) != 0) {
+ mmc->caps |= MMC_CAP_DISABLE;
+ mmc_set_disable_delay(mmc, 100);
+ if (mmc_host_enable(host->mmc) != 0) {
clk_put(host->iclk);
clk_put(host->fclk);
goto err1;
}

if (clk_enable(host->iclk) != 0) {
- clk_disable(host->fclk);
+ mmc_host_disable(host->mmc);
clk_put(host->iclk);
clk_put(host->fclk);
goto err1;
@@ -1192,6 +1235,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);

+ mmc_host_lazy_disable(host->mmc);
+
mmc_add_host(mmc);

if (host->pdata->slots[host->slot_id].name != NULL) {
@@ -1220,7 +1265,7 @@ err_irq_cd:
err_irq_cd_init:
free_irq(host->irq, host);
err_irq:
- clk_disable(host->fclk);
+ mmc_host_disable(host->mmc);
clk_disable(host->iclk);
clk_put(host->fclk);
clk_put(host->iclk);
@@ -1245,6 +1290,7 @@ static int omap_mmc_remove(struct platform_device *pdev)
struct resource *res;

if (host) {
+ mmc_host_enable(host->mmc);
mmc_remove_host(host->mmc);
if (host->pdata->cleanup)
host->pdata->cleanup(&pdev->dev);
@@ -1253,7 +1299,7 @@ static int omap_mmc_remove(struct platform_device *pdev)
free_irq(mmc_slot(host).card_detect_irq, host);
flush_scheduled_work();

- clk_disable(host->fclk);
+ mmc_host_disable(host->mmc);
clk_disable(host->iclk);
clk_put(host->fclk);
clk_put(host->iclk);
@@ -1284,6 +1330,7 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;

if (host) {
+ mmc_host_enable(host->mmc);
ret = mmc_suspend_host(host->mmc, state);
if (ret == 0) {
host->suspended = 1;
@@ -1302,10 +1349,11 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)

OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
- clk_disable(host->fclk);
+ mmc_host_disable(host->mmc);
clk_disable(host->iclk);
clk_disable(host->dbclk);
- }
+ } else
+ mmc_host_disable(host->mmc);

}
return ret;
@@ -1322,13 +1370,12 @@ static int omap_mmc_resume(struct platform_device *pdev)

if (host) {

- ret = clk_enable(host->fclk);
- if (ret)
+ if (mmc_host_enable(host->mmc) != 0)
goto clk_en_err;

ret = clk_enable(host->iclk);
if (ret) {
- clk_disable(host->fclk);
+ mmc_host_disable(host->mmc);
clk_put(host->fclk);
goto clk_en_err;
}
@@ -1350,6 +1397,7 @@ static int omap_mmc_resume(struct platform_device *pdev)
ret = mmc_resume_host(host->mmc);
if (ret == 0)
host->suspended = 0;
+ mmc_host_lazy_disable(host->mmc);
}

return ret;
--
1.5.6.3

2009-07-28 10:39:44

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 11/32] ARM: OMAP: mmc-twl4030: add context loss counter support

>From 53270a4f00a21cdf90bc6d93fa4cfc229096cc15 Mon Sep 17 00:00:00 2001
From: Denis Karpov <[email protected]>
Date: Tue, 7 Jul 2009 15:54:44 +0300
Subject: [PATCH] ARM: OMAP: mmc-twl4030: add context loss counter support

PM dynamic OFF state results in context loss. That is, the host
controller has been powered off at some point, which means the
registers have been reset. The driver must detect when this
happens, and restore the context. This patch adds the means
to detect context loss.

Note, the PM side is not yet implemented.

Signed-off-by: Denis Karpov <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
arch/arm/mach-omap2/mmc-twl4030.c | 15 +++++++++++++++
arch/arm/plat-omap/include/mach/mmc.h | 3 +++
2 files changed, 18 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c
index 1541fd4..5be3111 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.c
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -192,6 +192,18 @@ static int twl_mmc_resume(struct device *dev, int slot)
#define twl_mmc_resume NULL
#endif

+#if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM)
+
+static int twl4030_mmc_get_context_loss(struct device *dev)
+{
+ /* FIXME: PM DPS not implemented yet */
+ return 0;
+}
+
+#else
+#define twl4030_mmc_get_context_loss NULL
+#endif
+
static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
int vdd)
{
@@ -384,6 +396,9 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
} else
mmc->slots[0].switch_pin = -EINVAL;

+ mmc->get_context_loss_count =
+ twl4030_mmc_get_context_loss;
+
/* write protect normally uses an OMAP gpio */
if (gpio_is_valid(c->gpio_wp)) {
gpio_request(c->gpio_wp, "mmc_wp");
diff --git a/arch/arm/plat-omap/include/mach/mmc.h b/arch/arm/plat-omap/include/mach/mmc.h
index 81d5b36..2f7cf31 100644
--- a/arch/arm/plat-omap/include/mach/mmc.h
+++ b/arch/arm/plat-omap/include/mach/mmc.h
@@ -59,6 +59,9 @@ struct omap_mmc_platform_data {
int (*suspend)(struct device *dev, int slot);
int (*resume)(struct device *dev, int slot);

+ /* Return context loss count due to PM states changing */
+ int (*get_context_loss_count)(struct device *dev);
+
u64 dma_mask;

struct omap_mmc_slot_data {
--
1.5.6.3

2009-07-28 10:40:47

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 18/32] omap_hsmmc: support for deeper power saving states

>From ad38a2788cc03924eebc353159561566dc4911e5 Mon Sep 17 00:00:00 2001
From: Denis Karpov <[email protected]>
Date: Mon, 11 May 2009 14:41:30 +0300
Subject: [PATCH] omap_hsmmc: support for deeper power saving states

Support for multi-level dynamic power saving states in omap_hsmmc
(ENABLED->DISABLED->OFF).
In the "deepest" state (OFF) we switch off the voltage regulators.

Signed-off-by: Denis Karpov <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
arch/arm/mach-omap2/mmc-twl4030.c | 3 +
arch/arm/mach-omap2/mmc-twl4030.h | 1 +
arch/arm/plat-omap/include/mach/mmc.h | 3 +
drivers/mmc/host/omap_hsmmc.c | 245 +++++++++++++++++++++++++++++----
4 files changed, 222 insertions(+), 30 deletions(-)

diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c
index 67c22c6..3678f0e 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.c
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -412,6 +412,9 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
if (c->nonremovable)
mmc->slots[0].nonremovable = 1;

+ if (c->power_saving)
+ mmc->slots[0].power_saving = 1;
+
/* NOTE: MMC slots should have a Vcc regulator set up.
* This may be from a TWL4030-family chip, another
* controllable regulator, or a fixed supply.
diff --git a/arch/arm/mach-omap2/mmc-twl4030.h b/arch/arm/mach-omap2/mmc-twl4030.h
index 75b0c64..a47e685 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.h
+++ b/arch/arm/mach-omap2/mmc-twl4030.h
@@ -13,6 +13,7 @@ struct twl4030_hsmmc_info {
bool ext_clock; /* use external pin for input clock */
bool cover_only; /* No card detect - just cover switch */
bool nonremovable; /* Nonremovable e.g. eMMC */
+ bool power_saving; /* Try to sleep or power off when possible */
int gpio_cd; /* or -EINVAL */
int gpio_wp; /* or -EINVAL */
char *name; /* or NULL for default */
diff --git a/arch/arm/plat-omap/include/mach/mmc.h b/arch/arm/plat-omap/include/mach/mmc.h
index bab486c..82f1e29 100644
--- a/arch/arm/plat-omap/include/mach/mmc.h
+++ b/arch/arm/plat-omap/include/mach/mmc.h
@@ -86,6 +86,9 @@ struct omap_mmc_platform_data {
/* nonremovable e.g. eMMC */
unsigned nonremovable:1;

+ /* Try to sleep or power off when possible */
+ unsigned power_saving:1;
+
int switch_pin; /* gpio (card detect) */
int gpio_wp; /* gpio (write protect) */

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index e618bc0..a3418b5 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -111,6 +111,10 @@
#define OMAP_MMC_MASTER_CLOCK 96000000
#define DRIVER_NAME "mmci-omap-hs"

+/* Timeouts for entering power saving states on inactivity, msec */
+#define OMAP_MMC_DISABLED_TIMEOUT 100
+#define OMAP_MMC_OFF_TIMEOUT 1000
+
/*
* One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known
@@ -155,6 +159,7 @@ struct mmc_omap_host {
int dbclk_enabled;
int response_busy;
int context_loss;
+ int dpm_state;

struct omap_mmc_platform_data *pdata;
};
@@ -985,29 +990,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
return 0;
}

-static int omap_mmc_enable(struct mmc_host *mmc)
-{
- struct mmc_omap_host *host = mmc_priv(mmc);
- int err;
-
- err = clk_enable(host->fclk);
- if (err)
- return err;
- dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
- omap_mmc_restore_ctx(host);
- return 0;
-}
-
-static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
-{
- struct mmc_omap_host *host = mmc_priv(mmc);
-
- omap_mmc_save_ctx(host);
- clk_disable(host->fclk);
- dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
- return 0;
-}
-
/*
* Request function. for read/write operation
*/
@@ -1061,6 +1043,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
host->power_mode = ios->power_mode;
}

+ /* FIXME: set registers based only on changes to ios */
+
con = OMAP_HSMMC_READ(host->base, CON);
switch (mmc->ios.bus_width) {
case MMC_BUS_WIDTH_8:
@@ -1133,7 +1117,10 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
else
OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);

- mmc_host_lazy_disable(host->mmc);
+ if (host->power_mode == MMC_POWER_OFF)
+ mmc_host_disable(host->mmc);
+ else
+ mmc_host_lazy_disable(host->mmc);
}

static int omap_hsmmc_get_cd(struct mmc_host *mmc)
@@ -1183,7 +1170,191 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)
set_sd_bus_power(host);
}

-static struct mmc_host_ops mmc_omap_ops = {
+/*
+ * Dynamic power saving handling, FSM:
+ * ENABLED -> DISABLED -> OFF
+ * ^___________| |
+ * |______________________|
+ *
+ * ENABLED: mmc host is fully functional
+ * DISABLED: fclk is off
+ * OFF: fclk is off,voltage regulator is off
+ *
+ * Transition handlers return the timeout for the next state transition
+ * or negative error.
+ */
+
+enum {ENABLED = 0, DISABLED, OFF};
+
+/* Handler for [ENABLED -> DISABLED] transition */
+static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
+{
+ omap_mmc_save_ctx(host);
+ clk_disable(host->fclk);
+ host->dpm_state = DISABLED;
+
+ dev_dbg(mmc_dev(host->mmc), "ENABLED -> DISABLED\n");
+
+ if (host->power_mode == MMC_POWER_OFF)
+ return 0;
+
+ return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT);
+}
+
+/* Handler for [DISABLED -> OFF] transition */
+static int omap_mmc_disabled_to_off(struct mmc_omap_host *host)
+{
+ int new_state;
+
+ dev_dbg(mmc_dev(host->mmc), "DISABLED -> OFF\n");
+
+ if (!mmc_try_claim_host(host->mmc))
+ return 0;
+
+ clk_enable(host->fclk);
+
+ omap_mmc_restore_ctx(host);
+
+ if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
+ mmc_slot(host).card_detect ||
+ (mmc_slot(host).get_cover_state &&
+ mmc_slot(host).get_cover_state(host->dev, host->slot_id))) {
+ mmc_power_save_host(host->mmc);
+ new_state = OFF;
+ } else
+ new_state = DISABLED;
+
+ OMAP_HSMMC_WRITE(host->base, ISE, 0);
+ OMAP_HSMMC_WRITE(host->base, IE, 0);
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
+
+ clk_disable(host->fclk);
+ clk_disable(host->iclk);
+ clk_disable(host->dbclk);
+
+ host->dpm_state = new_state;
+
+ mmc_release_host(host->mmc);
+
+ return 0;
+}
+
+/* Handler for [DISABLED -> ENABLED] transition */
+static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
+{
+ int err;
+
+ err = clk_enable(host->fclk);
+ if (err < 0)
+ return err;
+
+ omap_mmc_restore_ctx(host);
+
+ host->dpm_state = ENABLED;
+
+ dev_dbg(mmc_dev(host->mmc), "DISABLED -> ENABLED\n");
+
+ return 0;
+}
+
+/* Handler for [OFF -> ENABLED] transition */
+static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
+{
+ clk_enable(host->fclk);
+ clk_enable(host->iclk);
+
+ if (clk_enable(host->dbclk))
+ dev_dbg(mmc_dev(host->mmc),
+ "Enabling debounce clk failed\n");
+
+ omap_mmc_restore_ctx(host);
+ omap_hsmmc_init(host);
+ mmc_power_restore_host(host->mmc);
+
+ host->dpm_state = ENABLED;
+
+ dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n");
+
+ return 0;
+}
+
+/*
+ * Bring MMC host to ENABLED from any other PM state.
+ */
+static int omap_mmc_enable(struct mmc_host *mmc)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+
+ switch (host->dpm_state) {
+ case DISABLED:
+ return omap_mmc_disabled_to_enabled(host);
+ case OFF:
+ return omap_mmc_off_to_enabled(host);
+ default:
+ dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
+ return -EINVAL;
+ }
+}
+
+/*
+ * Bring MMC host in PM state (one level deeper).
+ */
+static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+
+ switch (host->dpm_state) {
+ case ENABLED: {
+ int delay;
+
+ delay = omap_mmc_enabled_to_disabled(host);
+ if (lazy || delay < 0)
+ return delay;
+ return 0;
+ }
+ case DISABLED:
+ return omap_mmc_disabled_to_off(host);
+ default:
+ dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
+ return -EINVAL;
+ }
+}
+
+static int omap_mmc_enable_fclk(struct mmc_host *mmc)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+ int err;
+
+ err = clk_enable(host->fclk);
+ if (err)
+ return err;
+ dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
+ omap_mmc_restore_ctx(host);
+ return 0;
+}
+
+static int omap_mmc_disable_fclk(struct mmc_host *mmc, int lazy)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+
+ omap_mmc_save_ctx(host);
+ clk_disable(host->fclk);
+ dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
+ return 0;
+}
+
+static const struct mmc_host_ops mmc_omap_ops = {
+ .enable = omap_mmc_enable_fclk,
+ .disable = omap_mmc_disable_fclk,
+ .request = omap_mmc_request,
+ .set_ios = omap_mmc_set_ios,
+ .get_cd = omap_hsmmc_get_cd,
+ .get_ro = omap_hsmmc_get_ro,
+ /* NYET -- enable_sdio_irq */
+};
+
+static const struct mmc_host_ops mmc_omap_ps_ops = {
.enable = omap_mmc_enable,
.disable = omap_mmc_disable,
.request = omap_mmc_request,
@@ -1207,15 +1378,22 @@ static int mmc_regs_show(struct seq_file *s, void *data)

seq_printf(s, "mmc%d:\n"
" enabled:\t%d\n"
+ " dpm_state:\t%d\n"
" nesting_cnt:\t%d\n"
" ctx_loss:\t%d:%d\n"
"\nregs:\n",
- mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt,
+ mmc->index, mmc->enabled ? 1 : 0,
+ host->dpm_state, mmc->nesting_cnt,
host->context_loss, context_loss);

+ if (host->suspended || host->dpm_state == OFF) {
+ seq_printf(s, "host suspended, can't read registers\n");
+ return 0;
+ }
+
if (clk_enable(host->fclk) != 0) {
seq_printf(s, "can't read the regs\n");
- goto err;
+ return 0;
}

seq_printf(s, "SYSCONFIG:\t0x%08x\n",
@@ -1234,7 +1412,7 @@ static int mmc_regs_show(struct seq_file *s, void *data)
OMAP_HSMMC_READ(host->base, CAPA));

clk_disable(host->fclk);
-err:
+
return 0;
}

@@ -1316,7 +1494,11 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);

- mmc->ops = &mmc_omap_ops;
+ if (pdata->slots[host->slot_id].power_saving)
+ mmc->ops = &mmc_omap_ps_ops;
+ else
+ mmc->ops = &mmc_omap_ops;
+
mmc->f_min = 400000;
mmc->f_max = 52000000;

@@ -1339,7 +1521,10 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
omap_mmc_save_ctx(host);

mmc->caps |= MMC_CAP_DISABLE;
- mmc_set_disable_delay(mmc, 100);
+ mmc_set_disable_delay(mmc, OMAP_MMC_DISABLED_TIMEOUT);
+ /* we start off in DISABLED state */
+ host->dpm_state = DISABLED;
+
if (mmc_host_enable(host->mmc) != 0) {
clk_put(host->iclk);
clk_put(host->fclk);
--
1.5.6.3

2009-07-28 10:40:54

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 20/32] omap_hsmmc: put MMC regulator to sleep

>From a5e206faac032a8e52721904dbdda0bd4ba01ecf Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Tue, 12 May 2009 20:54:51 +0300
Subject: [PATCH] omap_hsmmc: put MMC regulator to sleep

When a card is not in use, the voltage regulator can be put
to sleep. This is an alternative to powering the card off,
when powering off is not safe because the card might be
replaced without the driver being aware of it.

That situation happens if:
- the card is removable i.e. not eMMC
- and there is no card detect
- and there is a cover switch but the cover is open

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 59 +++++++++++++++++++++++++++++++++++++---
1 files changed, 54 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index a3418b5..7a17901 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -160,6 +160,7 @@ struct mmc_omap_host {
int response_busy;
int context_loss;
int dpm_state;
+ int vdd;

struct omap_mmc_platform_data *pdata;
};
@@ -1031,10 +1032,12 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
case MMC_POWER_OFF:
mmc_slot(host).set_power(host->dev, host->slot_id,
0, 0);
+ host->vdd = 0;
break;
case MMC_POWER_UP:
mmc_slot(host).set_power(host->dev, host->slot_id,
1, ios->vdd);
+ host->vdd = ios->vdd;
break;
case MMC_POWER_ON:
do_send_init_stream = 1;
@@ -1172,19 +1175,20 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)

/*
* Dynamic power saving handling, FSM:
- * ENABLED -> DISABLED -> OFF
+ * ENABLED -> DISABLED -> OFF / REGSLEEP
* ^___________| |
* |______________________|
*
* ENABLED: mmc host is fully functional
* DISABLED: fclk is off
* OFF: fclk is off,voltage regulator is off
+ * REGSLEEP: fclk is off,voltage regulator is asleep
*
* Transition handlers return the timeout for the next state transition
* or negative error.
*/

-enum {ENABLED = 0, DISABLED, OFF};
+enum {ENABLED = 0, DISABLED, REGSLEEP, OFF};

/* Handler for [ENABLED -> DISABLED] transition */
static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
@@ -1221,8 +1225,12 @@ static int omap_mmc_disabled_to_off(struct mmc_omap_host *host)
mmc_slot(host).get_cover_state(host->dev, host->slot_id))) {
mmc_power_save_host(host->mmc);
new_state = OFF;
- } else
- new_state = DISABLED;
+ } else {
+ if (mmc_slot(host).set_sleep)
+ mmc_slot(host).set_sleep(host->dev, host->slot_id,
+ 1, 0, 0);
+ new_state = REGSLEEP;
+ }

OMAP_HSMMC_WRITE(host->base, ISE, 0);
OMAP_HSMMC_WRITE(host->base, IE, 0);
@@ -1279,6 +1287,44 @@ static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
return 0;
}

+/* Handler for [REGSLEEP -> ENABLED] transition */
+static int omap_mmc_regsleep_to_enabled(struct mmc_omap_host *host)
+{
+ unsigned long timeout;
+
+ dev_dbg(mmc_dev(host->mmc), "REGSLEEP -> ENABLED\n");
+
+ clk_enable(host->fclk);
+ clk_enable(host->iclk);
+
+ if (clk_enable(host->dbclk))
+ dev_dbg(mmc_dev(host->mmc),
+ "Enabling debounce clk failed\n");
+
+ omap_mmc_restore_ctx(host);
+
+ /*
+ * We turned off interrupts and bus power. Interrupts
+ * are turned on by 'mmc_omap_start_command()' so we
+ * just need to turn on the bus power here.
+ */
+ OMAP_HSMMC_WRITE(host->base, HCTL,
+ OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
+
+ timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
+ while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP &&
+ time_before(jiffies, timeout))
+ ;
+
+ if (mmc_slot(host).set_sleep)
+ mmc_slot(host).set_sleep(host->dev, host->slot_id,
+ 0, host->vdd, 0);
+
+ host->dpm_state = ENABLED;
+
+ return 0;
+}
+
/*
* Bring MMC host to ENABLED from any other PM state.
*/
@@ -1289,6 +1335,8 @@ static int omap_mmc_enable(struct mmc_host *mmc)
switch (host->dpm_state) {
case DISABLED:
return omap_mmc_disabled_to_enabled(host);
+ case REGSLEEP:
+ return omap_mmc_regsleep_to_enabled(host);
case OFF:
return omap_mmc_off_to_enabled(host);
default:
@@ -1386,7 +1434,8 @@ static int mmc_regs_show(struct seq_file *s, void *data)
host->dpm_state, mmc->nesting_cnt,
host->context_loss, context_loss);

- if (host->suspended || host->dpm_state == OFF) {
+ if (host->suspended || host->dpm_state == OFF ||
+ host->dpm_state == REGSLEEP) {
seq_printf(s, "host suspended, can't read registers\n");
return 0;
}
--
1.5.6.3

2009-07-28 10:40:28

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 16/32] omap_hsmmc: fix scatter-gather list sanity checking

>From e37dab1cd1f8ac4bf4e2050a744a842d3d7ffdc1 Mon Sep 17 00:00:00 2001
From: Jarkko Lavinen <[email protected]>
Date: Fri, 24 Apr 2009 14:20:43 +0300
Subject: [PATCH] omap_hsmmc: fix scatter-gather list sanity checking

Do not use host->dma_len when it is uninitialzed.
Finish the request with an error if the mmc_omap_prepare_data()
fails.

Signed-off-by: Jarkko Lavinen <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 14 ++++++++++++--
1 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index a9f7b74..6222940 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -871,7 +871,7 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
struct mmc_data *data = req->data;

/* Sanity check: all the SG entries must be aligned by block size. */
- for (i = 0; i < host->dma_len; i++) {
+ for (i = 0; i < data->sg_len; i++) {
struct scatterlist *sgl;

sgl = data->sg + i;
@@ -1014,10 +1014,20 @@ static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
{
struct mmc_omap_host *host = mmc_priv(mmc);
+ int err;

WARN_ON(host->mrq != NULL);
host->mrq = req;
- mmc_omap_prepare_data(host, req);
+ err = mmc_omap_prepare_data(host, req);
+ if (err) {
+ req->cmd->error = err;
+ if (req->data)
+ req->data->error = err;
+ host->mrq = NULL;
+ mmc_request_done(mmc, req);
+ return;
+ }
+
mmc_omap_start_command(host, req->cmd, req->data);
}

--
1.5.6.3

2009-07-28 10:42:18

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 29/32] omap_hsmmc: protect the card when the cover is open

>From f1228758121aab11ba2265f2c6fe37f65b3aae03 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Fri, 22 May 2009 16:53:49 +0300
Subject: [PATCH] omap_hsmmc: protect the card when the cover is open

Depending on the manufacturer, there is a small possibility that
removing a card while it is being written to, can render the
card permanently unusable. To prevent that, the card is made
inaccessible when the cover is open.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 63 +++++++++++++++++++++++++++++++++++++++-
1 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index c2b334d..32d5aef 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -165,6 +165,8 @@ struct omap_hsmmc_host {
int context_loss;
int dpm_state;
int vdd;
+ int protect_card;
+ int reqs_blocked;

struct omap_mmc_platform_data *pdata;
};
@@ -349,6 +351,9 @@ static void send_init_stream(struct omap_hsmmc_host *host)
int reg = 0;
unsigned long timeout;

+ if (host->protect_card)
+ return;
+
disable_irq(host->irq);
OMAP_HSMMC_WRITE(host->base, CON,
OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM);
@@ -779,6 +784,30 @@ err:
return ret;
}

+/* Protect the card while the cover is open */
+static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
+{
+ if (!mmc_slot(host).get_cover_state)
+ return;
+
+ host->reqs_blocked = 0;
+ if (mmc_slot(host).get_cover_state(host->dev, host->slot_id)) {
+ if (host->protect_card) {
+ printk(KERN_INFO "%s: cover is closed, "
+ "card is now accessible\n",
+ mmc_hostname(host->mmc));
+ host->protect_card = 0;
+ }
+ } else {
+ if (!host->protect_card) {
+ printk(KERN_INFO "%s: cover is open, "
+ "card is now inaccessible\n",
+ mmc_hostname(host->mmc));
+ host->protect_card = 1;
+ }
+ }
+}
+
/*
* Work Item to notify the core about card insertion/removal
*/
@@ -796,8 +825,10 @@ static void omap_hsmmc_detect(struct work_struct *work)

if (slot->card_detect)
carddetect = slot->card_detect(slot->card_detect_irq);
- else
+ else {
+ omap_hsmmc_protect_card(host);
carddetect = -ENOSYS;
+ }

if (carddetect) {
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
@@ -1033,8 +1064,32 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
* interrupts, but not if we are already in interrupt context i.e.
* retries.
*/
- if (!in_interrupt())
+ if (!in_interrupt()) {
spin_lock_irqsave(&host->irq_lock, host->flags);
+ /*
+ * Protect the card from I/O if there is a possibility
+ * it can be removed.
+ */
+ if (host->protect_card) {
+ if (host->reqs_blocked < 3) {
+ /*
+ * Ensure the controller is left in a consistent
+ * state by resetting the command and data state
+ * machines.
+ */
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+ host->reqs_blocked += 1;
+ }
+ req->cmd->error = -EBADF;
+ if (req->data)
+ req->data->error = -EBADF;
+ spin_unlock_irqrestore(&host->irq_lock, host->flags);
+ mmc_request_done(mmc, req);
+ return;
+ } else if (host->reqs_blocked)
+ host->reqs_blocked = 0;
+ }
WARN_ON(host->mrq != NULL);
host->mrq = req;
err = omap_hsmmc_prepare_data(host, req);
@@ -1728,6 +1783,8 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)

mmc_host_lazy_disable(host->mmc);

+ omap_hsmmc_protect_card(host);
+
mmc_add_host(mmc);

if (mmc_slot(host).name != NULL) {
@@ -1893,6 +1950,8 @@ static int omap_hsmmc_resume(struct platform_device *pdev)
"Unmask interrupt failed\n");
}

+ omap_hsmmc_protect_card(host);
+
/* Notify the core to resume the host */
ret = mmc_resume_host(host->mmc);
if (ret == 0)
--
1.5.6.3

2009-07-28 10:42:46

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 31/32] omap_hsmmc: set a large data timeout for commands with busy signal

>From d9cb1021a2d64c89bbc5763dbbcece68bcc0147c Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Fri, 26 Jun 2009 11:29:20 +0300
Subject: [PATCH] omap_hsmmc: set a large data timeout for commands with busy signal

Commands like SWITCH (CMD6) send a response and then signal busy
while the operation is completed. These commands are expected
to always succeed (otherwise the response would have indicated an
error).

Set an arbitrarily large data timeout value (100ms) for these commands
to ensure that premature timeouts do not occur.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 15 +++++++++++----
1 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index abe1fa1..f5f207d 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -990,7 +990,8 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
}

static void set_data_timeout(struct omap_hsmmc_host *host,
- struct mmc_request *req)
+ unsigned int timeout_ns,
+ unsigned int timeout_clks)
{
unsigned int timeout, cycle_ns;
uint32_t reg, clkd, dto = 0;
@@ -1001,8 +1002,8 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
clkd = 1;

cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd);
- timeout = req->data->timeout_ns / cycle_ns;
- timeout += req->data->timeout_clks;
+ timeout = timeout_ns / cycle_ns;
+ timeout += timeout_clks;
if (timeout) {
while ((timeout & 0x80000000) == 0) {
dto += 1;
@@ -1036,12 +1037,18 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)

if (req->data == NULL) {
OMAP_HSMMC_WRITE(host->base, BLK, 0);
+ /*
+ * Set an arbitrary 100ms data timeout for commands with
+ * busy signal.
+ */
+ if (req->cmd->flags & MMC_RSP_BUSY)
+ set_data_timeout(host, 100000000U, 0);
return 0;
}

OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
| (req->data->blocks << 16));
- set_data_timeout(host, req);
+ set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks);

if (host->use_dma) {
ret = omap_hsmmc_start_dma_transfer(host, req);
--
1.5.6.3

2009-07-28 10:41:37

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 15/32] omap_hsmmc: ensure workqueues are empty before suspend

>From 00dc421d613a159f9b3b9991e62e1c4f3866377f Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Fri, 24 Apr 2009 13:13:20 +0300
Subject: [PATCH] omap_hsmmc: ensure workqueues are empty before suspend

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 50 +++++++++++++++++++++++++++-------------
1 files changed, 34 insertions(+), 16 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 2b242c2..a9f7b74 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -149,7 +149,6 @@ struct mmc_omap_host {
u32 bytesleft;
int suspended;
int irq;
- int carddetect;
int use_dma, dma_ch;
int dma_line_tx, dma_line_rx;
int slot_id;
@@ -754,14 +753,19 @@ static void mmc_omap_detect(struct work_struct *work)
struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
mmc_carddetect_work);
struct omap_mmc_slot_data *slot = &mmc_slot(host);
+ int carddetect;
+
+ if (host->suspended)
+ return;
+
+ sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");

if (mmc_slot(host).card_detect)
- host->carddetect = slot->card_detect(slot->card_detect_irq);
+ carddetect = slot->card_detect(slot->card_detect_irq);
else
- host->carddetect = -ENOSYS;
+ carddetect = -ENOSYS;

- sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
- if (host->carddetect) {
+ if (carddetect) {
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
} else {
mmc_host_enable(host->mmc);
@@ -778,6 +782,8 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
{
struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id;

+ if (host->suspended)
+ return IRQ_HANDLED;
schedule_work(&host->mmc_carddetect_work);

return IRQ_HANDLED;
@@ -1519,30 +1525,42 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
return 0;

if (host) {
+ host->suspended = 1;
+ if (host->pdata->suspend) {
+ ret = host->pdata->suspend(&pdev->dev,
+ host->slot_id);
+ if (ret) {
+ dev_dbg(mmc_dev(host->mmc),
+ "Unable to handle MMC board"
+ " level suspend\n");
+ host->suspended = 0;
+ return ret;
+ }
+ }
+ cancel_work_sync(&host->mmc_carddetect_work);
mmc_host_enable(host->mmc);
ret = mmc_suspend_host(host->mmc, state);
if (ret == 0) {
- host->suspended = 1;
-
OMAP_HSMMC_WRITE(host->base, ISE, 0);
OMAP_HSMMC_WRITE(host->base, IE, 0);

- if (host->pdata->suspend) {
- ret = host->pdata->suspend(&pdev->dev,
- host->slot_id);
- if (ret)
- dev_dbg(mmc_dev(host->mmc),
- "Unable to handle MMC board"
- " level suspend\n");
- }

OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
mmc_host_disable(host->mmc);
clk_disable(host->iclk);
clk_disable(host->dbclk);
- } else
+ } else {
+ host->suspended = 0;
+ if (host->pdata->resume) {
+ ret = host->pdata->resume(&pdev->dev,
+ host->slot_id);
+ if (ret)
+ dev_dbg(mmc_dev(host->mmc),
+ "Unmask interrupt failed\n");
+ }
mmc_host_disable(host->mmc);
+ }

}
return ret;
--
1.5.6.3

2009-07-28 10:42:28

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 28/32] omap_hsmmc: code refactoring

>From c3c78e273388f1b74be248ea1c3cb2964b6897a3 Mon Sep 17 00:00:00 2001
From: Denis Karpov <[email protected]>
Date: Mon, 18 May 2009 13:29:18 +0300
Subject: [PATCH] omap_hsmmc: code refactoring

Functions', structures', variables' names are changed to start
with omap_hsmmc_ prefix.

Signed-off-by: Denis Karpov <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 322 +++++++++++++++++++++--------------------
1 files changed, 162 insertions(+), 160 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 42dfc49..c2b334d 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -133,7 +133,7 @@
#define OMAP_HSMMC_WRITE(base, reg, val) \
__raw_writel((val), (base) + OMAP_HSMMC_##reg)

-struct mmc_omap_host {
+struct omap_hsmmc_host {
struct device *dev;
struct mmc_host *mmc;
struct mmc_request *mrq;
@@ -172,7 +172,7 @@ struct mmc_omap_host {
/*
* Stop clock to the card
*/
-static void omap_mmc_stop_clock(struct mmc_omap_host *host)
+static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
{
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
@@ -186,7 +186,7 @@ static void omap_mmc_stop_clock(struct mmc_omap_host *host)
* Restore the MMC host context, if it was lost as result of a
* power state change.
*/
-static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
+static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
{
struct mmc_ios *ios = &host->mmc->ios;
struct omap_mmc_platform_data *pdata = host->pdata;
@@ -314,7 +314,7 @@ out:
/*
* Save the MMC host context (store the number of power state changes so far).
*/
-static void omap_mmc_save_ctx(struct mmc_omap_host *host)
+static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
{
struct omap_mmc_platform_data *pdata = host->pdata;
int context_loss;
@@ -329,12 +329,12 @@ static void omap_mmc_save_ctx(struct mmc_omap_host *host)

#else

-static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
+static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
{
return 0;
}

-static void omap_mmc_save_ctx(struct mmc_omap_host *host)
+static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
{
}

@@ -344,7 +344,7 @@ static void omap_mmc_save_ctx(struct mmc_omap_host *host)
* Send init stream sequence to card
* before sending IDLE command
*/
-static void send_init_stream(struct mmc_omap_host *host)
+static void send_init_stream(struct omap_hsmmc_host *host)
{
int reg = 0;
unsigned long timeout;
@@ -368,7 +368,7 @@ static void send_init_stream(struct mmc_omap_host *host)
}

static inline
-int mmc_omap_cover_is_closed(struct mmc_omap_host *host)
+int omap_hsmmc_cover_is_closed(struct omap_hsmmc_host *host)
{
int r = 1;

@@ -378,35 +378,35 @@ int mmc_omap_cover_is_closed(struct mmc_omap_host *host)
}

static ssize_t
-mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr,
+omap_hsmmc_show_cover_switch(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
- struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_hsmmc_host *host = mmc_priv(mmc);

- return sprintf(buf, "%s\n", mmc_omap_cover_is_closed(host) ? "closed" :
- "open");
+ return sprintf(buf, "%s\n",
+ omap_hsmmc_cover_is_closed(host) ? "closed" : "open");
}

-static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
+static DEVICE_ATTR(cover_switch, S_IRUGO, omap_hsmmc_show_cover_switch, NULL);

static ssize_t
-mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
+omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
- struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_hsmmc_host *host = mmc_priv(mmc);

return sprintf(buf, "%s\n", mmc_slot(host).name);
}

-static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
+static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL);

/*
* Configure the response type and send the cmd.
*/
static void
-mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
+omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
struct mmc_data *data)
{
int cmdreg = 0, resptype = 0, cmdtype = 0;
@@ -467,7 +467,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
}

static int
-mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
+omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)
{
if (data->flags & MMC_DATA_WRITE)
return DMA_TO_DEVICE;
@@ -479,7 +479,7 @@ mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
* Notify the transfer complete to MMC core
*/
static void
-mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
+omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
{
if (!data) {
struct mmc_request *mrq = host->mrq;
@@ -500,7 +500,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)

if (host->use_dma && host->dma_ch != -1)
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
- mmc_omap_get_dma_dir(host, data));
+ omap_hsmmc_get_dma_dir(host, data));

if (!data->error)
data->bytes_xfered += data->blocks * (data->blksz);
@@ -512,14 +512,14 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
mmc_request_done(host->mmc, data->mrq);
return;
}
- mmc_omap_start_command(host, data->stop, NULL);
+ omap_hsmmc_start_command(host, data->stop, NULL);
}

/*
* Notify the core about command completion
*/
static void
-mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
+omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
{
host->cmd = NULL;

@@ -544,13 +544,13 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
/*
* DMA clean up for command errors
*/
-static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno)
+static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
{
host->data->error = errno;

if (host->use_dma && host->dma_ch != -1) {
dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
- mmc_omap_get_dma_dir(host, host->data));
+ omap_hsmmc_get_dma_dir(host, host->data));
omap_free_dma(host->dma_ch);
host->dma_ch = -1;
up(&host->sem);
@@ -562,10 +562,10 @@ static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno)
* Readable error output
*/
#ifdef CONFIG_MMC_DEBUG
-static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status)
+static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)
{
/* --- means reserved bit without definition at documentation */
- static const char *mmc_omap_status_bits[] = {
+ static const char *omap_hsmmc_status_bits[] = {
"CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ",
"OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC",
"CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---",
@@ -578,9 +578,9 @@ static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status)
len = sprintf(buf, "MMC IRQ 0x%x :", status);
buf += len;

- for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++)
+ for (i = 0; i < ARRAY_SIZE(omap_hsmmc_status_bits); i++)
if (status & (1 << i)) {
- len = sprintf(buf, " %s", mmc_omap_status_bits[i]);
+ len = sprintf(buf, " %s", omap_hsmmc_status_bits[i]);
buf += len;
}

@@ -595,8 +595,8 @@ static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status)
* SRC or SRD bit of SYSCTL register
* Can be called from interrupt context
*/
-static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host,
- unsigned long bit)
+static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
+ unsigned long bit)
{
unsigned long i = 0;
unsigned long limit = (loops_per_jiffy *
@@ -618,9 +618,9 @@ static inline void mmc_omap_reset_controller_fsm(struct mmc_omap_host *host,
/*
* MMC controller IRQ handler
*/
-static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
+static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
{
- struct mmc_omap_host *host = dev_id;
+ struct omap_hsmmc_host *host = dev_id;
struct mmc_data *data;
int end_cmd = 0, end_trans = 0, status;

@@ -641,14 +641,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)

if (status & ERR) {
#ifdef CONFIG_MMC_DEBUG
- mmc_omap_report_irq(host, status);
+ omap_hsmmc_report_irq(host, status);
#endif
if ((status & CMD_TIMEOUT) ||
(status & CMD_CRC)) {
if (host->cmd) {
if (status & CMD_TIMEOUT) {
- mmc_omap_reset_controller_fsm(host,
- SRC);
+ omap_hsmmc_reset_controller_fsm(host,
+ SRC);
host->cmd->error = -ETIMEDOUT;
} else {
host->cmd->error = -EILSEQ;
@@ -657,9 +657,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
}
if (host->data || host->response_busy) {
if (host->data)
- mmc_dma_cleanup(host, -ETIMEDOUT);
+ omap_hsmmc_dma_cleanup(host,
+ -ETIMEDOUT);
host->response_busy = 0;
- mmc_omap_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRD);
}
}
if ((status & DATA_TIMEOUT) ||
@@ -669,11 +670,11 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
-ETIMEDOUT : -EILSEQ;

if (host->data)
- mmc_dma_cleanup(host, err);
+ omap_hsmmc_dma_cleanup(host, err);
else
host->mrq->cmd->error = err;
host->response_busy = 0;
- mmc_omap_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRD);
end_trans = 1;
}
}
@@ -692,16 +693,16 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
OMAP_HSMMC_READ(host->base, STAT);

if (end_cmd || ((status & CC) && host->cmd))
- mmc_omap_cmd_done(host, host->cmd);
+ omap_hsmmc_cmd_done(host, host->cmd);
if ((end_trans || (status & TC)) && host->mrq)
- mmc_omap_xfer_done(host, data);
+ omap_hsmmc_xfer_done(host, data);

spin_unlock(&host->irq_lock);

return IRQ_HANDLED;
}

-static void set_sd_bus_power(struct mmc_omap_host *host)
+static void set_sd_bus_power(struct omap_hsmmc_host *host)
{
unsigned long i;

@@ -721,7 +722,7 @@ static void set_sd_bus_power(struct mmc_omap_host *host)
* The MMC2 transceiver controls are used instead of DAT4..DAT7.
* Some chips, like eMMC ones, use internal transceivers.
*/
-static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
+static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
{
u32 reg_val = 0;
int ret;
@@ -752,7 +753,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd)
/*
* If a MMC dual voltage card is detected, the set_ios fn calls
* this fn with VDD bit set for 1.8V. Upon card removal from the
- * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF.
+ * slot, omap_hsmmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF.
*
* Cope with a bit of slop in the range ... per data sheets:
* - "1.8V" for vdds_mmc1/vdds_mmc1a can be up to 2.45V max,
@@ -781,10 +782,10 @@ err:
/*
* Work Item to notify the core about card insertion/removal
*/
-static void mmc_omap_detect(struct work_struct *work)
+static void omap_hsmmc_detect(struct work_struct *work)
{
- struct mmc_omap_host *host = container_of(work, struct mmc_omap_host,
- mmc_carddetect_work);
+ struct omap_hsmmc_host *host =
+ container_of(work, struct omap_hsmmc_host, mmc_carddetect_work);
struct omap_mmc_slot_data *slot = &mmc_slot(host);
int carddetect;

@@ -802,8 +803,9 @@ static void mmc_omap_detect(struct work_struct *work)
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
} else {
mmc_host_enable(host->mmc);
- mmc_omap_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRD);
mmc_host_lazy_disable(host->mmc);
+
mmc_detect_change(host->mmc, (HZ * 50) / 1000);
}
}
@@ -811,9 +813,9 @@ static void mmc_omap_detect(struct work_struct *work)
/*
* ISR for handling card insertion and removal
*/
-static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
+static irqreturn_t omap_hsmmc_cd_handler(int irq, void *dev_id)
{
- struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id;
+ struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id;

if (host->suspended)
return IRQ_HANDLED;
@@ -822,7 +824,7 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}

-static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
+static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host,
struct mmc_data *data)
{
int sync_dev;
@@ -834,7 +836,7 @@ static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
return sync_dev;
}

-static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
+static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
struct mmc_data *data,
struct scatterlist *sgl)
{
@@ -858,7 +860,7 @@ static void mmc_omap_config_dma_params(struct mmc_omap_host *host,

omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
- mmc_omap_get_dma_sync_dev(host, data),
+ omap_hsmmc_get_dma_sync_dev(host, data),
!(data->flags & MMC_DATA_WRITE));

omap_start_dma(dma_ch);
@@ -867,9 +869,9 @@ static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
/*
* DMA call back function
*/
-static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
+static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *data)
{
- struct mmc_omap_host *host = data;
+ struct omap_hsmmc_host *host = data;

if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ)
dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n");
@@ -880,7 +882,7 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
host->dma_sg_idx++;
if (host->dma_sg_idx < host->dma_len) {
/* Fire up the next transfer. */
- mmc_omap_config_dma_params(host, host->data,
+ omap_hsmmc_config_dma_params(host, host->data,
host->data->sg + host->dma_sg_idx);
return;
}
@@ -897,8 +899,8 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
/*
* Routine to configure and start DMA for the MMC card
*/
-static int
-mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
+static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
+ struct mmc_request *req)
{
int dma_ch = 0, ret = 0, err = 1, i;
struct mmc_data *data = req->data;
@@ -935,8 +937,8 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
return err;
}

- ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD",
- mmc_omap_dma_cb, host, &dma_ch);
+ ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
+ "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
if (ret != 0) {
dev_err(mmc_dev(host->mmc),
"%s: omap_request_dma() failed with %d\n",
@@ -945,16 +947,16 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
}

host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, mmc_omap_get_dma_dir(host, data));
+ data->sg_len, omap_hsmmc_get_dma_dir(host, data));
host->dma_ch = dma_ch;
host->dma_sg_idx = 0;

- mmc_omap_config_dma_params(host, data, data->sg);
+ omap_hsmmc_config_dma_params(host, data, data->sg);

return 0;
}

-static void set_data_timeout(struct mmc_omap_host *host,
+static void set_data_timeout(struct omap_hsmmc_host *host,
struct mmc_request *req)
{
unsigned int timeout, cycle_ns;
@@ -994,7 +996,7 @@ static void set_data_timeout(struct mmc_omap_host *host,
* Configure block length for MMC/SD cards and initiate the transfer.
*/
static int
-mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
+omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
{
int ret;
host->data = req->data;
@@ -1009,7 +1011,7 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
set_data_timeout(host, req);

if (host->use_dma) {
- ret = mmc_omap_start_dma_transfer(host, req);
+ ret = omap_hsmmc_start_dma_transfer(host, req);
if (ret != 0) {
dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n");
return ret;
@@ -1021,9 +1023,9 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
/*
* Request function. for read/write operation
*/
-static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
+static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
{
- struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
int err;

/*
@@ -1035,7 +1037,7 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
spin_lock_irqsave(&host->irq_lock, host->flags);
WARN_ON(host->mrq != NULL);
host->mrq = req;
- err = mmc_omap_prepare_data(host, req);
+ err = omap_hsmmc_prepare_data(host, req);
if (err) {
req->cmd->error = err;
if (req->data)
@@ -1047,14 +1049,13 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
return;
}

- mmc_omap_start_command(host, req->cmd, req->data);
+ omap_hsmmc_start_command(host, req->cmd, req->data);
}

-
/* Routine to configure clock values. Exposed API to core */
-static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
- struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
u16 dsor = 0;
unsigned long regval;
unsigned long timeout;
@@ -1113,8 +1114,8 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* MMC_POWER_UP upon recalculating the voltage.
* vdd 1.8v.
*/
- if (omap_mmc_switch_opcond(host, ios->vdd) != 0)
- dev_dbg(mmc_dev(host->mmc),
+ if (omap_hsmmc_switch_opcond(host, ios->vdd) != 0)
+ dev_dbg(mmc_dev(host->mmc),
"Switch operation failed\n");
}
}
@@ -1130,7 +1131,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (dsor > 250)
dsor = 250;
}
- omap_mmc_stop_clock(host);
+ omap_hsmmc_stop_clock(host);
regval = OMAP_HSMMC_READ(host->base, SYSCTL);
regval = regval & ~(CLKD_MASK);
regval = regval | (dsor << 6) | (DTO << 16);
@@ -1164,7 +1165,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)

static int omap_hsmmc_get_cd(struct mmc_host *mmc)
{
- struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_hsmmc_host *host = mmc_priv(mmc);

if (!mmc_slot(host).card_detect)
return -ENOSYS;
@@ -1173,14 +1174,14 @@ static int omap_hsmmc_get_cd(struct mmc_host *mmc)

static int omap_hsmmc_get_ro(struct mmc_host *mmc)
{
- struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_hsmmc_host *host = mmc_priv(mmc);

if (!mmc_slot(host).get_ro)
return -ENOSYS;
return mmc_slot(host).get_ro(host->dev, 0);
}

-static void omap_hsmmc_init(struct mmc_omap_host *host)
+static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host)
{
u32 hctl, capa, value;

@@ -1226,9 +1227,9 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)
enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF};

/* Handler for [ENABLED -> DISABLED] transition */
-static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
+static int omap_hsmmc_enabled_to_disabled(struct omap_hsmmc_host *host)
{
- omap_mmc_save_ctx(host);
+ omap_hsmmc_context_save(host);
clk_disable(host->fclk);
host->dpm_state = DISABLED;

@@ -1241,7 +1242,7 @@ static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
}

/* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */
-static int omap_mmc_disabled_to_sleep(struct mmc_omap_host *host)
+static int omap_hsmmc_disabled_to_sleep(struct omap_hsmmc_host *host)
{
int err, new_state;

@@ -1249,7 +1250,7 @@ static int omap_mmc_disabled_to_sleep(struct mmc_omap_host *host)
return 0;

clk_enable(host->fclk);
- omap_mmc_restore_ctx(host);
+ omap_hsmmc_context_restore(host);
if (mmc_card_can_sleep(host->mmc)) {
err = mmc_card_sleep(host->mmc);
if (err < 0) {
@@ -1258,8 +1259,9 @@ static int omap_mmc_disabled_to_sleep(struct mmc_omap_host *host)
return err;
}
new_state = CARDSLEEP;
- } else
+ } else {
new_state = REGSLEEP;
+ }
if (mmc_slot(host).set_sleep)
mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0,
new_state == CARDSLEEP);
@@ -1282,7 +1284,7 @@ static int omap_mmc_disabled_to_sleep(struct mmc_omap_host *host)
}

/* Handler for [REGSLEEP / CARDSLEEP -> OFF] transition */
-static int omap_mmc_sleep_to_off(struct mmc_omap_host *host)
+static int omap_hsmmc_sleep_to_off(struct omap_hsmmc_host *host)
{
if (!mmc_try_claim_host(host->mmc))
return 0;
@@ -1310,7 +1312,7 @@ static int omap_mmc_sleep_to_off(struct mmc_omap_host *host)
}

/* Handler for [DISABLED -> ENABLED] transition */
-static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
+static int omap_hsmmc_disabled_to_enabled(struct omap_hsmmc_host *host)
{
int err;

@@ -1318,8 +1320,7 @@ static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
if (err < 0)
return err;

- omap_mmc_restore_ctx(host);
-
+ omap_hsmmc_context_restore(host);
host->dpm_state = ENABLED;

dev_dbg(mmc_dev(host->mmc), "DISABLED -> ENABLED\n");
@@ -1328,13 +1329,13 @@ static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
}

/* Handler for [SLEEP -> ENABLED] transition */
-static int omap_mmc_sleep_to_enabled(struct mmc_omap_host *host)
+static int omap_hsmmc_sleep_to_enabled(struct omap_hsmmc_host *host)
{
if (!mmc_try_claim_host(host->mmc))
return 0;

clk_enable(host->fclk);
- omap_mmc_restore_ctx(host);
+ omap_hsmmc_context_restore(host);
if (mmc_slot(host).set_sleep)
mmc_slot(host).set_sleep(host->dev, host->slot_id, 0,
host->vdd, host->dpm_state == CARDSLEEP);
@@ -1352,12 +1353,12 @@ static int omap_mmc_sleep_to_enabled(struct mmc_omap_host *host)
}

/* Handler for [OFF -> ENABLED] transition */
-static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
+static int omap_hsmmc_off_to_enabled(struct omap_hsmmc_host *host)
{
clk_enable(host->fclk);

- omap_mmc_restore_ctx(host);
- omap_hsmmc_init(host);
+ omap_hsmmc_context_restore(host);
+ omap_hsmmc_conf_bus_power(host);
mmc_power_restore_host(host->mmc);

host->dpm_state = ENABLED;
@@ -1370,18 +1371,18 @@ static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
/*
* Bring MMC host to ENABLED from any other PM state.
*/
-static int omap_mmc_enable(struct mmc_host *mmc)
+static int omap_hsmmc_enable(struct mmc_host *mmc)
{
- struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_hsmmc_host *host = mmc_priv(mmc);

switch (host->dpm_state) {
case DISABLED:
- return omap_mmc_disabled_to_enabled(host);
+ return omap_hsmmc_disabled_to_enabled(host);
case CARDSLEEP:
case REGSLEEP:
- return omap_mmc_sleep_to_enabled(host);
+ return omap_hsmmc_sleep_to_enabled(host);
case OFF:
- return omap_mmc_off_to_enabled(host);
+ return omap_hsmmc_off_to_enabled(host);
default:
dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
return -EINVAL;
@@ -1391,68 +1392,68 @@ static int omap_mmc_enable(struct mmc_host *mmc)
/*
* Bring MMC host in PM state (one level deeper).
*/
-static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
+static int omap_hsmmc_disable(struct mmc_host *mmc, int lazy)
{
- struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_hsmmc_host *host = mmc_priv(mmc);

switch (host->dpm_state) {
case ENABLED: {
int delay;

- delay = omap_mmc_enabled_to_disabled(host);
+ delay = omap_hsmmc_enabled_to_disabled(host);
if (lazy || delay < 0)
return delay;
return 0;
}
case DISABLED:
- return omap_mmc_disabled_to_sleep(host);
+ return omap_hsmmc_disabled_to_sleep(host);
case CARDSLEEP:
case REGSLEEP:
- return omap_mmc_sleep_to_off(host);
+ return omap_hsmmc_sleep_to_off(host);
default:
dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
return -EINVAL;
}
}

-static int omap_mmc_enable_fclk(struct mmc_host *mmc)
+static int omap_hsmmc_enable_fclk(struct mmc_host *mmc)
{
- struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
int err;

err = clk_enable(host->fclk);
if (err)
return err;
dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
- omap_mmc_restore_ctx(host);
+ omap_hsmmc_context_restore(host);
return 0;
}

-static int omap_mmc_disable_fclk(struct mmc_host *mmc, int lazy)
+static int omap_hsmmc_disable_fclk(struct mmc_host *mmc, int lazy)
{
- struct mmc_omap_host *host = mmc_priv(mmc);
+ struct omap_hsmmc_host *host = mmc_priv(mmc);

- omap_mmc_save_ctx(host);
+ omap_hsmmc_context_save(host);
clk_disable(host->fclk);
dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
return 0;
}

-static const struct mmc_host_ops mmc_omap_ops = {
- .enable = omap_mmc_enable_fclk,
- .disable = omap_mmc_disable_fclk,
- .request = omap_mmc_request,
- .set_ios = omap_mmc_set_ios,
+static const struct mmc_host_ops omap_hsmmc_ops = {
+ .enable = omap_hsmmc_enable_fclk,
+ .disable = omap_hsmmc_disable_fclk,
+ .request = omap_hsmmc_request,
+ .set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
/* NYET -- enable_sdio_irq */
};

-static const struct mmc_host_ops mmc_omap_ps_ops = {
- .enable = omap_mmc_enable,
- .disable = omap_mmc_disable,
- .request = omap_mmc_request,
- .set_ios = omap_mmc_set_ios,
+static const struct mmc_host_ops omap_hsmmc_ps_ops = {
+ .enable = omap_hsmmc_enable,
+ .disable = omap_hsmmc_disable,
+ .request = omap_hsmmc_request,
+ .set_ios = omap_hsmmc_set_ios,
.get_cd = omap_hsmmc_get_cd,
.get_ro = omap_hsmmc_get_ro,
/* NYET -- enable_sdio_irq */
@@ -1460,15 +1461,14 @@ static const struct mmc_host_ops mmc_omap_ps_ops = {

#ifdef CONFIG_DEBUG_FS

-static int mmc_regs_show(struct seq_file *s, void *data)
+static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
{
struct mmc_host *mmc = s->private;
- struct mmc_omap_host *host = mmc_priv(mmc);
- struct omap_mmc_platform_data *pdata = host->pdata;
+ struct omap_hsmmc_host *host = mmc_priv(mmc);
int context_loss = 0;

- if (pdata->get_context_loss_count)
- context_loss = pdata->get_context_loss_count(host->dev);
+ if (host->pdata->get_context_loss_count)
+ context_loss = host->pdata->get_context_loss_count(host->dev);

seq_printf(s, "mmc%d:\n"
" enabled:\t%d\n"
@@ -1510,19 +1510,19 @@ static int mmc_regs_show(struct seq_file *s, void *data)
return 0;
}

-static int mmc_regs_open(struct inode *inode, struct file *file)
+static int omap_hsmmc_regs_open(struct inode *inode, struct file *file)
{
- return single_open(file, mmc_regs_show, inode->i_private);
+ return single_open(file, omap_hsmmc_regs_show, inode->i_private);
}

static const struct file_operations mmc_regs_fops = {
- .open = mmc_regs_open,
+ .open = omap_hsmmc_regs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};

-static void omap_mmc_debugfs(struct mmc_host *mmc)
+static void omap_hsmmc_debugfs(struct mmc_host *mmc)
{
if (mmc->debugfs_root)
debugfs_create_file("regs", S_IRUSR, mmc->debugfs_root,
@@ -1531,17 +1531,17 @@ static void omap_mmc_debugfs(struct mmc_host *mmc)

#else

-static void omap_mmc_debugfs(struct mmc_host *mmc)
+static void omap_hsmmc_debugfs(struct mmc_host *mmc)
{
}

#endif

-static int __init omap_mmc_probe(struct platform_device *pdev)
+static int __init omap_hsmmc_probe(struct platform_device *pdev)
{
struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;
struct mmc_host *mmc;
- struct mmc_omap_host *host = NULL;
+ struct omap_hsmmc_host *host = NULL;
struct resource *res;
int ret = 0, irq;

@@ -1565,7 +1565,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
if (res == NULL)
return -EBUSY;

- mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev);
+ mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev);
if (!mmc) {
ret = -ENOMEM;
goto err;
@@ -1586,12 +1586,12 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
host->power_mode = -1;

platform_set_drvdata(pdev, host);
- INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
+ INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect);

if (mmc_slot(host).power_saving)
- mmc->ops = &mmc_omap_ps_ops;
+ mmc->ops = &omap_hsmmc_ps_ops;
else
- mmc->ops = &mmc_omap_ops;
+ mmc->ops = &omap_hsmmc_ops;

mmc->f_min = 400000;
mmc->f_max = 52000000;
@@ -1613,7 +1613,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
goto err1;
}

- omap_mmc_save_ctx(host);
+ omap_hsmmc_context_save(host);

mmc->caps |= MMC_CAP_DISABLE;
mmc_set_disable_delay(mmc, OMAP_MMC_DISABLED_TIMEOUT);
@@ -1670,7 +1670,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
if (!(mmc->caps & (MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC)))
mmc->caps |= MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;

- omap_hsmmc_init(host);
+ omap_hsmmc_conf_bus_power(host);

/* Select DMA lines */
switch (host->id) {
@@ -1692,7 +1692,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
}

/* Request IRQ for MMC operations */
- ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED,
+ ret = request_irq(host->irq, omap_hsmmc_irq, IRQF_DISABLED,
mmc_hostname(mmc), host);
if (ret) {
dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
@@ -1702,7 +1702,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
/* initialize power supplies, gpios, etc */
if (pdata->init != NULL) {
if (pdata->init(&pdev->dev) != 0) {
- dev_dbg(mmc_dev(host->mmc), "late init error\n");
+ dev_dbg(mmc_dev(host->mmc),
+ "Unable to configure MMC IRQs\n");
goto err_irq_cd_init;
}
}
@@ -1711,7 +1712,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
/* Request IRQ for card detect */
if ((mmc_slot(host).card_detect_irq)) {
ret = request_irq(mmc_slot(host).card_detect_irq,
- omap_mmc_cd_handler,
+ omap_hsmmc_cd_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_DISABLED,
mmc_hostname(mmc), host);
@@ -1741,7 +1742,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
goto err_cover_switch;
}

- omap_mmc_debugfs(mmc);
+ omap_hsmmc_debugfs(mmc);

return 0;

@@ -1773,9 +1774,9 @@ err:
return ret;
}

-static int omap_mmc_remove(struct platform_device *pdev)
+static int omap_hsmmc_remove(struct platform_device *pdev)
{
- struct mmc_omap_host *host = platform_get_drvdata(pdev);
+ struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
struct resource *res;

if (host) {
@@ -1810,10 +1811,10 @@ static int omap_mmc_remove(struct platform_device *pdev)
}

#ifdef CONFIG_PM
-static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
+static int omap_hsmmc_suspend(struct platform_device *pdev, pm_message_t state)
{
int ret = 0;
- struct mmc_omap_host *host = platform_get_drvdata(pdev);
+ struct omap_hsmmc_host *host = platform_get_drvdata(pdev);

if (host && host->suspended)
return 0;
@@ -1861,10 +1862,10 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)
}

/* Routine to resume the MMC device */
-static int omap_mmc_resume(struct platform_device *pdev)
+static int omap_hsmmc_resume(struct platform_device *pdev)
{
int ret = 0;
- struct mmc_omap_host *host = platform_get_drvdata(pdev);
+ struct omap_hsmmc_host *host = platform_get_drvdata(pdev);

if (host && !host->suspended)
return 0;
@@ -1883,7 +1884,7 @@ static int omap_mmc_resume(struct platform_device *pdev)
goto clk_en_err;
}

- omap_hsmmc_init(host);
+ omap_hsmmc_conf_bus_power(host);

if (host->pdata->resume) {
ret = host->pdata->resume(&pdev->dev, host->slot_id);
@@ -1896,6 +1897,7 @@ static int omap_mmc_resume(struct platform_device *pdev)
ret = mmc_resume_host(host->mmc);
if (ret == 0)
host->suspended = 0;
+
mmc_host_lazy_disable(host->mmc);
}

@@ -1908,35 +1910,35 @@ clk_en_err:
}

#else
-#define omap_mmc_suspend NULL
-#define omap_mmc_resume NULL
+#define omap_hsmmc_suspend NULL
+#define omap_hsmmc_resume NULL
#endif

-static struct platform_driver omap_mmc_driver = {
- .probe = omap_mmc_probe,
- .remove = omap_mmc_remove,
- .suspend = omap_mmc_suspend,
- .resume = omap_mmc_resume,
+static struct platform_driver omap_hsmmc_driver = {
+ .probe = omap_hsmmc_probe,
+ .remove = omap_hsmmc_remove,
+ .suspend = omap_hsmmc_suspend,
+ .resume = omap_hsmmc_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
};

-static int __init omap_mmc_init(void)
+static int __init omap_hsmmc_init(void)
{
/* Register the MMC driver */
- return platform_driver_register(&omap_mmc_driver);
+ return platform_driver_register(&omap_hsmmc_driver);
}

-static void __exit omap_mmc_cleanup(void)
+static void __exit omap_hsmmc_cleanup(void)
{
/* Unregister MMC driver */
- platform_driver_unregister(&omap_mmc_driver);
+ platform_driver_unregister(&omap_hsmmc_driver);
}

-module_init(omap_mmc_init);
-module_exit(omap_mmc_cleanup);
+module_init(omap_hsmmc_init);
+module_exit(omap_hsmmc_cleanup);

MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver");
MODULE_LICENSE("GPL");
--
1.5.6.3

2009-07-28 10:43:03

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 32/32] ARM: OMAP: RX51: set MMC capabilities and power-saving flag

>From 3f9b4e7c30012bbad4f5cf678f2e02e943000b67 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Fri, 10 Jul 2009 10:32:44 +0300
Subject: [PATCH] ARM: OMAP: RX51: set MMC capabilities and power-saving flag

Specify MMC capabilities and set the power-saving flag
for RX51.

Signed-off-by: Adrian Hunter <[email protected]>
---
arch/arm/mach-omap2/board-rx51-peripherals.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c
index 9a0bf67..d2ce938 100644
--- a/arch/arm/mach-omap2/board-rx51-peripherals.c
+++ b/arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -19,6 +19,7 @@
#include <linux/delay.h>
#include <linux/regulator/machine.h>
#include <linux/gpio.h>
+#include <linux/mmc/host.h>

#include <mach/mcspi.h>
#include <mach/mux.h>
@@ -102,6 +103,8 @@ static struct twl4030_hsmmc_info mmc[] = {
.cover_only = true,
.gpio_cd = 160,
.gpio_wp = -EINVAL,
+ .power_saving = true,
+ .caps = MMC_CAP_SD,
},
{
.name = "internal",
@@ -109,6 +112,8 @@ static struct twl4030_hsmmc_info mmc[] = {
.wires = 8,
.gpio_cd = -EINVAL,
.gpio_wp = -EINVAL,
+ .power_saving = true,
+ .caps = MMC_CAP_MMC | MMC_CAP_NONREMOVABLE,
},
{} /* Terminator */
};
--
1.5.6.3

2009-07-28 10:42:12

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 26/32] omap_hsmmc: prevent races with irq handler

>From 4252559187e96877a2a7ab5a5b0d0631aa51c7d2 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Sat, 16 May 2009 10:32:34 +0300
Subject: [PATCH] omap_hsmmc: prevent races with irq handler

If an unexpected interrupt occurs while preparing the
next request, an oops can occur.

For example, a new request is setting up DMA for data
transfer so host->data is not NULL. An unexpected
transfer complete (TC) interrupt comes along and
the interrupt handler sets host->data to NULL. Oops!

Prevent that by adding a spinlock.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 25 +++++++++++++++++++++++++
1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 5121277..5213d87 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -146,6 +146,8 @@ struct mmc_omap_host {
struct work_struct mmc_carddetect_work;
void __iomem *base;
resource_size_t mapbase;
+ spinlock_t irq_lock; /* Prevent races with irq handler */
+ unsigned long flags;
unsigned int id;
unsigned int dma_len;
unsigned int dma_sg_idx;
@@ -452,6 +454,14 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
if (host->use_dma)
cmdreg |= DMA_EN;

+ /*
+ * In an interrupt context (i.e. STOP command), the spinlock is unlocked
+ * by the interrupt handler, otherwise (i.e. for a new request) it is
+ * unlocked here.
+ */
+ if (!in_interrupt())
+ spin_unlock_irqrestore(&host->irq_lock, host->flags);
+
OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
}
@@ -614,11 +624,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
struct mmc_data *data;
int end_cmd = 0, end_trans = 0, status;

+ spin_lock(&host->irq_lock);
+
if (host->mrq == NULL) {
OMAP_HSMMC_WRITE(host->base, STAT,
OMAP_HSMMC_READ(host->base, STAT));
/* Flush posted write */
OMAP_HSMMC_READ(host->base, STAT);
+ spin_unlock(&host->irq_lock);
return IRQ_HANDLED;
}

@@ -683,6 +696,8 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
if ((end_trans || (status & TC)) && host->mrq)
mmc_omap_xfer_done(host, data);

+ spin_unlock(&host->irq_lock);
+
return IRQ_HANDLED;
}

@@ -1011,6 +1026,13 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
struct mmc_omap_host *host = mmc_priv(mmc);
int err;

+ /*
+ * Prevent races with the interrupt handler because of unexpected
+ * interrupts, but not if we are already in interrupt context i.e.
+ * retries.
+ */
+ if (!in_interrupt())
+ spin_lock_irqsave(&host->irq_lock, host->flags);
WARN_ON(host->mrq != NULL);
host->mrq = req;
err = mmc_omap_prepare_data(host, req);
@@ -1019,6 +1041,8 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
if (req->data)
req->data->error = err;
host->mrq = NULL;
+ if (!in_interrupt())
+ spin_unlock_irqrestore(&host->irq_lock, host->flags);
mmc_request_done(mmc, req);
return;
}
@@ -1573,6 +1597,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
mmc->f_max = 52000000;

sema_init(&host->sem, 1);
+ spin_lock_init(&host->irq_lock);

host->iclk = clk_get(&pdev->dev, "ick");
if (IS_ERR(host->iclk)) {
--
1.5.6.3

2009-07-28 10:41:42

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 22/32] omap_hsmmc: fix NULL pointer dereference

>From 0f00e61d9f963d44771f655deaa8f5f4951606a8 Mon Sep 17 00:00:00 2001
From: Jarkko Lavinen <[email protected]>
Date: Tue, 12 May 2009 19:46:14 +0300
Subject: [PATCH] omap_hsmmc: fix NULL pointer dereference

Do not call 'mmc_omap_xfer_done()' if the request is
already done.

Signed-off-by: Jarkko Lavinen <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index d5fb066..4309031 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -670,7 +670,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)

if (end_cmd || ((status & CC) && host->cmd))
mmc_omap_cmd_done(host, host->cmd);
- if (end_trans || (status & TC))
+ if ((end_trans || (status & TC)) && host->mrq)
mmc_omap_xfer_done(host, data);

return IRQ_HANDLED;
--
1.5.6.3

2009-07-28 10:42:30

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 30/32] omap_hsmmc: ensure all clock enables and disables are paired

>From 946bac7ddcc387a15c1391e416b54c3041798883 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Sun, 31 May 2009 19:27:36 +0300
Subject: [PATCH] omap_hsmmc: ensure all clock enables and disables are paired

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 26 ++++++++++++++------------
1 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 32d5aef..abe1fa1 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -735,22 +735,24 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
/* Disable the clocks */
clk_disable(host->fclk);
clk_disable(host->iclk);
- clk_disable(host->dbclk);
+ if (host->dbclk_enabled)
+ clk_disable(host->dbclk);

/* Turn the power off */
ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
- if (ret != 0)
- goto err;

/* Turn the power ON with given VDD 1.8 or 3.0v */
- ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd);
+ if (!ret)
+ ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1,
+ vdd);
+ clk_enable(host->iclk);
+ if (host->dbclk_enabled)
+ clk_enable(host->dbclk);
+ clk_enable(host->fclk);
+
if (ret != 0)
goto err;

- clk_enable(host->fclk);
- clk_enable(host->iclk);
- clk_enable(host->dbclk);
-
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR);
reg_val = OMAP_HSMMC_READ(host->base, HCTL);
@@ -1901,7 +1903,8 @@ static int omap_hsmmc_suspend(struct platform_device *pdev, pm_message_t state)
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
mmc_host_disable(host->mmc);
clk_disable(host->iclk);
- clk_disable(host->dbclk);
+ if (host->dbclk_enabled)
+ clk_disable(host->dbclk);
} else {
host->suspended = 0;
if (host->pdata->resume) {
@@ -1932,9 +1935,8 @@ static int omap_hsmmc_resume(struct platform_device *pdev)
if (ret)
goto clk_en_err;

- if (clk_enable(host->dbclk) != 0)
- dev_dbg(mmc_dev(host->mmc),
- "Enabling debounce clk failed\n");
+ if (host->dbclk_enabled)
+ clk_enable(host->dbclk);

if (mmc_host_enable(host->mmc) != 0) {
clk_disable(host->iclk);
--
1.5.6.3

2009-07-28 10:41:50

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 24/32] omap_hsmmc: clear interrupt status after init sequence

>From 1aa0e844f89f4349a6e940079343efbf9e8130d6 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Sat, 16 May 2009 09:35:17 +0300
Subject: [PATCH] omap_hsmmc: clear interrupt status after init sequence

Clear the interrupt status after sending the initialization sequence,
as specified in the TRM.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 0373bdd..c7a6d88 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -358,6 +358,10 @@ static void send_init_stream(struct mmc_omap_host *host)

OMAP_HSMMC_WRITE(host->base, CON,
OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM);
+
+ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
+ OMAP_HSMMC_READ(host->base, STAT);
+
enable_irq(host->irq);
}

--
1.5.6.3

2009-07-28 10:41:55

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 25/32] omap_hsmmc: cater for weird CMD6 behaviour

>From 8520f6fd74c073a76121619403808f1e3da0f587 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Sat, 16 May 2009 10:05:40 +0300
Subject: [PATCH] omap_hsmmc: cater for weird CMD6 behaviour

Sometimes the controller unexpectedly produces a TC (transfer
complete) interrupt before the CC (command complete) interrupt for
command 6 (SWITCH). This is a problem because the CC interrupt
can get mixed up with the next request. Add a hack for CMD6.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index c7a6d88..5121277 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -474,6 +474,13 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
if (!data) {
struct mmc_request *mrq = host->mrq;

+ /* TC before CC from CMD6 - don't know why, but it happens */
+ if (host->cmd && host->cmd->opcode == 6 &&
+ host->response_busy) {
+ host->response_busy = 0;
+ return;
+ }
+
host->mrq = NULL;
mmc_request_done(host->mmc, mrq);
return;
--
1.5.6.3

2009-07-28 10:42:13

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 27/32] omap_hsmmc: pass host capabilities for SD only and MMC only

>From 6ed0f09877e25af46579c1e7754600d31356e302 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Mon, 18 May 2009 11:33:26 +0300
Subject: [PATCH] omap_hsmmc: pass host capabilities for SD only and MMC only

Some hosts can accept only certain types of cards.
For example, an eMMC is MMC only and a uSD slot may
be SD only. Pass host capabilities from the board
through to the driver.

Signed-off-by: Adrian Hunter <[email protected]>
---
arch/arm/mach-omap2/mmc-twl4030.c | 5 ++---
arch/arm/mach-omap2/mmc-twl4030.h | 2 +-
arch/arm/plat-omap/include/mach/mmc.h | 6 +++---
drivers/mmc/host/omap_hsmmc.c | 7 ++++---
4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c
index f95c702..48765bf 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.c
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -464,12 +464,11 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
} else
mmc->slots[0].gpio_wp = -EINVAL;

- if (c->nonremovable)
- mmc->slots[0].nonremovable = 1;
-
if (c->power_saving)
mmc->slots[0].power_saving = 1;

+ mmc->slots[0].caps = c->caps;
+
/* NOTE: MMC slots should have a Vcc regulator set up.
* This may be from a TWL4030-family chip, another
* controllable regulator, or a fixed supply.
diff --git a/arch/arm/mach-omap2/mmc-twl4030.h b/arch/arm/mach-omap2/mmc-twl4030.h
index a47e685..cb43f52 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.h
+++ b/arch/arm/mach-omap2/mmc-twl4030.h
@@ -12,8 +12,8 @@ struct twl4030_hsmmc_info {
bool transceiver; /* MMC-2 option */
bool ext_clock; /* use external pin for input clock */
bool cover_only; /* No card detect - just cover switch */
- bool nonremovable; /* Nonremovable e.g. eMMC */
bool power_saving; /* Try to sleep or power off when possible */
+ unsigned long caps; /* MMC host capabilities */
int gpio_cd; /* or -EINVAL */
int gpio_wp; /* or -EINVAL */
char *name; /* or NULL for default */
diff --git a/arch/arm/plat-omap/include/mach/mmc.h b/arch/arm/plat-omap/include/mach/mmc.h
index 9390297..48cf2de 100644
--- a/arch/arm/plat-omap/include/mach/mmc.h
+++ b/arch/arm/plat-omap/include/mach/mmc.h
@@ -83,12 +83,12 @@ struct omap_mmc_platform_data {
/* use the internal clock */
unsigned internal_clock:1;

- /* nonremovable e.g. eMMC */
- unsigned nonremovable:1;
-
/* Try to sleep or power off when possible */
unsigned power_saving:1;

+ /* MMC host capabilities */
+ unsigned long caps;
+
int switch_pin; /* gpio (card detect) */
int gpio_wp; /* gpio (write protect) */

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 5213d87..42dfc49 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1664,10 +1664,11 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
else if (mmc_slot(host).wires >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;

- if (mmc_slot(host).nonremovable)
- mmc->caps |= MMC_CAP_NONREMOVABLE;
+ mmc->caps |= mmc_slot(host).caps;

- mmc->caps |= MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;
+ /* If no card caps specified then assume them all */
+ if (!(mmc->caps & (MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC)))
+ mmc->caps |= MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;

omap_hsmmc_init(host);

--
1.5.6.3

2009-07-28 10:44:18

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 21/32] omap_hsmmc: add mmc card sleep and awake support

>From 372d05a33f04d6a664ad1d3a80bed3d9ae80e495 Mon Sep 17 00:00:00 2001
From: Jarkko Lavinen <[email protected]>
Date: Tue, 12 May 2009 19:46:14 +0300
Subject: [PATCH] omap_hsmmc: add mmc card sleep and awake support

After 1 second of inactivity, put card and/or regulator
to sleep. After 8 seconds of inactivity, turn off the
power.

Signed-off-by: Jarkko Lavinen <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 162 ++++++++++++++++++++++-------------------
1 files changed, 88 insertions(+), 74 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 7a17901..d5fb066 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -27,6 +27,7 @@
#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
+#include <linux/mmc/core.h>
#include <linux/io.h>
#include <linux/semaphore.h>
#include <mach/dma.h>
@@ -113,7 +114,8 @@

/* Timeouts for entering power saving states on inactivity, msec */
#define OMAP_MMC_DISABLED_TIMEOUT 100
-#define OMAP_MMC_OFF_TIMEOUT 1000
+#define OMAP_MMC_SLEEP_TIMEOUT 1000
+#define OMAP_MMC_OFF_TIMEOUT 8000

/*
* One controller can have multiple slots, like on some omap boards using
@@ -1175,20 +1177,21 @@ static void omap_hsmmc_init(struct mmc_omap_host *host)

/*
* Dynamic power saving handling, FSM:
- * ENABLED -> DISABLED -> OFF / REGSLEEP
- * ^___________| |
- * |______________________|
+ * ENABLED -> DISABLED -> CARDSLEEP / REGSLEEP -> OFF
+ * ^___________| | |
+ * |______________________|______________________|
*
* ENABLED: mmc host is fully functional
* DISABLED: fclk is off
- * OFF: fclk is off,voltage regulator is off
- * REGSLEEP: fclk is off,voltage regulator is asleep
+ * CARDSLEEP: fclk is off, card is asleep, voltage regulator is asleep
+ * REGSLEEP: fclk is off, voltage regulator is asleep
+ * OFF: fclk is off, voltage regulator is off
*
* Transition handlers return the timeout for the next state transition
* or negative error.
*/

-enum {ENABLED = 0, DISABLED, REGSLEEP, OFF};
+enum {ENABLED = 0, DISABLED, CARDSLEEP, REGSLEEP, OFF};

/* Handler for [ENABLED -> DISABLED] transition */
static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
@@ -1202,46 +1205,72 @@ static int omap_mmc_enabled_to_disabled(struct mmc_omap_host *host)
if (host->power_mode == MMC_POWER_OFF)
return 0;

- return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT);
+ return msecs_to_jiffies(OMAP_MMC_SLEEP_TIMEOUT);
}

-/* Handler for [DISABLED -> OFF] transition */
-static int omap_mmc_disabled_to_off(struct mmc_omap_host *host)
+/* Handler for [DISABLED -> REGSLEEP / CARDSLEEP] transition */
+static int omap_mmc_disabled_to_sleep(struct mmc_omap_host *host)
{
- int new_state;
-
- dev_dbg(mmc_dev(host->mmc), "DISABLED -> OFF\n");
+ int err, new_state;

if (!mmc_try_claim_host(host->mmc))
return 0;

clk_enable(host->fclk);
-
omap_mmc_restore_ctx(host);
+ if (mmc_card_can_sleep(host->mmc)) {
+ err = mmc_card_sleep(host->mmc);
+ if (err < 0) {
+ clk_disable(host->fclk);
+ mmc_release_host(host->mmc);
+ return err;
+ }
+ new_state = CARDSLEEP;
+ } else
+ new_state = REGSLEEP;
+ if (mmc_slot(host).set_sleep)
+ mmc_slot(host).set_sleep(host->dev, host->slot_id, 1, 0,
+ new_state == CARDSLEEP);
+ /* FIXME: turn off bus power and perhaps interrupts too */
+ clk_disable(host->fclk);
+ host->dpm_state = new_state;
+
+ mmc_release_host(host->mmc);
+
+ dev_dbg(mmc_dev(host->mmc), "DISABLED -> %s\n",
+ host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");

if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
mmc_slot(host).card_detect ||
(mmc_slot(host).get_cover_state &&
- mmc_slot(host).get_cover_state(host->dev, host->slot_id))) {
- mmc_power_save_host(host->mmc);
- new_state = OFF;
- } else {
- if (mmc_slot(host).set_sleep)
- mmc_slot(host).set_sleep(host->dev, host->slot_id,
- 1, 0, 0);
- new_state = REGSLEEP;
+ mmc_slot(host).get_cover_state(host->dev, host->slot_id)))
+ return msecs_to_jiffies(OMAP_MMC_OFF_TIMEOUT);
+
+ return 0;
+}
+
+/* Handler for [REGSLEEP / CARDSLEEP -> OFF] transition */
+static int omap_mmc_sleep_to_off(struct mmc_omap_host *host)
+{
+ if (!mmc_try_claim_host(host->mmc))
+ return 0;
+
+ if (!((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
+ mmc_slot(host).card_detect ||
+ (mmc_slot(host).get_cover_state &&
+ mmc_slot(host).get_cover_state(host->dev, host->slot_id)))) {
+ mmc_release_host(host->mmc);
+ return 0;
}

- OMAP_HSMMC_WRITE(host->base, ISE, 0);
- OMAP_HSMMC_WRITE(host->base, IE, 0);
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
+ mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
+ host->vdd = 0;
+ host->power_mode = MMC_POWER_OFF;

- clk_disable(host->fclk);
- clk_disable(host->iclk);
- clk_disable(host->dbclk);
+ dev_dbg(mmc_dev(host->mmc), "%s -> OFF\n",
+ host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");

- host->dpm_state = new_state;
+ host->dpm_state = OFF;

mmc_release_host(host->mmc);

@@ -1266,62 +1295,43 @@ static int omap_mmc_disabled_to_enabled(struct mmc_omap_host *host)
return 0;
}

-/* Handler for [OFF -> ENABLED] transition */
-static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
+/* Handler for [SLEEP -> ENABLED] transition */
+static int omap_mmc_sleep_to_enabled(struct mmc_omap_host *host)
{
- clk_enable(host->fclk);
- clk_enable(host->iclk);
-
- if (clk_enable(host->dbclk))
- dev_dbg(mmc_dev(host->mmc),
- "Enabling debounce clk failed\n");
+ if (!mmc_try_claim_host(host->mmc))
+ return 0;

+ clk_enable(host->fclk);
omap_mmc_restore_ctx(host);
- omap_hsmmc_init(host);
- mmc_power_restore_host(host->mmc);
+ if (mmc_slot(host).set_sleep)
+ mmc_slot(host).set_sleep(host->dev, host->slot_id, 0,
+ host->vdd, host->dpm_state == CARDSLEEP);
+ if (mmc_card_can_sleep(host->mmc))
+ mmc_card_awake(host->mmc);
+
+ dev_dbg(mmc_dev(host->mmc), "%s -> ENABLED\n",
+ host->dpm_state == CARDSLEEP ? "CARDSLEEP" : "REGSLEEP");

host->dpm_state = ENABLED;

- dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n");
+ mmc_release_host(host->mmc);

return 0;
}

-/* Handler for [REGSLEEP -> ENABLED] transition */
-static int omap_mmc_regsleep_to_enabled(struct mmc_omap_host *host)
+/* Handler for [OFF -> ENABLED] transition */
+static int omap_mmc_off_to_enabled(struct mmc_omap_host *host)
{
- unsigned long timeout;
-
- dev_dbg(mmc_dev(host->mmc), "REGSLEEP -> ENABLED\n");
-
clk_enable(host->fclk);
- clk_enable(host->iclk);
-
- if (clk_enable(host->dbclk))
- dev_dbg(mmc_dev(host->mmc),
- "Enabling debounce clk failed\n");

omap_mmc_restore_ctx(host);
-
- /*
- * We turned off interrupts and bus power. Interrupts
- * are turned on by 'mmc_omap_start_command()' so we
- * just need to turn on the bus power here.
- */
- OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
-
- timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
- while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP &&
- time_before(jiffies, timeout))
- ;
-
- if (mmc_slot(host).set_sleep)
- mmc_slot(host).set_sleep(host->dev, host->slot_id,
- 0, host->vdd, 0);
+ omap_hsmmc_init(host);
+ mmc_power_restore_host(host->mmc);

host->dpm_state = ENABLED;

+ dev_dbg(mmc_dev(host->mmc), "OFF -> ENABLED\n");
+
return 0;
}

@@ -1335,8 +1345,9 @@ static int omap_mmc_enable(struct mmc_host *mmc)
switch (host->dpm_state) {
case DISABLED:
return omap_mmc_disabled_to_enabled(host);
+ case CARDSLEEP:
case REGSLEEP:
- return omap_mmc_regsleep_to_enabled(host);
+ return omap_mmc_sleep_to_enabled(host);
case OFF:
return omap_mmc_off_to_enabled(host);
default:
@@ -1362,7 +1373,10 @@ static int omap_mmc_disable(struct mmc_host *mmc, int lazy)
return 0;
}
case DISABLED:
- return omap_mmc_disabled_to_off(host);
+ return omap_mmc_disabled_to_sleep(host);
+ case CARDSLEEP:
+ case REGSLEEP:
+ return omap_mmc_sleep_to_off(host);
default:
dev_dbg(mmc_dev(host->mmc), "UNKNOWN state\n");
return -EINVAL;
@@ -1434,8 +1448,7 @@ static int mmc_regs_show(struct seq_file *s, void *data)
host->dpm_state, mmc->nesting_cnt,
host->context_loss, context_loss);

- if (host->suspended || host->dpm_state == OFF ||
- host->dpm_state == REGSLEEP) {
+ if (host->suspended || host->dpm_state == OFF) {
seq_printf(s, "host suspended, can't read registers\n");
return 0;
}
@@ -1610,7 +1623,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size;

- mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+ mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
+ MMC_CAP_WAIT_WHILE_BUSY;

if (pdata->slots[host->slot_id].wires >= 8)
mmc->caps |= MMC_CAP_8_BIT_DATA;
--
1.5.6.3

2009-07-28 10:43:57

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 23/32] omap_hsmmc: cleanup macro usage

>From 6a8acf9de3b12869c76ff2819e61c54c81b2c753 Mon Sep 17 00:00:00 2001
From: Denis Karpov <[email protected]>
Date: Thu, 14 May 2009 09:11:38 +0200
Subject: [PATCH] omap_hsmmc: cleanup macro usage

Use macro mmc_slot() in omap_hsmmc.

Signed-off-by: Denis Karpov <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 42 ++++++++++++++++++----------------------
1 files changed, 19 insertions(+), 23 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 4309031..0373bdd 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -366,9 +366,8 @@ int mmc_omap_cover_is_closed(struct mmc_omap_host *host)
{
int r = 1;

- if (host->pdata->slots[host->slot_id].get_cover_state)
- r = host->pdata->slots[host->slot_id].get_cover_state(host->dev,
- host->slot_id);
+ if (mmc_slot(host).get_cover_state)
+ r = mmc_slot(host).get_cover_state(host->dev, host->slot_id);
return r;
}

@@ -391,9 +390,8 @@ mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
{
struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev);
struct mmc_omap_host *host = mmc_priv(mmc);
- struct omap_mmc_slot_data slot = host->pdata->slots[host->slot_id];

- return sprintf(buf, "%s\n", slot.name);
+ return sprintf(buf, "%s\n", mmc_slot(host).name);
}

static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
@@ -625,7 +623,8 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
(status & CMD_CRC)) {
if (host->cmd) {
if (status & CMD_TIMEOUT) {
- mmc_omap_reset_controller_fsm(host, SRC);
+ mmc_omap_reset_controller_fsm(host,
+ SRC);
host->cmd->error = -ETIMEDOUT;
} else {
host->cmd->error = -EILSEQ;
@@ -768,7 +767,7 @@ static void mmc_omap_detect(struct work_struct *work)

sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");

- if (mmc_slot(host).card_detect)
+ if (slot->card_detect)
carddetect = slot->card_detect(slot->card_detect_irq);
else
carddetect = -ENOSYS;
@@ -823,7 +822,7 @@ static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
sg_dma_address(sgl), 0, 0);
} else {
omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
+ (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
sg_dma_address(sgl), 0, 0);
}
@@ -911,7 +910,7 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
}

ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD",
- mmc_omap_dma_cb,host, &dma_ch);
+ mmc_omap_dma_cb, host, &dma_ch);
if (ret != 0) {
dev_err(mmc_dev(host->mmc),
"%s: omap_request_dma() failed with %d\n",
@@ -1131,21 +1130,19 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
{
struct mmc_omap_host *host = mmc_priv(mmc);
- struct omap_mmc_platform_data *pdata = host->pdata;

- if (!pdata->slots[0].card_detect)
+ if (!mmc_slot(host).card_detect)
return -ENOSYS;
- return pdata->slots[0].card_detect(pdata->slots[0].card_detect_irq);
+ return mmc_slot(host).card_detect(mmc_slot(host).card_detect_irq);
}

static int omap_hsmmc_get_ro(struct mmc_host *mmc)
{
struct mmc_omap_host *host = mmc_priv(mmc);
- struct omap_mmc_platform_data *pdata = host->pdata;

- if (!pdata->slots[0].get_ro)
+ if (!mmc_slot(host).get_ro)
return -ENOSYS;
- return pdata->slots[0].get_ro(host->dev, 0);
+ return mmc_slot(host).get_ro(host->dev, 0);
}

static void omap_hsmmc_init(struct mmc_omap_host *host)
@@ -1556,7 +1553,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);

- if (pdata->slots[host->slot_id].power_saving)
+ if (mmc_slot(host).power_saving)
mmc->ops = &mmc_omap_ps_ops;
else
mmc->ops = &mmc_omap_ops;
@@ -1626,12 +1623,12 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_WAIT_WHILE_BUSY;

- if (pdata->slots[host->slot_id].wires >= 8)
+ if (mmc_slot(host).wires >= 8)
mmc->caps |= MMC_CAP_8_BIT_DATA;
- else if (pdata->slots[host->slot_id].wires >= 4)
+ else if (mmc_slot(host).wires >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;

- if (pdata->slots[host->slot_id].nonremovable)
+ if (mmc_slot(host).nonremovable)
mmc->caps |= MMC_CAP_NONREMOVABLE;

mmc->caps |= MMC_CAP_SDIO | MMC_CAP_SD | MMC_CAP_MMC;
@@ -1695,13 +1692,12 @@ static int __init omap_mmc_probe(struct platform_device *pdev)

mmc_add_host(mmc);

- if (host->pdata->slots[host->slot_id].name != NULL) {
+ if (mmc_slot(host).name != NULL) {
ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name);
if (ret < 0)
goto err_slot_name;
}
- if (mmc_slot(host).card_detect_irq &&
- host->pdata->slots[host->slot_id].get_cover_state) {
+ if (mmc_slot(host).card_detect_irq && mmc_slot(host).get_cover_state) {
ret = device_create_file(&mmc->class_dev,
&dev_attr_cover_switch);
if (ret < 0)
@@ -1807,7 +1803,7 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state)


OMAP_HSMMC_WRITE(host->base, HCTL,
- OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
+ OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
mmc_host_disable(host->mmc);
clk_disable(host->iclk);
clk_disable(host->dbclk);
--
1.5.6.3

2009-07-28 10:44:59

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 12/32] omap_hsmmc: keep track of power mode

>From 3592682a0965d32d3498f7da11073ff4d88f1bf7 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Tue, 7 Jul 2009 16:44:11 +0300
Subject: [PATCH] omap_hsmmc: keep track of power mode

This patch is preparation for adding context save
and restore support.

Keep track of the current power mode so that the
context restore function can avoid restoring the
context for a card if the power has been switched
off. If the power is off, the card must be
reinitialized anyway which will re-establish the
context.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 27 +++++++++++++++++++--------
1 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index f9b7cfe..c28d055 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -141,6 +141,7 @@ struct mmc_omap_host {
unsigned int dma_len;
unsigned int dma_sg_idx;
unsigned char bus_mode;
+ unsigned char power_mode;
u32 *buffer;
u32 bytesleft;
int suspended;
@@ -856,16 +857,25 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
unsigned long regval;
unsigned long timeout;
u32 con;
+ int do_send_init_stream = 0;

mmc_host_enable(host->mmc);

- switch (ios->power_mode) {
- case MMC_POWER_OFF:
- mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
- break;
- case MMC_POWER_UP:
- mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd);
- break;
+ if (ios->power_mode != host->power_mode) {
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ mmc_slot(host).set_power(host->dev, host->slot_id,
+ 0, 0);
+ break;
+ case MMC_POWER_UP:
+ mmc_slot(host).set_power(host->dev, host->slot_id,
+ 1, ios->vdd);
+ break;
+ case MMC_POWER_ON:
+ do_send_init_stream = 1;
+ break;
+ }
+ host->power_mode = ios->power_mode;
}

con = OMAP_HSMMC_READ(host->base, CON);
@@ -931,7 +941,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);

- if (ios->power_mode == MMC_POWER_ON)
+ if (do_send_init_stream)
send_init_stream(host);

if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
@@ -1109,6 +1119,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
host->slot_id = 0;
host->mapbase = res->start;
host->base = ioremap(host->mapbase, SZ_4K);
+ host->power_mode = -1;

platform_set_drvdata(pdev, host);
INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
--
1.5.6.3

2009-07-28 10:39:55

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 8/32] mmc: check status after MMC SWITCH command

>From 98c0aabb4c04bcdf7d166c0fb7e2c859e33f8c70 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Wed, 3 Jun 2009 12:22:29 +0300
Subject: [PATCH] mmc: check status after MMC SWITCH command

According to the standard, the SWITCH command should
be followed by a SEND_STATUS command to check for
errors.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/core/mmc.c | 24 ++++++++++++++++++------
drivers/mmc/core/mmc_ops.c | 23 +++++++++++++++++++++++
include/linux/mmc/mmc.h | 1 +
3 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 8e2e3d2..f87cc0b 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -416,12 +416,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, 1);
- if (err)
+ if (err && err != -EBADMSG)
goto free_card;

- mmc_card_set_highspeed(card);
-
- mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+ if (err) {
+ printk(KERN_WARNING "%s: switch to highspeed failed\n",
+ mmc_hostname(card->host));
+ err = 0;
+ } else {
+ mmc_card_set_highspeed(card);
+ mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+ }
}

/*
@@ -456,10 +461,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, ext_csd_bit);

- if (err)
+ if (err && err != -EBADMSG)
goto free_card;

- mmc_set_bus_width(card->host, bus_width);
+ if (err) {
+ printk(KERN_WARNING "%s: switch to bus width %d "
+ "failed\n", mmc_hostname(card->host),
+ 1 << bus_width);
+ err = 0;
+ } else {
+ mmc_set_bus_width(card->host, bus_width);
+ }
}

if (!oldcard)
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 355c604..d2cb5c6 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -390,6 +390,7 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
{
int err;
struct mmc_command cmd;
+ u32 status;

BUG_ON(!card);
BUG_ON(!card->host);
@@ -407,6 +408,28 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
if (err)
return err;

+ /* Must check status to be sure of no errors */
+ do {
+ err = mmc_send_status(card, &status);
+ if (err)
+ return err;
+ if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ break;
+ if (mmc_host_is_spi(card->host))
+ break;
+ } while (R1_CURRENT_STATE(status) == 7);
+
+ if (mmc_host_is_spi(card->host)) {
+ if (status & R1_SPI_ILLEGAL_COMMAND)
+ return -EBADMSG;
+ } else {
+ if (status & 0xFDFFA000)
+ printk(KERN_WARNING "%s: unexpected status %#x after "
+ "switch", mmc_hostname(card->host), status);
+ if (status & R1_SWITCH_ERROR)
+ return -EBADMSG;
+ }
+
return 0;
}

diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index b2b4095..c02c8db 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -128,6 +128,7 @@
#define R1_STATUS(x) (x & 0xFFFFE000)
#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
#define R1_READY_FOR_DATA (1 << 8) /* sx, a */
+#define R1_SWITCH_ERROR (1 << 7) /* sx, c */
#define R1_APP_CMD (1 << 5) /* sr, c */

/*
--
1.5.6.3

2009-07-28 10:46:13

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 1/32] mmc: add 'enable' and 'disable' methods to mmc host

>From 4610f1ede273078f99214c6284202998aa537a1d Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Wed, 22 Apr 2009 12:50:45 +0300
Subject: [PATCH] mmc: add 'enable' and 'disable' methods to mmc host

MMC hosts that support power saving can use the 'enable' and
'disable' methods to exit and enter power saving states.
An explanation of their use is provided in the comments
added to include/linux/mmc/host.h.

Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/core/core.c | 177 ++++++++++++++++++++++++++++++++++++++++++++--
drivers/mmc/core/host.c | 1 +
drivers/mmc/core/host.h | 2 +
include/linux/mmc/host.h | 47 ++++++++++++
4 files changed, 221 insertions(+), 6 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index d84c880..9bc8d27 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -344,6 +344,101 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
EXPORT_SYMBOL(mmc_align_data_size);

/**
+ * mmc_host_enable - enable a host.
+ * @host: mmc host to enable
+ *
+ * Hosts that support power saving can use the 'enable' and 'disable'
+ * methods to exit and enter power saving states. For more information
+ * see comments for struct mmc_host_ops.
+ */
+int mmc_host_enable(struct mmc_host *host)
+{
+ if (!(host->caps & MMC_CAP_DISABLE))
+ return 0;
+
+ if (host->en_dis_recurs)
+ return 0;
+
+ if (host->nesting_cnt++)
+ return 0;
+
+ cancel_delayed_work_sync(&host->disable);
+
+ if (host->enabled)
+ return 0;
+
+ if (host->ops->enable) {
+ int err;
+
+ host->en_dis_recurs = 1;
+ err = host->ops->enable(host);
+ host->en_dis_recurs = 0;
+
+ if (err) {
+ pr_debug("%s: enable error %d\n",
+ mmc_hostname(host), err);
+ return err;
+ }
+ }
+ host->enabled = 1;
+ return 0;
+}
+EXPORT_SYMBOL(mmc_host_enable);
+
+static int mmc_host_do_disable(struct mmc_host *host, int lazy)
+{
+ if (host->ops->disable) {
+ int err;
+
+ host->en_dis_recurs = 1;
+ err = host->ops->disable(host, lazy);
+ host->en_dis_recurs = 0;
+
+ if (err < 0) {
+ pr_debug("%s: disable error %d\n",
+ mmc_hostname(host), err);
+ return err;
+ }
+ if (err > 0) {
+ unsigned long delay = msecs_to_jiffies(err);
+
+ mmc_schedule_delayed_work(&host->disable, delay);
+ }
+ }
+ host->enabled = 0;
+ return 0;
+}
+
+/**
+ * mmc_host_disable - disable a host.
+ * @host: mmc host to disable
+ *
+ * Hosts that support power saving can use the 'enable' and 'disable'
+ * methods to exit and enter power saving states. For more information
+ * see comments for struct mmc_host_ops.
+ */
+int mmc_host_disable(struct mmc_host *host)
+{
+ int err;
+
+ if (!(host->caps & MMC_CAP_DISABLE))
+ return 0;
+
+ if (host->en_dis_recurs)
+ return 0;
+
+ if (--host->nesting_cnt)
+ return 0;
+
+ if (!host->enabled)
+ return 0;
+
+ err = mmc_host_do_disable(host, 0);
+ return err;
+}
+EXPORT_SYMBOL(mmc_host_disable);
+
+/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
* @abort: whether or not the operation should be aborted
@@ -379,11 +474,81 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait);
+ if (!stop)
+ mmc_host_enable(host);
return stop;
}

EXPORT_SYMBOL(__mmc_claim_host);

+static int mmc_try_claim_host(struct mmc_host *host)
+{
+ int claimed_host = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ if (!host->claimed) {
+ host->claimed = 1;
+ claimed_host = 1;
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+ return claimed_host;
+}
+
+static void mmc_do_release_host(struct mmc_host *host)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+ host->claimed = 0;
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ wake_up(&host->wq);
+}
+
+void mmc_host_deeper_disable(struct work_struct *work)
+{
+ struct mmc_host *host =
+ container_of(work, struct mmc_host, disable.work);
+
+ /* If the host is claimed then we do not want to disable it anymore */
+ if (!mmc_try_claim_host(host))
+ return;
+ mmc_host_do_disable(host, 1);
+ mmc_do_release_host(host);
+}
+
+/**
+ * mmc_host_lazy_disable - lazily disable a host.
+ * @host: mmc host to disable
+ *
+ * Hosts that support power saving can use the 'enable' and 'disable'
+ * methods to exit and enter power saving states. For more information
+ * see comments for struct mmc_host_ops.
+ */
+int mmc_host_lazy_disable(struct mmc_host *host)
+{
+ if (!(host->caps & MMC_CAP_DISABLE))
+ return 0;
+
+ if (host->en_dis_recurs)
+ return 0;
+
+ if (--host->nesting_cnt)
+ return 0;
+
+ if (!host->enabled)
+ return 0;
+
+ if (host->disable_delay) {
+ mmc_schedule_delayed_work(&host->disable,
+ msecs_to_jiffies(host->disable_delay));
+ return 0;
+ } else
+ return mmc_host_do_disable(host, 1);
+}
+EXPORT_SYMBOL(mmc_host_lazy_disable);
+
/**
* mmc_release_host - release a host
* @host: mmc host to release
@@ -393,15 +558,11 @@ EXPORT_SYMBOL(__mmc_claim_host);
*/
void mmc_release_host(struct mmc_host *host)
{
- unsigned long flags;
-
WARN_ON(!host->claimed);

- spin_lock_irqsave(&host->lock, flags);
- host->claimed = 0;
- spin_unlock_irqrestore(&host->lock, flags);
+ mmc_host_lazy_disable(host);

- wake_up(&host->wq);
+ mmc_do_release_host(host);
}

EXPORT_SYMBOL(mmc_release_host);
@@ -947,6 +1108,8 @@ void mmc_stop_host(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
#endif

+ if (host->caps & MMC_CAP_DISABLE)
+ cancel_delayed_work(&host->disable);
cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work();

@@ -975,6 +1138,8 @@ void mmc_stop_host(struct mmc_host *host)
*/
int mmc_suspend_host(struct mmc_host *host, pm_message_t state)
{
+ if (host->caps & MMC_CAP_DISABLE)
+ cancel_delayed_work(&host->disable);
cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work();

diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 5e945e6..a268d12 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -83,6 +83,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
+ INIT_DELAYED_WORK_DEFERRABLE(&host->disable, mmc_host_deeper_disable);

/*
* By default, hosts do not support SGIO or large requests.
diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h
index c2dc3d2..8c87e11 100644
--- a/drivers/mmc/core/host.h
+++ b/drivers/mmc/core/host.h
@@ -14,5 +14,7 @@
int mmc_register_host_class(void);
void mmc_unregister_host_class(void);

+void mmc_host_deeper_disable(struct work_struct *work);
+
#endif

diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 3e7615e..338a9b3 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -51,6 +51,35 @@ struct mmc_ios {
};

struct mmc_host_ops {
+ /*
+ * Hosts that support power saving can use the 'enable' and 'disable'
+ * methods to exit and enter power saving states. 'enable' is called
+ * when the host is claimed and 'disable' is called (or scheduled with
+ * a delay) when the host is released. The 'disable' is scheduled if
+ * the disable delay set by 'mmc_set_disable_delay()' is non-zero,
+ * otherwise 'disable' is called immediately. 'disable' may be
+ * scheduled repeatedly, to permit ever greater power saving at the
+ * expense of ever greater latency to re-enable. Rescheduling is
+ * determined by the return value of the 'disable' method. A positive
+ * value gives the delay in milliseconds.
+ *
+ * In the case where a host function (like set_ios) may be called
+ * with or without the host claimed, enabling and disabling can be
+ * done directly and will nest correctly. Call 'mmc_host_enable()' and
+ * 'mmc_host_lazy_disable()' for this purpose, but note that these
+ * functions must be paired.
+ *
+ * Alternatively, 'mmc_host_enable()' may be paired with
+ * 'mmc_host_disable()' which calls 'disable' immediately. In this
+ * case the 'disable' method will be called with 'lazy' set to 0.
+ * This is mainly useful for error paths.
+ *
+ * Because lazy disable may be called from a work queue, the 'disable'
+ * method must claim the host when 'lazy' != 0, which will work
+ * correctly because recursion is detected and handled.
+ */
+ int (*enable)(struct mmc_host *host);
+ int (*disable)(struct mmc_host *host, int lazy);
void (*request)(struct mmc_host *host, struct mmc_request *req);
/*
* Avoid calling these three functions too often or in a "fast path",
@@ -118,6 +147,7 @@ struct mmc_host {
#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
+#define MMC_CAP_DISABLE (1 << 7) /* Can the host be disabled */

/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
@@ -142,6 +172,13 @@ struct mmc_host {
unsigned int removed:1; /* host is being removed */
#endif

+ /* Only used with MMC_CAP_DISABLE */
+ int enabled; /* host is enabled */
+ int nesting_cnt; /* "enable" nesting count */
+ int en_dis_recurs; /* detect recursion */
+ unsigned int disable_delay; /* disable delay in msecs */
+ struct delayed_work disable; /* disabling work */
+
struct mmc_card *card; /* device attached to this host */

wait_queue_head_t wq;
@@ -197,5 +234,15 @@ struct regulator;
int mmc_regulator_get_ocrmask(struct regulator *supply);
int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit);

+int mmc_host_enable(struct mmc_host *host);
+int mmc_host_disable(struct mmc_host *host);
+int mmc_host_lazy_disable(struct mmc_host *host);
+
+static inline void mmc_set_disable_delay(struct mmc_host *host,
+ unsigned int disable_delay)
+{
+ host->disable_delay = disable_delay;
+}
+
#endif

--
1.5.6.3

2009-07-28 10:40:06

by Adrian Hunter

[permalink] [raw]
Subject: [PATCH V2 14/32] omap_hsmmc: set open drain bit correctly

>From 60dc8abdafb94824a5984f769a05c9b82f784e6b Mon Sep 17 00:00:00 2001
From: Denis Karpov <[email protected]>
Date: Thu, 23 Apr 2009 16:44:58 +0300
Subject: [PATCH] omap_hsmmc: set open drain bit correctly

The code could set the bit to 1 but not reset it to 0.

Signed-off-by: Denis Karpov <[email protected]>
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index ac1a3bf..2b242c2 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1111,9 +1111,11 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (do_send_init_stream)
send_init_stream(host);

+ con = OMAP_HSMMC_READ(host->base, CON);
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
- OMAP_HSMMC_WRITE(host->base, CON,
- OMAP_HSMMC_READ(host->base, CON) | OD);
+ OMAP_HSMMC_WRITE(host->base, CON, con | OD);
+ else
+ OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);

mmc_host_lazy_disable(host->mmc);
}
--
1.5.6.3

2009-07-29 11:13:55

by Matt Fleming

[permalink] [raw]
Subject: Re: [PATCH V2 0/32] mmc and omap_hsmmc patches

On Tue, Jul 28, 2009 at 01:38:34PM +0300, Adrian Hunter wrote:
> Hi
>
> Here is version 2 of our 32 patches for mmc and omap_hsmmc.
>
> They include Matt Fleming's change for card caps, and 2 other fixes:
> - use a spin lock rather than enable / diable irq
> - make disable delay specified in milliseconds not jiffies because the
> value is an int, and jiffies are unsigned long (also millisecs are
> better anyway)
>

Thanks for doing this Adrian. I appreciate it. My Reviewed-by tag still
applies.

2009-07-30 01:06:23

by Madhusudhan

[permalink] [raw]
Subject: RE: [PATCH V2 4/32] mmc: add ability to save power by powering off cards



> -----Original Message-----
> From: [email protected] [mailto:linux-omap-
> [email protected]] On Behalf Of Adrian Hunter
> Sent: Tuesday, July 28, 2009 5:39 AM
> To: Andrew Morton
> Cc: Jarkko Lavinen; Adrian Hunter; linux-omap Mailing List; Pierre Ossman;
> Denis Karpov; Matt Fleming; lkml
> Subject: [PATCH V2 4/32] mmc: add ability to save power by powering off
> cards
>
> From e6355578d082d94707c8fda1e1342c478019b5c8 Mon Sep 17 00:00:00 2001
> From: Adrian Hunter <[email protected]>
> Date: Mon, 11 May 2009 12:20:57 +0300
> Subject: [PATCH] mmc: add ability to save power by powering off cards
>
> Power can be saved by powering off cards that are not
> in use. This is similar to suspend / resume except
> it is under the control of the driver, and does not
> require any power management support. It can only
> be used when the driver can monitor whether the card
> is removed, otherwise it is unsafe. This is possible
> because, unlike suspend, the driver still receives
> card detect and / or cover switch interrupts.
>
> Signed-off-by: Adrian Hunter <[email protected]>
> ---
> drivers/mmc/core/core.c | 34 ++++++++++++++++++++++++++++++++++
> drivers/mmc/core/core.h | 2 ++
> drivers/mmc/core/mmc.c | 11 +++++++++++
> drivers/mmc/core/sd.c | 11 +++++++++++
> include/linux/mmc/host.h | 3 +++
> 5 files changed, 61 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index bab5015..39f7bd1 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -1145,6 +1145,40 @@ void mmc_stop_host(struct mmc_host *host)
> mmc_power_off(host);
> }
>
> +void mmc_power_save_host(struct mmc_host *host)
> +{
> + mmc_bus_get(host);
> +
> + if (!host->bus_ops || host->bus_dead || !host->bus_ops-
> >power_restore) {
> + mmc_bus_put(host);
> + return;
> + }
> +
> + if (host->bus_ops->power_save)
> + host->bus_ops->power_save(host);
> +
> + mmc_bus_put(host);
> +
> + mmc_power_off(host);
> +}
> +EXPORT_SYMBOL(mmc_power_save_host);
> +
> +void mmc_power_restore_host(struct mmc_host *host)
> +{
> + mmc_bus_get(host);
> +
> + if (!host->bus_ops || host->bus_dead || !host->bus_ops-
> >power_restore) {
> + mmc_bus_put(host);
> + return;
> + }
> +
> + mmc_power_up(host);
> + host->bus_ops->power_restore(host);
> +
> + mmc_bus_put(host);
> +}
> +EXPORT_SYMBOL(mmc_power_restore_host);
> +

Who calls these exported functions " mmc_power_save_host" and "
mmc_power_restore_host"?


> #ifdef CONFIG_PM
>
> /**
> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
> index c819eff..f7eb4c4 100644
> --- a/drivers/mmc/core/core.h
> +++ b/drivers/mmc/core/core.h
> @@ -20,6 +20,8 @@ struct mmc_bus_ops {
> void (*detect)(struct mmc_host *);
> void (*suspend)(struct mmc_host *);
> void (*resume)(struct mmc_host *);
> + void (*power_save)(struct mmc_host *);
> + void (*power_restore)(struct mmc_host *);
> };
>
> void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops
> *ops);
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 3e35075..01f7226 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -549,6 +549,14 @@ static void mmc_resume(struct mmc_host *host)
>
> }
>
> +static void mmc_power_restore(struct mmc_host *host)
> +{
> + host->card->state &= ~MMC_STATE_HIGHSPEED;
> + mmc_claim_host(host);
> + mmc_init_card(host, host->ocr, host->card);
> + mmc_release_host(host);
> +}
> +
> #ifdef CONFIG_MMC_UNSAFE_RESUME
>
> static const struct mmc_bus_ops mmc_ops = {
> @@ -556,6 +564,7 @@ static const struct mmc_bus_ops mmc_ops = {
> .detect = mmc_detect,
> .suspend = mmc_suspend,
> .resume = mmc_resume,
> + .power_restore = mmc_power_restore,
> };
>
> static void mmc_attach_bus_ops(struct mmc_host *host)
> @@ -570,6 +579,7 @@ static const struct mmc_bus_ops mmc_ops = {
> .detect = mmc_detect,
> .suspend = NULL,
> .resume = NULL,
> + .power_restore = mmc_power_restore,
> };
>
> static const struct mmc_bus_ops mmc_ops_unsafe = {
> @@ -577,6 +587,7 @@ static const struct mmc_bus_ops mmc_ops_unsafe = {
> .detect = mmc_detect,
> .suspend = mmc_suspend,
> .resume = mmc_resume,
> + .power_restore = mmc_power_restore,
> };
>
> static void mmc_attach_bus_ops(struct mmc_host *host)
> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
> index 80cccd2..debe26e 100644
> --- a/drivers/mmc/core/sd.c
> +++ b/drivers/mmc/core/sd.c
> @@ -603,6 +603,14 @@ static void mmc_sd_resume(struct mmc_host *host)
>
> }
>
> +static void mmc_sd_power_restore(struct mmc_host *host)
> +{
> + host->card->state &= ~MMC_STATE_HIGHSPEED;
> + mmc_claim_host(host);
> + mmc_sd_init_card(host, host->ocr, host->card);
> + mmc_release_host(host);
> +}
> +
> #ifdef CONFIG_MMC_UNSAFE_RESUME
>
> static const struct mmc_bus_ops mmc_sd_ops = {
> @@ -610,6 +618,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
> .detect = mmc_sd_detect,
> .suspend = mmc_sd_suspend,
> .resume = mmc_sd_resume,
> + .power_restore = mmc_sd_power_restore,
> };
>
> static void mmc_sd_attach_bus_ops(struct mmc_host *host)
> @@ -624,6 +633,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
> .detect = mmc_sd_detect,
> .suspend = NULL,
> .resume = NULL,
> + .power_restore = mmc_sd_power_restore,
> };
>
> static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
> @@ -631,6 +641,7 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
> .detect = mmc_sd_detect,
> .suspend = mmc_sd_suspend,
> .resume = mmc_sd_resume,
> + .power_restore = mmc_sd_power_restore,
> };
>
> static void mmc_sd_attach_bus_ops(struct mmc_host *host)
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index bb867d2..c1cbe59 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -223,6 +223,9 @@ static inline void *mmc_priv(struct mmc_host *host)
> extern int mmc_suspend_host(struct mmc_host *, pm_message_t);
> extern int mmc_resume_host(struct mmc_host *);
>
> +extern void mmc_power_save_host(struct mmc_host *host);
> +extern void mmc_power_restore_host(struct mmc_host *host);
> +
> extern void mmc_detect_change(struct mmc_host *, unsigned long delay);
> extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
>
> --
> 1.5.6.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2009-07-30 02:01:12

by Madhusudhan

[permalink] [raw]
Subject: RE: [PATCH V2 13/32] omap_hsmmc: context save/restore support



> -----Original Message-----
> From: [email protected] [mailto:linux-omap-
> [email protected]] On Behalf Of Adrian Hunter
> Sent: Tuesday, July 28, 2009 5:40 AM
> To: Andrew Morton
> Cc: Jarkko Lavinen; Adrian Hunter; linux-omap Mailing List; Pierre Ossman;
> Denis Karpov; Matt Fleming; lkml
> Subject: [PATCH V2 13/32] omap_hsmmc: context save/restore support
>
> From 43e9fa346d7e386328876a8535dc8619bd1f47ae Mon Sep 17 00:00:00 2001
> From: Denis Karpov <[email protected]>
> Date: Wed, 22 Apr 2009 16:04:25 +0200
> Subject: [PATCH] omap_hsmmc: context save/restore support
>
> Keep the context over PM dynamic OFF states.
>
> Signed-off-by: Denis Karpov <[email protected]>
> Signed-off-by: Adrian Hunter <[email protected]>
> ---
> drivers/mmc/host/omap_hsmmc.c | 194
> ++++++++++++++++++++++++++++++++++++++--
> 1 files changed, 184 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index c28d055..ac1a3bf 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -37,6 +37,7 @@
>
> /* OMAP HSMMC Host Controller Registers */
> #define OMAP_HSMMC_SYSCONFIG 0x0010
> +#define OMAP_HSMMC_SYSSTATUS 0x0014
> #define OMAP_HSMMC_CON 0x002C
> #define OMAP_HSMMC_BLK 0x0104
> #define OMAP_HSMMC_ARG 0x0108
> @@ -94,6 +95,8 @@
> #define DUAL_VOLT_OCR_BIT 7
> #define SRC (1 << 25)
> #define SRD (1 << 26)
> +#define SOFTRESET (1 << 1)
> +#define RESETDONE (1 << 0)
>
> /*
> * FIXME: Most likely all the data using these _DEVID defines should come
> @@ -152,6 +155,8 @@ struct mmc_omap_host {
> int slot_id;
> int dbclk_enabled;
> int response_busy;
> + int context_loss;
> +
> struct omap_mmc_platform_data *pdata;
> };
>
> @@ -166,6 +171,166 @@ static void omap_mmc_stop_clock(struct mmc_omap_host
> *host)
> dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
> }
>
> +#ifdef CONFIG_PM
> +
> +/*
> + * Restore the MMC host context, if it was lost as result of a
> + * power state change.
> + */
> +static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
> +{
> + struct mmc_ios *ios = &host->mmc->ios;
> + struct omap_mmc_platform_data *pdata = host->pdata;
> + int context_loss = 0;
> + u32 hctl, capa, con;
> + u16 dsor = 0;
> + unsigned long timeout;
> +
> + if (pdata->get_context_loss_count) {
> + context_loss = pdata->get_context_loss_count(host->dev);
> + if (context_loss < 0)
> + return 1;
> + }
This seems to restore the context always. The context should be restored
only if the card was powered OFF, Right?

The context could also be lost if the CORE transitions to OFF. I assume that
case gets handled here without anything extra required if "power_saving" is
set to true. Am I right?

How about the case of eMMC? Since it is not a removable device the card is
not powered OFF. But the CORE OFF would result in context loss. Do we hit
the restore_ctx path in that case?

> +
> + dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
> + context_loss == host->context_loss ? "not " : "");
> + if (host->context_loss == context_loss)
> + return 1;
> +
> + /* Wait for hardware reset */
> + timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> + while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) !=
> RESETDONE
> + && time_before(jiffies, timeout))
> + ;
> +
> + /* Do software reset */
> + OMAP_HSMMC_WRITE(host->base, SYSCONFIG, SOFTRESET);
> + timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> + while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) !=
> RESETDONE
> + && time_before(jiffies, timeout))
> + ;
> +
> + OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
> + OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
> +
> + if (host->id == OMAP_MMC1_DEVID) {
> + if (host->power_mode != MMC_POWER_OFF &&
> + (1 << ios->vdd) <= MMC_VDD_23_24)
> + hctl = SDVS18;
> + else
> + hctl = SDVS30;
> + capa = VS30 | VS18;
> + } else {
> + hctl = SDVS18;
> + capa = VS18;
> + }
> +
> + OMAP_HSMMC_WRITE(host->base, HCTL,
> + OMAP_HSMMC_READ(host->base, HCTL) | hctl);
> +
> + OMAP_HSMMC_WRITE(host->base, CAPA,
> + OMAP_HSMMC_READ(host->base, CAPA) | capa);
> +
> + OMAP_HSMMC_WRITE(host->base, HCTL,
> + OMAP_HSMMC_READ(host->base, HCTL) | SDBP);
> +
> + timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> + while ((OMAP_HSMMC_READ(host->base, HCTL) & SDBP) != SDBP
> + && time_before(jiffies, timeout))
> + ;
> +
> + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
> + OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
> + OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
> +
> + /* Do not initialize card-specific things if the power is off */
> + if (host->power_mode == MMC_POWER_OFF)
> + goto out;
> +
> + con = OMAP_HSMMC_READ(host->base, CON);
> + switch (ios->bus_width) {
> + case MMC_BUS_WIDTH_8:
> + OMAP_HSMMC_WRITE(host->base, CON, con | DW8);
> + break;
> + case MMC_BUS_WIDTH_4:
> + OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
> + OMAP_HSMMC_WRITE(host->base, HCTL,
> + OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT);
> + break;
> + case MMC_BUS_WIDTH_1:
> + OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8);
> + OMAP_HSMMC_WRITE(host->base, HCTL,
> + OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT);
> + break;
> + }
> +
> + if (ios->clock) {
> + dsor = OMAP_MMC_MASTER_CLOCK / ios->clock;
> + if (dsor < 1)
> + dsor = 1;
> +
> + if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
> + dsor++;
> +
> + if (dsor > 250)
> + dsor = 250;
> + }
> +
> + OMAP_HSMMC_WRITE(host->base, SYSCTL,
> + OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
> + OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16));
> + OMAP_HSMMC_WRITE(host->base, SYSCTL,
> + OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
> +
> + timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> + while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
> + && time_before(jiffies, timeout))
> + ;
> +
> + OMAP_HSMMC_WRITE(host->base, SYSCTL,
> + OMAP_HSMMC_READ(host->base, SYSCTL) | CEN);
> +
> + con = OMAP_HSMMC_READ(host->base, CON);
> + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
> + OMAP_HSMMC_WRITE(host->base, CON, con | OD);
> + else
> + OMAP_HSMMC_WRITE(host->base, CON, con & ~OD);
> +out:
> + host->context_loss = context_loss;
> +
> + dev_dbg(mmc_dev(host->mmc), "context is restored\n");
> + return 0;
> +}
> +
> +/*
> + * Save the MMC host context (store the number of power state changes so
> far).
> + */
> +static void omap_mmc_save_ctx(struct mmc_omap_host *host)
> +{
> + struct omap_mmc_platform_data *pdata = host->pdata;
> + int context_loss;
> +
> + if (pdata->get_context_loss_count) {
> + context_loss = pdata->get_context_loss_count(host->dev);
> + if (context_loss < 0)
> + return;
> + host->context_loss = context_loss;
> + }
> +}
> +
> +#else
> +
> +static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
> +{
> + return 0;
> +}
> +
> +static void omap_mmc_save_ctx(struct mmc_omap_host *host)
> +{
> +}
> +
> +#endif
> +
> /*
> * Send init stream sequence to card
> * before sending IDLE command
> @@ -823,6 +988,7 @@ static int omap_mmc_enable(struct mmc_host *mmc)
> if (err)
> return err;
> dev_dbg(mmc_dev(host->mmc), "mmc_fclk: enabled\n");
> + omap_mmc_restore_ctx(host);
> return 0;
> }
>
> @@ -830,6 +996,7 @@ static int omap_mmc_disable(struct mmc_host *mmc, int
> lazy)
> {
> struct mmc_omap_host *host = mmc_priv(mmc);
>
> + omap_mmc_save_ctx(host);
> clk_disable(host->fclk);
> dev_dbg(mmc_dev(host->mmc), "mmc_fclk: disabled\n");
> return 0;
> @@ -934,7 +1101,7 @@ static void omap_mmc_set_ios(struct mmc_host *mmc,
> struct mmc_ios *ios)
>
> /* Wait till the ICS bit is set */
> timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
> - while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2
> + while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != ICS
> && time_before(jiffies, timeout))
> msleep(1);
>
> @@ -1014,12 +1181,19 @@ static int mmc_regs_show(struct seq_file *s, void
> *data)
> {
> struct mmc_host *mmc = s->private;
> struct mmc_omap_host *host = mmc_priv(mmc);
> + struct omap_mmc_platform_data *pdata = host->pdata;
> + int context_loss = 0;
> +
> + if (pdata->get_context_loss_count)
> + context_loss = pdata->get_context_loss_count(host->dev);
>
> seq_printf(s, "mmc%d:\n"
> " enabled:\t%d\n"
> " nesting_cnt:\t%d\n"
> + " ctx_loss:\t%d:%d\n"
> "\nregs:\n",
> - mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt);
> + mmc->index, mmc->enabled ? 1 : 0, mmc->nesting_cnt,
> + host->context_loss, context_loss);
>
> if (clk_enable(host->fclk) != 0) {
> seq_printf(s, "can't read the regs\n");
> @@ -1144,6 +1318,8 @@ static int __init omap_mmc_probe(struct
> platform_device *pdev)
> goto err1;
> }
>
> + omap_mmc_save_ctx(host);
> +
> mmc->caps |= MMC_CAP_DISABLE;
> mmc_set_disable_delay(mmc, 100);
> if (mmc_host_enable(host->mmc) != 0) {
> @@ -1380,21 +1556,19 @@ static int omap_mmc_resume(struct platform_device
> *pdev)
> return 0;
>
> if (host) {
> -
> - if (mmc_host_enable(host->mmc) != 0)
> - goto clk_en_err;
> -
> ret = clk_enable(host->iclk);
> - if (ret) {
> - mmc_host_disable(host->mmc);
> - clk_put(host->fclk);
> + if (ret)
> goto clk_en_err;
> - }
>
> if (clk_enable(host->dbclk) != 0)
> dev_dbg(mmc_dev(host->mmc),
> "Enabling debounce clk failed\n");
>
> + if (mmc_host_enable(host->mmc) != 0) {
> + clk_disable(host->iclk);
> + goto clk_en_err;
> + }
> +
> omap_hsmmc_init(host);
>
> if (host->pdata->resume) {
> --
> 1.5.6.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2009-07-30 07:16:18

by Adrian Hunter

[permalink] [raw]
Subject: Re: [PATCH V2 4/32] mmc: add ability to save power by powering off cards

Madhusudhan wrote:
>
>> -----Original Message-----
>> From: [email protected] [mailto:linux-omap-
>> [email protected]] On Behalf Of Adrian Hunter
>> Sent: Tuesday, July 28, 2009 5:39 AM
>> To: Andrew Morton
>> Cc: Jarkko Lavinen; Adrian Hunter; linux-omap Mailing List; Pierre Ossman;
>> Denis Karpov; Matt Fleming; lkml
>> Subject: [PATCH V2 4/32] mmc: add ability to save power by powering off
>> cards
>>
>> From e6355578d082d94707c8fda1e1342c478019b5c8 Mon Sep 17 00:00:00 2001
>> From: Adrian Hunter <[email protected]>
>> Date: Mon, 11 May 2009 12:20:57 +0300
>> Subject: [PATCH] mmc: add ability to save power by powering off cards
>>
>> Power can be saved by powering off cards that are not
>> in use. This is similar to suspend / resume except
>> it is under the control of the driver, and does not
>> require any power management support. It can only
>> be used when the driver can monitor whether the card
>> is removed, otherwise it is unsafe. This is possible
>> because, unlike suspend, the driver still receives
>> card detect and / or cover switch interrupts.
>>
>> Signed-off-by: Adrian Hunter <[email protected]>
>> ---
>> drivers/mmc/core/core.c | 34 ++++++++++++++++++++++++++++++++++
>> drivers/mmc/core/core.h | 2 ++
>> drivers/mmc/core/mmc.c | 11 +++++++++++
>> drivers/mmc/core/sd.c | 11 +++++++++++
>> include/linux/mmc/host.h | 3 +++
>> 5 files changed, 61 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index bab5015..39f7bd1 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -1145,6 +1145,40 @@ void mmc_stop_host(struct mmc_host *host)
>> mmc_power_off(host);
>> }
>>
>> +void mmc_power_save_host(struct mmc_host *host)
>> +{
>> + mmc_bus_get(host);
>> +
>> + if (!host->bus_ops || host->bus_dead || !host->bus_ops-
>>> power_restore) {
>> + mmc_bus_put(host);
>> + return;
>> + }
>> +
>> + if (host->bus_ops->power_save)
>> + host->bus_ops->power_save(host);
>> +
>> + mmc_bus_put(host);
>> +
>> + mmc_power_off(host);
>> +}
>> +EXPORT_SYMBOL(mmc_power_save_host);
>> +
>> +void mmc_power_restore_host(struct mmc_host *host)
>> +{
>> + mmc_bus_get(host);
>> +
>> + if (!host->bus_ops || host->bus_dead || !host->bus_ops-
>>> power_restore) {
>> + mmc_bus_put(host);
>> + return;
>> + }
>> +
>> + mmc_power_up(host);
>> + host->bus_ops->power_restore(host);
>> +
>> + mmc_bus_put(host);
>> +}
>> +EXPORT_SYMBOL(mmc_power_restore_host);
>> +
>
> Who calls these exported functions " mmc_power_save_host" and "
> mmc_power_restore_host"?

The present design has the host driver calling them i.e. for us it
is omap_hsmmc. See the "support for deeper power saving states"
patch.

2009-07-30 07:40:22

by Adrian Hunter

[permalink] [raw]
Subject: Re: [PATCH V2 13/32] omap_hsmmc: context save/restore support

Madhusudhan wrote:
>
>> -----Original Message-----
>> From: [email protected] [mailto:linux-omap-
>> [email protected]] On Behalf Of Adrian Hunter
>> Sent: Tuesday, July 28, 2009 5:40 AM
>> To: Andrew Morton
>> Cc: Jarkko Lavinen; Adrian Hunter; linux-omap Mailing List; Pierre Ossman;
>> Denis Karpov; Matt Fleming; lkml
>> Subject: [PATCH V2 13/32] omap_hsmmc: context save/restore support
>>
>> From 43e9fa346d7e386328876a8535dc8619bd1f47ae Mon Sep 17 00:00:00 2001
>> From: Denis Karpov <[email protected]>
>> Date: Wed, 22 Apr 2009 16:04:25 +0200
>> Subject: [PATCH] omap_hsmmc: context save/restore support
>>
>> Keep the context over PM dynamic OFF states.
>>
>> Signed-off-by: Denis Karpov <[email protected]>
>> Signed-off-by: Adrian Hunter <[email protected]>
>> ---
>> drivers/mmc/host/omap_hsmmc.c | 194
>> ++++++++++++++++++++++++++++++++++++++--
>> 1 files changed, 184 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>> index c28d055..ac1a3bf 100644
>> --- a/drivers/mmc/host/omap_hsmmc.c
>> +++ b/drivers/mmc/host/omap_hsmmc.c
>> @@ -37,6 +37,7 @@
>>
>> /* OMAP HSMMC Host Controller Registers */
>> #define OMAP_HSMMC_SYSCONFIG 0x0010
>> +#define OMAP_HSMMC_SYSSTATUS 0x0014
>> #define OMAP_HSMMC_CON 0x002C
>> #define OMAP_HSMMC_BLK 0x0104
>> #define OMAP_HSMMC_ARG 0x0108
>> @@ -94,6 +95,8 @@
>> #define DUAL_VOLT_OCR_BIT 7
>> #define SRC (1 << 25)
>> #define SRD (1 << 26)
>> +#define SOFTRESET (1 << 1)
>> +#define RESETDONE (1 << 0)
>>
>> /*
>> * FIXME: Most likely all the data using these _DEVID defines should come
>> @@ -152,6 +155,8 @@ struct mmc_omap_host {
>> int slot_id;
>> int dbclk_enabled;
>> int response_busy;
>> + int context_loss;
>> +
>> struct omap_mmc_platform_data *pdata;
>> };
>>
>> @@ -166,6 +171,166 @@ static void omap_mmc_stop_clock(struct mmc_omap_host
>> *host)
>> dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
>> }
>>
>> +#ifdef CONFIG_PM
>> +
>> +/*
>> + * Restore the MMC host context, if it was lost as result of a
>> + * power state change.
>> + */
>> +static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
>> +{
>> + struct mmc_ios *ios = &host->mmc->ios;
>> + struct omap_mmc_platform_data *pdata = host->pdata;
>> + int context_loss = 0;
>> + u32 hctl, capa, con;
>> + u16 dsor = 0;
>> + unsigned long timeout;
>> +
>> + if (pdata->get_context_loss_count) {
>> + context_loss = pdata->get_context_loss_count(host->dev);
>> + if (context_loss < 0)
>> + return 1;
>> + }
> This seems to restore the context always. The context should be restored
> only if the card was powered OFF, Right?

Not exactly. The "context" is just the host controller registers, which get
lost if power management decides to power off the host controller, which it
will if it decides it is not doing anything. For us, switching off the
functional clock lets PM power off the host controller. The card can be in
any power state: on, off, card/regulator sleep.

The context is restored only when it has been lost - see the following
lines. But this is done from the "enable" method, so this is only called
if someone is trying to access the card.

+ dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
+ context_loss == host->context_loss ? "not " : "");
+ if (host->context_loss == context_loss)
+ return 1;

And then further down, we skip some things if the card is off:

+ /* Do not initialize card-specific things if the power is off */
+ if (host->power_mode == MMC_POWER_OFF)
+ goto out;


> The context could also be lost if the CORE transitions to OFF. I assume that
> case gets handled here without anything extra required if "power_saving" is
> set to true. Am I right?

Yes, "power_saving" is not related to "Power Management". "power_saving"
is about controlling the power regulators, which the host controller does
itself without any support from PM.

To put it another way, "power_saving" and CONFIG_PM can be set independently
of one another.

> How about the case of eMMC? Since it is not a removable device the card is
> not powered OFF. But the CORE OFF would result in context loss. Do we hit
> the restore_ctx path in that case?

On the contrary, eMMC *is* powered off. After 100ms of inactivity, eMMC is
put to card sleep, and the regulator is put to sleep also - in fact there
are two regulators and one is powered off here. After 8 seconds
the other regulator is powered off and consequently after that the eMMC must
be reinitialised (rescanned) before it can be used.

2009-07-30 19:12:54

by Madhusudhan

[permalink] [raw]
Subject: RE: [PATCH V2 13/32] omap_hsmmc: context save/restore support



> -----Original Message-----
> From: Adrian Hunter [mailto:[email protected]]
> Sent: Thursday, July 30, 2009 2:40 AM
> To: Madhusudhan
> Cc: 'Andrew Morton'; Lavinen Jarkko (Nokia-D/Helsinki); 'linux-omap
> Mailing List'; 'Pierre Ossman'; Karpov Denis.2 (EXT-Teleca/Helsinki);
> 'Matt Fleming'; 'lkml'
> Subject: Re: [PATCH V2 13/32] omap_hsmmc: context save/restore support
>
> Madhusudhan wrote:
> >
> >> -----Original Message-----
> >> From: [email protected] [mailto:linux-omap-
> >> [email protected]] On Behalf Of Adrian Hunter
> >> Sent: Tuesday, July 28, 2009 5:40 AM
> >> To: Andrew Morton
> >> Cc: Jarkko Lavinen; Adrian Hunter; linux-omap Mailing List; Pierre
> Ossman;
> >> Denis Karpov; Matt Fleming; lkml
> >> Subject: [PATCH V2 13/32] omap_hsmmc: context save/restore support
> >>
> >> From 43e9fa346d7e386328876a8535dc8619bd1f47ae Mon Sep 17 00:00:00 2001
> >> From: Denis Karpov <[email protected]>
> >> Date: Wed, 22 Apr 2009 16:04:25 +0200
> >> Subject: [PATCH] omap_hsmmc: context save/restore support
> >>
> >> Keep the context over PM dynamic OFF states.
> >>
> >> Signed-off-by: Denis Karpov <[email protected]>
> >> Signed-off-by: Adrian Hunter <[email protected]>
> >> ---
> >> drivers/mmc/host/omap_hsmmc.c | 194
> >> ++++++++++++++++++++++++++++++++++++++--
> >> 1 files changed, 184 insertions(+), 10 deletions(-)
> >>
> >> diff --git a/drivers/mmc/host/omap_hsmmc.c
> b/drivers/mmc/host/omap_hsmmc.c
> >> index c28d055..ac1a3bf 100644
> >> --- a/drivers/mmc/host/omap_hsmmc.c
> >> +++ b/drivers/mmc/host/omap_hsmmc.c
> >> @@ -37,6 +37,7 @@
> >>
> >> /* OMAP HSMMC Host Controller Registers */
> >> #define OMAP_HSMMC_SYSCONFIG 0x0010
> >> +#define OMAP_HSMMC_SYSSTATUS 0x0014
> >> #define OMAP_HSMMC_CON 0x002C
> >> #define OMAP_HSMMC_BLK 0x0104
> >> #define OMAP_HSMMC_ARG 0x0108
> >> @@ -94,6 +95,8 @@
> >> #define DUAL_VOLT_OCR_BIT 7
> >> #define SRC (1 << 25)
> >> #define SRD (1 << 26)
> >> +#define SOFTRESET (1 << 1)
> >> +#define RESETDONE (1 << 0)
> >>
> >> /*
> >> * FIXME: Most likely all the data using these _DEVID defines should
> come
> >> @@ -152,6 +155,8 @@ struct mmc_omap_host {
> >> int slot_id;
> >> int dbclk_enabled;
> >> int response_busy;
> >> + int context_loss;
> >> +
> >> struct omap_mmc_platform_data *pdata;
> >> };
> >>
> >> @@ -166,6 +171,166 @@ static void omap_mmc_stop_clock(struct
> mmc_omap_host
> >> *host)
> >> dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
> >> }
> >>
> >> +#ifdef CONFIG_PM
> >> +
> >> +/*
> >> + * Restore the MMC host context, if it was lost as result of a
> >> + * power state change.
> >> + */
> >> +static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
> >> +{
> >> + struct mmc_ios *ios = &host->mmc->ios;
> >> + struct omap_mmc_platform_data *pdata = host->pdata;
> >> + int context_loss = 0;
> >> + u32 hctl, capa, con;
> >> + u16 dsor = 0;
> >> + unsigned long timeout;
> >> +
> >> + if (pdata->get_context_loss_count) {
> >> + context_loss = pdata->get_context_loss_count(host->dev);
> >> + if (context_loss < 0)
> >> + return 1;
> >> + }
> > This seems to restore the context always. The context should be restored
> > only if the card was powered OFF, Right?
>
> Not exactly. The "context" is just the host controller registers, which
> get
> lost if power management decides to power off the host controller, which
> it
> will if it decides it is not doing anything. For us, switching off the
> functional clock lets PM power off the host controller. The card can be
> in
> any power state: on, off, card/regulator sleep.
>
> The context is restored only when it has been lost - see the following
> lines. But this is done from the "enable" method, so this is only called
> if someone is trying to access the card.
>
> + dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
> + context_loss == host->context_loss ? "not " : "");
> + if (host->context_loss == context_loss)
> + return 1;
>
> And then further down, we skip some things if the card is off:
>
> + /* Do not initialize card-specific things if the power is off */
> + if (host->power_mode == MMC_POWER_OFF)
> + goto out;
>
>
> > The context could also be lost if the CORE transitions to OFF. I assume
> that
> > case gets handled here without anything extra required if "power_saving"
> is
> > set to true. Am I right?
>
> Yes, "power_saving" is not related to "Power Management". "power_saving"
> is about controlling the power regulators, which the host controller does
> itself without any support from PM.
>
> To put it another way, "power_saving" and CONFIG_PM can be set
> independently
> of one another.

But the "omap_hsmmc_context_save" and " omap_hsmmc_context_restore" are
under CONFIG_PM. They are empty functions if CONFIG_PM is not set. Does this
mean that if CONFIG_PM is not set the host regulators are not turned OFF
even if there is no activity on MMC?

>
> > How about the case of eMMC? Since it is not a removable device the card
> is
> > not powered OFF. But the CORE OFF would result in context loss. Do we
> hit
> > the restore_ctx path in that case?
>
> On the contrary, eMMC *is* powered off. After 100ms of inactivity, eMMC
> is
> put to card sleep, and the regulator is put to sleep also - in fact there
> are two regulators and one is powered off here. After 8 seconds
> the other regulator is powered off and consequently after that the eMMC
> must
> be reinitialised (rescanned) before it can be used.

2009-07-30 19:33:13

by Adrian Hunter

[permalink] [raw]
Subject: Re: [PATCH V2 13/32] omap_hsmmc: context save/restore support

ext Madhusudhan wrote:
>
>> -----Original Message-----
>> From: Adrian Hunter [mailto:[email protected]]
>> Sent: Thursday, July 30, 2009 2:40 AM
>> To: Madhusudhan
>> Cc: 'Andrew Morton'; Lavinen Jarkko (Nokia-D/Helsinki); 'linux-omap
>> Mailing List'; 'Pierre Ossman'; Karpov Denis.2 (EXT-Teleca/Helsinki);
>> 'Matt Fleming'; 'lkml'
>> Subject: Re: [PATCH V2 13/32] omap_hsmmc: context save/restore support
>>
>> Madhusudhan wrote:
>>>> -----Original Message-----
>>>> From: [email protected] [mailto:linux-omap-
>>>> [email protected]] On Behalf Of Adrian Hunter
>>>> Sent: Tuesday, July 28, 2009 5:40 AM
>>>> To: Andrew Morton
>>>> Cc: Jarkko Lavinen; Adrian Hunter; linux-omap Mailing List; Pierre
>> Ossman;
>>>> Denis Karpov; Matt Fleming; lkml
>>>> Subject: [PATCH V2 13/32] omap_hsmmc: context save/restore support
>>>>
>>>> From 43e9fa346d7e386328876a8535dc8619bd1f47ae Mon Sep 17 00:00:00 2001
>>>> From: Denis Karpov <[email protected]>
>>>> Date: Wed, 22 Apr 2009 16:04:25 +0200
>>>> Subject: [PATCH] omap_hsmmc: context save/restore support
>>>>
>>>> Keep the context over PM dynamic OFF states.
>>>>
>>>> Signed-off-by: Denis Karpov <[email protected]>
>>>> Signed-off-by: Adrian Hunter <[email protected]>
>>>> ---
>>>> drivers/mmc/host/omap_hsmmc.c | 194
>>>> ++++++++++++++++++++++++++++++++++++++--
>>>> 1 files changed, 184 insertions(+), 10 deletions(-)
>>>>
>>>> diff --git a/drivers/mmc/host/omap_hsmmc.c
>> b/drivers/mmc/host/omap_hsmmc.c
>>>> index c28d055..ac1a3bf 100644
>>>> --- a/drivers/mmc/host/omap_hsmmc.c
>>>> +++ b/drivers/mmc/host/omap_hsmmc.c
>>>> @@ -37,6 +37,7 @@
>>>>
>>>> /* OMAP HSMMC Host Controller Registers */
>>>> #define OMAP_HSMMC_SYSCONFIG 0x0010
>>>> +#define OMAP_HSMMC_SYSSTATUS 0x0014
>>>> #define OMAP_HSMMC_CON 0x002C
>>>> #define OMAP_HSMMC_BLK 0x0104
>>>> #define OMAP_HSMMC_ARG 0x0108
>>>> @@ -94,6 +95,8 @@
>>>> #define DUAL_VOLT_OCR_BIT 7
>>>> #define SRC (1 << 25)
>>>> #define SRD (1 << 26)
>>>> +#define SOFTRESET (1 << 1)
>>>> +#define RESETDONE (1 << 0)
>>>>
>>>> /*
>>>> * FIXME: Most likely all the data using these _DEVID defines should
>> come
>>>> @@ -152,6 +155,8 @@ struct mmc_omap_host {
>>>> int slot_id;
>>>> int dbclk_enabled;
>>>> int response_busy;
>>>> + int context_loss;
>>>> +
>>>> struct omap_mmc_platform_data *pdata;
>>>> };
>>>>
>>>> @@ -166,6 +171,166 @@ static void omap_mmc_stop_clock(struct
>> mmc_omap_host
>>>> *host)
>>>> dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
>>>> }
>>>>
>>>> +#ifdef CONFIG_PM
>>>> +
>>>> +/*
>>>> + * Restore the MMC host context, if it was lost as result of a
>>>> + * power state change.
>>>> + */
>>>> +static int omap_mmc_restore_ctx(struct mmc_omap_host *host)
>>>> +{
>>>> + struct mmc_ios *ios = &host->mmc->ios;
>>>> + struct omap_mmc_platform_data *pdata = host->pdata;
>>>> + int context_loss = 0;
>>>> + u32 hctl, capa, con;
>>>> + u16 dsor = 0;
>>>> + unsigned long timeout;
>>>> +
>>>> + if (pdata->get_context_loss_count) {
>>>> + context_loss = pdata->get_context_loss_count(host->dev);
>>>> + if (context_loss < 0)
>>>> + return 1;
>>>> + }
>>> This seems to restore the context always. The context should be restored
>>> only if the card was powered OFF, Right?
>> Not exactly. The "context" is just the host controller registers, which
>> get
>> lost if power management decides to power off the host controller, which
>> it
>> will if it decides it is not doing anything. For us, switching off the
>> functional clock lets PM power off the host controller. The card can be
>> in
>> any power state: on, off, card/regulator sleep.
>>
>> The context is restored only when it has been lost - see the following
>> lines. But this is done from the "enable" method, so this is only called
>> if someone is trying to access the card.
>>
>> + dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
>> + context_loss == host->context_loss ? "not " : "");
>> + if (host->context_loss == context_loss)
>> + return 1;
>>
>> And then further down, we skip some things if the card is off:
>>
>> + /* Do not initialize card-specific things if the power is off */
>> + if (host->power_mode == MMC_POWER_OFF)
>> + goto out;
>>
>>
>>> The context could also be lost if the CORE transitions to OFF. I assume
>> that
>>> case gets handled here without anything extra required if "power_saving"
>> is
>>> set to true. Am I right?
>> Yes, "power_saving" is not related to "Power Management". "power_saving"
>> is about controlling the power regulators, which the host controller does
>> itself without any support from PM.
>>
>> To put it another way, "power_saving" and CONFIG_PM can be set
>> independently
>> of one another.
>
> But the "omap_hsmmc_context_save" and " omap_hsmmc_context_restore" are
> under CONFIG_PM. They are empty functions if CONFIG_PM is not set. Does this
> mean that if CONFIG_PM is not set the host regulators are not turned OFF
> even if there is no activity on MMC?

No. Context save/restore *is* a part of Power Management. It is needed
to support Dynamic Power Switching (DPS).

The voltage regulators are controlled via the enable/disable methods
even when CONFIG_PM is not defined.

Note that the voltage regulators to which I refer, power the cards not
the host controllers.

So the power to the host controller is under the control of Power Management
and is supported by context save/restore.

The power to the cards is controlled by the driver (omap_hsmmc) using the
enable/disable methods.

2009-07-31 00:53:07

by Madhusudhan

[permalink] [raw]
Subject: RE: [PATCH V2 0/32] mmc and omap_hsmmc patches



> -----Original Message-----
> From: [email protected] [mailto:linux-omap-
> [email protected]] On Behalf Of Matt Fleming
> Sent: Wednesday, July 29, 2009 6:13 AM
> To: Adrian Hunter
> Cc: Andrew Morton; Jarkko Lavinen; linux-omap Mailing List; Pierre Ossman;
> Denis Karpov; lkml
> Subject: Re: [PATCH V2 0/32] mmc and omap_hsmmc patches
>
> On Tue, Jul 28, 2009 at 01:38:34PM +0300, Adrian Hunter wrote:
> > Hi
> >
> > Here is version 2 of our 32 patches for mmc and omap_hsmmc.
> >
> > They include Matt Fleming's change for card caps, and 2 other fixes:
> > - use a spin lock rather than enable / diable irq
> > - make disable delay specified in milliseconds not jiffies because
> the
> > value is an int, and jiffies are unsigned long (also millisecs are
> > better anyway)
> >
>
> Thanks for doing this Adrian. I appreciate it. My Reviewed-by tag still
> applies.

I reviewed the omap_hsmmc series. They look good.

Regards,
Madhu
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html