From b95144c1b702f98c7902c75beb83f323701eb7c5 Mon Sep 17 00:00:00 2001 From: Maarten ter Huurne Date: Sun, 19 Jun 2011 10:57:18 +0200 Subject: [PATCH 13/21] MMC: JZ4740: Added support for CPU frequency changing. The MSC device clock is stopped before the frequency change. After the change a new divider is computed and the clock is restarted. Also the frequency change is postponed if an I/O operation is in progress. --- drivers/mmc/host/jz4740_mmc.c | 69 +++++++++++++++++++++++++++++++++++++++- 1 files changed, 67 insertions(+), 2 deletions(-) --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -685,6 +686,60 @@ static void jz4740_mmc_enable_sdio_irq(s jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); } +#ifdef CONFIG_CPU_FREQ + +static struct jz4740_mmc_host *cpufreq_host; + +static int jz4740_mmc_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + /* TODO: We only have to take action when the PLL freq changes: + the main dividers have no influence on the MSC device clock. */ + + if (val == CPUFREQ_PRECHANGE) { + mmc_claim_host(cpufreq_host->mmc); + clk_disable(cpufreq_host->clk); + } else if (val == CPUFREQ_POSTCHANGE) { + struct mmc_ios *ios = &cpufreq_host->mmc->ios; + if (ios->clock) + jz4740_mmc_set_clock_rate(cpufreq_host, ios->clock); + if (ios->power_mode != MMC_POWER_OFF) + clk_enable(cpufreq_host->clk); + mmc_release_host(cpufreq_host->mmc); + } + return 0; +} + +static struct notifier_block jz4740_mmc_cpufreq_nb = { + .notifier_call = jz4740_mmc_cpufreq_transition, +}; + +static inline int jz4740_mmc_cpufreq_register(struct jz4740_mmc_host *host) +{ + cpufreq_host = host; + return cpufreq_register_notifier(&jz4740_mmc_cpufreq_nb, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void jz4740_mmc_cpufreq_unregister(void) +{ + cpufreq_unregister_notifier(&jz4740_mmc_cpufreq_nb, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else + +static inline int jz4740_mmc_cpufreq_register(struct jz4740_mmc_host *host) +{ + return 0; +} + +static inline void jz4740_mmc_cpufreq_unregister(void) +{ +} + +#endif + static const struct mmc_host_ops jz4740_mmc_ops = { .request = jz4740_mmc_request, .set_ios = jz4740_mmc_set_ios, @@ -834,11 +889,18 @@ static int __devinit jz4740_mmc_probe(st goto err_free_host; } + ret = jz4740_mmc_cpufreq_register(host); + if (ret) { + dev_err(&pdev->dev, + "Failed to register cpufreq transition notifier\n"); + goto err_clk_put; + } + host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!host->mem) { ret = -ENOENT; dev_err(&pdev->dev, "Failed to get base platform memory\n"); - goto err_clk_put; + goto err_cpufreq_unreg; } host->mem = request_mem_region(host->mem->start, @@ -846,7 +908,7 @@ static int __devinit jz4740_mmc_probe(st if (!host->mem) { ret = -EBUSY; dev_err(&pdev->dev, "Failed to request base memory region\n"); - goto err_clk_put; + goto err_cpufreq_unreg; } host->base = ioremap_nocache(host->mem->start, resource_size(host->mem)); @@ -929,6 +991,8 @@ err_iounmap: iounmap(host->base); err_release_mem_region: release_mem_region(host->mem->start, resource_size(host->mem)); +err_cpufreq_unreg: + jz4740_mmc_cpufreq_unregister(); err_clk_put: clk_put(host->clk); err_free_host: @@ -958,6 +1022,7 @@ static int __devexit jz4740_mmc_remove(s iounmap(host->base); release_mem_region(host->mem->start, resource_size(host->mem)); + jz4740_mmc_cpufreq_unregister(); clk_put(host->clk); platform_set_drvdata(pdev, NULL);