tick.c revision 330897
1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2006-2007 Bruce M. Simpson. 5 * Copyright (c) 2003-2004 Juli Mallett. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30/* 31 * Simple driver for the 32-bit interval counter built in to all 32 * MIPS32 CPUs. 33 */ 34 35#include <sys/cdefs.h> 36__FBSDID("$FreeBSD: stable/11/sys/mips/mips/tick.c 330897 2018-03-14 03:19:51Z eadler $"); 37 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/sysctl.h> 41#include <sys/bus.h> 42#include <sys/kernel.h> 43#include <sys/module.h> 44#include <sys/rman.h> 45#include <sys/power.h> 46#include <sys/smp.h> 47#include <sys/time.h> 48#include <sys/timeet.h> 49#include <sys/timetc.h> 50 51#include <machine/hwfunc.h> 52#include <machine/clock.h> 53#include <machine/locore.h> 54#include <machine/md_var.h> 55 56#ifdef INTRNG 57#include <machine/intr.h> 58#endif 59 60uint64_t counter_freq; 61 62struct timecounter *platform_timecounter; 63 64static DPCPU_DEFINE(uint32_t, cycles_per_tick); 65static uint32_t cycles_per_usec; 66 67static DPCPU_DEFINE(volatile uint32_t, counter_upper); 68static DPCPU_DEFINE(volatile uint32_t, counter_lower_last); 69static DPCPU_DEFINE(uint32_t, compare_ticks); 70static DPCPU_DEFINE(uint32_t, lost_ticks); 71 72struct clock_softc { 73 int intr_rid; 74 struct resource *intr_res; 75 void *intr_handler; 76 struct timecounter tc; 77 struct eventtimer et; 78}; 79static struct clock_softc *softc; 80 81/* 82 * Device methods 83 */ 84static int clock_probe(device_t); 85static void clock_identify(driver_t *, device_t); 86static int clock_attach(device_t); 87static unsigned counter_get_timecount(struct timecounter *tc); 88 89void 90mips_timer_early_init(uint64_t clock_hz) 91{ 92 /* Initialize clock early so that we can use DELAY sooner */ 93 counter_freq = clock_hz; 94 cycles_per_usec = (clock_hz / (1000 * 1000)); 95} 96 97void 98platform_initclocks(void) 99{ 100 101 if (platform_timecounter != NULL) 102 tc_init(platform_timecounter); 103} 104 105static uint64_t 106tick_ticker(void) 107{ 108 uint64_t ret; 109 uint32_t ticktock; 110 uint32_t t_lower_last, t_upper; 111 112 /* 113 * Disable preemption because we are working with cpu specific data. 114 */ 115 critical_enter(); 116 117 /* 118 * Note that even though preemption is disabled, interrupts are 119 * still enabled. In particular there is a race with clock_intr() 120 * reading the values of 'counter_upper' and 'counter_lower_last'. 121 * 122 * XXX this depends on clock_intr() being executed periodically 123 * so that 'counter_upper' and 'counter_lower_last' are not stale. 124 */ 125 do { 126 t_upper = DPCPU_GET(counter_upper); 127 t_lower_last = DPCPU_GET(counter_lower_last); 128 } while (t_upper != DPCPU_GET(counter_upper)); 129 130 ticktock = mips_rd_count(); 131 132 critical_exit(); 133 134 /* COUNT register wrapped around */ 135 if (ticktock < t_lower_last) 136 t_upper++; 137 138 ret = ((uint64_t)t_upper << 32) | ticktock; 139 return (ret); 140} 141 142void 143mips_timer_init_params(uint64_t platform_counter_freq, int double_count) 144{ 145 146 /* 147 * XXX: Do not use printf here: uart code 8250 may use DELAY so this 148 * function should be called before cninit. 149 */ 150 counter_freq = platform_counter_freq; 151 /* 152 * XXX: Some MIPS32 cores update the Count register only every two 153 * pipeline cycles. 154 * We know this because of status registers in CP0, make it automatic. 155 */ 156 if (double_count != 0) 157 counter_freq /= 2; 158 159 cycles_per_usec = counter_freq / (1 * 1000 * 1000); 160 set_cputicker(tick_ticker, counter_freq, 1); 161} 162 163static int 164sysctl_machdep_counter_freq(SYSCTL_HANDLER_ARGS) 165{ 166 int error; 167 uint64_t freq; 168 169 if (softc == NULL) 170 return (EOPNOTSUPP); 171 freq = counter_freq; 172 error = sysctl_handle_64(oidp, &freq, sizeof(freq), req); 173 if (error == 0 && req->newptr != NULL) { 174 counter_freq = freq; 175 softc->et.et_frequency = counter_freq; 176 softc->tc.tc_frequency = counter_freq; 177 } 178 return (error); 179} 180 181SYSCTL_PROC(_machdep, OID_AUTO, counter_freq, CTLTYPE_U64 | CTLFLAG_RW, 182 NULL, 0, sysctl_machdep_counter_freq, "QU", 183 "Timecounter frequency in Hz"); 184 185static unsigned 186counter_get_timecount(struct timecounter *tc) 187{ 188 189 return (mips_rd_count()); 190} 191 192/* 193 * Wait for about n microseconds (at least!). 194 */ 195void 196DELAY(int n) 197{ 198 uint32_t cur, last, delta, usecs; 199 200 /* 201 * This works by polling the timer and counting the number of 202 * microseconds that go by. 203 */ 204 last = mips_rd_count(); 205 delta = usecs = 0; 206 207 while (n > usecs) { 208 cur = mips_rd_count(); 209 210 /* Check to see if the timer has wrapped around. */ 211 if (cur < last) 212 delta += cur + (0xffffffff - last) + 1; 213 else 214 delta += cur - last; 215 216 last = cur; 217 218 if (delta >= cycles_per_usec) { 219 usecs += delta / cycles_per_usec; 220 delta %= cycles_per_usec; 221 } 222 } 223} 224 225static int 226clock_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 227{ 228 uint32_t fdiv, div, next; 229 230 if (period != 0) { 231 div = (et->et_frequency * period) >> 32; 232 } else 233 div = 0; 234 if (first != 0) 235 fdiv = (et->et_frequency * first) >> 32; 236 else 237 fdiv = div; 238 DPCPU_SET(cycles_per_tick, div); 239 next = mips_rd_count() + fdiv; 240 DPCPU_SET(compare_ticks, next); 241 mips_wr_compare(next); 242 return (0); 243} 244 245static int 246clock_stop(struct eventtimer *et) 247{ 248 249 DPCPU_SET(cycles_per_tick, 0); 250 mips_wr_compare(0xffffffff); 251 return (0); 252} 253 254/* 255 * Device section of file below 256 */ 257static int 258clock_intr(void *arg) 259{ 260 struct clock_softc *sc = (struct clock_softc *)arg; 261 uint32_t cycles_per_tick; 262 uint32_t count, compare_last, compare_next, lost_ticks; 263 264 cycles_per_tick = DPCPU_GET(cycles_per_tick); 265 /* 266 * Set next clock edge. 267 */ 268 count = mips_rd_count(); 269 compare_last = DPCPU_GET(compare_ticks); 270 if (cycles_per_tick > 0) { 271 compare_next = count + cycles_per_tick; 272 DPCPU_SET(compare_ticks, compare_next); 273 mips_wr_compare(compare_next); 274 } else /* In one-shot mode timer should be stopped after the event. */ 275 mips_wr_compare(0xffffffff); 276 277 /* COUNT register wrapped around */ 278 if (count < DPCPU_GET(counter_lower_last)) { 279 DPCPU_SET(counter_upper, DPCPU_GET(counter_upper) + 1); 280 } 281 DPCPU_SET(counter_lower_last, count); 282 283 if (cycles_per_tick > 0) { 284 285 /* 286 * Account for the "lost time" between when the timer interrupt 287 * fired and when 'clock_intr' actually started executing. 288 */ 289 lost_ticks = DPCPU_GET(lost_ticks); 290 lost_ticks += count - compare_last; 291 292 /* 293 * If the COUNT and COMPARE registers are no longer in sync 294 * then make up some reasonable value for the 'lost_ticks'. 295 * 296 * This could happen, for e.g., after we resume normal 297 * operations after exiting the debugger. 298 */ 299 if (lost_ticks > 2 * cycles_per_tick) 300 lost_ticks = cycles_per_tick; 301 302 while (lost_ticks >= cycles_per_tick) { 303 if (sc->et.et_active) 304 sc->et.et_event_cb(&sc->et, sc->et.et_arg); 305 lost_ticks -= cycles_per_tick; 306 } 307 DPCPU_SET(lost_ticks, lost_ticks); 308 } 309 if (sc->et.et_active) 310 sc->et.et_event_cb(&sc->et, sc->et.et_arg); 311 return (FILTER_HANDLED); 312} 313 314static int 315clock_probe(device_t dev) 316{ 317 318 device_set_desc(dev, "Generic MIPS32 ticker"); 319 return (BUS_PROBE_NOWILDCARD); 320} 321 322static void 323clock_identify(driver_t * drv, device_t parent) 324{ 325 326 BUS_ADD_CHILD(parent, 0, "clock", 0); 327} 328 329static int 330clock_attach(device_t dev) 331{ 332 struct clock_softc *sc; 333#ifndef INTRNG 334 int error; 335#endif 336 337 if (device_get_unit(dev) != 0) 338 panic("can't attach more clocks"); 339 340 softc = sc = device_get_softc(dev); 341#ifdef INTRNG 342 cpu_establish_hardintr("clock", clock_intr, NULL, sc, 5, INTR_TYPE_CLK, 343 NULL); 344#else 345 sc->intr_rid = 0; 346 sc->intr_res = bus_alloc_resource(dev, 347 SYS_RES_IRQ, &sc->intr_rid, 5, 5, 1, RF_ACTIVE); 348 if (sc->intr_res == NULL) { 349 device_printf(dev, "failed to allocate irq\n"); 350 return (ENXIO); 351 } 352 error = bus_setup_intr(dev, sc->intr_res, INTR_TYPE_CLK, 353 clock_intr, NULL, sc, &sc->intr_handler); 354 if (error != 0) { 355 device_printf(dev, "bus_setup_intr returned %d\n", error); 356 return (error); 357 } 358#endif 359 360 sc->tc.tc_get_timecount = counter_get_timecount; 361 sc->tc.tc_counter_mask = 0xffffffff; 362 sc->tc.tc_frequency = counter_freq; 363 sc->tc.tc_name = "MIPS32"; 364 sc->tc.tc_quality = 800; 365 sc->tc.tc_priv = sc; 366 tc_init(&sc->tc); 367 sc->et.et_name = "MIPS32"; 368 sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | 369 ET_FLAGS_PERCPU; 370 sc->et.et_quality = 800; 371 sc->et.et_frequency = counter_freq; 372 sc->et.et_min_period = 0x00004000LLU; /* To be safe. */ 373 sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; 374 sc->et.et_start = clock_start; 375 sc->et.et_stop = clock_stop; 376 sc->et.et_priv = sc; 377 et_register(&sc->et); 378 return (0); 379} 380 381static device_method_t clock_methods[] = { 382 /* Device interface */ 383 DEVMETHOD(device_probe, clock_probe), 384 DEVMETHOD(device_identify, clock_identify), 385 DEVMETHOD(device_attach, clock_attach), 386 DEVMETHOD(device_detach, bus_generic_detach), 387 DEVMETHOD(device_shutdown, bus_generic_shutdown), 388 389 {0, 0} 390}; 391 392static driver_t clock_driver = { 393 "clock", 394 clock_methods, 395 sizeof(struct clock_softc), 396}; 397 398static devclass_t clock_devclass; 399 400DRIVER_MODULE(clock, nexus, clock_driver, clock_devclass, 0, 0); 401