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/generic_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  * Copyright (c) 2013 Ruslan Bukin <br@bsdpad.com>
    4  * All rights reserved.
    5  *
    6  * Based on mpcore_timer.c developed by Ben Gray <ben.r.gray@gmail.com>
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. The name of the company nor the name of the author may be used to
   17  *    endorse or promote products derived from this software without specific
   18  *    prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   23  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   30  * SUCH DAMAGE.
   31  */
   32 
   33 /**
   34  *      Cortex-A15 (and probably A7) Generic Timer
   35  */
   36 
   37 #include <sys/cdefs.h>
   38 __FBSDID("$FreeBSD: releng/10.2/sys/arm/arm/generic_timer.c 275764 2014-12-14 15:41:56Z andrew $");
   39 
   40 #include <sys/param.h>
   41 #include <sys/systm.h>
   42 #include <sys/bus.h>
   43 #include <sys/kernel.h>
   44 #include <sys/module.h>
   45 #include <sys/malloc.h>
   46 #include <sys/rman.h>
   47 #include <sys/timeet.h>
   48 #include <sys/timetc.h>
   49 #include <sys/watchdog.h>
   50 #include <machine/bus.h>
   51 #include <machine/cpu.h>
   52 #include <machine/intr.h>
   53 
   54 #include <dev/fdt/fdt_common.h>
   55 #include <dev/ofw/openfirm.h>
   56 #include <dev/ofw/ofw_bus.h>
   57 #include <dev/ofw/ofw_bus_subr.h>
   58 
   59 #include <machine/bus.h>
   60 #include <machine/fdt.h>
   61 
   62 #define GT_CTRL_ENABLE          (1 << 0)
   63 #define GT_CTRL_INT_MASK        (1 << 1)
   64 #define GT_CTRL_INT_STAT        (1 << 2)
   65 #define GT_REG_CTRL             0
   66 #define GT_REG_TVAL             1
   67 
   68 #define GT_CNTKCTL_PL0PTEN      (1 << 9) /* PL0 Physical timer reg access */
   69 #define GT_CNTKCTL_PL0VTEN      (1 << 8) /* PL0 Virtual timer reg access */
   70 #define GT_CNTKCTL_EVNTI        (1 << 4) /* Virtual counter event bits */
   71 #define GT_CNTKCTL_EVNTDIR      (1 << 3) /* Virtual counter event transition */
   72 #define GT_CNTKCTL_EVNTEN       (1 << 2) /* Enables virtual counter events */
   73 #define GT_CNTKCTL_PL0VCTEN     (1 << 1) /* PL0 CNTVCT and CNTFRQ access */
   74 #define GT_CNTKCTL_PL0PCTEN     (1 << 0) /* PL0 CNTPCT and CNTFRQ access */
   75 
   76 struct arm_tmr_softc {
   77         struct resource         *res[4];
   78         void                    *ihl[4];
   79         uint32_t                clkfreq;
   80         struct eventtimer       et;
   81 };
   82 
   83 static struct arm_tmr_softc *arm_tmr_sc = NULL;
   84 
   85 static struct resource_spec timer_spec[] = {
   86         { SYS_RES_IRQ,          0,      RF_ACTIVE },    /* Secure */
   87         { SYS_RES_IRQ,          1,      RF_ACTIVE },    /* Non-secure */
   88         { SYS_RES_IRQ,          2,      RF_ACTIVE },    /* Virt */
   89         { SYS_RES_IRQ,          3,      RF_ACTIVE | RF_OPTIONAL }, /* Hyp */
   90         { -1, 0 }
   91 };
   92 
   93 static timecounter_get_t arm_tmr_get_timecount;
   94 
   95 static struct timecounter arm_tmr_timecount = {
   96         .tc_name           = "ARM MPCore Timecounter",
   97         .tc_get_timecount  = arm_tmr_get_timecount,
   98         .tc_poll_pps       = NULL,
   99         .tc_counter_mask   = ~0u,
  100         .tc_frequency      = 0,
  101         .tc_quality        = 1000,
  102 };
  103 
  104 static inline int
  105 get_freq(void)
  106 {
  107         uint32_t val;
  108 
  109         __asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
  110 
  111         return (val);
  112 }
  113 
  114 static inline int
  115 set_freq(uint32_t val)
  116 {
  117 
  118         __asm volatile("mcr p15, 0, %[val], c14, c0, 0" : :
  119             [val] "r" (val));
  120         isb();
  121 
  122         return (val);
  123 }
  124 
  125 
  126 static inline long
  127 get_cntpct(void)
  128 {
  129         uint64_t val;
  130 
  131         __asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val));
  132 
  133         return (val);
  134 }
  135 
  136 static inline int
  137 set_ctrl(uint32_t val)
  138 {
  139 
  140         __asm volatile("mcr p15, 0, %[val], c14, c2, 1" : :
  141             [val] "r" (val));
  142         isb();
  143 
  144         return (0);
  145 }
  146 
  147 static inline int
  148 set_tval(uint32_t val)
  149 {
  150 
  151         __asm volatile("mcr p15, 0, %[val], c14, c2, 0" : :
  152             [val] "r" (val));
  153         isb();
  154 
  155         return (0);
  156 }
  157 
  158 static inline int
  159 get_ctrl(void)
  160 {
  161         uint32_t val;
  162 
  163         __asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
  164 
  165         return (val);
  166 }
  167 
  168 static inline int
  169 get_tval(void)
  170 {
  171         uint32_t val;
  172 
  173         __asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
  174 
  175         return (val);
  176 }
  177 
  178 static inline void
  179 disable_user_access(void)
  180 {
  181         uint32_t cntkctl;
  182 
  183         __asm volatile("mrc p15, 0, %0, c14, c1, 0" : "=r" (cntkctl));
  184         cntkctl &= ~(GT_CNTKCTL_PL0PTEN | GT_CNTKCTL_PL0VTEN |
  185             GT_CNTKCTL_EVNTEN | GT_CNTKCTL_PL0VCTEN | GT_CNTKCTL_PL0PCTEN);
  186         __asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl));
  187         isb();
  188 }
  189 
  190 static unsigned
  191 arm_tmr_get_timecount(struct timecounter *tc)
  192 {
  193 
  194         return (get_cntpct());
  195 }
  196 
  197 static int
  198 arm_tmr_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
  199 {
  200         struct arm_tmr_softc *sc;
  201         int counts, ctrl;
  202 
  203         sc = (struct arm_tmr_softc *)et->et_priv;
  204 
  205         if (first != 0) {
  206                 counts = ((uint32_t)et->et_frequency * first) >> 32;
  207                 ctrl = get_ctrl();
  208                 ctrl &= ~GT_CTRL_INT_MASK;
  209                 ctrl |= GT_CTRL_ENABLE;
  210                 set_tval(counts);
  211                 set_ctrl(ctrl);
  212                 return (0);
  213         }
  214 
  215         return (EINVAL);
  216 
  217 }
  218 
  219 static int
  220 arm_tmr_stop(struct eventtimer *et)
  221 {
  222         int ctrl;
  223 
  224         ctrl = get_ctrl();
  225         ctrl &= GT_CTRL_ENABLE;
  226         set_ctrl(ctrl);
  227 
  228         return (0);
  229 }
  230 
  231 static int
  232 arm_tmr_intr(void *arg)
  233 {
  234         struct arm_tmr_softc *sc;
  235         int ctrl;
  236 
  237         sc = (struct arm_tmr_softc *)arg;
  238         ctrl = get_ctrl();
  239         if (ctrl & GT_CTRL_INT_STAT) {
  240                 ctrl |= GT_CTRL_INT_MASK;
  241                 set_ctrl(ctrl);
  242         }
  243 
  244         if (sc->et.et_active)
  245                 sc->et.et_event_cb(&sc->et, sc->et.et_arg);
  246 
  247         return (FILTER_HANDLED);
  248 }
  249 
  250 static int
  251 arm_tmr_probe(device_t dev)
  252 {
  253 
  254         if (!ofw_bus_status_okay(dev))
  255                 return (ENXIO);
  256 
  257         if (!ofw_bus_is_compatible(dev, "arm,armv7-timer"))
  258                 return (ENXIO);
  259 
  260         device_set_desc(dev, "ARMv7 Generic Timer");
  261         return (BUS_PROBE_DEFAULT);
  262 }
  263 
  264 
  265 static int
  266 arm_tmr_attach(device_t dev)
  267 {
  268         struct arm_tmr_softc *sc;
  269         phandle_t node;
  270         pcell_t clock;
  271         int error;
  272         int i;
  273 
  274         sc = device_get_softc(dev);
  275         if (arm_tmr_sc)
  276                 return (ENXIO);
  277 
  278         /* Get the base clock frequency */
  279         node = ofw_bus_get_node(dev);
  280         error = OF_getprop(node, "clock-frequency", &clock, sizeof(clock));
  281         if (error > 0) {
  282                 sc->clkfreq = fdt32_to_cpu(clock);
  283         }
  284 
  285         if (sc->clkfreq == 0) {
  286                 /* Try to get clock frequency from timer */
  287                 sc->clkfreq = get_freq();
  288         }
  289 
  290         if (sc->clkfreq == 0) {
  291                 device_printf(dev, "No clock frequency specified\n");
  292                 return (ENXIO);
  293         }
  294 
  295         if (bus_alloc_resources(dev, timer_spec, sc->res)) {
  296                 device_printf(dev, "could not allocate resources\n");
  297                 return (ENXIO);
  298         };
  299 
  300         arm_tmr_sc = sc;
  301 
  302         /* Setup secure and non-secure IRQs handler */
  303         for (i = 0; i < 2; i++) {
  304                 error = bus_setup_intr(dev, sc->res[i], INTR_TYPE_CLK,
  305                     arm_tmr_intr, NULL, sc, &sc->ihl[i]);
  306                 if (error) {
  307                         device_printf(dev, "Unable to alloc int resource.\n");
  308                         return (ENXIO);
  309                 }
  310         }
  311 
  312         disable_user_access();
  313 
  314         arm_tmr_timecount.tc_frequency = sc->clkfreq;
  315         tc_init(&arm_tmr_timecount);
  316 
  317         sc->et.et_name = "ARM MPCore Eventtimer";
  318         sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERCPU;
  319         sc->et.et_quality = 1000;
  320 
  321         sc->et.et_frequency = sc->clkfreq;
  322         sc->et.et_min_period = (0x00000002LLU << 32) / sc->et.et_frequency;
  323         sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
  324         sc->et.et_start = arm_tmr_start;
  325         sc->et.et_stop = arm_tmr_stop;
  326         sc->et.et_priv = sc;
  327         et_register(&sc->et);
  328 
  329         return (0);
  330 }
  331 
  332 static device_method_t arm_tmr_methods[] = {
  333         DEVMETHOD(device_probe,         arm_tmr_probe),
  334         DEVMETHOD(device_attach,        arm_tmr_attach),
  335         { 0, 0 }
  336 };
  337 
  338 static driver_t arm_tmr_driver = {
  339         "generic_timer",
  340         arm_tmr_methods,
  341         sizeof(struct arm_tmr_softc),
  342 };
  343 
  344 static devclass_t arm_tmr_devclass;
  345 
  346 EARLY_DRIVER_MODULE(timer, simplebus, arm_tmr_driver, arm_tmr_devclass, 0, 0,
  347     BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
  348 EARLY_DRIVER_MODULE(timer, ofwbus, arm_tmr_driver, arm_tmr_devclass, 0, 0,
  349     BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE);
  350 
  351 void
  352 DELAY(int usec)
  353 {
  354         int32_t counts, counts_per_usec;
  355         uint32_t first, last;
  356 
  357         /*
  358          * Check the timers are setup, if not just
  359          * use a for loop for the meantime
  360          */
  361         if (arm_tmr_sc == NULL) {
  362                 for (; usec > 0; usec--)
  363                         for (counts = 200; counts > 0; counts--)
  364                                 /*
  365                                  * Prevent gcc from optimizing
  366                                  * out the loop
  367                                  */
  368                                 cpufunc_nullop();
  369                 return;
  370         }
  371 
  372         /* Get the number of times to count */
  373         counts_per_usec = ((arm_tmr_timecount.tc_frequency / 1000000) + 1);
  374 
  375         /*
  376          * Clamp the timeout at a maximum value (about 32 seconds with
  377          * a 66MHz clock). *Nobody* should be delay()ing for anywhere
  378          * near that length of time and if they are, they should be hung
  379          * out to dry.
  380          */
  381         if (usec >= (0x80000000U / counts_per_usec))
  382                 counts = (0x80000000U / counts_per_usec) - 1;
  383         else
  384                 counts = usec * counts_per_usec;
  385 
  386         first = get_cntpct();
  387 
  388         while (counts > 0) {
  389                 last = get_cntpct();
  390                 counts -= (int32_t)(last - first);
  391                 first = last;
  392         }
  393 }

Cache object: 123437e553bb84027efdf36fa17c9dda


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