]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/arm/mach-sa1100/time.c | |
3 | * | |
4 | * Copyright (C) 1998 Deborah Wallach. | |
93982535 KE |
5 | * Twiddles (C) 1999 Hugo Fiennes <hugo@empeg.com> |
6 | * | |
2f82af08 | 7 | * 2000/03/29 (C) Nicolas Pitre <nico@fluxnic.net> |
1da177e4 LT |
8 | * Rewritten: big cleanup, much simpler, better HZ accuracy. |
9 | * | |
10 | */ | |
11 | #include <linux/init.h> | |
12 | #include <linux/errno.h> | |
13 | #include <linux/interrupt.h> | |
119c641c | 14 | #include <linux/irq.h> |
1da177e4 | 15 | #include <linux/timex.h> |
3e238be2 | 16 | #include <linux/clockchips.h> |
1da177e4 LT |
17 | |
18 | #include <asm/mach/time.h> | |
a09e64fb | 19 | #include <mach/hardware.h> |
1da177e4 | 20 | |
3e238be2 | 21 | #define MIN_OSCR_DELTA 2 |
1da177e4 | 22 | |
3e238be2 | 23 | static irqreturn_t sa1100_ost0_interrupt(int irq, void *dev_id) |
1da177e4 | 24 | { |
3e238be2 | 25 | struct clock_event_device *c = dev_id; |
1da177e4 | 26 | |
3e238be2 RK |
27 | /* Disarm the compare/match, signal the event. */ |
28 | OIER &= ~OIER_E0; | |
29 | OSSR = OSSR_M0; | |
30 | c->event_handler(c); | |
1da177e4 | 31 | |
3e238be2 RK |
32 | return IRQ_HANDLED; |
33 | } | |
569d2c34 | 34 | |
3e238be2 RK |
35 | static int |
36 | sa1100_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c) | |
1da177e4 | 37 | { |
3e238be2 | 38 | unsigned long flags, next, oscr; |
1da177e4 | 39 | |
3e238be2 RK |
40 | raw_local_irq_save(flags); |
41 | OIER |= OIER_E0; | |
42 | next = OSCR + delta; | |
43 | OSMR0 = next; | |
44 | oscr = OSCR; | |
45 | raw_local_irq_restore(flags); | |
569d2c34 | 46 | |
3e238be2 RK |
47 | return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0; |
48 | } | |
1da177e4 | 49 | |
3e238be2 RK |
50 | static void |
51 | sa1100_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *c) | |
52 | { | |
53 | unsigned long flags; | |
54 | ||
55 | switch (mode) { | |
56 | case CLOCK_EVT_MODE_ONESHOT: | |
57 | case CLOCK_EVT_MODE_UNUSED: | |
58 | case CLOCK_EVT_MODE_SHUTDOWN: | |
59 | raw_local_irq_save(flags); | |
60 | OIER &= ~OIER_E0; | |
61 | OSSR = OSSR_M0; | |
62 | raw_local_irq_restore(flags); | |
63 | break; | |
64 | ||
65 | case CLOCK_EVT_MODE_RESUME: | |
66 | case CLOCK_EVT_MODE_PERIODIC: | |
67 | break; | |
68 | } | |
1da177e4 LT |
69 | } |
70 | ||
3e238be2 RK |
71 | static struct clock_event_device ckevt_sa1100_osmr0 = { |
72 | .name = "osmr0", | |
73 | .features = CLOCK_EVT_FEAT_ONESHOT, | |
74 | .shift = 32, | |
75 | .rating = 200, | |
3e238be2 RK |
76 | .set_next_event = sa1100_osmr0_set_next_event, |
77 | .set_mode = sa1100_osmr0_set_mode, | |
1da177e4 LT |
78 | }; |
79 | ||
d142b6e7 RK |
80 | static cycle_t sa1100_read_oscr(void) |
81 | { | |
82 | return OSCR; | |
83 | } | |
84 | ||
85 | static struct clocksource cksrc_sa1100_oscr = { | |
86 | .name = "oscr", | |
87 | .rating = 200, | |
88 | .read = sa1100_read_oscr, | |
89 | .mask = CLOCKSOURCE_MASK(32), | |
90 | .shift = 20, | |
91 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
92 | }; | |
93 | ||
3e238be2 RK |
94 | static struct irqaction sa1100_timer_irq = { |
95 | .name = "ost0", | |
96 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | |
97 | .handler = sa1100_ost0_interrupt, | |
98 | .dev_id = &ckevt_sa1100_osmr0, | |
99 | }; | |
100 | ||
1da177e4 LT |
101 | static void __init sa1100_timer_init(void) |
102 | { | |
5285eb57 | 103 | OIER = 0; /* disable any timer interrupts */ |
1da177e4 | 104 | OSSR = 0xf; /* clear status on all timers */ |
3e238be2 RK |
105 | |
106 | ckevt_sa1100_osmr0.mult = | |
107 | div_sc(3686400, NSEC_PER_SEC, ckevt_sa1100_osmr0.shift); | |
108 | ckevt_sa1100_osmr0.max_delta_ns = | |
109 | clockevent_delta2ns(0x7fffffff, &ckevt_sa1100_osmr0); | |
110 | ckevt_sa1100_osmr0.min_delta_ns = | |
111 | clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_sa1100_osmr0) + 1; | |
320ab2b0 | 112 | ckevt_sa1100_osmr0.cpumask = cpumask_of(0); |
d142b6e7 RK |
113 | |
114 | cksrc_sa1100_oscr.mult = | |
115 | clocksource_hz2mult(CLOCK_TICK_RATE, cksrc_sa1100_oscr.shift); | |
116 | ||
3e238be2 | 117 | setup_irq(IRQ_OST0, &sa1100_timer_irq); |
569d2c34 | 118 | |
3e238be2 RK |
119 | clocksource_register(&cksrc_sa1100_oscr); |
120 | clockevents_register_device(&ckevt_sa1100_osmr0); | |
569d2c34 NP |
121 | } |
122 | ||
1da177e4 LT |
123 | #ifdef CONFIG_PM |
124 | unsigned long osmr[4], oier; | |
125 | ||
126 | static void sa1100_timer_suspend(void) | |
127 | { | |
128 | osmr[0] = OSMR0; | |
129 | osmr[1] = OSMR1; | |
130 | osmr[2] = OSMR2; | |
131 | osmr[3] = OSMR3; | |
132 | oier = OIER; | |
133 | } | |
134 | ||
135 | static void sa1100_timer_resume(void) | |
136 | { | |
137 | OSSR = 0x0f; | |
138 | OSMR0 = osmr[0]; | |
139 | OSMR1 = osmr[1]; | |
140 | OSMR2 = osmr[2]; | |
141 | OSMR3 = osmr[3]; | |
142 | OIER = oier; | |
143 | ||
144 | /* | |
145 | * OSMR0 is the system timer: make sure OSCR is sufficiently behind | |
146 | */ | |
147 | OSCR = OSMR0 - LATCH; | |
148 | } | |
149 | #else | |
150 | #define sa1100_timer_suspend NULL | |
151 | #define sa1100_timer_resume NULL | |
152 | #endif | |
153 | ||
154 | struct sys_timer sa1100_timer = { | |
155 | .init = sa1100_timer_init, | |
156 | .suspend = sa1100_timer_suspend, | |
157 | .resume = sa1100_timer_resume, | |
1da177e4 | 158 | }; |