1 /* $OpenBSD: clock.c,v 1.14 2024/01/27 12:05:40 kettenis Exp $ */
2
3 /*
4 * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
5 * Copyright (c) 2003 Dale Rahn <drahn@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <sys/param.h>
21 #include <sys/kernel.h>
22 #include <sys/systm.h>
23 #include <sys/clockintr.h>
24 #include <sys/evcount.h>
25 #include <sys/stdint.h>
26 #include <sys/timetc.h>
27
28 #include <machine/cpufunc.h>
29 #include <machine/sbi.h>
30
31 #include <riscv64/dev/riscv_cpu_intc.h>
32
33 extern uint64_t tb_freq; /* machdep.c */
34 uint64_t timer_nsec_max;
35 uint64_t timer_nsec_cycle_ratio;
36
37 struct evcount clock_count;
38
39 void timer_startclock(void);
40 void timer_rearm(void *, uint64_t);
41 void timer_trigger(void *);
42
43 const struct intrclock timer_intrclock = {
44 .ic_rearm = timer_rearm,
45 .ic_trigger = timer_trigger
46 };
47
48 u_int tb_get_timecount(struct timecounter *);
49
50 static struct timecounter tb_timecounter = {
51 .tc_get_timecount = tb_get_timecount,
52 .tc_counter_mask = 0xffffffff,
53 .tc_frequency = 0,
54 .tc_name = "tb",
55 .tc_quality = 0,
56 .tc_priv = NULL,
57 .tc_user = TC_TB,
58 };
59
60 void (*cpu_startclock_fcn)(void) = timer_startclock;
61 int clock_intr(void *);
62
63 void
timer_rearm(void * unused,uint64_t nsecs)64 timer_rearm(void *unused, uint64_t nsecs)
65 {
66 uint32_t cycles;
67
68 if (nsecs > timer_nsec_max)
69 nsecs = timer_nsec_max;
70 cycles = (nsecs * timer_nsec_cycle_ratio) >> 32;
71 sbi_set_timer(rdtime() + cycles);
72 }
73
74 void
timer_trigger(void * unused)75 timer_trigger(void *unused)
76 {
77 sbi_set_timer(0);
78 }
79
80 u_int
tb_get_timecount(struct timecounter * tc)81 tb_get_timecount(struct timecounter *tc)
82 {
83 return rdtime();
84 }
85
86 void
cpu_initclocks(void)87 cpu_initclocks(void)
88 {
89 tb_timecounter.tc_frequency = tb_freq;
90 tc_init(&tb_timecounter);
91
92 stathz = hz;
93 profhz = stathz * 10;
94 statclock_is_randomized = 1;
95
96 if (cpu_startclock_fcn == timer_startclock) {
97 timer_nsec_cycle_ratio = tb_freq * (1ULL << 32) / 1000000000;
98 timer_nsec_max = UINT64_MAX / timer_nsec_cycle_ratio;
99
100 riscv_intc_intr_establish(IRQ_TIMER_SUPERVISOR, 0,
101 clock_intr, NULL, NULL);
102
103 evcount_attach(&clock_count, "clock", NULL);
104 evcount_percpu(&clock_count);
105 }
106 }
107
108 void
timer_startclock(void)109 timer_startclock(void)
110 {
111 clockintr_cpu_init(&timer_intrclock);
112 clockintr_trigger();
113 csr_set(sie, SIE_STIE);
114 }
115
116 void
cpu_startclock(void)117 cpu_startclock(void)
118 {
119 cpu_startclock_fcn();
120 }
121
122 int
clock_intr(void * frame)123 clock_intr(void *frame)
124 {
125 struct cpu_info *ci = curcpu();
126 int s;
127
128 sbi_set_timer(UINT64_MAX); /* clear timer interrupt */
129
130 /*
131 * If the clock interrupt is masked, defer all clock interrupt
132 * work until the clock interrupt is unmasked from splx(9).
133 */
134 if (ci->ci_cpl >= IPL_CLOCK) {
135 ci->ci_timer_deferred = 1;
136 return 0;
137 }
138 ci->ci_timer_deferred = 0;
139
140 s = splclock();
141 intr_enable();
142 clockintr_dispatch(frame);
143 intr_disable();
144 splx(s);
145
146 evcount_inc(&clock_count);
147
148 return 0;
149 }
150
151 void
setstatclockrate(int newhz)152 setstatclockrate(int newhz)
153 {
154 }
155
156 void
delay(u_int us)157 delay(u_int us)
158 {
159 uint64_t tb;
160
161 tb = rdtime();
162 tb += (us * tb_freq + 999999) / 1000000;
163 while (tb > rdtime())
164 continue;
165 }
166