From d944934bbddc281938fb1905a69e35c0581937c2 Mon Sep 17 00:00:00 2001
From: Mike Westerhof <mwester@dls.net>
Date: Thu, 13 Nov 2008 20:39:36 +0000
Subject: [PATCH] GTA01-use-slow-SD-clock-when-gps-on.patch

This patch implements Andy Green's SD clock slow-down code
for the GTA01 device.

Signed-off-by: Mike Westerhof <mwester@dls.net>
---
 arch/arm/mach-s3c2410/mach-gta01.c |    8 ++++++
 drivers/mmc/host/s3cmci.c          |   43 ++++++++++++++++++++++++++++++++++-
 drivers/mmc/host/s3cmci.h          |    1 +
 include/asm-arm/arch-s3c2410/mci.h |    1 +
 4 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-s3c2410/mach-gta01.c b/arch/arm/mach-s3c2410/mach-gta01.c
index a364b76..c0817dc 100644
--- a/arch/arm/mach-s3c2410/mach-gta01.c
+++ b/arch/arm/mach-s3c2410/mach-gta01.c
@@ -75,6 +75,8 @@
 #include <asm/plat-s3c24xx/neo1973.h>
 #include <asm/arch-s3c2410/neo1973-pm-gsm.h>
 
+#include "../plat-s3c24xx/neo1973_pm_gps.h"
+
 #include <linux/jbt6k74.h>
 
 static struct map_desc gta01_iodesc[] __initdata = {
@@ -432,9 +434,15 @@ static void gta01_mmc_set_power(unsigned char power_mode, unsigned short vdd)
 	}
 }
 
+static int gta01_mmc_use_slow(void)
+{
+	return neo1973_pm_gps_is_on();
+}
+
 static struct s3c24xx_mci_pdata gta01_mmc_cfg = {
 	.gpio_detect	= GTA01_GPIO_nSD_DETECT,
 	.set_power	= &gta01_mmc_set_power,
+	.use_slow	= &gta01_mmc_use_slow,
 	.ocr_avail	= MMC_VDD_165_195|MMC_VDD_20_21|
 			  MMC_VDD_21_22|MMC_VDD_22_23|MMC_VDD_23_24|
 			  MMC_VDD_24_25|MMC_VDD_25_26|MMC_VDD_26_27|
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index 8f88721..87d24b2 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -59,6 +59,23 @@ module_param(sd_max_clk, int, 0644);
 static int sd_idleclk;	 /* disallow idle clock by default */
 module_param(sd_idleclk, int, 0644);
 
+/*
+ * Slow SD clock rate
+ *
+ * you can override this on kernel commandline using
+ *
+ *  s3cmci.sd_slow_ratio=8
+ *
+ * for example.
+ *
+ * A platform callback is used to decide effective clock rate.  If not
+ * defined, then the max is used, if defined and the callback returns
+ * nonzero, the rate is divided by this factor.
+ */
+
+static int sd_slow_ratio = 8;
+module_param(sd_slow_ratio, int, 0644);
+
 /* used to stash real idleclk state in suspend: we force it to run in there */
 static int suspend_sd_idleclk;
 
@@ -252,7 +269,7 @@ static inline void do_pio_read(struct s3cmci_host *host)
 	void __iomem *from_ptr;
 
 	/* write real prescaler to host, it might be set slow to fix */
-	writel(host->prescaler, host->base + S3C2410_SDIPRE);
+	writel(host->sdipre, host->base + S3C2410_SDIPRE);
 
 	from_ptr = host->base + host->sdidata;
 
@@ -755,7 +772,7 @@ static void finalize_request(struct s3cmci_host *host)
 	cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);
 
 	/* reset clock speed, as it could still be set low for */
-	writel(host->prescaler, host->base + S3C2410_SDIPRE);
+	writel(host->sdipre, host->base + S3C2410_SDIPRE);
 
 	if (cmd->error)
 		debug_as_failure = 1;
@@ -1032,6 +1049,7 @@ static void s3cmci_send_request(struct mmc_host *mmc)
 	struct s3cmci_host *host = mmc_priv(mmc);
 	struct mmc_request *mrq = host->mrq;
 	struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;
+	int pre;
 
 	host->ccnt++;
 #ifdef CONFIG_MMC_DEBUG
@@ -1073,6 +1091,26 @@ static void s3cmci_send_request(struct mmc_host *mmc)
 
 	}
 
+	/* establish the correct prescaler depending on the sd_slow_ratio */
+
+	if ((sd_slow_ratio > 1) &&
+	    host->pdata->use_slow && (host->pdata->use_slow)()) {
+		/* compute the slower speed */
+		pre = host->prescaler * sd_slow_ratio;
+		if (pre > 255)
+			pre = 255;
+	} else {
+		/* use the normal speed */
+		pre = host->prescaler;
+	}
+
+	if (host->sdipre != pre) {
+		dbg(host, dbg_conf, "prescaler changed: %d -> %d\n",
+		    (int)host->sdipre, pre);
+		host->sdipre = pre;
+		writel(host->sdipre, host->base + S3C2410_SDIPRE);
+	}
+
 	__s3cmci_enable_clock(host);
 	s3cmci_send_command(host, cmd);
 	enable_irq(host->irq);
@@ -1138,6 +1176,7 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 	if (mci_psc > 255)
 		mci_psc = 255;
 	host->prescaler = mci_psc;
+	host->sdipre = mci_psc;
 
 	writel(host->prescaler, host->base + S3C2410_SDIPRE);
 
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
index 9644b45..11974ff 100644
--- a/drivers/mmc/host/s3cmci.h
+++ b/drivers/mmc/host/s3cmci.h
@@ -32,6 +32,7 @@ struct s3cmci_host {
 	unsigned long		clk_div;
 	unsigned long		real_rate;
 	u8			prescaler;
+	u8			sdipre;
 
 	int			is2440;
 	unsigned		sdiimsk;
diff --git a/include/asm-arm/arch-s3c2410/mci.h b/include/asm-arm/arch-s3c2410/mci.h
index 24e6cd1..f087229 100644
--- a/include/asm-arm/arch-s3c2410/mci.h
+++ b/include/asm-arm/arch-s3c2410/mci.h
@@ -8,6 +8,7 @@ struct s3c24xx_mci_pdata {
 	unsigned int	do_dma;
 	void		(*set_power)(unsigned char power_mode,
 				     unsigned short vdd);
+	int		(*use_slow)(void);
 };
 
 #endif /* _ARCH_NCI_H */
-- 
1.5.6.5