1*8a2bbcc7Skettenis /* $OpenBSD: clock.c,v 1.44 2008/07/15 22:49:01 kettenis Exp $ */ 2284584b0Sjason /* $NetBSD: clock.c,v 1.41 2001/07/24 19:29:25 eeh Exp $ */ 3284584b0Sjason 4284584b0Sjason /* 5284584b0Sjason * Copyright (c) 1992, 1993 6284584b0Sjason * The Regents of the University of California. All rights reserved. 7284584b0Sjason * Copyright (c) 1994 Gordon W. Ross 8284584b0Sjason * Copyright (c) 1993 Adam Glass 9284584b0Sjason * Copyright (c) 1996 Paul Kranenburg 10284584b0Sjason * Copyright (c) 1996 11284584b0Sjason * The President and Fellows of Harvard College. All rights reserved. 12284584b0Sjason * 13284584b0Sjason * This software was developed by the Computer Systems Engineering group 14284584b0Sjason * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 15284584b0Sjason * contributed to Berkeley. 16284584b0Sjason * 17284584b0Sjason * All advertising materials mentioning features or use of this software 18284584b0Sjason * must display the following acknowledgement: 19284584b0Sjason * This product includes software developed by Harvard University. 20284584b0Sjason * This product includes software developed by the University of 21284584b0Sjason * California, Lawrence Berkeley Laboratory. 22284584b0Sjason * 23284584b0Sjason * Redistribution and use in source and binary forms, with or without 24284584b0Sjason * modification, are permitted provided that the following conditions 25284584b0Sjason * are met: 26284584b0Sjason * 27284584b0Sjason * 1. Redistributions of source code must retain the above copyright 28284584b0Sjason * notice, this list of conditions and the following disclaimer. 29284584b0Sjason * 2. Redistributions in binary form must reproduce the above copyright 30284584b0Sjason * notice, this list of conditions and the following disclaimer in the 31284584b0Sjason * documentation and/or other materials provided with the distribution. 32284584b0Sjason * 3. All advertising materials mentioning features or use of this software 33284584b0Sjason * must display the following acknowledgement: 34284584b0Sjason * This product includes software developed by the University of 35284584b0Sjason * California, Berkeley and its contributors. 36284584b0Sjason * This product includes software developed by Paul Kranenburg. 37284584b0Sjason * This product includes software developed by Harvard University. 38284584b0Sjason * 4. Neither the name of the University nor the names of its contributors 39284584b0Sjason * may be used to endorse or promote products derived from this software 40284584b0Sjason * without specific prior written permission. 41284584b0Sjason * 42284584b0Sjason * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 43284584b0Sjason * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 44284584b0Sjason * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 45284584b0Sjason * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 46284584b0Sjason * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 47284584b0Sjason * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 48284584b0Sjason * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 49284584b0Sjason * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 50284584b0Sjason * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 51284584b0Sjason * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52284584b0Sjason * SUCH DAMAGE. 53284584b0Sjason * 54284584b0Sjason * @(#)clock.c 8.1 (Berkeley) 6/11/93 55284584b0Sjason * 56284584b0Sjason */ 57284584b0Sjason 58284584b0Sjason /* 59284584b0Sjason * Clock driver. This is the id prom and eeprom driver as well 60284584b0Sjason * and includes the timer register functions too. 61284584b0Sjason */ 62284584b0Sjason 63284584b0Sjason /* Define this for a 1/4s clock to ease debugging */ 64284584b0Sjason /* #define INTR_DEBUG */ 65284584b0Sjason 66284584b0Sjason #include <sys/param.h> 67284584b0Sjason #include <sys/kernel.h> 68284584b0Sjason #include <sys/device.h> 69284584b0Sjason #include <sys/proc.h> 70284584b0Sjason #include <sys/resourcevar.h> 71284584b0Sjason #include <sys/malloc.h> 72284584b0Sjason #include <sys/systm.h> 73284584b0Sjason #ifdef GPROF 74284584b0Sjason #include <sys/gmon.h> 75284584b0Sjason #endif 76284584b0Sjason #include <sys/sched.h> 7705a26ea5Sart #include <sys/timetc.h> 78284584b0Sjason 79284584b0Sjason #include <uvm/uvm_extern.h> 80284584b0Sjason 81284584b0Sjason #include <machine/bus.h> 82284584b0Sjason #include <machine/autoconf.h> 83284584b0Sjason #include <machine/cpu.h> 84284584b0Sjason #include <machine/idprom.h> 85284584b0Sjason 86284584b0Sjason #include <dev/clock_subr.h> 87284584b0Sjason #include <dev/ic/mk48txxreg.h> 88284584b0Sjason 89284584b0Sjason #include <sparc64/sparc64/intreg.h> 90284584b0Sjason #include <sparc64/sparc64/timerreg.h> 91284584b0Sjason #include <sparc64/dev/iommureg.h> 92284584b0Sjason #include <sparc64/dev/sbusreg.h> 93284584b0Sjason #include <dev/sbus/sbusvar.h> 94284584b0Sjason #include <sparc64/dev/ebusreg.h> 95284584b0Sjason #include <sparc64/dev/ebusvar.h> 961d2e6c84Sjason #include <sparc64/dev/fhcvar.h> 97284584b0Sjason 98284584b0Sjason extern u_int64_t cpu_clockrate; 99284584b0Sjason 100eb79e960Shenric struct clock_wenable_info { 101eb79e960Shenric bus_space_tag_t cwi_bt; 102eb79e960Shenric bus_space_handle_t cwi_bh; 103eb79e960Shenric bus_size_t cwi_size; 104eb79e960Shenric }; 105eb79e960Shenric 106284584b0Sjason struct cfdriver clock_cd = { 107284584b0Sjason NULL, "clock", DV_DULL 108284584b0Sjason }; 109284584b0Sjason 11005a26ea5Sart u_int tick_get_timecount(struct timecounter *); 11105a26ea5Sart 11205a26ea5Sart struct timecounter tick_timecounter = { 11305a26ea5Sart tick_get_timecount, NULL, ~0u, 0, "tick", 0, NULL 11405a26ea5Sart }; 11505a26ea5Sart 116*8a2bbcc7Skettenis u_int sys_tick_get_timecount(struct timecounter *); 117*8a2bbcc7Skettenis 118*8a2bbcc7Skettenis struct timecounter sys_tick_timecounter = { 119*8a2bbcc7Skettenis sys_tick_get_timecount, NULL, ~0u, 0, "sys_tick", 1000, NULL 120*8a2bbcc7Skettenis }; 121*8a2bbcc7Skettenis 122284584b0Sjason /* 123284584b0Sjason * Statistics clock interval and variance, in usec. Variance must be a 124284584b0Sjason * power of two. Since this gives us an even number, not an odd number, 125284584b0Sjason * we discard one case and compensate. That is, a variance of 1024 would 126284584b0Sjason * give us offsets in [0..1023]. Instead, we take offsets in [1..1023]. 127284584b0Sjason * This is symmetric about the point 512, or statvar/2, and thus averages 128284584b0Sjason * to that value (assuming uniform random numbers). 129284584b0Sjason */ 130284584b0Sjason /* XXX fix comment to match value */ 131284584b0Sjason int statvar = 8192; 132284584b0Sjason int statmin; /* statclock interval - 1/2*variance */ 133284584b0Sjason 13458c73160Skettenis static long tick_increment; 135c4071fd1Smillert int schedintr(void *); 136284584b0Sjason 137284584b0Sjason static struct intrhand level10 = { clockintr }; 138284584b0Sjason static struct intrhand level0 = { tickintr }; 139284584b0Sjason static struct intrhand level14 = { statintr }; 140284584b0Sjason static struct intrhand schedint = { schedintr }; 141284584b0Sjason 142284584b0Sjason /* 143284584b0Sjason * clock (eeprom) attaches at the sbus or the ebus (PCI) 144284584b0Sjason */ 145c4071fd1Smillert static int clockmatch_sbus(struct device *, void *, void *); 146c4071fd1Smillert static void clockattach_sbus(struct device *, struct device *, void *); 147c4071fd1Smillert static int clockmatch_ebus(struct device *, void *, void *); 148c4071fd1Smillert static void clockattach_ebus(struct device *, struct device *, void *); 1491d2e6c84Sjason static int clockmatch_fhc(struct device *, void *, void *); 1501d2e6c84Sjason static void clockattach_fhc(struct device *, struct device *, void *); 151c4071fd1Smillert static void clockattach(int, bus_space_tag_t, bus_space_handle_t); 152284584b0Sjason 153284584b0Sjason struct cfattach clock_sbus_ca = { 154284584b0Sjason sizeof(struct device), clockmatch_sbus, clockattach_sbus 155284584b0Sjason }; 156284584b0Sjason 157284584b0Sjason struct cfattach clock_ebus_ca = { 158284584b0Sjason sizeof(struct device), clockmatch_ebus, clockattach_ebus 159284584b0Sjason }; 160284584b0Sjason 1611d2e6c84Sjason struct cfattach clock_fhc_ca = { 1621d2e6c84Sjason sizeof(struct device), clockmatch_fhc, clockattach_fhc 1631d2e6c84Sjason }; 1641d2e6c84Sjason 165284584b0Sjason /* Global TOD clock handle & idprom pointer */ 166d5e78757Skettenis todr_chip_handle_t todr_handle = NULL; 167284584b0Sjason static struct idprom *idprom; 168284584b0Sjason 169c4071fd1Smillert static int timermatch(struct device *, void *, void *); 170c4071fd1Smillert static void timerattach(struct device *, struct device *, void *); 171284584b0Sjason 172284584b0Sjason struct timerreg_4u timerreg_4u; /* XXX - need more cleanup */ 173284584b0Sjason 174284584b0Sjason struct cfattach timer_ca = { 175284584b0Sjason sizeof(struct device), timermatch, timerattach 176284584b0Sjason }; 177284584b0Sjason 178284584b0Sjason struct cfdriver timer_cd = { 179284584b0Sjason NULL, "timer", DV_DULL 180284584b0Sjason }; 181284584b0Sjason 182eb79e960Shenric int clock_bus_wenable(struct todr_chip_handle *, int); 183284584b0Sjason struct chiptime; 184c4071fd1Smillert void myetheraddr(u_char *); 185eba3994dSjason struct idprom *getidprom(void); 186c4071fd1Smillert int chiptotime(int, int, int, int, int, int); 187c4071fd1Smillert void timetochip(struct chiptime *); 188c4071fd1Smillert void stopcounter(struct timer_4u *); 189284584b0Sjason 190284584b0Sjason int timerblurb = 10; /* Guess a value; used before clock is attached */ 191284584b0Sjason 192284584b0Sjason /* 193284584b0Sjason * The OPENPROM calls the clock the "eeprom", so we have to have our 194284584b0Sjason * own special match function to call it the "clock". 195284584b0Sjason */ 196284584b0Sjason static int 197284584b0Sjason clockmatch_sbus(parent, cf, aux) 198284584b0Sjason struct device *parent; 199284584b0Sjason void *cf; 200284584b0Sjason void *aux; 201284584b0Sjason { 202284584b0Sjason struct sbus_attach_args *sa = aux; 203284584b0Sjason 204284584b0Sjason return (strcmp("eeprom", sa->sa_name) == 0); 205284584b0Sjason } 206284584b0Sjason 207284584b0Sjason static int 208284584b0Sjason clockmatch_ebus(parent, cf, aux) 209284584b0Sjason struct device *parent; 210284584b0Sjason void *cf; 211284584b0Sjason void *aux; 212284584b0Sjason { 213284584b0Sjason struct ebus_attach_args *ea = aux; 214284584b0Sjason 215284584b0Sjason return (strcmp("eeprom", ea->ea_name) == 0); 216284584b0Sjason } 217284584b0Sjason 218284584b0Sjason static int 2191d2e6c84Sjason clockmatch_fhc(parent, cf, aux) 2201d2e6c84Sjason struct device *parent; 2211d2e6c84Sjason void *cf; 2221d2e6c84Sjason void *aux; 2231d2e6c84Sjason { 2241d2e6c84Sjason struct fhc_attach_args *fa = aux; 2251d2e6c84Sjason 2261d2e6c84Sjason return (strcmp("eeprom", fa->fa_name) == 0); 2271d2e6c84Sjason } 2281d2e6c84Sjason 229284584b0Sjason /* 230284584b0Sjason * Attach a clock (really `eeprom') to the sbus or ebus. 231284584b0Sjason * 232284584b0Sjason * We ignore any existing virtual address as we need to map 233284584b0Sjason * this read-only and make it read-write only temporarily, 234284584b0Sjason * whenever we read or write the clock chip. The clock also 235284584b0Sjason * contains the ID ``PROM'', and I have already had the pleasure 236284584b0Sjason * of reloading the cpu type, Ethernet address, etc, by hand from 237284584b0Sjason * the console FORTH interpreter. I intend not to enjoy it again. 238284584b0Sjason * 239284584b0Sjason * the MK48T02 is 2K. the MK48T08 is 8K, and the MK48T59 is 240284584b0Sjason * supposed to be identical to it. 241284584b0Sjason * 242284584b0Sjason * This is *UGLY*! We probably have multiple mappings. But I do 243284584b0Sjason * know that this all fits inside an 8K page, so I'll just map in 244284584b0Sjason * once. 245284584b0Sjason * 246284584b0Sjason * What we really need is some way to record the bus attach args 247284584b0Sjason * so we can call *_bus_map() later with BUS_SPACE_MAP_READONLY 248284584b0Sjason * or not to write enable/disable the device registers. This is 249284584b0Sjason * a non-trivial operation. 250284584b0Sjason */ 251284584b0Sjason 252284584b0Sjason /* ARGSUSED */ 253284584b0Sjason static void 254284584b0Sjason clockattach_sbus(parent, self, aux) 255284584b0Sjason struct device *parent, *self; 256284584b0Sjason void *aux; 257284584b0Sjason { 258284584b0Sjason struct sbus_attach_args *sa = aux; 259284584b0Sjason bus_space_tag_t bt = sa->sa_bustag; 260284584b0Sjason int sz; 261eb79e960Shenric static struct clock_wenable_info cwi; 262284584b0Sjason 263284584b0Sjason /* use sa->sa_regs[0].size? */ 264284584b0Sjason sz = 8192; 265284584b0Sjason 266284584b0Sjason if (sbus_bus_map(bt, 267284584b0Sjason sa->sa_slot, 268284584b0Sjason (sa->sa_offset & ~NBPG), 269284584b0Sjason sz, 270284584b0Sjason BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY, 271eb79e960Shenric 0, &cwi.cwi_bh) != 0) { 272284584b0Sjason printf("%s: can't map register\n", self->dv_xname); 273284584b0Sjason return; 274284584b0Sjason } 275eb79e960Shenric clockattach(sa->sa_node, bt, cwi.cwi_bh); 276284584b0Sjason 277284584b0Sjason /* Save info for the clock wenable call. */ 278eb79e960Shenric cwi.cwi_bt = bt; 279eb79e960Shenric cwi.cwi_size = sz; 280eb79e960Shenric todr_handle->bus_cookie = &cwi; 281eb79e960Shenric todr_handle->todr_setwen = clock_bus_wenable; 282284584b0Sjason } 283284584b0Sjason 284284584b0Sjason /* 285284584b0Sjason * Write en/dis-able clock registers. We coordinate so that several 286284584b0Sjason * writers can run simultaneously. 287eb79e960Shenric * XXX There is still a race here. The page change and the "writers" 288eb79e960Shenric * change are not atomic. 289284584b0Sjason */ 290284584b0Sjason int 291eb79e960Shenric clock_bus_wenable(handle, onoff) 292284584b0Sjason struct todr_chip_handle *handle; 293284584b0Sjason int onoff; 294284584b0Sjason { 295eb79e960Shenric int s, err = 0; 296eb79e960Shenric int prot; /* nonzero => change prot */ 297eb79e960Shenric volatile static int writers; 298eb79e960Shenric struct clock_wenable_info *cwi = handle->bus_cookie; 299284584b0Sjason 300284584b0Sjason s = splhigh(); 301284584b0Sjason if (onoff) 302eb79e960Shenric prot = writers++ == 0 ? 303eb79e960Shenric VM_PROT_READ | VM_PROT_WRITE | PMAP_WIRED : 0; 304284584b0Sjason else 305284584b0Sjason prot = --writers == 0 ? 306eb79e960Shenric VM_PROT_READ | PMAP_WIRED : 0; 307284584b0Sjason splx(s); 308284584b0Sjason 309eb79e960Shenric if (prot) { 310eb79e960Shenric err = bus_space_protect(cwi->cwi_bt, cwi->cwi_bh, cwi->cwi_size, 311eb79e960Shenric onoff ? 0 : BUS_SPACE_MAP_READONLY); 312eb79e960Shenric if (err) 313eb79e960Shenric printf("clock_wenable_info: WARNING -- cannot %s " 314eb79e960Shenric "page protection\n", onoff ? "disable" : "enable"); 315284584b0Sjason } 316284584b0Sjason return (err); 317284584b0Sjason } 318284584b0Sjason 319284584b0Sjason /* ARGSUSED */ 320284584b0Sjason static void 321284584b0Sjason clockattach_ebus(parent, self, aux) 322284584b0Sjason struct device *parent, *self; 323284584b0Sjason void *aux; 324284584b0Sjason { 325284584b0Sjason struct ebus_attach_args *ea = aux; 326eb79e960Shenric bus_space_tag_t bt; 327284584b0Sjason int sz; 328eb79e960Shenric static struct clock_wenable_info cwi; 329284584b0Sjason 330284584b0Sjason /* hard code to 8K? */ 331284584b0Sjason sz = ea->ea_regs[0].size; 332284584b0Sjason 333e9e8d4c3Skettenis if (ea->ea_nvaddrs) { 334e9e8d4c3Skettenis if (bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0, 335e9e8d4c3Skettenis BUS_SPACE_MAP_PROMADDRESS, &cwi.cwi_bh) != 0) { 336e9e8d4c3Skettenis printf("%s: can't map register\n", self->dv_xname); 337e9e8d4c3Skettenis return; 338e9e8d4c3Skettenis } 339e9e8d4c3Skettenis bt = ea->ea_memtag; 340e9e8d4c3Skettenis } else if (ebus_bus_map(ea->ea_iotag, 0, 341eb79e960Shenric EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), sz, 0, 0, &cwi.cwi_bh) == 0) { 342eb79e960Shenric bt = ea->ea_iotag; 343eb79e960Shenric } else if (ebus_bus_map(ea->ea_memtag, 0, 344eb79e960Shenric EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), sz, 345eb79e960Shenric BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY, 346eb79e960Shenric 0, &cwi.cwi_bh) == 0) { 347eb79e960Shenric bt = ea->ea_memtag; 348eb79e960Shenric } else { 349284584b0Sjason printf("%s: can't map register\n", self->dv_xname); 350284584b0Sjason return; 351284584b0Sjason } 352eb79e960Shenric 353eb79e960Shenric clockattach(ea->ea_node, bt, cwi.cwi_bh); 354284584b0Sjason 355284584b0Sjason /* Save info for the clock wenable call. */ 356eb79e960Shenric cwi.cwi_bt = bt; 357eb79e960Shenric cwi.cwi_size = sz; 358eb79e960Shenric todr_handle->bus_cookie = &cwi; 359eb79e960Shenric todr_handle->todr_setwen = (ea->ea_memtag == bt) ? 360eb79e960Shenric clock_bus_wenable : NULL; 361284584b0Sjason } 362284584b0Sjason 363284584b0Sjason static void 3641d2e6c84Sjason clockattach_fhc(parent, self, aux) 3651d2e6c84Sjason struct device *parent, *self; 3661d2e6c84Sjason void *aux; 3671d2e6c84Sjason { 3681d2e6c84Sjason struct fhc_attach_args *fa = aux; 3691d2e6c84Sjason bus_space_tag_t bt = fa->fa_bustag; 3701d2e6c84Sjason int sz; 3711d2e6c84Sjason static struct clock_wenable_info cwi; 3721d2e6c84Sjason 3731d2e6c84Sjason /* use sa->sa_regs[0].size? */ 3741d2e6c84Sjason sz = 8192; 3751d2e6c84Sjason 3761d2e6c84Sjason if (fhc_bus_map(bt, fa->fa_reg[0].fbr_slot, 3771d2e6c84Sjason (fa->fa_reg[0].fbr_offset & ~NBPG), fa->fa_reg[0].fbr_size, 3781d2e6c84Sjason BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_READONLY, &cwi.cwi_bh) != 0) { 3791d2e6c84Sjason printf("%s: can't map register\n", self->dv_xname); 3801d2e6c84Sjason return; 3811d2e6c84Sjason } 3821d2e6c84Sjason 3831d2e6c84Sjason clockattach(fa->fa_node, bt, cwi.cwi_bh); 3841d2e6c84Sjason 3851d2e6c84Sjason /* Save info for the clock wenable call. */ 3861d2e6c84Sjason cwi.cwi_bt = bt; 3871d2e6c84Sjason cwi.cwi_size = sz; 3881d2e6c84Sjason todr_handle->bus_cookie = &cwi; 3891d2e6c84Sjason todr_handle->todr_setwen = clock_bus_wenable; 3901d2e6c84Sjason } 3911d2e6c84Sjason 3921d2e6c84Sjason static void 393284584b0Sjason clockattach(node, bt, bh) 394284584b0Sjason int node; 395284584b0Sjason bus_space_tag_t bt; 396284584b0Sjason bus_space_handle_t bh; 397284584b0Sjason { 398284584b0Sjason char *model; 399284584b0Sjason struct idprom *idp; 400284584b0Sjason int h; 401284584b0Sjason 402284584b0Sjason model = getpropstring(node, "model"); 403284584b0Sjason 404284584b0Sjason #ifdef DIAGNOSTIC 405284584b0Sjason if (model == NULL) 406284584b0Sjason panic("clockattach: no model property"); 407284584b0Sjason #endif 408284584b0Sjason 409284584b0Sjason /* Our TOD clock year 0 is 1968 */ 410284584b0Sjason if ((todr_handle = mk48txx_attach(bt, bh, model, 1968)) == NULL) 411284584b0Sjason panic("Can't attach %s tod clock", model); 412284584b0Sjason 413284584b0Sjason #define IDPROM_OFFSET (8*1024 - 40) /* XXX - get nvram sz from driver */ 414eba3994dSjason if (idprom == NULL) { 415eba3994dSjason idp = getidprom(); 416eba3994dSjason if (idp == NULL) 417eb79e960Shenric idp = (struct idprom *)(bus_space_vaddr(bt, bh) + 418eb79e960Shenric IDPROM_OFFSET); 419eba3994dSjason idprom = idp; 420eba3994dSjason } else 421eba3994dSjason idp = idprom; 422284584b0Sjason h = idp->id_machine << 24; 423284584b0Sjason h |= idp->id_hostid[0] << 16; 424284584b0Sjason h |= idp->id_hostid[1] << 8; 425284584b0Sjason h |= idp->id_hostid[2]; 426284584b0Sjason hostid = h; 427b4117920Smiod printf("\n"); 428eba3994dSjason } 429eba3994dSjason 430eba3994dSjason struct idprom * 431b4117920Smiod getidprom() 432b4117920Smiod { 433eba3994dSjason struct idprom *idp = NULL; 434eba3994dSjason int node, n; 435eba3994dSjason 436eba3994dSjason node = findroot(); 437eba3994dSjason if (getprop(node, "idprom", sizeof(*idp), &n, (void **)&idp) != 0) 438eba3994dSjason return (NULL); 439eba3994dSjason if (n != 1) { 440eba3994dSjason free(idp, M_DEVBUF); 441eba3994dSjason return (NULL); 442eba3994dSjason } 443eba3994dSjason return (idp); 444284584b0Sjason } 445284584b0Sjason 446284584b0Sjason /* 447284584b0Sjason * The sun4u OPENPROMs call the timer the "counter-timer", except for 448284584b0Sjason * the lame UltraSPARC IIi PCI machines that don't have them. 449284584b0Sjason */ 450284584b0Sjason static int 451284584b0Sjason timermatch(parent, cf, aux) 452284584b0Sjason struct device *parent; 453284584b0Sjason void *cf; 454284584b0Sjason void *aux; 455284584b0Sjason { 4569d649671Skettenis #ifndef MULTIPROCESSOR 457284584b0Sjason struct mainbus_attach_args *ma = aux; 458284584b0Sjason 459915c144aSmiod if (!timerreg_4u.t_timer || !timerreg_4u.t_clrintr) 460284584b0Sjason return (strcmp("counter-timer", ma->ma_name) == 0); 461915c144aSmiod else 4629d649671Skettenis #endif 463915c144aSmiod return (0); 464284584b0Sjason } 465284584b0Sjason 466284584b0Sjason static void 467284584b0Sjason timerattach(parent, self, aux) 468284584b0Sjason struct device *parent, *self; 469284584b0Sjason void *aux; 470284584b0Sjason { 471284584b0Sjason struct mainbus_attach_args *ma = aux; 472284584b0Sjason u_int *va = ma->ma_address; 473284584b0Sjason 474284584b0Sjason /* 475284584b0Sjason * What we should have are 3 sets of registers that reside on 476284584b0Sjason * different parts of SYSIO or PSYCHO. We'll use the prom 477284584b0Sjason * mappings cause we can't get rid of them and set up appropriate 478284584b0Sjason * pointers on the timerreg_4u structure. 479284584b0Sjason */ 480284584b0Sjason timerreg_4u.t_timer = (struct timer_4u *)(u_long)va[0]; 481284584b0Sjason timerreg_4u.t_clrintr = (int64_t *)(u_long)va[1]; 482284584b0Sjason timerreg_4u.t_mapintr = (int64_t *)(u_long)va[2]; 483284584b0Sjason 484284584b0Sjason /* Install the appropriate interrupt vector here */ 4856fbc409cSkettenis level10.ih_number = INTVEC(ma->ma_interrupts[0]); 486284584b0Sjason level10.ih_clr = (void *)&timerreg_4u.t_clrintr[0]; 487eb79e960Shenric level10.ih_map = (void *)&timerreg_4u.t_mapintr[0]; 488fc635044Saaron strlcpy(level10.ih_name, "clock", sizeof(level10.ih_name)); 489284584b0Sjason intr_establish(10, &level10); 490eb79e960Shenric 4916fbc409cSkettenis level14.ih_number = INTVEC(ma->ma_interrupts[1]); 492284584b0Sjason level14.ih_clr = (void *)&timerreg_4u.t_clrintr[1]; 493eb79e960Shenric level14.ih_map = (void *)&timerreg_4u.t_mapintr[1]; 494fc635044Saaron strlcpy(level14.ih_name, "prof", sizeof(level14.ih_name)); 495284584b0Sjason intr_establish(14, &level14); 496eb79e960Shenric 49720eecc27Skettenis printf(" ivec 0x%x, 0x%x\n", INTVEC(level10.ih_number), 49820eecc27Skettenis INTVEC(level14.ih_number)); 499284584b0Sjason } 500284584b0Sjason 501284584b0Sjason void 502284584b0Sjason stopcounter(creg) 503284584b0Sjason struct timer_4u *creg; 504284584b0Sjason { 505284584b0Sjason /* Stop the clock */ 506284584b0Sjason volatile int discard; 507284584b0Sjason discard = creg->t_limit; 508284584b0Sjason creg->t_limit = 0; 509284584b0Sjason } 510284584b0Sjason 511284584b0Sjason /* 512284584b0Sjason * XXX this belongs elsewhere 513284584b0Sjason */ 514284584b0Sjason void 515284584b0Sjason myetheraddr(cp) 516284584b0Sjason u_char *cp; 517284584b0Sjason { 518284584b0Sjason struct idprom *idp; 519284584b0Sjason 520284584b0Sjason if ((idp = idprom) == NULL) { 521284584b0Sjason int node, n; 522284584b0Sjason 523284584b0Sjason node = findroot(); 524284584b0Sjason if (getprop(node, "idprom", sizeof *idp, &n, (void **)&idp) || 525284584b0Sjason n != 1) { 526284584b0Sjason printf("\nmyetheraddr: clock not setup yet, " 527284584b0Sjason "and no idprom property in /\n"); 528284584b0Sjason return; 529284584b0Sjason } 530284584b0Sjason } 531284584b0Sjason 532284584b0Sjason cp[0] = idp->id_ether[0]; 533284584b0Sjason cp[1] = idp->id_ether[1]; 534284584b0Sjason cp[2] = idp->id_ether[2]; 535284584b0Sjason cp[3] = idp->id_ether[3]; 536284584b0Sjason cp[4] = idp->id_ether[4]; 537284584b0Sjason cp[5] = idp->id_ether[5]; 538284584b0Sjason if (idprom == NULL) 539284584b0Sjason free(idp, M_DEVBUF); 540284584b0Sjason } 541284584b0Sjason 542284584b0Sjason /* 543284584b0Sjason * Set up the real-time and statistics clocks. Leave stathz 0 only if 544284584b0Sjason * no alternative timer is available. 545284584b0Sjason * 546284584b0Sjason * The frequencies of these clocks must be an even number of microseconds. 547284584b0Sjason */ 548284584b0Sjason void 549*8a2bbcc7Skettenis cpu_initclocks(void) 550284584b0Sjason { 551284584b0Sjason int statint, minint; 552284584b0Sjason #ifdef DEBUG 553284584b0Sjason extern int intrdebug; 554284584b0Sjason #endif 555*8a2bbcc7Skettenis u_int sys_tick_rate; 556*8a2bbcc7Skettenis int impl = 0; 557284584b0Sjason 558284584b0Sjason #ifdef DEBUG 559284584b0Sjason /* Set a 1s clock */ 560284584b0Sjason if (intrdebug) { 561284584b0Sjason hz = 1; 562284584b0Sjason tick = 1000000 / hz; 563284584b0Sjason printf("intrdebug set: 1Hz clock\n"); 564284584b0Sjason } 565284584b0Sjason #endif 566284584b0Sjason 567284584b0Sjason if (1000000 % hz) { 568284584b0Sjason printf("cannot get %d Hz clock; using 100 Hz\n", hz); 569284584b0Sjason hz = 100; 570284584b0Sjason tick = 1000000 / hz; 571284584b0Sjason } 572284584b0Sjason 573284584b0Sjason /* Make sure we have a sane cpu_clockrate -- we'll need it */ 574284584b0Sjason if (!cpu_clockrate) 575284584b0Sjason /* Default to 200MHz clock XXXXX */ 576284584b0Sjason cpu_clockrate = 200000000; 577284584b0Sjason 57805a26ea5Sart tick_timecounter.tc_frequency = cpu_clockrate; 57905a26ea5Sart tc_init(&tick_timecounter); 580284584b0Sjason 581284584b0Sjason /* 582*8a2bbcc7Skettenis * UltraSPARC IIe processors do have a STICK register, but it 583*8a2bbcc7Skettenis * lives on the PCI host bridge and isn't accessable through 584*8a2bbcc7Skettenis * ASR24. 585*8a2bbcc7Skettenis */ 586*8a2bbcc7Skettenis if (CPU_ISSUN4U || CPU_ISSUN4US) 587*8a2bbcc7Skettenis impl = (getver() & VER_IMPL) >> VER_IMPL_SHIFT; 588*8a2bbcc7Skettenis 589*8a2bbcc7Skettenis sys_tick_rate = getpropint(findroot(), "stick-frequency", 0); 590*8a2bbcc7Skettenis if (sys_tick_rate > 0 && impl != IMPL_HUMMINGBIRD) { 591*8a2bbcc7Skettenis sys_tick_timecounter.tc_frequency = sys_tick_rate; 592*8a2bbcc7Skettenis tc_init(&sys_tick_timecounter); 593*8a2bbcc7Skettenis } 594*8a2bbcc7Skettenis 595*8a2bbcc7Skettenis /* 596284584b0Sjason * Now handle machines w/o counter-timers. 597284584b0Sjason */ 598284584b0Sjason 599284584b0Sjason if (!timerreg_4u.t_timer || !timerreg_4u.t_clrintr) { 600284584b0Sjason /* We don't have a counter-timer -- use %tick */ 601284584b0Sjason level0.ih_clr = 0; 602284584b0Sjason /* 603284584b0Sjason * Establish a level 10 interrupt handler 604284584b0Sjason * 605284584b0Sjason * We will have a conflict with the softint handler, 606284584b0Sjason * so we set the ih_number to 1. 607284584b0Sjason */ 608284584b0Sjason level0.ih_number = 1; 609fc635044Saaron strlcpy(level0.ih_name, "clock", sizeof(level0.ih_name)); 610284584b0Sjason intr_establish(10, &level0); 611284584b0Sjason /* We only have one timer so we have no statclock */ 612284584b0Sjason stathz = 0; 613284584b0Sjason 614284584b0Sjason /* set the next interrupt time */ 615284584b0Sjason tick_increment = cpu_clockrate / hz; 616284584b0Sjason #ifdef DEBUG 617eb79e960Shenric printf("Using %%tick -- intr in %ld cycles...", 618eb79e960Shenric tick_increment); 619284584b0Sjason #endif 62092d28d96Skettenis tick_start(); 621284584b0Sjason #ifdef DEBUG 622284584b0Sjason printf("done.\n"); 623284584b0Sjason #endif 624284584b0Sjason return; 625284584b0Sjason } 626284584b0Sjason 627284584b0Sjason if (stathz == 0) 628284584b0Sjason stathz = hz; 629284584b0Sjason if (1000000 % stathz) { 630284584b0Sjason printf("cannot get %d Hz statclock; using 100 Hz\n", stathz); 631284584b0Sjason stathz = 100; 632284584b0Sjason } 633284584b0Sjason 634284584b0Sjason profhz = stathz; /* always */ 635284584b0Sjason 636284584b0Sjason statint = 1000000 / stathz; 637284584b0Sjason minint = statint / 2 + 100; 638284584b0Sjason while (statvar > minint) 639284584b0Sjason statvar >>= 1; 640284584b0Sjason 641284584b0Sjason /* 642284584b0Sjason * Establish scheduler softint. 643284584b0Sjason */ 644284584b0Sjason schedint.ih_pil = PIL_SCHED; 645284584b0Sjason schedint.ih_clr = NULL; 646284584b0Sjason schedint.ih_arg = 0; 647284584b0Sjason schedint.ih_pending = 0; 648284584b0Sjason schedhz = stathz/4; 649284584b0Sjason 650284584b0Sjason /* 651284584b0Sjason * Enable timers 652284584b0Sjason * 653284584b0Sjason * Also need to map the interrupts cause we're not a child of the sbus. 654284584b0Sjason * N.B. By default timer[0] is disabled and timer[1] is enabled. 655284584b0Sjason */ 656284584b0Sjason stxa((vaddr_t)&timerreg_4u.t_timer[0].t_limit, ASI_NUCLEUS, 657284584b0Sjason tmr_ustolim(tick)|TMR_LIM_IEN|TMR_LIM_PERIODIC|TMR_LIM_RELOAD); 658284584b0Sjason stxa((vaddr_t)&timerreg_4u.t_mapintr[0], ASI_NUCLEUS, 659284584b0Sjason timerreg_4u.t_mapintr[0]|INTMAP_V); 660284584b0Sjason 661284584b0Sjason #ifdef DEBUG 662284584b0Sjason if (intrdebug) 663284584b0Sjason /* Neglect to enable timer */ 664284584b0Sjason stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS, 665284584b0Sjason tmr_ustolim(statint)|TMR_LIM_RELOAD); 666284584b0Sjason else 667284584b0Sjason #endif 668284584b0Sjason stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS, 669284584b0Sjason tmr_ustolim(statint)|TMR_LIM_IEN|TMR_LIM_RELOAD); 670284584b0Sjason stxa((vaddr_t)&timerreg_4u.t_mapintr[1], ASI_NUCLEUS, 671284584b0Sjason timerreg_4u.t_mapintr[1]|INTMAP_V); 672284584b0Sjason 673284584b0Sjason statmin = statint - (statvar >> 1); 674284584b0Sjason 675284584b0Sjason } 676284584b0Sjason 677284584b0Sjason /* 678284584b0Sjason * Dummy setstatclockrate(), since we know profhz==hz. 679284584b0Sjason */ 680284584b0Sjason /* ARGSUSED */ 681284584b0Sjason void 682284584b0Sjason setstatclockrate(newhz) 683284584b0Sjason int newhz; 684284584b0Sjason { 685284584b0Sjason /* nothing */ 686284584b0Sjason } 687284584b0Sjason 688284584b0Sjason /* 689284584b0Sjason * Level 10 (clock) interrupts. If we are using the FORTH PROM for 690284584b0Sjason * console input, we need to check for that here as well, and generate 691284584b0Sjason * a software interrupt to read it. 692284584b0Sjason */ 693284584b0Sjason #ifdef DEBUG 694284584b0Sjason static int clockcheck = 0; 695284584b0Sjason #endif 696284584b0Sjason int 697284584b0Sjason clockintr(cap) 698284584b0Sjason void *cap; 699284584b0Sjason { 700284584b0Sjason #ifdef DEBUG 701284584b0Sjason static int64_t tick_base = 0; 702b636f8fdSkettenis struct timeval ctime; 7035f018631Sjason int64_t t; 7045f018631Sjason 7055f018631Sjason t = tick() & TICK_TICKS; 706284584b0Sjason 707b636f8fdSkettenis microtime(&ctime); 708284584b0Sjason if (!tick_base) { 709b636f8fdSkettenis tick_base = (ctime.tv_sec * 1000000LL + ctime.tv_usec) 710284584b0Sjason * 1000000LL / cpu_clockrate; 711284584b0Sjason tick_base -= t; 712284584b0Sjason } else if (clockcheck) { 713284584b0Sjason int64_t tk = t; 714b636f8fdSkettenis int64_t clk = (ctime.tv_sec * 1000000LL + ctime.tv_usec); 715284584b0Sjason t -= tick_base; 716284584b0Sjason t = t * 1000000LL / cpu_clockrate; 717284584b0Sjason if (t - clk > hz) { 718284584b0Sjason printf("Clock lost an interrupt!\n"); 719eb79e960Shenric printf("Actual: %llx Expected: %llx tick %llx " 720eb79e960Shenric "tick_base %llx\n", (long long)t, (long long)clk, 721eb79e960Shenric (long long)tk, (long long)tick_base); 722284584b0Sjason #ifdef DDB 723284584b0Sjason Debugger(); 724284584b0Sjason #endif 725284584b0Sjason tick_base = 0; 726284584b0Sjason } 727284584b0Sjason } 728284584b0Sjason #endif 729284584b0Sjason /* Let locore.s clear the interrupt for us. */ 730284584b0Sjason hardclock((struct clockframe *)cap); 7315f018631Sjason 732284584b0Sjason return (1); 733284584b0Sjason } 734284584b0Sjason 735284584b0Sjason /* 736284584b0Sjason * Level 10 (clock) interrupts. If we are using the FORTH PROM for 737284584b0Sjason * console input, we need to check for that here as well, and generate 738284584b0Sjason * a software interrupt to read it. 739284584b0Sjason * 740284584b0Sjason * %tick is really a level-14 interrupt. We need to remap this in 741284584b0Sjason * locore.s to a level 10. 742284584b0Sjason */ 743284584b0Sjason int 744284584b0Sjason tickintr(cap) 745284584b0Sjason void *cap; 746284584b0Sjason { 74754821dc7Skettenis struct cpu_info *ci = curcpu(); 74854821dc7Skettenis u_int64_t s; 749284584b0Sjason 75092d28d96Skettenis /* 75154821dc7Skettenis * No need to worry about overflow; %tick is architecturally 75254821dc7Skettenis * defined not to do that for at least 10 years. 75392d28d96Skettenis */ 75454821dc7Skettenis while (ci->ci_tick < tick()) { 75554821dc7Skettenis ci->ci_tick += tick_increment; 75654821dc7Skettenis hardclock((struct clockframe *)cap); 757fc635044Saaron level0.ih_count.ec_count++; 75854821dc7Skettenis } 75954821dc7Skettenis 76054821dc7Skettenis /* Reset the interrupt. */ 76154821dc7Skettenis s = intr_disable(); 76254821dc7Skettenis tickcmpr_set(ci->ci_tick); 76392d28d96Skettenis intr_restore(s); 764284584b0Sjason 765284584b0Sjason return (1); 766284584b0Sjason } 767284584b0Sjason 768284584b0Sjason /* 769284584b0Sjason * Level 14 (stat clock) interrupt handler. 770284584b0Sjason */ 771284584b0Sjason int 772284584b0Sjason statintr(cap) 773284584b0Sjason void *cap; 774284584b0Sjason { 775eb79e960Shenric u_long newint, r, var; 776284584b0Sjason struct cpu_info *ci = curcpu(); 777284584b0Sjason 778284584b0Sjason #ifdef NOT_DEBUG 779284584b0Sjason printf("statclock: count %x:%x, limit %x:%x\n", 780284584b0Sjason timerreg_4u.t_timer[1].t_count, timerreg_4u.t_timer[1].t_limit); 781284584b0Sjason #endif 782284584b0Sjason #ifdef NOT_DEBUG 783284584b0Sjason prom_printf("!"); 784284584b0Sjason #endif 785284584b0Sjason statclock((struct clockframe *)cap); 786284584b0Sjason #ifdef NOTDEF_DEBUG 787284584b0Sjason /* Don't re-schedule the IRQ */ 788284584b0Sjason return 1; 789284584b0Sjason #endif 790284584b0Sjason /* 791284584b0Sjason * Compute new randomized interval. The intervals are uniformly 792284584b0Sjason * distributed on [statint - statvar / 2, statint + statvar / 2], 793284584b0Sjason * and therefore have mean statint, giving a stathz frequency clock. 794284584b0Sjason */ 795284584b0Sjason var = statvar; 796284584b0Sjason do { 797284584b0Sjason r = random() & (var - 1); 798284584b0Sjason } while (r == 0); 799284584b0Sjason newint = statmin + r; 800284584b0Sjason 801284584b0Sjason if (schedhz) 802284584b0Sjason if ((++ci->ci_schedstate.spc_schedticks & 3) == 0) 803284584b0Sjason send_softint(-1, PIL_SCHED, &schedint); 804284584b0Sjason stxa((vaddr_t)&timerreg_4u.t_timer[1].t_limit, ASI_NUCLEUS, 805284584b0Sjason tmr_ustolim(newint)|TMR_LIM_IEN|TMR_LIM_RELOAD); 806fc635044Saaron 807284584b0Sjason return (1); 808284584b0Sjason } 809284584b0Sjason 810284584b0Sjason int 811284584b0Sjason schedintr(arg) 812284584b0Sjason void *arg; 813284584b0Sjason { 814284584b0Sjason if (curproc) 815284584b0Sjason schedclock(curproc); 816284584b0Sjason return (1); 817284584b0Sjason } 818284584b0Sjason 819284584b0Sjason 820284584b0Sjason /* 821284584b0Sjason * `sparc_clock_time_is_ok' is used in cpu_reboot() to determine 822284584b0Sjason * whether it is appropriate to call resettodr() to consolidate 823284584b0Sjason * pending time adjustments. 824284584b0Sjason */ 825284584b0Sjason int sparc_clock_time_is_ok; 826284584b0Sjason 827284584b0Sjason /* 828284584b0Sjason * Set up the system's time, given a `reasonable' time value. 829284584b0Sjason */ 830284584b0Sjason void 831284584b0Sjason inittodr(base) 832284584b0Sjason time_t base; 833284584b0Sjason { 834284584b0Sjason int badbase = 0, waszero = base == 0; 8354da6a00eSderaadt char *bad = NULL; 83605a26ea5Sart struct timeval tv; 83705a26ea5Sart struct timespec ts; 838284584b0Sjason 839284584b0Sjason if (base < 5 * SECYR) { 840284584b0Sjason /* 841284584b0Sjason * If base is 0, assume filesystem time is just unknown 842284584b0Sjason * in stead of preposterous. Don't bark. 843284584b0Sjason */ 844284584b0Sjason if (base != 0) 845284584b0Sjason printf("WARNING: preposterous time in file system\n"); 846284584b0Sjason /* not going to use it anyway, if the chip is readable */ 847284584b0Sjason base = 21*SECYR + 186*SECDAY + SECDAY/2; 848284584b0Sjason badbase = 1; 849284584b0Sjason } 850284584b0Sjason 85105a26ea5Sart if (todr_handle && (todr_gettime(todr_handle, &tv) != 0 || 85205a26ea5Sart tv.tv_sec == 0)) { 853284584b0Sjason /* 854284584b0Sjason * Believe the time in the file system for lack of 855284584b0Sjason * anything better, resetting the clock. 856284584b0Sjason */ 8574da6a00eSderaadt bad = "WARNING: bad date in battery clock"; 85805a26ea5Sart tv.tv_sec = base; 85905a26ea5Sart tv.tv_usec = 0; 860284584b0Sjason if (!badbase) 861284584b0Sjason resettodr(); 862284584b0Sjason } else { 86305a26ea5Sart int deltat = tv.tv_sec - base; 864284584b0Sjason 865284584b0Sjason sparc_clock_time_is_ok = 1; 866284584b0Sjason 867284584b0Sjason if (deltat < 0) 868284584b0Sjason deltat = -deltat; 86905a26ea5Sart if (!(waszero || deltat < 2 * SECDAY)) { 8704da6a00eSderaadt #ifndef SMALL_KERNEL 871eb79e960Shenric printf("WARNING: clock %s %ld days", 87205a26ea5Sart tv.tv_sec < base ? "lost" : "gained", deltat / SECDAY); 8734da6a00eSderaadt bad = ""; 8744da6a00eSderaadt #endif 875284584b0Sjason } 8760f9af073Sderaadt } 87705a26ea5Sart 87805a26ea5Sart ts.tv_sec = tv.tv_sec; 87905a26ea5Sart ts.tv_nsec = tv.tv_usec * 1000; 88005a26ea5Sart tc_setclock(&ts); 88105a26ea5Sart 8824da6a00eSderaadt if (bad) { 8834da6a00eSderaadt printf("%s", bad); 884284584b0Sjason printf(" -- CHECK AND RESET THE DATE!\n"); 885284584b0Sjason } 8864da6a00eSderaadt } 887284584b0Sjason 888284584b0Sjason /* 889284584b0Sjason * Reset the clock based on the current time. 890284584b0Sjason * Used when the current clock is preposterous, when the time is changed, 891284584b0Sjason * and when rebooting. Do nothing if the time is not yet known, e.g., 892284584b0Sjason * when crashing during autoconfig. 893284584b0Sjason */ 894284584b0Sjason void 895284584b0Sjason resettodr() 896284584b0Sjason { 89705a26ea5Sart struct timeval tv; 898284584b0Sjason 89905a26ea5Sart if (time_second == 0) 900284584b0Sjason return; 901284584b0Sjason 90205a26ea5Sart microtime(&tv); 90305a26ea5Sart 904284584b0Sjason sparc_clock_time_is_ok = 1; 90505a26ea5Sart if (todr_handle == 0 || todr_settime(todr_handle, &tv) != 0) 906284584b0Sjason printf("Cannot set time in time-of-day clock\n"); 907284584b0Sjason } 908284584b0Sjason 90958c73160Skettenis void 91058c73160Skettenis tick_start(void) 91158c73160Skettenis { 91254821dc7Skettenis struct cpu_info *ci = curcpu(); 91354821dc7Skettenis u_int64_t s; 91458c73160Skettenis 91558c73160Skettenis /* 91658c73160Skettenis * Try to make the tick interrupts as synchronously as possible on 91754821dc7Skettenis * all CPUs to avoid inaccuracies for migrating processes. 91858c73160Skettenis */ 91958c73160Skettenis 92058c73160Skettenis s = intr_disable(); 92154821dc7Skettenis ci->ci_tick = roundup(tick(), tick_increment); 92254821dc7Skettenis tickcmpr_set(ci->ci_tick); 92358c73160Skettenis intr_restore(s); 92458c73160Skettenis } 92558c73160Skettenis 92605a26ea5Sart u_int 92705a26ea5Sart tick_get_timecount(struct timecounter *tc) 928bec6c41fSjason { 929bec6c41fSjason u_int64_t tick; 930bec6c41fSjason 931bec6c41fSjason __asm __volatile("rd %%tick, %0" : "=r" (tick) :); 932bec6c41fSjason 93305a26ea5Sart return (tick & ~0u); 934bec6c41fSjason } 935*8a2bbcc7Skettenis 936*8a2bbcc7Skettenis u_int 937*8a2bbcc7Skettenis sys_tick_get_timecount(struct timecounter *tc) 938*8a2bbcc7Skettenis { 939*8a2bbcc7Skettenis u_int64_t tick; 940*8a2bbcc7Skettenis 941*8a2bbcc7Skettenis __asm __volatile("rd %%sys_tick, %0" : "=r" (tick) :); 942*8a2bbcc7Skettenis 943*8a2bbcc7Skettenis return (tick & ~0u); 944*8a2bbcc7Skettenis } 945