| 1 | From b95144c1b702f98c7902c75beb83f323701eb7c5 Mon Sep 17 00:00:00 2001 |
| 2 | From: Maarten ter Huurne <maarten@treewalker.org> |
| 3 | Date: Sun, 19 Jun 2011 10:57:18 +0200 |
| 4 | Subject: [PATCH 13/21] MMC: JZ4740: Added support for CPU frequency changing. |
| 5 | |
| 6 | The MSC device clock is stopped before the frequency change. |
| 7 | After the change a new divider is computed and the clock is restarted. |
| 8 | Also the frequency change is postponed if an I/O operation is in progress. |
| 9 | --- |
| 10 | drivers/mmc/host/jz4740_mmc.c | 69 +++++++++++++++++++++++++++++++++++++++- |
| 11 | 1 files changed, 67 insertions(+), 2 deletions(-) |
| 12 | |
| 13 | --- a/drivers/mmc/host/jz4740_mmc.c |
| 14 | +++ b/drivers/mmc/host/jz4740_mmc.c |
| 15 | @@ -23,6 +23,7 @@ |
| 16 | #include <linux/delay.h> |
| 17 | #include <linux/scatterlist.h> |
| 18 | #include <linux/clk.h> |
| 19 | +#include <linux/cpufreq.h> |
| 20 | |
| 21 | #include <linux/bitops.h> |
| 22 | #include <linux/gpio.h> |
| 23 | @@ -685,6 +686,60 @@ static void jz4740_mmc_enable_sdio_irq(s |
| 24 | jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); |
| 25 | } |
| 26 | |
| 27 | +#ifdef CONFIG_CPU_FREQ |
| 28 | + |
| 29 | +static struct jz4740_mmc_host *cpufreq_host; |
| 30 | + |
| 31 | +static int jz4740_mmc_cpufreq_transition(struct notifier_block *nb, |
| 32 | + unsigned long val, void *data) |
| 33 | +{ |
| 34 | + /* TODO: We only have to take action when the PLL freq changes: |
| 35 | + the main dividers have no influence on the MSC device clock. */ |
| 36 | + |
| 37 | + if (val == CPUFREQ_PRECHANGE) { |
| 38 | + mmc_claim_host(cpufreq_host->mmc); |
| 39 | + clk_disable(cpufreq_host->clk); |
| 40 | + } else if (val == CPUFREQ_POSTCHANGE) { |
| 41 | + struct mmc_ios *ios = &cpufreq_host->mmc->ios; |
| 42 | + if (ios->clock) |
| 43 | + jz4740_mmc_set_clock_rate(cpufreq_host, ios->clock); |
| 44 | + if (ios->power_mode != MMC_POWER_OFF) |
| 45 | + clk_enable(cpufreq_host->clk); |
| 46 | + mmc_release_host(cpufreq_host->mmc); |
| 47 | + } |
| 48 | + return 0; |
| 49 | +} |
| 50 | + |
| 51 | +static struct notifier_block jz4740_mmc_cpufreq_nb = { |
| 52 | + .notifier_call = jz4740_mmc_cpufreq_transition, |
| 53 | +}; |
| 54 | + |
| 55 | +static inline int jz4740_mmc_cpufreq_register(struct jz4740_mmc_host *host) |
| 56 | +{ |
| 57 | + cpufreq_host = host; |
| 58 | + return cpufreq_register_notifier(&jz4740_mmc_cpufreq_nb, |
| 59 | + CPUFREQ_TRANSITION_NOTIFIER); |
| 60 | +} |
| 61 | + |
| 62 | +static inline void jz4740_mmc_cpufreq_unregister(void) |
| 63 | +{ |
| 64 | + cpufreq_unregister_notifier(&jz4740_mmc_cpufreq_nb, |
| 65 | + CPUFREQ_TRANSITION_NOTIFIER); |
| 66 | +} |
| 67 | + |
| 68 | +#else |
| 69 | + |
| 70 | +static inline int jz4740_mmc_cpufreq_register(struct jz4740_mmc_host *host) |
| 71 | +{ |
| 72 | + return 0; |
| 73 | +} |
| 74 | + |
| 75 | +static inline void jz4740_mmc_cpufreq_unregister(void) |
| 76 | +{ |
| 77 | +} |
| 78 | + |
| 79 | +#endif |
| 80 | + |
| 81 | static const struct mmc_host_ops jz4740_mmc_ops = { |
| 82 | .request = jz4740_mmc_request, |
| 83 | .set_ios = jz4740_mmc_set_ios, |
| 84 | @@ -834,11 +889,18 @@ static int __devinit jz4740_mmc_probe(st |
| 85 | goto err_free_host; |
| 86 | } |
| 87 | |
| 88 | + ret = jz4740_mmc_cpufreq_register(host); |
| 89 | + if (ret) { |
| 90 | + dev_err(&pdev->dev, |
| 91 | + "Failed to register cpufreq transition notifier\n"); |
| 92 | + goto err_clk_put; |
| 93 | + } |
| 94 | + |
| 95 | host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 96 | if (!host->mem) { |
| 97 | ret = -ENOENT; |
| 98 | dev_err(&pdev->dev, "Failed to get base platform memory\n"); |
| 99 | - goto err_clk_put; |
| 100 | + goto err_cpufreq_unreg; |
| 101 | } |
| 102 | |
| 103 | host->mem = request_mem_region(host->mem->start, |
| 104 | @@ -846,7 +908,7 @@ static int __devinit jz4740_mmc_probe(st |
| 105 | if (!host->mem) { |
| 106 | ret = -EBUSY; |
| 107 | dev_err(&pdev->dev, "Failed to request base memory region\n"); |
| 108 | - goto err_clk_put; |
| 109 | + goto err_cpufreq_unreg; |
| 110 | } |
| 111 | |
| 112 | host->base = ioremap_nocache(host->mem->start, resource_size(host->mem)); |
| 113 | @@ -929,6 +991,8 @@ err_iounmap: |
| 114 | iounmap(host->base); |
| 115 | err_release_mem_region: |
| 116 | release_mem_region(host->mem->start, resource_size(host->mem)); |
| 117 | +err_cpufreq_unreg: |
| 118 | + jz4740_mmc_cpufreq_unregister(); |
| 119 | err_clk_put: |
| 120 | clk_put(host->clk); |
| 121 | err_free_host: |
| 122 | @@ -958,6 +1022,7 @@ static int __devexit jz4740_mmc_remove(s |
| 123 | iounmap(host->base); |
| 124 | release_mem_region(host->mem->start, resource_size(host->mem)); |
| 125 | |
| 126 | + jz4740_mmc_cpufreq_unregister(); |
| 127 | clk_put(host->clk); |
| 128 | |
| 129 | platform_set_drvdata(pdev, NULL); |
| 130 | |