aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource
diff options
context:
space:
mode:
authorKumar Gala <galak@codeaurora.org>2014-01-29 17:17:30 -0500
committerKumar Gala <galak@codeaurora.org>2014-02-04 18:48:54 -0500
commit3f8e8cee2f4bd02367583cc2d143887d1f49fd6c (patch)
tree4f1f256517ce35f790384256092cc3ca339a0945 /drivers/clocksource
parent6a032dba7d2329084dca41cc8d82c0cda13103ef (diff)
clocksource: qcom: Move clocksource code out of mach-msm
We intend to share the clocksource code for MSM platforms between legacy and multiplatform supported qcom SoCs. Acked-by: Olof Johansson <olof@lixom.net> Signed-off-by: Kumar Gala <galak@codeaurora.org>
Diffstat (limited to 'drivers/clocksource')
-rw-r--r--drivers/clocksource/Kconfig3
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/qcom-timer.c329
3 files changed, 333 insertions, 0 deletions
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index cd6950fd8caf..6510ec4f45ff 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -140,3 +140,6 @@ config VF_PIT_TIMER
140 bool 140 bool
141 help 141 help
142 Support for Period Interrupt Timer on Freescale Vybrid Family SoCs. 142 Support for Period Interrupt Timer on Freescale Vybrid Family SoCs.
143
144config CLKSRC_QCOM
145 bool
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index c7ca50a9c232..2e0c0cc0a014 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_CLKSRC_EFM32) += time-efm32.o
32obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o 32obj-$(CONFIG_CLKSRC_EXYNOS_MCT) += exynos_mct.o
33obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o 33obj-$(CONFIG_CLKSRC_SAMSUNG_PWM) += samsung_pwm_timer.o
34obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o 34obj-$(CONFIG_VF_PIT_TIMER) += vf_pit_timer.o
35obj-$(CONFIG_CLKSRC_QCOM) += qcom-timer.o
35 36
36obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o 37obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o
37obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o 38obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
new file mode 100644
index 000000000000..dca829ec859b
--- /dev/null
+++ b/drivers/clocksource/qcom-timer.c
@@ -0,0 +1,329 @@
1/*
2 *
3 * Copyright (C) 2007 Google, Inc.
4 * Copyright (c) 2009-2012,2014, The Linux Foundation. All rights reserved.
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/clocksource.h>
18#include <linux/clockchips.h>
19#include <linux/cpu.h>
20#include <linux/init.h>
21#include <linux/interrupt.h>
22#include <linux/irq.h>
23#include <linux/io.h>
24#include <linux/of.h>
25#include <linux/of_address.h>
26#include <linux/of_irq.h>
27#include <linux/sched_clock.h>
28
29#define TIMER_MATCH_VAL 0x0000
30#define TIMER_COUNT_VAL 0x0004
31#define TIMER_ENABLE 0x0008
32#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1)
33#define TIMER_ENABLE_EN BIT(0)
34#define TIMER_CLEAR 0x000C
35#define DGT_CLK_CTL 0x10
36#define DGT_CLK_CTL_DIV_4 0x3
37#define TIMER_STS_GPT0_CLR_PEND BIT(10)
38
39#define GPT_HZ 32768
40
41#define MSM_DGT_SHIFT 5
42
43static void __iomem *event_base;
44static void __iomem *sts_base;
45
46static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
47{
48 struct clock_event_device *evt = dev_id;
49 /* Stop the timer tick */
50 if (evt->mode == CLOCK_EVT_MODE_ONESHOT) {
51 u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
52 ctrl &= ~TIMER_ENABLE_EN;
53 writel_relaxed(ctrl, event_base + TIMER_ENABLE);
54 }
55 evt->event_handler(evt);
56 return IRQ_HANDLED;
57}
58
59static int msm_timer_set_next_event(unsigned long cycles,
60 struct clock_event_device *evt)
61{
62 u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
63
64 ctrl &= ~TIMER_ENABLE_EN;
65 writel_relaxed(ctrl, event_base + TIMER_ENABLE);
66
67 writel_relaxed(ctrl, event_base + TIMER_CLEAR);
68 writel_relaxed(cycles, event_base + TIMER_MATCH_VAL);
69
70 if (sts_base)
71 while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND)
72 cpu_relax();
73
74 writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE);
75 return 0;
76}
77
78static void msm_timer_set_mode(enum clock_event_mode mode,
79 struct clock_event_device *evt)
80{
81 u32 ctrl;
82
83 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
84 ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN);
85
86 switch (mode) {
87 case CLOCK_EVT_MODE_RESUME:
88 case CLOCK_EVT_MODE_PERIODIC:
89 break;
90 case CLOCK_EVT_MODE_ONESHOT:
91 /* Timer is enabled in set_next_event */
92 break;
93 case CLOCK_EVT_MODE_UNUSED:
94 case CLOCK_EVT_MODE_SHUTDOWN:
95 break;
96 }
97 writel_relaxed(ctrl, event_base + TIMER_ENABLE);
98}
99
100static struct clock_event_device __percpu *msm_evt;
101
102static void __iomem *source_base;
103
104static notrace cycle_t msm_read_timer_count(struct clocksource *cs)
105{
106 return readl_relaxed(source_base + TIMER_COUNT_VAL);
107}
108
109static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
110{
111 /*
112 * Shift timer count down by a constant due to unreliable lower bits
113 * on some targets.
114 */
115 return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
116}
117
118static struct clocksource msm_clocksource = {
119 .name = "dg_timer",
120 .rating = 300,
121 .read = msm_read_timer_count,
122 .mask = CLOCKSOURCE_MASK(32),
123 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
124};
125
126static int msm_timer_irq;
127static int msm_timer_has_ppi;
128
129static int msm_local_timer_setup(struct clock_event_device *evt)
130{
131 int cpu = smp_processor_id();
132 int err;
133
134 evt->irq = msm_timer_irq;
135 evt->name = "msm_timer";
136 evt->features = CLOCK_EVT_FEAT_ONESHOT;
137 evt->rating = 200;
138 evt->set_mode = msm_timer_set_mode;
139 evt->set_next_event = msm_timer_set_next_event;
140 evt->cpumask = cpumask_of(cpu);
141
142 clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff);
143
144 if (msm_timer_has_ppi) {
145 enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING);
146 } else {
147 err = request_irq(evt->irq, msm_timer_interrupt,
148 IRQF_TIMER | IRQF_NOBALANCING |
149 IRQF_TRIGGER_RISING, "gp_timer", evt);
150 if (err)
151 pr_err("request_irq failed\n");
152 }
153
154 return 0;
155}
156
157static void msm_local_timer_stop(struct clock_event_device *evt)
158{
159 evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
160 disable_percpu_irq(evt->irq);
161}
162
163static int msm_timer_cpu_notify(struct notifier_block *self,
164 unsigned long action, void *hcpu)
165{
166 /*
167 * Grab cpu pointer in each case to avoid spurious
168 * preemptible warnings
169 */
170 switch (action & ~CPU_TASKS_FROZEN) {
171 case CPU_STARTING:
172 msm_local_timer_setup(this_cpu_ptr(msm_evt));
173 break;
174 case CPU_DYING:
175 msm_local_timer_stop(this_cpu_ptr(msm_evt));
176 break;
177 }
178
179 return NOTIFY_OK;
180}
181
182static struct notifier_block msm_timer_cpu_nb = {
183 .notifier_call = msm_timer_cpu_notify,
184};
185
186static u64 notrace msm_sched_clock_read(void)
187{
188 return msm_clocksource.read(&msm_clocksource);
189}
190
191static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
192 bool percpu)
193{
194 struct clocksource *cs = &msm_clocksource;
195 int res = 0;
196
197 msm_timer_irq = irq;
198 msm_timer_has_ppi = percpu;
199
200 msm_evt = alloc_percpu(struct clock_event_device);
201 if (!msm_evt) {
202 pr_err("memory allocation failed for clockevents\n");
203 goto err;
204 }
205
206 if (percpu)
207 res = request_percpu_irq(irq, msm_timer_interrupt,
208 "gp_timer", msm_evt);
209
210 if (res) {
211 pr_err("request_percpu_irq failed\n");
212 } else {
213 res = register_cpu_notifier(&msm_timer_cpu_nb);
214 if (res) {
215 free_percpu_irq(irq, msm_evt);
216 goto err;
217 }
218
219 /* Immediately configure the timer on the boot CPU */
220 msm_local_timer_setup(__this_cpu_ptr(msm_evt));
221 }
222
223err:
224 writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
225 res = clocksource_register_hz(cs, dgt_hz);
226 if (res)
227 pr_err("clocksource_register failed\n");
228 sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
229}
230
231#ifdef CONFIG_OF
232static void __init msm_dt_timer_init(struct device_node *np)
233{
234 u32 freq;
235 int irq;
236 struct resource res;
237 u32 percpu_offset;
238 void __iomem *base;
239 void __iomem *cpu0_base;
240
241 base = of_iomap(np, 0);
242 if (!base) {
243 pr_err("Failed to map event base\n");
244 return;
245 }
246
247 /* We use GPT0 for the clockevent */
248 irq = irq_of_parse_and_map(np, 1);
249 if (irq <= 0) {
250 pr_err("Can't get irq\n");
251 return;
252 }
253
254 /* We use CPU0's DGT for the clocksource */
255 if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
256 percpu_offset = 0;
257
258 if (of_address_to_resource(np, 0, &res)) {
259 pr_err("Failed to parse DGT resource\n");
260 return;
261 }
262
263 cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res));
264 if (!cpu0_base) {
265 pr_err("Failed to map source base\n");
266 return;
267 }
268
269 if (of_property_read_u32(np, "clock-frequency", &freq)) {
270 pr_err("Unknown frequency\n");
271 return;
272 }
273
274 event_base = base + 0x4;
275 sts_base = base + 0x88;
276 source_base = cpu0_base + 0x24;
277 freq /= 4;
278 writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL);
279
280 msm_timer_init(freq, 32, irq, !!percpu_offset);
281}
282CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
283CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
284#endif
285
286static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
287 u32 sts)
288{
289 void __iomem *base;
290
291 base = ioremap(addr, SZ_256);
292 if (!base) {
293 pr_err("Failed to map timer base\n");
294 return -ENOMEM;
295 }
296 event_base = base + event;
297 source_base = base + source;
298 if (sts)
299 sts_base = base + sts;
300
301 return 0;
302}
303
304void __init msm7x01_timer_init(void)
305{
306 struct clocksource *cs = &msm_clocksource;
307
308 if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0))
309 return;
310 cs->read = msm_read_timer_count_shift;
311 cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
312 /* 600 KHz */
313 msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7,
314 false);
315}
316
317void __init msm7x30_timer_init(void)
318{
319 if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80))
320 return;
321 msm_timer_init(24576000 / 4, 32, 1, false);
322}
323
324void __init qsd8x50_timer_init(void)
325{
326 if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34))
327 return;
328 msm_timer_init(19200000 / 4, 32, 7, false);
329}