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: releng/10.2/sys/mips/rmi/tick.c 265999 2014-05-14 01:35:43Z ian $"); 35198607Srrs 36198607Srrs#include <sys/param.h> 37198607Srrs#include <sys/systm.h> 38198607Srrs#include <sys/sysctl.h> 39210528Sjchandra#include <sys/bus.h> 40198607Srrs#include <sys/kernel.h> 41210528Sjchandra#include <sys/module.h> 42210528Sjchandra#include <sys/rman.h> 43198607Srrs#include <sys/power.h> 44198607Srrs#include <sys/smp.h> 45210528Sjchandra#include <sys/time.h> 46210528Sjchandra#include <sys/timeet.h> 47210528Sjchandra#include <sys/timetc.h> 48210528Sjchandra 49210528Sjchandra#include <machine/hwfunc.h> 50198607Srrs#include <machine/clock.h> 51198607Srrs#include <machine/locore.h> 52198607Srrs#include <machine/md_var.h> 53210528Sjchandra#include <machine/intr_machdep.h> 54210528Sjchandra#include <mips/rmi/interrupt.h> 55198607Srrs 56210528Sjchandrauint64_t counter_freq; 57198607Srrs 58210528Sjchandrastruct timecounter *platform_timecounter; 59210528Sjchandra 60215701Sdimstatic DPCPU_DEFINE(uint32_t, cycles_per_tick); 61210528Sjchandrastatic uint32_t cycles_per_usec; 62210528Sjchandra 63215701Sdimstatic DPCPU_DEFINE(volatile uint32_t, counter_upper); 64215701Sdimstatic DPCPU_DEFINE(volatile uint32_t, counter_lower_last); 65215701Sdimstatic DPCPU_DEFINE(uint32_t, compare_ticks); 66215701Sdimstatic DPCPU_DEFINE(uint32_t, lost_ticks); 67210528Sjchandra 68210528Sjchandrastruct clock_softc { 69210528Sjchandra int intr_rid; 70210528Sjchandra struct resource *intr_res; 71210528Sjchandra void *intr_handler; 72210528Sjchandra struct timecounter tc; 73210528Sjchandra struct eventtimer et; 74198607Srrs}; 75210528Sjchandrastatic struct clock_softc *softc; 76198607Srrs 77210528Sjchandra/* 78210528Sjchandra * Device methods 79210528Sjchandra */ 80210528Sjchandrastatic int clock_probe(device_t); 81210528Sjchandrastatic void clock_identify(driver_t *, device_t); 82210528Sjchandrastatic int clock_attach(device_t); 83210528Sjchandrastatic unsigned counter_get_timecount(struct timecounter *tc); 84198607Srrs 85210528Sjchandravoid 86210528Sjchandramips_timer_early_init(uint64_t clock_hz) 87210528Sjchandra{ 88210528Sjchandra /* Initialize clock early so that we can use DELAY sooner */ 89210528Sjchandra counter_freq = clock_hz; 90210528Sjchandra cycles_per_usec = (clock_hz / (1000 * 1000)); 91210528Sjchandra} 92198607Srrs 93198607Srrsvoid 94210528Sjchandraplatform_initclocks(void) 95198607Srrs{ 96198607Srrs 97210528Sjchandra if (platform_timecounter != NULL) 98210528Sjchandra tc_init(platform_timecounter); 99198607Srrs} 100198607Srrs 101210528Sjchandrastatic uint64_t 102210528Sjchandratick_ticker(void) 103210528Sjchandra{ 104210528Sjchandra uint64_t ret; 105210528Sjchandra uint32_t ticktock; 106210542Sjchandra uint32_t t_lower_last, t_upper; 107210528Sjchandra 108210528Sjchandra /* 109211799Sjchandra * Disable preemption because we are working with cpu specific data. 110210528Sjchandra */ 111211799Sjchandra critical_enter(); 112211799Sjchandra 113211799Sjchandra /* 114211799Sjchandra * Note that even though preemption is disabled, interrupts are 115211799Sjchandra * still enabled. In particular there is a race with clock_intr() 116211799Sjchandra * reading the values of 'counter_upper' and 'counter_lower_last'. 117211799Sjchandra * 118211799Sjchandra * XXX this depends on clock_intr() being executed periodically 119211799Sjchandra * so that 'counter_upper' and 'counter_lower_last' are not stale. 120211799Sjchandra */ 121211799Sjchandra do { 122211799Sjchandra t_upper = DPCPU_GET(counter_upper); 123211799Sjchandra t_lower_last = DPCPU_GET(counter_lower_last); 124211799Sjchandra } while (t_upper != DPCPU_GET(counter_upper)); 125211799Sjchandra 126210528Sjchandra ticktock = mips_rd_count(); 127211799Sjchandra 128211799Sjchandra critical_exit(); 129211799Sjchandra 130211799Sjchandra /* COUNT register wrapped around */ 131210542Sjchandra if (ticktock < t_lower_last) 132210542Sjchandra t_upper++; 133210528Sjchandra 134211799Sjchandra ret = ((uint64_t)t_upper << 32) | ticktock; 135210528Sjchandra return (ret); 136210528Sjchandra} 137210528Sjchandra 138210528Sjchandravoid 139210528Sjchandramips_timer_init_params(uint64_t platform_counter_freq, int double_count) 140210528Sjchandra{ 141210528Sjchandra 142210528Sjchandra /* 143210528Sjchandra * XXX: Do not use printf here: uart code 8250 may use DELAY so this 144210528Sjchandra * function should be called before cninit. 145210528Sjchandra */ 146210528Sjchandra counter_freq = platform_counter_freq; 147210528Sjchandra /* 148210528Sjchandra * XXX: Some MIPS32 cores update the Count register only every two 149210528Sjchandra * pipeline cycles. 150210528Sjchandra * We know this because of status registers in CP0, make it automatic. 151210528Sjchandra */ 152210528Sjchandra if (double_count != 0) 153210528Sjchandra counter_freq /= 2; 154210528Sjchandra 155210528Sjchandra cycles_per_usec = counter_freq / (1 * 1000 * 1000); 156210528Sjchandra set_cputicker(tick_ticker, counter_freq, 1); 157210528Sjchandra} 158210528Sjchandra 159198607Srrsstatic int 160198607Srrssysctl_machdep_counter_freq(SYSCTL_HANDLER_ARGS) 161198607Srrs{ 162198607Srrs int error; 163198607Srrs uint64_t freq; 164198625Srrs 165210528Sjchandra if (softc == NULL) 166198607Srrs return (EOPNOTSUPP); 167198607Srrs freq = counter_freq; 168217616Smdf error = sysctl_handle_64(oidp, &freq, sizeof(freq), req); 169198607Srrs if (error == 0 && req->newptr != NULL) { 170198607Srrs counter_freq = freq; 171210528Sjchandra softc->et.et_frequency = counter_freq; 172210528Sjchandra softc->tc.tc_frequency = counter_freq; 173198607Srrs } 174198607Srrs return (error); 175198607Srrs} 176198607Srrs 177217616SmdfSYSCTL_PROC(_machdep, OID_AUTO, counter_freq, CTLTYPE_U64 | CTLFLAG_RW, 178217616Smdf NULL, 0, sysctl_machdep_counter_freq, "QU", 179210528Sjchandra "Timecounter frequency in Hz"); 180210528Sjchandra 181210528Sjchandrastatic unsigned 182210528Sjchandracounter_get_timecount(struct timecounter *tc) 183210528Sjchandra{ 184210528Sjchandra 185210528Sjchandra return (mips_rd_count()); 186210528Sjchandra} 187210528Sjchandra 188210528Sjchandra/* 189210528Sjchandra * Wait for about n microseconds (at least!). 190210528Sjchandra */ 191210528Sjchandravoid 192210528SjchandraDELAY(int n) 193210528Sjchandra{ 194210528Sjchandra uint32_t cur, last, delta, usecs; 195210528Sjchandra 196210528Sjchandra /* 197210528Sjchandra * This works by polling the timer and counting the number of 198210528Sjchandra * microseconds that go by. 199210528Sjchandra */ 200210528Sjchandra last = mips_rd_count(); 201210528Sjchandra delta = usecs = 0; 202210528Sjchandra 203210528Sjchandra while (n > usecs) { 204210528Sjchandra cur = mips_rd_count(); 205210528Sjchandra 206210528Sjchandra /* Check to see if the timer has wrapped around. */ 207210528Sjchandra if (cur < last) 208210528Sjchandra delta += cur + (0xffffffff - last) + 1; 209210528Sjchandra else 210210528Sjchandra delta += cur - last; 211210528Sjchandra 212210528Sjchandra last = cur; 213210528Sjchandra 214210528Sjchandra if (delta >= cycles_per_usec) { 215210528Sjchandra usecs += delta / cycles_per_usec; 216210528Sjchandra delta %= cycles_per_usec; 217210528Sjchandra } 218210528Sjchandra } 219210528Sjchandra} 220210528Sjchandra 221210528Sjchandrastatic int 222247463Smavclock_start(struct eventtimer *et, sbintime_t first, sbintime_t period) 223210528Sjchandra{ 224210528Sjchandra uint32_t fdiv, div, next; 225210528Sjchandra 226247463Smav if (period != 0) 227247463Smav div = (et->et_frequency * period) >> 32; 228247463Smav else 229210528Sjchandra div = 0; 230247463Smav if (first != 0) 231247463Smav fdiv = (et->et_frequency * first) >> 32; 232247463Smav else 233210528Sjchandra fdiv = div; 234210528Sjchandra DPCPU_SET(cycles_per_tick, div); 235210528Sjchandra next = mips_rd_count() + fdiv; 236210528Sjchandra DPCPU_SET(compare_ticks, next); 237210528Sjchandra mips_wr_compare(next); 238210528Sjchandra return (0); 239210528Sjchandra} 240210528Sjchandra 241210528Sjchandrastatic int 242210528Sjchandraclock_stop(struct eventtimer *et) 243210528Sjchandra{ 244210528Sjchandra 245210528Sjchandra DPCPU_SET(cycles_per_tick, 0); 246210528Sjchandra mips_wr_compare(0xffffffff); 247210528Sjchandra return (0); 248210528Sjchandra} 249210528Sjchandra 250210528Sjchandra/* 251210528Sjchandra * Device section of file below 252210528Sjchandra */ 253210528Sjchandrastatic int 254210528Sjchandraclock_intr(void *arg) 255210528Sjchandra{ 256210528Sjchandra struct clock_softc *sc = (struct clock_softc *)arg; 257210528Sjchandra uint32_t cycles_per_tick; 258210528Sjchandra uint32_t count, compare_last, compare_next, lost_ticks; 259210528Sjchandra 260210528Sjchandra cycles_per_tick = DPCPU_GET(cycles_per_tick); 261210528Sjchandra /* 262210528Sjchandra * Set next clock edge. 263210528Sjchandra */ 264210528Sjchandra count = mips_rd_count(); 265210528Sjchandra compare_last = DPCPU_GET(compare_ticks); 266210528Sjchandra if (cycles_per_tick > 0) { 267210528Sjchandra compare_next = count + cycles_per_tick; 268210528Sjchandra DPCPU_SET(compare_ticks, compare_next); 269210528Sjchandra mips_wr_compare(compare_next); 270210528Sjchandra } else /* In one-shot mode timer should be stopped after the event. */ 271210528Sjchandra mips_wr_compare(0xffffffff); 272210528Sjchandra 273211799Sjchandra /* COUNT register wrapped around */ 274210542Sjchandra if (count < DPCPU_GET(counter_lower_last)) { 275210542Sjchandra DPCPU_SET(counter_upper, DPCPU_GET(counter_upper) + 1); 276210528Sjchandra } 277211799Sjchandra DPCPU_SET(counter_lower_last, count); 278210528Sjchandra 279210528Sjchandra if (cycles_per_tick > 0) { 280210528Sjchandra 281210528Sjchandra /* 282210528Sjchandra * Account for the "lost time" between when the timer interrupt 283210528Sjchandra * fired and when 'clock_intr' actually started executing. 284210528Sjchandra */ 285210528Sjchandra lost_ticks = DPCPU_GET(lost_ticks); 286210528Sjchandra lost_ticks += count - compare_last; 287210528Sjchandra 288210528Sjchandra /* 289210528Sjchandra * If the COUNT and COMPARE registers are no longer in sync 290210528Sjchandra * then make up some reasonable value for the 'lost_ticks'. 291210528Sjchandra * 292210528Sjchandra * This could happen, for e.g., after we resume normal 293210528Sjchandra * operations after exiting the debugger. 294210528Sjchandra */ 295210528Sjchandra if (lost_ticks > 2 * cycles_per_tick) 296210528Sjchandra lost_ticks = cycles_per_tick; 297210528Sjchandra 298210528Sjchandra while (lost_ticks >= cycles_per_tick) { 299210528Sjchandra if (sc->et.et_active) 300210528Sjchandra sc->et.et_event_cb(&sc->et, sc->et.et_arg); 301210528Sjchandra lost_ticks -= cycles_per_tick; 302210528Sjchandra } 303210528Sjchandra DPCPU_SET(lost_ticks, lost_ticks); 304210528Sjchandra } 305210528Sjchandra if (sc->et.et_active) 306210528Sjchandra sc->et.et_event_cb(&sc->et, sc->et.et_arg); 307210528Sjchandra return (FILTER_HANDLED); 308210528Sjchandra} 309210528Sjchandra 310210528Sjchandrastatic int 311210528Sjchandraclock_probe(device_t dev) 312210528Sjchandra{ 313210528Sjchandra 314210528Sjchandra if (device_get_unit(dev) != 0) 315210528Sjchandra panic("can't attach more clocks"); 316210528Sjchandra 317210528Sjchandra device_set_desc(dev, "Generic MIPS32 ticker"); 318265999Sian return (BUS_PROBE_NOWILDCARD); 319210528Sjchandra} 320210528Sjchandra 321210528Sjchandrastatic void 322210528Sjchandraclock_identify(driver_t * drv, device_t parent) 323210528Sjchandra{ 324210528Sjchandra 325210528Sjchandra BUS_ADD_CHILD(parent, 0, "clock", 0); 326210528Sjchandra} 327210528Sjchandra 328210528Sjchandrastatic int 329210528Sjchandraclock_attach(device_t dev) 330210528Sjchandra{ 331210528Sjchandra struct clock_softc *sc; 332210528Sjchandra 333210528Sjchandra softc = sc = device_get_softc(dev); 334210528Sjchandra cpu_establish_hardintr("compare", clock_intr, NULL, 335210528Sjchandra sc, IRQ_TIMER, INTR_TYPE_CLK, &sc->intr_handler); 336210528Sjchandra 337210528Sjchandra sc->tc.tc_get_timecount = counter_get_timecount; 338210528Sjchandra sc->tc.tc_counter_mask = 0xffffffff; 339210528Sjchandra sc->tc.tc_frequency = counter_freq; 340210528Sjchandra sc->tc.tc_name = "MIPS32"; 341210528Sjchandra sc->tc.tc_quality = 800; 342210528Sjchandra sc->tc.tc_priv = sc; 343210528Sjchandra tc_init(&sc->tc); 344210528Sjchandra sc->et.et_name = "MIPS32"; 345210528Sjchandra sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | 346210528Sjchandra ET_FLAGS_PERCPU; 347210528Sjchandra sc->et.et_quality = 800; 348210528Sjchandra sc->et.et_frequency = counter_freq; 349247463Smav sc->et.et_min_period = 0x00004000LLU; /* To be safe. */ 350247463Smav sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency; 351210528Sjchandra sc->et.et_start = clock_start; 352210528Sjchandra sc->et.et_stop = clock_stop; 353210528Sjchandra sc->et.et_priv = sc; 354210528Sjchandra et_register(&sc->et); 355210528Sjchandra return (0); 356210528Sjchandra} 357210528Sjchandra 358210528Sjchandrastatic device_method_t clock_methods[] = { 359210528Sjchandra /* Device interface */ 360210528Sjchandra DEVMETHOD(device_probe, clock_probe), 361210528Sjchandra DEVMETHOD(device_identify, clock_identify), 362210528Sjchandra DEVMETHOD(device_attach, clock_attach), 363210528Sjchandra DEVMETHOD(device_detach, bus_generic_detach), 364210528Sjchandra DEVMETHOD(device_shutdown, bus_generic_shutdown), 365210528Sjchandra 366210528Sjchandra {0, 0} 367210528Sjchandra}; 368210528Sjchandra 369210528Sjchandrastatic driver_t clock_driver = { 370210528Sjchandra "clock", 371210528Sjchandra clock_methods, 372210528Sjchandra sizeof(struct clock_softc), 373210528Sjchandra}; 374210528Sjchandra 375210528Sjchandrastatic devclass_t clock_devclass; 376210528Sjchandra 377210528SjchandraDRIVER_MODULE(clock, nexus, clock_driver, clock_devclass, 0, 0); 378