diff options
Diffstat (limited to 'drivers/clocksource')
-rw-r--r-- | drivers/clocksource/Makefile | 1 | ||||
-rw-r--r-- | drivers/clocksource/bcm_kona_timer.c | 211 |
2 files changed, 212 insertions, 0 deletions
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 98f220a7a92c..4897f243a000 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile | |||
@@ -20,6 +20,7 @@ obj-$(CONFIG_ARCH_MXS) += mxs_timer.o | |||
20 | obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o | 20 | obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o |
21 | obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o | 21 | obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o |
22 | obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o | 22 | obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o |
23 | obj-$(CONFIG_ARCH_BCM) += bcm_kona_timer.o | ||
23 | 24 | ||
24 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o | 25 | obj-$(CONFIG_ARM_ARCH_TIMER) += arm_arch_timer.o |
25 | obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o | 26 | obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o |
diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c new file mode 100644 index 000000000000..350f49356458 --- /dev/null +++ b/drivers/clocksource/bcm_kona_timer.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Broadcom Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License as | ||
6 | * published by the Free Software Foundation version 2. | ||
7 | * | ||
8 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
9 | * kind, whether express or implied; without even the implied warranty | ||
10 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/irq.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/jiffies.h> | ||
18 | #include <linux/clockchips.h> | ||
19 | #include <linux/types.h> | ||
20 | |||
21 | #include <linux/io.h> | ||
22 | #include <asm/mach/time.h> | ||
23 | |||
24 | #include <linux/of.h> | ||
25 | #include <linux/of_address.h> | ||
26 | #include <linux/of_irq.h> | ||
27 | |||
28 | |||
29 | #define KONA_GPTIMER_STCS_OFFSET 0x00000000 | ||
30 | #define KONA_GPTIMER_STCLO_OFFSET 0x00000004 | ||
31 | #define KONA_GPTIMER_STCHI_OFFSET 0x00000008 | ||
32 | #define KONA_GPTIMER_STCM0_OFFSET 0x0000000C | ||
33 | |||
34 | #define KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT 0 | ||
35 | #define KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT 4 | ||
36 | |||
37 | struct kona_bcm_timers { | ||
38 | int tmr_irq; | ||
39 | void __iomem *tmr_regs; | ||
40 | }; | ||
41 | |||
42 | static struct kona_bcm_timers timers; | ||
43 | |||
44 | static u32 arch_timer_rate; | ||
45 | |||
46 | /* | ||
47 | * We use the peripheral timers for system tick, the cpu global timer for | ||
48 | * profile tick | ||
49 | */ | ||
50 | static void kona_timer_disable_and_clear(void __iomem *base) | ||
51 | { | ||
52 | uint32_t reg; | ||
53 | |||
54 | /* | ||
55 | * clear and disable interrupts | ||
56 | * We are using compare/match register 0 for our system interrupts | ||
57 | */ | ||
58 | reg = readl(base + KONA_GPTIMER_STCS_OFFSET); | ||
59 | |||
60 | /* Clear compare (0) interrupt */ | ||
61 | reg |= 1 << KONA_GPTIMER_STCS_TIMER_MATCH_SHIFT; | ||
62 | /* disable compare */ | ||
63 | reg &= ~(1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT); | ||
64 | |||
65 | writel(reg, base + KONA_GPTIMER_STCS_OFFSET); | ||
66 | |||
67 | } | ||
68 | |||
69 | static void | ||
70 | kona_timer_get_counter(void *timer_base, uint32_t *msw, uint32_t *lsw) | ||
71 | { | ||
72 | void __iomem *base = IOMEM(timer_base); | ||
73 | int loop_limit = 4; | ||
74 | |||
75 | /* | ||
76 | * Read 64-bit free running counter | ||
77 | * 1. Read hi-word | ||
78 | * 2. Read low-word | ||
79 | * 3. Read hi-word again | ||
80 | * 4.1 | ||
81 | * if new hi-word is not equal to previously read hi-word, then | ||
82 | * start from #1 | ||
83 | * 4.2 | ||
84 | * if new hi-word is equal to previously read hi-word then stop. | ||
85 | */ | ||
86 | |||
87 | while (--loop_limit) { | ||
88 | *msw = readl(base + KONA_GPTIMER_STCHI_OFFSET); | ||
89 | *lsw = readl(base + KONA_GPTIMER_STCLO_OFFSET); | ||
90 | if (*msw == readl(base + KONA_GPTIMER_STCHI_OFFSET)) | ||
91 | break; | ||
92 | } | ||
93 | if (!loop_limit) { | ||
94 | pr_err("bcm_kona_timer: getting counter failed.\n"); | ||
95 | pr_err(" Timer will be impacted\n"); | ||
96 | } | ||
97 | |||
98 | return; | ||
99 | } | ||
100 | |||
101 | static const struct of_device_id bcm_timer_ids[] __initconst = { | ||
102 | {.compatible = "bcm,kona-timer"}, | ||
103 | {}, | ||
104 | }; | ||
105 | |||
106 | static void __init kona_timers_init(void) | ||
107 | { | ||
108 | struct device_node *node; | ||
109 | u32 freq; | ||
110 | |||
111 | node = of_find_matching_node(NULL, bcm_timer_ids); | ||
112 | |||
113 | if (!node) | ||
114 | panic("No timer"); | ||
115 | |||
116 | if (!of_property_read_u32(node, "clock-frequency", &freq)) | ||
117 | arch_timer_rate = freq; | ||
118 | else | ||
119 | panic("clock-frequency not set in the .dts file"); | ||
120 | |||
121 | /* Setup IRQ numbers */ | ||
122 | timers.tmr_irq = irq_of_parse_and_map(node, 0); | ||
123 | |||
124 | /* Setup IO addresses */ | ||
125 | timers.tmr_regs = of_iomap(node, 0); | ||
126 | |||
127 | kona_timer_disable_and_clear(timers.tmr_regs); | ||
128 | } | ||
129 | |||
130 | static int kona_timer_set_next_event(unsigned long clc, | ||
131 | struct clock_event_device *unused) | ||
132 | { | ||
133 | /* | ||
134 | * timer (0) is disabled by the timer interrupt already | ||
135 | * so, here we reload the next event value and re-enable | ||
136 | * the timer. | ||
137 | * | ||
138 | * This way, we are potentially losing the time between | ||
139 | * timer-interrupt->set_next_event. CPU local timers, when | ||
140 | * they come in should get rid of skew. | ||
141 | */ | ||
142 | |||
143 | uint32_t lsw, msw; | ||
144 | uint32_t reg; | ||
145 | |||
146 | kona_timer_get_counter(timers.tmr_regs, &msw, &lsw); | ||
147 | |||
148 | /* Load the "next" event tick value */ | ||
149 | writel(lsw + clc, timers.tmr_regs + KONA_GPTIMER_STCM0_OFFSET); | ||
150 | |||
151 | /* Enable compare */ | ||
152 | reg = readl(timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET); | ||
153 | reg |= (1 << KONA_GPTIMER_STCS_COMPARE_ENABLE_SHIFT); | ||
154 | writel(reg, timers.tmr_regs + KONA_GPTIMER_STCS_OFFSET); | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static void kona_timer_set_mode(enum clock_event_mode mode, | ||
160 | struct clock_event_device *unused) | ||
161 | { | ||
162 | switch (mode) { | ||
163 | case CLOCK_EVT_MODE_ONESHOT: | ||
164 | /* by default mode is one shot don't do any thing */ | ||
165 | break; | ||
166 | case CLOCK_EVT_MODE_UNUSED: | ||
167 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
168 | default: | ||
169 | kona_timer_disable_and_clear(timers.tmr_regs); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | static struct clock_event_device kona_clockevent_timer = { | ||
174 | .name = "timer 1", | ||
175 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
176 | .set_next_event = kona_timer_set_next_event, | ||
177 | .set_mode = kona_timer_set_mode | ||
178 | }; | ||
179 | |||
180 | static void __init kona_timer_clockevents_init(void) | ||
181 | { | ||
182 | kona_clockevent_timer.cpumask = cpumask_of(0); | ||
183 | clockevents_config_and_register(&kona_clockevent_timer, | ||
184 | arch_timer_rate, 6, 0xffffffff); | ||
185 | } | ||
186 | |||
187 | static irqreturn_t kona_timer_interrupt(int irq, void *dev_id) | ||
188 | { | ||
189 | struct clock_event_device *evt = &kona_clockevent_timer; | ||
190 | |||
191 | kona_timer_disable_and_clear(timers.tmr_regs); | ||
192 | evt->event_handler(evt); | ||
193 | return IRQ_HANDLED; | ||
194 | } | ||
195 | |||
196 | static struct irqaction kona_timer_irq = { | ||
197 | .name = "Kona Timer Tick", | ||
198 | .flags = IRQF_TIMER, | ||
199 | .handler = kona_timer_interrupt, | ||
200 | }; | ||
201 | |||
202 | static void __init kona_timer_init(void) | ||
203 | { | ||
204 | kona_timers_init(); | ||
205 | kona_timer_clockevents_init(); | ||
206 | setup_irq(timers.tmr_irq, &kona_timer_irq); | ||
207 | kona_timer_set_next_event((arch_timer_rate / HZ), NULL); | ||
208 | } | ||
209 | |||
210 | CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer", | ||
211 | kona_timer_init); | ||