diff options
author | Arnd Bergmann <arnd@arndb.de> | 2012-01-09 12:06:36 -0500 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2012-01-09 12:06:36 -0500 |
commit | 421b759b86eb8a914cbbd11f6d09a74f411762c6 (patch) | |
tree | 505ca7f23987d8eaaa519a7e8506b854e2c0d030 /arch/arm/kernel/smp_twd.c | |
parent | e067096c8d57d191f29d734cd5692695c95cc36e (diff) | |
parent | a07613a54d700a974f3a4a657da78ef5d097315d (diff) |
Merge branch 'samsung/cleanup' into next/boards
Conflicts:
arch/arm/mach-imx/mach-imx6q.c
arch/arm/mach-omap2/board-ti8168evm.c
arch/arm/mach-s3c64xx/Kconfig
arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/board-dt-tegra20.c
arch/arm/mach-tegra/common.c
Lots of relatively simple conflicts between the board
changes and stuff from the arm tree. This pulls in
the resolution from the samsung/cleanup tree, so we
don't get conflicting merges.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/arm/kernel/smp_twd.c')
-rw-r--r-- | arch/arm/kernel/smp_twd.c | 95 |
1 files changed, 88 insertions, 7 deletions
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index a8a6682d6b5..c8e938553d4 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c | |||
@@ -10,8 +10,11 @@ | |||
10 | */ | 10 | */ |
11 | #include <linux/init.h> | 11 | #include <linux/init.h> |
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/clk.h> | ||
14 | #include <linux/cpufreq.h> | ||
13 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
14 | #include <linux/device.h> | 16 | #include <linux/device.h> |
17 | #include <linux/err.h> | ||
15 | #include <linux/smp.h> | 18 | #include <linux/smp.h> |
16 | #include <linux/jiffies.h> | 19 | #include <linux/jiffies.h> |
17 | #include <linux/clockchips.h> | 20 | #include <linux/clockchips.h> |
@@ -25,6 +28,7 @@ | |||
25 | /* set up by the platform code */ | 28 | /* set up by the platform code */ |
26 | void __iomem *twd_base; | 29 | void __iomem *twd_base; |
27 | 30 | ||
31 | static struct clk *twd_clk; | ||
28 | static unsigned long twd_timer_rate; | 32 | static unsigned long twd_timer_rate; |
29 | 33 | ||
30 | static struct clock_event_device __percpu **twd_evt; | 34 | static struct clock_event_device __percpu **twd_evt; |
@@ -89,6 +93,52 @@ void twd_timer_stop(struct clock_event_device *clk) | |||
89 | disable_percpu_irq(clk->irq); | 93 | disable_percpu_irq(clk->irq); |
90 | } | 94 | } |
91 | 95 | ||
96 | #ifdef CONFIG_CPU_FREQ | ||
97 | |||
98 | /* | ||
99 | * Updates clockevent frequency when the cpu frequency changes. | ||
100 | * Called on the cpu that is changing frequency with interrupts disabled. | ||
101 | */ | ||
102 | static void twd_update_frequency(void *data) | ||
103 | { | ||
104 | twd_timer_rate = clk_get_rate(twd_clk); | ||
105 | |||
106 | clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate); | ||
107 | } | ||
108 | |||
109 | static int twd_cpufreq_transition(struct notifier_block *nb, | ||
110 | unsigned long state, void *data) | ||
111 | { | ||
112 | struct cpufreq_freqs *freqs = data; | ||
113 | |||
114 | /* | ||
115 | * The twd clock events must be reprogrammed to account for the new | ||
116 | * frequency. The timer is local to a cpu, so cross-call to the | ||
117 | * changing cpu. | ||
118 | */ | ||
119 | if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) | ||
120 | smp_call_function_single(freqs->cpu, twd_update_frequency, | ||
121 | NULL, 1); | ||
122 | |||
123 | return NOTIFY_OK; | ||
124 | } | ||
125 | |||
126 | static struct notifier_block twd_cpufreq_nb = { | ||
127 | .notifier_call = twd_cpufreq_transition, | ||
128 | }; | ||
129 | |||
130 | static int twd_cpufreq_init(void) | ||
131 | { | ||
132 | if (!IS_ERR(twd_clk)) | ||
133 | return cpufreq_register_notifier(&twd_cpufreq_nb, | ||
134 | CPUFREQ_TRANSITION_NOTIFIER); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | core_initcall(twd_cpufreq_init); | ||
139 | |||
140 | #endif | ||
141 | |||
92 | static void __cpuinit twd_calibrate_rate(void) | 142 | static void __cpuinit twd_calibrate_rate(void) |
93 | { | 143 | { |
94 | unsigned long count; | 144 | unsigned long count; |
@@ -140,6 +190,35 @@ static irqreturn_t twd_handler(int irq, void *dev_id) | |||
140 | return IRQ_NONE; | 190 | return IRQ_NONE; |
141 | } | 191 | } |
142 | 192 | ||
193 | static struct clk *twd_get_clock(void) | ||
194 | { | ||
195 | struct clk *clk; | ||
196 | int err; | ||
197 | |||
198 | clk = clk_get_sys("smp_twd", NULL); | ||
199 | if (IS_ERR(clk)) { | ||
200 | pr_err("smp_twd: clock not found: %d\n", (int)PTR_ERR(clk)); | ||
201 | return clk; | ||
202 | } | ||
203 | |||
204 | err = clk_prepare(clk); | ||
205 | if (err) { | ||
206 | pr_err("smp_twd: clock failed to prepare: %d\n", err); | ||
207 | clk_put(clk); | ||
208 | return ERR_PTR(err); | ||
209 | } | ||
210 | |||
211 | err = clk_enable(clk); | ||
212 | if (err) { | ||
213 | pr_err("smp_twd: clock failed to enable: %d\n", err); | ||
214 | clk_unprepare(clk); | ||
215 | clk_put(clk); | ||
216 | return ERR_PTR(err); | ||
217 | } | ||
218 | |||
219 | return clk; | ||
220 | } | ||
221 | |||
143 | /* | 222 | /* |
144 | * Setup the local clock events for a CPU. | 223 | * Setup the local clock events for a CPU. |
145 | */ | 224 | */ |
@@ -165,7 +244,13 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) | |||
165 | } | 244 | } |
166 | } | 245 | } |
167 | 246 | ||
168 | twd_calibrate_rate(); | 247 | if (!twd_clk) |
248 | twd_clk = twd_get_clock(); | ||
249 | |||
250 | if (!IS_ERR_OR_NULL(twd_clk)) | ||
251 | twd_timer_rate = clk_get_rate(twd_clk); | ||
252 | else | ||
253 | twd_calibrate_rate(); | ||
169 | 254 | ||
170 | clk->name = "local_timer"; | 255 | clk->name = "local_timer"; |
171 | clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | | 256 | clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | |
@@ -173,15 +258,11 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) | |||
173 | clk->rating = 350; | 258 | clk->rating = 350; |
174 | clk->set_mode = twd_set_mode; | 259 | clk->set_mode = twd_set_mode; |
175 | clk->set_next_event = twd_set_next_event; | 260 | clk->set_next_event = twd_set_next_event; |
176 | clk->shift = 20; | ||
177 | clk->mult = div_sc(twd_timer_rate, NSEC_PER_SEC, clk->shift); | ||
178 | clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); | ||
179 | clk->min_delta_ns = clockevent_delta2ns(0xf, clk); | ||
180 | 261 | ||
181 | this_cpu_clk = __this_cpu_ptr(twd_evt); | 262 | this_cpu_clk = __this_cpu_ptr(twd_evt); |
182 | *this_cpu_clk = clk; | 263 | *this_cpu_clk = clk; |
183 | 264 | ||
184 | clockevents_register_device(clk); | 265 | clockevents_config_and_register(clk, twd_timer_rate, |
185 | 266 | 0xf, 0xffffffff); | |
186 | enable_percpu_irq(clk->irq, 0); | 267 | enable_percpu_irq(clk->irq, 0); |
187 | } | 268 | } |