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