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/arm/arm/mpcore_timer.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) 2011 The FreeBSD Foundation
    3  * All rights reserved.
    4  *
    5  * Developed by Ben Gray <ben.r.gray@gmail.com>
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  * 3. The name of the company nor the name of the author may be used to
   16  *    endorse or promote products derived from this software without specific
   17  *    prior written permission.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  */
   31 
   32 /**
   33  *      The ARM Cortex-A9 core can support a global timer plus a private and
   34  *      watchdog timer per core.  This driver reserves memory and interrupt
   35  *      resources for accessing both timer register sets, these resources are
   36  *      stored globally and used to setup the timecount and eventtimer.
   37  *
   38  *      The timecount timer uses the global 64-bit counter, whereas the
   39  *      per-CPU eventtimer uses the private 32-bit counters.
   40  *
   41  *
   42  *      REF: ARM Cortex-A9 MPCore, Technical Reference Manual (rev. r2p2)
   43  */
   44 
   45 #include <sys/cdefs.h>
   46 __FBSDID("$FreeBSD: releng/10.0/sys/arm/arm/mpcore_timer.c 253971 2013-08-05 20:14:56Z cognet $");
   47 
   48 #include <sys/param.h>
   49 #include <sys/systm.h>
   50 #include <sys/bus.h>
   51 #include <sys/kernel.h>
   52 #include <sys/module.h>
   53 #include <sys/malloc.h>
   54 #include <sys/rman.h>
   55 #include <sys/timeet.h>
   56 #include <sys/timetc.h>
   57 #include <sys/watchdog.h>
   58 #include <machine/bus.h>
   59 #include <machine/cpu.h>
   60 #include <machine/frame.h>
   61 #include <machine/intr.h>
   62 
   63 #include <dev/fdt/fdt_common.h>
   64 #include <dev/ofw/openfirm.h>
   65 #include <dev/ofw/ofw_bus.h>
   66 #include <dev/ofw/ofw_bus_subr.h>
   67 
   68 #include <machine/bus.h>
   69 #include <machine/fdt.h>
   70 
   71 /* Private (per-CPU) timer register map */
   72 #define PRV_TIMER_LOAD                 0x0000
   73 #define PRV_TIMER_COUNT                0x0004
   74 #define PRV_TIMER_CTRL                 0x0008
   75 #define PRV_TIMER_INTR                 0x000C
   76 
   77 #define PRV_TIMER_CTR_PRESCALER_SHIFT  8
   78 #define PRV_TIMER_CTRL_IRQ_ENABLE      (1UL << 2)
   79 #define PRV_TIMER_CTRL_AUTO_RELOAD     (1UL << 1)
   80 #define PRV_TIMER_CTRL_TIMER_ENABLE    (1UL << 0)
   81 
   82 #define PRV_TIMER_INTR_EVENT           (1UL << 0)
   83 
   84 /* Global timer register map */
   85 #define GBL_TIMER_COUNT_LOW            0x0000
   86 #define GBL_TIMER_COUNT_HIGH           0x0004
   87 #define GBL_TIMER_CTRL                 0x0008
   88 #define GBL_TIMER_INTR                 0x000C
   89 
   90 #define GBL_TIMER_CTR_PRESCALER_SHIFT  8
   91 #define GBL_TIMER_CTRL_AUTO_INC        (1UL << 3)
   92 #define GBL_TIMER_CTRL_IRQ_ENABLE      (1UL << 2)
   93 #define GBL_TIMER_CTRL_COMP_ENABLE     (1UL << 1)
   94 #define GBL_TIMER_CTRL_TIMER_ENABLE    (1UL << 0)
   95 
   96 #define GBL_TIMER_INTR_EVENT           (1UL << 0)
   97 
   98 struct arm_tmr_softc {
   99         struct resource *       tmr_res[4];
  100         bus_space_tag_t         prv_bst;
  101         bus_space_tag_t         gbl_bst;
  102         bus_space_handle_t      prv_bsh;
  103         bus_space_handle_t      gbl_bsh;
  104         uint32_t                clkfreq;
  105         struct eventtimer       et;
  106 };
  107 
  108 static struct resource_spec arm_tmr_spec[] = {
  109         { SYS_RES_MEMORY,       0,      RF_ACTIVE },    /* Global registers */
  110         { SYS_RES_IRQ,          0,      RF_ACTIVE },    /* Global timer interrupt (unused) */
  111         { SYS_RES_MEMORY,       1,      RF_ACTIVE },    /* Private (per-CPU) registers */
  112         { SYS_RES_IRQ,          1,      RF_ACTIVE },    /* Private timer interrupt */
  113         { -1, 0 }
  114 };
  115 
  116 static struct arm_tmr_softc *arm_tmr_sc = NULL;
  117 
  118 uint32_t platform_arm_tmr_freq = 0;
  119 
  120 #define tmr_prv_read_4(reg)             \
  121     bus_space_read_4(arm_tmr_sc->prv_bst, arm_tmr_sc->prv_bsh, reg)
  122 #define tmr_prv_write_4(reg, val)               \
  123     bus_space_write_4(arm_tmr_sc->prv_bst, arm_tmr_sc->prv_bsh, reg, val)
  124 #define tmr_gbl_read_4(reg)             \
  125     bus_space_read_4(arm_tmr_sc->gbl_bst, arm_tmr_sc->gbl_bsh, reg)
  126 #define tmr_gbl_write_4(reg, val)               \
  127     bus_space_write_4(arm_tmr_sc->gbl_bst, arm_tmr_sc->gbl_bsh, reg, val)
  128 
  129 
  130 static timecounter_get_t arm_tmr_get_timecount;
  131 
  132 static struct timecounter arm_tmr_timecount = {
  133         .tc_name           = "ARM MPCore Timecounter",
  134         .tc_get_timecount  = arm_tmr_get_timecount,
  135         .tc_poll_pps       = NULL,
  136         .tc_counter_mask   = ~0u,
  137         .tc_frequency      = 0,
  138         .tc_quality        = 1000,
  139 };
  140 
  141 /**
  142  *      arm_tmr_get_timecount - reads the timecount (global) timer
  143  *      @tc: pointer to arm_tmr_timecount struct
  144  *
  145  *      We only read the lower 32-bits, the timecount stuff only uses 32-bits
  146  *      so (for now?) ignore the upper 32-bits.
  147  *
  148  *      RETURNS
  149  *      The lower 32-bits of the counter.
  150  */
  151 static unsigned
  152 arm_tmr_get_timecount(struct timecounter *tc)
  153 {
  154         return (tmr_gbl_read_4(GBL_TIMER_COUNT_LOW));
  155 }
  156 
  157 /**
  158  *      arm_tmr_start - starts the eventtimer (private) timer
  159  *      @et: pointer to eventtimer struct
  160  *      @first: the number of seconds and fractional sections to trigger in
  161  *      @period: the period (in seconds and fractional sections) to set
  162  *
  163  *      If the eventtimer is required to be in oneshot mode, period will be
  164  *      NULL and first will point to the time to trigger.  If in periodic mode
  165  *      period will contain the time period and first may optionally contain
  166  *      the time for the first period.
  167  *
  168  *      RETURNS
  169  *      Always returns 0
  170  */
  171 static int
  172 arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
  173 {
  174         uint32_t load, count;
  175         uint32_t ctrl;
  176 
  177         ctrl = PRV_TIMER_CTRL_IRQ_ENABLE | PRV_TIMER_CTRL_TIMER_ENABLE;
  178 
  179         if (period != 0) {
  180                 load = ((uint32_t)et->et_frequency * period) >> 32;
  181                 ctrl |= PRV_TIMER_CTRL_AUTO_RELOAD;
  182         } else
  183                 load = 0;
  184 
  185         if (first != 0)
  186                 count = ((uint32_t)et->et_frequency * first) >> 32;
  187         else
  188                 count = load;
  189 
  190         tmr_prv_write_4(PRV_TIMER_LOAD, load);
  191         tmr_prv_write_4(PRV_TIMER_COUNT, count);
  192 
  193         tmr_prv_write_4(PRV_TIMER_CTRL, ctrl);
  194         return (0);
  195 }
  196 
  197 /**
  198  *      arm_tmr_stop - stops the eventtimer (private) timer
  199  *      @et: pointer to eventtimer struct
  200  *
  201  *      Simply stops the private timer by clearing all bits in the ctrl register.
  202  *
  203  *      RETURNS
  204  *      Always returns 0
  205  */
  206 static int
  207 arm_tmr_stop(struct eventtimer *et)
  208 {
  209         tmr_prv_write_4(PRV_TIMER_CTRL, 0);
  210         return (0);
  211 }
  212 
  213 /**
  214  *      arm_tmr_intr - ISR for the eventtimer (private) timer
  215  *      @arg: pointer to arm_tmr_softc struct
  216  *
  217  *      Clears the event register and then calls the eventtimer callback.
  218  *
  219  *      RETURNS
  220  *      Always returns FILTER_HANDLED
  221  */
  222 static int
  223 arm_tmr_intr(void *arg)
  224 {
  225         struct arm_tmr_softc *sc = (struct arm_tmr_softc *)arg;
  226 
  227         tmr_prv_write_4(PRV_TIMER_INTR, PRV_TIMER_INTR_EVENT);
  228 
  229         if (sc->et.et_active)
  230                 sc->et.et_event_cb(&sc->et, sc->et.et_arg);
  231 
  232         return (FILTER_HANDLED);
  233 }
  234 
  235 
  236 
  237 
  238 /**
  239  *      arm_tmr_probe - timer probe routine
  240  *      @dev: new device
  241  *
  242  *      The probe function returns success when probed with the fdt compatible
  243  *      string set to "arm,mpcore-timers".
  244  *
  245  *      RETURNS
  246  *      BUS_PROBE_DEFAULT if the fdt device is compatible, otherwise ENXIO.
  247  */
  248 static int
  249 arm_tmr_probe(device_t dev)
  250 {
  251         if (!ofw_bus_is_compatible(dev, "arm,mpcore-timers"))
  252                 return (ENXIO);
  253 
  254         device_set_desc(dev, "ARM Generic MPCore Timers");
  255         return (BUS_PROBE_DEFAULT);
  256 }
  257 
  258 /**
  259  *      arm_tmr_attach - attaches the timer to the simplebus
  260  *      @dev: new device
  261  *
  262  *      Reserves memory and interrupt resources, stores the softc structure
  263  *      globally and registers both the timecount and eventtimer objects.
  264  *
  265  *      RETURNS
  266  *      Zero on sucess or ENXIO if an error occuried.
  267  */
  268 static int
  269 arm_tmr_attach(device_t dev)
  270 {
  271         struct arm_tmr_softc *sc = device_get_softc(dev);
  272         phandle_t node;
  273         pcell_t clock;
  274         void *ihl;
  275 
  276         if (arm_tmr_sc)
  277                 return (ENXIO);
  278 
  279         if (platform_arm_tmr_freq != 0)
  280                 sc->clkfreq = platform_arm_tmr_freq;
  281         else {
  282                 /* Get the base clock frequency */
  283                 node = ofw_bus_get_node(dev);
  284                 if ((OF_getprop(node, "clock-frequency", &clock,
  285                     sizeof(clock))) <= 0) {
  286                         device_printf(dev, "missing clock-frequency attribute in FDT\n");
  287                         return (ENXIO);
  288                 }
  289                 sc->clkfreq = fdt32_to_cpu(clock);
  290         }
  291 
  292 
  293         if (bus_alloc_resources(dev, arm_tmr_spec, sc->tmr_res)) {
  294                 device_printf(dev, "could not allocate resources\n");
  295                 return (ENXIO);
  296         }
  297 
  298         /* Global timer interface */
  299         sc->gbl_bst = rman_get_bustag(sc->tmr_res[0]);
  300         sc->gbl_bsh = rman_get_bushandle(sc->tmr_res[0]);
  301 
  302         /* Private per-CPU timer interface */
  303         sc->prv_bst = rman_get_bustag(sc->tmr_res[2]);
  304         sc->prv_bsh = rman_get_bushandle(sc->tmr_res[2]);
  305 
  306         arm_tmr_sc = sc;
  307 
  308         /* Disable both timers to start off */
  309         tmr_prv_write_4(PRV_TIMER_CTRL, 0x00000000);
  310         tmr_gbl_write_4(GBL_TIMER_CTRL, 0x00000000);
  311 
  312         /* Setup and enable the global timer to use as the timecounter */
  313         tmr_gbl_write_4(GBL_TIMER_CTRL, (0x00 << GBL_TIMER_CTR_PRESCALER_SHIFT) | 
  314                                         GBL_TIMER_CTRL_TIMER_ENABLE);
  315 
  316         arm_tmr_timecount.tc_frequency = sc->clkfreq;
  317         tc_init(&arm_tmr_timecount);
  318 
  319         /* Setup and enable the timer */
  320         if (bus_setup_intr(dev, sc->tmr_res[3], INTR_TYPE_CLK, arm_tmr_intr,
  321                         NULL, sc, &ihl) != 0) {
  322                 bus_release_resources(dev, arm_tmr_spec, sc->tmr_res);
  323                 device_printf(dev, "Unable to setup the clock irq handler.\n");
  324                 return (ENXIO);
  325         }
  326 
  327         sc->et.et_name = "ARM MPCore Eventtimer";
  328         sc->et.et_flags = ET_FLAGS_PERIODIC | ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
  329         sc->et.et_quality = 1000;
  330 
  331         sc->et.et_frequency = sc->clkfreq;
  332         sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency;
  333         sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
  334         sc->et.et_start = arm_tmr_start;
  335         sc->et.et_stop = arm_tmr_stop;
  336         sc->et.et_priv = sc;
  337         et_register(&sc->et);
  338 
  339         return (0);
  340 }
  341 
  342 static device_method_t arm_tmr_methods[] = {
  343         DEVMETHOD(device_probe,         arm_tmr_probe),
  344         DEVMETHOD(device_attach,        arm_tmr_attach),
  345         { 0, 0 }
  346 };
  347 
  348 static driver_t arm_tmr_driver = {
  349         "mp_tmr",
  350         arm_tmr_methods,
  351         sizeof(struct arm_tmr_softc),
  352 };
  353 
  354 static devclass_t arm_tmr_devclass;
  355 
  356 DRIVER_MODULE(mp_tmr, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0);
  357 
  358 /**
  359  *      cpu_initclocks - called by system to initialise the cpu clocks
  360  *
  361  *      This is a boilerplat function, most of the setup has already been done
  362  *      when the driver was attached.  Therefore this function must only be called
  363  *      after the driver is attached.
  364  *
  365  *      RETURNS
  366  *      nothing
  367  */
  368 void
  369 cpu_initclocks(void)
  370 {
  371         if (PCPU_GET(cpuid) == 0)
  372                 cpu_initclocks_bsp();
  373         else
  374                 cpu_initclocks_ap();
  375 }
  376 
  377 /**
  378  *      DELAY - Delay for at least usec microseconds.
  379  *      @usec: number of microseconds to delay by
  380  *
  381  *      This function is called all over the kernel and is suppose to provide a
  382  *      consistent delay.  This function may also be called before the console 
  383  *      is setup so no printf's can be called here.
  384  *
  385  *      RETURNS:
  386  *      nothing
  387  */
  388 void
  389 DELAY(int usec)
  390 {
  391         int32_t counts_per_usec;
  392         int32_t counts;
  393         uint32_t first, last;
  394 
  395         /* Check the timers are setup, if not just use a for loop for the meantime */
  396         if (arm_tmr_sc == NULL) {
  397                 for (; usec > 0; usec--)
  398                         for (counts = 200; counts > 0; counts--)
  399                                 cpufunc_nullop();       /* Prevent gcc from optimizing
  400                                                          * out the loop
  401                                                          */
  402                 return;
  403         }
  404 
  405         /* Get the number of times to count */
  406         counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
  407 
  408         /*
  409          * Clamp the timeout at a maximum value (about 32 seconds with
  410          * a 66MHz clock). *Nobody* should be delay()ing for anywhere
  411          * near that length of time and if they are, they should be hung
  412          * out to dry.
  413          */
  414         if (usec >= (0x80000000U / counts_per_usec))
  415                 counts = (0x80000000U / counts_per_usec) - 1;
  416         else
  417                 counts = usec * counts_per_usec;
  418 
  419         first = tmr_gbl_read_4(GBL_TIMER_COUNT_LOW);
  420 
  421         while (counts > 0) {
  422                 last = tmr_gbl_read_4(GBL_TIMER_COUNT_LOW);
  423                 counts -= (int32_t)(last - first);
  424                 first = last;
  425         }
  426 }

Cache object: ad11a75fa87a08f846551e9eb775b06c


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