aboutsummaryrefslogtreecommitdiff
path: root/drivers/mfd/glamo/glamo-mci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/glamo/glamo-mci.c')
-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;
}