1 /* $OpenBSD: clock.c,v 1.87 2024/04/08 20:05:51 miod Exp $ */
2 /* $NetBSD: clock.c,v 1.41 2001/07/24 19:29:25 eeh Exp $ */
3
4 /*
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
7 * Copyright (c) 1994 Gordon W. Ross
8 * Copyright (c) 1993 Adam Glass
9 * Copyright (c) 1996 Paul Kranenburg
10 * Copyright (c) 1996
11 * The President and Fellows of Harvard College. All rights reserved.
12 *
13 * This software was developed by the Computer Systems Engineering group
14 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
15 * contributed to Berkeley.
16 *
17 * All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Harvard University.
20 * This product includes software developed by the University of
21 * California, Lawrence Berkeley Laboratory.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 *
27 * 1. Redistributions of source code must retain the above copyright
28 * notice, this list of conditions and the following disclaimer.
29 * 2. Redistributions in binary form must reproduce the above copyright
30 * notice, this list of conditions and the following disclaimer in the
31 * documentation and/or other materials provided with the distribution.
32 * 3. All advertising materials mentioning features or use of this software
33 * must display the following acknowledgement:
34 * This product includes software developed by the University of
35 * California, Berkeley and its contributors.
36 * This product includes software developed by Paul Kranenburg.
37 * This product includes software developed by Harvard University.
38 * 4. Neither the name of the University nor the names of its contributors
39 * may be used to endorse or promote products derived from this software
40 * without specific prior written permission.
41 *
42 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52 * SUCH DAMAGE.
53 *
54 * @(#)clock.c 8.1 (Berkeley) 6/11/93
55 *
56 */
57
58 /*
59 * Clock driver. This is the id prom and eeprom driver as well
60 * and includes the timer register functions too.
61 */
62
63 /* Define this for a 1/4s clock to ease debugging */
64 /* #define INTR_DEBUG */
65
66 #include <sys/param.h>
67 #include <sys/kernel.h>
68 #include <sys/device.h>
69 #include <sys/malloc.h>
70 #include <sys/systm.h>
71 #include <sys/clockintr.h>
72 #include <sys/sched.h>
73 #include <sys/stdint.h>
74 #include <sys/timetc.h>
75 #include <sys/atomic.h>
76
77 #include <machine/bus.h>
78 #include <machine/autoconf.h>
79 #include <machine/cpu.h>
80 #include <machine/idprom.h>
81
82 #include <dev/clock_subr.h>
83 #include <dev/ic/mk48txxreg.h>
84
85 #include <sparc64/dev/iommureg.h>
86 #include <sparc64/dev/sbusreg.h>
87 #include <dev/sbus/sbusvar.h>
88 #include <sparc64/dev/ebusreg.h>
89 #include <sparc64/dev/ebusvar.h>
90 #include <sparc64/dev/fhcvar.h>
91
92 extern u_int64_t cpu_clockrate;
93
94 struct clock_wenable_info {
95 bus_space_tag_t cwi_bt;
96 bus_space_handle_t cwi_bh;
97 bus_size_t cwi_size;
98 };
99
100 struct cfdriver clock_cd = {
101 NULL, "clock", DV_DULL
102 };
103
104 u_int tick_get_timecount(struct timecounter *);
105
106 struct timecounter tick_timecounter = {
107 .tc_get_timecount = tick_get_timecount,
108 .tc_counter_mask = ~0u,
109 .tc_frequency = 0,
110 .tc_name = "tick",
111 .tc_quality = 0,
112 .tc_priv = NULL,
113 .tc_user = TC_TICK,
114 };
115
116 u_int sys_tick_get_timecount(struct timecounter *);
117
118 struct timecounter sys_tick_timecounter = {
119 .tc_get_timecount = sys_tick_get_timecount,
120 .tc_counter_mask = ~0u,
121 .tc_frequency = 0,
122 .tc_name = "sys_tick",
123 .tc_quality = 1000,
124 .tc_priv = NULL,
125 .tc_user = TC_SYS_TICK,
126 };
127
128 void tick_start(void);
129 void sys_tick_start(void);
130 void stick_start(void);
131
132 int tickintr(void *);
133 int sys_tickintr(void *);
134 int stickintr(void *);
135
136 /* %TICK is at most a 63-bit counter. */
137 #define TICK_COUNT_MASK 0x7fffffffffffffff
138
139 uint64_t tick_nsec_cycle_ratio;
140 uint64_t tick_nsec_max;
141
142 void tick_rearm(void *, uint64_t);
143 void tick_trigger(void *);
144
145 const struct intrclock tick_intrclock = {
146 .ic_rearm = tick_rearm,
147 .ic_trigger = tick_trigger
148 };
149
150 /* %STICK is at most a 63-bit counter. */
151 #define STICK_COUNT_MASK 0x7fffffffffffffff
152
153 uint64_t sys_tick_nsec_cycle_ratio;
154 uint64_t sys_tick_nsec_max;
155
156 void sys_tick_rearm(void *, uint64_t);
157 void sys_tick_trigger(void *);
158
159 const struct intrclock sys_tick_intrclock = {
160 .ic_rearm = sys_tick_rearm,
161 .ic_trigger = sys_tick_trigger
162 };
163
164 void stick_rearm(void *, uint64_t);
165 void stick_trigger(void *);
166
167 const struct intrclock stick_intrclock = {
168 .ic_rearm = stick_rearm,
169 .ic_trigger = stick_trigger
170 };
171
172 void sparc64_raise_clockintr(void);
173
174 static struct intrhand level10 = {
175 .ih_fun = tickintr,
176 .ih_number = 1,
177 .ih_pil = 10,
178 .ih_name = "clock"
179 };
180
181 /*
182 * clock (eeprom) attaches at the sbus or the ebus (PCI)
183 */
184 static int clockmatch_sbus(struct device *, void *, void *);
185 static void clockattach_sbus(struct device *, struct device *, void *);
186 static int clockmatch_ebus(struct device *, void *, void *);
187 static void clockattach_ebus(struct device *, struct device *, void *);
188 static int clockmatch_fhc(struct device *, void *, void *);
189 static void clockattach_fhc(struct device *, struct device *, void *);
190 static void clockattach(int, bus_space_tag_t, bus_space_handle_t);
191
192 const struct cfattach clock_sbus_ca = {
193 sizeof(struct device), clockmatch_sbus, clockattach_sbus
194 };
195
196 const struct cfattach clock_ebus_ca = {
197 sizeof(struct device), clockmatch_ebus, clockattach_ebus
198 };
199
200 const struct cfattach clock_fhc_ca = {
201 sizeof(struct device), clockmatch_fhc, clockattach_fhc
202 };
203
204 /* Global TOD clock handle & idprom pointer */
205 extern todr_chip_handle_t todr_handle;
206 static struct idprom *idprom;
207
208 int clock_bus_wenable(struct todr_chip_handle *, int);
209 void myetheraddr(u_char *);
210 struct idprom *getidprom(void);
211
212 /*
213 * The OPENPROM calls the clock the "eeprom", so we have to have our
214 * own special match function to call it the "clock".
215 */
216 static int
clockmatch_sbus(struct device * parent,void * cf,void * aux)217 clockmatch_sbus(struct device *parent, void *cf, void *aux)
218 {
219 struct sbus_attach_args *sa = aux;
220
221 return (strcmp("eeprom", sa->sa_name) == 0);
222 }
223
224 static int
clockmatch_ebus(struct device * parent,void * cf,void * aux)225 clockmatch_ebus(struct device *parent, void *cf, void *aux)
226 {
227 struct ebus_attach_args *ea = aux;
228
229 return (strcmp("eeprom", ea->ea_name) == 0);
230 }
231
232 static int
clockmatch_fhc(struct device * parent,void * cf,void * aux)233 clockmatch_fhc(struct device *parent, void *cf, void *aux)
234 {
235 struct fhc_attach_args *fa = aux;
236
237 return (strcmp("eeprom", fa->fa_name) == 0);
238 }
239
240 /*
241 * Attach a clock (really `eeprom') to the sbus or ebus.
242 *
243 * We ignore any existing virtual address as we need to map
244 * this read-only and make it read-write only temporarily,
245 * whenever we read or write the clock chip. The clock also
246 * contains the ID ``PROM'', and I have already had the pleasure
247 * of reloading the cpu type, Ethernet address, etc, by hand from
248 * the console FORTH interpreter. I intend not to enjoy it again.
249 *
250 * the MK48T02 is 2K. the MK48T08 is 8K, and the MK48T59 is
251 * supposed to be identical to it.
252 *
253 * This is *UGLY*! We probably have multiple mappings. But I do
254 * know that this all fits inside an 8K page, so I'll just map in
255 * once.
256 *
257 * What we really need is some way to record the bus attach args
258 * so we can call *_bus_map() later with BUS_SPACE_MAP_READONLY
259 * or not to write enable/disable the device registers. This is
260 * a non-trivial operation.
261 */
262
263 static void
clockattach_sbus(struct device * parent,struct device * self,void * aux)264 clockattach_sbus(struct device *parent, struct device *self, void *aux)
265 {
266 struct sbus_attach_args *sa = aux;
267 bus_space_tag_t bt = sa->sa_bustag;
268 int sz;
269 static struct clock_wenable_info cwi;
270
271 /* use sa->sa_regs[0].size? */
272 sz = 8192;
273
274 if (sbus_bus_map(bt,
275 sa->sa_slot,
276 (sa->sa_offset & ~NBPG),
277 sz,
278 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY,
279 0, &cwi.cwi_bh) != 0) {
280 printf("%s: can't map register\n", self->dv_xname);
281 return;
282 }
283 clockattach(sa->sa_node, bt, cwi.cwi_bh);
284
285 /* Save info for the clock wenable call. */
286 cwi.cwi_bt = bt;
287 cwi.cwi_size = sz;
288 todr_handle->bus_cookie = &cwi;
289 todr_handle->todr_setwen = clock_bus_wenable;
290 }
291
292 /*
293 * Write en/dis-able clock registers. We coordinate so that several
294 * writers can run simultaneously.
295 * XXX There is still a race here. The page change and the "writers"
296 * change are not atomic.
297 */
298 int
clock_bus_wenable(struct todr_chip_handle * handle,int onoff)299 clock_bus_wenable(struct todr_chip_handle *handle, int onoff)
300 {
301 int s, err = 0;
302 int prot; /* nonzero => change prot */
303 volatile static int writers;
304 struct clock_wenable_info *cwi = handle->bus_cookie;
305
306 s = splhigh();
307 if (onoff)
308 prot = writers++ == 0 ? 1 : 0;
309 else
310 prot = --writers == 0 ? 1 : 0;
311 splx(s);
312
313 if (prot) {
314 err = bus_space_protect(cwi->cwi_bt, cwi->cwi_bh, cwi->cwi_size,
315 onoff ? 0 : BUS_SPACE_MAP_READONLY);
316 if (err)
317 printf("clock_wenable_info: WARNING -- cannot %s "
318 "page protection\n", onoff ? "disable" : "enable");
319 }
320 return (err);
321 }
322
323 static void
clockattach_ebus(struct device * parent,struct device * self,void * aux)324 clockattach_ebus(struct device *parent, struct device *self, void *aux)
325 {
326 struct ebus_attach_args *ea = aux;
327 bus_space_tag_t bt;
328 int sz;
329 static struct clock_wenable_info cwi;
330
331 /* hard code to 8K? */
332 sz = ea->ea_regs[0].size;
333
334 if (ebus_bus_map(ea->ea_iotag, 0,
335 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), sz, 0, 0, &cwi.cwi_bh) == 0) {
336 bt = ea->ea_iotag;
337 } else if (ebus_bus_map(ea->ea_memtag, 0,
338 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), sz,
339 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY,
340 0, &cwi.cwi_bh) == 0) {
341 bt = ea->ea_memtag;
342 } else {
343 printf("%s: can't map register\n", self->dv_xname);
344 return;
345 }
346
347 clockattach(ea->ea_node, bt, cwi.cwi_bh);
348
349 /* Save info for the clock wenable call. */
350 cwi.cwi_bt = bt;
351 cwi.cwi_size = sz;
352 todr_handle->bus_cookie = &cwi;
353 todr_handle->todr_setwen = (ea->ea_memtag == bt) ?
354 clock_bus_wenable : NULL;
355 }
356
357 static void
clockattach_fhc(struct device * parent,struct device * self,void * aux)358 clockattach_fhc(struct device *parent, struct device *self, void *aux)
359 {
360 struct fhc_attach_args *fa = aux;
361 bus_space_tag_t bt = fa->fa_bustag;
362 int sz;
363 static struct clock_wenable_info cwi;
364
365 /* use sa->sa_regs[0].size? */
366 sz = 8192;
367
368 if (fhc_bus_map(bt, fa->fa_reg[0].fbr_slot,
369 (fa->fa_reg[0].fbr_offset & ~NBPG), fa->fa_reg[0].fbr_size,
370 BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY, &cwi.cwi_bh) != 0) {
371 printf("%s: can't map register\n", self->dv_xname);
372 return;
373 }
374
375 clockattach(fa->fa_node, bt, cwi.cwi_bh);
376
377 /* Save info for the clock wenable call. */
378 cwi.cwi_bt = bt;
379 cwi.cwi_size = sz;
380 todr_handle->bus_cookie = &cwi;
381 todr_handle->todr_setwen = clock_bus_wenable;
382 }
383
384 static void
clockattach(int node,bus_space_tag_t bt,bus_space_handle_t bh)385 clockattach(int node, bus_space_tag_t bt, bus_space_handle_t bh)
386 {
387 char *model;
388 struct idprom *idp;
389 int h;
390
391 model = getpropstring(node, "model");
392
393 #ifdef DIAGNOSTIC
394 if (model == NULL)
395 panic("clockattach: no model property");
396 #endif
397
398 /* Our TOD clock year 0 is 1968 */
399 if ((todr_handle = mk48txx_attach(bt, bh, model, 1968)) == NULL)
400 panic("Can't attach %s tod clock", model);
401
402 #define IDPROM_OFFSET (8*1024 - 40) /* XXX - get nvram sz from driver */
403 if (idprom == NULL) {
404 idp = getidprom();
405 if (idp == NULL)
406 idp = (struct idprom *)(bus_space_vaddr(bt, bh) +
407 IDPROM_OFFSET);
408 idprom = idp;
409 } else
410 idp = idprom;
411 h = idp->id_machine << 24;
412 h |= idp->id_hostid[0] << 16;
413 h |= idp->id_hostid[1] << 8;
414 h |= idp->id_hostid[2];
415 hostid = h;
416 printf("\n");
417 }
418
419 struct idprom *
getidprom(void)420 getidprom(void)
421 {
422 struct idprom *idp = NULL;
423 int node, n;
424
425 node = findroot();
426 if (getprop(node, "idprom", sizeof(*idp), &n, (void **)&idp) != 0)
427 return (NULL);
428 if (n != 1) {
429 free(idp, M_DEVBUF, 0);
430 return (NULL);
431 }
432 return (idp);
433 }
434
435 /*
436 * XXX this belongs elsewhere
437 */
438 void
myetheraddr(u_char * cp)439 myetheraddr(u_char *cp)
440 {
441 struct idprom *idp;
442
443 if ((idp = idprom) == NULL) {
444 int node, n;
445
446 node = findroot();
447 if (getprop(node, "idprom", sizeof *idp, &n, (void **)&idp) ||
448 n != 1) {
449 printf("\nmyetheraddr: clock not setup yet, "
450 "and no idprom property in /\n");
451 return;
452 }
453 }
454
455 cp[0] = idp->id_ether[0];
456 cp[1] = idp->id_ether[1];
457 cp[2] = idp->id_ether[2];
458 cp[3] = idp->id_ether[3];
459 cp[4] = idp->id_ether[4];
460 cp[5] = idp->id_ether[5];
461 if (idprom == NULL)
462 free(idp, M_DEVBUF, 0);
463 }
464
465 /*
466 * Set up the real-time and statistics clocks.
467 *
468 * The frequencies of these clocks must be an even number of microseconds.
469 */
470 void
cpu_initclocks(void)471 cpu_initclocks(void)
472 {
473 u_int sys_tick_rate;
474 int impl = 0;
475
476 if (1000000 % hz) {
477 printf("cannot get %d Hz clock; using 100 Hz\n", hz);
478 hz = 100;
479 tick = 1000000 / hz;
480 tick_nsec = 1000000000 / hz;
481 }
482
483 stathz = hz;
484 profhz = stathz * 10;
485 statclock_is_randomized = 1;
486
487 /* Make sure we have a sane cpu_clockrate -- we'll need it */
488 if (!cpu_clockrate)
489 /* Default to 200MHz clock XXXXX */
490 cpu_clockrate = 200000000;
491
492 tick_timecounter.tc_frequency = cpu_clockrate;
493 tc_init(&tick_timecounter);
494
495 /*
496 * UltraSPARC IIe processors do have a STICK register, but it
497 * lives on the PCI host bridge and isn't accessible through
498 * ASR24.
499 */
500 if (CPU_ISSUN4U || CPU_ISSUN4US)
501 impl = (getver() & VER_IMPL) >> VER_IMPL_SHIFT;
502
503 sys_tick_rate = getpropint(findroot(), "stick-frequency", 0);
504 if (sys_tick_rate > 0 && impl != IMPL_HUMMINGBIRD) {
505 sys_tick_timecounter.tc_frequency = sys_tick_rate;
506 tc_init(&sys_tick_timecounter);
507 }
508
509 struct cpu_info *ci;
510
511 /*
512 * Establish a level 10 interrupt handler
513 *
514 * We will have a conflict with the softint handler,
515 * so we set the ih_number to 1.
516 */
517 intr_establish(&level10);
518 evcount_percpu(&level10.ih_count);
519
520 if (sys_tick_rate > 0) {
521 sys_tick_nsec_cycle_ratio =
522 sys_tick_rate * (1ULL << 32) / 1000000000;
523 sys_tick_nsec_max = UINT64_MAX / sys_tick_nsec_cycle_ratio;
524 if (impl == IMPL_HUMMINGBIRD) {
525 level10.ih_fun = stickintr;
526 cpu_start_clock = stick_start;
527 } else {
528 level10.ih_fun = sys_tickintr;
529 cpu_start_clock = sys_tick_start;
530 }
531 } else {
532 tick_nsec_cycle_ratio =
533 cpu_clockrate * (1ULL << 32) / 1000000000;
534 tick_nsec_max = UINT64_MAX / tick_nsec_cycle_ratio;
535 level10.ih_fun = tickintr;
536 cpu_start_clock = tick_start;
537 }
538
539 for (ci = cpus; ci != NULL; ci = ci->ci_next)
540 memcpy(&ci->ci_tickintr, &level10, sizeof(level10));
541 }
542
543 void
cpu_startclock(void)544 cpu_startclock(void)
545 {
546 cpu_start_clock();
547 }
548
549 void
setstatclockrate(int newhz)550 setstatclockrate(int newhz)
551 {
552 }
553
554 /*
555 * Level 10 (clock) interrupts. If we are using the FORTH PROM for
556 * console input, we need to check for that here as well, and generate
557 * a software interrupt to read it.
558 *
559 * %tick is really a level-14 interrupt. We need to remap this in
560 * locore.s to a level 10.
561 */
562 int
tickintr(void * cap)563 tickintr(void *cap)
564 {
565 clockintr_dispatch(cap);
566 evcount_inc(&level10.ih_count);
567 return (1);
568 }
569
570 int
sys_tickintr(void * cap)571 sys_tickintr(void *cap)
572 {
573 clockintr_dispatch(cap);
574 evcount_inc(&level10.ih_count);
575 return (1);
576 }
577
578 int
stickintr(void * cap)579 stickintr(void *cap)
580 {
581 clockintr_dispatch(cap);
582 evcount_inc(&level10.ih_count);
583 return (1);
584 }
585
586 void
tick_start(void)587 tick_start(void)
588 {
589 tick_enable();
590
591 clockintr_cpu_init(&tick_intrclock);
592 clockintr_trigger();
593 }
594
595 void
tick_rearm(void * unused,uint64_t nsecs)596 tick_rearm(void *unused, uint64_t nsecs)
597 {
598 uint64_t s, t0;
599 uint32_t cycles;
600
601 if (nsecs > tick_nsec_max)
602 nsecs = tick_nsec_max;
603 cycles = (nsecs * tick_nsec_cycle_ratio) >> 32;
604
605 s = intr_disable();
606 t0 = tick();
607 tickcmpr_set((t0 + cycles) & TICK_COUNT_MASK);
608 if (cycles <= ((tick() - t0) & TICK_COUNT_MASK))
609 sparc64_raise_clockintr();
610 intr_restore(s);
611 }
612
613 void
tick_trigger(void * unused)614 tick_trigger(void *unused)
615 {
616 sparc64_raise_clockintr();
617 }
618
619 void
sys_tick_start(void)620 sys_tick_start(void)
621 {
622 if (CPU_ISSUN4U || CPU_ISSUN4US) {
623 tick_enable();
624 sys_tick_enable();
625 }
626
627 clockintr_cpu_init(&sys_tick_intrclock);
628 clockintr_trigger();
629 }
630
631 void
sys_tick_rearm(void * unused,uint64_t nsecs)632 sys_tick_rearm(void *unused, uint64_t nsecs)
633 {
634 uint64_t s, t0;
635 uint32_t cycles;
636
637 if (nsecs > sys_tick_nsec_max)
638 nsecs = sys_tick_nsec_max;
639 cycles = (nsecs * sys_tick_nsec_cycle_ratio) >> 32;
640
641 s = intr_disable();
642 t0 = sys_tick();
643 sys_tickcmpr_set((t0 + cycles) & STICK_COUNT_MASK);
644 if (cycles <= ((sys_tick() - t0) & STICK_COUNT_MASK))
645 sparc64_raise_clockintr();
646 intr_restore(s);
647 }
648
649 void
sys_tick_trigger(void * unused)650 sys_tick_trigger(void *unused)
651 {
652 sparc64_raise_clockintr();
653 }
654
655 void
stick_start(void)656 stick_start(void)
657 {
658 tick_enable();
659
660 clockintr_cpu_init(&stick_intrclock);
661 clockintr_trigger();
662 }
663
664 void
stick_rearm(void * unused,uint64_t nsecs)665 stick_rearm(void *unused, uint64_t nsecs)
666 {
667 uint64_t s, t0;
668 uint32_t cycles;
669
670 if (nsecs > sys_tick_nsec_max)
671 nsecs = sys_tick_nsec_max;
672 cycles = (nsecs * sys_tick_nsec_cycle_ratio) >> 32;
673
674 s = intr_disable();
675 t0 = stick();
676 stickcmpr_set((t0 + cycles) & STICK_COUNT_MASK);
677 if (cycles <= ((stick() - t0) & STICK_COUNT_MASK))
678 sparc64_raise_clockintr();
679 intr_restore(s);
680 }
681
682 void
stick_trigger(void * unused)683 stick_trigger(void *unused)
684 {
685 sparc64_raise_clockintr();
686 }
687
688 u_int
tick_get_timecount(struct timecounter * tc)689 tick_get_timecount(struct timecounter *tc)
690 {
691 u_int64_t tick;
692
693 __asm volatile("rd %%tick, %0" : "=r" (tick));
694
695 return (tick & ~0u);
696 }
697
698 u_int
sys_tick_get_timecount(struct timecounter * tc)699 sys_tick_get_timecount(struct timecounter *tc)
700 {
701 u_int64_t tick;
702
703 __asm volatile("rd %%sys_tick, %0" : "=r" (tick));
704
705 return (tick & ~0u);
706 }
707
708 void
sparc64_raise_clockintr(void)709 sparc64_raise_clockintr(void)
710 {
711 send_softint(PIL_CLOCK, &curcpu()->ci_tickintr);
712 }
713