aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorAndy Green <andy@openmoko.com>2008-11-19 17:09:56 +0000
committerAndy Green <agreen@pads.home.warmcat.com>2008-11-19 17:09:56 +0000
commit3fb1c905f88b8a65a791dee7a8afe7fbf41e15c0 (patch)
tree9f6df6d235309ecb6c3de0eea292f4ce1c0188c2 /drivers
parent29ac5eccf72371572204adbc4e3a3d0f7bc8dee4 (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/glamo/glamo-mci.c126
1 files changed, 89 insertions, 37 deletions
diff --git a/drivers/mfd/glamo/glamo-mci.c b/drivers/mfd/glamo/glamo-mci.c
index 37e3d3cb76a..b53827edd1a 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;
}