xref: /openbsd-src/sys/arch/riscv64/riscv64/clock.c (revision eda78419691a0f30e20eb4a2878e56aa339650a3)
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