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