mirror of https://github.com/hak5/openwrt.git
380 lines
11 KiB
Diff
380 lines
11 KiB
Diff
|
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");
|
||
|
+
|
||
|
|