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. */ | |
e5abf78b G |
29 | writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER); |
30 | writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR); | |
02b2ee16 G |
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 | ||
e5abf78b G |
41 | writel(readl(OST_OIER) | OST_OIER_E0, OST_OIER); |
42 | next = readl(OST_OSCR) + delta; | |
43 | writel(next, OST_OSMR0); | |
44 | oscr = readl(OST_OSCR); | |
02b2ee16 G |
45 | |
46 | return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0; | |
47 | } | |
48 | ||
3078c8df | 49 | static int puv3_osmr0_shutdown(struct clock_event_device *evt) |
02b2ee16 | 50 | { |
3078c8df VK |
51 | writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER); |
52 | writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR); | |
53 | return 0; | |
02b2ee16 G |
54 | } |
55 | ||
56 | static struct clock_event_device ckevt_puv3_osmr0 = { | |
3078c8df VK |
57 | .name = "osmr0", |
58 | .features = CLOCK_EVT_FEAT_ONESHOT, | |
59 | .rating = 200, | |
60 | .set_next_event = puv3_osmr0_set_next_event, | |
61 | .set_state_shutdown = puv3_osmr0_shutdown, | |
62 | .set_state_oneshot = puv3_osmr0_shutdown, | |
02b2ee16 G |
63 | }; |
64 | ||
65 | static cycle_t puv3_read_oscr(struct clocksource *cs) | |
66 | { | |
e5abf78b | 67 | return readl(OST_OSCR); |
02b2ee16 G |
68 | } |
69 | ||
70 | static struct clocksource cksrc_puv3_oscr = { | |
71 | .name = "oscr", | |
72 | .rating = 200, | |
73 | .read = puv3_read_oscr, | |
74 | .mask = CLOCKSOURCE_MASK(32), | |
75 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
76 | }; | |
77 | ||
78 | static struct irqaction puv3_timer_irq = { | |
79 | .name = "ost0", | |
86abc23e | 80 | .flags = IRQF_TIMER | IRQF_IRQPOLL, |
02b2ee16 G |
81 | .handler = puv3_ost0_interrupt, |
82 | .dev_id = &ckevt_puv3_osmr0, | |
83 | }; | |
84 | ||
85 | void __init time_init(void) | |
86 | { | |
e5abf78b G |
87 | writel(0, OST_OIER); /* disable any timer interrupts */ |
88 | writel(0, OST_OSSR); /* clear status on all timers */ | |
02b2ee16 | 89 | |
a913a823 G |
90 | clockevents_calc_mult_shift(&ckevt_puv3_osmr0, CLOCK_TICK_RATE, 5); |
91 | ||
02b2ee16 G |
92 | ckevt_puv3_osmr0.max_delta_ns = |
93 | clockevent_delta2ns(0x7fffffff, &ckevt_puv3_osmr0); | |
94 | ckevt_puv3_osmr0.min_delta_ns = | |
95 | clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_puv3_osmr0) + 1; | |
96 | ckevt_puv3_osmr0.cpumask = cpumask_of(0); | |
97 | ||
98 | setup_irq(IRQ_TIMER0, &puv3_timer_irq); | |
99 | ||
100 | clocksource_register_hz(&cksrc_puv3_oscr, CLOCK_TICK_RATE); | |
101 | clockevents_register_device(&ckevt_puv3_osmr0); | |
102 | } | |
103 | ||
104 | #ifdef CONFIG_PM | |
105 | unsigned long osmr[4], oier; | |
106 | ||
107 | void puv3_timer_suspend(void) | |
108 | { | |
e5abf78b G |
109 | osmr[0] = readl(OST_OSMR0); |
110 | osmr[1] = readl(OST_OSMR1); | |
111 | osmr[2] = readl(OST_OSMR2); | |
112 | osmr[3] = readl(OST_OSMR3); | |
113 | oier = readl(OST_OIER); | |
02b2ee16 G |
114 | } |
115 | ||
116 | void puv3_timer_resume(void) | |
117 | { | |
e5abf78b G |
118 | writel(0, OST_OSSR); |
119 | writel(osmr[0], OST_OSMR0); | |
120 | writel(osmr[1], OST_OSMR1); | |
121 | writel(osmr[2], OST_OSMR2); | |
122 | writel(osmr[3], OST_OSMR3); | |
123 | writel(oier, OST_OIER); | |
02b2ee16 G |
124 | |
125 | /* | |
126 | * OSMR0 is the system timer: make sure OSCR is sufficiently behind | |
127 | */ | |
e5abf78b | 128 | writel(readl(OST_OSMR0) - LATCH, OST_OSCR); |
02b2ee16 G |
129 | } |
130 | #else | |
131 | void puv3_timer_suspend(void) { }; | |
132 | void puv3_timer_resume(void) { }; | |
133 | #endif | |
134 |