1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
From b95144c1b702f98c7902c75beb83f323701eb7c5 Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
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 <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <linux/bitops.h>
#include <linux/gpio.h>
@@ -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 jz4740_mmc_probe(struct platf
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 jz4740_mmc_probe(struct platf
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 jz4740_mmc_remove(struct plat
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);
|