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