]>
Commit | Line | Data |
---|---|---|
02b2ee16 G |
1 | /* |
2 | * linux/arch/unicore32/kernel/time.c | |
3 | * | |
4 | * Code specific to PKUnity SoC and UniCore ISA | |
5 | * | |
6 | * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> | |
7 | * Copyright (C) 2001-2010 Guan Xuetao | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | #include <linux/init.h> | |
14 | #include <linux/errno.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/irq.h> | |
17 | #include <linux/timex.h> | |
18 | #include <linux/clockchips.h> | |
19 | ||
20 | #include <mach/hardware.h> | |
21 | ||
22 | #define MIN_OSCR_DELTA 2 | |
23 | ||
24 | static irqreturn_t puv3_ost0_interrupt(int irq, void *dev_id) | |
25 | { | |
26 | struct clock_event_device *c = dev_id; | |
27 | ||
28 | /* Disarm the compare/match, signal the event. */ | |
29 | OST_OIER &= ~OST_OIER_E0; | |
30 | OST_OSSR &= ~OST_OSSR_M0; | |
31 | c->event_handler(c); | |
32 | ||
33 | return IRQ_HANDLED; | |
34 | } | |
35 | ||
36 | static int | |
37 | puv3_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c) | |
38 | { | |
39 | unsigned long next, oscr; | |
40 | ||
41 | OST_OIER |= OST_OIER_E0; | |
42 | next = OST_OSCR + delta; | |
43 | OST_OSMR0 = next; | |
44 | oscr = OST_OSCR; | |
45 | ||
46 | return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0; | |
47 | } | |
48 | ||
49 | static void | |
50 | puv3_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *c) | |
51 | { | |
52 | switch (mode) { | |
53 | case CLOCK_EVT_MODE_ONESHOT: | |
54 | case CLOCK_EVT_MODE_UNUSED: | |
55 | case CLOCK_EVT_MODE_SHUTDOWN: | |
56 | OST_OIER &= ~OST_OIER_E0; | |
57 | OST_OSSR &= ~OST_OSSR_M0; | |
58 | break; | |
59 | ||
60 | case CLOCK_EVT_MODE_RESUME: | |
61 | case CLOCK_EVT_MODE_PERIODIC: | |
62 | break; | |
63 | } | |
64 | } | |
65 | ||
66 | static struct clock_event_device ckevt_puv3_osmr0 = { | |
67 | .name = "osmr0", | |
68 | .features = CLOCK_EVT_FEAT_ONESHOT, | |
69 | #ifdef CONFIG_ARCH_FPGA | |
70 | .shift = 18, /* correct shift val: 16, but warn_on_slowpath */ | |
71 | #else | |
72 | .shift = 30, | |
73 | #endif | |
74 | .rating = 200, | |
75 | .set_next_event = puv3_osmr0_set_next_event, | |
76 | .set_mode = puv3_osmr0_set_mode, | |
77 | }; | |
78 | ||
79 | static cycle_t puv3_read_oscr(struct clocksource *cs) | |
80 | { | |
81 | return OST_OSCR; | |
82 | } | |
83 | ||
84 | static struct clocksource cksrc_puv3_oscr = { | |
85 | .name = "oscr", | |
86 | .rating = 200, | |
87 | .read = puv3_read_oscr, | |
88 | .mask = CLOCKSOURCE_MASK(32), | |
89 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
90 | }; | |
91 | ||
92 | static struct irqaction puv3_timer_irq = { | |
93 | .name = "ost0", | |
94 | .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, | |
95 | .handler = puv3_ost0_interrupt, | |
96 | .dev_id = &ckevt_puv3_osmr0, | |
97 | }; | |
98 | ||
99 | void __init time_init(void) | |
100 | { | |
101 | OST_OIER = 0; /* disable any timer interrupts */ | |
102 | OST_OSSR = 0; /* clear status on all timers */ | |
103 | ||
104 | ckevt_puv3_osmr0.mult = | |
105 | div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC, ckevt_puv3_osmr0.shift); | |
106 | ckevt_puv3_osmr0.max_delta_ns = | |
107 | clockevent_delta2ns(0x7fffffff, &ckevt_puv3_osmr0); | |
108 | ckevt_puv3_osmr0.min_delta_ns = | |
109 | clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_puv3_osmr0) + 1; | |
110 | ckevt_puv3_osmr0.cpumask = cpumask_of(0); | |
111 | ||
112 | setup_irq(IRQ_TIMER0, &puv3_timer_irq); | |
113 | ||
114 | clocksource_register_hz(&cksrc_puv3_oscr, CLOCK_TICK_RATE); | |
115 | clockevents_register_device(&ckevt_puv3_osmr0); | |
116 | } | |
117 | ||
118 | #ifdef CONFIG_PM | |
119 | unsigned long osmr[4], oier; | |
120 | ||
121 | void puv3_timer_suspend(void) | |
122 | { | |
123 | osmr[0] = OST_OSMR0; | |
124 | osmr[1] = OST_OSMR1; | |
125 | osmr[2] = OST_OSMR2; | |
126 | osmr[3] = OST_OSMR3; | |
127 | oier = OST_OIER; | |
128 | } | |
129 | ||
130 | void puv3_timer_resume(void) | |
131 | { | |
132 | OST_OSSR = 0; | |
133 | OST_OSMR0 = osmr[0]; | |
134 | OST_OSMR1 = osmr[1]; | |
135 | OST_OSMR2 = osmr[2]; | |
136 | OST_OSMR3 = osmr[3]; | |
137 | OST_OIER = oier; | |
138 | ||
139 | /* | |
140 | * OSMR0 is the system timer: make sure OSCR is sufficiently behind | |
141 | */ | |
142 | OST_OSCR = OST_OSMR0 - LATCH; | |
143 | } | |
144 | #else | |
145 | void puv3_timer_suspend(void) { }; | |
146 | void puv3_timer_resume(void) { }; | |
147 | #endif | |
148 |