xref: /openbsd-src/sys/arch/hppa/dev/clock.c (revision 3374c67d44f9b75b98444cbf63020f777792342e)
1 /*	$OpenBSD: clock.c,v 1.33 2022/12/06 00:40:09 cheloha Exp $	*/
2 
3 /*
4  * Copyright (c) 1998-2003 Michael Shalayeff
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
25  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26  * THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/clockintr.h>
33 #include <sys/stdint.h>
34 #include <sys/timetc.h>
35 
36 #include <dev/clock_subr.h>
37 
38 #include <machine/pdc.h>
39 #include <machine/iomod.h>
40 #include <machine/psl.h>
41 #include <machine/intr.h>
42 #include <machine/reg.h>
43 #include <machine/cpufunc.h>
44 #include <machine/autoconf.h>
45 
46 uint64_t itmr_nsec_cycle_ratio;
47 uint64_t itmr_nsec_max;
48 
49 u_int	itmr_get_timecount(struct timecounter *);
50 int	itmr_intr(void *);
51 void	itmr_rearm(void *, uint64_t);
52 void	itmr_trigger(void);
53 void	itmr_trigger_masked(void);
54 void	itmr_trigger_wrapper(void *);
55 
56 struct timecounter itmr_timecounter = {
57 	.tc_get_timecount = itmr_get_timecount,
58 	.tc_poll_pps = NULL,
59 	.tc_counter_mask = 0xffffffff,
60 	.tc_frequency = 0,
61 	.tc_name = "itmr",
62 	.tc_quality = 0,
63 	.tc_priv = NULL,
64 	.tc_user = 0,
65 };
66 
67 const struct intrclock itmr_intrclock = {
68 	.ic_rearm = itmr_rearm,
69 	.ic_trigger = itmr_trigger_wrapper
70 };
71 
72 extern todr_chip_handle_t todr_handle;
73 struct todr_chip_handle pdc_todr;
74 
75 int
76 pdc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
77 {
78 	struct pdc_tod tod PDC_ALIGNMENT;
79 	int error;
80 
81 	if ((error = pdc_call((iodcio_t)pdc, 1, PDC_TOD, PDC_TOD_READ,
82 	    &tod, 0, 0, 0, 0, 0))) {
83 		printf("clock: failed to fetch (%d)\n", error);
84 		return EIO;
85 	}
86 
87 	tv->tv_sec = tod.sec;
88 	tv->tv_usec = tod.usec;
89 	return 0;
90 }
91 
92 int
93 pdc_settime(struct todr_chip_handle *handle, struct timeval *tv)
94 {
95 	int error;
96 
97 	if ((error = pdc_call((iodcio_t)pdc, 1, PDC_TOD, PDC_TOD_WRITE,
98 	    tv->tv_sec, tv->tv_usec))) {
99 		printf("clock: failed to save (%d)\n", error);
100 		return EIO;
101 	}
102 
103 	return 0;
104 }
105 
106 void
107 cpu_initclocks(void)
108 {
109 	uint64_t itmr_freq = PAGE0->mem_10msec * 100;
110 
111 	pdc_todr.todr_gettime = pdc_gettime;
112 	pdc_todr.todr_settime = pdc_settime;
113 	todr_handle = &pdc_todr;
114 
115 	itmr_timecounter.tc_frequency = itmr_freq;
116 	tc_init(&itmr_timecounter);
117 
118 	stathz = hz;
119 	profhz = stathz * 10;
120 	clockintr_init(CL_RNDSTAT);
121 
122 	itmr_nsec_cycle_ratio = itmr_freq * (1ULL << 32) / 1000000000;
123 	itmr_nsec_max = UINT64_MAX / itmr_nsec_cycle_ratio;
124 
125 	cpu_startclock();
126 }
127 
128 void
129 cpu_startclock(void)
130 {
131 	clockintr_cpu_init(&itmr_intrclock);
132 	clockintr_trigger();
133 }
134 
135 int
136 itmr_intr(void *v)
137 {
138 	clockintr_dispatch(v);
139 	return (1);
140 }
141 
142 void
143 setstatclockrate(int newhz)
144 {
145 	clockintr_setstatclockrate(newhz);
146 }
147 
148 u_int
149 itmr_get_timecount(struct timecounter *tc)
150 {
151 	u_long __itmr;
152 
153 	mfctl(CR_ITMR, __itmr);
154 	return (__itmr);
155 }
156 
157 /*
158  * Program the next clock interrupt, making sure it will
159  * indeed happen in the future.  This is done with interrupts
160  * disabled to avoid a possible race.
161  */
162 void
163 itmr_rearm(void *unused, uint64_t nsecs)
164 {
165 	uint32_t cycles, t0, t1, target;
166 	register_t eiem, eirr;
167 
168 	if (nsecs > itmr_nsec_max)
169 		nsecs = itmr_nsec_max;
170 	cycles = (nsecs * itmr_nsec_cycle_ratio) >> 32;
171 
172 	eiem = hppa_intr_disable();
173 	mfctl(CR_ITMR, t0);
174 	target = t0 + cycles;
175 	mtctl(target, CR_ITMR);
176 	mfctl(CR_ITMR, t1);
177 
178 	/*
179 	 * If the interrupt isn't already pending we need to check if
180 	 * we missed.  In general, we are checking whether ITMR had
181 	 * already passed the target value when we wrote the register.
182 	 * There are two cases.
183 	 *
184 	 * 1. If (t0 + cycles) did not overflow, we want t1 to be between
185 	 *    t0 and target.  If t0 <= t1 < target, we didn't miss.
186 	 *
187 	 * 2. If (t0 + cycles) overflowed, either t0 <= t1 or t1 < target
188 	 *    are sufficient to show we didn't miss.
189 	 *
190 	 * Only try once.  Fall back to itmr_trigger_masked() if we miss.
191 	 */
192 	mfctl(CR_EIRR, eirr);
193 	if (!ISSET(eirr, 1U << 31)) {
194 		if (t0 <= target) {
195 			if (target <= t1 || t1 < t0)
196 				itmr_trigger_masked();
197 		} else {
198 			if (target <= t1 && t1 < t0)
199 				itmr_trigger_masked();
200 		}
201 	}
202 	hppa_intr_enable(eiem);
203 }
204 
205 void
206 itmr_trigger(void)
207 {
208 	register_t eiem;
209 
210 	eiem = hppa_intr_disable();
211 	itmr_trigger_masked();
212 	hppa_intr_enable(eiem);
213 }
214 
215 /* Trigger our own ITMR interrupt by setting EIR{0}. */
216 void
217 itmr_trigger_masked(void)
218 {
219 	struct iomod *cpu = (struct iomod *)curcpu()->ci_hpa;
220 
221 	cpu->io_eir = 0;
222 	__asm volatile ("sync" ::: "memory");
223 }
224 
225 void
226 itmr_trigger_wrapper(void *unused)
227 {
228 	itmr_trigger();
229 }
230