Commit | Line | Data |
---|---|---|
28ad94ec AR |
1 | /* |
2 | * linux/arch/arm/mach-nomadik/timer.c | |
3 | * | |
4 | * Copyright (C) 2008 STMicroelectronics | |
5 | * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2, as | |
9 | * published by the Free Software Foundation. | |
10 | */ | |
11 | #include <linux/init.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/irq.h> | |
14 | #include <linux/io.h> | |
15 | #include <linux/clockchips.h> | |
16 | #include <linux/jiffies.h> | |
17 | #include <asm/mach/time.h> | |
28ad94ec | 18 | |
59b559d7 | 19 | #include <plat/mtu.h> |
28ad94ec AR |
20 | |
21 | static u32 nmdk_count; /* accumulated count */ | |
22 | static u32 nmdk_cycle; /* write-once */ | |
59b559d7 SK |
23 | |
24 | /* setup by the platform code */ | |
25 | void __iomem *mtu_base; | |
28ad94ec AR |
26 | |
27 | /* | |
28 | * clocksource: the MTU device is a decrementing counters, so we negate | |
29 | * the value being read. | |
30 | */ | |
31 | static cycle_t nmdk_read_timer(struct clocksource *cs) | |
32 | { | |
33 | u32 count = readl(mtu_base + MTU_VAL(0)); | |
34 | return nmdk_count + nmdk_cycle - count; | |
35 | ||
36 | } | |
37 | ||
38 | static struct clocksource nmdk_clksrc = { | |
39 | .name = "mtu_0", | |
40 | .rating = 120, | |
41 | .read = nmdk_read_timer, | |
42 | .shift = 20, | |
43 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
44 | }; | |
45 | ||
46 | /* | |
47 | * Clockevent device: currently only periodic mode is supported | |
48 | */ | |
49 | static void nmdk_clkevt_mode(enum clock_event_mode mode, | |
50 | struct clock_event_device *dev) | |
51 | { | |
28ad94ec AR |
52 | switch (mode) { |
53 | case CLOCK_EVT_MODE_PERIODIC: | |
a602f0f2 | 54 | /* count current value? */ |
28ad94ec | 55 | writel(readl(mtu_base + MTU_IMSC) | 1, mtu_base + MTU_IMSC); |
28ad94ec AR |
56 | break; |
57 | case CLOCK_EVT_MODE_ONESHOT: | |
58 | BUG(); /* Not supported, yet */ | |
59 | /* FALLTHROUGH */ | |
60 | case CLOCK_EVT_MODE_SHUTDOWN: | |
61 | case CLOCK_EVT_MODE_UNUSED: | |
28ad94ec | 62 | writel(readl(mtu_base + MTU_IMSC) & ~1, mtu_base + MTU_IMSC); |
28ad94ec AR |
63 | break; |
64 | case CLOCK_EVT_MODE_RESUME: | |
65 | break; | |
66 | } | |
67 | } | |
68 | ||
69 | static struct clock_event_device nmdk_clkevt = { | |
70 | .name = "mtu_0", | |
71 | .features = CLOCK_EVT_FEAT_PERIODIC, | |
72 | .shift = 32, | |
73 | .rating = 100, | |
74 | .set_mode = nmdk_clkevt_mode, | |
75 | }; | |
76 | ||
77 | /* | |
78 | * IRQ Handler for the timer 0 of the MTU block. The irq is not shared | |
79 | * as we are the only users of mtu0 by now. | |
80 | */ | |
81 | static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id) | |
82 | { | |
83 | /* ack: "interrupt clear register" */ | |
59b559d7 | 84 | writel(1 << 0, mtu_base + MTU_ICR); |
28ad94ec AR |
85 | |
86 | /* we can't count lost ticks, unfortunately */ | |
87 | nmdk_count += nmdk_cycle; | |
88 | nmdk_clkevt.event_handler(&nmdk_clkevt); | |
89 | ||
90 | return IRQ_HANDLED; | |
91 | } | |
92 | ||
93 | /* | |
94 | * Set up timer interrupt, and return the current time in seconds. | |
95 | */ | |
96 | static struct irqaction nmdk_timer_irq = { | |
97 | .name = "Nomadik Timer Tick", | |
98 | .flags = IRQF_DISABLED | IRQF_TIMER, | |
99 | .handler = nmdk_timer_interrupt, | |
100 | }; | |
101 | ||
102 | static void nmdk_timer_reset(void) | |
103 | { | |
104 | u32 cr; | |
105 | ||
106 | writel(0, mtu_base + MTU_CR(0)); /* off */ | |
107 | ||
108 | /* configure load and background-load, and fire it up */ | |
109 | writel(nmdk_cycle, mtu_base + MTU_LR(0)); | |
110 | writel(nmdk_cycle, mtu_base + MTU_BGLR(0)); | |
111 | cr = MTU_CRn_PERIODIC | MTU_CRn_PRESCALE_1 | MTU_CRn_32BITS; | |
112 | writel(cr, mtu_base + MTU_CR(0)); | |
113 | writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0)); | |
114 | } | |
115 | ||
59b559d7 | 116 | void __init nmdk_timer_init(void) |
28ad94ec | 117 | { |
28ad94ec AR |
118 | unsigned long rate; |
119 | int bits; | |
120 | ||
121 | rate = CLOCK_TICK_RATE; /* 2.4MHz */ | |
122 | nmdk_cycle = (rate + HZ/2) / HZ; | |
123 | ||
28ad94ec AR |
124 | /* Init the timer and register clocksource */ |
125 | nmdk_timer_reset(); | |
126 | ||
127 | nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift); | |
128 | bits = 8*sizeof(nmdk_count); | |
129 | nmdk_clksrc.mask = CLOCKSOURCE_MASK(bits); | |
130 | ||
59b559d7 SK |
131 | if (clocksource_register(&nmdk_clksrc)) |
132 | printk(KERN_ERR "timer: failed to initialize clock " | |
133 | "source %s\n", nmdk_clksrc.name); | |
28ad94ec AR |
134 | |
135 | /* Register irq and clockevents */ | |
136 | setup_irq(IRQ_MTU0, &nmdk_timer_irq); | |
137 | nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift); | |
138 | nmdk_clkevt.cpumask = cpumask_of(0); | |
139 | clockevents_register_device(&nmdk_clkevt); | |
140 | } |