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/ingenic/jz4780_timer.c

Version: -  FREEBSD  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-2  -  FREEBSD-11-1  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-4  -  FREEBSD-10-3  -  FREEBSD-10-2  -  FREEBSD-10-1  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-3  -  FREEBSD-9-2  -  FREEBSD-9-1  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-4  -  FREEBSD-8-3  -  FREEBSD-8-2  -  FREEBSD-8-1  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-4  -  FREEBSD-7-3  -  FREEBSD-7-2  -  FREEBSD-7-1  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-4  -  FREEBSD-6-3  -  FREEBSD-6-2  -  FREEBSD-6-1  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-5  -  FREEBSD-5-4  -  FREEBSD-5-3  -  FREEBSD-5-2  -  FREEBSD-5-1  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  linux-2.6  -  linux-2.4.22  -  MK83  -  MK84  -  PLAN9  -  DFBSD  -  NETBSD  -  NETBSD5  -  NETBSD4  -  NETBSD3  -  NETBSD20  -  OPENBSD  -  xnu-517  -  xnu-792  -  xnu-792.6.70  -  xnu-1228  -  xnu-1456.1.26  -  xnu-1699.24.8  -  xnu-2050.18.24  -  OPENSOLARIS  -  minix-3-1-1 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright 2013-2015 Alexander Kabaev <kan@FreeBSD.org>
    3  * Copyright 2013-2015 John Wehle <john@feith.com>
    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 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD: stable/12/sys/mips/ingenic/jz4780_timer.c 343709 2019-02-03 08:53:03Z gonzo $");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/bus.h>
   35 #include <sys/kernel.h>
   36 #include <sys/module.h>
   37 #include <sys/malloc.h>
   38 #include <sys/rman.h>
   39 #include <sys/timetc.h>
   40 #include <sys/timeet.h>
   41 
   42 #include <machine/bus.h>
   43 #include <machine/cpu.h>
   44 #include <machine/hwfunc.h>
   45 
   46 #include <dev/extres/clk/clk.h>
   47 
   48 #include <dev/fdt/fdt_common.h>
   49 #include <dev/ofw/ofw_bus.h>
   50 #include <dev/ofw/ofw_bus_subr.h>
   51 
   52 #include <mips/ingenic/jz4780_regs.h>
   53 
   54 struct jz4780_timer_softc {
   55         device_t                dev;
   56         struct resource *       res[4];
   57         void *                  ih_cookie;
   58         struct eventtimer       et;
   59         struct timecounter      tc;
   60 };
   61 
   62 static struct resource_spec jz4780_timer_spec[] = {
   63         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
   64         { SYS_RES_IRQ,          0,      RF_ACTIVE },    /* OST */
   65         { SYS_RES_IRQ,          1,      RF_ACTIVE },    /* TC5 */
   66         { SYS_RES_IRQ,          2,      RF_ACTIVE },    /* TC0-4,6 */
   67         { -1, 0 }
   68 };
   69 
   70 /*
   71  * devclass_get_device / device_get_softc could be used
   72  * to dynamically locate this, however the timers are a
   73  * required device which can't be unloaded so there's
   74  * no need for the overhead.
   75  */
   76 static struct jz4780_timer_softc *jz4780_timer_sc = NULL;
   77 
   78 #define CSR_WRITE_4(sc, reg, val)       bus_write_4((sc)->res[0], reg, (val))
   79 #define CSR_READ_4(sc, reg)             bus_read_4((sc)->res[0], reg)
   80 
   81 static unsigned
   82 jz4780_get_timecount(struct timecounter *tc)
   83 {
   84         struct jz4780_timer_softc *sc =
   85             (struct jz4780_timer_softc *)tc->tc_priv;
   86 
   87         return CSR_READ_4(sc, JZ_OST_CNT_LO);
   88 }
   89 
   90 static int
   91 jz4780_hardclock(void *arg)
   92 {
   93         struct jz4780_timer_softc *sc = (struct jz4780_timer_softc *)arg;
   94 
   95         CSR_WRITE_4(sc, JZ_TC_TFCR, TFR_FFLAG5);
   96         CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST5);
   97 
   98         if (sc->et.et_active)
   99                 sc->et.et_event_cb(&sc->et, sc->et.et_arg);
  100 
  101         return (FILTER_HANDLED);
  102 }
  103 
  104 static int
  105 jz4780_timer_start(struct eventtimer *et, sbintime_t first, sbintime_t period)
  106 {
  107         struct jz4780_timer_softc *sc =
  108             (struct jz4780_timer_softc *)et->et_priv;
  109         uint32_t ticks;
  110 
  111         ticks = (first * et->et_frequency) / SBT_1S;
  112         if (ticks == 0)
  113                 return (EINVAL);
  114 
  115         CSR_WRITE_4(sc, JZ_TC_TDFR(5), ticks);
  116         CSR_WRITE_4(sc, JZ_TC_TCNT(5), 0);
  117         CSR_WRITE_4(sc, JZ_TC_TESR, TESR_TCST5);
  118 
  119         return (0);
  120 }
  121 
  122 static int
  123 jz4780_timer_stop(struct eventtimer *et)
  124 {
  125         struct jz4780_timer_softc *sc =
  126             (struct jz4780_timer_softc *)et->et_priv;
  127 
  128         CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST5);
  129         return (0);
  130 }
  131 
  132 static int
  133 jz4780_timer_probe(device_t dev)
  134 {
  135 
  136         if (!ofw_bus_status_okay(dev))
  137                 return (ENXIO);
  138 
  139         if (!ofw_bus_is_compatible(dev, "ingenic,jz4780-tcu"))
  140                 return (ENXIO);
  141 
  142         device_set_desc(dev, "Ingenic JZ4780 timer");
  143 
  144         return (BUS_PROBE_DEFAULT);
  145 }
  146 
  147 static int
  148 jz4780_timer_attach(device_t dev)
  149 {
  150         struct jz4780_timer_softc *sc = device_get_softc(dev);
  151         pcell_t counter_freq;
  152         clk_t clk;
  153 
  154         /* There should be exactly one instance. */
  155         if (jz4780_timer_sc != NULL)
  156                 return (ENXIO);
  157 
  158         sc->dev = dev;
  159 
  160         if (bus_alloc_resources(dev, jz4780_timer_spec, sc->res)) {
  161                 device_printf(dev, "can not allocate resources for device\n");
  162                 return (ENXIO);
  163         }
  164 
  165         counter_freq = 0;
  166         if (clk_get_by_name(dev, "ext", &clk) == 0) {
  167                 uint64_t clk_freq;
  168 
  169                 if (clk_get_freq(clk, &clk_freq) == 0)
  170                         counter_freq = (uint32_t)clk_freq / 16;
  171                 clk_release(clk);
  172         }
  173         if (counter_freq == 0) {
  174                 device_printf(dev, "unable to determine ext clock frequency\n");
  175                 /* Hardcode value we 'know' is correct */
  176                 counter_freq = 48000000 / 16;
  177         }
  178 
  179         /*
  180          * Disable the timers, select the input for each timer,
  181          * clear and then start OST.
  182          */
  183 
  184         /* Stop OST, if it happens to be running */
  185         CSR_WRITE_4(sc, JZ_TC_TECR, TESR_OST);
  186         /* Stop all other channels as well */
  187         CSR_WRITE_4(sc, JZ_TC_TECR, TESR_TCST0 | TESR_TCST1 | TESR_TCST2 |
  188             TESR_TCST3 | TESR_TCST4 | TESR_TCST5 | TESR_TCST6 | TESR_TCST7);
  189         /* Clear detect mask flags */
  190         CSR_WRITE_4(sc, JZ_TC_TFCR, 0xFFFFFFFF);
  191         /* Mask all interrupts */
  192         CSR_WRITE_4(sc, JZ_TC_TMSR, 0xFFFFFFFF);
  193 
  194         /* Init counter with known data */
  195         CSR_WRITE_4(sc, JZ_OST_CTRL, 0);
  196         CSR_WRITE_4(sc, JZ_OST_CNT_LO, 0);
  197         CSR_WRITE_4(sc, JZ_OST_CNT_HI, 0);
  198         CSR_WRITE_4(sc, JZ_OST_DATA, 0xffffffff);
  199 
  200         /* Configure counter for external clock */
  201         CSR_WRITE_4(sc, JZ_OST_CTRL, OSTC_EXT_EN | OSTC_MODE | OSTC_DIV_16);
  202 
  203         /* Start the counter again */
  204         CSR_WRITE_4(sc, JZ_TC_TESR, TESR_OST);
  205 
  206         /* Configure TCU channel 5 similarly to OST and leave it disabled */
  207         CSR_WRITE_4(sc, JZ_TC_TCSR(5), TCSR_EXT_EN | TCSR_DIV_16);
  208         CSR_WRITE_4(sc, JZ_TC_TMCR, TMR_FMASK(5));
  209 
  210         if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_CLK,
  211             jz4780_hardclock, NULL, sc, &sc->ih_cookie)) {
  212                 device_printf(dev, "could not setup interrupt handler\n");
  213                 bus_release_resources(dev, jz4780_timer_spec, sc->res);
  214                 return (ENXIO);
  215         }
  216 
  217         sc->et.et_name = "JZ4780 TCU5";
  218         sc->et.et_flags = ET_FLAGS_ONESHOT;
  219         sc->et.et_frequency = counter_freq;
  220         sc->et.et_quality = 1000;
  221         sc->et.et_min_period = (0x00000002LLU * SBT_1S) / sc->et.et_frequency;
  222         sc->et.et_max_period = (0x0000fffeLLU * SBT_1S) / sc->et.et_frequency;
  223         sc->et.et_start = jz4780_timer_start;
  224         sc->et.et_stop = jz4780_timer_stop;
  225         sc->et.et_priv = sc;
  226 
  227         et_register(&sc->et);
  228 
  229         sc->tc.tc_get_timecount = jz4780_get_timecount;
  230         sc->tc.tc_name = "JZ4780 OST";
  231         sc->tc.tc_frequency = counter_freq;
  232         sc->tc.tc_counter_mask = ~0u;
  233         sc->tc.tc_quality = 1000;
  234         sc->tc.tc_priv = sc;
  235 
  236         tc_init(&sc->tc);
  237 
  238         /* Now when tc is initialized, allow DELAY to find it */
  239         jz4780_timer_sc = sc;
  240 
  241         return (0);
  242 }
  243 
  244 static int
  245 jz4780_timer_detach(device_t dev)
  246 {
  247 
  248         return (EBUSY);
  249 }
  250 
  251 static device_method_t jz4780_timer_methods[] = {
  252         /* Device interface */
  253         DEVMETHOD(device_probe,         jz4780_timer_probe),
  254         DEVMETHOD(device_attach,        jz4780_timer_attach),
  255         DEVMETHOD(device_detach,        jz4780_timer_detach),
  256 
  257         DEVMETHOD_END
  258 };
  259 
  260 static driver_t jz4780_timer_driver = {
  261         "timer",
  262         jz4780_timer_methods,
  263         sizeof(struct jz4780_timer_softc),
  264 };
  265 
  266 static devclass_t jz4780_timer_devclass;
  267 
  268 EARLY_DRIVER_MODULE(timer, simplebus, jz4780_timer_driver,
  269     jz4780_timer_devclass, 0, 0, BUS_PASS_TIMER);
  270 
  271 void
  272 DELAY(int usec)
  273 {
  274         uint32_t counter;
  275         uint32_t delta, now, previous, remaining;
  276 
  277         /* Timer has not yet been initialized */
  278         if (jz4780_timer_sc == NULL) {
  279                 for (; usec > 0; usec--)
  280                         for (counter = 200; counter > 0; counter--) {
  281                                 /* Prevent gcc from optimizing out the loop */
  282                                 mips_rd_cause();
  283                         }
  284                 return;
  285         }
  286         TSENTER();
  287 
  288         /*
  289          * Some of the other timers in the source tree do this calculation as:
  290          *
  291          *   usec * ((sc->tc.tc_frequency / 1000000) + 1)
  292          *
  293          * which gives a fairly pessimistic result when tc_frequency is an exact
  294          * multiple of 1000000.  Given the data type and typical values for
  295          * tc_frequency adding 999999 shouldn't overflow.
  296          */
  297         remaining = usec * ((jz4780_timer_sc->tc.tc_frequency + 999999) /
  298             1000000);
  299 
  300         /*
  301          * We add one since the first iteration may catch the counter just
  302          * as it is changing.
  303          */
  304         remaining += 1;
  305 
  306         previous = jz4780_get_timecount(&jz4780_timer_sc->tc);
  307 
  308         for ( ; ; ) {
  309                 now = jz4780_get_timecount(&jz4780_timer_sc->tc);
  310 
  311                 /*
  312                  * If the timer has rolled over, then we have the case:
  313                  *
  314                  *   if (previous > now) {
  315                  *     delta = (0 - previous) + now
  316                  *   }
  317                  *
  318                  * which is really no different then the normal case.
  319                  * Both cases are simply:
  320                  *
  321                  *   delta = now - previous.
  322                  */
  323                 delta = now - previous;
  324 
  325                 if (delta >= remaining)
  326                         break;
  327 
  328                 previous = now;
  329                 remaining -= delta;
  330         }
  331         TSEXIT();
  332 }
  333 
  334 void
  335 platform_initclocks(void)
  336 {
  337 
  338 }
  339 

Cache object: d263359b2442de920d082528cb71af23


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