|
1 | 1 | // SPDX-License-Identifier: GPL-2.0
|
2 | 2 | /*
|
3 |
| - * linux/arch/parisc/kernel/time.c |
| 3 | + * Common time service routines for parisc machines. |
| 4 | + * based on arch/loongarch/kernel/time.c |
4 | 5 | *
|
5 |
| - * Copyright (C) 1991, 1992, 1995 Linus Torvalds |
6 |
| - * Modifications for ARM (C) 1994, 1995, 1996,1997 Russell King |
7 |
| - * Copyright (C) 1999 SuSE GmbH, (Philipp Rumpf, prumpf@tux.org) |
8 |
| - * |
9 |
| - * 1994-07-02 Alan Modra |
10 |
| - * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime |
11 |
| - * 1998-12-20 Updated NTP code according to technical memorandum Jan '96 |
12 |
| - * "A Kernel Model for Precision Timekeeping" by Dave Mills |
| 6 | + * Copyright (C) 2024 Helge Deller <deller@gmx.de> |
13 | 7 | */
|
14 |
| -#include <linux/errno.h> |
15 |
| -#include <linux/module.h> |
16 |
| -#include <linux/rtc.h> |
17 |
| -#include <linux/sched.h> |
18 |
| -#include <linux/sched/clock.h> |
19 |
| -#include <linux/sched_clock.h> |
20 |
| -#include <linux/kernel.h> |
21 |
| -#include <linux/param.h> |
22 |
| -#include <linux/string.h> |
23 |
| -#include <linux/mm.h> |
24 |
| -#include <linux/interrupt.h> |
25 |
| -#include <linux/time.h> |
| 8 | +#include <linux/clockchips.h> |
| 9 | +#include <linux/delay.h> |
| 10 | +#include <linux/export.h> |
26 | 11 | #include <linux/init.h>
|
27 |
| -#include <linux/smp.h> |
28 |
| -#include <linux/profile.h> |
29 |
| -#include <linux/clocksource.h> |
| 12 | +#include <linux/interrupt.h> |
| 13 | +#include <linux/kernel.h> |
| 14 | +#include <linux/sched_clock.h> |
| 15 | +#include <linux/spinlock.h> |
| 16 | +#include <linux/rtc.h> |
30 | 17 | #include <linux/platform_device.h>
|
31 |
| -#include <linux/ftrace.h> |
| 18 | +#include <asm/processor.h> |
32 | 19 |
|
33 |
| -#include <linux/uaccess.h> |
34 |
| -#include <asm/io.h> |
35 |
| -#include <asm/irq.h> |
36 |
| -#include <asm/page.h> |
37 |
| -#include <asm/param.h> |
38 |
| -#include <asm/pdc.h> |
39 |
| -#include <asm/led.h> |
| 20 | +static u64 cr16_clock_freq; |
| 21 | +static unsigned long clocktick; |
40 | 22 |
|
41 |
| -#include <linux/timex.h> |
| 23 | +int time_keeper_id; /* CPU used for timekeeping */ |
42 | 24 |
|
43 |
| -int time_keeper_id __read_mostly; /* CPU used for timekeeping. */ |
| 25 | +static DEFINE_PER_CPU(struct clock_event_device, parisc_clockevent_device); |
44 | 26 |
|
45 |
| -static unsigned long clocktick __ro_after_init; /* timer cycles per tick */ |
| 27 | +static void parisc_event_handler(struct clock_event_device *dev) |
| 28 | +{ |
| 29 | +} |
46 | 30 |
|
47 |
| -/* |
48 |
| - * We keep time on PA-RISC Linux by using the Interval Timer which is |
49 |
| - * a pair of registers; one is read-only and one is write-only; both |
50 |
| - * accessed through CR16. The read-only register is 32 or 64 bits wide, |
51 |
| - * and increments by 1 every CPU clock tick. The architecture only |
52 |
| - * guarantees us a rate between 0.5 and 2, but all implementations use a |
53 |
| - * rate of 1. The write-only register is 32-bits wide. When the lowest |
54 |
| - * 32 bits of the read-only register compare equal to the write-only |
55 |
| - * register, it raises a maskable external interrupt. Each processor has |
56 |
| - * an Interval Timer of its own and they are not synchronised. |
57 |
| - * |
58 |
| - * We want to generate an interrupt every 1/HZ seconds. So we program |
59 |
| - * CR16 to interrupt every @clocktick cycles. The it_value in cpu_data |
60 |
| - * is programmed with the intended time of the next tick. We can be |
61 |
| - * held off for an arbitrarily long period of time by interrupts being |
62 |
| - * disabled, so we may miss one or more ticks. |
63 |
| - */ |
64 |
| -irqreturn_t __irq_entry timer_interrupt(int irq, void *dev_id) |
| 31 | +static int parisc_timer_next_event(unsigned long delta, struct clock_event_device *evt) |
65 | 32 | {
|
66 |
| - unsigned long now; |
67 |
| - unsigned long next_tick; |
68 |
| - unsigned long ticks_elapsed = 0; |
69 |
| - unsigned int cpu = smp_processor_id(); |
70 |
| - struct cpuinfo_parisc *cpuinfo = &per_cpu(cpu_data, cpu); |
71 |
| - |
72 |
| - /* gcc can optimize for "read-only" case with a local clocktick */ |
73 |
| - unsigned long cpt = clocktick; |
74 |
| - |
75 |
| - /* Initialize next_tick to the old expected tick time. */ |
76 |
| - next_tick = cpuinfo->it_value; |
77 |
| - |
78 |
| - /* Calculate how many ticks have elapsed. */ |
79 |
| - now = mfctl(16); |
80 |
| - do { |
81 |
| - ++ticks_elapsed; |
82 |
| - next_tick += cpt; |
83 |
| - } while (next_tick - now > cpt); |
84 |
| - |
85 |
| - /* Store (in CR16 cycles) up to when we are accounting right now. */ |
86 |
| - cpuinfo->it_value = next_tick; |
87 |
| - |
88 |
| - /* Go do system house keeping. */ |
89 |
| - if (IS_ENABLED(CONFIG_SMP) && (cpu != time_keeper_id)) |
90 |
| - ticks_elapsed = 0; |
91 |
| - legacy_timer_tick(ticks_elapsed); |
92 |
| - |
93 |
| - /* Skip clockticks on purpose if we know we would miss those. |
94 |
| - * The new CR16 must be "later" than current CR16 otherwise |
95 |
| - * itimer would not fire until CR16 wrapped - e.g 4 seconds |
96 |
| - * later on a 1Ghz processor. We'll account for the missed |
97 |
| - * ticks on the next timer interrupt. |
98 |
| - * We want IT to fire modulo clocktick even if we miss/skip some. |
99 |
| - * But those interrupts don't in fact get delivered that regularly. |
100 |
| - * |
101 |
| - * "next_tick - now" will always give the difference regardless |
102 |
| - * if one or the other wrapped. If "now" is "bigger" we'll end up |
103 |
| - * with a very large unsigned number. |
104 |
| - */ |
105 |
| - now = mfctl(16); |
106 |
| - while (next_tick - now > cpt) |
107 |
| - next_tick += cpt; |
108 |
| - |
109 |
| - /* Program the IT when to deliver the next interrupt. |
110 |
| - * Only bottom 32-bits of next_tick are writable in CR16! |
111 |
| - * Timer interrupt will be delivered at least a few hundred cycles |
112 |
| - * after the IT fires, so if we are too close (<= 8000 cycles) to the |
113 |
| - * next cycle, simply skip it. |
114 |
| - */ |
115 |
| - if (next_tick - now <= 8000) |
116 |
| - next_tick += cpt; |
117 |
| - mtctl(next_tick, 16); |
| 33 | + unsigned long new_cr16; |
118 | 34 |
|
119 |
| - return IRQ_HANDLED; |
120 |
| -} |
| 35 | + new_cr16 = mfctl(16) + delta; |
| 36 | + mtctl(new_cr16, 16); |
121 | 37 |
|
| 38 | + return 0; |
| 39 | +} |
122 | 40 |
|
123 |
| -unsigned long profile_pc(struct pt_regs *regs) |
| 41 | +irqreturn_t timer_interrupt(int irq, void *data) |
124 | 42 | {
|
125 |
| - unsigned long pc = instruction_pointer(regs); |
| 43 | + struct clock_event_device *cd; |
| 44 | + int cpu = smp_processor_id(); |
126 | 45 |
|
127 |
| - if (regs->gr[0] & PSW_N) |
128 |
| - pc -= 4; |
| 46 | + cd = &per_cpu(parisc_clockevent_device, cpu); |
129 | 47 |
|
130 |
| -#ifdef CONFIG_SMP |
131 |
| - if (in_lock_functions(pc)) |
132 |
| - pc = regs->gr[2]; |
133 |
| -#endif |
| 48 | + if (clockevent_state_periodic(cd)) |
| 49 | + parisc_timer_next_event(clocktick, cd); |
134 | 50 |
|
135 |
| - return pc; |
| 51 | + if (clockevent_state_periodic(cd) || clockevent_state_oneshot(cd)) |
| 52 | + cd->event_handler(cd); |
| 53 | + |
| 54 | + return IRQ_HANDLED; |
136 | 55 | }
|
137 |
| -EXPORT_SYMBOL(profile_pc); |
138 | 56 |
|
| 57 | +static int parisc_set_state_oneshot(struct clock_event_device *evt) |
| 58 | +{ |
| 59 | + parisc_timer_next_event(clocktick, evt); |
139 | 60 |
|
140 |
| -/* clock source code */ |
| 61 | + return 0; |
| 62 | +} |
141 | 63 |
|
142 |
| -static u64 notrace read_cr16(struct clocksource *cs) |
| 64 | +static int parisc_set_state_periodic(struct clock_event_device *evt) |
143 | 65 | {
|
144 |
| - return get_cycles(); |
| 66 | + parisc_timer_next_event(clocktick, evt); |
| 67 | + |
| 68 | + return 0; |
145 | 69 | }
|
146 | 70 |
|
147 |
| -static struct clocksource clocksource_cr16 = { |
148 |
| - .name = "cr16", |
149 |
| - .rating = 300, |
150 |
| - .read = read_cr16, |
151 |
| - .mask = CLOCKSOURCE_MASK(BITS_PER_LONG), |
152 |
| - .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
153 |
| -}; |
| 71 | +static int parisc_set_state_shutdown(struct clock_event_device *evt) |
| 72 | +{ |
| 73 | + return 0; |
| 74 | +} |
154 | 75 |
|
155 |
| -void start_cpu_itimer(void) |
| 76 | +void parisc_clockevent_init(void) |
156 | 77 | {
|
157 | 78 | unsigned int cpu = smp_processor_id();
|
158 |
| - unsigned long next_tick = mfctl(16) + clocktick; |
| 79 | + unsigned long min_delta = 0x600; /* XXX */ |
| 80 | + unsigned long max_delta = (1UL << (BITS_PER_LONG - 1)); |
| 81 | + struct clock_event_device *cd; |
| 82 | + |
| 83 | + cd = &per_cpu(parisc_clockevent_device, cpu); |
| 84 | + |
| 85 | + cd->name = "cr16_clockevent"; |
| 86 | + cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | |
| 87 | + CLOCK_EVT_FEAT_PERCPU; |
| 88 | + |
| 89 | + cd->irq = TIMER_IRQ; |
| 90 | + cd->rating = 320; |
| 91 | + cd->cpumask = cpumask_of(cpu); |
| 92 | + cd->set_state_oneshot = parisc_set_state_oneshot; |
| 93 | + cd->set_state_oneshot_stopped = parisc_set_state_shutdown; |
| 94 | + cd->set_state_periodic = parisc_set_state_periodic; |
| 95 | + cd->set_state_shutdown = parisc_set_state_shutdown; |
| 96 | + cd->set_next_event = parisc_timer_next_event; |
| 97 | + cd->event_handler = parisc_event_handler; |
| 98 | + |
| 99 | + clockevents_config_and_register(cd, cr16_clock_freq, min_delta, max_delta); |
| 100 | +} |
| 101 | + |
| 102 | +unsigned long notrace profile_pc(struct pt_regs *regs) |
| 103 | +{ |
| 104 | + unsigned long pc = instruction_pointer(regs); |
159 | 105 |
|
160 |
| - mtctl(next_tick, 16); /* kick off Interval Timer (CR16) */ |
| 106 | + if (regs->gr[0] & PSW_N) |
| 107 | + pc -= 4; |
| 108 | + |
| 109 | +#ifdef CONFIG_SMP |
| 110 | + if (in_lock_functions(pc)) |
| 111 | + pc = regs->gr[2]; |
| 112 | +#endif |
161 | 113 |
|
162 |
| - per_cpu(cpu_data, cpu).it_value = next_tick; |
| 114 | + return pc; |
163 | 115 | }
|
| 116 | +EXPORT_SYMBOL(profile_pc); |
164 | 117 |
|
165 | 118 | #if IS_ENABLED(CONFIG_RTC_DRV_GENERIC)
|
166 | 119 | static int rtc_generic_get_time(struct device *dev, struct rtc_time *tm)
|
@@ -224,46 +177,42 @@ void read_persistent_clock64(struct timespec64 *ts)
|
224 | 177 | }
|
225 | 178 | }
|
226 | 179 |
|
227 |
| - |
228 | 180 | static u64 notrace read_cr16_sched_clock(void)
|
229 | 181 | {
|
230 | 182 | return get_cycles();
|
231 | 183 | }
|
232 | 184 |
|
| 185 | +static u64 notrace read_cr16(struct clocksource *cs) |
| 186 | +{ |
| 187 | + return get_cycles(); |
| 188 | +} |
| 189 | + |
| 190 | +static struct clocksource clocksource_cr16 = { |
| 191 | + .name = "cr16", |
| 192 | + .rating = 300, |
| 193 | + .read = read_cr16, |
| 194 | + .mask = CLOCKSOURCE_MASK(BITS_PER_LONG), |
| 195 | + .flags = CLOCK_SOURCE_IS_CONTINUOUS | |
| 196 | + CLOCK_SOURCE_VALID_FOR_HRES | |
| 197 | + CLOCK_SOURCE_MUST_VERIFY | |
| 198 | + CLOCK_SOURCE_VERIFY_PERCPU, |
| 199 | +}; |
| 200 | + |
233 | 201 |
|
234 | 202 | /*
|
235 | 203 | * timer interrupt and sched_clock() initialization
|
236 | 204 | */
|
237 | 205 |
|
238 | 206 | void __init time_init(void)
|
239 | 207 | {
|
240 |
| - unsigned long cr16_hz; |
241 |
| - |
242 |
| - clocktick = (100 * PAGE0->mem_10msec) / HZ; |
243 |
| - start_cpu_itimer(); /* get CPU 0 started */ |
244 |
| - |
245 |
| - cr16_hz = 100 * PAGE0->mem_10msec; /* Hz */ |
| 208 | + cr16_clock_freq = 100 * PAGE0->mem_10msec; /* Hz */ |
| 209 | + clocktick = cr16_clock_freq / HZ; |
246 | 210 |
|
247 | 211 | /* register as sched_clock source */
|
248 |
| - sched_clock_register(read_cr16_sched_clock, BITS_PER_LONG, cr16_hz); |
249 |
| -} |
| 212 | + sched_clock_register(read_cr16_sched_clock, BITS_PER_LONG, cr16_clock_freq); |
250 | 213 |
|
251 |
| -static int __init init_cr16_clocksource(void) |
252 |
| -{ |
253 |
| - /* |
254 |
| - * The cr16 interval timers are not synchronized across CPUs. |
255 |
| - */ |
256 |
| - if (num_online_cpus() > 1 && !running_on_qemu) { |
257 |
| - clocksource_cr16.name = "cr16_unstable"; |
258 |
| - clocksource_cr16.flags = CLOCK_SOURCE_UNSTABLE; |
259 |
| - clocksource_cr16.rating = 0; |
260 |
| - } |
| 214 | + parisc_clockevent_init(); |
261 | 215 |
|
262 | 216 | /* register at clocksource framework */
|
263 |
| - clocksource_register_hz(&clocksource_cr16, |
264 |
| - 100 * PAGE0->mem_10msec); |
265 |
| - |
266 |
| - return 0; |
| 217 | + clocksource_register_hz(&clocksource_cr16, cr16_clock_freq); |
267 | 218 | }
|
268 |
| - |
269 |
| -device_initcall(init_cr16_clocksource); |
0 commit comments