[s3c24xx] Add 2.6.31 patches
[openwrt/svn-archive/archive.git] / target / linux / s3c24xx / patches-2.6.31 / 200-s3c-mci.patch
diff --git a/target/linux/s3c24xx/patches-2.6.31/200-s3c-mci.patch b/target/linux/s3c24xx/patches-2.6.31/200-s3c-mci.patch
new file mode 100644 (file)
index 0000000..3c1c249
--- /dev/null
@@ -0,0 +1,379 @@
+diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
+index d84c880..3e79f43 100644
+--- a/drivers/mmc/core/core.c
++++ b/drivers/mmc/core/core.c
+@@ -59,10 +59,11 @@ static int mmc_schedule_delayed_work(struct delayed_work *work,
+ /*
+  * Internal function. Flush all scheduled work from the MMC work queue.
+  */
+-static void mmc_flush_scheduled_work(void)
++void mmc_flush_scheduled_work(void)
+ {
+       flush_workqueue(workqueue);
+ }
++EXPORT_SYMBOL_GPL(mmc_flush_scheduled_work);
+ /**
+  *    mmc_request_done - finish processing an MMC request
+--- a/include/linux/mmc/core.h
++++ b/include/linux/mmc/core.h
+@@ -129,6 +129,8 @@ struct mmc_request {
+ struct mmc_host;
+ struct mmc_card;
++extern void mmc_flush_scheduled_work(void);
++
+ extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
+ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
+ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
+diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
+index 891ef18..fa1889a 100644
+--- a/drivers/mmc/host/Kconfig
++++ b/drivers/mmc/host/Kconfig
+@@ -55,6 +55,18 @@ config MMC_SDHCI_PCI
+         If unsure, say N.
++config MMC_SDHCI_S3C
++      tristate "SDHCI support on Samsung S3C SoC"
++      depends on MMC_SDHCI && PLAT_S3C24XX
++      help
++        This selects the Secure Digital Host Controller Interface (SDHCI)
++        often referrered to as the HSMMC block in some of the Samsung S3C
++        range of SoC.
++
++        If you have a controller with this interface, say Y or M here.
++
++        If unsure, say N.
++
+ config MMC_RICOH_MMC
+       tristate "Ricoh MMC Controller Disabler  (EXPERIMENTAL)"
+       depends on MMC_SDHCI_PCI
+diff --git a/arch/arm/plat-s3c/include/plat/sdhci.h b/arch/arm/plat-s3c/include/plat/sdhci.h
+index f615308..570da2d 100644
+--- a/arch/arm/plat-s3c/include/plat/sdhci.h
++++ b/arch/arm/plat-s3c/include/plat/sdhci.h
+@@ -29,6 +29,7 @@ struct mmc_ios;
+  *            is necessary the controllers and/or GPIO blocks require the
+  *          changing of driver-strength and other controls dependant on
+  *          the card and speed of operation.
++ * sdhci_host: Pointer kept during init, allows presence change notification
+  *
+  * Initialisation data specific to either the machine or the platform
+  * for the device driver to use or call-back when configuring gpio or
+@@ -45,8 +46,11 @@ struct s3c_sdhci_platdata {
+                           void __iomem *regbase,
+                           struct mmc_ios *ios,
+                           struct mmc_card *card);
++      struct sdhci_host * sdhci_host;
+ };
++extern void sdhci_s3c_force_presence_change(struct platform_device *pdev);
++
+ /**
+  * s3c_sdhci0_set_platdata - Set platform data for S3C SDHCI device.
+  * @pd: Platform data to register to device.
+--- /dev/null
++++ b/arch/arm/mach-s3c2410/include/mach/mci.h
+@@ -0,0 +1,13 @@
++#ifndef _ARCH_MCI_H
++#define _ARCH_MCI_H
++
++struct s3c24xx_mci_pdata {
++      unsigned int    gpio_detect;
++      unsigned int    gpio_wprotect;
++      unsigned long   ocr_avail;
++      unsigned int    do_dma;
++      void            (*set_power)(unsigned char power_mode,
++                                   unsigned short vdd);
++};
++
++#endif /* _ARCH_NCI_H */
+diff --git a/arch/arm/mach-s3c2440/s3c2440.c b/arch/arm/mach-s3c2440/s3c2440.c
+index ac1f7ea..f7f8f31 100644
+--- a/arch/arm/mach-s3c2440/s3c2440.c
++++ b/arch/arm/mach-s3c2440/s3c2440.c
+@@ -46,6 +46,9 @@ int __init s3c2440_init(void)
+       s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT;
+       s3c_device_wdt.resource[1].end   = IRQ_S3C2440_WDT;
++      /* make sure SD/MMC driver can distinguish 2440 from 2410 */
++      s3c_device_sdi.name = "s3c2440-sdi";
++
+       /* register our system device for everything else */
+       return sysdev_register(&s3c2440_sysdev);
+diff --git a/arch/arm/mach-s3c2442/s3c2442.c b/arch/arm/mach-s3c2442/s3c2442.c
+index 4663bdc..9602d57 100644
+--- a/arch/arm/mach-s3c2442/s3c2442.c
++++ b/arch/arm/mach-s3c2442/s3c2442.c
+@@ -21,6 +21,7 @@
+ #include <plat/s3c2442.h>
+ #include <plat/cpu.h>
++#include <plat/devs.h>
+ static struct sys_device s3c2442_sysdev = {
+       .cls            = &s3c2442_sysclass,
+@@ -30,5 +31,8 @@ int __init s3c2442_init(void)
+ {
+       printk("S3C2442: Initialising architecture\n");
++      /* make sure SD/MMC driver can distinguish 2440 from 2410 */
++      s3c_device_sdi.name = "s3c2440-sdi";
++
+       return sysdev_register(&s3c2442_sysdev);
+ }
+diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
+index cf153f6..c25f464 100644
+--- a/drivers/mmc/host/Makefile
++++ b/drivers/mmc/host/Makefile
+@@ -13,6 +13,7 @@ endif
+ obj-$(CONFIG_MMC_MXC)         += mxcmmc.o
+ obj-$(CONFIG_MMC_SDHCI)               += sdhci.o
+ obj-$(CONFIG_MMC_SDHCI_PCI)   += sdhci-pci.o
++obj-$(CONFIG_MMC_SDHCI_S3C)   += sdhci-s3c.o
+ obj-$(CONFIG_MMC_RICOH_MMC)   += ricoh_mmc.o
+ obj-$(CONFIG_MMC_SDHCI_OF)    += sdhci-of.o
+ obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
+diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
+index ca1ba3d..7f80047 100644
+--- a/drivers/mmc/host/s3cmci.h
++++ b/drivers/mmc/host/s3cmci.h
+@@ -8,6 +8,10 @@
+  * published by the Free Software Foundation.
+  */
++
++#include <mach/regs-sdi.h>
++#include <linux/regulator/consumer.h>
++
+ /* FIXME: DMA Resource management ?! */
+ #define S3CMCI_DMA 0
+@@ -68,7 +72,16 @@ struct s3cmci_host {
+       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
++
++      struct regulator *regulator;
+ };
+diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
+index 8c08cd7..b3cded4 100644
+--- a/drivers/mmc/host/s3cmci.c
++++ b/drivers/mmc/host/s3cmci.c
+@@ -2,6 +2,7 @@
+  *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
+  *
+  *  Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
++ *  Copyright (C) 2007 Harald Welte <laforge@gnumonks.org>
+  *
+  * Current driver maintained by Ben Dooks and Simtec Electronics
+  *  Copyright (C) 2008 Simtec Electronics <ben-linux@fluff.org>
+@@ -25,9 +26,18 @@
+ #include <mach/regs-sdi.h>
+ #include <mach/regs-gpio.h>
++#include <mach/hardware.h>
+ #include <plat/mci.h>
++#include <asm/dma.h>
++#include <asm/dma-mapping.h>
++
++#include <asm/io.h>
++#include <mach/regs-gpio.h>
++#include <mach/mci.h>
++#include <mach/dma.h>
++
+ #include "s3cmci.h"
+ #define DRIVER_NAME "s3c-mci"
+@@ -48,6 +58,9 @@ static const int dbgmap_err   = dbg_fail;
+ static const int dbgmap_info  = dbg_info | dbg_conf;
+ 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 {                                      \
+       if (dbgmap_err & channels)                \
+@@ -281,8 +294,11 @@ static void do_pio_read(struct s3cmci_host *host)
+                * an even multiple of 4. */
+               if (fifo >= host->pio_bytes)
+                       fifo = host->pio_bytes;
+-              else
++              else {
+                       fifo -= fifo & 3;
++                      if (!fifo)
++                              break;
++              }
+               host->pio_bytes -= fifo;
+               host->pio_count += fifo;
+@@ -330,7 +346,7 @@ static void do_pio_write(struct s3cmci_host *host)
+       to_ptr = host->base + host->sdidata;
+-      while ((fifo = fifo_free(host)) > 3) {
++      while ((fifo = fifo_free(host))) {
+               if (!host->pio_bytes) {
+                       res = get_data_buffer(host, &host->pio_bytes,
+                                                       &host->pio_ptr);
+@@ -354,8 +370,11 @@ static void do_pio_write(struct s3cmci_host *host)
+                * words, so round down to an even multiple of 4. */
+               if (fifo >= host->pio_bytes)
+                       fifo = host->pio_bytes;
+-              else
++              else {
+                       fifo -= fifo & 3;
++                      if (!fifo)
++                              break;
++              }
+               host->pio_bytes -= fifo;
+               host->pio_count += fifo;
+@@ -374,7 +393,6 @@ static void pio_tasklet(unsigned long data)
+ {
+       struct s3cmci_host *host = (struct s3cmci_host *) data;
+-
+       disable_irq(host->irq);
+       if (host->pio_active == XFER_WRITE)
+@@ -615,7 +633,6 @@ irq_out:
+       spin_unlock_irqrestore(&host->complete_lock, iflags);
+       return IRQ_HANDLED;
+-
+ }
+ /*
+@@ -1027,6 +1044,7 @@ static void s3cmci_send_request(struct mmc_host *mmc)
+                       dbg(host, dbg_err, "data prepare error %d\n", res);
+                       cmd->error = res;
+                       cmd->data->error = res;
++                      cmd->data->error = -EIO;
+                       mmc_request_done(mmc, mrq);
+                       return;
+@@ -1264,10 +1282,8 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
+       host->is2440    = is2440;
+       host->pdata = pdev->dev.platform_data;
+-      if (!host->pdata) {
+-              pdev->dev.platform_data = &s3cmci_def_pdata;
++      if (!host->pdata)
+               host->pdata = &s3cmci_def_pdata;
+-      }
+       spin_lock_init(&host->complete_lock);
+       tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
+@@ -1380,6 +1396,18 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
+       mmc->f_min      = host->clk_rate / (host->clk_div * 256);
+       mmc->f_max      = host->clk_rate / host->clk_div;
++      if (f_max >= 0) {
++              unsigned f = f_max;
++
++              if (f < mmc->f_min)
++                      f = mmc->f_min;
++              if (mmc->f_max > f) {
++                      dev_info(&pdev->dev, "f_max lowered from %u to %u Hz\n",
++                          mmc->f_max, f);
++                      mmc->f_max = f;
++              }
++      }
++
+       if (host->pdata->ocr_avail)
+               mmc->ocr_avail = host->pdata->ocr_avail;
+@@ -1492,18 +1520,60 @@ static int __devinit s3cmci_2440_probe(struct platform_device *dev)
+ #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 */
+@@ -1561,9 +1631,13 @@ static void __exit s3cmci_exit(void)
+ module_init(s3cmci_init);
+ 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");
+ MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>, Ben Dooks <ben-linux@fluff.org>");
+ MODULE_ALIAS("platform:s3c2410-sdi");
+ MODULE_ALIAS("platform:s3c2412-sdi");
+ MODULE_ALIAS("platform:s3c2440-sdi");
++
+