diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.24/1322-fix-gta01-s3c-mci-stop-clock-when-idle.patch.patch')
-rw-r--r-- | target/linux/s3c24xx/patches-2.6.24/1322-fix-gta01-s3c-mci-stop-clock-when-idle.patch.patch | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.24/1322-fix-gta01-s3c-mci-stop-clock-when-idle.patch.patch b/target/linux/s3c24xx/patches-2.6.24/1322-fix-gta01-s3c-mci-stop-clock-when-idle.patch.patch new file mode 100644 index 000000000..7717199b2 --- /dev/null +++ b/target/linux/s3c24xx/patches-2.6.24/1322-fix-gta01-s3c-mci-stop-clock-when-idle.patch.patch @@ -0,0 +1,211 @@ +From ff31834cfaac386f94ddd65fab8b87d090c69bcc Mon Sep 17 00:00:00 2001 +From: Mike Westerhof <mwester@dls.net> +Date: Thu, 13 Nov 2008 20:38:35 +0000 +Subject: [PATCH] fix-gta01-s3c-mci-stop-clock-when-idle.patch + +This patch, based on the work done by Andy Green for the Glamo mci +driver, makes sure that the SD clock only runs during data transfers. +This can be overridden on the kernel command line if desired. Also +added is the ability for the maximum SD clock speed to be limited. + +Signed-off-by: Mike Westerhof (mwester@dls.net) +--- + drivers/mmc/host/s3cmci.c | 113 +++++++++++++++++++++++++++++++++++++++++++-- + 1 files changed, 109 insertions(+), 4 deletions(-) + +diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c +index edba055..8f88721 100644 +--- a/drivers/mmc/host/s3cmci.c ++++ b/drivers/mmc/host/s3cmci.c +@@ -15,6 +15,8 @@ + #include <linux/mmc/host.h> + #include <linux/platform_device.h> + #include <linux/irq.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> + + #include <asm/dma.h> + #include <asm/dma-mapping.h> +@@ -29,6 +31,37 @@ + + #define DRIVER_NAME "s3c-mci" + ++static spinlock_t clock_lock; ++ ++/* ++ * Max SD clock rate (in Hz) ++ * ++ * you can override this on the kernel command line using ++ * ++ * s3cmci.sd_max_clk=10000000 ++ * ++ * for example. ++ */ ++ ++static int sd_max_clk = 25000000; ++module_param(sd_max_clk, int, 0644); ++ ++/* ++ * SD allow SD clock to run while idle ++ * ++ * you can override this on kernel commandline using ++ * ++ * s3cmci.sd_idleclk=0 ++ * ++ * for example. ++ */ ++ ++static int sd_idleclk; /* disallow idle clock by default */ ++module_param(sd_idleclk, int, 0644); ++ ++/* used to stash real idleclk state in suspend: we force it to run in there */ ++static int suspend_sd_idleclk; ++ + enum dbg_channels { + dbg_err = (1 << 0), + dbg_debug = (1 << 1), +@@ -368,6 +401,40 @@ static void pio_tasklet(unsigned long data) + enable_irq(host->irq); + } + ++static void __s3cmci_enable_clock(struct s3cmci_host *host) ++{ ++ u32 mci_con; ++ unsigned long flags; ++ ++ /* enable the clock if clock rate is > 0 */ ++ if (host->real_rate) { ++ spin_lock_irqsave(&clock_lock, flags); ++ ++ mci_con = readl(host->base + S3C2410_SDICON); ++ mci_con |= S3C2410_SDICON_CLOCKTYPE; ++ writel(mci_con, host->base + S3C2410_SDICON); ++ ++ spin_unlock_irqrestore(&clock_lock, flags); ++ } ++} ++ ++static void __s3cmci_disable_clock(struct s3cmci_host *host) ++{ ++ u32 mci_con; ++ unsigned long flags; ++ ++ if (!sd_idleclk) { ++ spin_lock_irqsave(&clock_lock, flags); ++ ++ mci_con = readl(host->base + S3C2410_SDICON); ++ mci_con &= ~S3C2410_SDICON_CLOCKTYPE; ++ writel(mci_con, host->base + S3C2410_SDICON); ++ ++ spin_unlock_irqrestore(&clock_lock, flags); ++ } ++} ++ ++ + /* + * ISR for SDI Interface IRQ + * Communication between driver and ISR works as follows: +@@ -749,6 +816,7 @@ static void finalize_request(struct s3cmci_host *host) + } + + request_done: ++ __s3cmci_disable_clock(host); + host->complete_what = COMPLETION_NONE; + host->mrq = NULL; + mmc_request_done(host->mmc, mrq); +@@ -1005,6 +1073,7 @@ static void s3cmci_send_request(struct mmc_host *mmc) + + } + ++ __s3cmci_enable_clock(host); + s3cmci_send_command(host, cmd); + enable_irq(host->irq); + } +@@ -1087,14 +1156,30 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) + if ((ios->power_mode == MMC_POWER_ON) + || (ios->power_mode == MMC_POWER_UP)) { + +- dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n", +- host->real_rate/1000, ios->clock/1000); ++ dbg(host, dbg_conf, ++ "powered (vdd: %d), clk: %lukHz div=%lu (req: %ukHz)," ++ " bus width: %d\n", ios->vdd, host->real_rate / 1000, ++ host->clk_div * (host->prescaler + 1), ++ ios->clock / 1000, ios->bus_width); ++ ++ /* After power-up, we need to give the card 74 clocks to ++ * initialize, so sleep just a moment before we disable ++ * the clock again. ++ */ ++ if (ios->clock) ++ msleep(1); ++ + } else { + dbg(host, dbg_conf, "powered down.\n"); + } + + host->bus_width = ios->bus_width; + ++ /* No need to run the clock until we have data to move */ ++ if (!sd_idleclk) { ++ __s3cmci_disable_clock(host); ++ dbg(host, dbg_conf, "SD clock disabled when idle.\n"); ++ } + } + + static void s3cmci_reset(struct s3cmci_host *host) +@@ -1267,7 +1352,7 @@ static int s3cmci_probe(struct platform_device *pdev, int is2440) + mmc->ocr_avail = host->pdata->ocr_avail; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; + mmc->f_min = host->clk_rate / (host->clk_div * 256); +- mmc->f_max = host->clk_rate / host->clk_div; ++ mmc->f_max = sd_max_clk; + + mmc->max_blk_count = 4095; + mmc->max_blk_size = 4095; +@@ -1354,14 +1439,33 @@ static int s3cmci_probe_2440(struct platform_device *dev) + static int s3cmci_suspend(struct platform_device *dev, pm_message_t state) + { + struct mmc_host *mmc = platform_get_drvdata(dev); ++ struct s3cmci_host *host = mmc_priv(mmc); ++ int ret; ++ ++ /* Ensure clock is running so it will be running on resume */ ++ __s3cmci_enable_clock(host); + +- return mmc_suspend_host(mmc, state); ++ /* We will do more commands, make sure the clock stays running, ++ * and save our state so that we can restore it on resume. ++ */ ++ suspend_sd_idleclk = sd_idleclk; ++ sd_idleclk = 1; ++ ++ ret = mmc_suspend_host(mmc, state); ++ ++ /* so that when we resume, we use any modified max rate */ ++ mmc->f_max = sd_max_clk; ++ ++ return ret; + } + + static int s3cmci_resume(struct platform_device *dev) + { + struct mmc_host *mmc = platform_get_drvdata(dev); + ++ /* Put the sd_idleclk state back to what it was */ ++ sd_idleclk = suspend_sd_idleclk; ++ + return mmc_resume_host(mmc); + } + +@@ -1398,6 +1502,7 @@ static struct platform_driver s3cmci_driver_2440 = { + + static int __init s3cmci_init(void) + { ++ spin_lock_init(&clock_lock); + platform_driver_register(&s3cmci_driver_2410); + platform_driver_register(&s3cmci_driver_2412); + platform_driver_register(&s3cmci_driver_2440); +-- +1.5.6.5 + |