Hi Pierre
Here are 32 patches for mmc and omap_hsmmc.
Sorry there are so many.
They mostly relate to trying to save power.
They split into 2 groups: the first 8 affect mmc core only
and the remaining ones affect omap_hsmmc only.
They apply cleanly on top of Linux 2.6.31-rc2
Adrian Hunter (20):
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: add host capabilities for SD only and MMC only
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
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 | 288 +++++++-
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/omap_hsmmc.c | 1025 +++++++++++++++++++++-----
include/linux/mmc/card.h | 2 +
include/linux/mmc/core.h | 1 +
include/linux/mmc/host.h | 65 ++
include/linux/mmc/mmc.h | 3 +
17 files changed, 1512 insertions(+), 216 deletions(-)
Regards
Adrian
>From 2d65438fce412a63f09f90fb9af1049c775cbd70 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 41fd127..b262dc6 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -458,16 +458,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);
@@ -478,29 +480,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 583c068..9cc5be9 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
>From 52fa498f893db9a7dc203cee36ad8889241ab0b0 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 9cc5be9..6847fce 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
>From a0164897276e4d1f972fd90b1e9499e1ab8d221e 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 | 174 ++++++++++++++++++++++++++++++++++++++++++++--
drivers/mmc/core/host.c | 1 +
drivers/mmc/core/host.h | 2 +
include/linux/mmc/host.h | 47 ++++++++++++
4 files changed, 218 insertions(+), 6 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index d84c880..41fd127 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -344,6 +344,98 @@ 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)
+ mmc_schedule_delayed_work(&host->disable, err);
+ }
+ 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 +471,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 +555,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 +1105,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 +1135,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..583c068 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 jiffies.
+ *
+ * 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 disble 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
>From dd860f790842933bb4b4ece789b895115ecd1be8 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 b262dc6..b76a0f7 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1142,6 +1142,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 6847fce..e24d992 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
>From 72aa4541a135f40ebe41e67680be912ad56a076f 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 1461025..c5a7857 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1134,6 +1134,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
>From 9b89eb8712def837ddfd40819ae578656ea893cb 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 cbd8376..0fded10 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, msecs_to_jiffies(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;
@@ -1190,6 +1233,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) {
@@ -1218,7 +1263,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);
@@ -1243,6 +1288,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);
@@ -1251,7 +1297,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);
@@ -1282,6 +1328,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;
@@ -1300,10 +1347,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;
@@ -1320,13 +1368,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;
}
@@ -1348,6 +1395,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
>From b2f023bed880e3560aaa7de5caa6f899d79840f5 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 b76a0f7..1461025 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1176,6 +1176,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 e24d992..0a60b02 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
>From 27ed1443884c0e46855485cfc2190e1d80a0f568 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Tue, 7 Jul 2009 12:20:48 +0300
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
cannot be used, and amend the card scanning logic
to skip scanning for those types.
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/core/core.c | 16 +++++++++++++++-
include/linux/mmc/host.h | 7 +++++++
2 files changed, 22 insertions(+), 1 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c5a7857..db43a1d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1066,7 +1066,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_NOT_SDIO) || !(host->caps & MMC_CAP_NOT_SD))
+ mmc_send_if_cond(host, host->ocr_avail);
+
+ if (host->caps & MMC_CAP_NOT_SDIO)
+ goto not_sdio;
/*
* First we search for SDIO...
@@ -1078,6 +1082,10 @@ void mmc_rescan(struct work_struct *work)
goto out;
}
+not_sdio:
+ if (host->caps & MMC_CAP_NOT_SD)
+ goto not_sd;
+
/*
* ...then normal SD...
*/
@@ -1088,6 +1096,10 @@ void mmc_rescan(struct work_struct *work)
goto out;
}
+not_sd:
+ if (host->caps & MMC_CAP_NOT_MMC)
+ goto not_mmc;
+
/*
* ...and finally MMC.
*/
@@ -1098,6 +1110,8 @@ void mmc_rescan(struct work_struct *work)
goto out;
}
+not_mmc:
+
mmc_release_host(host);
mmc_power_off(host);
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 0a60b02..e996967 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -150,6 +150,13 @@ 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_NOT_SDIO (1 << 10) /* Card cannot be SDIO */
+#define MMC_CAP_NOT_SD (1 << 11) /* Card cannot be SD */
+#define MMC_CAP_NOT_MMC (1 << 12) /* Card cannot be MMC */
+
+#define MMC_CAP_SDIO_ONLY (MMC_CAP_NOT_SD | MMC_CAP_NOT_MMC)
+#define MMC_CAP_SD_ONLY (MMC_CAP_NOT_SDIO | MMC_CAP_NOT_MMC)
+#define MMC_CAP_MMC_ONLY (MMC_CAP_NOT_SDIO | MMC_CAP_NOT_SD)
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
--
1.5.6.3
>From 4951591bffd4c215e4d74e13fc46f0a625264288 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 1cf9cfb..cbd8376 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;
@@ -1150,6 +1205,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
>From 57e1d1301b974e57f65a8f7b1287629b3c97a3b8 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
>From cda3074c6233408ec29365c5e63b80567bff8c5c 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
>From ad6fd97ce361b6527fc3a951ca6efa5f9a0b59ee 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 0fded10..dad0548 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
>From 3eb49df725b26329e85db6e4fc6f72fc029501a3 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 dad0548..762aa01 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, msecs_to_jiffies(100));
if (mmc_host_enable(host->mmc) != 0) {
@@ -1378,21 +1554,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
>From 78e90903abbc084ebdd7716498eac9429c61e878 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 762aa01..2e2a81c 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
>From 85020821e459904d381684dde45a2a500e02cadb 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 41500ac..15f1a9a 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
>From ece776be05f73787dad93802000f2d04218197d0 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 41c330d..fe46234 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
>From 99e5f5c1e7e4e8195cae27d2fc5be75424d5bc75 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
>From a86c083ca740b4311daf3dc8e219506b63756896 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 7b56988..c69ab89 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, msecs_to_jiffies(100));
+ mmc_set_disable_delay(mmc, msecs_to_jiffies(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
>From 697301e98721b32335caa2dc83b654c81866a97e 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 2e2a81c..41500ac 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;
@@ -1517,30 +1523,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
>From 8bf0af61703bd4a1d397f565104bee69c77ff586 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 eb301c1..41c330d 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
>From 66c91dd04118c2e2111dade9d5a327c442912046 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 c69ab89..eb301c1 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
>From ecb91d032ea4e68f61317a0e5b880277cd02117b 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 fe46234..743c07a 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;
omap_hsmmc_init(host);
@@ -1693,13 +1690,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)
@@ -1805,7 +1801,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
>From 2c975c1612a3ef5f641881b69491265038f0c0b2 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 | 3 +--
4 files changed, 7 insertions(+), 9 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 38e1410..fa5f401 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1655,8 +1655,7 @@ 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;
omap_hsmmc_init(host);
--
1.5.6.3
>From 5fffc71c4b754453a286bec4c461ca6897941ad1 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 743c07a..315d1df 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
>From 92159741642af71828cb664f716b098152b7a1c1 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 2570bfe..427bd49 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -727,22 +727,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);
@@ -1888,7 +1890,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) {
@@ -1919,9 +1922,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
>From 9b524a70c3d8f3d138646630bb2144c9895af47a 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 5055d52..2570bfe 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -163,6 +163,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;
};
@@ -347,6 +349,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);
@@ -771,6 +776,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
*/
@@ -788,8 +817,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);
@@ -1025,8 +1056,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()) {
disable_irq(host->irq);
+ /*
+ * 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;
+ enable_irq(host->irq);
+ 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);
@@ -1715,6 +1770,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) {
@@ -1880,6 +1937,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
>From 4ad1f699fad1f7132e5a6a7ad1a2a433accb88d4 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 315d1df..28563d6 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
>From 913b1f23036a578427626e5b0a154cd7547c0609 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 15f1a9a..7b56988 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;
+
omap_hsmmc_init(host);
/* Select DMA lines */
--
1.5.6.3
>From 242fae6293adec671b14354f215217354f5076a0 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 disabling interrupts while setting up
a new request.
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 16 ++++++++++++++++
1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 28563d6..38e1410 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -452,6 +452,13 @@ 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 interrupt is already
+ * enabled, otherwise it is not (i.e. new request).
+ */
+ if (!in_interrupt())
+ enable_irq(host->irq);
+
OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
}
@@ -1011,6 +1018,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())
+ disable_irq(host->irq);
WARN_ON(host->mrq != NULL);
host->mrq = req;
err = mmc_omap_prepare_data(host, req);
@@ -1019,6 +1033,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())
+ enable_irq(host->irq);
mmc_request_done(mmc, req);
return;
}
--
1.5.6.3
>From 10908c5264a19de415af6406ee19ef2bd68928c6 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 fa5f401..5055d52 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;
@@ -170,7 +170,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);
@@ -184,7 +184,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;
@@ -312,7 +312,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;
@@ -327,12 +327,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)
{
}
@@ -342,7 +342,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;
@@ -366,7 +366,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;
@@ -376,35 +376,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;
@@ -464,7 +464,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;
@@ -476,7 +476,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;
@@ -497,7 +497,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);
@@ -509,14 +509,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;
@@ -541,13 +541,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);
@@ -559,10 +559,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", "---",
@@ -575,9 +575,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;
}
@@ -592,8 +592,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 *
@@ -615,9 +615,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;
@@ -635,14 +635,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;
@@ -651,9 +651,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) ||
@@ -663,11 +664,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;
}
}
@@ -686,14 +687,14 @@ 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);
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;
@@ -713,7 +714,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;
@@ -744,7 +745,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,
@@ -773,10 +774,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;
@@ -794,8 +795,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);
}
}
@@ -803,9 +805,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;
@@ -814,7 +816,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;
@@ -826,7 +828,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)
{
@@ -850,7 +852,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);
@@ -859,9 +861,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");
@@ -872,7 +874,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;
}
@@ -889,8 +891,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;
@@ -927,8 +929,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",
@@ -937,16 +939,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;
@@ -986,7 +988,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;
@@ -1001,7 +1003,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;
@@ -1013,9 +1015,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;
/*
@@ -1027,7 +1029,7 @@ static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
disable_irq(host->irq);
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)
@@ -1039,14 +1041,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;
@@ -1105,8 +1106,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");
}
}
@@ -1122,7 +1123,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);
@@ -1156,7 +1157,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;
@@ -1165,14 +1166,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;
@@ -1218,9 +1219,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;
@@ -1233,7 +1234,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;
@@ -1241,7 +1242,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) {
@@ -1250,8 +1251,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);
@@ -1274,7 +1276,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;
@@ -1302,7 +1304,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;
@@ -1310,8 +1312,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");
@@ -1320,13 +1321,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);
@@ -1344,12 +1345,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;
@@ -1362,18 +1363,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;
@@ -1383,68 +1384,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 */
@@ -1452,15 +1453,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"
@@ -1502,19 +1502,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,
@@ -1523,17 +1523,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;
@@ -1557,7 +1557,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;
@@ -1578,12 +1578,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;
@@ -1604,7 +1604,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, msecs_to_jiffies(OMAP_MMC_DISABLED_TIMEOUT));
@@ -1657,7 +1657,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
mmc->caps |= mmc_slot(host).caps;
- omap_hsmmc_init(host);
+ omap_hsmmc_conf_bus_power(host);
/* Select DMA lines */
switch (host->id) {
@@ -1679,7 +1679,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");
@@ -1689,7 +1689,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;
}
}
@@ -1698,7 +1699,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);
@@ -1728,7 +1729,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;
@@ -1760,9 +1761,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) {
@@ -1797,10 +1798,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;
@@ -1848,10 +1849,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;
@@ -1870,7 +1871,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);
@@ -1883,6 +1884,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);
}
@@ -1895,35 +1897,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
>From 3865867159f25cf706480236f6d4f0e4adde5dac 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..5cc815b 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_ONLY,
},
{
.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_ONLY | MMC_CAP_NONREMOVABLE,
},
{} /* Terminator */
};
--
1.5.6.3
>From 54996066acb35e82f89715c6c59b28bc81e12239 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 427bd49..ae8d33b 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -982,7 +982,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;
@@ -993,8 +994,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;
@@ -1028,12 +1029,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
Hi Adrian,
The patch numbers 7 and 28 in the series seems to be missing?
Regards,
Madhu
> -----Original Message-----
> From: [email protected] [mailto:linux-omap-
> [email protected]] On Behalf Of Adrian Hunter
> Sent: Friday, July 10, 2009 7:40 AM
> To: Pierre Ossman
> Cc: Jarkko Lavinen; Denis Karpov; Adrian Hunter; linux-omap Mailing List;
> lkml
> Subject: [PATCH 1/32] mmc: add 'enable' and 'disable' methods to mmc host
>
> From a0164897276e4d1f972fd90b1e9499e1ab8d221e 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 | 174
> ++++++++++++++++++++++++++++++++++++++++++++--
> drivers/mmc/core/host.c | 1 +
> drivers/mmc/core/host.h | 2 +
> include/linux/mmc/host.h | 47 ++++++++++++
> 4 files changed, 218 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index d84c880..41fd127 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -344,6 +344,98 @@ 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)
> + mmc_schedule_delayed_work(&host->disable, err);
> + }
> + 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 +471,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 +555,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 +1105,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 +1135,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..583c068 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 jiffies.
> + *
> + * 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 disble 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
>
> --
> 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
Madhusudhan wrote:
> The patch numbers 7 and 28 in the series seems to be missing?
I can see them in mailing list archives - lkml.org or gmane.org
Acked-by: Linus Walleij <[email protected]>
And please drop my other patch in favor of this one then.
Linus Walleij
2009/7/10 Adrian Hunter <[email protected]>:
> From a0164897276e4d1f972fd90b1e9499e1ab8d221e 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 ?| ?174 ++++++++++++++++++++++++++++++++++++++++++++--
> ?drivers/mmc/core/host.c ?| ? ?1 +
> ?drivers/mmc/core/host.h ?| ? ?2 +
> ?include/linux/mmc/host.h | ? 47 ++++++++++++
> ?4 files changed, 218 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index d84c880..41fd127 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -344,6 +344,98 @@ 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)
> + ? ? ? ? ? ? ? ? ? ? ? mmc_schedule_delayed_work(&host->disable, err);
> + ? ? ? }
> + ? ? ? 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 +471,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 +555,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 +1105,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 +1135,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..583c068 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 jiffies.
> + ? ? ? ?*
> + ? ? ? ?* 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 disble 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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at ?http://www.tux.org/lkml/
>
On Fri, 10 Jul 2009 15:42:27 +0300
Adrian Hunter <[email protected]> wrote:
> +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;
> +}
We take no action if the wait for SDBP timed out?
On Fri, 10 Jul 2009 15:43:09 +0300
Adrian Hunter <[email protected]> wrote:
> >From 242fae6293adec671b14354f215217354f5076a0 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 disabling interrupts while setting up
> a new request.
>
> Signed-off-by: Adrian Hunter <[email protected]>
> ---
> drivers/mmc/host/omap_hsmmc.c | 16 ++++++++++++++++
> 1 files changed, 16 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index 28563d6..38e1410 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -452,6 +452,13 @@ 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 interrupt is already
> + * enabled, otherwise it is not (i.e. new request).
> + */
> + if (!in_interrupt())
> + enable_irq(host->irq);
> +
> OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
> OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
> }
> @@ -1011,6 +1018,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())
> + disable_irq(host->irq);
> WARN_ON(host->mrq != NULL);
> host->mrq = req;
> err = mmc_omap_prepare_data(host, req);
> @@ -1019,6 +1033,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())
> + enable_irq(host->irq);
> mmc_request_done(mmc, req);
> return;
> }
That seems pretty crude. Disabling an interrupt line can be expensive,
and will shut off any other innocent devices which share the line.
The usual and superior way of fixing races such as this is
spin_lock_irq[save]().
On Fri, Jul 10, 2009 at 03:40:54PM +0300, Adrian Hunter wrote:
>
> 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
> cannot be used, and amend the card scanning logic
> to skip scanning for those types.
>
I'm only nitpicking here, but I think that logic is a little inverted.
By saying which cards cannot be used (as opposed to which cards can be
used), we get conditionals like this,
>
> - mmc_send_if_cond(host, host->ocr_avail);
> + if (!(host->caps & MMC_CAP_NOT_SDIO) || !(host->caps & MMC_CAP_NOT_SD))
> + mmc_send_if_cond(host, host->ocr_avail);
> +
Whilst reviewing this patch it took my brain a few too many seconds to
parse that as "if the host is capable of SDIO or SD".
> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
> index 0a60b02..e996967 100644
> --- a/include/linux/mmc/host.h
> +++ b/include/linux/mmc/host.h
> @@ -150,6 +150,13 @@ 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_NOT_SDIO (1 << 10) /* Card cannot be SDIO */
> +#define MMC_CAP_NOT_SD (1 << 11) /* Card cannot be SD */
> +#define MMC_CAP_NOT_MMC (1 << 12) /* Card cannot be MMC */
> +
> +#define MMC_CAP_SDIO_ONLY (MMC_CAP_NOT_SD | MMC_CAP_NOT_MMC)
> +#define MMC_CAP_SD_ONLY (MMC_CAP_NOT_SDIO | MMC_CAP_NOT_MMC)
> +#define MMC_CAP_MMC_ONLY (MMC_CAP_NOT_SDIO | MMC_CAP_NOT_SD)
>
And by saying what capabilities a host supports, when we add new
capabilities we don't have to modify existing code to say that it
doesn't support the new capability.
Matt Fleming wrote:
> On Fri, Jul 10, 2009 at 03:40:54PM +0300, Adrian Hunter wrote:
>> 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
>> cannot be used, and amend the card scanning logic
>> to skip scanning for those types.
>>
>
> I'm only nitpicking here, but I think that logic is a little inverted.
> By saying which cards cannot be used (as opposed to which cards can be
> used), we get conditionals like this,
>
>>
>> - mmc_send_if_cond(host, host->ocr_avail);
>> + if (!(host->caps & MMC_CAP_NOT_SDIO) || !(host->caps & MMC_CAP_NOT_SD))
>> + mmc_send_if_cond(host, host->ocr_avail);
>> +
>
> Whilst reviewing this patch it took my brain a few too many seconds to
> parse that as "if the host is capable of SDIO or SD".
>
>
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index 0a60b02..e996967 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -150,6 +150,13 @@ 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_NOT_SDIO (1 << 10) /* Card cannot be SDIO */
>> +#define MMC_CAP_NOT_SD (1 << 11) /* Card cannot be SD */
>> +#define MMC_CAP_NOT_MMC (1 << 12) /* Card cannot be MMC */
>> +
>> +#define MMC_CAP_SDIO_ONLY (MMC_CAP_NOT_SD | MMC_CAP_NOT_MMC)
>> +#define MMC_CAP_SD_ONLY (MMC_CAP_NOT_SDIO | MMC_CAP_NOT_MMC)
>> +#define MMC_CAP_MMC_ONLY (MMC_CAP_NOT_SDIO | MMC_CAP_NOT_SD)
>>
>
> And by saying what capabilities a host supports, when we add new
> capabilities we don't have to modify existing code to say that it
> doesn't support the new capability.
If the capabilities are the other way around, then all existing drivers
must be changed. On the other hand, the if statement can easily be
improved:
#define mmc_cap_mmc(host) (!((host)->caps & MMC_CAP_NOT_MMC))
#define mmc_cap_sd(host) (!((host)->caps & MMC_CAP_NOT_SD))
#define mmc_cap_sdio(host) (!((host)->caps & MMC_CAP_NOT_SDIO))
- mmc_send_if_cond(host, host->ocr_avail);
+ if (mmc_cap_sdio(host) || mmc_cap_sd(host))
+ mmc_send_if_cond(host, host->ocr_avail);
+
On Sat, Jul 25, 2009 at 07:17:14PM +0300, Adrian Hunter wrote:
> Matt Fleming wrote:
>>
>> And by saying what capabilities a host supports, when we add new
>> capabilities we don't have to modify existing code to say that it
>> doesn't support the new capability.
>
> If the capabilities are the other way around, then all existing drivers
> must be changed. On the other hand, the if statement can easily be
> improved:
>
> #define mmc_cap_mmc(host) (!((host)->caps & MMC_CAP_NOT_MMC))
> #define mmc_cap_sd(host) (!((host)->caps & MMC_CAP_NOT_SD))
> #define mmc_cap_sdio(host) (!((host)->caps & MMC_CAP_NOT_SDIO))
>
> - mmc_send_if_cond(host, host->ocr_avail);
> + if (mmc_cap_sdio(host) || mmc_cap_sd(host))
> + mmc_send_if_cond(host, host->ocr_avail);
> +
OK, that's a fair point, all drivers would need to be changed. However,
I'm not adverse to making a one-time change to all drivers in the name
of sanity. Enumerating what cards a controller doesn't support just
doesn't sound correct to me.
It wouldn't be a complicated change either, I'd even be happy to write
the patch ;-)
Matt Fleming wrote:
> On Sat, Jul 25, 2009 at 07:17:14PM +0300, Adrian Hunter wrote:
>> Matt Fleming wrote:
>>> And by saying what capabilities a host supports, when we add new
>>> capabilities we don't have to modify existing code to say that it
>>> doesn't support the new capability.
>> If the capabilities are the other way around, then all existing drivers
>> must be changed. On the other hand, the if statement can easily be
>> improved:
>>
>> #define mmc_cap_mmc(host) (!((host)->caps & MMC_CAP_NOT_MMC))
>> #define mmc_cap_sd(host) (!((host)->caps & MMC_CAP_NOT_SD))
>> #define mmc_cap_sdio(host) (!((host)->caps & MMC_CAP_NOT_SDIO))
>>
>> - mmc_send_if_cond(host, host->ocr_avail);
>> + if (mmc_cap_sdio(host) || mmc_cap_sd(host))
>> + mmc_send_if_cond(host, host->ocr_avail);
>> +
>
> OK, that's a fair point, all drivers would need to be changed. However,
> I'm not adverse to making a one-time change to all drivers in the name
> of sanity. Enumerating what cards a controller doesn't support just
> doesn't sound correct to me.
>
> It wouldn't be a complicated change either, I'd even be happy to write
> the patch ;-)
I have no objections if you write such a patch :-)
On Sat, Jul 25, 2009 at 10:45:07PM +0300, Adrian Hunter wrote:
>
> I have no objections if you write such a patch :-)
See also my change to [PATCH 32/32] of this series.
I don't have hardware to test all of the affected drivers but I was
thinking of something along the lines of,
---
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/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 +++
20 files changed, 48 insertions(+), 17 deletions(-)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index c5a7857..7d905f9 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1066,7 +1066,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...
@@ -1078,6 +1082,10 @@ void mmc_rescan(struct work_struct *work)
goto out;
}
+not_sdio:
+ if (!(host->caps & MMC_CAP_SD))
+ goto not_sd;
+
/*
* ...then normal SD...
*/
@@ -1088,6 +1096,10 @@ void mmc_rescan(struct work_struct *work)
goto out;
}
+not_sd:
+ if (!(host->caps & MMC_CAP_MMC))
+ goto not_mmc;
+
/*
* ...and finally MMC.
*/
@@ -1098,6 +1110,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/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 c246191..a14d1af 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -555,7 +555,8 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
}
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 4ae5766..3902c5c 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.6.4.rc0
On Fri, Jul 10, 2009 at 03:43:53PM +0300, Adrian Hunter wrote:
> >From 3865867159f25cf706480236f6d4f0e4adde5dac 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.
>
After the changes to [PATCH 7/32], this becomes,
---
ARM: OMAP: RX51: set MMC capabilities and power-saving flag
Specify MMC capabilities and set the power-saving flag for RX51.
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]>
---
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.6.4.rc0
Matt Fleming wrote:
> On Sat, Jul 25, 2009 at 10:45:07PM +0300, Adrian Hunter wrote:
>> I have no objections if you write such a patch :-)
>
> See also my change to [PATCH 32/32] of this series.
>
> I don't have hardware to test all of the affected drivers but I was
> thinking of something along the lines of,
Seems fine to me.
>
> ---
>
> 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/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 +++
> 20 files changed, 48 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
> index c5a7857..7d905f9 100644
> --- a/drivers/mmc/core/core.c
> +++ b/drivers/mmc/core/core.c
> @@ -1066,7 +1066,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...
> @@ -1078,6 +1082,10 @@ void mmc_rescan(struct work_struct *work)
> goto out;
> }
>
> +not_sdio:
> + if (!(host->caps & MMC_CAP_SD))
> + goto not_sd;
> +
> /*
> * ...then normal SD...
> */
> @@ -1088,6 +1096,10 @@ void mmc_rescan(struct work_struct *work)
> goto out;
> }
>
> +not_sd:
> + if (!(host->caps & MMC_CAP_MMC))
> + goto not_mmc;
> +
> /*
> * ...and finally MMC.
> */
> @@ -1098,6 +1110,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/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 c246191..a14d1af 100644
> --- a/drivers/mmc/host/tmio_mmc.c
> +++ b/drivers/mmc/host/tmio_mmc.c
> @@ -555,7 +555,8 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev)
> }
>
> 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 4ae5766..3902c5c 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.6.4.rc0
>
>
Andrew Morton wrote:
> On Fri, 10 Jul 2009 15:42:27 +0300
> Adrian Hunter <[email protected]> wrote:
>
>> +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;
>> +}
>
> We take no action if the wait for SDBP timed out?
Yeah that is on my TODO list, but SDBP (and other bits we wait for)
will not timeout unless the hardware is broken or there is a bug
in the driver. In either of those cases we are ruined anyway.
On Fri, Jul 10, 2009 at 03:40:04PM +0300, Adrian Hunter wrote:
> Hi Pierre
>
> Here are 32 patches for mmc and omap_hsmmc.
>
This whole series looks good to me.
Reviewed-by: Matt Fleming <[email protected]>
Matt Fleming wrote:
> On Fri, Jul 10, 2009 at 03:43:53PM +0300, Adrian Hunter wrote:
>> >From 3865867159f25cf706480236f6d4f0e4adde5dac 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.
>>
>
> After the changes to [PATCH 7/32], this becomes,
I think this will leave every other board that uses omap_hsmmc
without any card caps.
What about adding something like this:
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 8900bae..f2548f1 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1720,6 +1720,11 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
mmc->caps |= MMC_CAP_4_BIT_DATA;
mmc->caps |= mmc_slot(host).caps;
+ /* If no card caps specified then assume them all */
+ if (!((mmc->caps & MMC_CAP_SD) ||
+ (mmc->caps & MMC_CAP_MMC) ||
+ (mmc->caps & MMC_CAP_SDIO)))
+ mmc->caps |= MMC_CAP_SD & MMC_CAP_MMC & MMC_CAP_SDIO;
omap_hsmmc_conf_bus_power(host);
> ---
>
> ARM: OMAP: RX51: set MMC capabilities and power-saving flag
>
> Specify MMC capabilities and set the power-saving flag for RX51.
>
> 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]>
> ---
> 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 */
> };
On Mon, Jul 27, 2009 at 10:58:56AM +0300, Adrian Hunter wrote:
> Matt Fleming wrote:
>> On Fri, Jul 10, 2009 at 03:43:53PM +0300, Adrian Hunter wrote:
>>> >From 3865867159f25cf706480236f6d4f0e4adde5dac 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.
>>>
>>
>> After the changes to [PATCH 7/32], this becomes,
>
> I think this will leave every other board that uses omap_hsmmc
> without any card caps.
>
Doh. You're right, good catch!
> What about adding something like this:
>
>
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index 8900bae..f2548f1 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -1720,6 +1720,11 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
> mmc->caps |= MMC_CAP_4_BIT_DATA;
>
> mmc->caps |= mmc_slot(host).caps;
> + /* If no card caps specified then assume them all */
> + if (!((mmc->caps & MMC_CAP_SD) ||
> + (mmc->caps & MMC_CAP_MMC) ||
> + (mmc->caps & MMC_CAP_SDIO)))
> + mmc->caps |= MMC_CAP_SD & MMC_CAP_MMC & MMC_CAP_SDIO;
>
> omap_hsmmc_conf_bus_power(host);
>
Those & should be |
/* If no card caps specified then assume them all */
if (!((mmc->caps & MMC_CAP_SD) ||
(mmc->caps & MMC_CAP_MMC) ||
(mmc->caps & MMC_CAP_SDIO)))
mmc->caps |= MMC_CAP_SD | MMC_CAP_MMC | MMC_CAP_SDIO;
or more compactly,
/* If no card caps specified then assume them all */
if (!(mmc->caps & (MMC_CAP_SD | MMC_CAP_SDIO | MMC_CAP_MMC)))
mmc->caps |= MMC_CAP_SD | MMC_CAP_MMC | MMC_CAP_SDIO;
ext Matt Fleming wrote:
> On Mon, Jul 27, 2009 at 10:58:56AM +0300, Adrian Hunter wrote:
>> Matt Fleming wrote:
>>> On Fri, Jul 10, 2009 at 03:43:53PM +0300, Adrian Hunter wrote:
>>>> >From 3865867159f25cf706480236f6d4f0e4adde5dac 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.
>>>>
>>> After the changes to [PATCH 7/32], this becomes,
>> I think this will leave every other board that uses omap_hsmmc
>> without any card caps.
>>
>
> Doh. You're right, good catch!
>
>> What about adding something like this:
>>
>>
>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>> index 8900bae..f2548f1 100644
>> --- a/drivers/mmc/host/omap_hsmmc.c
>> +++ b/drivers/mmc/host/omap_hsmmc.c
>> @@ -1720,6 +1720,11 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
>> mmc->caps |= MMC_CAP_4_BIT_DATA;
>>
>> mmc->caps |= mmc_slot(host).caps;
>> + /* If no card caps specified then assume them all */
>> + if (!((mmc->caps & MMC_CAP_SD) ||
>> + (mmc->caps & MMC_CAP_MMC) ||
>> + (mmc->caps & MMC_CAP_SDIO)))
>> + mmc->caps |= MMC_CAP_SD & MMC_CAP_MMC & MMC_CAP_SDIO;
>>
>> omap_hsmmc_conf_bus_power(host);
>>
>
> Those & should be |
>
> /* If no card caps specified then assume them all */
> if (!((mmc->caps & MMC_CAP_SD) ||
> (mmc->caps & MMC_CAP_MMC) ||
> (mmc->caps & MMC_CAP_SDIO)))
> mmc->caps |= MMC_CAP_SD | MMC_CAP_MMC | MMC_CAP_SDIO;
>
> or more compactly,
>
> /* If no card caps specified then assume them all */
> if (!(mmc->caps & (MMC_CAP_SD | MMC_CAP_SDIO | MMC_CAP_MMC)))
> mmc->caps |= MMC_CAP_SD | MMC_CAP_MMC | MMC_CAP_SDIO;
Yes much nicer. Will you add it to your patch?
Andrew Morton wrote:
> On Fri, 10 Jul 2009 15:43:09 +0300
> Adrian Hunter <[email protected]> wrote:
>
>> >From 242fae6293adec671b14354f215217354f5076a0 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 disabling interrupts while setting up
>> a new request.
>>
>> Signed-off-by: Adrian Hunter <[email protected]>
>> ---
>> drivers/mmc/host/omap_hsmmc.c | 16 ++++++++++++++++
>> 1 files changed, 16 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
>> index 28563d6..38e1410 100644
>> --- a/drivers/mmc/host/omap_hsmmc.c
>> +++ b/drivers/mmc/host/omap_hsmmc.c
>> @@ -452,6 +452,13 @@ 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 interrupt is already
>> + * enabled, otherwise it is not (i.e. new request).
>> + */
>> + if (!in_interrupt())
>> + enable_irq(host->irq);
>> +
>> OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
>> OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
>> }
>> @@ -1011,6 +1018,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())
>> + disable_irq(host->irq);
>> WARN_ON(host->mrq != NULL);
>> host->mrq = req;
>> err = mmc_omap_prepare_data(host, req);
>> @@ -1019,6 +1033,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())
>> + enable_irq(host->irq);
>> mmc_request_done(mmc, req);
>> return;
>> }
>
> That seems pretty crude. Disabling an interrupt line can be expensive,
> and will shut off any other innocent devices which share the line.
>
> The usual and superior way of fixing races such as this is
> spin_lock_irq[save]().
>
>
>From f54be3678907b85a00d9f51e3ce4c828fc21ee95 Mon Sep 17 00:00:00 2001
From: Adrian Hunter <[email protected]>
Date: Mon, 27 Jul 2009 12:00:17 +0300
Subject: [PATCH] omap_hsmmc: use spin_lock not disable_irq
Disabling an interrupt line can be expensive. The usual and
superior way of fixing races such as this is spin_lock_irqsave.
Signed-off-by: Adrian Hunter <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 16 ++++++++++++----
1 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 8900bae..9850280 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -146,6 +146,8 @@ struct omap_hsmmc_host {
struct work_struct mmc_carddetect_work;
void __iomem *base;
resource_size_t mapbase;
+ spinlock_t irq_lock;
+ unsigned long flags;
unsigned int id;
unsigned int dma_len;
unsigned int dma_sg_idx;
@@ -462,7 +464,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
* enabled, otherwise it is not (i.e. new request).
*/
if (!in_interrupt())
- enable_irq(host->irq);
+ spin_unlock_irqrestore(&host->irq_lock, host->flags);
OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
@@ -626,11 +628,14 @@ static irqreturn_t omap_hsmmc_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;
}
@@ -696,6 +701,8 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id)
if ((end_trans || (status & TC)) && host->mrq)
omap_hsmmc_xfer_done(host, data);
+ spin_unlock(&host->irq_lock);
+
return IRQ_HANDLED;
}
@@ -1066,7 +1073,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
* retries.
*/
if (!in_interrupt()) {
- disable_irq(host->irq);
+ spin_lock_irqsave(&host->irq_lock, host->flags);
/*
* Protect the card from I/O if there is a possibility
* it can be removed.
@@ -1085,7 +1092,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
req->cmd->error = -EBADF;
if (req->data)
req->data->error = -EBADF;
- enable_irq(host->irq);
+ spin_unlock_irqrestore(&host->irq_lock, host->flags);
mmc_request_done(mmc, req);
return;
} else if (host->reqs_blocked)
@@ -1100,7 +1107,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
req->data->error = err;
host->mrq = NULL;
if (!in_interrupt())
- enable_irq(host->irq);
+ spin_unlock_irqrestore(&host->irq_lock, host->flags);
mmc_request_done(mmc, req);
return;
}
@@ -1653,6 +1660,7 @@ static int __init omap_hsmmc_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
On Mon, Jul 27, 2009 at 11:58:22AM +0300, Adrian Hunter wrote:
>
> Yes much nicer. Will you add it to your patch?
Sure, I'll reply with an updated patch to 07/32 of this series.
Thanks Adrian.