18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// Copyright (C) 2016-2020 Arm Limited 38c2ecf20Sopenharmony_ci// CMN-600 Coherent Mesh Network PMU driver 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#include <linux/acpi.h> 68c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 78c2ecf20Sopenharmony_ci#include <linux/bitops.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/sort.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* Common register stuff */ 208c2ecf20Sopenharmony_ci#define CMN_NODE_INFO 0x0000 218c2ecf20Sopenharmony_ci#define CMN_NI_NODE_TYPE GENMASK_ULL(15, 0) 228c2ecf20Sopenharmony_ci#define CMN_NI_NODE_ID GENMASK_ULL(31, 16) 238c2ecf20Sopenharmony_ci#define CMN_NI_LOGICAL_ID GENMASK_ULL(47, 32) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define CMN_NODEID_DEVID(reg) ((reg) & 3) 268c2ecf20Sopenharmony_ci#define CMN_NODEID_PID(reg) (((reg) >> 2) & 1) 278c2ecf20Sopenharmony_ci#define CMN_NODEID_X(reg, bits) ((reg) >> (3 + (bits))) 288c2ecf20Sopenharmony_ci#define CMN_NODEID_Y(reg, bits) (((reg) >> 3) & ((1U << (bits)) - 1)) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define CMN_CHILD_INFO 0x0080 318c2ecf20Sopenharmony_ci#define CMN_CI_CHILD_COUNT GENMASK_ULL(15, 0) 328c2ecf20Sopenharmony_ci#define CMN_CI_CHILD_PTR_OFFSET GENMASK_ULL(31, 16) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define CMN_CHILD_NODE_ADDR GENMASK(27,0) 358c2ecf20Sopenharmony_ci#define CMN_CHILD_NODE_EXTERNAL BIT(31) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define CMN_ADDR_NODE_PTR GENMASK(27, 14) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define CMN_NODE_PTR_DEVID(ptr) (((ptr) >> 2) & 3) 408c2ecf20Sopenharmony_ci#define CMN_NODE_PTR_PID(ptr) ((ptr) & 1) 418c2ecf20Sopenharmony_ci#define CMN_NODE_PTR_X(ptr, bits) ((ptr) >> (6 + (bits))) 428c2ecf20Sopenharmony_ci#define CMN_NODE_PTR_Y(ptr, bits) (((ptr) >> 6) & ((1U << (bits)) - 1)) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define CMN_MAX_XPS (8 * 8) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* The CFG node has one other useful purpose */ 478c2ecf20Sopenharmony_ci#define CMN_CFGM_PERIPH_ID_2 0x0010 488c2ecf20Sopenharmony_ci#define CMN_CFGM_PID2_REVISION GENMASK(7, 4) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* PMU registers occupy the 3rd 4KB page of each node's 16KB space */ 518c2ecf20Sopenharmony_ci#define CMN_PMU_OFFSET 0x2000 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* For most nodes, this is all there is */ 548c2ecf20Sopenharmony_ci#define CMN_PMU_EVENT_SEL 0x000 558c2ecf20Sopenharmony_ci#define CMN_PMU_EVENTn_ID_SHIFT(n) ((n) * 8) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* DTMs live in the PMU space of XP registers */ 588c2ecf20Sopenharmony_ci#define CMN_DTM_WPn(n) (0x1A0 + (n) * 0x18) 598c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG(n) (CMN_DTM_WPn(n) + 0x00) 608c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG_WP_COMBINE BIT(6) 618c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG_WP_EXCLUSIVE BIT(5) 628c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG_WP_GRP BIT(4) 638c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG_WP_CHN_SEL GENMASK_ULL(3, 1) 648c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_CONFIG_WP_DEV_SEL BIT(0) 658c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_VAL(n) (CMN_DTM_WPn(n) + 0x08) 668c2ecf20Sopenharmony_ci#define CMN_DTM_WPn_MASK(n) (CMN_DTM_WPn(n) + 0x10) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define CMN_DTM_PMU_CONFIG 0x210 698c2ecf20Sopenharmony_ci#define CMN__PMEVCNT0_INPUT_SEL GENMASK_ULL(37, 32) 708c2ecf20Sopenharmony_ci#define CMN__PMEVCNT0_INPUT_SEL_WP 0x00 718c2ecf20Sopenharmony_ci#define CMN__PMEVCNT0_INPUT_SEL_XP 0x04 728c2ecf20Sopenharmony_ci#define CMN__PMEVCNT0_INPUT_SEL_DEV 0x10 738c2ecf20Sopenharmony_ci#define CMN__PMEVCNT0_GLOBAL_NUM GENMASK_ULL(18, 16) 748c2ecf20Sopenharmony_ci#define CMN__PMEVCNTn_GLOBAL_NUM_SHIFT(n) ((n) * 4) 758c2ecf20Sopenharmony_ci#define CMN__PMEVCNT_PAIRED(n) BIT(4 + (n)) 768c2ecf20Sopenharmony_ci#define CMN__PMEVCNT23_COMBINED BIT(2) 778c2ecf20Sopenharmony_ci#define CMN__PMEVCNT01_COMBINED BIT(1) 788c2ecf20Sopenharmony_ci#define CMN_DTM_PMU_CONFIG_PMU_EN BIT(0) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define CMN_DTM_PMEVCNT 0x220 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#define CMN_DTM_PMEVCNTSR 0x240 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define CMN_DTM_NUM_COUNTERS 4 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* The DTC node is where the magic happens */ 878c2ecf20Sopenharmony_ci#define CMN_DT_DTC_CTL 0x0a00 888c2ecf20Sopenharmony_ci#define CMN_DT_DTC_CTL_DT_EN BIT(0) 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* DTC counters are paired in 64-bit registers on a 16-byte stride. Yuck */ 918c2ecf20Sopenharmony_ci#define _CMN_DT_CNT_REG(n) ((((n) / 2) * 4 + (n) % 2) * 4) 928c2ecf20Sopenharmony_ci#define CMN_DT_PMEVCNT(n) (CMN_PMU_OFFSET + _CMN_DT_CNT_REG(n)) 938c2ecf20Sopenharmony_ci#define CMN_DT_PMCCNTR (CMN_PMU_OFFSET + 0x40) 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define CMN_DT_PMEVCNTSR(n) (CMN_PMU_OFFSET + 0x50 + _CMN_DT_CNT_REG(n)) 968c2ecf20Sopenharmony_ci#define CMN_DT_PMCCNTRSR (CMN_PMU_OFFSET + 0x90) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define CMN_DT_PMCR (CMN_PMU_OFFSET + 0x100) 998c2ecf20Sopenharmony_ci#define CMN_DT_PMCR_PMU_EN BIT(0) 1008c2ecf20Sopenharmony_ci#define CMN_DT_PMCR_CNTR_RST BIT(5) 1018c2ecf20Sopenharmony_ci#define CMN_DT_PMCR_OVFL_INTR_EN BIT(6) 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define CMN_DT_PMOVSR (CMN_PMU_OFFSET + 0x118) 1048c2ecf20Sopenharmony_ci#define CMN_DT_PMOVSR_CLR (CMN_PMU_OFFSET + 0x120) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define CMN_DT_PMSSR (CMN_PMU_OFFSET + 0x128) 1078c2ecf20Sopenharmony_ci#define CMN_DT_PMSSR_SS_STATUS(n) BIT(n) 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#define CMN_DT_PMSRR (CMN_PMU_OFFSET + 0x130) 1108c2ecf20Sopenharmony_ci#define CMN_DT_PMSRR_SS_REQ BIT(0) 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci#define CMN_DT_NUM_COUNTERS 8 1138c2ecf20Sopenharmony_ci#define CMN_MAX_DTCS 4 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * Even in the worst case a DTC counter can't wrap in fewer than 2^42 cycles, 1178c2ecf20Sopenharmony_ci * so throwing away one bit to make overflow handling easy is no big deal. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci#define CMN_COUNTER_INIT 0x80000000 1208c2ecf20Sopenharmony_ci/* Similarly for the 40-bit cycle counter */ 1218c2ecf20Sopenharmony_ci#define CMN_CC_INIT 0x8000000000ULL 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* Event attributes */ 1258c2ecf20Sopenharmony_ci#define CMN_CONFIG_TYPE GENMASK(15, 0) 1268c2ecf20Sopenharmony_ci#define CMN_CONFIG_EVENTID GENMASK(23, 16) 1278c2ecf20Sopenharmony_ci#define CMN_CONFIG_OCCUPID GENMASK(27, 24) 1288c2ecf20Sopenharmony_ci#define CMN_CONFIG_BYNODEID BIT(31) 1298c2ecf20Sopenharmony_ci#define CMN_CONFIG_NODEID GENMASK(47, 32) 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define CMN_EVENT_TYPE(event) FIELD_GET(CMN_CONFIG_TYPE, (event)->attr.config) 1328c2ecf20Sopenharmony_ci#define CMN_EVENT_EVENTID(event) FIELD_GET(CMN_CONFIG_EVENTID, (event)->attr.config) 1338c2ecf20Sopenharmony_ci#define CMN_EVENT_OCCUPID(event) FIELD_GET(CMN_CONFIG_OCCUPID, (event)->attr.config) 1348c2ecf20Sopenharmony_ci#define CMN_EVENT_BYNODEID(event) FIELD_GET(CMN_CONFIG_BYNODEID, (event)->attr.config) 1358c2ecf20Sopenharmony_ci#define CMN_EVENT_NODEID(event) FIELD_GET(CMN_CONFIG_NODEID, (event)->attr.config) 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define CMN_CONFIG_WP_COMBINE GENMASK(27, 24) 1388c2ecf20Sopenharmony_ci#define CMN_CONFIG_WP_DEV_SEL BIT(48) 1398c2ecf20Sopenharmony_ci#define CMN_CONFIG_WP_CHN_SEL GENMASK(50, 49) 1408c2ecf20Sopenharmony_ci#define CMN_CONFIG_WP_GRP BIT(52) 1418c2ecf20Sopenharmony_ci#define CMN_CONFIG_WP_EXCLUSIVE BIT(53) 1428c2ecf20Sopenharmony_ci#define CMN_CONFIG1_WP_VAL GENMASK(63, 0) 1438c2ecf20Sopenharmony_ci#define CMN_CONFIG2_WP_MASK GENMASK(63, 0) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_COMBINE(event) FIELD_GET(CMN_CONFIG_WP_COMBINE, (event)->attr.config) 1468c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_DEV_SEL(event) FIELD_GET(CMN_CONFIG_WP_DEV_SEL, (event)->attr.config) 1478c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_CHN_SEL(event) FIELD_GET(CMN_CONFIG_WP_CHN_SEL, (event)->attr.config) 1488c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_GRP(event) FIELD_GET(CMN_CONFIG_WP_GRP, (event)->attr.config) 1498c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_EXCLUSIVE(event) FIELD_GET(CMN_CONFIG_WP_EXCLUSIVE, (event)->attr.config) 1508c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_VAL(event) FIELD_GET(CMN_CONFIG1_WP_VAL, (event)->attr.config1) 1518c2ecf20Sopenharmony_ci#define CMN_EVENT_WP_MASK(event) FIELD_GET(CMN_CONFIG2_WP_MASK, (event)->attr.config2) 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* Made-up event IDs for watchpoint direction */ 1548c2ecf20Sopenharmony_ci#define CMN_WP_UP 0 1558c2ecf20Sopenharmony_ci#define CMN_WP_DOWN 2 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* r0px probably don't exist in silicon, thankfully */ 1598c2ecf20Sopenharmony_cienum cmn_revision { 1608c2ecf20Sopenharmony_ci CMN600_R1P0, 1618c2ecf20Sopenharmony_ci CMN600_R1P1, 1628c2ecf20Sopenharmony_ci CMN600_R1P2, 1638c2ecf20Sopenharmony_ci CMN600_R1P3, 1648c2ecf20Sopenharmony_ci CMN600_R2P0, 1658c2ecf20Sopenharmony_ci CMN600_R3P0, 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cienum cmn_node_type { 1698c2ecf20Sopenharmony_ci CMN_TYPE_INVALID, 1708c2ecf20Sopenharmony_ci CMN_TYPE_DVM, 1718c2ecf20Sopenharmony_ci CMN_TYPE_CFG, 1728c2ecf20Sopenharmony_ci CMN_TYPE_DTC, 1738c2ecf20Sopenharmony_ci CMN_TYPE_HNI, 1748c2ecf20Sopenharmony_ci CMN_TYPE_HNF, 1758c2ecf20Sopenharmony_ci CMN_TYPE_XP, 1768c2ecf20Sopenharmony_ci CMN_TYPE_SBSX, 1778c2ecf20Sopenharmony_ci CMN_TYPE_RNI = 0xa, 1788c2ecf20Sopenharmony_ci CMN_TYPE_RND = 0xd, 1798c2ecf20Sopenharmony_ci CMN_TYPE_RNSAM = 0xf, 1808c2ecf20Sopenharmony_ci CMN_TYPE_CXRA = 0x100, 1818c2ecf20Sopenharmony_ci CMN_TYPE_CXHA = 0x101, 1828c2ecf20Sopenharmony_ci CMN_TYPE_CXLA = 0x102, 1838c2ecf20Sopenharmony_ci /* Not a real node type */ 1848c2ecf20Sopenharmony_ci CMN_TYPE_WP = 0x7770 1858c2ecf20Sopenharmony_ci}; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistruct arm_cmn_node { 1888c2ecf20Sopenharmony_ci void __iomem *pmu_base; 1898c2ecf20Sopenharmony_ci u16 id, logid; 1908c2ecf20Sopenharmony_ci enum cmn_node_type type; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci union { 1938c2ecf20Sopenharmony_ci /* Device node */ 1948c2ecf20Sopenharmony_ci struct { 1958c2ecf20Sopenharmony_ci int to_xp; 1968c2ecf20Sopenharmony_ci /* DN/HN-F/CXHA */ 1978c2ecf20Sopenharmony_ci unsigned int occupid_val; 1988c2ecf20Sopenharmony_ci unsigned int occupid_count; 1998c2ecf20Sopenharmony_ci }; 2008c2ecf20Sopenharmony_ci /* XP */ 2018c2ecf20Sopenharmony_ci struct { 2028c2ecf20Sopenharmony_ci int dtc; 2038c2ecf20Sopenharmony_ci u32 pmu_config_low; 2048c2ecf20Sopenharmony_ci union { 2058c2ecf20Sopenharmony_ci u8 input_sel[4]; 2068c2ecf20Sopenharmony_ci __le32 pmu_config_high; 2078c2ecf20Sopenharmony_ci }; 2088c2ecf20Sopenharmony_ci s8 wp_event[4]; 2098c2ecf20Sopenharmony_ci }; 2108c2ecf20Sopenharmony_ci }; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci union { 2138c2ecf20Sopenharmony_ci u8 event[4]; 2148c2ecf20Sopenharmony_ci __le32 event_sel; 2158c2ecf20Sopenharmony_ci }; 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistruct arm_cmn_dtc { 2198c2ecf20Sopenharmony_ci void __iomem *base; 2208c2ecf20Sopenharmony_ci int irq; 2218c2ecf20Sopenharmony_ci int irq_friend; 2228c2ecf20Sopenharmony_ci bool cc_active; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci struct perf_event *counters[CMN_DT_NUM_COUNTERS]; 2258c2ecf20Sopenharmony_ci struct perf_event *cycles; 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci#define CMN_STATE_DISABLED BIT(0) 2298c2ecf20Sopenharmony_ci#define CMN_STATE_TXN BIT(1) 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistruct arm_cmn { 2328c2ecf20Sopenharmony_ci struct device *dev; 2338c2ecf20Sopenharmony_ci void __iomem *base; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci enum cmn_revision rev; 2368c2ecf20Sopenharmony_ci u8 mesh_x; 2378c2ecf20Sopenharmony_ci u8 mesh_y; 2388c2ecf20Sopenharmony_ci u16 num_xps; 2398c2ecf20Sopenharmony_ci u16 num_dns; 2408c2ecf20Sopenharmony_ci struct arm_cmn_node *xps; 2418c2ecf20Sopenharmony_ci struct arm_cmn_node *dns; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci struct arm_cmn_dtc *dtc; 2448c2ecf20Sopenharmony_ci unsigned int num_dtcs; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci int cpu; 2478c2ecf20Sopenharmony_ci struct hlist_node cpuhp_node; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci unsigned int state; 2508c2ecf20Sopenharmony_ci struct pmu pmu; 2518c2ecf20Sopenharmony_ci}; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci#define to_cmn(p) container_of(p, struct arm_cmn, pmu) 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int arm_cmn_hp_state; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistruct arm_cmn_hw_event { 2588c2ecf20Sopenharmony_ci struct arm_cmn_node *dn; 2598c2ecf20Sopenharmony_ci u64 dtm_idx[2]; 2608c2ecf20Sopenharmony_ci unsigned int dtc_idx; 2618c2ecf20Sopenharmony_ci u8 dtcs_used; 2628c2ecf20Sopenharmony_ci u8 num_dns; 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci#define for_each_hw_dn(hw, dn, i) \ 2668c2ecf20Sopenharmony_ci for (i = 0, dn = hw->dn; i < hw->num_dns; i++, dn++) 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic struct arm_cmn_hw_event *to_cmn_hw(struct perf_event *event) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct arm_cmn_hw_event) > offsetof(struct hw_perf_event, target)); 2718c2ecf20Sopenharmony_ci return (struct arm_cmn_hw_event *)&event->hw; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void arm_cmn_set_index(u64 x[], unsigned int pos, unsigned int val) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci x[pos / 32] |= (u64)val << ((pos % 32) * 2); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic unsigned int arm_cmn_get_index(u64 x[], unsigned int pos) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci return (x[pos / 32] >> ((pos % 32) * 2)) & 3; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistruct arm_cmn_event_attr { 2858c2ecf20Sopenharmony_ci struct device_attribute attr; 2868c2ecf20Sopenharmony_ci enum cmn_node_type type; 2878c2ecf20Sopenharmony_ci u8 eventid; 2888c2ecf20Sopenharmony_ci u8 occupid; 2898c2ecf20Sopenharmony_ci}; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistruct arm_cmn_format_attr { 2928c2ecf20Sopenharmony_ci struct device_attribute attr; 2938c2ecf20Sopenharmony_ci u64 field; 2948c2ecf20Sopenharmony_ci int config; 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int arm_cmn_xyidbits(const struct arm_cmn *cmn) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci return cmn->mesh_x > 4 || cmn->mesh_y > 4 ? 3 : 2; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic void arm_cmn_init_node_to_xp(const struct arm_cmn *cmn, 3038c2ecf20Sopenharmony_ci struct arm_cmn_node *dn) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci int bits = arm_cmn_xyidbits(cmn); 3068c2ecf20Sopenharmony_ci int x = CMN_NODEID_X(dn->id, bits); 3078c2ecf20Sopenharmony_ci int y = CMN_NODEID_Y(dn->id, bits); 3088c2ecf20Sopenharmony_ci int xp_idx = cmn->mesh_x * y + x; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci dn->to_xp = (cmn->xps + xp_idx) - dn; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic struct arm_cmn_node *arm_cmn_node_to_xp(struct arm_cmn_node *dn) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci return dn->type == CMN_TYPE_XP ? dn : dn + dn->to_xp; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic struct arm_cmn_node *arm_cmn_node(const struct arm_cmn *cmn, 3198c2ecf20Sopenharmony_ci enum cmn_node_type type) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int i; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci for (i = 0; i < cmn->num_dns; i++) 3248c2ecf20Sopenharmony_ci if (cmn->dns[i].type == type) 3258c2ecf20Sopenharmony_ci return &cmn->dns[i]; 3268c2ecf20Sopenharmony_ci return NULL; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci#define CMN_EVENT_ATTR(_name, _type, _eventid, _occupid) \ 3308c2ecf20Sopenharmony_ci (&((struct arm_cmn_event_attr[]) {{ \ 3318c2ecf20Sopenharmony_ci .attr = __ATTR(_name, 0444, arm_cmn_event_show, NULL), \ 3328c2ecf20Sopenharmony_ci .type = _type, \ 3338c2ecf20Sopenharmony_ci .eventid = _eventid, \ 3348c2ecf20Sopenharmony_ci .occupid = _occupid, \ 3358c2ecf20Sopenharmony_ci }})[0].attr.attr) 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic bool arm_cmn_is_occup_event(enum cmn_node_type type, unsigned int id) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci return (type == CMN_TYPE_DVM && id == 0x05) || 3408c2ecf20Sopenharmony_ci (type == CMN_TYPE_HNF && id == 0x0f); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic ssize_t arm_cmn_event_show(struct device *dev, 3448c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct arm_cmn_event_attr *eattr; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci eattr = container_of(attr, typeof(*eattr), attr); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (eattr->type == CMN_TYPE_DTC) 3518c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "type=0x%x\n", eattr->type); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (eattr->type == CMN_TYPE_WP) 3548c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, 3558c2ecf20Sopenharmony_ci "type=0x%x,eventid=0x%x,wp_dev_sel=?,wp_chn_sel=?,wp_grp=?,wp_val=?,wp_mask=?\n", 3568c2ecf20Sopenharmony_ci eattr->type, eattr->eventid); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (arm_cmn_is_occup_event(eattr->type, eattr->eventid)) 3598c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "type=0x%x,eventid=0x%x,occupid=0x%x\n", 3608c2ecf20Sopenharmony_ci eattr->type, eattr->eventid, eattr->occupid); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "type=0x%x,eventid=0x%x\n", 3638c2ecf20Sopenharmony_ci eattr->type, eattr->eventid); 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj, 3678c2ecf20Sopenharmony_ci struct attribute *attr, 3688c2ecf20Sopenharmony_ci int unused) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 3718c2ecf20Sopenharmony_ci struct arm_cmn *cmn = to_cmn(dev_get_drvdata(dev)); 3728c2ecf20Sopenharmony_ci struct arm_cmn_event_attr *eattr; 3738c2ecf20Sopenharmony_ci enum cmn_node_type type; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci eattr = container_of(attr, typeof(*eattr), attr.attr); 3768c2ecf20Sopenharmony_ci type = eattr->type; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Watchpoints aren't nodes */ 3798c2ecf20Sopenharmony_ci if (type == CMN_TYPE_WP) 3808c2ecf20Sopenharmony_ci type = CMN_TYPE_XP; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* Revision-specific differences */ 3838c2ecf20Sopenharmony_ci if (cmn->rev < CMN600_R1P2) { 3848c2ecf20Sopenharmony_ci if (type == CMN_TYPE_HNF && eattr->eventid == 0x1b) 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (!arm_cmn_node(cmn, type)) 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci return attr->mode; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci#define _CMN_EVENT_DVM(_name, _event, _occup) \ 3958c2ecf20Sopenharmony_ci CMN_EVENT_ATTR(dn_##_name, CMN_TYPE_DVM, _event, _occup) 3968c2ecf20Sopenharmony_ci#define CMN_EVENT_DTC(_name) \ 3978c2ecf20Sopenharmony_ci CMN_EVENT_ATTR(dtc_##_name, CMN_TYPE_DTC, 0, 0) 3988c2ecf20Sopenharmony_ci#define _CMN_EVENT_HNF(_name, _event, _occup) \ 3998c2ecf20Sopenharmony_ci CMN_EVENT_ATTR(hnf_##_name, CMN_TYPE_HNF, _event, _occup) 4008c2ecf20Sopenharmony_ci#define CMN_EVENT_HNI(_name, _event) \ 4018c2ecf20Sopenharmony_ci CMN_EVENT_ATTR(hni_##_name, CMN_TYPE_HNI, _event, 0) 4028c2ecf20Sopenharmony_ci#define __CMN_EVENT_XP(_name, _event) \ 4038c2ecf20Sopenharmony_ci CMN_EVENT_ATTR(mxp_##_name, CMN_TYPE_XP, _event, 0) 4048c2ecf20Sopenharmony_ci#define CMN_EVENT_SBSX(_name, _event) \ 4058c2ecf20Sopenharmony_ci CMN_EVENT_ATTR(sbsx_##_name, CMN_TYPE_SBSX, _event, 0) 4068c2ecf20Sopenharmony_ci#define CMN_EVENT_RNID(_name, _event) \ 4078c2ecf20Sopenharmony_ci CMN_EVENT_ATTR(rnid_##_name, CMN_TYPE_RNI, _event, 0) 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci#define CMN_EVENT_DVM(_name, _event) \ 4108c2ecf20Sopenharmony_ci _CMN_EVENT_DVM(_name, _event, 0) 4118c2ecf20Sopenharmony_ci#define CMN_EVENT_HNF(_name, _event) \ 4128c2ecf20Sopenharmony_ci _CMN_EVENT_HNF(_name, _event, 0) 4138c2ecf20Sopenharmony_ci#define _CMN_EVENT_XP(_name, _event) \ 4148c2ecf20Sopenharmony_ci __CMN_EVENT_XP(e_##_name, (_event) | (0 << 2)), \ 4158c2ecf20Sopenharmony_ci __CMN_EVENT_XP(w_##_name, (_event) | (1 << 2)), \ 4168c2ecf20Sopenharmony_ci __CMN_EVENT_XP(n_##_name, (_event) | (2 << 2)), \ 4178c2ecf20Sopenharmony_ci __CMN_EVENT_XP(s_##_name, (_event) | (3 << 2)), \ 4188c2ecf20Sopenharmony_ci __CMN_EVENT_XP(p0_##_name, (_event) | (4 << 2)), \ 4198c2ecf20Sopenharmony_ci __CMN_EVENT_XP(p1_##_name, (_event) | (5 << 2)) 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/* Good thing there are only 3 fundamental XP events... */ 4228c2ecf20Sopenharmony_ci#define CMN_EVENT_XP(_name, _event) \ 4238c2ecf20Sopenharmony_ci _CMN_EVENT_XP(req_##_name, (_event) | (0 << 5)), \ 4248c2ecf20Sopenharmony_ci _CMN_EVENT_XP(rsp_##_name, (_event) | (1 << 5)), \ 4258c2ecf20Sopenharmony_ci _CMN_EVENT_XP(snp_##_name, (_event) | (2 << 5)), \ 4268c2ecf20Sopenharmony_ci _CMN_EVENT_XP(dat_##_name, (_event) | (3 << 5)) 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic struct attribute *arm_cmn_event_attrs[] = { 4308c2ecf20Sopenharmony_ci CMN_EVENT_DTC(cycles), 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* 4338c2ecf20Sopenharmony_ci * DVM node events conflict with HN-I events in the equivalent PMU 4348c2ecf20Sopenharmony_ci * slot, but our lazy short-cut of using the DTM counter index for 4358c2ecf20Sopenharmony_ci * the PMU index as well happens to avoid that by construction. 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_ci CMN_EVENT_DVM(rxreq_dvmop, 0x01), 4388c2ecf20Sopenharmony_ci CMN_EVENT_DVM(rxreq_dvmsync, 0x02), 4398c2ecf20Sopenharmony_ci CMN_EVENT_DVM(rxreq_dvmop_vmid_filtered, 0x03), 4408c2ecf20Sopenharmony_ci CMN_EVENT_DVM(rxreq_retried, 0x04), 4418c2ecf20Sopenharmony_ci _CMN_EVENT_DVM(rxreq_trk_occupancy_all, 0x05, 0), 4428c2ecf20Sopenharmony_ci _CMN_EVENT_DVM(rxreq_trk_occupancy_dvmop, 0x05, 1), 4438c2ecf20Sopenharmony_ci _CMN_EVENT_DVM(rxreq_trk_occupancy_dvmsync, 0x05, 2), 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci CMN_EVENT_HNF(cache_miss, 0x01), 4468c2ecf20Sopenharmony_ci CMN_EVENT_HNF(slc_sf_cache_access, 0x02), 4478c2ecf20Sopenharmony_ci CMN_EVENT_HNF(cache_fill, 0x03), 4488c2ecf20Sopenharmony_ci CMN_EVENT_HNF(pocq_retry, 0x04), 4498c2ecf20Sopenharmony_ci CMN_EVENT_HNF(pocq_reqs_recvd, 0x05), 4508c2ecf20Sopenharmony_ci CMN_EVENT_HNF(sf_hit, 0x06), 4518c2ecf20Sopenharmony_ci CMN_EVENT_HNF(sf_evictions, 0x07), 4528c2ecf20Sopenharmony_ci CMN_EVENT_HNF(dir_snoops_sent, 0x08), 4538c2ecf20Sopenharmony_ci CMN_EVENT_HNF(brd_snoops_sent, 0x09), 4548c2ecf20Sopenharmony_ci CMN_EVENT_HNF(slc_eviction, 0x0a), 4558c2ecf20Sopenharmony_ci CMN_EVENT_HNF(slc_fill_invalid_way, 0x0b), 4568c2ecf20Sopenharmony_ci CMN_EVENT_HNF(mc_retries, 0x0c), 4578c2ecf20Sopenharmony_ci CMN_EVENT_HNF(mc_reqs, 0x0d), 4588c2ecf20Sopenharmony_ci CMN_EVENT_HNF(qos_hh_retry, 0x0e), 4598c2ecf20Sopenharmony_ci _CMN_EVENT_HNF(qos_pocq_occupancy_all, 0x0f, 0), 4608c2ecf20Sopenharmony_ci _CMN_EVENT_HNF(qos_pocq_occupancy_read, 0x0f, 1), 4618c2ecf20Sopenharmony_ci _CMN_EVENT_HNF(qos_pocq_occupancy_write, 0x0f, 2), 4628c2ecf20Sopenharmony_ci _CMN_EVENT_HNF(qos_pocq_occupancy_atomic, 0x0f, 3), 4638c2ecf20Sopenharmony_ci _CMN_EVENT_HNF(qos_pocq_occupancy_stash, 0x0f, 4), 4648c2ecf20Sopenharmony_ci CMN_EVENT_HNF(pocq_addrhaz, 0x10), 4658c2ecf20Sopenharmony_ci CMN_EVENT_HNF(pocq_atomic_addrhaz, 0x11), 4668c2ecf20Sopenharmony_ci CMN_EVENT_HNF(ld_st_swp_adq_full, 0x12), 4678c2ecf20Sopenharmony_ci CMN_EVENT_HNF(cmp_adq_full, 0x13), 4688c2ecf20Sopenharmony_ci CMN_EVENT_HNF(txdat_stall, 0x14), 4698c2ecf20Sopenharmony_ci CMN_EVENT_HNF(txrsp_stall, 0x15), 4708c2ecf20Sopenharmony_ci CMN_EVENT_HNF(seq_full, 0x16), 4718c2ecf20Sopenharmony_ci CMN_EVENT_HNF(seq_hit, 0x17), 4728c2ecf20Sopenharmony_ci CMN_EVENT_HNF(snp_sent, 0x18), 4738c2ecf20Sopenharmony_ci CMN_EVENT_HNF(sfbi_dir_snp_sent, 0x19), 4748c2ecf20Sopenharmony_ci CMN_EVENT_HNF(sfbi_brd_snp_sent, 0x1a), 4758c2ecf20Sopenharmony_ci CMN_EVENT_HNF(snp_sent_untrk, 0x1b), 4768c2ecf20Sopenharmony_ci CMN_EVENT_HNF(intv_dirty, 0x1c), 4778c2ecf20Sopenharmony_ci CMN_EVENT_HNF(stash_snp_sent, 0x1d), 4788c2ecf20Sopenharmony_ci CMN_EVENT_HNF(stash_data_pull, 0x1e), 4798c2ecf20Sopenharmony_ci CMN_EVENT_HNF(snp_fwded, 0x1f), 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci CMN_EVENT_HNI(rrt_rd_occ_cnt_ovfl, 0x20), 4828c2ecf20Sopenharmony_ci CMN_EVENT_HNI(rrt_wr_occ_cnt_ovfl, 0x21), 4838c2ecf20Sopenharmony_ci CMN_EVENT_HNI(rdt_rd_occ_cnt_ovfl, 0x22), 4848c2ecf20Sopenharmony_ci CMN_EVENT_HNI(rdt_wr_occ_cnt_ovfl, 0x23), 4858c2ecf20Sopenharmony_ci CMN_EVENT_HNI(wdb_occ_cnt_ovfl, 0x24), 4868c2ecf20Sopenharmony_ci CMN_EVENT_HNI(rrt_rd_alloc, 0x25), 4878c2ecf20Sopenharmony_ci CMN_EVENT_HNI(rrt_wr_alloc, 0x26), 4888c2ecf20Sopenharmony_ci CMN_EVENT_HNI(rdt_rd_alloc, 0x27), 4898c2ecf20Sopenharmony_ci CMN_EVENT_HNI(rdt_wr_alloc, 0x28), 4908c2ecf20Sopenharmony_ci CMN_EVENT_HNI(wdb_alloc, 0x29), 4918c2ecf20Sopenharmony_ci CMN_EVENT_HNI(txrsp_retryack, 0x2a), 4928c2ecf20Sopenharmony_ci CMN_EVENT_HNI(arvalid_no_arready, 0x2b), 4938c2ecf20Sopenharmony_ci CMN_EVENT_HNI(arready_no_arvalid, 0x2c), 4948c2ecf20Sopenharmony_ci CMN_EVENT_HNI(awvalid_no_awready, 0x2d), 4958c2ecf20Sopenharmony_ci CMN_EVENT_HNI(awready_no_awvalid, 0x2e), 4968c2ecf20Sopenharmony_ci CMN_EVENT_HNI(wvalid_no_wready, 0x2f), 4978c2ecf20Sopenharmony_ci CMN_EVENT_HNI(txdat_stall, 0x30), 4988c2ecf20Sopenharmony_ci CMN_EVENT_HNI(nonpcie_serialization, 0x31), 4998c2ecf20Sopenharmony_ci CMN_EVENT_HNI(pcie_serialization, 0x32), 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci CMN_EVENT_XP(txflit_valid, 0x01), 5028c2ecf20Sopenharmony_ci CMN_EVENT_XP(txflit_stall, 0x02), 5038c2ecf20Sopenharmony_ci CMN_EVENT_XP(partial_dat_flit, 0x03), 5048c2ecf20Sopenharmony_ci /* We treat watchpoints as a special made-up class of XP events */ 5058c2ecf20Sopenharmony_ci CMN_EVENT_ATTR(watchpoint_up, CMN_TYPE_WP, 0, 0), 5068c2ecf20Sopenharmony_ci CMN_EVENT_ATTR(watchpoint_down, CMN_TYPE_WP, 2, 0), 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(rd_req, 0x01), 5098c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(wr_req, 0x02), 5108c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(cmo_req, 0x03), 5118c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(txrsp_retryack, 0x04), 5128c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(txdat_flitv, 0x05), 5138c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(txrsp_flitv, 0x06), 5148c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(rd_req_trkr_occ_cnt_ovfl, 0x11), 5158c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(wr_req_trkr_occ_cnt_ovfl, 0x12), 5168c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(cmo_req_trkr_occ_cnt_ovfl, 0x13), 5178c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(wdb_occ_cnt_ovfl, 0x14), 5188c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(rd_axi_trkr_occ_cnt_ovfl, 0x15), 5198c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(cmo_axi_trkr_occ_cnt_ovfl, 0x16), 5208c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(arvalid_no_arready, 0x21), 5218c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(awvalid_no_awready, 0x22), 5228c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(wvalid_no_wready, 0x23), 5238c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(txdat_stall, 0x24), 5248c2ecf20Sopenharmony_ci CMN_EVENT_SBSX(txrsp_stall, 0x25), 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci CMN_EVENT_RNID(s0_rdata_beats, 0x01), 5278c2ecf20Sopenharmony_ci CMN_EVENT_RNID(s1_rdata_beats, 0x02), 5288c2ecf20Sopenharmony_ci CMN_EVENT_RNID(s2_rdata_beats, 0x03), 5298c2ecf20Sopenharmony_ci CMN_EVENT_RNID(rxdat_flits, 0x04), 5308c2ecf20Sopenharmony_ci CMN_EVENT_RNID(txdat_flits, 0x05), 5318c2ecf20Sopenharmony_ci CMN_EVENT_RNID(txreq_flits_total, 0x06), 5328c2ecf20Sopenharmony_ci CMN_EVENT_RNID(txreq_flits_retried, 0x07), 5338c2ecf20Sopenharmony_ci CMN_EVENT_RNID(rrt_occ_ovfl, 0x08), 5348c2ecf20Sopenharmony_ci CMN_EVENT_RNID(wrt_occ_ovfl, 0x09), 5358c2ecf20Sopenharmony_ci CMN_EVENT_RNID(txreq_flits_replayed, 0x0a), 5368c2ecf20Sopenharmony_ci CMN_EVENT_RNID(wrcancel_sent, 0x0b), 5378c2ecf20Sopenharmony_ci CMN_EVENT_RNID(s0_wdata_beats, 0x0c), 5388c2ecf20Sopenharmony_ci CMN_EVENT_RNID(s1_wdata_beats, 0x0d), 5398c2ecf20Sopenharmony_ci CMN_EVENT_RNID(s2_wdata_beats, 0x0e), 5408c2ecf20Sopenharmony_ci CMN_EVENT_RNID(rrt_alloc, 0x0f), 5418c2ecf20Sopenharmony_ci CMN_EVENT_RNID(wrt_alloc, 0x10), 5428c2ecf20Sopenharmony_ci CMN_EVENT_RNID(rdb_unord, 0x11), 5438c2ecf20Sopenharmony_ci CMN_EVENT_RNID(rdb_replay, 0x12), 5448c2ecf20Sopenharmony_ci CMN_EVENT_RNID(rdb_hybrid, 0x13), 5458c2ecf20Sopenharmony_ci CMN_EVENT_RNID(rdb_ord, 0x14), 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci NULL 5488c2ecf20Sopenharmony_ci}; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic const struct attribute_group arm_cmn_event_attrs_group = { 5518c2ecf20Sopenharmony_ci .name = "events", 5528c2ecf20Sopenharmony_ci .attrs = arm_cmn_event_attrs, 5538c2ecf20Sopenharmony_ci .is_visible = arm_cmn_event_attr_is_visible, 5548c2ecf20Sopenharmony_ci}; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic ssize_t arm_cmn_format_show(struct device *dev, 5578c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct arm_cmn_format_attr *fmt = container_of(attr, typeof(*fmt), attr); 5608c2ecf20Sopenharmony_ci int lo = __ffs(fmt->field), hi = __fls(fmt->field); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (lo == hi) 5638c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "config:%d\n", lo); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (!fmt->config) 5668c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "config:%d-%d\n", lo, hi); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "config%d:%d-%d\n", fmt->config, lo, hi); 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci#define _CMN_FORMAT_ATTR(_name, _cfg, _fld) \ 5728c2ecf20Sopenharmony_ci (&((struct arm_cmn_format_attr[]) {{ \ 5738c2ecf20Sopenharmony_ci .attr = __ATTR(_name, 0444, arm_cmn_format_show, NULL), \ 5748c2ecf20Sopenharmony_ci .config = _cfg, \ 5758c2ecf20Sopenharmony_ci .field = _fld, \ 5768c2ecf20Sopenharmony_ci }})[0].attr.attr) 5778c2ecf20Sopenharmony_ci#define CMN_FORMAT_ATTR(_name, _fld) _CMN_FORMAT_ATTR(_name, 0, _fld) 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic struct attribute *arm_cmn_format_attrs[] = { 5808c2ecf20Sopenharmony_ci CMN_FORMAT_ATTR(type, CMN_CONFIG_TYPE), 5818c2ecf20Sopenharmony_ci CMN_FORMAT_ATTR(eventid, CMN_CONFIG_EVENTID), 5828c2ecf20Sopenharmony_ci CMN_FORMAT_ATTR(occupid, CMN_CONFIG_OCCUPID), 5838c2ecf20Sopenharmony_ci CMN_FORMAT_ATTR(bynodeid, CMN_CONFIG_BYNODEID), 5848c2ecf20Sopenharmony_ci CMN_FORMAT_ATTR(nodeid, CMN_CONFIG_NODEID), 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci CMN_FORMAT_ATTR(wp_dev_sel, CMN_CONFIG_WP_DEV_SEL), 5878c2ecf20Sopenharmony_ci CMN_FORMAT_ATTR(wp_chn_sel, CMN_CONFIG_WP_CHN_SEL), 5888c2ecf20Sopenharmony_ci CMN_FORMAT_ATTR(wp_grp, CMN_CONFIG_WP_GRP), 5898c2ecf20Sopenharmony_ci CMN_FORMAT_ATTR(wp_exclusive, CMN_CONFIG_WP_EXCLUSIVE), 5908c2ecf20Sopenharmony_ci CMN_FORMAT_ATTR(wp_combine, CMN_CONFIG_WP_COMBINE), 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci _CMN_FORMAT_ATTR(wp_val, 1, CMN_CONFIG1_WP_VAL), 5938c2ecf20Sopenharmony_ci _CMN_FORMAT_ATTR(wp_mask, 2, CMN_CONFIG2_WP_MASK), 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci NULL 5968c2ecf20Sopenharmony_ci}; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic const struct attribute_group arm_cmn_format_attrs_group = { 5998c2ecf20Sopenharmony_ci .name = "format", 6008c2ecf20Sopenharmony_ci .attrs = arm_cmn_format_attrs, 6018c2ecf20Sopenharmony_ci}; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic ssize_t arm_cmn_cpumask_show(struct device *dev, 6048c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct arm_cmn *cmn = to_cmn(dev_get_drvdata(dev)); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, cpumask_of(cmn->cpu)); 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic struct device_attribute arm_cmn_cpumask_attr = 6128c2ecf20Sopenharmony_ci __ATTR(cpumask, 0444, arm_cmn_cpumask_show, NULL); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic struct attribute *arm_cmn_cpumask_attrs[] = { 6158c2ecf20Sopenharmony_ci &arm_cmn_cpumask_attr.attr, 6168c2ecf20Sopenharmony_ci NULL, 6178c2ecf20Sopenharmony_ci}; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic struct attribute_group arm_cmn_cpumask_attr_group = { 6208c2ecf20Sopenharmony_ci .attrs = arm_cmn_cpumask_attrs, 6218c2ecf20Sopenharmony_ci}; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic const struct attribute_group *arm_cmn_attr_groups[] = { 6248c2ecf20Sopenharmony_ci &arm_cmn_event_attrs_group, 6258c2ecf20Sopenharmony_ci &arm_cmn_format_attrs_group, 6268c2ecf20Sopenharmony_ci &arm_cmn_cpumask_attr_group, 6278c2ecf20Sopenharmony_ci NULL 6288c2ecf20Sopenharmony_ci}; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int arm_cmn_wp_idx(struct perf_event *event) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci return CMN_EVENT_EVENTID(event) + CMN_EVENT_WP_GRP(event); 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_cistatic u32 arm_cmn_wp_config(struct perf_event *event) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci u32 config; 6388c2ecf20Sopenharmony_ci u32 dev = CMN_EVENT_WP_DEV_SEL(event); 6398c2ecf20Sopenharmony_ci u32 chn = CMN_EVENT_WP_CHN_SEL(event); 6408c2ecf20Sopenharmony_ci u32 grp = CMN_EVENT_WP_GRP(event); 6418c2ecf20Sopenharmony_ci u32 exc = CMN_EVENT_WP_EXCLUSIVE(event); 6428c2ecf20Sopenharmony_ci u32 combine = CMN_EVENT_WP_COMBINE(event); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci config = FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_DEV_SEL, dev) | 6458c2ecf20Sopenharmony_ci FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_CHN_SEL, chn) | 6468c2ecf20Sopenharmony_ci FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_GRP, grp) | 6478c2ecf20Sopenharmony_ci FIELD_PREP(CMN_DTM_WPn_CONFIG_WP_EXCLUSIVE, exc); 6488c2ecf20Sopenharmony_ci if (combine && !grp) 6498c2ecf20Sopenharmony_ci config |= CMN_DTM_WPn_CONFIG_WP_COMBINE; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return config; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic void arm_cmn_set_state(struct arm_cmn *cmn, u32 state) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci if (!cmn->state) 6578c2ecf20Sopenharmony_ci writel_relaxed(0, cmn->dtc[0].base + CMN_DT_PMCR); 6588c2ecf20Sopenharmony_ci cmn->state |= state; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic void arm_cmn_clear_state(struct arm_cmn *cmn, u32 state) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci cmn->state &= ~state; 6648c2ecf20Sopenharmony_ci if (!cmn->state) 6658c2ecf20Sopenharmony_ci writel_relaxed(CMN_DT_PMCR_PMU_EN | CMN_DT_PMCR_OVFL_INTR_EN, 6668c2ecf20Sopenharmony_ci cmn->dtc[0].base + CMN_DT_PMCR); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic void arm_cmn_pmu_enable(struct pmu *pmu) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci arm_cmn_clear_state(to_cmn(pmu), CMN_STATE_DISABLED); 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic void arm_cmn_pmu_disable(struct pmu *pmu) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci arm_cmn_set_state(to_cmn(pmu), CMN_STATE_DISABLED); 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic u64 arm_cmn_read_dtm(struct arm_cmn *cmn, struct arm_cmn_hw_event *hw, 6808c2ecf20Sopenharmony_ci bool snapshot) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci struct arm_cmn_node *dn; 6838c2ecf20Sopenharmony_ci unsigned int i, offset; 6848c2ecf20Sopenharmony_ci u64 count = 0; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci offset = snapshot ? CMN_DTM_PMEVCNTSR : CMN_DTM_PMEVCNT; 6878c2ecf20Sopenharmony_ci for_each_hw_dn(hw, dn, i) { 6888c2ecf20Sopenharmony_ci struct arm_cmn_node *xp = arm_cmn_node_to_xp(dn); 6898c2ecf20Sopenharmony_ci int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i); 6908c2ecf20Sopenharmony_ci u64 reg = readq_relaxed(xp->pmu_base + offset); 6918c2ecf20Sopenharmony_ci u16 dtm_count = reg >> (dtm_idx * 16); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci count += dtm_count; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci return count; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic u64 arm_cmn_read_cc(struct arm_cmn_dtc *dtc) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci u64 val = readq_relaxed(dtc->base + CMN_DT_PMCCNTR); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci writeq_relaxed(CMN_CC_INIT, dtc->base + CMN_DT_PMCCNTR); 7038c2ecf20Sopenharmony_ci return (val - CMN_CC_INIT) & ((CMN_CC_INIT << 1) - 1); 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic u32 arm_cmn_read_counter(struct arm_cmn_dtc *dtc, int idx) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci u32 val, pmevcnt = CMN_DT_PMEVCNT(idx); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci val = readl_relaxed(dtc->base + pmevcnt); 7118c2ecf20Sopenharmony_ci writel_relaxed(CMN_COUNTER_INIT, dtc->base + pmevcnt); 7128c2ecf20Sopenharmony_ci return val - CMN_COUNTER_INIT; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic void arm_cmn_init_counter(struct perf_event *event) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci struct arm_cmn *cmn = to_cmn(event->pmu); 7188c2ecf20Sopenharmony_ci struct arm_cmn_hw_event *hw = to_cmn_hw(event); 7198c2ecf20Sopenharmony_ci unsigned int i, pmevcnt = CMN_DT_PMEVCNT(hw->dtc_idx); 7208c2ecf20Sopenharmony_ci u64 count; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci for (i = 0; hw->dtcs_used & (1U << i); i++) { 7238c2ecf20Sopenharmony_ci writel_relaxed(CMN_COUNTER_INIT, cmn->dtc[i].base + pmevcnt); 7248c2ecf20Sopenharmony_ci cmn->dtc[i].counters[hw->dtc_idx] = event; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci count = arm_cmn_read_dtm(cmn, hw, false); 7288c2ecf20Sopenharmony_ci local64_set(&event->hw.prev_count, count); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic void arm_cmn_event_read(struct perf_event *event) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct arm_cmn *cmn = to_cmn(event->pmu); 7348c2ecf20Sopenharmony_ci struct arm_cmn_hw_event *hw = to_cmn_hw(event); 7358c2ecf20Sopenharmony_ci u64 delta, new, prev; 7368c2ecf20Sopenharmony_ci unsigned long flags; 7378c2ecf20Sopenharmony_ci unsigned int i; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (hw->dtc_idx == CMN_DT_NUM_COUNTERS) { 7408c2ecf20Sopenharmony_ci i = __ffs(hw->dtcs_used); 7418c2ecf20Sopenharmony_ci delta = arm_cmn_read_cc(cmn->dtc + i); 7428c2ecf20Sopenharmony_ci local64_add(delta, &event->count); 7438c2ecf20Sopenharmony_ci return; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci new = arm_cmn_read_dtm(cmn, hw, false); 7468c2ecf20Sopenharmony_ci prev = local64_xchg(&event->hw.prev_count, new); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci delta = new - prev; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci local_irq_save(flags); 7518c2ecf20Sopenharmony_ci for (i = 0; hw->dtcs_used & (1U << i); i++) { 7528c2ecf20Sopenharmony_ci new = arm_cmn_read_counter(cmn->dtc + i, hw->dtc_idx); 7538c2ecf20Sopenharmony_ci delta += new << 16; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci local_irq_restore(flags); 7568c2ecf20Sopenharmony_ci local64_add(delta, &event->count); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic void arm_cmn_event_start(struct perf_event *event, int flags) 7608c2ecf20Sopenharmony_ci{ 7618c2ecf20Sopenharmony_ci struct arm_cmn *cmn = to_cmn(event->pmu); 7628c2ecf20Sopenharmony_ci struct arm_cmn_hw_event *hw = to_cmn_hw(event); 7638c2ecf20Sopenharmony_ci struct arm_cmn_node *dn; 7648c2ecf20Sopenharmony_ci enum cmn_node_type type = CMN_EVENT_TYPE(event); 7658c2ecf20Sopenharmony_ci int i; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (type == CMN_TYPE_DTC) { 7688c2ecf20Sopenharmony_ci i = __ffs(hw->dtcs_used); 7698c2ecf20Sopenharmony_ci writeq_relaxed(CMN_CC_INIT, cmn->dtc[i].base + CMN_DT_PMCCNTR); 7708c2ecf20Sopenharmony_ci cmn->dtc[i].cc_active = true; 7718c2ecf20Sopenharmony_ci } else if (type == CMN_TYPE_WP) { 7728c2ecf20Sopenharmony_ci int wp_idx = arm_cmn_wp_idx(event); 7738c2ecf20Sopenharmony_ci u64 val = CMN_EVENT_WP_VAL(event); 7748c2ecf20Sopenharmony_ci u64 mask = CMN_EVENT_WP_MASK(event); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci for_each_hw_dn(hw, dn, i) { 7778c2ecf20Sopenharmony_ci writeq_relaxed(val, dn->pmu_base + CMN_DTM_WPn_VAL(wp_idx)); 7788c2ecf20Sopenharmony_ci writeq_relaxed(mask, dn->pmu_base + CMN_DTM_WPn_MASK(wp_idx)); 7798c2ecf20Sopenharmony_ci } 7808c2ecf20Sopenharmony_ci } else for_each_hw_dn(hw, dn, i) { 7818c2ecf20Sopenharmony_ci int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci dn->event[dtm_idx] = CMN_EVENT_EVENTID(event); 7848c2ecf20Sopenharmony_ci writel_relaxed(le32_to_cpu(dn->event_sel), dn->pmu_base + CMN_PMU_EVENT_SEL); 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic void arm_cmn_event_stop(struct perf_event *event, int flags) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct arm_cmn *cmn = to_cmn(event->pmu); 7918c2ecf20Sopenharmony_ci struct arm_cmn_hw_event *hw = to_cmn_hw(event); 7928c2ecf20Sopenharmony_ci struct arm_cmn_node *dn; 7938c2ecf20Sopenharmony_ci enum cmn_node_type type = CMN_EVENT_TYPE(event); 7948c2ecf20Sopenharmony_ci int i; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (type == CMN_TYPE_DTC) { 7978c2ecf20Sopenharmony_ci i = __ffs(hw->dtcs_used); 7988c2ecf20Sopenharmony_ci cmn->dtc[i].cc_active = false; 7998c2ecf20Sopenharmony_ci } else if (type == CMN_TYPE_WP) { 8008c2ecf20Sopenharmony_ci int wp_idx = arm_cmn_wp_idx(event); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci for_each_hw_dn(hw, dn, i) { 8038c2ecf20Sopenharmony_ci writeq_relaxed(0, dn->pmu_base + CMN_DTM_WPn_MASK(wp_idx)); 8048c2ecf20Sopenharmony_ci writeq_relaxed(~0ULL, dn->pmu_base + CMN_DTM_WPn_VAL(wp_idx)); 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci } else for_each_hw_dn(hw, dn, i) { 8078c2ecf20Sopenharmony_ci int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci dn->event[dtm_idx] = 0; 8108c2ecf20Sopenharmony_ci writel_relaxed(le32_to_cpu(dn->event_sel), dn->pmu_base + CMN_PMU_EVENT_SEL); 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci arm_cmn_event_read(event); 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistruct arm_cmn_val { 8178c2ecf20Sopenharmony_ci u8 dtm_count[CMN_MAX_XPS]; 8188c2ecf20Sopenharmony_ci u8 occupid[CMN_MAX_XPS]; 8198c2ecf20Sopenharmony_ci u8 wp[CMN_MAX_XPS][4]; 8208c2ecf20Sopenharmony_ci int dtc_count; 8218c2ecf20Sopenharmony_ci bool cycles; 8228c2ecf20Sopenharmony_ci}; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic void arm_cmn_val_add_event(struct arm_cmn_val *val, struct perf_event *event) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci struct arm_cmn_hw_event *hw = to_cmn_hw(event); 8278c2ecf20Sopenharmony_ci struct arm_cmn_node *dn; 8288c2ecf20Sopenharmony_ci enum cmn_node_type type; 8298c2ecf20Sopenharmony_ci int i; 8308c2ecf20Sopenharmony_ci u8 occupid; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci if (is_software_event(event)) 8338c2ecf20Sopenharmony_ci return; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci type = CMN_EVENT_TYPE(event); 8368c2ecf20Sopenharmony_ci if (type == CMN_TYPE_DTC) { 8378c2ecf20Sopenharmony_ci val->cycles = true; 8388c2ecf20Sopenharmony_ci return; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci val->dtc_count++; 8428c2ecf20Sopenharmony_ci if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event))) 8438c2ecf20Sopenharmony_ci occupid = CMN_EVENT_OCCUPID(event) + 1; 8448c2ecf20Sopenharmony_ci else 8458c2ecf20Sopenharmony_ci occupid = 0; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci for_each_hw_dn(hw, dn, i) { 8488c2ecf20Sopenharmony_ci int wp_idx, xp = arm_cmn_node_to_xp(dn)->logid; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci val->dtm_count[xp]++; 8518c2ecf20Sopenharmony_ci val->occupid[xp] = occupid; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci if (type != CMN_TYPE_WP) 8548c2ecf20Sopenharmony_ci continue; 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci wp_idx = arm_cmn_wp_idx(event); 8578c2ecf20Sopenharmony_ci val->wp[xp][wp_idx] = CMN_EVENT_WP_COMBINE(event) + 1; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_cistatic int arm_cmn_validate_group(struct perf_event *event) 8628c2ecf20Sopenharmony_ci{ 8638c2ecf20Sopenharmony_ci struct arm_cmn_hw_event *hw = to_cmn_hw(event); 8648c2ecf20Sopenharmony_ci struct arm_cmn_node *dn; 8658c2ecf20Sopenharmony_ci struct perf_event *sibling, *leader = event->group_leader; 8668c2ecf20Sopenharmony_ci enum cmn_node_type type; 8678c2ecf20Sopenharmony_ci struct arm_cmn_val val; 8688c2ecf20Sopenharmony_ci int i; 8698c2ecf20Sopenharmony_ci u8 occupid; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (leader == event) 8728c2ecf20Sopenharmony_ci return 0; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci if (event->pmu != leader->pmu && !is_software_event(leader)) 8758c2ecf20Sopenharmony_ci return -EINVAL; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci memset(&val, 0, sizeof(val)); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci arm_cmn_val_add_event(&val, leader); 8808c2ecf20Sopenharmony_ci for_each_sibling_event(sibling, leader) 8818c2ecf20Sopenharmony_ci arm_cmn_val_add_event(&val, sibling); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci type = CMN_EVENT_TYPE(event); 8848c2ecf20Sopenharmony_ci if (type == CMN_TYPE_DTC) 8858c2ecf20Sopenharmony_ci return val.cycles ? -EINVAL : 0; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (val.dtc_count == CMN_DT_NUM_COUNTERS) 8888c2ecf20Sopenharmony_ci return -EINVAL; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event))) 8918c2ecf20Sopenharmony_ci occupid = CMN_EVENT_OCCUPID(event) + 1; 8928c2ecf20Sopenharmony_ci else 8938c2ecf20Sopenharmony_ci occupid = 0; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci for_each_hw_dn(hw, dn, i) { 8968c2ecf20Sopenharmony_ci int wp_idx, wp_cmb, xp = arm_cmn_node_to_xp(dn)->logid; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci if (val.dtm_count[xp] == CMN_DTM_NUM_COUNTERS) 8998c2ecf20Sopenharmony_ci return -EINVAL; 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci if (occupid && val.occupid[xp] && occupid != val.occupid[xp]) 9028c2ecf20Sopenharmony_ci return -EINVAL; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci if (type != CMN_TYPE_WP) 9058c2ecf20Sopenharmony_ci continue; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci wp_idx = arm_cmn_wp_idx(event); 9088c2ecf20Sopenharmony_ci if (val.wp[xp][wp_idx]) 9098c2ecf20Sopenharmony_ci return -EINVAL; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci wp_cmb = val.wp[xp][wp_idx ^ 1]; 9128c2ecf20Sopenharmony_ci if (wp_cmb && wp_cmb != CMN_EVENT_WP_COMBINE(event) + 1) 9138c2ecf20Sopenharmony_ci return -EINVAL; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci return 0; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic int arm_cmn_event_init(struct perf_event *event) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct arm_cmn *cmn = to_cmn(event->pmu); 9228c2ecf20Sopenharmony_ci struct arm_cmn_hw_event *hw = to_cmn_hw(event); 9238c2ecf20Sopenharmony_ci enum cmn_node_type type; 9248c2ecf20Sopenharmony_ci unsigned int i; 9258c2ecf20Sopenharmony_ci bool bynodeid; 9268c2ecf20Sopenharmony_ci u16 nodeid, eventid; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (event->attr.type != event->pmu->type) 9298c2ecf20Sopenharmony_ci return -ENOENT; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) 9328c2ecf20Sopenharmony_ci return -EINVAL; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci event->cpu = cmn->cpu; 9358c2ecf20Sopenharmony_ci if (event->cpu < 0) 9368c2ecf20Sopenharmony_ci return -EINVAL; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci type = CMN_EVENT_TYPE(event); 9398c2ecf20Sopenharmony_ci /* DTC events (i.e. cycles) already have everything they need */ 9408c2ecf20Sopenharmony_ci if (type == CMN_TYPE_DTC) 9418c2ecf20Sopenharmony_ci return 0; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* For watchpoints we need the actual XP node here */ 9448c2ecf20Sopenharmony_ci if (type == CMN_TYPE_WP) { 9458c2ecf20Sopenharmony_ci type = CMN_TYPE_XP; 9468c2ecf20Sopenharmony_ci /* ...and we need a "real" direction */ 9478c2ecf20Sopenharmony_ci eventid = CMN_EVENT_EVENTID(event); 9488c2ecf20Sopenharmony_ci if (eventid != CMN_WP_UP && eventid != CMN_WP_DOWN) 9498c2ecf20Sopenharmony_ci return -EINVAL; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci bynodeid = CMN_EVENT_BYNODEID(event); 9538c2ecf20Sopenharmony_ci nodeid = CMN_EVENT_NODEID(event); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci hw->dn = arm_cmn_node(cmn, type); 9568c2ecf20Sopenharmony_ci for (i = hw->dn - cmn->dns; i < cmn->num_dns && cmn->dns[i].type == type; i++) { 9578c2ecf20Sopenharmony_ci if (!bynodeid) { 9588c2ecf20Sopenharmony_ci hw->num_dns++; 9598c2ecf20Sopenharmony_ci } else if (cmn->dns[i].id != nodeid) { 9608c2ecf20Sopenharmony_ci hw->dn++; 9618c2ecf20Sopenharmony_ci } else { 9628c2ecf20Sopenharmony_ci hw->num_dns = 1; 9638c2ecf20Sopenharmony_ci break; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if (!hw->num_dns) { 9688c2ecf20Sopenharmony_ci int bits = arm_cmn_xyidbits(cmn); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci dev_dbg(cmn->dev, "invalid node 0x%x (%d,%d,%d,%d) type 0x%x\n", 9718c2ecf20Sopenharmony_ci nodeid, CMN_NODEID_X(nodeid, bits), CMN_NODEID_Y(nodeid, bits), 9728c2ecf20Sopenharmony_ci CMN_NODEID_PID(nodeid), CMN_NODEID_DEVID(nodeid), type); 9738c2ecf20Sopenharmony_ci return -EINVAL; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci /* 9768c2ecf20Sopenharmony_ci * By assuming events count in all DTC domains, we cunningly avoid 9778c2ecf20Sopenharmony_ci * needing to know anything about how XPs are assigned to domains. 9788c2ecf20Sopenharmony_ci */ 9798c2ecf20Sopenharmony_ci hw->dtcs_used = (1U << cmn->num_dtcs) - 1; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci return arm_cmn_validate_group(event); 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic void arm_cmn_event_clear(struct arm_cmn *cmn, struct perf_event *event, 9858c2ecf20Sopenharmony_ci int i) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct arm_cmn_hw_event *hw = to_cmn_hw(event); 9888c2ecf20Sopenharmony_ci enum cmn_node_type type = CMN_EVENT_TYPE(event); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci while (i--) { 9918c2ecf20Sopenharmony_ci struct arm_cmn_node *xp = arm_cmn_node_to_xp(hw->dn + i); 9928c2ecf20Sopenharmony_ci unsigned int dtm_idx = arm_cmn_get_index(hw->dtm_idx, i); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (type == CMN_TYPE_WP) 9958c2ecf20Sopenharmony_ci hw->dn[i].wp_event[arm_cmn_wp_idx(event)] = -1; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event))) 9988c2ecf20Sopenharmony_ci hw->dn[i].occupid_count--; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci xp->pmu_config_low &= ~CMN__PMEVCNT_PAIRED(dtm_idx); 10018c2ecf20Sopenharmony_ci writel_relaxed(xp->pmu_config_low, xp->pmu_base + CMN_DTM_PMU_CONFIG); 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci memset(hw->dtm_idx, 0, sizeof(hw->dtm_idx)); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci for (i = 0; hw->dtcs_used & (1U << i); i++) 10068c2ecf20Sopenharmony_ci cmn->dtc[i].counters[hw->dtc_idx] = NULL; 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic int arm_cmn_event_add(struct perf_event *event, int flags) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci struct arm_cmn *cmn = to_cmn(event->pmu); 10128c2ecf20Sopenharmony_ci struct arm_cmn_hw_event *hw = to_cmn_hw(event); 10138c2ecf20Sopenharmony_ci struct arm_cmn_dtc *dtc = &cmn->dtc[0]; 10148c2ecf20Sopenharmony_ci struct arm_cmn_node *dn; 10158c2ecf20Sopenharmony_ci enum cmn_node_type type = CMN_EVENT_TYPE(event); 10168c2ecf20Sopenharmony_ci unsigned int i, dtc_idx, input_sel; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (type == CMN_TYPE_DTC) { 10198c2ecf20Sopenharmony_ci i = 0; 10208c2ecf20Sopenharmony_ci while (cmn->dtc[i].cycles) 10218c2ecf20Sopenharmony_ci if (++i == cmn->num_dtcs) 10228c2ecf20Sopenharmony_ci return -ENOSPC; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci cmn->dtc[i].cycles = event; 10258c2ecf20Sopenharmony_ci hw->dtc_idx = CMN_DT_NUM_COUNTERS; 10268c2ecf20Sopenharmony_ci hw->dtcs_used = 1U << i; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (flags & PERF_EF_START) 10298c2ecf20Sopenharmony_ci arm_cmn_event_start(event, 0); 10308c2ecf20Sopenharmony_ci return 0; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* Grab a free global counter first... */ 10348c2ecf20Sopenharmony_ci dtc_idx = 0; 10358c2ecf20Sopenharmony_ci while (dtc->counters[dtc_idx]) 10368c2ecf20Sopenharmony_ci if (++dtc_idx == CMN_DT_NUM_COUNTERS) 10378c2ecf20Sopenharmony_ci return -ENOSPC; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci hw->dtc_idx = dtc_idx; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci /* ...then the local counters to feed it. */ 10428c2ecf20Sopenharmony_ci for_each_hw_dn(hw, dn, i) { 10438c2ecf20Sopenharmony_ci struct arm_cmn_node *xp = arm_cmn_node_to_xp(dn); 10448c2ecf20Sopenharmony_ci unsigned int dtm_idx, shift; 10458c2ecf20Sopenharmony_ci u64 reg; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci dtm_idx = 0; 10488c2ecf20Sopenharmony_ci while (xp->pmu_config_low & CMN__PMEVCNT_PAIRED(dtm_idx)) 10498c2ecf20Sopenharmony_ci if (++dtm_idx == CMN_DTM_NUM_COUNTERS) 10508c2ecf20Sopenharmony_ci goto free_dtms; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (type == CMN_TYPE_XP) { 10538c2ecf20Sopenharmony_ci input_sel = CMN__PMEVCNT0_INPUT_SEL_XP + dtm_idx; 10548c2ecf20Sopenharmony_ci } else if (type == CMN_TYPE_WP) { 10558c2ecf20Sopenharmony_ci int tmp, wp_idx = arm_cmn_wp_idx(event); 10568c2ecf20Sopenharmony_ci u32 cfg = arm_cmn_wp_config(event); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (dn->wp_event[wp_idx] >= 0) 10598c2ecf20Sopenharmony_ci goto free_dtms; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci tmp = dn->wp_event[wp_idx ^ 1]; 10628c2ecf20Sopenharmony_ci if (tmp >= 0 && CMN_EVENT_WP_COMBINE(event) != 10638c2ecf20Sopenharmony_ci CMN_EVENT_WP_COMBINE(dtc->counters[tmp])) 10648c2ecf20Sopenharmony_ci goto free_dtms; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci input_sel = CMN__PMEVCNT0_INPUT_SEL_WP + wp_idx; 10678c2ecf20Sopenharmony_ci dn->wp_event[wp_idx] = dtc_idx; 10688c2ecf20Sopenharmony_ci writel_relaxed(cfg, dn->pmu_base + CMN_DTM_WPn_CONFIG(wp_idx)); 10698c2ecf20Sopenharmony_ci } else { 10708c2ecf20Sopenharmony_ci unsigned int port = CMN_NODEID_PID(dn->id); 10718c2ecf20Sopenharmony_ci unsigned int dev = CMN_NODEID_DEVID(dn->id); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci input_sel = CMN__PMEVCNT0_INPUT_SEL_DEV + dtm_idx + 10748c2ecf20Sopenharmony_ci (port << 4) + (dev << 2); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (arm_cmn_is_occup_event(type, CMN_EVENT_EVENTID(event))) { 10778c2ecf20Sopenharmony_ci int occupid = CMN_EVENT_OCCUPID(event); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (dn->occupid_count == 0) { 10808c2ecf20Sopenharmony_ci dn->occupid_val = occupid; 10818c2ecf20Sopenharmony_ci writel_relaxed(occupid, 10828c2ecf20Sopenharmony_ci dn->pmu_base + CMN_PMU_EVENT_SEL + 4); 10838c2ecf20Sopenharmony_ci } else if (dn->occupid_val != occupid) { 10848c2ecf20Sopenharmony_ci goto free_dtms; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci dn->occupid_count++; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci arm_cmn_set_index(hw->dtm_idx, i, dtm_idx); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci xp->input_sel[dtm_idx] = input_sel; 10938c2ecf20Sopenharmony_ci shift = CMN__PMEVCNTn_GLOBAL_NUM_SHIFT(dtm_idx); 10948c2ecf20Sopenharmony_ci xp->pmu_config_low &= ~(CMN__PMEVCNT0_GLOBAL_NUM << shift); 10958c2ecf20Sopenharmony_ci xp->pmu_config_low |= FIELD_PREP(CMN__PMEVCNT0_GLOBAL_NUM, dtc_idx) << shift; 10968c2ecf20Sopenharmony_ci xp->pmu_config_low |= CMN__PMEVCNT_PAIRED(dtm_idx); 10978c2ecf20Sopenharmony_ci reg = (u64)le32_to_cpu(xp->pmu_config_high) << 32 | xp->pmu_config_low; 10988c2ecf20Sopenharmony_ci writeq_relaxed(reg, xp->pmu_base + CMN_DTM_PMU_CONFIG); 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* Go go go! */ 11028c2ecf20Sopenharmony_ci arm_cmn_init_counter(event); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci if (flags & PERF_EF_START) 11058c2ecf20Sopenharmony_ci arm_cmn_event_start(event, 0); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci return 0; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_cifree_dtms: 11108c2ecf20Sopenharmony_ci arm_cmn_event_clear(cmn, event, i); 11118c2ecf20Sopenharmony_ci return -ENOSPC; 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic void arm_cmn_event_del(struct perf_event *event, int flags) 11158c2ecf20Sopenharmony_ci{ 11168c2ecf20Sopenharmony_ci struct arm_cmn *cmn = to_cmn(event->pmu); 11178c2ecf20Sopenharmony_ci struct arm_cmn_hw_event *hw = to_cmn_hw(event); 11188c2ecf20Sopenharmony_ci enum cmn_node_type type = CMN_EVENT_TYPE(event); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci arm_cmn_event_stop(event, PERF_EF_UPDATE); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci if (type == CMN_TYPE_DTC) 11238c2ecf20Sopenharmony_ci cmn->dtc[__ffs(hw->dtcs_used)].cycles = NULL; 11248c2ecf20Sopenharmony_ci else 11258c2ecf20Sopenharmony_ci arm_cmn_event_clear(cmn, event, hw->num_dns); 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci/* 11298c2ecf20Sopenharmony_ci * We stop the PMU for both add and read, to avoid skew across DTM counters. 11308c2ecf20Sopenharmony_ci * In theory we could use snapshots to read without stopping, but then it 11318c2ecf20Sopenharmony_ci * becomes a lot trickier to deal with overlow and racing against interrupts, 11328c2ecf20Sopenharmony_ci * plus it seems they don't work properly on some hardware anyway :( 11338c2ecf20Sopenharmony_ci */ 11348c2ecf20Sopenharmony_cistatic void arm_cmn_start_txn(struct pmu *pmu, unsigned int flags) 11358c2ecf20Sopenharmony_ci{ 11368c2ecf20Sopenharmony_ci arm_cmn_set_state(to_cmn(pmu), CMN_STATE_TXN); 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic void arm_cmn_end_txn(struct pmu *pmu) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci arm_cmn_clear_state(to_cmn(pmu), CMN_STATE_TXN); 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic int arm_cmn_commit_txn(struct pmu *pmu) 11458c2ecf20Sopenharmony_ci{ 11468c2ecf20Sopenharmony_ci arm_cmn_end_txn(pmu); 11478c2ecf20Sopenharmony_ci return 0; 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_cistatic int arm_cmn_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci struct arm_cmn *cmn; 11538c2ecf20Sopenharmony_ci unsigned int i, target; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci cmn = hlist_entry_safe(node, struct arm_cmn, cpuhp_node); 11568c2ecf20Sopenharmony_ci if (cpu != cmn->cpu) 11578c2ecf20Sopenharmony_ci return 0; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci target = cpumask_any_but(cpu_online_mask, cpu); 11608c2ecf20Sopenharmony_ci if (target >= nr_cpu_ids) 11618c2ecf20Sopenharmony_ci return 0; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci perf_pmu_migrate_context(&cmn->pmu, cpu, target); 11648c2ecf20Sopenharmony_ci for (i = 0; i < cmn->num_dtcs; i++) 11658c2ecf20Sopenharmony_ci irq_set_affinity_hint(cmn->dtc[i].irq, cpumask_of(target)); 11668c2ecf20Sopenharmony_ci cmn->cpu = target; 11678c2ecf20Sopenharmony_ci return 0; 11688c2ecf20Sopenharmony_ci} 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_cistatic irqreturn_t arm_cmn_handle_irq(int irq, void *dev_id) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci struct arm_cmn_dtc *dtc = dev_id; 11738c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci for (;;) { 11768c2ecf20Sopenharmony_ci u32 status = readl_relaxed(dtc->base + CMN_DT_PMOVSR); 11778c2ecf20Sopenharmony_ci u64 delta; 11788c2ecf20Sopenharmony_ci int i; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci for (i = 0; i < CMN_DT_NUM_COUNTERS; i++) { 11818c2ecf20Sopenharmony_ci if (status & (1U << i)) { 11828c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 11838c2ecf20Sopenharmony_ci if (WARN_ON(!dtc->counters[i])) 11848c2ecf20Sopenharmony_ci continue; 11858c2ecf20Sopenharmony_ci delta = (u64)arm_cmn_read_counter(dtc, i) << 16; 11868c2ecf20Sopenharmony_ci local64_add(delta, &dtc->counters[i]->count); 11878c2ecf20Sopenharmony_ci } 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci if (status & (1U << CMN_DT_NUM_COUNTERS)) { 11918c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 11928c2ecf20Sopenharmony_ci if (dtc->cc_active && !WARN_ON(!dtc->cycles)) { 11938c2ecf20Sopenharmony_ci delta = arm_cmn_read_cc(dtc); 11948c2ecf20Sopenharmony_ci local64_add(delta, &dtc->cycles->count); 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci writel_relaxed(status, dtc->base + CMN_DT_PMOVSR_CLR); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci if (!dtc->irq_friend) 12018c2ecf20Sopenharmony_ci return ret; 12028c2ecf20Sopenharmony_ci dtc += dtc->irq_friend; 12038c2ecf20Sopenharmony_ci } 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci/* We can reasonably accommodate DTCs of the same CMN sharing IRQs */ 12078c2ecf20Sopenharmony_cistatic int arm_cmn_init_irqs(struct arm_cmn *cmn) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci int i, j, irq, err; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci for (i = 0; i < cmn->num_dtcs; i++) { 12128c2ecf20Sopenharmony_ci irq = cmn->dtc[i].irq; 12138c2ecf20Sopenharmony_ci for (j = i; j--; ) { 12148c2ecf20Sopenharmony_ci if (cmn->dtc[j].irq == irq) { 12158c2ecf20Sopenharmony_ci cmn->dtc[j].irq_friend = i - j; 12168c2ecf20Sopenharmony_ci goto next; 12178c2ecf20Sopenharmony_ci } 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci err = devm_request_irq(cmn->dev, irq, arm_cmn_handle_irq, 12208c2ecf20Sopenharmony_ci IRQF_NOBALANCING | IRQF_NO_THREAD, 12218c2ecf20Sopenharmony_ci dev_name(cmn->dev), &cmn->dtc[i]); 12228c2ecf20Sopenharmony_ci if (err) 12238c2ecf20Sopenharmony_ci return err; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci err = irq_set_affinity_hint(irq, cpumask_of(cmn->cpu)); 12268c2ecf20Sopenharmony_ci if (err) 12278c2ecf20Sopenharmony_ci return err; 12288c2ecf20Sopenharmony_ci next: 12298c2ecf20Sopenharmony_ci ; /* isn't C great? */ 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci return 0; 12328c2ecf20Sopenharmony_ci} 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_cistatic void arm_cmn_init_dtm(struct arm_cmn_node *xp) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci int i; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 12398c2ecf20Sopenharmony_ci xp->wp_event[i] = -1; 12408c2ecf20Sopenharmony_ci writeq_relaxed(0, xp->pmu_base + CMN_DTM_WPn_MASK(i)); 12418c2ecf20Sopenharmony_ci writeq_relaxed(~0ULL, xp->pmu_base + CMN_DTM_WPn_VAL(i)); 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci xp->pmu_config_low = CMN_DTM_PMU_CONFIG_PMU_EN; 12448c2ecf20Sopenharmony_ci xp->dtc = -1; 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic int arm_cmn_init_dtc(struct arm_cmn *cmn, struct arm_cmn_node *dn, int idx) 12488c2ecf20Sopenharmony_ci{ 12498c2ecf20Sopenharmony_ci struct arm_cmn_dtc *dtc = cmn->dtc + idx; 12508c2ecf20Sopenharmony_ci struct arm_cmn_node *xp; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci dtc->base = dn->pmu_base - CMN_PMU_OFFSET; 12538c2ecf20Sopenharmony_ci dtc->irq = platform_get_irq(to_platform_device(cmn->dev), idx); 12548c2ecf20Sopenharmony_ci if (dtc->irq < 0) 12558c2ecf20Sopenharmony_ci return dtc->irq; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci writel_relaxed(CMN_DT_DTC_CTL_DT_EN, dtc->base + CMN_DT_DTC_CTL); 12588c2ecf20Sopenharmony_ci writel_relaxed(CMN_DT_PMCR_PMU_EN | CMN_DT_PMCR_OVFL_INTR_EN, dtc->base + CMN_DT_PMCR); 12598c2ecf20Sopenharmony_ci writeq_relaxed(0, dtc->base + CMN_DT_PMCCNTR); 12608c2ecf20Sopenharmony_ci writel_relaxed(0x1ff, dtc->base + CMN_DT_PMOVSR_CLR); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* We do at least know that a DTC's XP must be in that DTC's domain */ 12638c2ecf20Sopenharmony_ci xp = arm_cmn_node_to_xp(dn); 12648c2ecf20Sopenharmony_ci xp->dtc = idx; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci return 0; 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic int arm_cmn_node_cmp(const void *a, const void *b) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci const struct arm_cmn_node *dna = a, *dnb = b; 12728c2ecf20Sopenharmony_ci int cmp; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci cmp = dna->type - dnb->type; 12758c2ecf20Sopenharmony_ci if (!cmp) 12768c2ecf20Sopenharmony_ci cmp = dna->logid - dnb->logid; 12778c2ecf20Sopenharmony_ci return cmp; 12788c2ecf20Sopenharmony_ci} 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_cistatic int arm_cmn_init_dtcs(struct arm_cmn *cmn) 12818c2ecf20Sopenharmony_ci{ 12828c2ecf20Sopenharmony_ci struct arm_cmn_node *dn; 12838c2ecf20Sopenharmony_ci int dtc_idx = 0; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci cmn->dtc = devm_kcalloc(cmn->dev, cmn->num_dtcs, sizeof(cmn->dtc[0]), GFP_KERNEL); 12868c2ecf20Sopenharmony_ci if (!cmn->dtc) 12878c2ecf20Sopenharmony_ci return -ENOMEM; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci sort(cmn->dns, cmn->num_dns, sizeof(cmn->dns[0]), arm_cmn_node_cmp, NULL); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci cmn->xps = arm_cmn_node(cmn, CMN_TYPE_XP); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci for (dn = cmn->dns; dn < cmn->dns + cmn->num_dns; dn++) { 12948c2ecf20Sopenharmony_ci if (dn->type != CMN_TYPE_XP) 12958c2ecf20Sopenharmony_ci arm_cmn_init_node_to_xp(cmn, dn); 12968c2ecf20Sopenharmony_ci else if (cmn->num_dtcs == 1) 12978c2ecf20Sopenharmony_ci dn->dtc = 0; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (dn->type == CMN_TYPE_DTC) 13008c2ecf20Sopenharmony_ci arm_cmn_init_dtc(cmn, dn, dtc_idx++); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci /* To the PMU, RN-Ds don't add anything over RN-Is, so smoosh them together */ 13038c2ecf20Sopenharmony_ci if (dn->type == CMN_TYPE_RND) 13048c2ecf20Sopenharmony_ci dn->type = CMN_TYPE_RNI; 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci arm_cmn_set_state(cmn, CMN_STATE_DISABLED); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci return 0; 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_cistatic void arm_cmn_init_node_info(struct arm_cmn *cmn, u32 offset, struct arm_cmn_node *node) 13138c2ecf20Sopenharmony_ci{ 13148c2ecf20Sopenharmony_ci int level; 13158c2ecf20Sopenharmony_ci u64 reg = readq_relaxed(cmn->base + offset + CMN_NODE_INFO); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci node->type = FIELD_GET(CMN_NI_NODE_TYPE, reg); 13188c2ecf20Sopenharmony_ci node->id = FIELD_GET(CMN_NI_NODE_ID, reg); 13198c2ecf20Sopenharmony_ci node->logid = FIELD_GET(CMN_NI_LOGICAL_ID, reg); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci node->pmu_base = cmn->base + offset + CMN_PMU_OFFSET; 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci if (node->type == CMN_TYPE_CFG) 13248c2ecf20Sopenharmony_ci level = 0; 13258c2ecf20Sopenharmony_ci else if (node->type == CMN_TYPE_XP) 13268c2ecf20Sopenharmony_ci level = 1; 13278c2ecf20Sopenharmony_ci else 13288c2ecf20Sopenharmony_ci level = 2; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci dev_dbg(cmn->dev, "node%*c%#06hx%*ctype:%-#6x id:%-4hd off:%#x\n", 13318c2ecf20Sopenharmony_ci (level * 2) + 1, ' ', node->id, 5 - (level * 2), ' ', 13328c2ecf20Sopenharmony_ci node->type, node->logid, offset); 13338c2ecf20Sopenharmony_ci} 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_cistatic int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci void __iomem *cfg_region; 13388c2ecf20Sopenharmony_ci struct arm_cmn_node cfg, *dn; 13398c2ecf20Sopenharmony_ci u16 child_count, child_poff; 13408c2ecf20Sopenharmony_ci u32 xp_offset[CMN_MAX_XPS]; 13418c2ecf20Sopenharmony_ci u64 reg; 13428c2ecf20Sopenharmony_ci int i, j; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci cfg_region = cmn->base + rgn_offset; 13458c2ecf20Sopenharmony_ci reg = readl_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_2); 13468c2ecf20Sopenharmony_ci cmn->rev = FIELD_GET(CMN_CFGM_PID2_REVISION, reg); 13478c2ecf20Sopenharmony_ci dev_dbg(cmn->dev, "periph_id_2 revision: %d\n", cmn->rev); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci arm_cmn_init_node_info(cmn, rgn_offset, &cfg); 13508c2ecf20Sopenharmony_ci if (cfg.type != CMN_TYPE_CFG) 13518c2ecf20Sopenharmony_ci return -ENODEV; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci reg = readq_relaxed(cfg_region + CMN_CHILD_INFO); 13548c2ecf20Sopenharmony_ci child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg); 13558c2ecf20Sopenharmony_ci child_poff = FIELD_GET(CMN_CI_CHILD_PTR_OFFSET, reg); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci cmn->num_xps = child_count; 13588c2ecf20Sopenharmony_ci cmn->num_dns = cmn->num_xps; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci /* Pass 1: visit the XPs, enumerate their children */ 13618c2ecf20Sopenharmony_ci for (i = 0; i < cmn->num_xps; i++) { 13628c2ecf20Sopenharmony_ci reg = readq_relaxed(cfg_region + child_poff + i * 8); 13638c2ecf20Sopenharmony_ci xp_offset[i] = reg & CMN_CHILD_NODE_ADDR; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci reg = readq_relaxed(cmn->base + xp_offset[i] + CMN_CHILD_INFO); 13668c2ecf20Sopenharmony_ci cmn->num_dns += FIELD_GET(CMN_CI_CHILD_COUNT, reg); 13678c2ecf20Sopenharmony_ci } 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci /* Cheeky +1 to help terminate pointer-based iteration */ 13708c2ecf20Sopenharmony_ci cmn->dns = devm_kcalloc(cmn->dev, cmn->num_dns + 1, 13718c2ecf20Sopenharmony_ci sizeof(*cmn->dns), GFP_KERNEL); 13728c2ecf20Sopenharmony_ci if (!cmn->dns) 13738c2ecf20Sopenharmony_ci return -ENOMEM; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci /* Pass 2: now we can actually populate the nodes */ 13768c2ecf20Sopenharmony_ci dn = cmn->dns; 13778c2ecf20Sopenharmony_ci for (i = 0; i < cmn->num_xps; i++) { 13788c2ecf20Sopenharmony_ci void __iomem *xp_region = cmn->base + xp_offset[i]; 13798c2ecf20Sopenharmony_ci struct arm_cmn_node *xp = dn++; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci arm_cmn_init_node_info(cmn, xp_offset[i], xp); 13828c2ecf20Sopenharmony_ci arm_cmn_init_dtm(xp); 13838c2ecf20Sopenharmony_ci /* 13848c2ecf20Sopenharmony_ci * Thanks to the order in which XP logical IDs seem to be 13858c2ecf20Sopenharmony_ci * assigned, we can handily infer the mesh X dimension by 13868c2ecf20Sopenharmony_ci * looking out for the XP at (0,1) without needing to know 13878c2ecf20Sopenharmony_ci * the exact node ID format, which we can later derive. 13888c2ecf20Sopenharmony_ci */ 13898c2ecf20Sopenharmony_ci if (xp->id == (1 << 3)) 13908c2ecf20Sopenharmony_ci cmn->mesh_x = xp->logid; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci reg = readq_relaxed(xp_region + CMN_CHILD_INFO); 13938c2ecf20Sopenharmony_ci child_count = FIELD_GET(CMN_CI_CHILD_COUNT, reg); 13948c2ecf20Sopenharmony_ci child_poff = FIELD_GET(CMN_CI_CHILD_PTR_OFFSET, reg); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci for (j = 0; j < child_count; j++) { 13978c2ecf20Sopenharmony_ci reg = readq_relaxed(xp_region + child_poff + j * 8); 13988c2ecf20Sopenharmony_ci /* 13998c2ecf20Sopenharmony_ci * Don't even try to touch anything external, since in general 14008c2ecf20Sopenharmony_ci * we haven't a clue how to power up arbitrary CHI requesters. 14018c2ecf20Sopenharmony_ci * As of CMN-600r1 these could only be RN-SAMs or CXLAs, 14028c2ecf20Sopenharmony_ci * neither of which have any PMU events anyway. 14038c2ecf20Sopenharmony_ci * (Actually, CXLAs do seem to have grown some events in r1p2, 14048c2ecf20Sopenharmony_ci * but they don't go to regular XP DTMs, and they depend on 14058c2ecf20Sopenharmony_ci * secure configuration which we can't easily deal with) 14068c2ecf20Sopenharmony_ci */ 14078c2ecf20Sopenharmony_ci if (reg & CMN_CHILD_NODE_EXTERNAL) { 14088c2ecf20Sopenharmony_ci dev_dbg(cmn->dev, "ignoring external node %llx\n", reg); 14098c2ecf20Sopenharmony_ci continue; 14108c2ecf20Sopenharmony_ci } 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci arm_cmn_init_node_info(cmn, reg & CMN_CHILD_NODE_ADDR, dn); 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci switch (dn->type) { 14158c2ecf20Sopenharmony_ci case CMN_TYPE_DTC: 14168c2ecf20Sopenharmony_ci cmn->num_dtcs++; 14178c2ecf20Sopenharmony_ci dn++; 14188c2ecf20Sopenharmony_ci break; 14198c2ecf20Sopenharmony_ci /* These guys have PMU events */ 14208c2ecf20Sopenharmony_ci case CMN_TYPE_DVM: 14218c2ecf20Sopenharmony_ci case CMN_TYPE_HNI: 14228c2ecf20Sopenharmony_ci case CMN_TYPE_HNF: 14238c2ecf20Sopenharmony_ci case CMN_TYPE_SBSX: 14248c2ecf20Sopenharmony_ci case CMN_TYPE_RNI: 14258c2ecf20Sopenharmony_ci case CMN_TYPE_RND: 14268c2ecf20Sopenharmony_ci case CMN_TYPE_CXRA: 14278c2ecf20Sopenharmony_ci case CMN_TYPE_CXHA: 14288c2ecf20Sopenharmony_ci dn++; 14298c2ecf20Sopenharmony_ci break; 14308c2ecf20Sopenharmony_ci /* Nothing to see here */ 14318c2ecf20Sopenharmony_ci case CMN_TYPE_RNSAM: 14328c2ecf20Sopenharmony_ci case CMN_TYPE_CXLA: 14338c2ecf20Sopenharmony_ci break; 14348c2ecf20Sopenharmony_ci /* Something has gone horribly wrong */ 14358c2ecf20Sopenharmony_ci default: 14368c2ecf20Sopenharmony_ci dev_err(cmn->dev, "invalid device node type: 0x%x\n", dn->type); 14378c2ecf20Sopenharmony_ci return -ENODEV; 14388c2ecf20Sopenharmony_ci } 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci /* Correct for any nodes we skipped */ 14438c2ecf20Sopenharmony_ci cmn->num_dns = dn - cmn->dns; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci /* 14468c2ecf20Sopenharmony_ci * If mesh_x wasn't set during discovery then we never saw 14478c2ecf20Sopenharmony_ci * an XP at (0,1), thus we must have an Nx1 configuration. 14488c2ecf20Sopenharmony_ci */ 14498c2ecf20Sopenharmony_ci if (!cmn->mesh_x) 14508c2ecf20Sopenharmony_ci cmn->mesh_x = cmn->num_xps; 14518c2ecf20Sopenharmony_ci cmn->mesh_y = cmn->num_xps / cmn->mesh_x; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci dev_dbg(cmn->dev, "mesh %dx%d, ID width %d\n", 14548c2ecf20Sopenharmony_ci cmn->mesh_x, cmn->mesh_y, arm_cmn_xyidbits(cmn)); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci return 0; 14578c2ecf20Sopenharmony_ci} 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_cistatic int arm_cmn_acpi_probe(struct platform_device *pdev, struct arm_cmn *cmn) 14608c2ecf20Sopenharmony_ci{ 14618c2ecf20Sopenharmony_ci struct resource *cfg, *root; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci cfg = platform_get_resource(pdev, IORESOURCE_MEM, 0); 14648c2ecf20Sopenharmony_ci if (!cfg) 14658c2ecf20Sopenharmony_ci return -EINVAL; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci root = platform_get_resource(pdev, IORESOURCE_MEM, 1); 14688c2ecf20Sopenharmony_ci if (!root) 14698c2ecf20Sopenharmony_ci return -EINVAL; 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci if (!resource_contains(cfg, root)) 14728c2ecf20Sopenharmony_ci swap(cfg, root); 14738c2ecf20Sopenharmony_ci /* 14748c2ecf20Sopenharmony_ci * Note that devm_ioremap_resource() is dumb and won't let the platform 14758c2ecf20Sopenharmony_ci * device claim cfg when the ACPI companion device has already claimed 14768c2ecf20Sopenharmony_ci * root within it. But since they *are* already both claimed in the 14778c2ecf20Sopenharmony_ci * appropriate name, we don't really need to do it again here anyway. 14788c2ecf20Sopenharmony_ci */ 14798c2ecf20Sopenharmony_ci cmn->base = devm_ioremap(cmn->dev, cfg->start, resource_size(cfg)); 14808c2ecf20Sopenharmony_ci if (!cmn->base) 14818c2ecf20Sopenharmony_ci return -ENOMEM; 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci return root->start - cfg->start; 14848c2ecf20Sopenharmony_ci} 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_cistatic int arm_cmn_of_probe(struct platform_device *pdev, struct arm_cmn *cmn) 14878c2ecf20Sopenharmony_ci{ 14888c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 14898c2ecf20Sopenharmony_ci u32 rootnode; 14908c2ecf20Sopenharmony_ci int ret; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci cmn->base = devm_platform_ioremap_resource(pdev, 0); 14938c2ecf20Sopenharmony_ci if (IS_ERR(cmn->base)) 14948c2ecf20Sopenharmony_ci return PTR_ERR(cmn->base); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "arm,root-node", &rootnode); 14978c2ecf20Sopenharmony_ci if (ret) 14988c2ecf20Sopenharmony_ci return ret; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci return rootnode; 15018c2ecf20Sopenharmony_ci} 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_cistatic int arm_cmn_probe(struct platform_device *pdev) 15048c2ecf20Sopenharmony_ci{ 15058c2ecf20Sopenharmony_ci struct arm_cmn *cmn; 15068c2ecf20Sopenharmony_ci const char *name; 15078c2ecf20Sopenharmony_ci static atomic_t id; 15088c2ecf20Sopenharmony_ci int err, rootnode; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci cmn = devm_kzalloc(&pdev->dev, sizeof(*cmn), GFP_KERNEL); 15118c2ecf20Sopenharmony_ci if (!cmn) 15128c2ecf20Sopenharmony_ci return -ENOMEM; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci cmn->dev = &pdev->dev; 15158c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, cmn); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci if (has_acpi_companion(cmn->dev)) 15188c2ecf20Sopenharmony_ci rootnode = arm_cmn_acpi_probe(pdev, cmn); 15198c2ecf20Sopenharmony_ci else 15208c2ecf20Sopenharmony_ci rootnode = arm_cmn_of_probe(pdev, cmn); 15218c2ecf20Sopenharmony_ci if (rootnode < 0) 15228c2ecf20Sopenharmony_ci return rootnode; 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci err = arm_cmn_discover(cmn, rootnode); 15258c2ecf20Sopenharmony_ci if (err) 15268c2ecf20Sopenharmony_ci return err; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci err = arm_cmn_init_dtcs(cmn); 15298c2ecf20Sopenharmony_ci if (err) 15308c2ecf20Sopenharmony_ci return err; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci err = arm_cmn_init_irqs(cmn); 15338c2ecf20Sopenharmony_ci if (err) 15348c2ecf20Sopenharmony_ci return err; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci cmn->cpu = raw_smp_processor_id(); 15378c2ecf20Sopenharmony_ci cmn->pmu = (struct pmu) { 15388c2ecf20Sopenharmony_ci .module = THIS_MODULE, 15398c2ecf20Sopenharmony_ci .attr_groups = arm_cmn_attr_groups, 15408c2ecf20Sopenharmony_ci .capabilities = PERF_PMU_CAP_NO_EXCLUDE, 15418c2ecf20Sopenharmony_ci .task_ctx_nr = perf_invalid_context, 15428c2ecf20Sopenharmony_ci .pmu_enable = arm_cmn_pmu_enable, 15438c2ecf20Sopenharmony_ci .pmu_disable = arm_cmn_pmu_disable, 15448c2ecf20Sopenharmony_ci .event_init = arm_cmn_event_init, 15458c2ecf20Sopenharmony_ci .add = arm_cmn_event_add, 15468c2ecf20Sopenharmony_ci .del = arm_cmn_event_del, 15478c2ecf20Sopenharmony_ci .start = arm_cmn_event_start, 15488c2ecf20Sopenharmony_ci .stop = arm_cmn_event_stop, 15498c2ecf20Sopenharmony_ci .read = arm_cmn_event_read, 15508c2ecf20Sopenharmony_ci .start_txn = arm_cmn_start_txn, 15518c2ecf20Sopenharmony_ci .commit_txn = arm_cmn_commit_txn, 15528c2ecf20Sopenharmony_ci .cancel_txn = arm_cmn_end_txn, 15538c2ecf20Sopenharmony_ci }; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci name = devm_kasprintf(cmn->dev, GFP_KERNEL, "arm_cmn_%d", atomic_fetch_inc(&id)); 15568c2ecf20Sopenharmony_ci if (!name) 15578c2ecf20Sopenharmony_ci return -ENOMEM; 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci err = cpuhp_state_add_instance(arm_cmn_hp_state, &cmn->cpuhp_node); 15608c2ecf20Sopenharmony_ci if (err) 15618c2ecf20Sopenharmony_ci return err; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci err = perf_pmu_register(&cmn->pmu, name, -1); 15648c2ecf20Sopenharmony_ci if (err) 15658c2ecf20Sopenharmony_ci cpuhp_state_remove_instance(arm_cmn_hp_state, &cmn->cpuhp_node); 15668c2ecf20Sopenharmony_ci return err; 15678c2ecf20Sopenharmony_ci} 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_cistatic int arm_cmn_remove(struct platform_device *pdev) 15708c2ecf20Sopenharmony_ci{ 15718c2ecf20Sopenharmony_ci struct arm_cmn *cmn = platform_get_drvdata(pdev); 15728c2ecf20Sopenharmony_ci int i; 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci writel_relaxed(0, cmn->dtc[0].base + CMN_DT_DTC_CTL); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci perf_pmu_unregister(&cmn->pmu); 15778c2ecf20Sopenharmony_ci cpuhp_state_remove_instance(arm_cmn_hp_state, &cmn->cpuhp_node); 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci for (i = 0; i < cmn->num_dtcs; i++) 15808c2ecf20Sopenharmony_ci irq_set_affinity_hint(cmn->dtc[i].irq, NULL); 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci return 0; 15838c2ecf20Sopenharmony_ci} 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 15868c2ecf20Sopenharmony_cistatic const struct of_device_id arm_cmn_of_match[] = { 15878c2ecf20Sopenharmony_ci { .compatible = "arm,cmn-600", }, 15888c2ecf20Sopenharmony_ci {} 15898c2ecf20Sopenharmony_ci}; 15908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, arm_cmn_of_match); 15918c2ecf20Sopenharmony_ci#endif 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 15948c2ecf20Sopenharmony_cistatic const struct acpi_device_id arm_cmn_acpi_match[] = { 15958c2ecf20Sopenharmony_ci { "ARMHC600", }, 15968c2ecf20Sopenharmony_ci {} 15978c2ecf20Sopenharmony_ci}; 15988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, arm_cmn_acpi_match); 15998c2ecf20Sopenharmony_ci#endif 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_cistatic struct platform_driver arm_cmn_driver = { 16028c2ecf20Sopenharmony_ci .driver = { 16038c2ecf20Sopenharmony_ci .name = "arm-cmn", 16048c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(arm_cmn_of_match), 16058c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(arm_cmn_acpi_match), 16068c2ecf20Sopenharmony_ci }, 16078c2ecf20Sopenharmony_ci .probe = arm_cmn_probe, 16088c2ecf20Sopenharmony_ci .remove = arm_cmn_remove, 16098c2ecf20Sopenharmony_ci}; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_cistatic int __init arm_cmn_init(void) 16128c2ecf20Sopenharmony_ci{ 16138c2ecf20Sopenharmony_ci int ret; 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, 16168c2ecf20Sopenharmony_ci "perf/arm/cmn:online", NULL, 16178c2ecf20Sopenharmony_ci arm_cmn_pmu_offline_cpu); 16188c2ecf20Sopenharmony_ci if (ret < 0) 16198c2ecf20Sopenharmony_ci return ret; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci arm_cmn_hp_state = ret; 16228c2ecf20Sopenharmony_ci ret = platform_driver_register(&arm_cmn_driver); 16238c2ecf20Sopenharmony_ci if (ret) 16248c2ecf20Sopenharmony_ci cpuhp_remove_multi_state(arm_cmn_hp_state); 16258c2ecf20Sopenharmony_ci return ret; 16268c2ecf20Sopenharmony_ci} 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_cistatic void __exit arm_cmn_exit(void) 16298c2ecf20Sopenharmony_ci{ 16308c2ecf20Sopenharmony_ci platform_driver_unregister(&arm_cmn_driver); 16318c2ecf20Sopenharmony_ci cpuhp_remove_multi_state(arm_cmn_hp_state); 16328c2ecf20Sopenharmony_ci} 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_cimodule_init(arm_cmn_init); 16358c2ecf20Sopenharmony_cimodule_exit(arm_cmn_exit); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Robin Murphy <robin.murphy@arm.com>"); 16388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Arm CMN-600 PMU driver"); 16398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1640