From nobody Sun Jun 26 22:28:03 2022 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 27833861886; Sun, 26 Jun 2022 22:28:04 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4LWQVC6w8mz3r6b; Sun, 26 Jun 2022 22:28:03 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1656282484; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=A87YYAf1EfxUsI5LRcDGMq+TfDnVUdDfpRNWr1R1/3Y=; b=GzIihn5hfvgX6h6d0Luv4SpKLZJDuZjv7gnOmRCetZW6dnBCi9Hq0asRM33rZJgCXjgYJc AOyowtPm4S4JhRGR8ejC9KIS7pDtv84eNHo2oRnizFl21Ma6hgDh7rgJcI1ogc0U6PgizM I6u5pmVp3zJOyx/vUuNZ9tlpUgVNQeZg+oPRWvzOXQtMbCmBG7HD0FooqAsLU4VEaueAgk hZi1emDO2D9fF2Iu7W82iHyJBLylBMzyiRlSoc0Nff8OC8iYEc5MXZMUqPFUXpmxdteRSA jqvcVV6P/ZPdh8Q+lI+bUB2UgKc8oBDYTyAJh+b3BOc8oI+s+OrZ7QpMskNmLg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id CD1C01C817; Sun, 26 Jun 2022 22:28:03 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 25QMS3Ql089732; Sun, 26 Jun 2022 22:28:03 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 25QMS37N089731; Sun, 26 Jun 2022 22:28:03 GMT (envelope-from git) Date: Sun, 26 Jun 2022 22:28:03 GMT Message-Id: <202206262228.25QMS37N089731@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Toomas Soome Subject: git: 59191f3573f6 - main - Add support of ARM CMN-600 controller, PMU access functions only. Add support of PMU counters of ARM CMN-600 controller. List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: tsoome X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 59191f3573f6cb2ea055ac319cbcb68823ca8e17 Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1656282484; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=A87YYAf1EfxUsI5LRcDGMq+TfDnVUdDfpRNWr1R1/3Y=; b=pvjnPATWC86RkP5pAX/eSCh66hUjKtukZaWDRID0dJk7NjcTSaRg6SGw8eJqqrZZgBael4 udohPPRn9nCbTW2KnXH5vBP1I+C/hIf/6eDOkUraW+MGs4r0IRZlnS0PYoQEBt8wWgqxUV 8775+G4sz9tFE3lNk71hXNpVkF+UzHnlgqs/NcHNXhjJp2ax2a+nr4ukCevD6lNQl6lvP4 q334wHDcN6klXEXAURlJFrPqiNC7bvaDrUZWW5Q9IF/oS6O5YJicqIKpQoaTGfFZWdQ4rs rCXetZEnGhRVwgYLJ6CDPa9udFp/Aa7rJbA8byff5ByzM4q19rohSunXQyFm0Q== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1656282484; a=rsa-sha256; cv=none; b=jkXA4zqo3NxDzodZSw2Zwe2sAuf/uqMLIJ5Q8ltdQ04JHA6OlJnVHz0CD436yDAlO0eEJO mOKYeNnTBclM28p+sbnR5XiKd/6Lm3KXz36MdvS4JUjDlDbSlItK9WMiG1xcQ1E4k9E+Ay ZQEYOm9ZgueOUPF/cVSO2Ve+v7nKDOv7KZcQPQHbYH0ZGVAjr1osmyQ1VSpd8mEYv9HIlS VnWILJhqEMUqKi/4SfiSOcuhjogQllJB4ElV8/4Y9UXlPvy9YOfxD/0VpRIyIT2FPK5aLB 9xODsFguGs6t4ZtD2t3LcRlciVx2zBK+49yC2oZw5/QnzV7UXlbaxqHW6ihQQg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by tsoome: URL: https://cgit.FreeBSD.org/src/commit/?id=59191f3573f6cb2ea055ac319cbcb68823ca8e17 commit 59191f3573f6cb2ea055ac319cbcb68823ca8e17 Author: Aleksandr Rybalko AuthorDate: 2022-02-16 00:36:17 +0000 Commit: Toomas Soome 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 +__FBSDID("$FreeBSD$"); + +#include "opt_acpi.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#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, ""); + 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 ""; + } +#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 ""; + } +#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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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 ***