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/mips/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 (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: releng/8.0/sys/mips/mips/tick.c 181236 2008-08-03 14:11:06Z trhodes $");
   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/timetc.h>
   47 
   48 #include <machine/clock.h>
   49 #include <machine/locore.h>
   50 #include <machine/md_var.h>
   51 
   52 uint64_t counter_freq;
   53 uint64_t cycles_per_tick;
   54 uint64_t cycles_per_usec;
   55 uint64_t cycles_per_sec;
   56 uint64_t cycles_per_hz;
   57 
   58 u_int32_t counter_upper = 0;
   59 u_int32_t counter_lower_last = 0;
   60 int     tick_started = 0;
   61 
   62 struct clk_ticks
   63 {
   64         u_long hard_ticks;
   65         u_long stat_ticks;
   66         u_long prof_ticks;
   67         /*
   68          * pad for cache line alignment of pcpu info
   69          * cache-line-size - number of used bytes
   70          */
   71         char   pad[32-(3*sizeof (u_long))];
   72 } static pcpu_ticks[MAXCPU];
   73 
   74 /*
   75  * Device methods
   76  */
   77 static int clock_probe(device_t);
   78 static void clock_identify(driver_t *, device_t);
   79 static int clock_attach(device_t);
   80 static unsigned counter_get_timecount(struct timecounter *tc);
   81 
   82 static struct timecounter counter_timecounter = {
   83         counter_get_timecount,  /* get_timecount */
   84         0,                      /* no poll_pps */
   85         0xffffffffu,            /* counter_mask */
   86         0,                      /* frequency */
   87         "MIPS32",               /* name */
   88         800,                    /* quality (adjusted in code) */
   89 };
   90 
   91 void 
   92 mips_timer_early_init(uint64_t clock_hz)
   93 {
   94         /* Initialize clock early so that we can use DELAY sooner */
   95         counter_freq = clock_hz;
   96         cycles_per_usec = (clock_hz / (1000 * 1000));
   97 }
   98 
   99 void
  100 cpu_initclocks(void)
  101 {
  102 
  103         if (!tick_started) {
  104                 tc_init(&counter_timecounter);
  105                 tick_started++;
  106         }
  107 }
  108 
  109 static uint64_t
  110 tick_ticker(void)
  111 {
  112         uint64_t ret;
  113         uint32_t ticktock;
  114 
  115         /*
  116          * XXX: MIPS64 platforms can read 64-bits of counter directly.
  117          * Also: the tc code is supposed to cope with things wrapping
  118          * from the time counter, so I'm not sure why all these hoops
  119          * are even necessary.
  120          */
  121         ticktock = mips_rd_count();
  122         critical_enter();
  123         if (ticktock < counter_lower_last)
  124                 counter_upper++;
  125         counter_lower_last = ticktock;
  126         critical_exit();
  127 
  128         ret = ((uint64_t) counter_upper << 32) | counter_lower_last;
  129         return (ret);
  130 }
  131 
  132 void
  133 mips_timer_init_params(uint64_t platform_counter_freq, int double_count)
  134 {
  135 
  136         /*
  137          * XXX: Do not use printf here: uart code 8250 may use DELAY so this
  138          * function should  be called before cninit.
  139          */
  140         counter_freq = platform_counter_freq;
  141         cycles_per_tick = counter_freq / 1000;
  142         if (double_count)
  143                 cycles_per_tick *= 2;
  144         cycles_per_hz = counter_freq / hz;
  145         cycles_per_usec = counter_freq / (1 * 1000 * 1000);
  146         cycles_per_sec =  counter_freq ;
  147         
  148         counter_timecounter.tc_frequency = counter_freq;
  149         /*
  150          * XXX: Some MIPS32 cores update the Count register only every two
  151          * pipeline cycles.
  152          * XXX2: We can read this from the hardware register on some
  153          * systems.  Need to investigate.
  154          */
  155         if (double_count != 0) {
  156                 cycles_per_hz /= 2;
  157                 cycles_per_usec /= 2;
  158                 cycles_per_sec /= 2;
  159         }
  160         printf("hz=%d cyl_per_hz:%jd cyl_per_usec:%jd freq:%jd cyl_per_hz:%jd cyl_per_sec:%jd\n",
  161                hz,
  162                cycles_per_tick,
  163                cycles_per_usec,
  164                counter_freq,
  165                cycles_per_hz,
  166                cycles_per_sec
  167                );
  168         set_cputicker(tick_ticker, counter_freq, 1);
  169 }
  170 
  171 static int
  172 sysctl_machdep_counter_freq(SYSCTL_HANDLER_ARGS)
  173 {
  174         int error;
  175         uint64_t freq;
  176 
  177         if (counter_timecounter.tc_frequency == 0)
  178                 return (EOPNOTSUPP);
  179         freq = counter_freq;
  180         error = sysctl_handle_int(oidp, &freq, sizeof(freq), req);
  181         if (error == 0 && req->newptr != NULL) {
  182                 counter_freq = freq;
  183                 counter_timecounter.tc_frequency = counter_freq;
  184         }
  185         return (error);
  186 }
  187 
  188 SYSCTL_PROC(_machdep, OID_AUTO, counter_freq, CTLTYPE_QUAD | CTLFLAG_RW,
  189     0, sizeof(u_int), sysctl_machdep_counter_freq, "IU",
  190     "Timecounter frequency in Hz");
  191 
  192 static unsigned
  193 counter_get_timecount(struct timecounter *tc)
  194 {
  195 
  196         return (mips_rd_count());
  197 }
  198 
  199 
  200 void
  201 cpu_startprofclock(void)
  202 {
  203         /* nothing to do */
  204 }
  205 
  206 void
  207 cpu_stopprofclock(void)
  208 {
  209         /* nothing to do */
  210 }
  211 
  212 /*
  213  * Wait for about n microseconds (at least!).
  214  */
  215 void
  216 DELAY(int n)
  217 {
  218         uint32_t cur, last, delta, usecs;
  219 
  220         /*
  221          * This works by polling the timer and counting the number of
  222          * microseconds that go by.
  223          */
  224         last = mips_rd_count();
  225         delta = usecs = 0;
  226 
  227         while (n > usecs) {
  228                 cur = mips_rd_count();
  229 
  230                 /* Check to see if the timer has wrapped around. */
  231                 if (cur < last)
  232                         delta += (cur + (cycles_per_hz - last));
  233                 else
  234                         delta += (cur - last);
  235 
  236                 last = cur;
  237 
  238                 if (delta >= cycles_per_usec) {
  239                         usecs += delta / cycles_per_usec;
  240                         delta %= cycles_per_usec;
  241                 }
  242         }
  243 }
  244 
  245 #ifdef TARGET_OCTEON
  246 int64_t wheel_run = 0;
  247 
  248 void octeon_led_run_wheel(void);
  249 
  250 #endif
  251 /*
  252  * Device section of file below
  253  */
  254 static int
  255 clock_intr(void *arg)
  256 {
  257         struct clk_ticks *cpu_ticks;
  258         struct trapframe *tf;
  259         uint32_t ltick;
  260         /*
  261          * Set next clock edge.
  262          */
  263         ltick = mips_rd_count();
  264         mips_wr_compare(ltick + cycles_per_tick);
  265         cpu_ticks = &pcpu_ticks[PCPU_GET(cpuid)];
  266         critical_enter();
  267         if (ltick < counter_lower_last) {
  268                 counter_upper++;
  269                 counter_lower_last = ltick;
  270         }
  271         /*
  272          * Magic.  Setting up with an arg of NULL means we get passed tf.
  273          */
  274         tf = (struct trapframe *)arg;
  275 
  276         /* Fire hardclock at hz. */
  277         cpu_ticks->hard_ticks += cycles_per_tick;
  278         if (cpu_ticks->hard_ticks >= cycles_per_hz) {
  279                 cpu_ticks->hard_ticks -= cycles_per_hz;
  280                 if (PCPU_GET(cpuid) == 0)
  281                         hardclock(USERMODE(tf->sr), tf->pc);
  282                 else
  283                         hardclock_cpu(USERMODE(tf->sr));
  284         }
  285         /* Fire statclock at stathz. */
  286         cpu_ticks->stat_ticks += stathz;
  287         if (cpu_ticks->stat_ticks >= cycles_per_hz) {
  288                 cpu_ticks->stat_ticks -= cycles_per_hz;
  289                 statclock(USERMODE(tf->sr));
  290         }
  291 
  292         /* Fire profclock at profhz, but only when needed. */
  293         cpu_ticks->prof_ticks += profhz;
  294         if (cpu_ticks->prof_ticks >= cycles_per_hz) {
  295                 cpu_ticks->prof_ticks -= cycles_per_hz;
  296                 if (profprocs != 0)
  297                         profclock(USERMODE(tf->sr), tf->pc);
  298         }
  299         critical_exit();
  300 #ifdef TARGET_OCTEON
  301         /* Run the FreeBSD display once every hz ticks  */
  302         wheel_run += cycles_per_tick;
  303         if (wheel_run >= cycles_per_sec) {
  304                 wheel_run = 0;
  305                 octeon_led_run_wheel();
  306         }
  307 #endif
  308         return (FILTER_HANDLED);
  309 }
  310 
  311 static int
  312 clock_probe(device_t dev)
  313 {
  314 
  315         if (device_get_unit(dev) != 0)
  316                 panic("can't attach more clocks");
  317 
  318         device_set_desc(dev, "Generic MIPS32 ticker");
  319         return (0);
  320 }
  321 
  322 static void
  323 clock_identify(driver_t * drv, device_t parent)
  324 {
  325 
  326         BUS_ADD_CHILD(parent, 0, "clock", 0);
  327 }
  328 
  329 static int
  330 clock_attach(device_t dev)
  331 {
  332         struct resource *irq;
  333         int error;
  334         int rid;
  335 
  336         rid = 0;
  337         irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 5, 5, 1, RF_ACTIVE);
  338         if (irq == NULL) {
  339                 device_printf(dev, "failed to allocate irq\n");
  340                 return (ENXIO);
  341         }
  342         error = bus_setup_intr(dev, irq, INTR_TYPE_CLK, clock_intr, NULL,
  343             NULL, NULL);
  344 
  345         if (error != 0) {
  346                 device_printf(dev, "bus_setup_intr returned %d\n", error);
  347                 return (error);
  348         }
  349         mips_wr_compare(mips_rd_count() + counter_freq / hz);
  350         return (0);
  351 }
  352 
  353 static device_method_t clock_methods[] = {
  354         /* Device interface */
  355         DEVMETHOD(device_probe, clock_probe),
  356         DEVMETHOD(device_identify, clock_identify),
  357         DEVMETHOD(device_attach, clock_attach),
  358         DEVMETHOD(device_detach, bus_generic_detach),
  359         DEVMETHOD(device_shutdown, bus_generic_shutdown),
  360 
  361         {0, 0}
  362 };
  363 
  364 static driver_t clock_driver = {
  365         "clock", clock_methods, 32
  366 };
  367 
  368 static devclass_t clock_devclass;
  369 
  370 DRIVER_MODULE(clock, nexus, clock_driver, clock_devclass, 0, 0);

Cache object: be63d2478651372988d1cf47bcacebbc


[ 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.