1 /* $NetBSD: clock.c,v 1.2 2026/08/07 23:37:46 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2023, 2025 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1992, 1993 34 * The Regents of the University of California. All rights reserved. 35 * 36 * This software was developed by the Computer Systems Engineering group 37 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 38 * contributed to Berkeley. 39 * 40 * All advertising materials mentioning features or use of this software 41 * must display the following acknowledgement: 42 * This product includes software developed by the University of 43 * California, Lawrence Berkeley Laboratory. 44 * 45 * Redistribution and use in source and binary forms, with or without 46 * modification, are permitted provided that the following conditions 47 * are met: 48 * 1. Redistributions of source code must retain the above copyright 49 * notice, this list of conditions and the following disclaimer. 50 * 2. Redistributions in binary form must reproduce the above copyright 51 * notice, this list of conditions and the following disclaimer in the 52 * documentation and/or other materials provided with the distribution. 53 * 3. Neither the name of the University nor the names of its contributors 54 * may be used to endorse or promote products derived from this software 55 * without specific prior written permission. 56 * 57 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 58 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 59 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 60 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 61 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 62 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 63 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 64 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 65 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 66 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 67 * SUCH DAMAGE. 68 * 69 * @(#)clock.c 8.1 (Berkeley) 6/11/93 70 */ 71 72 #include "opt_fdt.h" 73 74 #include <sys/cdefs.h> 75 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.2 2026/08/07 23:37:46 thorpej Exp $"); 76 77 #include <sys/param.h> 78 #include <sys/kernel.h> 79 #include <sys/systm.h> 80 #include <sys/device.h> 81 82 #include <machine/clockvar.h> 83 #include <machine/psl.h> 84 #include <machine/bus.h> 85 86 static struct clock_vars { 87 const char *name; 88 struct clock_if *clkif; 89 struct evcnt counter; 90 } m68k_clocks[NCLOCKS] = { 91 [CLOCK_HARDCLOCK] = { 92 .name = "hardclock", 93 }, 94 [CLOCK_STATCLOCK] = { 95 .name = "statclock", 96 }, 97 }; 98 99 static TAILQ_HEAD(, clock_if) clock_candidates = 100 TAILQ_HEAD_INITIALIZER(clock_candidates); 101 102 /* 103 * Statistics clock interval and variance, in usec. Variance must be a 104 * power of two. Since this gives us an even number, not an odd number, 105 * we discard one case and compensate. That is, a variance of 1024 would 106 * give us offsets in [0..1023]. Instead, we take offsets in [1..1023]. 107 * This is symmetric about the point 512, or statvar/2, and thus averages 108 * to that value (assuming uniform random numbers). 109 */ 110 /* XXX fix comment to match value */ 111 int clock_statvar = 8192; 112 int clock_statmin; /* statclock interval - (1/2 * variance) */ 113 114 #include <dev/fdt/fdtvar.h> 115 #include <dev/ofw/openfirm.h> 116 #include <libfdt.h> 117 118 /* 119 * Given a device phandle, see if it is one of the system clock 120 * sources. 121 */ 122 static int 123 clock_from_phandle(int phandle) 124 { 125 static const char * propnames[NCLOCKS] = { 126 [CLOCK_HARDCLOCK] = "netbsd,hardclock", 127 [CLOCK_STATCLOCK] = "netbsd,statclock", 128 }; 129 const int off = fdt_path_offset(fdtbus_get_data(), "/chosen"); 130 const char *propval; 131 132 if (off < 0) { 133 return CLOCK_NONE; 134 } 135 136 for (int i = 0; i < NCLOCKS; i++) { 137 propval = fdt_getprop(fdtbus_get_data(), off, 138 propnames[i], NULL); 139 if (propval == NULL) { 140 continue; 141 } 142 if (*propval != '/') { 143 /* Alias */ 144 propval = fdt_get_alias(fdtbus_get_data(), propval); 145 if (propval == NULL) { 146 continue; 147 } 148 } 149 if (OF_finddevice(propval) == phandle) { 150 return i; 151 } 152 } 153 return CLOCK_NONE; 154 } 155 156 static int 157 clock_hardclock_intr(void *v) 158 { 159 struct clock_if * const clk = m68k_clocks[CLOCK_HARDCLOCK].clkif; 160 161 /* ACK interrupt, re-load timer. */ 162 (*clk->clk_intr)(clk); 163 164 m68k_clocks[CLOCK_HARDCLOCK].counter.ev_count++; 165 hardclock((struct clockframe *)v); 166 167 return 1; 168 } 169 170 static int 171 clock_statclock_intr(void *v) 172 { 173 struct clock_if * const clk = m68k_clocks[CLOCK_STATCLOCK].clkif; 174 175 /* ACK interrupt, re-load timer. */ 176 /* XXX add variance */ 177 (*clk->clk_intr)(clk); 178 179 m68k_clocks[CLOCK_STATCLOCK].counter.ev_count++; 180 statclock((struct clockframe *)v); 181 182 return 1; 183 } 184 185 /* 186 * Common parts of clock autoconfiguration. 187 */ 188 void 189 clock_attach(struct clock_if *clk) 190 { 191 clk->clk_which = 192 clock_from_phandle(devhandle_to_of(device_handle(clk->clk_dev))); 193 if (clk->clk_which == CLOCK_NONE) { 194 TAILQ_INSERT_TAIL(&clock_candidates, clk, clk_list); 195 } else { 196 TAILQ_INSERT_HEAD(&clock_candidates, clk, clk_list); 197 } 198 } 199 200 static const char * 201 clock_name(int which) 202 { 203 KASSERT(which >= 0); 204 KASSERT(which < NCLOCKS); 205 206 return m68k_clocks[which].name; 207 } 208 209 /* 210 * Set up the real-time and statistics clocks. Leave stathz 0 only 211 * if no alternative timer is available. 212 * 213 * The frequencies of these clocks must be an even number of microseconds. 214 */ 215 void 216 cpu_initclocks(void) 217 { 218 int i, statint = 0, minint = 0; 219 struct clock_if *clk; 220 221 /* 222 * Assign the clocks. All the clocks with pre-assigned roles 223 * are at the head of the list, and all of the unassigned clocks 224 * are at the end. 225 */ 226 TAILQ_FOREACH(clk, &clock_candidates, clk_list) { 227 if (clk->clk_which != CLOCK_NONE) { 228 KASSERT(m68k_clocks[clk->clk_which].clkif == NULL); 229 m68k_clocks[clk->clk_which].clkif = clk; 230 } else if (m68k_clocks[CLOCK_HARDCLOCK].clkif == NULL) { 231 clk->clk_which = CLOCK_HARDCLOCK; 232 m68k_clocks[CLOCK_HARDCLOCK].clkif = clk; 233 } else if (m68k_clocks[CLOCK_STATCLOCK].clkif == NULL) { 234 clk->clk_which = CLOCK_STATCLOCK; 235 m68k_clocks[CLOCK_STATCLOCK].clkif = clk; 236 } 237 } 238 239 if (m68k_clocks[CLOCK_HARDCLOCK].clkif == NULL) { 240 panic("Clock not configured"); 241 } 242 243 if (1000000 % hz) { 244 aprint_error("Cannot get %d Hz clock; using 100 Hz\n", hz); 245 hz = 100; 246 tick = 1000000 / hz; 247 } 248 249 if (m68k_clocks[CLOCK_STATCLOCK].clkif == NULL) { 250 aprint_normal("No statclock; using hardclock.\n"); 251 stathz = 0; 252 statint = 0; 253 } else if (stathz == 0) { 254 stathz = hz; 255 if (1000000 % stathz) { 256 aprint_error("Cannot get %d Hz statclock; " 257 "using 100 Hz\n", stathz); 258 stathz = 100; 259 } 260 profhz = stathz; /* always */ 261 statint = 1000000 / stathz; 262 minint = statint / 2 + 100; 263 while (clock_statvar > minint) 264 clock_statvar >>= 1; 265 clock_statmin = statint - (clock_statvar >> 1); 266 } 267 268 for (i = 0; i < NCLOCKS; i++) { 269 clk = m68k_clocks[i].clkif; 270 unsigned int freq, interval; 271 int (*func)(void *); 272 int error; 273 274 if (clk == NULL) { 275 continue; 276 } 277 switch (i) { 278 case CLOCK_HARDCLOCK: 279 freq = hz; 280 interval = tick; 281 func = clock_hardclock_intr; 282 break; 283 284 case CLOCK_STATCLOCK: 285 freq = stathz; 286 interval = statint; 287 func = clock_statclock_intr; 288 break; 289 290 default: 291 KASSERT(0); 292 } 293 294 evcnt_attach_dynamic(&m68k_clocks[i].counter, 295 EVCNT_TYPE_INTR, clk->clk_parent_evcnt, 296 device_xname(clk->clk_dev), m68k_clocks[i].name); 297 298 aprint_normal_dev(clk->clk_dev, 299 "%s: freq=%u Hz, interval=%u usec\n", 300 clock_name(i), freq, interval); 301 error = (*clk->clk_init)(clk, func); 302 if (error) { 303 panic("Failed to initialize %s for %s (error %d)", 304 device_xname(clk->clk_dev), 305 clock_name(i), error); 306 } 307 error = (*clk->clk_arm)(clk, interval); 308 if (error) { 309 panic("Failed to arm %s for %s (error %d)", 310 device_xname(clk->clk_dev), 311 clock_name(i), error); 312 } 313 } 314 } 315 316 void 317 setstatclockrate(int newhz) 318 { 319 320 /* XXX should we do something here? XXX */ 321 } 322