The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/mips/nlm/tick.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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: releng/10.2/sys/mips/nlm/tick.c 265999 2014-05-14 01:35:43Z ian $");
   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 
   59 uint64_t counter_freq;
   60 
   61 struct timecounter *platform_timecounter;
   62 
   63 static DPCPU_DEFINE(uint32_t, cycles_per_tick);
   64 static uint32_t cycles_per_usec;
   65 
   66 static DPCPU_DEFINE(volatile uint32_t, counter_upper);
   67 static DPCPU_DEFINE(volatile uint32_t, counter_lower_last);
   68 static DPCPU_DEFINE(uint32_t, compare_ticks);
   69 static DPCPU_DEFINE(uint32_t, lost_ticks);
   70 
   71 struct clock_softc {
   72         int intr_rid;
   73         struct resource *intr_res;
   74         void *intr_handler;
   75         struct timecounter tc;
   76         struct eventtimer et;
   77 };
   78 static struct clock_softc *softc;
   79 
   80 /*
   81  * Device methods
   82  */
   83 static int clock_probe(device_t);
   84 static void clock_identify(driver_t *, device_t);
   85 static int clock_attach(device_t);
   86 static unsigned counter_get_timecount(struct timecounter *tc);
   87 
   88 void 
   89 mips_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 
   96 void
   97 platform_initclocks(void)
   98 {
   99 
  100         if (platform_timecounter != NULL)
  101                 tc_init(platform_timecounter);
  102 }
  103 
  104 static uint64_t
  105 tick_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 
  141 void
  142 mips_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 
  162 static int
  163 sysctl_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 
  180 SYSCTL_PROC(_machdep, OID_AUTO, counter_freq, CTLTYPE_U64 | CTLFLAG_RW,
  181     NULL, 0, sysctl_machdep_counter_freq, "QU",
  182     "Timecounter frequency in Hz");
  183 
  184 static unsigned
  185 counter_get_timecount(struct timecounter *tc)
  186 {
  187 
  188         return (mips_rd_count());
  189 }
  190 
  191 /*
  192  * Wait for about n microseconds (at least!).
  193  */
  194 void
  195 DELAY(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 
  224 static int
  225 clock_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 
  244 static int
  245 clock_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  */
  256 static int
  257 clock_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 
  313 static int
  314 clock_probe(device_t dev)
  315 {
  316 
  317         if (device_get_unit(dev) != 0)
  318                 panic("can't attach more clocks");
  319 
  320         device_set_desc(dev, "Generic MIPS32 ticker");
  321         return (BUS_PROBE_NOWILDCARD);
  322 }
  323 
  324 static void
  325 clock_identify(driver_t * drv, device_t parent)
  326 {
  327 
  328         BUS_ADD_CHILD(parent, 0, "clock", 0);
  329 }
  330 
  331 static int
  332 clock_attach(device_t dev)
  333 {
  334         struct clock_softc *sc;
  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 
  364 static 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 
  375 static driver_t clock_driver = {
  376         "clock",
  377         clock_methods,
  378         sizeof(struct clock_softc),
  379 };
  380 
  381 static devclass_t clock_devclass;
  382 
  383 DRIVER_MODULE(clock, nexus, clock_driver, clock_devclass, 0, 0);

Cache object: a845c486984612ecffba68e02fa19144


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.