]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
02b2ee16 G |
2 | /* |
3 | * linux/arch/unicore32/kernel/time.c | |
4 | * | |
5 | * Code specific to PKUnity SoC and UniCore ISA | |
6 | * | |
7 | * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn> | |
8 | * Copyright (C) 2001-2010 Guan Xuetao | |
02b2ee16 G |
9 | */ |
10 | #include <linux/init.h> | |
11 | #include <linux/errno.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/irq.h> | |
14 | #include <linux/timex.h> | |
15 | #include <linux/clockchips.h> | |
16 | ||
17 | #include <mach/hardware.h> | |
18 | ||
19 | #define MIN_OSCR_DELTA 2 | |
20 | ||
21 | static irqreturn_t puv3_ost0_interrupt(int irq, void *dev_id) | |
22 | { | |
23 | struct clock_event_device *c = dev_id; | |
24 | ||
25 | /* Disarm the compare/match, signal the event. */ | |
e5abf78b G |
26 | writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER); |
27 | writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR); | |
02b2ee16 G |
28 | c->event_handler(c); |
29 | ||
30 | return IRQ_HANDLED; | |
31 | } | |
32 | ||
33 | static int | |
34 | puv3_osmr0_set_next_event(unsigned long delta, struct clock_event_device *c) | |
35 | { | |
36 | unsigned long next, oscr; | |
37 | ||
e5abf78b G |
38 | writel(readl(OST_OIER) | OST_OIER_E0, OST_OIER); |
39 | next = readl(OST_OSCR) + delta; | |
40 | writel(next, OST_OSMR0); | |
41 | oscr = readl(OST_OSCR); | |
02b2ee16 G |
42 | |
43 | return (signed)(next - oscr) <= MIN_OSCR_DELTA ? -ETIME : 0; | |
44 | } | |
45 | ||
3078c8df | 46 | static int puv3_osmr0_shutdown(struct clock_event_device *evt) |
02b2ee16 | 47 | { |
3078c8df VK |
48 | writel(readl(OST_OIER) & ~OST_OIER_E0, OST_OIER); |
49 | writel(readl(OST_OSSR) & ~OST_OSSR_M0, OST_OSSR); | |
50 | return 0; | |
02b2ee16 G |
51 | } |
52 | ||
53 | static struct clock_event_device ckevt_puv3_osmr0 = { | |
3078c8df VK |
54 | .name = "osmr0", |
55 | .features = CLOCK_EVT_FEAT_ONESHOT, | |
56 | .rating = 200, | |
57 | .set_next_event = puv3_osmr0_set_next_event, | |
58 | .set_state_shutdown = puv3_osmr0_shutdown, | |
59 | .set_state_oneshot = puv3_osmr0_shutdown, | |
02b2ee16 G |
60 | }; |
61 | ||
a5a1d1c2 | 62 | static u64 puv3_read_oscr(struct clocksource *cs) |
02b2ee16 | 63 | { |
e5abf78b | 64 | return readl(OST_OSCR); |
02b2ee16 G |
65 | } |
66 | ||
67 | static struct clocksource cksrc_puv3_oscr = { | |
68 | .name = "oscr", | |
69 | .rating = 200, | |
70 | .read = puv3_read_oscr, | |
71 | .mask = CLOCKSOURCE_MASK(32), | |
72 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | |
73 | }; | |
74 | ||
75 | static struct irqaction puv3_timer_irq = { | |
76 | .name = "ost0", | |
86abc23e | 77 | .flags = IRQF_TIMER | IRQF_IRQPOLL, |
02b2ee16 G |
78 | .handler = puv3_ost0_interrupt, |
79 | .dev_id = &ckevt_puv3_osmr0, | |
80 | }; | |
81 | ||
82 | void __init time_init(void) | |
83 | { | |
e5abf78b G |
84 | writel(0, OST_OIER); /* disable any timer interrupts */ |
85 | writel(0, OST_OSSR); /* clear status on all timers */ | |
02b2ee16 | 86 | |
a913a823 G |
87 | clockevents_calc_mult_shift(&ckevt_puv3_osmr0, CLOCK_TICK_RATE, 5); |
88 | ||
02b2ee16 G |
89 | ckevt_puv3_osmr0.max_delta_ns = |
90 | clockevent_delta2ns(0x7fffffff, &ckevt_puv3_osmr0); | |
16c125b6 | 91 | ckevt_puv3_osmr0.max_delta_ticks = 0x7fffffff; |
02b2ee16 G |
92 | ckevt_puv3_osmr0.min_delta_ns = |
93 | clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_puv3_osmr0) + 1; | |
16c125b6 | 94 | ckevt_puv3_osmr0.min_delta_ticks = MIN_OSCR_DELTA * 2; |
02b2ee16 G |
95 | ckevt_puv3_osmr0.cpumask = cpumask_of(0); |
96 | ||
97 | setup_irq(IRQ_TIMER0, &puv3_timer_irq); | |
98 | ||
99 | clocksource_register_hz(&cksrc_puv3_oscr, CLOCK_TICK_RATE); | |
100 | clockevents_register_device(&ckevt_puv3_osmr0); | |
101 | } | |
102 | ||
103 | #ifdef CONFIG_PM | |
104 | unsigned long osmr[4], oier; | |
105 | ||
106 | void puv3_timer_suspend(void) | |
107 | { | |
e5abf78b G |
108 | osmr[0] = readl(OST_OSMR0); |
109 | osmr[1] = readl(OST_OSMR1); | |
110 | osmr[2] = readl(OST_OSMR2); | |
111 | osmr[3] = readl(OST_OSMR3); | |
112 | oier = readl(OST_OIER); | |
02b2ee16 G |
113 | } |
114 | ||
115 | void puv3_timer_resume(void) | |
116 | { | |
e5abf78b G |
117 | writel(0, OST_OSSR); |
118 | writel(osmr[0], OST_OSMR0); | |
119 | writel(osmr[1], OST_OSMR1); | |
120 | writel(osmr[2], OST_OSMR2); | |
121 | writel(osmr[3], OST_OSMR3); | |
122 | writel(oier, OST_OIER); | |
02b2ee16 G |
123 | |
124 | /* | |
125 | * OSMR0 is the system timer: make sure OSCR is sufficiently behind | |
126 | */ | |
e5abf78b | 127 | writel(readl(OST_OSMR0) - LATCH, OST_OSCR); |
02b2ee16 G |
128 | } |
129 | #else | |
130 | void puv3_timer_suspend(void) { }; | |
131 | void puv3_timer_resume(void) { }; | |
132 | #endif | |
133 |