diff options
author | Maxime Ripard <maxime.ripard@free-electrons.com> | 2014-09-02 12:12:35 -0400 |
---|---|---|
committer | Nicolas Ferre <nicolas.ferre@atmel.com> | 2014-09-15 11:55:48 -0400 |
commit | b052ff30cd450c91a32e8e928979bca021462996 (patch) | |
tree | 201a1cf76c134b1262db953dc51f29ffe72f5142 /drivers/clocksource/timer-atmel-pit.c | |
parent | 7d80335e29701e9c2cc55ec0ea79afe6351aa20b (diff) |
ARM: at91: PIT: Move the driver to drivers/clocksource
Now that we don't depend on anyting in the mach-at91 directory, we can just
move the driver to where it belongs.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Acked-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Conflicts:
arch/arm/mach-at91/Kconfig
arch/arm/mach-at91/Makefile
Diffstat (limited to 'drivers/clocksource/timer-atmel-pit.c')
-rw-r--r-- | drivers/clocksource/timer-atmel-pit.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c new file mode 100644 index 000000000000..d5289098b3df --- /dev/null +++ b/drivers/clocksource/timer-atmel-pit.c | |||
@@ -0,0 +1,296 @@ | |||
1 | /* | ||
2 | * at91sam926x_time.c - Periodic Interval Timer (PIT) for at91sam926x | ||
3 | * | ||
4 | * Copyright (C) 2005-2006 M. Amine SAYA, ATMEL Rousset, France | ||
5 | * Revision 2005 M. Nicolas Diremdjian, ATMEL Rousset, France | ||
6 | * Converted to ClockSource/ClockEvents by David Brownell. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #define pr_fmt(fmt) "AT91: PIT: " fmt | ||
14 | |||
15 | #include <linux/clk.h> | ||
16 | #include <linux/clockchips.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/irq.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/of.h> | ||
21 | #include <linux/of_address.h> | ||
22 | #include <linux/of_irq.h> | ||
23 | #include <linux/slab.h> | ||
24 | |||
25 | #define AT91_PIT_MR 0x00 /* Mode Register */ | ||
26 | #define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */ | ||
27 | #define AT91_PIT_PITEN BIT(24) /* Timer Enabled */ | ||
28 | #define AT91_PIT_PIV GENMASK(19, 0) /* Periodic Interval Value */ | ||
29 | |||
30 | #define AT91_PIT_SR 0x04 /* Status Register */ | ||
31 | #define AT91_PIT_PITS BIT(0) /* Timer Status */ | ||
32 | |||
33 | #define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */ | ||
34 | #define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */ | ||
35 | #define AT91_PIT_PICNT GENMASK(31, 20) /* Interval Counter */ | ||
36 | #define AT91_PIT_CPIV GENMASK(19, 0) /* Inverval Value */ | ||
37 | |||
38 | #define PIT_CPIV(x) ((x) & AT91_PIT_CPIV) | ||
39 | #define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20) | ||
40 | |||
41 | struct pit_data { | ||
42 | struct clock_event_device clkevt; | ||
43 | struct clocksource clksrc; | ||
44 | |||
45 | void __iomem *base; | ||
46 | u32 cycle; | ||
47 | u32 cnt; | ||
48 | unsigned int irq; | ||
49 | struct clk *mck; | ||
50 | }; | ||
51 | |||
52 | static inline struct pit_data *clksrc_to_pit_data(struct clocksource *clksrc) | ||
53 | { | ||
54 | return container_of(clksrc, struct pit_data, clksrc); | ||
55 | } | ||
56 | |||
57 | static inline struct pit_data *clkevt_to_pit_data(struct clock_event_device *clkevt) | ||
58 | { | ||
59 | return container_of(clkevt, struct pit_data, clkevt); | ||
60 | } | ||
61 | |||
62 | static inline unsigned int pit_read(void __iomem *base, unsigned int reg_offset) | ||
63 | { | ||
64 | return __raw_readl(base + reg_offset); | ||
65 | } | ||
66 | |||
67 | static inline void pit_write(void __iomem *base, unsigned int reg_offset, unsigned long value) | ||
68 | { | ||
69 | __raw_writel(value, base + reg_offset); | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * Clocksource: just a monotonic counter of MCK/16 cycles. | ||
74 | * We don't care whether or not PIT irqs are enabled. | ||
75 | */ | ||
76 | static cycle_t read_pit_clk(struct clocksource *cs) | ||
77 | { | ||
78 | struct pit_data *data = clksrc_to_pit_data(cs); | ||
79 | unsigned long flags; | ||
80 | u32 elapsed; | ||
81 | u32 t; | ||
82 | |||
83 | raw_local_irq_save(flags); | ||
84 | elapsed = data->cnt; | ||
85 | t = pit_read(data->base, AT91_PIT_PIIR); | ||
86 | raw_local_irq_restore(flags); | ||
87 | |||
88 | elapsed += PIT_PICNT(t) * data->cycle; | ||
89 | elapsed += PIT_CPIV(t); | ||
90 | return elapsed; | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Clockevent device: interrupts every 1/HZ (== pit_cycles * MCK/16) | ||
95 | */ | ||
96 | static void | ||
97 | pit_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *dev) | ||
98 | { | ||
99 | struct pit_data *data = clkevt_to_pit_data(dev); | ||
100 | |||
101 | switch (mode) { | ||
102 | case CLOCK_EVT_MODE_PERIODIC: | ||
103 | /* update clocksource counter */ | ||
104 | data->cnt += data->cycle * PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); | ||
105 | pit_write(data->base, AT91_PIT_MR, | ||
106 | (data->cycle - 1) | AT91_PIT_PITEN | AT91_PIT_PITIEN); | ||
107 | break; | ||
108 | case CLOCK_EVT_MODE_ONESHOT: | ||
109 | BUG(); | ||
110 | /* FALLTHROUGH */ | ||
111 | case CLOCK_EVT_MODE_SHUTDOWN: | ||
112 | case CLOCK_EVT_MODE_UNUSED: | ||
113 | /* disable irq, leaving the clocksource active */ | ||
114 | pit_write(data->base, AT91_PIT_MR, | ||
115 | (data->cycle - 1) | AT91_PIT_PITEN); | ||
116 | break; | ||
117 | case CLOCK_EVT_MODE_RESUME: | ||
118 | break; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | static void at91sam926x_pit_suspend(struct clock_event_device *cedev) | ||
123 | { | ||
124 | struct pit_data *data = clkevt_to_pit_data(cedev); | ||
125 | |||
126 | /* Disable timer */ | ||
127 | pit_write(data->base, AT91_PIT_MR, 0); | ||
128 | } | ||
129 | |||
130 | static void at91sam926x_pit_reset(struct pit_data *data) | ||
131 | { | ||
132 | /* Disable timer and irqs */ | ||
133 | pit_write(data->base, AT91_PIT_MR, 0); | ||
134 | |||
135 | /* Clear any pending interrupts, wait for PIT to stop counting */ | ||
136 | while (PIT_CPIV(pit_read(data->base, AT91_PIT_PIVR)) != 0) | ||
137 | cpu_relax(); | ||
138 | |||
139 | /* Start PIT but don't enable IRQ */ | ||
140 | pit_write(data->base, AT91_PIT_MR, | ||
141 | (data->cycle - 1) | AT91_PIT_PITEN); | ||
142 | } | ||
143 | |||
144 | static void at91sam926x_pit_resume(struct clock_event_device *cedev) | ||
145 | { | ||
146 | struct pit_data *data = clkevt_to_pit_data(cedev); | ||
147 | |||
148 | at91sam926x_pit_reset(data); | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * IRQ handler for the timer. | ||
153 | */ | ||
154 | static irqreturn_t at91sam926x_pit_interrupt(int irq, void *dev_id) | ||
155 | { | ||
156 | struct pit_data *data = dev_id; | ||
157 | |||
158 | /* | ||
159 | * irqs should be disabled here, but as the irq is shared they are only | ||
160 | * guaranteed to be off if the timer irq is registered first. | ||
161 | */ | ||
162 | WARN_ON_ONCE(!irqs_disabled()); | ||
163 | |||
164 | /* The PIT interrupt may be disabled, and is shared */ | ||
165 | if ((data->clkevt.mode == CLOCK_EVT_MODE_PERIODIC) && | ||
166 | (pit_read(data->base, AT91_PIT_SR) & AT91_PIT_PITS)) { | ||
167 | unsigned nr_ticks; | ||
168 | |||
169 | /* Get number of ticks performed before irq, and ack it */ | ||
170 | nr_ticks = PIT_PICNT(pit_read(data->base, AT91_PIT_PIVR)); | ||
171 | do { | ||
172 | data->cnt += data->cycle; | ||
173 | data->clkevt.event_handler(&data->clkevt); | ||
174 | nr_ticks--; | ||
175 | } while (nr_ticks); | ||
176 | |||
177 | return IRQ_HANDLED; | ||
178 | } | ||
179 | |||
180 | return IRQ_NONE; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * Set up both clocksource and clockevent support. | ||
185 | */ | ||
186 | static void __init at91sam926x_pit_common_init(struct pit_data *data) | ||
187 | { | ||
188 | unsigned long pit_rate; | ||
189 | unsigned bits; | ||
190 | int ret; | ||
191 | |||
192 | /* | ||
193 | * Use our actual MCK to figure out how many MCK/16 ticks per | ||
194 | * 1/HZ period (instead of a compile-time constant LATCH). | ||
195 | */ | ||
196 | pit_rate = clk_get_rate(data->mck) / 16; | ||
197 | data->cycle = DIV_ROUND_CLOSEST(pit_rate, HZ); | ||
198 | WARN_ON(((data->cycle - 1) & ~AT91_PIT_PIV) != 0); | ||
199 | |||
200 | /* Initialize and enable the timer */ | ||
201 | at91sam926x_pit_reset(data); | ||
202 | |||
203 | /* | ||
204 | * Register clocksource. The high order bits of PIV are unused, | ||
205 | * so this isn't a 32-bit counter unless we get clockevent irqs. | ||
206 | */ | ||
207 | bits = 12 /* PICNT */ + ilog2(data->cycle) /* PIV */; | ||
208 | data->clksrc.mask = CLOCKSOURCE_MASK(bits); | ||
209 | data->clksrc.name = "pit"; | ||
210 | data->clksrc.rating = 175; | ||
211 | data->clksrc.read = read_pit_clk, | ||
212 | data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
213 | clocksource_register_hz(&data->clksrc, pit_rate); | ||
214 | |||
215 | /* Set up irq handler */ | ||
216 | ret = request_irq(data->irq, at91sam926x_pit_interrupt, | ||
217 | IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, | ||
218 | "at91_tick", data); | ||
219 | if (ret) | ||
220 | panic(pr_fmt("Unable to setup IRQ\n")); | ||
221 | |||
222 | /* Set up and register clockevents */ | ||
223 | data->clkevt.name = "pit"; | ||
224 | data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC; | ||
225 | data->clkevt.shift = 32; | ||
226 | data->clkevt.mult = div_sc(pit_rate, NSEC_PER_SEC, data->clkevt.shift); | ||
227 | data->clkevt.rating = 100; | ||
228 | data->clkevt.cpumask = cpumask_of(0); | ||
229 | |||
230 | data->clkevt.set_mode = pit_clkevt_mode; | ||
231 | data->clkevt.resume = at91sam926x_pit_resume; | ||
232 | data->clkevt.suspend = at91sam926x_pit_suspend; | ||
233 | clockevents_register_device(&data->clkevt); | ||
234 | } | ||
235 | |||
236 | static void __init at91sam926x_pit_dt_init(struct device_node *node) | ||
237 | { | ||
238 | struct pit_data *data; | ||
239 | |||
240 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
241 | if (!data) | ||
242 | panic(pr_fmt("Unable to allocate memory\n")); | ||
243 | |||
244 | data->base = of_iomap(node, 0); | ||
245 | if (!data->base) | ||
246 | panic(pr_fmt("Could not map PIT address\n")); | ||
247 | |||
248 | data->mck = of_clk_get(node, 0); | ||
249 | if (IS_ERR(data->mck)) | ||
250 | /* Fallback on clkdev for !CCF-based boards */ | ||
251 | data->mck = clk_get(NULL, "mck"); | ||
252 | |||
253 | if (IS_ERR(data->mck)) | ||
254 | panic(pr_fmt("Unable to get mck clk\n")); | ||
255 | |||
256 | /* Get the interrupts property */ | ||
257 | data->irq = irq_of_parse_and_map(node, 0); | ||
258 | if (!data->irq) | ||
259 | panic(pr_fmt("Unable to get IRQ from DT\n")); | ||
260 | |||
261 | at91sam926x_pit_common_init(data); | ||
262 | } | ||
263 | CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit", | ||
264 | at91sam926x_pit_dt_init); | ||
265 | |||
266 | static void __iomem *pit_base_addr; | ||
267 | |||
268 | void __init at91sam926x_pit_init(int irq) | ||
269 | { | ||
270 | struct pit_data *data; | ||
271 | |||
272 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
273 | if (!data) | ||
274 | panic(pr_fmt("Unable to allocate memory\n")); | ||
275 | |||
276 | data->base = pit_base_addr; | ||
277 | |||
278 | data->mck = clk_get(NULL, "mck"); | ||
279 | if (IS_ERR(data->mck)) | ||
280 | panic(pr_fmt("Unable to get mck clk\n")); | ||
281 | |||
282 | data->irq = irq; | ||
283 | |||
284 | at91sam926x_pit_common_init(data); | ||
285 | } | ||
286 | |||
287 | void __init at91sam926x_ioremap_pit(u32 addr) | ||
288 | { | ||
289 | if (of_have_populated_dt()) | ||
290 | return; | ||
291 | |||
292 | pit_base_addr = ioremap(addr, 16); | ||
293 | |||
294 | if (!pit_base_addr) | ||
295 | panic(pr_fmt("Impossible to ioremap PIT\n")); | ||
296 | } | ||