Allow s3cmci to preserve its state across suspend/resume without
letting the stack take down and re-scan the interface.

This can be useful for cards that implement their own low-power
mode and that have state that can be preserved and that should
be available quickly after resume. E.g., a WLAN card with an
active association.

This patch does not change the default behaviour. To keep the
interface alive across suspend/resume, set the "persist" option,
either with persist=1 (module loading) or s3cmci.persist=1 (on
the boot parameter line).

Signed-off-by: Werner Almesberger <[EMAIL PROTECTED]>

---

Index: ktrack/drivers/mmc/host/s3cmci.c
===================================================================
--- ktrack.orig/drivers/mmc/host/s3cmci.c       2008-11-26 22:17:53.000000000 
-0200
+++ ktrack/drivers/mmc/host/s3cmci.c    2008-11-26 22:29:43.000000000 -0200
@@ -57,6 +57,7 @@
 static const int dbgmap_debug = dbg_err | dbg_debug;
 
 static int f_max = -1; /* override maximum frequency limit */
+static int persist; /* keep interface alive across suspend/resume */
 
 #define dbg(host, channels, args...)             \
        do {                                      \
@@ -1518,18 +1519,60 @@
 
 #ifdef CONFIG_PM
 
+static int save_regs(struct mmc_host *mmc)
+{
+       struct s3cmci_host *host = mmc_priv(mmc);
+       unsigned long flags;
+       unsigned from;
+       u32 *to = host->saved;
+
+       mmc_flush_scheduled_work();
+
+       local_irq_save(flags);
+       for (from = S3C2410_SDICON; from != S3C2410_SDIIMSK+4; from += 4)
+               if (from != host->sdidata)
+                       *to++ = readl(host->base + from);
+       BUG_ON(to-host->saved != ARRAY_SIZE(host->saved));
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+static int restore_regs(struct mmc_host *mmc)
+{
+       struct s3cmci_host *host = mmc_priv(mmc);
+       unsigned long flags;
+       unsigned to;
+       u32 *from = host->saved;
+
+       /*
+        * Before we begin with the necromancy, make sure we don't
+        * inadvertently start something we'll regret microseconds later.
+        */
+       from[S3C2410_SDICMDCON - S3C2410_SDICON] = 0;
+
+       local_irq_save(flags);
+       for (to = S3C2410_SDICON; to != S3C2410_SDIIMSK+4; to += 4)
+               if (to != host->sdidata)
+                       writel(*from++, host->base + to);
+       BUG_ON(from-host->saved != ARRAY_SIZE(host->saved));
+       local_irq_restore(flags);
+
+       return 0;
+}
+
 static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
 {
        struct mmc_host *mmc = platform_get_drvdata(dev);
 
-       return  mmc_suspend_host(mmc, state);
+       return persist ? save_regs(mmc) : mmc_suspend_host(mmc, state);
 }
 
 static int s3cmci_resume(struct platform_device *dev)
 {
        struct mmc_host *mmc = platform_get_drvdata(dev);
 
-       return mmc_resume_host(mmc);
+       return persist ? restore_regs(mmc) : mmc_resume_host(mmc);
 }
 
 #else /* CONFIG_PM */
@@ -1588,6 +1631,7 @@
 module_exit(s3cmci_exit);
 
 module_param(f_max, int, 0644);
+module_param(persist, int, 0644);
 
 MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
 MODULE_LICENSE("GPL v2");
Index: ktrack/drivers/mmc/host/s3cmci.h
===================================================================
--- ktrack.orig/drivers/mmc/host/s3cmci.h       2008-11-26 22:17:53.000000000 
-0200
+++ ktrack/drivers/mmc/host/s3cmci.h    2008-11-26 22:24:26.000000000 -0200
@@ -8,6 +8,9 @@
  * published by the Free Software Foundation.
  */
 
+
+#include <mach/regs-sdi.h>
+
 /* FIXME: DMA Resource management ?! */
 #define S3CMCI_DMA 0
 
@@ -68,6 +71,13 @@
        unsigned int            ccnt, dcnt;
        struct tasklet_struct   pio_tasklet;
 
+       /*
+        * Here's where we save the registers during suspend. Note that we skip
+        * SDIDATA, which is at different positions on 2410 and 2440, so
+        * there's no "+1" in the array size.
+        */
+       u32                     saved[(S3C2410_SDIIMSK-S3C2410_SDICON)/4];
+
 #ifdef CONFIG_CPU_FREQ
        struct notifier_block   freq_transition;
 #endif

Reply via email to