tick.c revision 210542
1198607Srrs/*- 2210528Sjchandra * Copyright (c) 2006-2007 Bruce M. Simpson. 3210528Sjchandra * Copyright (c) 2003-2004 Juli Mallett. 4198607Srrs * All rights reserved. 5198607Srrs * 6198607Srrs * Redistribution and use in source and binary forms, with or without 7198607Srrs * modification, are permitted provided that the following conditions 8198607Srrs * are met: 9198607Srrs * 1. Redistributions of source code must retain the above copyright 10210528Sjchandra * notice, this list of conditions and the following disclaimer. 11198607Srrs * 2. Redistributions in binary form must reproduce the above copyright 12210528Sjchandra * notice, this list of conditions and the following disclaimer in the 13210528Sjchandra * documentation and/or other materials provided with the distribution. 14198607Srrs * 15198607Srrs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16198607Srrs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17198607Srrs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18198607Srrs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19198607Srrs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20198607Srrs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21198607Srrs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22198607Srrs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23198607Srrs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24198607Srrs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25198607Srrs * SUCH DAMAGE. 26198607Srrs */ 27198607Srrs 28198607Srrs/* 29198607Srrs * Simple driver for the 32-bit interval counter built in to all 30198607Srrs * MIPS32 CPUs. 31198607Srrs */ 32198607Srrs 33198607Srrs#include <sys/cdefs.h> 34198607Srrs__FBSDID("$FreeBSD: head/sys/mips/rmi/tick.c 210542 2010-07-27 15:10:05Z jchandra $"); 35198607Srrs 36210528Sjchandra#include "opt_cputype.h" 37210528Sjchandra 38198607Srrs#include <sys/param.h> 39198607Srrs#include <sys/systm.h> 40198607Srrs#include <sys/sysctl.h> 41210528Sjchandra#include <sys/bus.h> 42198607Srrs#include <sys/kernel.h> 43210528Sjchandra#include <sys/module.h> 44210528Sjchandra#include <sys/rman.h> 45198607Srrs#include <sys/power.h> 46198607Srrs#include <sys/smp.h> 47210528Sjchandra#include <sys/time.h> 48210528Sjchandra#include <sys/timeet.h> 49210528Sjchandra#include <sys/timetc.h> 50210528Sjchandra 51210528Sjchandra#include <machine/hwfunc.h> 52198607Srrs#include <machine/clock.h> 53198607Srrs#include <machine/locore.h> 54198607Srrs#include <machine/md_var.h> 55210528Sjchandra#include <machine/intr_machdep.h> 56210528Sjchandra#include <mips/rmi/interrupt.h> 57198607Srrs 58210528Sjchandrauint64_t counter_freq; 59198607Srrs 60210528Sjchandrastruct timecounter *platform_timecounter; 61210528Sjchandra 62210528Sjchandrastatic DPCPU_DEFINE(uint32_t, cycles_per_tick); 63210528Sjchandrastatic uint32_t cycles_per_usec; 64210528Sjchandra 65210542Sjchandrastatic DPCPU_DEFINE(uint32_t, counter_upper); 66210542Sjchandrastatic DPCPU_DEFINE(uint32_t, counter_lower_last); 67210528Sjchandrastatic DPCPU_DEFINE(uint32_t, compare_ticks); 68210528Sjchandrastatic DPCPU_DEFINE(uint32_t, lost_ticks); 69210528Sjchandra 70210528Sjchandrastruct clock_softc { 71210528Sjchandra int intr_rid; 72210528Sjchandra struct resource *intr_res; 73210528Sjchandra void *intr_handler; 74210528Sjchandra struct timecounter tc; 75210528Sjchandra struct eventtimer et; 76198607Srrs}; 77210528Sjchandrastatic struct clock_softc *softc; 78198607Srrs 79210528Sjchandra/* 80210528Sjchandra * Device methods 81210528Sjchandra */ 82210528Sjchandrastatic int clock_probe(device_t); 83210528Sjchandrastatic void clock_identify(driver_t *, device_t); 84210528Sjchandrastatic int clock_attach(device_t); 85210528Sjchandrastatic unsigned counter_get_timecount(struct timecounter *tc); 86198607Srrs 87210528Sjchandravoid 88210528Sjchandramips_timer_early_init(uint64_t clock_hz) 89210528Sjchandra{ 90210528Sjchandra /* Initialize clock early so that we can use DELAY sooner */ 91210528Sjchandra counter_freq = clock_hz; 92210528Sjchandra cycles_per_usec = (clock_hz / (1000 * 1000)); 93210528Sjchandra} 94198607Srrs 95198607Srrsvoid 96210528Sjchandraplatform_initclocks(void) 97198607Srrs{ 98198607Srrs 99210528Sjchandra if (platform_timecounter != NULL) 100210528Sjchandra tc_init(platform_timecounter); 101198607Srrs} 102198607Srrs 103210528Sjchandrastatic uint64_t 104210528Sjchandratick_ticker(void) 105210528Sjchandra{ 106210528Sjchandra uint64_t ret; 107210528Sjchandra uint32_t ticktock; 108210542Sjchandra uint32_t t_lower_last, t_upper; 109210528Sjchandra 110210528Sjchandra /* 111210528Sjchandra * XXX: MIPS64 platforms can read 64-bits of counter directly. 112210528Sjchandra * Also: the tc code is supposed to cope with things wrapping 113210528Sjchandra * from the time counter, so I'm not sure why all these hoops 114210528Sjchandra * are even necessary. 115210528Sjchandra */ 116210528Sjchandra ticktock = mips_rd_count(); 117210528Sjchandra critical_enter(); 118210542Sjchandra t_lower_last = DPCPU_GET(counter_lower_last); 119210542Sjchandra t_upper = DPCPU_GET(counter_upper); 120210542Sjchandra if (ticktock < t_lower_last) 121210542Sjchandra t_upper++; 122210542Sjchandra t_lower_last = ticktock; 123210528Sjchandra critical_exit(); 124210528Sjchandra 125210542Sjchandra DPCPU_SET(counter_upper, t_upper); 126210542Sjchandra DPCPU_SET(counter_lower_last, t_lower_last); 127210542Sjchandra ret = ((uint64_t)t_upper << 32) | t_lower_last; 128210528Sjchandra return (ret); 129210528Sjchandra} 130210528Sjchandra 131210528Sjchandravoid 132210528Sjchandramips_timer_init_params(uint64_t platform_counter_freq, int double_count) 133210528Sjchandra{ 134210528Sjchandra 135210528Sjchandra /* 136210528Sjchandra * XXX: Do not use printf here: uart code 8250 may use DELAY so this 137210528Sjchandra * function should be called before cninit. 138210528Sjchandra */ 139210528Sjchandra counter_freq = platform_counter_freq; 140210528Sjchandra /* 141210528Sjchandra * XXX: Some MIPS32 cores update the Count register only every two 142210528Sjchandra * pipeline cycles. 143210528Sjchandra * We know this because of status registers in CP0, make it automatic. 144210528Sjchandra */ 145210528Sjchandra if (double_count != 0) 146210528Sjchandra counter_freq /= 2; 147210528Sjchandra 148210528Sjchandra cycles_per_usec = counter_freq / (1 * 1000 * 1000); 149210528Sjchandra set_cputicker(tick_ticker, counter_freq, 1); 150210528Sjchandra} 151210528Sjchandra 152198607Srrsstatic int 153198607Srrssysctl_machdep_counter_freq(SYSCTL_HANDLER_ARGS) 154198607Srrs{ 155198607Srrs int error; 156198607Srrs uint64_t freq; 157198625Srrs 158210528Sjchandra if (softc == NULL) 159198607Srrs return (EOPNOTSUPP); 160198607Srrs freq = counter_freq; 161198607Srrs error = sysctl_handle_int(oidp, &freq, sizeof(freq), req); 162198607Srrs if (error == 0 && req->newptr != NULL) { 163198607Srrs counter_freq = freq; 164210528Sjchandra softc->et.et_frequency = counter_freq; 165210528Sjchandra softc->tc.tc_frequency = counter_freq; 166198607Srrs } 167198607Srrs return (error); 168198607Srrs} 169198607Srrs 170198607SrrsSYSCTL_PROC(_machdep, OID_AUTO, counter_freq, CTLTYPE_QUAD | CTLFLAG_RW, 171210528Sjchandra 0, sizeof(u_int), sysctl_machdep_counter_freq, "IU", 172210528Sjchandra "Timecounter frequency in Hz"); 173210528Sjchandra 174210528Sjchandrastatic unsigned 175210528Sjchandracounter_get_timecount(struct timecounter *tc) 176210528Sjchandra{ 177210528Sjchandra 178210528Sjchandra return (mips_rd_count()); 179210528Sjchandra} 180210528Sjchandra 181210528Sjchandra/* 182210528Sjchandra * Wait for about n microseconds (at least!). 183210528Sjchandra */ 184210528Sjchandravoid 185210528SjchandraDELAY(int n) 186210528Sjchandra{ 187210528Sjchandra uint32_t cur, last, delta, usecs; 188210528Sjchandra 189210528Sjchandra /* 190210528Sjchandra * This works by polling the timer and counting the number of 191210528Sjchandra * microseconds that go by. 192210528Sjchandra */ 193210528Sjchandra last = mips_rd_count(); 194210528Sjchandra delta = usecs = 0; 195210528Sjchandra 196210528Sjchandra while (n > usecs) { 197210528Sjchandra cur = mips_rd_count(); 198210528Sjchandra 199210528Sjchandra /* Check to see if the timer has wrapped around. */ 200210528Sjchandra if (cur < last) 201210528Sjchandra delta += cur + (0xffffffff - last) + 1; 202210528Sjchandra else 203210528Sjchandra delta += cur - last; 204210528Sjchandra 205210528Sjchandra last = cur; 206210528Sjchandra 207210528Sjchandra if (delta >= cycles_per_usec) { 208210528Sjchandra usecs += delta / cycles_per_usec; 209210528Sjchandra delta %= cycles_per_usec; 210210528Sjchandra } 211210528Sjchandra } 212210528Sjchandra} 213210528Sjchandra 214210528Sjchandrastatic int 215210528Sjchandraclock_start(struct eventtimer *et, 216210528Sjchandra struct bintime *first, struct bintime *period) 217210528Sjchandra{ 218210528Sjchandra uint32_t fdiv, div, next; 219210528Sjchandra 220210528Sjchandra if (period != NULL) { 221210528Sjchandra div = (et->et_frequency * (period->frac >> 32)) >> 32; 222210528Sjchandra if (period->sec != 0) 223210528Sjchandra div += et->et_frequency * period->sec; 224210528Sjchandra } else 225210528Sjchandra div = 0; 226210528Sjchandra if (first != NULL) { 227210528Sjchandra fdiv = (et->et_frequency * (first->frac >> 32)) >> 32; 228210528Sjchandra if (first->sec != 0) 229210528Sjchandra fdiv += et->et_frequency * first->sec; 230210528Sjchandra } else 231210528Sjchandra fdiv = div; 232210528Sjchandra DPCPU_SET(cycles_per_tick, div); 233210528Sjchandra next = mips_rd_count() + fdiv; 234210528Sjchandra DPCPU_SET(compare_ticks, next); 235210528Sjchandra mips_wr_compare(next); 236210528Sjchandra return (0); 237210528Sjchandra} 238210528Sjchandra 239210528Sjchandrastatic int 240210528Sjchandraclock_stop(struct eventtimer *et) 241210528Sjchandra{ 242210528Sjchandra 243210528Sjchandra DPCPU_SET(cycles_per_tick, 0); 244210528Sjchandra mips_wr_compare(0xffffffff); 245210528Sjchandra return (0); 246210528Sjchandra} 247210528Sjchandra 248210528Sjchandra/* 249210528Sjchandra * Device section of file below 250210528Sjchandra */ 251210528Sjchandrastatic int 252210528Sjchandraclock_intr(void *arg) 253210528Sjchandra{ 254210528Sjchandra struct clock_softc *sc = (struct clock_softc *)arg; 255210528Sjchandra uint32_t cycles_per_tick; 256210528Sjchandra uint32_t count, compare_last, compare_next, lost_ticks; 257210528Sjchandra 258210528Sjchandra cycles_per_tick = DPCPU_GET(cycles_per_tick); 259210528Sjchandra /* 260210528Sjchandra * Set next clock edge. 261210528Sjchandra */ 262210528Sjchandra count = mips_rd_count(); 263210528Sjchandra compare_last = DPCPU_GET(compare_ticks); 264210528Sjchandra if (cycles_per_tick > 0) { 265210528Sjchandra compare_next = count + cycles_per_tick; 266210528Sjchandra DPCPU_SET(compare_ticks, compare_next); 267210528Sjchandra mips_wr_compare(compare_next); 268210528Sjchandra } else /* In one-shot mode timer should be stopped after the event. */ 269210528Sjchandra mips_wr_compare(0xffffffff); 270210528Sjchandra 271210528Sjchandra critical_enter(); 272210542Sjchandra if (count < DPCPU_GET(counter_lower_last)) { 273210542Sjchandra DPCPU_SET(counter_upper, DPCPU_GET(counter_upper) + 1); 274210542Sjchandra DPCPU_SET(counter_lower_last, count); 275210528Sjchandra } 276210528Sjchandra 277210528Sjchandra if (cycles_per_tick > 0) { 278210528Sjchandra 279210528Sjchandra /* 280210528Sjchandra * Account for the "lost time" between when the timer interrupt 281210528Sjchandra * fired and when 'clock_intr' actually started executing. 282210528Sjchandra */ 283210528Sjchandra lost_ticks = DPCPU_GET(lost_ticks); 284210528Sjchandra lost_ticks += count - compare_last; 285210528Sjchandra 286210528Sjchandra /* 287210528Sjchandra * If the COUNT and COMPARE registers are no longer in sync 288210528Sjchandra * then make up some reasonable value for the 'lost_ticks'. 289210528Sjchandra * 290210528Sjchandra * This could happen, for e.g., after we resume normal 291210528Sjchandra * operations after exiting the debugger. 292210528Sjchandra */ 293210528Sjchandra if (lost_ticks > 2 * cycles_per_tick) 294210528Sjchandra lost_ticks = cycles_per_tick; 295210528Sjchandra 296210528Sjchandra while (lost_ticks >= cycles_per_tick) { 297210528Sjchandra if (sc->et.et_active) 298210528Sjchandra sc->et.et_event_cb(&sc->et, sc->et.et_arg); 299210528Sjchandra lost_ticks -= cycles_per_tick; 300210528Sjchandra } 301210528Sjchandra DPCPU_SET(lost_ticks, lost_ticks); 302210528Sjchandra } 303210528Sjchandra if (sc->et.et_active) 304210528Sjchandra sc->et.et_event_cb(&sc->et, sc->et.et_arg); 305210528Sjchandra critical_exit(); 306210528Sjchandra return (FILTER_HANDLED); 307210528Sjchandra} 308210528Sjchandra 309210528Sjchandrastatic int 310210528Sjchandraclock_probe(device_t dev) 311210528Sjchandra{ 312210528Sjchandra 313210528Sjchandra if (device_get_unit(dev) != 0) 314210528Sjchandra panic("can't attach more clocks"); 315210528Sjchandra 316210528Sjchandra device_set_desc(dev, "Generic MIPS32 ticker"); 317210528Sjchandra return (0); 318210528Sjchandra} 319210528Sjchandra 320210528Sjchandrastatic void 321210528Sjchandraclock_identify(driver_t * drv, device_t parent) 322210528Sjchandra{ 323210528Sjchandra 324210528Sjchandra BUS_ADD_CHILD(parent, 0, "clock", 0); 325210528Sjchandra} 326210528Sjchandra 327210528Sjchandrastatic int 328210528Sjchandraclock_attach(device_t dev) 329210528Sjchandra{ 330210528Sjchandra struct clock_softc *sc; 331210528Sjchandra 332210528Sjchandra softc = sc = device_get_softc(dev); 333210528Sjchandra cpu_establish_hardintr("compare", clock_intr, NULL, 334210528Sjchandra sc, IRQ_TIMER, INTR_TYPE_CLK, &sc->intr_handler); 335210528Sjchandra 336210528Sjchandra sc->tc.tc_get_timecount = counter_get_timecount; 337210528Sjchandra sc->tc.tc_counter_mask = 0xffffffff; 338210528Sjchandra sc->tc.tc_frequency = counter_freq; 339210528Sjchandra sc->tc.tc_name = "MIPS32"; 340210528Sjchandra sc->tc.tc_quality = 800; 341210528Sjchandra sc->tc.tc_priv = sc; 342210528Sjchandra tc_init(&sc->tc); 343210528Sjchandra sc->et.et_name = "MIPS32"; 344210528Sjchandra sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | 345210528Sjchandra ET_FLAGS_PERCPU; 346210528Sjchandra sc->et.et_quality = 800; 347210528Sjchandra sc->et.et_frequency = counter_freq; 348210528Sjchandra sc->et.et_min_period.sec = 0; 349210528Sjchandra sc->et.et_min_period.frac = 0x00004000LLU << 32; /* To be safe. */ 350210528Sjchandra sc->et.et_max_period.sec = 0xfffffffeU / sc->et.et_frequency; 351210528Sjchandra sc->et.et_max_period.frac = 352210528Sjchandra ((0xfffffffeLLU << 32) / sc->et.et_frequency) << 32; 353210528Sjchandra sc->et.et_start = clock_start; 354210528Sjchandra sc->et.et_stop = clock_stop; 355210528Sjchandra sc->et.et_priv = sc; 356210528Sjchandra et_register(&sc->et); 357210528Sjchandra return (0); 358210528Sjchandra} 359210528Sjchandra 360210528Sjchandrastatic device_method_t clock_methods[] = { 361210528Sjchandra /* Device interface */ 362210528Sjchandra DEVMETHOD(device_probe, clock_probe), 363210528Sjchandra DEVMETHOD(device_identify, clock_identify), 364210528Sjchandra DEVMETHOD(device_attach, clock_attach), 365210528Sjchandra DEVMETHOD(device_detach, bus_generic_detach), 366210528Sjchandra DEVMETHOD(device_shutdown, bus_generic_shutdown), 367210528Sjchandra 368210528Sjchandra {0, 0} 369210528Sjchandra}; 370210528Sjchandra 371210528Sjchandrastatic driver_t clock_driver = { 372210528Sjchandra "clock", 373210528Sjchandra clock_methods, 374210528Sjchandra sizeof(struct clock_softc), 375210528Sjchandra}; 376210528Sjchandra 377210528Sjchandrastatic devclass_t clock_devclass; 378210528Sjchandra 379210528SjchandraDRIVER_MODULE(clock, nexus, clock_driver, clock_devclass, 0, 0); 380