diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.31/200-s3c-mci.patch')
-rw-r--r-- | target/linux/s3c24xx/patches-2.6.31/200-s3c-mci.patch | 379 |
1 files changed, 379 insertions, 0 deletions
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 index 0000000000..3c1c249231 --- /dev/null +++ b/target/linux/s3c24xx/patches-2.6.31/200-s3c-mci.patch @@ -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"); ++ + |