diff options
-rw-r--r-- | 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 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; } |