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/dev/hwpmc/pmu_dmc620.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  * SPDX-License-Identifier: BSD-2-Clause
    3  *
    4  * Copyright (c) 2021 Ampere Computing LLC
    5  * Copyright (c) 2022 Arm Ltd
    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  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  * $FreeBSD$
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 
   34 #include "opt_hwpmc_hooks.h"
   35 #include "opt_acpi.h"
   36 
   37 /*
   38  * This depends on ACPI, but is built unconditionally in the hwpmc module.
   39  */
   40 #ifdef DEV_ACPI
   41 #include <sys/param.h>
   42 #include <sys/bus.h>
   43 #include <sys/module.h>
   44 #include <sys/rman.h>
   45 #include <sys/pmc.h>
   46 #include <sys/pmckern.h>
   47 
   48 #include <machine/bus.h>
   49 #include <machine/cpu.h>
   50 
   51 #include <contrib/dev/acpica/include/acpi.h>
   52 #include <dev/acpica/acpivar.h>
   53 
   54 #include <dev/hwpmc/pmu_dmc620_reg.h>
   55 
   56 static char *pmu_dmc620_ids[] = {
   57         "ARMHD620",
   58         NULL
   59 };
   60 
   61 static struct resource_spec pmu_dmc620_res_spec[] = {
   62         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
   63         { SYS_RES_IRQ,          0,      RF_ACTIVE | RF_SHAREABLE },
   64         { -1, 0 }
   65 };
   66 
   67 struct pmu_dmc620_softc {
   68         device_t        sc_dev;
   69         int             sc_unit;
   70         int             sc_domain;
   71         struct resource *sc_res[2];
   72         void            *sc_ih;
   73         uint32_t        sc_clkdiv2_conters_hi[DMC620_CLKDIV2_COUNTERS_N];
   74         uint32_t        sc_clk_conters_hi[DMC620_CLK_COUNTERS_N];
   75         uint32_t        sc_saved_control[DMC620_COUNTERS_N];
   76 };
   77 
   78 #define RD4(sc, r)              bus_read_4((sc)->sc_res[0], (r))
   79 #define WR4(sc, r, v)           bus_write_4((sc)->sc_res[0], (r), (v))
   80 #define MD4(sc, r, c, s)        WR4((sc), (r), RD4((sc), (r)) & ~(c) | (s))
   81 
   82 #define CD2MD4(sc, u, r, c, s)  MD4((sc), DMC620_CLKDIV2_REG((u), (r)), (c), (s))
   83 #define CMD4(sc, u, r, c, s)    MD4((sc), DMC620_CLK_REG((u), (r)), (c), (s))
   84 
   85 static int pmu_dmc620_counter_overflow_intr(void *arg);
   86 
   87 uint32_t
   88 pmu_dmc620_rd4(void *arg, u_int cntr, off_t reg)
   89 {
   90         struct pmu_dmc620_softc *sc;
   91         uint32_t val;
   92 
   93         sc = (struct pmu_dmc620_softc *)arg;
   94         KASSERT(cntr < DMC620_COUNTERS_N, ("Wrong counter unit %d", cntr));
   95 
   96         val = RD4(sc, DMC620_REG(cntr, reg));
   97         return (val);
   98 }
   99 
  100 void
  101 pmu_dmc620_wr4(void *arg, u_int cntr, off_t reg, uint32_t val)
  102 {
  103         struct pmu_dmc620_softc *sc;
  104 
  105         sc = (struct pmu_dmc620_softc *)arg;
  106         KASSERT(cntr < DMC620_COUNTERS_N, ("Wrong counter unit %d", cntr));
  107 
  108         WR4(sc, DMC620_REG(cntr, reg), val);
  109 }
  110 
  111 static int
  112 pmu_dmc620_acpi_probe(device_t dev)
  113 {
  114         int err;
  115 
  116         err = ACPI_ID_PROBE(device_get_parent(dev), dev, pmu_dmc620_ids, NULL);
  117         if (err <= 0)
  118                 device_set_desc(dev, "ARM DMC-620 Memory Controller PMU");
  119 
  120         return (err);
  121 }
  122 
  123 static int
  124 pmu_dmc620_acpi_attach(device_t dev)
  125 {
  126         struct pmu_dmc620_softc *sc;
  127         int domain, i, u;
  128         const char *dname;
  129 
  130         dname = device_get_name(dev);
  131         sc = device_get_softc(dev);
  132         sc->sc_dev = dev;
  133         u = device_get_unit(dev);
  134         sc->sc_unit = u;
  135 
  136         /*
  137          * Ampere Altra support NUMA emulation, but DMC-620 PMU units have no
  138          * mapping. Emulate this with kenv/hints.
  139          * Format "hint.pmu_dmc620.3.domain=1".
  140          */
  141         if ((resource_int_value(dname, u, "domain", &domain) == 0 ||
  142             bus_get_domain(dev, &domain) == 0) && domain < MAXMEMDOM) {
  143                 sc->sc_domain = domain;
  144         }
  145         device_printf(dev, "domain=%d\n", domain);
  146 
  147         i = bus_alloc_resources(dev, pmu_dmc620_res_spec, sc->sc_res);
  148         if (i != 0) {
  149                 device_printf(dev, "cannot allocate resources for device (%d)\n",
  150                     i);
  151                 return (i);
  152         }
  153         /* Disable counter before enable interrupt. */
  154         for (i = 0; i < DMC620_CLKDIV2_COUNTERS_N; i++) {
  155                 CD2MD4(sc, i, DMC620_COUNTER_CONTROL,
  156                     DMC620_COUNTER_CONTROL_ENABLE, 0);
  157         }
  158         for (i = 0; i < DMC620_CLK_COUNTERS_N; i++) {
  159                 CMD4(sc, i, DMC620_COUNTER_CONTROL,
  160                     DMC620_COUNTER_CONTROL_ENABLE, 0);
  161         }
  162 
  163         /* Clear intr status. */
  164         WR4(sc, DMC620_OVERFLOW_STATUS_CLKDIV2, 0);
  165         WR4(sc, DMC620_OVERFLOW_STATUS_CLK, 0);
  166 
  167         if (sc->sc_res[1] != NULL && bus_setup_intr(dev, sc->sc_res[1],
  168             INTR_TYPE_MISC | INTR_MPSAFE, pmu_dmc620_counter_overflow_intr,
  169             NULL, sc, &sc->sc_ih)) {
  170                 bus_release_resources(dev, pmu_dmc620_res_spec, sc->sc_res);
  171                 device_printf(dev, "cannot setup interrupt handler\n");
  172                 return (ENXIO);
  173         }
  174         dmc620_pmc_register(u, sc, domain);
  175         return (0);
  176 }
  177 
  178 static int
  179 pmu_dmc620_acpi_detach(device_t dev)
  180 {
  181         struct pmu_dmc620_softc *sc;
  182 
  183         sc = device_get_softc(dev);
  184         dmc620_pmc_unregister(device_get_unit(dev));
  185         if (sc->sc_res[1] != NULL) {
  186                 bus_teardown_intr(dev, sc->sc_res[1], sc->sc_ih);
  187         }
  188         bus_release_resources(dev, pmu_dmc620_res_spec, sc->sc_res);
  189 
  190         return (0);
  191 }
  192 
  193 static void
  194 pmu_dmc620_clkdiv2_overflow(struct trapframe *tf, struct pmu_dmc620_softc *sc,
  195     u_int i)
  196 {
  197 
  198         atomic_add_32(&sc->sc_clkdiv2_conters_hi[i], 1);
  199         /* Call dmc620 handler directly, because hook busy by arm64_intr. */
  200         dmc620_intr(tf, PMC_CLASS_DMC620_PMU_CD2, sc->sc_unit, i);
  201 }
  202 
  203 static void
  204 pmu_dmc620_clk_overflow(struct trapframe *tf, struct pmu_dmc620_softc *sc,
  205     u_int i)
  206 {
  207 
  208         atomic_add_32(&sc->sc_clk_conters_hi[i], 1);
  209         /* Call dmc620 handler directly, because hook busy by arm64_intr. */
  210         dmc620_intr(tf, PMC_CLASS_DMC620_PMU_C, sc->sc_unit, i);
  211 
  212 }
  213 
  214 static int
  215 pmu_dmc620_counter_overflow_intr(void *arg)
  216 {
  217         uint32_t clkdiv2_stat, clk_stat;
  218         struct pmu_dmc620_softc *sc;
  219         struct trapframe *tf;
  220         u_int i;
  221 
  222         tf = PCPU_GET(curthread)->td_intr_frame;
  223         sc = (struct pmu_dmc620_softc *) arg;
  224         clkdiv2_stat = RD4(sc, DMC620_OVERFLOW_STATUS_CLKDIV2);
  225         clk_stat = RD4(sc, DMC620_OVERFLOW_STATUS_CLK);
  226 
  227         if ((clkdiv2_stat == 0) && (clk_stat == 0))
  228                 return (FILTER_STRAY);
  229         /* Stop and save states of all counters. */
  230         for (i = 0; i < DMC620_COUNTERS_N; i++) {
  231                 sc->sc_saved_control[i] = RD4(sc, DMC620_REG(i,
  232                     DMC620_COUNTER_CONTROL));
  233                 WR4(sc, DMC620_REG(i, DMC620_COUNTER_CONTROL),
  234                     sc->sc_saved_control[i] & ~DMC620_COUNTER_CONTROL_ENABLE);
  235         }
  236 
  237         if (clkdiv2_stat != 0) {
  238                 for (i = 0; i < DMC620_CLKDIV2_COUNTERS_N; i++) {
  239                         if ((clkdiv2_stat & (1 << i)) == 0)
  240                                 continue;
  241                         pmu_dmc620_clkdiv2_overflow(tf, sc, i);
  242                 }
  243                 WR4(sc, DMC620_OVERFLOW_STATUS_CLKDIV2, 0);
  244         }
  245         if (clk_stat != 0) {
  246                 for (i = 0; i < DMC620_CLK_COUNTERS_N; i++) {
  247                         if ((clk_stat & (1 << i)) == 0)
  248                                 continue;
  249                         pmu_dmc620_clk_overflow(tf, sc, i);
  250                 }
  251                 WR4(sc, DMC620_OVERFLOW_STATUS_CLK, 0);
  252         }
  253 
  254         /* Restore states of all counters. */
  255         for (i = 0; i < DMC620_COUNTERS_N; i++) {
  256                 WR4(sc, DMC620_REG(i, DMC620_COUNTER_CONTROL),
  257                     sc->sc_saved_control[i]);
  258         }
  259 
  260         return (FILTER_HANDLED);
  261 }
  262 
  263 static device_method_t pmu_dmc620_acpi_methods[] = {
  264         /* Device interface */
  265         DEVMETHOD(device_probe,                 pmu_dmc620_acpi_probe),
  266         DEVMETHOD(device_attach,                pmu_dmc620_acpi_attach),
  267         DEVMETHOD(device_detach,                pmu_dmc620_acpi_detach),
  268 
  269         /* End */
  270         DEVMETHOD_END
  271 };
  272 
  273 static driver_t pmu_dmc620_acpi_driver = {
  274         "pmu_dmc620",
  275         pmu_dmc620_acpi_methods,
  276         sizeof(struct pmu_dmc620_softc),
  277 };
  278 
  279 DRIVER_MODULE(pmu_dmc620, acpi, pmu_dmc620_acpi_driver, 0, 0);
  280 /* Reverse dependency. hwpmc needs DMC-620 on ARM64. */
  281 MODULE_DEPEND(pmc, pmu_dmc620, 1, 1, 1);
  282 MODULE_VERSION(pmu_dmc620, 1);
  283 #endif /* DEV_ACPI */

Cache object: 8d3b0325b87967e041ef37b6034fb5ef


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