aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/em_sti.c
diff options
context:
space:
mode:
authorMagnus Damm <magnus.damm@gmail.com>2012-05-25 03:03:44 -0400
committerThomas Gleixner <tglx@linutronix.de>2012-05-25 05:32:06 -0400
commitb9dbf9517784084ee9496f9f17f9754c1d021a9e (patch)
tree9b51e9d9becd1d9ed40ffaf34e960bc22970fae8 /drivers/clocksource/em_sti.c
parente5400321a6f15ce0fe77c8455954f213ef7dcc54 (diff)
clocksource: em_sti: Emma Mobile STI driver
The STI hardware is based on a single 48-bit 32kHz counter that together with two individual compare registers can generate interrupts. There are no timer operating modes selectable which means that the timer can not clear on match. This driver is providing clocksource support for the 48-bit counter. Clockevents are also supported using the same timer in oneshot mode. Signed-off-by: Magnus Damm <damm@opensource.se> Cc: horms@verge.net.au Cc: arnd@arndb.de Cc: johnstul@us.ibm.com Cc: rjw@sisk.pl Cc: lethal@linux-sh.org Cc: gregkh@linuxfoundation.org Cc: olof@lixom.net Link: http://lkml.kernel.org/r/20120525070344.23443.69756.sendpatchset@w520 Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers/clocksource/em_sti.c')
-rw-r--r--drivers/clocksource/em_sti.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c
new file mode 100644
index 000000000000..719584a5952b
--- /dev/null
+++ b/drivers/clocksource/em_sti.c
@@ -0,0 +1,399 @@
1/*
2 * Emma Mobile Timer Support - STI
3 *
4 * Copyright (C) 2012 Magnus Damm
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License
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 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include <linux/init.h>
21#include <linux/platform_device.h>
22#include <linux/spinlock.h>
23#include <linux/interrupt.h>
24#include <linux/ioport.h>
25#include <linux/io.h>
26#include <linux/clk.h>
27#include <linux/irq.h>
28#include <linux/err.h>
29#include <linux/delay.h>
30#include <linux/clocksource.h>
31#include <linux/clockchips.h>
32#include <linux/slab.h>
33#include <linux/module.h>
34
35enum { USER_CLOCKSOURCE, USER_CLOCKEVENT, USER_NR };
36
37struct em_sti_priv {
38 void __iomem *base;
39 struct clk *clk;
40 struct platform_device *pdev;
41 unsigned int active[USER_NR];
42 unsigned long rate;
43 raw_spinlock_t lock;
44 struct clock_event_device ced;
45 struct clocksource cs;
46};
47
48#define STI_CONTROL 0x00
49#define STI_COMPA_H 0x10
50#define STI_COMPA_L 0x14
51#define STI_COMPB_H 0x18
52#define STI_COMPB_L 0x1c
53#define STI_COUNT_H 0x20
54#define STI_COUNT_L 0x24
55#define STI_COUNT_RAW_H 0x28
56#define STI_COUNT_RAW_L 0x2c
57#define STI_SET_H 0x30
58#define STI_SET_L 0x34
59#define STI_INTSTATUS 0x40
60#define STI_INTRAWSTATUS 0x44
61#define STI_INTENSET 0x48
62#define STI_INTENCLR 0x4c
63#define STI_INTFFCLR 0x50
64
65static inline unsigned long em_sti_read(struct em_sti_priv *p, int offs)
66{
67 return ioread32(p->base + offs);
68}
69
70static inline void em_sti_write(struct em_sti_priv *p, int offs,
71 unsigned long value)
72{
73 iowrite32(value, p->base + offs);
74}
75
76static int em_sti_enable(struct em_sti_priv *p)
77{
78 int ret;
79
80 /* enable clock */
81 ret = clk_enable(p->clk);
82 if (ret) {
83 dev_err(&p->pdev->dev, "cannot enable clock\n");
84 return ret;
85 }
86
87 /* configure channel, periodic mode and maximum timeout */
88 p->rate = clk_get_rate(p->clk);
89
90 /* reset the counter */
91 em_sti_write(p, STI_SET_H, 0x40000000);
92 em_sti_write(p, STI_SET_L, 0x00000000);
93
94 /* mask and clear pending interrupts */
95 em_sti_write(p, STI_INTENCLR, 3);
96 em_sti_write(p, STI_INTFFCLR, 3);
97
98 /* enable updates of counter registers */
99 em_sti_write(p, STI_CONTROL, 1);
100
101 return 0;
102}
103
104static void em_sti_disable(struct em_sti_priv *p)
105{
106 /* mask interrupts */
107 em_sti_write(p, STI_INTENCLR, 3);
108
109 /* stop clock */
110 clk_disable(p->clk);
111}
112
113static cycle_t em_sti_count(struct em_sti_priv *p)
114{
115 cycle_t ticks;
116 unsigned long flags;
117
118 /* the STI hardware buffers the 48-bit count, but to
119 * break it out into two 32-bit access the registers
120 * must be accessed in a certain order.
121 * Always read STI_COUNT_H before STI_COUNT_L.
122 */
123 raw_spin_lock_irqsave(&p->lock, flags);
124 ticks = (cycle_t)(em_sti_read(p, STI_COUNT_H) & 0xffff) << 32;
125 ticks |= em_sti_read(p, STI_COUNT_L);
126 raw_spin_unlock_irqrestore(&p->lock, flags);
127
128 return ticks;
129}
130
131static cycle_t em_sti_set_next(struct em_sti_priv *p, cycle_t next)
132{
133 unsigned long flags;
134
135 raw_spin_lock_irqsave(&p->lock, flags);
136
137 /* mask compare A interrupt */
138 em_sti_write(p, STI_INTENCLR, 1);
139
140 /* update compare A value */
141 em_sti_write(p, STI_COMPA_H, next >> 32);
142 em_sti_write(p, STI_COMPA_L, next & 0xffffffff);
143
144 /* clear compare A interrupt source */
145 em_sti_write(p, STI_INTFFCLR, 1);
146
147 /* unmask compare A interrupt */
148 em_sti_write(p, STI_INTENSET, 1);
149
150 raw_spin_unlock_irqrestore(&p->lock, flags);
151
152 return next;
153}
154
155static irqreturn_t em_sti_interrupt(int irq, void *dev_id)
156{
157 struct em_sti_priv *p = dev_id;
158
159 p->ced.event_handler(&p->ced);
160 return IRQ_HANDLED;
161}
162
163static int em_sti_start(struct em_sti_priv *p, unsigned int user)
164{
165 unsigned long flags;
166 int used_before;
167 int ret = 0;
168
169 raw_spin_lock_irqsave(&p->lock, flags);
170 used_before = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
171 if (!used_before)
172 ret = em_sti_enable(p);
173
174 if (!ret)
175 p->active[user] = 1;
176 raw_spin_unlock_irqrestore(&p->lock, flags);
177
178 return ret;
179}
180
181static void em_sti_stop(struct em_sti_priv *p, unsigned int user)
182{
183 unsigned long flags;
184 int used_before, used_after;
185
186 raw_spin_lock_irqsave(&p->lock, flags);
187 used_before = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
188 p->active[user] = 0;
189 used_after = p->active[USER_CLOCKSOURCE] | p->active[USER_CLOCKEVENT];
190
191 if (used_before && !used_after)
192 em_sti_disable(p);
193 raw_spin_unlock_irqrestore(&p->lock, flags);
194}
195
196static struct em_sti_priv *cs_to_em_sti(struct clocksource *cs)
197{
198 return container_of(cs, struct em_sti_priv, cs);
199}
200
201static cycle_t em_sti_clocksource_read(struct clocksource *cs)
202{
203 return em_sti_count(cs_to_em_sti(cs));
204}
205
206static int em_sti_clocksource_enable(struct clocksource *cs)
207{
208 int ret;
209 struct em_sti_priv *p = cs_to_em_sti(cs);
210
211 ret = em_sti_start(p, USER_CLOCKSOURCE);
212 if (!ret)
213 __clocksource_updatefreq_hz(cs, p->rate);
214 return ret;
215}
216
217static void em_sti_clocksource_disable(struct clocksource *cs)
218{
219 em_sti_stop(cs_to_em_sti(cs), USER_CLOCKSOURCE);
220}
221
222static void em_sti_clocksource_resume(struct clocksource *cs)
223{
224 em_sti_clocksource_enable(cs);
225}
226
227static int em_sti_register_clocksource(struct em_sti_priv *p)
228{
229 struct clocksource *cs = &p->cs;
230
231 memset(cs, 0, sizeof(*cs));
232 cs->name = dev_name(&p->pdev->dev);
233 cs->rating = 200;
234 cs->read = em_sti_clocksource_read;
235 cs->enable = em_sti_clocksource_enable;
236 cs->disable = em_sti_clocksource_disable;
237 cs->suspend = em_sti_clocksource_disable;
238 cs->resume = em_sti_clocksource_resume;
239 cs->mask = CLOCKSOURCE_MASK(48);
240 cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;
241
242 dev_info(&p->pdev->dev, "used as clock source\n");
243
244 /* Register with dummy 1 Hz value, gets updated in ->enable() */
245 clocksource_register_hz(cs, 1);
246 return 0;
247}
248
249static struct em_sti_priv *ced_to_em_sti(struct clock_event_device *ced)
250{
251 return container_of(ced, struct em_sti_priv, ced);
252}
253
254static void em_sti_clock_event_mode(enum clock_event_mode mode,
255 struct clock_event_device *ced)
256{
257 struct em_sti_priv *p = ced_to_em_sti(ced);
258
259 /* deal with old setting first */
260 switch (ced->mode) {
261 case CLOCK_EVT_MODE_ONESHOT:
262 em_sti_stop(p, USER_CLOCKEVENT);
263 break;
264 default:
265 break;
266 }
267
268 switch (mode) {
269 case CLOCK_EVT_MODE_ONESHOT:
270 dev_info(&p->pdev->dev, "used for oneshot clock events\n");
271 em_sti_start(p, USER_CLOCKEVENT);
272 clockevents_config(&p->ced, p->rate);
273 break;
274 case CLOCK_EVT_MODE_SHUTDOWN:
275 case CLOCK_EVT_MODE_UNUSED:
276 em_sti_stop(p, USER_CLOCKEVENT);
277 break;
278 default:
279 break;
280 }
281}
282
283static int em_sti_clock_event_next(unsigned long delta,
284 struct clock_event_device *ced)
285{
286 struct em_sti_priv *p = ced_to_em_sti(ced);
287 cycle_t next;
288 int safe;
289
290 next = em_sti_set_next(p, em_sti_count(p) + delta);
291 safe = em_sti_count(p) < (next - 1);
292
293 return !safe;
294}
295
296static void em_sti_register_clockevent(struct em_sti_priv *p)
297{
298 struct clock_event_device *ced = &p->ced;
299
300 memset(ced, 0, sizeof(*ced));
301 ced->name = dev_name(&p->pdev->dev);
302 ced->features = CLOCK_EVT_FEAT_ONESHOT;
303 ced->rating = 200;
304 ced->cpumask = cpumask_of(0);
305 ced->set_next_event = em_sti_clock_event_next;
306 ced->set_mode = em_sti_clock_event_mode;
307
308 dev_info(&p->pdev->dev, "used for clock events\n");
309
310 /* Register with dummy 1 Hz value, gets updated in ->set_mode() */
311 clockevents_config_and_register(ced, 1, 2, 0xffffffff);
312}
313
314static int __devinit em_sti_probe(struct platform_device *pdev)
315{
316 struct em_sti_priv *p;
317 struct resource *res;
318 int irq, ret;
319
320 p = kzalloc(sizeof(*p), GFP_KERNEL);
321 if (p == NULL) {
322 dev_err(&pdev->dev, "failed to allocate driver data\n");
323 ret = -ENOMEM;
324 goto err0;
325 }
326
327 p->pdev = pdev;
328 platform_set_drvdata(pdev, p);
329
330 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
331 if (!res) {
332 dev_err(&pdev->dev, "failed to get I/O memory\n");
333 ret = -EINVAL;
334 goto err0;
335 }
336
337 irq = platform_get_irq(pdev, 0);
338 if (irq < 0) {
339 dev_err(&pdev->dev, "failed to get irq\n");
340 ret = -EINVAL;
341 goto err0;
342 }
343
344 /* map memory, let base point to the STI instance */
345 p->base = ioremap_nocache(res->start, resource_size(res));
346 if (p->base == NULL) {
347 dev_err(&pdev->dev, "failed to remap I/O memory\n");
348 ret = -ENXIO;
349 goto err0;
350 }
351
352 /* get hold of clock */
353 p->clk = clk_get(&pdev->dev, "sclk");
354 if (IS_ERR(p->clk)) {
355 dev_err(&pdev->dev, "cannot get clock\n");
356 ret = PTR_ERR(p->clk);
357 goto err1;
358 }
359
360 if (request_irq(irq, em_sti_interrupt,
361 IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
362 dev_name(&pdev->dev), p)) {
363 dev_err(&pdev->dev, "failed to request low IRQ\n");
364 ret = -ENOENT;
365 goto err2;
366 }
367
368 raw_spin_lock_init(&p->lock);
369 em_sti_register_clockevent(p);
370 em_sti_register_clocksource(p);
371 return 0;
372
373err2:
374 clk_put(p->clk);
375err1:
376 iounmap(p->base);
377err0:
378 kfree(p);
379 return ret;
380}
381
382static int __devexit em_sti_remove(struct platform_device *pdev)
383{
384 return -EBUSY; /* cannot unregister clockevent and clocksource */
385}
386
387static struct platform_driver em_sti_device_driver = {
388 .probe = em_sti_probe,
389 .remove = __devexit_p(em_sti_remove),
390 .driver = {
391 .name = "em_sti",
392 }
393};
394
395module_platform_driver(em_sti_device_driver);
396
397MODULE_AUTHOR("Magnus Damm");
398MODULE_DESCRIPTION("Renesas Emma Mobile STI Timer Driver");
399MODULE_LICENSE("GPL v2");