The branch main has been updated by tsoome:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=59191f3573f6cb2ea055ac319cbcb68823ca8e17

commit 59191f3573f6cb2ea055ac319cbcb68823ca8e17
Author:     Aleksandr Rybalko <r...@freebsd.org>
AuthorDate: 2022-02-16 00:36:17 +0000
Commit:     Toomas Soome <tso...@freebsd.org>
CommitDate: 2022-06-26 19:03:04 +0000

    Add support of ARM CMN-600 controller, PMU access functions only. Add 
support of PMU counters of ARM CMN-600 controller.
    
    Add support of ARM CMN-600 controller, PMU access functions only.
    Add support of PMU counters of ARM CMN-600 controller.
    
    Reviewed by: mhorne
    Sponsored By: ARM
    Differential Revision: https://reviews.freebsd.org/D32321
---
 sys/arm64/arm64/cmn600.c       | 834 +++++++++++++++++++++++++++++++++++++++++
 sys/arm64/include/cmn600_reg.h |  16 +
 sys/dev/hwpmc/hwpmc_cmn600.c   | 830 ++++++++++++++++++++++++++++++++++++++++
 sys/modules/hwpmc/Makefile     |   1 +
 4 files changed, 1681 insertions(+)

diff --git a/sys/arm64/arm64/cmn600.c b/sys/arm64/arm64/cmn600.c
new file mode 100644
index 000000000000..9ed998bc0354
--- /dev/null
+++ b/sys/arm64/arm64/cmn600.c
@@ -0,0 +1,834 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 ARM Ltd
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Arm CoreLink CMN-600 Coherent Mesh Network Driver */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_acpi.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/smp.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+#include <dev/acpica/acpivar.h>
+
+#include <machine/cmn600_reg.h>
+
+#define        RD4(sc, r)              bus_read_4((sc)->sc_res[0], (r))
+#define        RD8(sc, r)              bus_read_8((sc)->sc_res[0], (r))
+#define        WR4(sc, r, v)           bus_write_4((sc)->sc_res[0], (r), (v))
+#define        WR8(sc, r, v)           bus_write_8((sc)->sc_res[0], (r), (v))
+#define        FLD(v, n)               (((v) & n ## _MASK) >> n ## _SHIFT)
+
+static char *cmn600_ids[] = {
+       "ARMHC600",
+       NULL
+};
+
+static struct resource_spec cmn600_res_spec[] = {
+       { SYS_RES_MEMORY,       0,      RF_ACTIVE },
+       { SYS_RES_MEMORY,       1,      RF_ACTIVE | RF_UNMAPPED | RF_OPTIONAL },
+       { SYS_RES_IRQ,          0,      RF_ACTIVE },
+       { -1, 0 }
+};
+
+struct cmn600_node;
+
+typedef uint64_t (*nd_read_8_t)(struct cmn600_node *, uint32_t);
+typedef uint32_t (*nd_read_4_t)(struct cmn600_node *, uint32_t);
+typedef void (*nd_write_8_t)(struct cmn600_node *, uint32_t, uint64_t);
+typedef void (*nd_write_4_t)(struct cmn600_node *, uint32_t, uint32_t);
+
+struct cmn600_node {
+       struct cmn600_softc     *sc;
+       off_t                    nd_offset;
+       int                      nd_type;
+       uint16_t                 nd_id;
+       uint16_t                 nd_logical_id;
+       uint8_t                  nd_x, nd_y, nd_port, nd_sub;
+       uint16_t                 nd_child_count;
+       uint32_t                 nd_paired;
+       struct cmn600_node      *nd_parent;
+       nd_read_8_t              nd_read8;
+       nd_read_4_t              nd_read4;
+       nd_write_8_t             nd_write8;
+       nd_write_4_t             nd_write4;
+       struct cmn600_node      **nd_children;
+};
+
+struct cmn600_softc {
+       device_t         sc_dev;
+       int              sc_unit;
+       int              sc_domain;
+       int              sc_longid;
+       int              sc_mesh_x;
+       int              sc_mesh_y;
+       struct resource *sc_res[3];
+       void            *sc_ih;
+       int              sc_r2;
+       int              sc_rev;
+       struct cmn600_node *sc_rootnode;
+       struct cmn600_node *sc_dtcnode;
+       struct cmn600_node *sc_dvmnode;
+       struct cmn600_node *sc_xpnodes[64];
+       int (*sc_pmu_ih)(struct trapframe *tf, int unit, int i);
+};
+
+static struct cmn600_pmc cmn600_pmcs[CMN600_UNIT_MAX];
+static int cmn600_npmcs = 0;
+
+static int cmn600_acpi_detach(device_t dev);
+static int cmn600_intr(void *arg);
+
+static void
+cmn600_pmc_register(int unit, void *arg, int domain)
+{
+
+       if (unit >= CMN600_UNIT_MAX) {
+               /* TODO */
+               return;
+       }
+
+       cmn600_pmcs[unit].arg = arg;
+       cmn600_pmcs[unit].domain = domain;
+       cmn600_npmcs++;
+}
+
+static void
+cmn600_pmc_unregister(int unit)
+{
+
+       cmn600_pmcs[unit].arg = NULL;
+       cmn600_npmcs--;
+}
+
+int
+cmn600_pmc_nunits(void)
+{
+
+       return (cmn600_npmcs);
+}
+
+int
+cmn600_pmc_getunit(int unit, void **arg, int *domain)
+{
+
+       if (unit >= cmn600_npmcs)
+               return (EINVAL);
+       if (cmn600_pmcs[unit].arg == NULL)
+               return (EINVAL);
+       *arg = cmn600_pmcs[unit].arg;
+       *domain = cmn600_pmcs[unit].domain;
+       return (0);
+}
+
+int
+pmu_cmn600_rev(void *arg)
+{
+       struct cmn600_softc *sc;
+
+       sc = (struct cmn600_softc *)arg;
+       switch (sc->sc_rev) {
+       case 0x0:
+               return (0x100);
+       case 0x1:
+               return (0x101);
+       case 0x2:
+               return (0x102);
+       case 0x3:
+               return (0x103);
+       case 0x4:
+               return (0x200);
+       case 0x5:
+               return (0x300);
+       case 0x6:
+               return (0x301);
+       }
+       return (0x302); /* Unknown revision. */
+}
+
+static uint64_t
+cmn600_node_read8(struct cmn600_node *nd, uint32_t reg)
+{
+
+       return (RD8(nd->sc, nd->nd_offset + reg));
+}
+
+static void
+cmn600_node_write8(struct cmn600_node *nd, uint32_t reg, uint64_t val)
+{
+
+       WR8(nd->sc, nd->nd_offset + reg, val);
+}
+
+static uint32_t
+cmn600_node_read4(struct cmn600_node *nd, uint32_t reg)
+{
+
+       return (RD4(nd->sc, nd->nd_offset + reg));
+}
+
+static void
+cmn600_node_write4(struct cmn600_node *nd, uint32_t reg, uint32_t val)
+{
+
+       WR4(nd->sc, nd->nd_offset + reg, val);
+}
+
+static const char *
+cmn600_node_type_str(int type)
+{
+
+#define        NAME_OF(t, n)   case NODE_TYPE_ ## t: return n
+       switch (type) {
+       NAME_OF(INVALID, "<invalid node>");
+       NAME_OF(DVM, "DVM");
+       NAME_OF(CFG, "CFG");
+       NAME_OF(DTC, "DTC");
+       NAME_OF(HN_I, "HN-I");
+       NAME_OF(HN_F, "HN-F");
+       NAME_OF(XP, "XP");
+       NAME_OF(SBSX, "SBSX");
+       NAME_OF(RN_I, "RN-I");
+       NAME_OF(RN_D, "RN-D");
+       NAME_OF(RN_SAM, "RN-SAM");
+       NAME_OF(CXRA, "CXRA");
+       NAME_OF(CXHA, "CXHA");
+       NAME_OF(CXLA, "CXLA");
+       default:
+               return "<unknown node>";
+       }
+#undef NAME_OF
+}
+
+static const char *
+cmn600_xpport_dev_type_str(uint8_t type)
+{
+
+#define        NAME_OF(t, n)   case POR_MXP_PX_INFO_DEV_TYPE_ ## t: return n
+       switch (type) {
+       NAME_OF(RN_I, "RN-I");
+       NAME_OF(RN_D, "RN-D");
+       NAME_OF(RN_F_CHIB, "RN-F CHIB");
+       NAME_OF(RN_F_CHIB_ESAM, "RN-F CHIB ESAM");
+       NAME_OF(RN_F_CHIA, "RN-F CHIA");
+       NAME_OF(RN_F_CHIA_ESAM, "RN-F CHIA ESAM");
+       NAME_OF(HN_T, "HN-T");
+       NAME_OF(HN_I, "HN-I");
+       NAME_OF(HN_D, "HN-D");
+       NAME_OF(SN_F, "SN-F");
+       NAME_OF(SBSX, "SBSX");
+       NAME_OF(HN_F, "HN-F");
+       NAME_OF(CXHA, "CXHA");
+       NAME_OF(CXRA, "CXRA");
+       NAME_OF(CXRH, "CXRH");
+       default:
+               return "<unknown>";
+       }
+#undef NAME_OF
+}
+
+static void
+cmn600_dump_node(struct cmn600_node *node, int lvl)
+{
+       int i;
+
+       for (i = 0; i < lvl; i++) printf("    ");
+       printf("%s [%dx%d:%d:%d] id: 0x%x @0x%lx Logical Id: 0x%x",
+           cmn600_node_type_str(node->nd_type), node->nd_x, node->nd_y,
+           node->nd_port, node->nd_sub, node->nd_id, node->nd_offset,
+           node->nd_logical_id);
+       if (node->nd_child_count > 0)
+               printf(", Children: %d", node->nd_child_count);
+       printf("\n");
+       if (node->nd_type == NODE_TYPE_XP)
+               printf("\tPort 0: %s\n\tPort 1: %s\n",
+                   cmn600_xpport_dev_type_str(node->nd_read4(node,
+                       POR_MXP_P0_INFO) & 0x1f),
+                   cmn600_xpport_dev_type_str(node->nd_read4(node,
+                       POR_MXP_P1_INFO) & 0x1f));
+}
+
+static void
+cmn600_dump_node_recursive(struct cmn600_node *node, int lvl)
+{
+       int i;
+
+       cmn600_dump_node(node, lvl);
+       for (i = 0; i < node->nd_child_count; i++) {
+               cmn600_dump_node_recursive(node->nd_children[i], lvl + 1);
+       }
+}
+
+static void
+cmn600_dump_nodes_tree(struct cmn600_softc *sc)
+{
+
+       device_printf(sc->sc_dev, " nodes:\n");
+       cmn600_dump_node_recursive(sc->sc_rootnode, 0);
+}
+
+static int
+cmn600_sysctl_dump_nodes(SYSCTL_HANDLER_ARGS)
+{
+       struct cmn600_softc *sc;
+       uint32_t val;
+       int err;
+
+       sc = (struct cmn600_softc *)arg1;
+       val = 0;
+       err = sysctl_handle_int(oidp, &val, 0, req);
+
+       if (err)
+               return (err);
+
+       if (val != 0)
+               cmn600_dump_nodes_tree(sc);
+
+       return (0);
+}
+
+static struct cmn600_node *
+cmn600_create_node(struct cmn600_softc *sc, off_t node_offset,
+    struct cmn600_node *parent, int lvl)
+{
+       struct cmn600_node *node;
+       off_t child_offset;
+       uint64_t val;
+       int i;
+
+       node = malloc(sizeof(struct cmn600_node), M_DEVBUF, M_WAITOK);
+       if (node == NULL)
+               return (NULL);
+
+       node->sc = sc;
+       node->nd_offset = node_offset;
+       node->nd_parent = parent;
+       node->nd_read4 = cmn600_node_read4;
+       node->nd_read8 = cmn600_node_read8;
+       node->nd_write4 = cmn600_node_write4;
+       node->nd_write8 = cmn600_node_write8;
+
+       val = node->nd_read8(node, POR_CFGM_NODE_INFO);
+       node->nd_type = FLD(val, POR_CFGM_NODE_INFO_NODE_TYPE);
+       node->nd_id = FLD(val, POR_CFGM_NODE_INFO_NODE_ID);
+       node->nd_logical_id = FLD(val, POR_CFGM_NODE_INFO_LOGICAL_ID);
+
+       val = node->nd_read8(node, POR_CFGM_CHILD_INFO);
+       node->nd_child_count = FLD(val, POR_CFGM_CHILD_INFO_CHILD_COUNT);
+       child_offset = FLD(val, POR_CFGM_CHILD_INFO_CHILD_PTR_OFFSET);
+
+       if (parent == NULL) {
+               /* Find XP node with Id 8. It have to be last in a row. */
+               for (i = 0; i < node->nd_child_count; i++) {
+                       val = node->nd_read8(node, child_offset + (i * 8));
+                       val &= POR_CFGM_CHILD_POINTER_BASE_MASK;
+                       val = RD8(sc, val + POR_CFGM_NODE_INFO);
+
+                       if (FLD(val, POR_CFGM_NODE_INFO_NODE_ID) != 8)
+                               continue;
+
+                       sc->sc_mesh_x = FLD(val, POR_CFGM_NODE_INFO_LOGICAL_ID);
+                       sc->sc_mesh_y = node->nd_child_count / sc->sc_mesh_x;
+                       if (bootverbose)
+                               printf("Mesh width X/Y %d/%d\n", sc->sc_mesh_x,
+                                   sc->sc_mesh_y);
+
+                       if ((sc->sc_mesh_x > 4) || (sc->sc_mesh_y > 4))
+                               sc->sc_longid = 1;
+                       break;
+               }
+
+               val = node->nd_read8(node, POR_INFO_GLOBAL);
+               sc->sc_r2 = (val & POR_INFO_GLOBAL_R2_ENABLE) ? 1 : 0;
+               val = node->nd_read4(node, POR_CFGM_PERIPH_ID_2_PERIPH_ID_3);
+               sc->sc_rev = FLD(val, POR_CFGM_PERIPH_ID_2_REV);
+               if (bootverbose)
+                       printf("  Rev: %d, R2_ENABLE = %s\n", sc->sc_rev,
+                           sc->sc_r2 ? "true" : "false");
+       }
+       node->nd_sub = FLD(node->nd_id, NODE_ID_SUB);
+       node->nd_port = FLD(node->nd_id, NODE_ID_PORT);
+       node->nd_paired = 0;
+       if (sc->sc_longid == 1) {
+               node->nd_x = FLD(node->nd_id, NODE_ID_X3B);
+               node->nd_y = FLD(node->nd_id, NODE_ID_Y3B);
+       } else {
+               node->nd_x = FLD(node->nd_id, NODE_ID_X2B);
+               node->nd_y = FLD(node->nd_id, NODE_ID_Y2B);
+       }
+
+       if (bootverbose) {
+               cmn600_dump_node(node, lvl);
+       }
+
+       node->nd_children = (struct cmn600_node **)mallocarray(
+           node->nd_child_count, sizeof(struct cmn600_node *), M_DEVBUF,
+           M_WAITOK);
+       if (node->nd_children == NULL)
+               goto FAIL;
+       for (i = 0; i < node->nd_child_count; i++) {
+               val = node->nd_read8(node, child_offset + (i * 8));
+               node->nd_children[i] = cmn600_create_node(sc, val &
+                   POR_CFGM_CHILD_POINTER_BASE_MASK, node, lvl + 1);
+       }
+       switch (node->nd_type) {
+       case NODE_TYPE_DTC:
+               sc->sc_dtcnode = node;
+               break;
+       case NODE_TYPE_DVM:
+               sc->sc_dvmnode = node;
+               break;
+       case NODE_TYPE_XP:
+               sc->sc_xpnodes[node->nd_id >> NODE_ID_X2B_SHIFT] = node;
+               break;
+       default:
+               break;
+       }
+       return (node);
+FAIL:
+       free(node, M_DEVBUF);
+       return (NULL);
+}
+
+static void
+cmn600_destroy_node(struct cmn600_node *node)
+{
+       int i;
+
+       for (i = 0; i < node->nd_child_count; i++) {
+               if (node->nd_children[i] == NULL)
+                       continue;
+               cmn600_destroy_node(node->nd_children[i]);
+       }
+       free(node->nd_children, M_DEVBUF);
+       free(node, M_DEVBUF);
+}
+
+static int
+cmn600_find_node(struct cmn600_softc *sc, int node_id, int type,
+    struct cmn600_node **node)
+{
+       struct cmn600_node *xp, *child;
+       uint8_t xp_xy;
+       int i;
+
+       switch (type) {
+       case NODE_TYPE_INVALID:
+               return (ENXIO);
+       case NODE_TYPE_CFG:
+               *node = sc->sc_rootnode;
+               return (0);
+       case NODE_TYPE_DTC:
+               *node = sc->sc_dtcnode;
+               return (0);
+       case NODE_TYPE_DVM:
+               *node = sc->sc_dvmnode;
+               return (0);
+       default:
+               break;
+       }
+
+       xp_xy = node_id >> NODE_ID_X2B_SHIFT;
+       if (xp_xy >= 64)
+               return (ENXIO);
+       if (sc->sc_xpnodes[xp_xy] == NULL)
+               return (ENOENT);
+
+       switch (type) {
+       case NODE_TYPE_XP:
+               *node = sc->sc_xpnodes[xp_xy];
+               return (0);
+       default:
+               xp = sc->sc_xpnodes[xp_xy];
+               for (i = 0; i < xp->nd_child_count; i++) {
+                       child = xp->nd_children[i];
+                       if (child->nd_id == node_id && child->nd_type == type) {
+                               *node = child;
+                               return (0);
+                       }
+               }
+       }
+       return (ENOENT);
+}
+
+int
+pmu_cmn600_alloc_localpmc(void *arg, int nodeid, int node_type, int *counter)
+{
+       struct cmn600_node *node;
+       struct cmn600_softc *sc;
+       uint32_t new, old;
+       int i, ret;
+
+       sc = (struct cmn600_softc *)arg;
+       switch (node_type) {
+       case NODE_TYPE_CXLA:
+               break;
+       default:
+               node_type = NODE_TYPE_XP;
+               /* Parent XP node has always zero port and device bits. */
+               nodeid &= ~0x07;
+       }
+       ret = cmn600_find_node(sc, nodeid, node_type, &node);
+       if (ret != 0)
+               return (ret);
+       for (i = 0; i < 4; i++) {
+               new = old = node->nd_paired;
+               if (old == 0xf)
+                       return (EBUSY);
+               if ((old & (1 << i)) != 0)
+                       continue;
+               new |= 1 << i;
+               if (atomic_cmpset_32(&node->nd_paired, old, new) != 0)
+                       break;
+       }
+       *counter = i;
+       return (0);
+}
+
+int
+pmu_cmn600_free_localpmc(void *arg, int nodeid, int node_type, int counter)
+{
+       struct cmn600_node *node;
+       struct cmn600_softc *sc;
+       uint32_t new, old;
+       int ret;
+
+       sc = (struct cmn600_softc *)arg;
+       switch (node_type) {
+       case NODE_TYPE_CXLA:
+               break;
+       default:
+               node_type = NODE_TYPE_XP;
+       }
+       ret = cmn600_find_node(sc, nodeid, node_type, &node);
+       if (ret != 0)
+               return (ret);
+
+       do {
+               new = old = node->nd_paired;
+               new &= ~(1 << counter);
+       } while (atomic_cmpset_32(&node->nd_paired, old, new) == 0);
+       return (0);
+}
+
+uint32_t
+pmu_cmn600_rd4(void *arg, int nodeid, int node_type, off_t reg)
+{
+       struct cmn600_node *node;
+       struct cmn600_softc *sc;
+       int ret;
+
+       sc = (struct cmn600_softc *)arg;
+       ret = cmn600_find_node(sc, nodeid, node_type, &node);
+       if (ret != 0)
+               return (UINT32_MAX);
+       return (cmn600_node_read4(node, reg));
+}
+
+int
+pmu_cmn600_wr4(void *arg, int nodeid, int node_type, off_t reg, uint32_t val)
+{
+       struct cmn600_node *node;
+       struct cmn600_softc *sc;
+       int ret;
+
+       sc = (struct cmn600_softc *)arg;
+       ret = cmn600_find_node(sc, nodeid, node_type, &node);
+       if (ret != 0)
+               return (ret);
+       cmn600_node_write4(node, reg, val);
+       return (0);
+}
+
+uint64_t
+pmu_cmn600_rd8(void *arg, int nodeid, int node_type, off_t reg)
+{
+       struct cmn600_node *node;
+       struct cmn600_softc *sc;
+       int ret;
+
+       sc = (struct cmn600_softc *)arg;
+       ret = cmn600_find_node(sc, nodeid, node_type, &node);
+       if (ret != 0)
+               return (UINT64_MAX);
+       return (cmn600_node_read8(node, reg));
+}
+
+int
+pmu_cmn600_wr8(void *arg, int nodeid, int node_type, off_t reg, uint64_t val)
+{
+       struct cmn600_node *node;
+       struct cmn600_softc *sc;
+       int ret;
+
+       sc = (struct cmn600_softc *)arg;
+       ret = cmn600_find_node(sc, nodeid, node_type, &node);
+       if (ret != 0)
+               return (ret);
+       cmn600_node_write8(node, reg, val);
+       return (0);
+}
+
+int
+pmu_cmn600_set8(void *arg, int nodeid, int node_type, off_t reg, uint64_t val)
+{
+       struct cmn600_node *node;
+       struct cmn600_softc *sc;
+       int ret;
+
+       sc = (struct cmn600_softc *)arg;
+       ret = cmn600_find_node(sc, nodeid, node_type, &node);
+       if (ret != 0)
+               return (ret);
+       cmn600_node_write8(node, reg, cmn600_node_read8(node, reg) | val);
+       return (0);
+}
+
+int
+pmu_cmn600_clr8(void *arg, int nodeid, int node_type, off_t reg, uint64_t val)
+{
+       struct cmn600_node *node;
+       struct cmn600_softc *sc;
+       int ret;
+
+       sc = (struct cmn600_softc *)arg;
+       ret = cmn600_find_node(sc, nodeid, node_type, &node);
+       if (ret != 0)
+               return (ret);
+       cmn600_node_write8(node, reg, cmn600_node_read8(node, reg) & ~val);
+       return (0);
+}
+
+int
+pmu_cmn600_md8(void *arg, int nodeid, int node_type, off_t reg, uint64_t mask,
+    uint64_t val)
+{
+       struct cmn600_node *node;
+       struct cmn600_softc *sc;
+       int ret;
+
+       sc = (struct cmn600_softc *)arg;
+       ret = cmn600_find_node(sc, nodeid, node_type, &node);
+       if (ret != 0)
+               return (ret);
+       cmn600_node_write8(node, reg, (cmn600_node_read8(node, reg) & ~mask) |
+           val);
+       return (0);
+}
+
+static int
+cmn600_acpi_probe(device_t dev)
+{
+       int err;
+
+       err = ACPI_ID_PROBE(device_get_parent(dev), dev, cmn600_ids, NULL);
+       if (err <= 0)
+               device_set_desc(dev, "Arm CoreLink CMN-600 Coherent Mesh 
Network");
+
+       return (err);
+}
+
+static int
+cmn600_acpi_attach(device_t dev)
+{
+       struct sysctl_ctx_list *ctx;
+       struct sysctl_oid_list *child;
+       struct cmn600_softc *sc;
+       int cpu, domain, i, u;
+       const char *dname;
+       rman_res_t count, periph_base, rootnode_base;
+       struct cmn600_node *node;
+
+       dname = device_get_name(dev);
+       sc = device_get_softc(dev);
+       sc->sc_dev = dev;
+       u = device_get_unit(dev);
+       sc->sc_unit = u;
+       domain = 0;
+
+       if ((resource_int_value(dname, u, "domain", &domain) == 0 ||
+           bus_get_domain(dev, &domain) == 0) && domain < MAXMEMDOM) {
+               sc->sc_domain = domain;
+       }
+       if (domain == -1) /* NUMA not supported. Use single domain. */
+               domain = 0;
+       sc->sc_domain = domain;
+       device_printf(dev, "domain=%d\n", sc->sc_domain);
+
+       cpu = CPU_FFS(&cpuset_domain[domain]) - 1;
+
+       i = bus_alloc_resources(dev, cmn600_res_spec, sc->sc_res);
+       if (i != 0) {
+               device_printf(dev, "cannot allocate resources for device 
(%d)\n",
+                   i);
+               return (i);
+       }
+
+       bus_get_resource(dev, cmn600_res_spec[0].type, cmn600_res_spec[0].rid,
+           &periph_base, &count);
+       bus_get_resource(dev, cmn600_res_spec[1].type, cmn600_res_spec[1].rid,
+           &rootnode_base, &count);
+       rootnode_base -= periph_base;
+       if (bootverbose)
+               printf("ROOTNODE at %lx x %lx\n", rootnode_base, count);
+
+       sc->sc_rootnode = cmn600_create_node(sc, rootnode_base, NULL, 0);
+       ctx = device_get_sysctl_ctx(sc->sc_dev);
+
+       child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
+       SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "dump_nodes", CTLTYPE_INT |
+           CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, cmn600_sysctl_dump_nodes,
+           "U", "Dump CMN-600 nodes tree");
+
+       node = sc->sc_dtcnode;
+       if (node == NULL)
+               return (ENXIO);
+
+       cmn600_pmc_register(sc->sc_unit, (void *)sc, domain);
+
+       node->nd_write8(node, POR_DT_PMCR, 0);
+       node->nd_write8(node, POR_DT_PMOVSR_CLR, POR_DT_PMOVSR_ALL);
+       node->nd_write8(node, POR_DT_PMCR, POR_DT_PMCR_OVFL_INTR_EN);
+       node->nd_write8(node, POR_DT_DTC_CTL, POR_DT_DTC_CTL_DT_EN);
+
+       if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE,
+           cmn600_intr, NULL, sc, &sc->sc_ih)) {
+               bus_release_resources(dev, cmn600_res_spec, sc->sc_res);
+               device_printf(dev, "cannot setup interrupt handler\n");
+               cmn600_acpi_detach(dev);
+               return (ENXIO);
+       }
+       if (bus_bind_intr(dev, sc->sc_res[2], cpu)) {
+               bus_teardown_intr(dev, sc->sc_res[2], sc->sc_ih);
+               bus_release_resources(dev, cmn600_res_spec, sc->sc_res);
+               device_printf(dev, "cannot setup interrupt handler\n");
+               cmn600_acpi_detach(dev);
+               return (ENXIO);
+       }
+       return (0);
+}
+
+static int
+cmn600_acpi_detach(device_t dev)
+{
+       struct cmn600_softc *sc;
+       struct cmn600_node *node;
+
+       sc = device_get_softc(dev);
+       if (sc->sc_res[2] != NULL) {
+               bus_teardown_intr(dev, sc->sc_res[2], sc->sc_ih);
+       }
+
+       node = sc->sc_dtcnode;
+       node->nd_write4(node, POR_DT_DTC_CTL,
+           node->nd_read4(node, POR_DT_DTC_CTL) & ~POR_DT_DTC_CTL_DT_EN);
+       node->nd_write8(node, POR_DT_PMOVSR_CLR, POR_DT_PMOVSR_ALL);
+
+       cmn600_pmc_unregister(sc->sc_unit);
+       cmn600_destroy_node(sc->sc_rootnode);
+       bus_release_resources(dev, cmn600_res_spec, sc->sc_res);
+
+       return (0);
+}
+
+int
+cmn600_pmu_intr_cb(void *arg, int (*handler)(struct trapframe *tf, int unit,
+    int i))
+{
+       struct cmn600_softc *sc;
+
+       sc = (struct cmn600_softc *) arg;
+       sc->sc_pmu_ih = handler;
+       return (0);
+}
+
+static int
+cmn600_intr(void *arg)
+{
+       struct cmn600_node *node;
+       struct cmn600_softc *sc;
+       struct trapframe *tf;
+       uint64_t mask, ready, val;
+       int i;
+       
+       tf = PCPU_GET(curthread)->td_intr_frame;
+       sc = (struct cmn600_softc *) arg;
+       node = sc->sc_dtcnode;
+       val = node->nd_read8(node, POR_DT_PMOVSR);
+       if (val & POR_DT_PMOVSR_CYCLE_COUNTER)
+               node->nd_write8(node, POR_DT_PMOVSR_CLR,
+                   POR_DT_PMOVSR_CYCLE_COUNTER);
+       if (val & POR_DT_PMOVSR_EVENT_COUNTERS) {
+               for (ready = 0, i = 0; i < 8; i++) {
+                       mask = 1 << i;
+                       if ((val & mask) == 0)
+                               continue;
+                       if (sc->sc_pmu_ih != NULL)
+                               sc->sc_pmu_ih(tf, sc->sc_unit, i);
+                       ready |= mask;
+
+               }
+               node->nd_write8(node, POR_DT_PMOVSR_CLR, ready);
+       }
+
+       return (FILTER_HANDLED);
+}
+
+static device_method_t cmn600_acpi_methods[] = {
+       /* Device interface */
+       DEVMETHOD(device_probe,                 cmn600_acpi_probe),
+       DEVMETHOD(device_attach,                cmn600_acpi_attach),
+       DEVMETHOD(device_detach,                cmn600_acpi_detach),
+
+       /* End */
+       DEVMETHOD_END
+};
+
+static driver_t cmn600_acpi_driver = {
+       "cmn600",
+       cmn600_acpi_methods,
+       sizeof(struct cmn600_softc),
+};
+static devclass_t cmn600_acpi_devclass;
+
+DRIVER_MODULE(cmn600, acpi, cmn600_acpi_driver, cmn600_acpi_devclass, 0, 0);
+MODULE_VERSION(cmn600, 1);
diff --git a/sys/arm64/include/cmn600_reg.h b/sys/arm64/include/cmn600_reg.h
index ffc4634f2781..d7fc9ecaac98 100644
--- a/sys/arm64/include/cmn600_reg.h
+++ b/sys/arm64/include/cmn600_reg.h
@@ -75,6 +75,19 @@ int pmu_cmn600_md8(void *arg, int nodeid, int node_type, 
off_t reg,
 #define                POR_CFGM_NODE_INFO_NODE_TYPE_MASK       0xffff
 #define                POR_CFGM_NODE_INFO_NODE_TYPE_SHIFT      0
 
+#define                NODE_ID_SUB_MASK                0x3
+#define                NODE_ID_SUB_SHIFT               0
+#define                NODE_ID_PORT_MASK               0x4
+#define                NODE_ID_PORT_SHIFT              2
+#define                NODE_ID_X2B_MASK                (0x3 << 3)
+#define                NODE_ID_X2B_SHIFT               3
+#define                NODE_ID_Y2B_MASK                (0x3 << 5)
+#define                NODE_ID_Y2B_SHIFT               5
+#define                NODE_ID_X3B_MASK                (0x7 << 3)
+#define                NODE_ID_X3B_SHIFT               3
+#define                NODE_ID_Y3B_MASK                (0x7 << 6)
+#define                NODE_ID_Y3B_SHIFT               6
+
 #define        NODE_TYPE_INVALID       0x000
 #define        NODE_TYPE_DVM           0x001
 #define        NODE_TYPE_CFG           0x002
@@ -265,6 +278,8 @@ int pmu_cmn600_md8(void *arg, int nodeid, int node_type, 
off_t reg,
 #define        POR_DT_PMEVCNTEF                        0x2020  /* rw */
 #define        POR_DT_PMEVCNTGH                        0x2030  /* rw */
 #define        POR_DT_PMEVCNT(x)                       (0x2000 + ((x) * 0x10))
+#define                POR_DT_PMEVCNT_EVENCNT_SHIFT    0
+#define                POR_DT_PMEVCNT_ODDCNT_SHIFT     32
 #define        POR_DT_PMCCNTR                          0x2040  /* rw */
 #define        POR_DT_PMEVCNTSRAB                      0x2050  /* rw */
 #define        POR_DT_PMEVCNTSRCD                      0x2060  /* rw */
@@ -512,6 +527,7 @@ int pmu_cmn600_md8(void *arg, int nodeid, int node_type, 
off_t reg,
 #define                POR_DTM_PMU_CONFIG_VCNT_INPUT_SEL_SHIFT 32
 #define                POR_DTM_PMU_CONFIG_VCNT_INPUT_SEL_WIDTH 8
 #define        POR_DTM_PMEVCNT                         0x2220  /* rw */
+#define                POR_DTM_PMEVCNT_CNTR_WIDTH      16
 #define        POR_DTM_PMEVCNTSR                       0x2240  /* rw */
 
 /* RN-D registers */
diff --git a/sys/dev/hwpmc/hwpmc_cmn600.c b/sys/dev/hwpmc/hwpmc_cmn600.c
new file mode 100644
index 000000000000..b33dd2693b25
--- /dev/null
+++ b/sys/dev/hwpmc/hwpmc_cmn600.c
@@ -0,0 +1,830 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2003-2008 Joseph Koshy
+ * Copyright (c) 2007 The FreeBSD Foundation
+ * Copyright (c) 2021 ARM Ltd
+ *
+ * Portions of this software were developed by A. Joseph Koshy under
+ * sponsorship from the FreeBSD Foundation and Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Arm CoreLink CMN-600 Coherent Mesh Network PMU Driver */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/pmc.h>
+#include <sys/pmckern.h>
+#include <sys/systm.h>
+
+#include <machine/cmn600_reg.h>
+
+struct cmn600_descr {
+       struct pmc_descr pd_descr;  /* "base class" */
+       void            *pd_rw_arg; /* Argument to use with read/write */
+       struct pmc      *pd_pmc;
+       struct pmc_hw   *pd_phw;
+       uint32_t         pd_nodeid;
+       int32_t          pd_node_type;
+       int              pd_local_counter;
+
+};
+
+static struct cmn600_descr **cmn600_pmcdesc;
+
+static struct cmn600_pmc cmn600_pmcs[CMN600_UNIT_MAX];
+static int cmn600_units = 0;
+
+static inline struct cmn600_descr *
+cmn600desc(int ri)
+{
+
+       return (cmn600_pmcdesc[ri]);
+}
+
+static inline int
+class_ri2unit(int ri)
+{
+
*** 767 LINES SKIPPED ***

Reply via email to