614683faf8
SVN-Revision: 13613
240 lines
7.1 KiB
Diff
Executable file
240 lines
7.1 KiB
Diff
Executable file
From ee83ea40a3a1d1cb6447fdd345384d9cabf21c83 Mon Sep 17 00:00:00 2001
|
|
From: Andy Green <andy@openmoko.com>
|
|
Date: Fri, 25 Jul 2008 23:06:20 +0100
|
|
Subject: [PATCH] fix-force-sdcard-clk-off-when-idle.patch
|
|
|
|
Existing Glamo bit for stopping SD Card Clock when there is no
|
|
transfer taking place does not work. This patch adds stuff around
|
|
the transfer code to force the SD clock up when something is going on
|
|
and down when it is idle. This'll save a little power and noise ;-)
|
|
|
|
I tested it briefly and was able to SD Boot normally on Sandisk 512M.
|
|
Wider testing is appreciated.
|
|
|
|
Signed-off-by: Andy Green <andy@openmoko.com>
|
|
---
|
|
drivers/mfd/glamo/glamo-mci.c | 126 +++++++++++++++++++++++++++++------------
|
|
1 files changed, 89 insertions(+), 37 deletions(-)
|
|
|
|
diff --git a/drivers/mfd/glamo/glamo-mci.c b/drivers/mfd/glamo/glamo-mci.c
|
|
index 37e3d3c..b53827e 100644
|
|
--- a/drivers/mfd/glamo/glamo-mci.c
|
|
+++ b/drivers/mfd/glamo/glamo-mci.c
|
|
@@ -20,6 +20,7 @@
|
|
#include <linux/pcf50633.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/interrupt.h>
|
|
+#include <linux/spinlock.h>
|
|
|
|
#include <asm/dma.h>
|
|
#include <asm/dma-mapping.h>
|
|
@@ -32,6 +33,7 @@
|
|
/* from glamo-core.c */
|
|
extern struct glamo_mci_pdata glamo_mci_def_pdata;
|
|
|
|
+static spinlock_t clock_lock;
|
|
|
|
#define DRIVER_NAME "glamo-mci"
|
|
#define RESSIZE(ressource) (((ressource)->end - (ressource)->start) + 1)
|
|
@@ -164,6 +166,67 @@ static int do_pio_write(struct glamo_mci_host *host)
|
|
return err;
|
|
}
|
|
|
|
+static void __glamo_mci_fix_card_div(struct glamo_mci_host *host, int div)
|
|
+{
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&clock_lock, flags);
|
|
+
|
|
+ if (div < 0) {
|
|
+ /* stop clock - remove clock from divider input */
|
|
+ writew(readw(glamo_mci_def_pdata.pglamo->base +
|
|
+ GLAMO_REG_CLOCK_GEN5_1) & (~GLAMO_CLOCK_GEN51_EN_DIV_TCLK),
|
|
+ glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
|
|
+ } else {
|
|
+ /* set the nearest prescaler factor
|
|
+ *
|
|
+ * register shared with SCLK divisor -- no chance of race because
|
|
+ * we don't use sensor interface
|
|
+ */
|
|
+ writew_dly((readw(glamo_mci_def_pdata.pglamo->base +
|
|
+ GLAMO_REG_CLOCK_GEN8) & 0xff00) | div,
|
|
+ glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN8);
|
|
+ /* enable clock to divider input */
|
|
+ writew_dly(readw(glamo_mci_def_pdata.pglamo->base +
|
|
+ GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK,
|
|
+ glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
|
|
+ }
|
|
+
|
|
+ spin_unlock_irqrestore(&clock_lock, flags);
|
|
+}
|
|
+
|
|
+static int __glamo_mci_set_card_clock(struct glamo_mci_host *host, int freq,
|
|
+ int *division)
|
|
+{
|
|
+ int div = 0;
|
|
+ int real_rate = 0;
|
|
+
|
|
+ if (freq) {
|
|
+ /* Set clock */
|
|
+ for (div = 0; div < 256; div++) {
|
|
+ real_rate = host->clk_rate / (div + 1);
|
|
+ if (real_rate <= freq)
|
|
+ break;
|
|
+ }
|
|
+ if (div > 255)
|
|
+ div = 255;
|
|
+
|
|
+ if (division)
|
|
+ *division = div;
|
|
+
|
|
+ __glamo_mci_fix_card_div(host, div);
|
|
+
|
|
+ } else {
|
|
+ /* stop clock */
|
|
+ if (division)
|
|
+ *division = 0xff;
|
|
+
|
|
+ __glamo_mci_fix_card_div(host, -1); /* clock off */
|
|
+ }
|
|
+
|
|
+ return real_rate;
|
|
+}
|
|
+
|
|
static void glamo_mci_irq(unsigned int irq, struct irq_desc *desc)
|
|
{
|
|
struct glamo_mci_host *host = (struct glamo_mci_host *)
|
|
@@ -212,6 +275,10 @@ static void glamo_mci_irq(unsigned int irq, struct irq_desc *desc)
|
|
glamo_mci_send_request(host->mmc);
|
|
host->cmd_is_stop = 0;
|
|
}
|
|
+
|
|
+ /* clock off */
|
|
+ __glamo_mci_fix_card_div(host, -1);
|
|
+
|
|
done:
|
|
host->complete_what = COMPLETION_NONE;
|
|
host->mrq = NULL;
|
|
@@ -441,8 +508,11 @@ static void glamo_mci_send_request(struct mmc_host *mmc)
|
|
cmd->opcode, cmd->arg, cmd->data, cmd->mrq->stop,
|
|
cmd->flags);
|
|
|
|
+ /* resume requested clock rate */
|
|
+ __glamo_mci_fix_card_div(host, host->clk_div);
|
|
+
|
|
if (glamo_mci_send_command(host, cmd))
|
|
- return;
|
|
+ goto bail;
|
|
/*
|
|
* we must spin until response is ready or timed out
|
|
* -- we don't get interrupts unless there is a bulk rx
|
|
@@ -464,7 +534,7 @@ static void glamo_mci_send_request(struct mmc_host *mmc)
|
|
cmd->error = -EILSEQ;
|
|
|
|
if (host->cmd_is_stop)
|
|
- return;
|
|
+ goto bail;
|
|
|
|
if (cmd->error) {
|
|
dev_err(&host->pdev->dev, "Error after cmd: 0x%x\n", status);
|
|
@@ -516,10 +586,12 @@ static void glamo_mci_send_request(struct mmc_host *mmc)
|
|
if (cmd->data->error)
|
|
cmd->data->error = -ETIMEDOUT;
|
|
dev_err(&host->pdev->dev, "Payload timeout\n");
|
|
- return;
|
|
+ goto bail;
|
|
}
|
|
|
|
- /* yay we are an interrupt controller! -- call the ISR */
|
|
+ /* yay we are an interrupt controller! -- call the ISR
|
|
+ * it will stop clock to card
|
|
+ */
|
|
glamo_mci_irq(IRQ_GLAMO(GLAMO_IRQIDX_MMC),
|
|
irq_desc + IRQ_GLAMO(GLAMO_IRQIDX_MMC));
|
|
}
|
|
@@ -529,6 +601,12 @@ done:
|
|
host->complete_what = COMPLETION_NONE;
|
|
host->mrq = NULL;
|
|
mmc_request_done(host->mmc, cmd->mrq);
|
|
+ return;
|
|
+
|
|
+bail:
|
|
+ /* stop the clock to card */
|
|
+ __glamo_mci_fix_card_div(host, -1);
|
|
+ return;
|
|
}
|
|
|
|
static void glamo_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|
@@ -556,11 +634,12 @@ static void glamo_mci_reset(struct glamo_mci_host *host)
|
|
glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_MMC);
|
|
}
|
|
|
|
+
|
|
static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
{
|
|
struct glamo_mci_host *host = mmc_priv(mmc);
|
|
- int mci_psc = 0;
|
|
int n = 0;
|
|
+ int div;
|
|
|
|
/* Set power */
|
|
switch(ios->power_mode) {
|
|
@@ -590,43 +669,15 @@ static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
}
|
|
host->power_mode_current = ios->power_mode;
|
|
|
|
- /* Set clock */
|
|
-/* if (ios->clock) { */
|
|
- for (mci_psc = 0; mci_psc < 256; mci_psc++) {
|
|
- host->real_rate = host->clk_rate / (mci_psc + 1);
|
|
- if (host->real_rate <= ios->clock)
|
|
- break;
|
|
- }
|
|
- if (mci_psc > 255)
|
|
- mci_psc = 255;
|
|
- host->clk_div = mci_psc;
|
|
- /* set the nearest prescaler factor
|
|
- *
|
|
- * register shared with SCLK divisor -- no chance of race because
|
|
- * we don't use sensor interface
|
|
- */
|
|
- writew_dly((readw(glamo_mci_def_pdata.pglamo->base +
|
|
- GLAMO_REG_CLOCK_GEN8) & 0xff00) | host->clk_div,
|
|
- glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN8);
|
|
- /* enable clock to divider input */
|
|
- writew_dly(readw(glamo_mci_def_pdata.pglamo->base +
|
|
- GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK,
|
|
- glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
|
|
-#if 0
|
|
- } else { /* stop clock */
|
|
- host->real_rate = 0;
|
|
- /* remove clock from divider input */
|
|
- writew(readw(glamo_mci_def_pdata.pglamo->base +
|
|
- GLAMO_REG_CLOCK_GEN5_1) & (~GLAMO_CLOCK_GEN51_EN_DIV_TCLK),
|
|
- glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
|
|
- }
|
|
-#endif
|
|
+ host->real_rate = __glamo_mci_set_card_clock(host, ios->clock, &div);
|
|
+ host->clk_div = div;
|
|
+
|
|
if ((ios->power_mode == MMC_POWER_ON) ||
|
|
(ios->power_mode == MMC_POWER_UP)) {
|
|
dev_info(&host->pdev->dev,
|
|
"powered (vdd = %d) clk: %lukHz div=%d (req: %ukHz). "
|
|
"Bus width=%d\n",ios->vdd,
|
|
- host->real_rate / 1000, mci_psc,
|
|
+ host->real_rate / 1000, host->real_rate,
|
|
ios->clock / 1000, ios->bus_width);
|
|
} else
|
|
dev_info(&host->pdev->dev, "glamo_mci_set_ios: power down.\n");
|
|
@@ -856,6 +907,7 @@ static struct platform_driver glamo_mci_driver =
|
|
|
|
static int __init glamo_mci_init(void)
|
|
{
|
|
+ spin_lock_init(&clock_lock);
|
|
platform_driver_register(&glamo_mci_driver);
|
|
return 0;
|
|
}
|
|
--
|
|
1.5.6.3
|
|
|