Merge branch 'for-next/perf' into for-next/core

* for-next/perf: (33 commits)
  perf: arm-ni: Fix an NULL vs IS_ERR() bug
  perf: arm_pmuv3: Use BR_RETIRED for HW branch event if enabled
  MAINTAINERS: List Arm interconnect PMUs as supported
  perf: Add driver for Arm NI-700 interconnect PMU
  dt-bindings/perf: Add Arm NI-700 PMU
  perf/arm-cmn: Improve format attr printing
  perf/arm-cmn: Clean up unnecessary NUMA_NO_NODE check
  perf/arm-cmn: Support CMN S3
  dt-bindings: perf: arm-cmn: Add CMN S3
  perf/arm-cmn: Refactor DTC PMU register access
  perf/arm-cmn: Make cycle counts less surprising
  perf/arm-cmn: Improve build-time assertion
  perf/arm-cmn: Ensure dtm_idx is big enough
  perf/arm-cmn: Fix CCLA register offset
  perf/arm-cmn: Refactor node ID handling. Again.
  drivers/perf: hisi_pcie: Export supported Root Ports [bdf_min, bdf_max]
  drivers/perf: hisi_pcie: Fix TLP headers bandwidth counting
  drivers/perf: hisi_pcie: Record hardware counts correctly
  drivers/perf: arm_spe: Use perf_allow_kernel() for permissions
  perf/dwc_pcie: Add support for QCOM vendor devices
  ...
This commit is contained in:
Will Deacon 2024-09-12 13:43:16 +01:00
commit 119e3eef32
35 changed files with 1476 additions and 484 deletions

View File

@ -0,0 +1,17 @@
====================================
Arm Network-on Chip Interconnect PMU
====================================
NI-700 and friends implement a distinct PMU for each clock domain within the
interconnect. Correspondingly, the driver exposes multiple PMU devices named
arm_ni_<x>_cd_<y>, where <x> is an (arbitrary) instance identifier and <y> is
the clock domain ID within that particular instance. If multiple NI instances
exist within a system, the PMU devices can be correlated with the underlying
hardware instance via sysfs parentage.
Each PMU exposes base event aliases for the interface types present in its clock
domain. These require qualifying with the "eventid" and "nodeid" parameters
to specify the event code to count and the interface at which to count it
(per the configured hardware ID as reflected in the xxNI_NODE_INFO register).
The exception is the "cycles" alias for the PMU cycle counter, which is encoded
with the PMU node type and needs no further qualification.

View File

@ -46,16 +46,16 @@ Some of the events only exist for specific configurations.
DesignWare Cores (DWC) PCIe PMU Driver DesignWare Cores (DWC) PCIe PMU Driver
======================================= =======================================
This driver adds PMU devices for each PCIe Root Port named based on the BDF of This driver adds PMU devices for each PCIe Root Port named based on the SBDF of
the Root Port. For example, the Root Port. For example,
30:03.0 PCI bridge: Device 1ded:8000 (rev 01) 0001:30:03.0 PCI bridge: Device 1ded:8000 (rev 01)
the PMU device name for this Root Port is dwc_rootport_3018. the PMU device name for this Root Port is dwc_rootport_13018.
The DWC PCIe PMU driver registers a perf PMU driver, which provides The DWC PCIe PMU driver registers a perf PMU driver, which provides
description of available events and configuration options in sysfs, see description of available events and configuration options in sysfs, see
/sys/bus/event_source/devices/dwc_rootport_{bdf}. /sys/bus/event_source/devices/dwc_rootport_{sbdf}.
The "format" directory describes format of the config fields of the The "format" directory describes format of the config fields of the
perf_event_attr structure. The "events" directory provides configuration perf_event_attr structure. The "events" directory provides configuration
@ -66,16 +66,16 @@ The "perf list" command shall list the available events from sysfs, e.g.::
$# perf list | grep dwc_rootport $# perf list | grep dwc_rootport
<...> <...>
dwc_rootport_3018/Rx_PCIe_TLP_Data_Payload/ [Kernel PMU event] dwc_rootport_13018/Rx_PCIe_TLP_Data_Payload/ [Kernel PMU event]
<...> <...>
dwc_rootport_3018/rx_memory_read,lane=?/ [Kernel PMU event] dwc_rootport_13018/rx_memory_read,lane=?/ [Kernel PMU event]
Time Based Analysis Event Usage Time Based Analysis Event Usage
------------------------------- -------------------------------
Example usage of counting PCIe RX TLP data payload (Units of bytes):: Example usage of counting PCIe RX TLP data payload (Units of bytes)::
$# perf stat -a -e dwc_rootport_3018/Rx_PCIe_TLP_Data_Payload/ $# perf stat -a -e dwc_rootport_13018/Rx_PCIe_TLP_Data_Payload/
The average RX/TX bandwidth can be calculated using the following formula: The average RX/TX bandwidth can be calculated using the following formula:
@ -88,7 +88,7 @@ Lane Event Usage
Each lane has the same event set and to avoid generating a list of hundreds Each lane has the same event set and to avoid generating a list of hundreds
of events, the user need to specify the lane ID explicitly, e.g.:: of events, the user need to specify the lane ID explicitly, e.g.::
$# perf stat -a -e dwc_rootport_3018/rx_memory_read,lane=4/ $# perf stat -a -e dwc_rootport_13018/rx_memory_read,lane=4/
The driver does not support sampling, therefore "perf record" will not The driver does not support sampling, therefore "perf record" will not
work. Per-task (without "-a") perf sessions are not supported. work. Per-task (without "-a") perf sessions are not supported.

View File

@ -28,7 +28,9 @@ The "identifier" sysfs file allows users to identify the version of the
PMU hardware device. PMU hardware device.
The "bus" sysfs file allows users to get the bus number of Root Ports The "bus" sysfs file allows users to get the bus number of Root Ports
monitored by PMU. monitored by PMU. Furthermore users can get the Root Ports range in
[bdf_min, bdf_max] from "bdf_min" and "bdf_max" sysfs attributes
respectively.
Example usage of perf:: Example usage of perf::

View File

@ -16,6 +16,7 @@ Performance monitor support
starfive_starlink_pmu starfive_starlink_pmu
arm-ccn arm-ccn
arm-cmn arm-cmn
arm-ni
xgene-pmu xgene-pmu
arm_dsu_pmu arm_dsu_pmu
thunderx2-pmu thunderx2-pmu

View File

@ -16,6 +16,7 @@ properties:
- arm,cmn-600 - arm,cmn-600
- arm,cmn-650 - arm,cmn-650
- arm,cmn-700 - arm,cmn-700
- arm,cmn-s3
- arm,ci-700 - arm,ci-700
reg: reg:

View File

@ -0,0 +1,30 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/perf/arm,ni.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Arm NI (Network-on-Chip Interconnect) Performance Monitors
maintainers:
- Robin Murphy <robin.murphy@arm.com>
properties:
compatible:
const: arm,ni-700
reg:
items:
- description: Complete configuration register space
interrupts:
minItems: 1
maxItems: 32
description: Overflow interrupts, one per clock domain, in order of domain ID
required:
- compatible
- reg
- interrupts
additionalProperties: false

View File

@ -1738,6 +1738,17 @@ F: drivers/mtd/maps/physmap-versatile.*
F: drivers/power/reset/arm-versatile-reboot.c F: drivers/power/reset/arm-versatile-reboot.c
F: drivers/soc/versatile/ F: drivers/soc/versatile/
ARM INTERCONNECT PMU DRIVERS
M: Robin Murphy <robin.murphy@arm.com>
S: Supported
F: Documentation/admin-guide/perf/arm-cmn.rst
F: Documentation/admin-guide/perf/arm-ni.rst
F: Documentation/devicetree/bindings/perf/arm,cmn.yaml
F: Documentation/devicetree/bindings/perf/arm,ni.yaml
F: drivers/perf/arm-cmn.c
F: drivers/perf/arm-ni.c
F: tools/perf/pmu-events/arch/arm64/arm/cmn/
ARM KOMEDA DRM-KMS DRIVER ARM KOMEDA DRM-KMS DRIVER
M: Liviu Dudau <liviu.dudau@arm.com> M: Liviu Dudau <liviu.dudau@arm.com>
S: Supported S: Supported

View File

@ -127,6 +127,12 @@ static inline u32 read_pmuver(void)
return (dfr0 >> 24) & 0xf; return (dfr0 >> 24) & 0xf;
} }
static inline bool pmuv3_has_icntr(void)
{
/* FEAT_PMUv3_ICNTR not accessible for 32-bit */
return false;
}
static inline void write_pmcr(u32 val) static inline void write_pmcr(u32 val)
{ {
write_sysreg(val, PMCR); write_sysreg(val, PMCR);
@ -152,6 +158,13 @@ static inline u64 read_pmccntr(void)
return read_sysreg(PMCCNTR); return read_sysreg(PMCCNTR);
} }
static inline void write_pmicntr(u64 val) {}
static inline u64 read_pmicntr(void)
{
return 0;
}
static inline void write_pmcntenset(u32 val) static inline void write_pmcntenset(u32 val)
{ {
write_sysreg(val, PMCNTENSET); write_sysreg(val, PMCNTENSET);
@ -177,6 +190,13 @@ static inline void write_pmccfiltr(u32 val)
write_sysreg(val, PMCCFILTR); write_sysreg(val, PMCCFILTR);
} }
static inline void write_pmicfiltr(u64 val) {}
static inline u64 read_pmicfiltr(void)
{
return 0;
}
static inline void write_pmovsclr(u32 val) static inline void write_pmovsclr(u32 val)
{ {
write_sysreg(val, PMOVSR); write_sysreg(val, PMOVSR);

View File

@ -33,6 +33,14 @@ static inline void write_pmevtypern(int n, unsigned long val)
PMEVN_SWITCH(n, WRITE_PMEVTYPERN); PMEVN_SWITCH(n, WRITE_PMEVTYPERN);
} }
#define RETURN_READ_PMEVTYPERN(n) \
return read_sysreg(pmevtyper##n##_el0)
static inline unsigned long read_pmevtypern(int n)
{
PMEVN_SWITCH(n, RETURN_READ_PMEVTYPERN);
return 0;
}
static inline unsigned long read_pmmir(void) static inline unsigned long read_pmmir(void)
{ {
return read_cpuid(PMMIR_EL1); return read_cpuid(PMMIR_EL1);
@ -46,6 +54,14 @@ static inline u32 read_pmuver(void)
ID_AA64DFR0_EL1_PMUVer_SHIFT); ID_AA64DFR0_EL1_PMUVer_SHIFT);
} }
static inline bool pmuv3_has_icntr(void)
{
u64 dfr1 = read_sysreg(id_aa64dfr1_el1);
return !!cpuid_feature_extract_unsigned_field(dfr1,
ID_AA64DFR1_EL1_PMICNTR_SHIFT);
}
static inline void write_pmcr(u64 val) static inline void write_pmcr(u64 val)
{ {
write_sysreg(val, pmcr_el0); write_sysreg(val, pmcr_el0);
@ -71,22 +87,32 @@ static inline u64 read_pmccntr(void)
return read_sysreg(pmccntr_el0); return read_sysreg(pmccntr_el0);
} }
static inline void write_pmcntenset(u32 val) static inline void write_pmicntr(u64 val)
{
write_sysreg_s(val, SYS_PMICNTR_EL0);
}
static inline u64 read_pmicntr(void)
{
return read_sysreg_s(SYS_PMICNTR_EL0);
}
static inline void write_pmcntenset(u64 val)
{ {
write_sysreg(val, pmcntenset_el0); write_sysreg(val, pmcntenset_el0);
} }
static inline void write_pmcntenclr(u32 val) static inline void write_pmcntenclr(u64 val)
{ {
write_sysreg(val, pmcntenclr_el0); write_sysreg(val, pmcntenclr_el0);
} }
static inline void write_pmintenset(u32 val) static inline void write_pmintenset(u64 val)
{ {
write_sysreg(val, pmintenset_el1); write_sysreg(val, pmintenset_el1);
} }
static inline void write_pmintenclr(u32 val) static inline void write_pmintenclr(u64 val)
{ {
write_sysreg(val, pmintenclr_el1); write_sysreg(val, pmintenclr_el1);
} }
@ -96,12 +122,27 @@ static inline void write_pmccfiltr(u64 val)
write_sysreg(val, pmccfiltr_el0); write_sysreg(val, pmccfiltr_el0);
} }
static inline void write_pmovsclr(u32 val) static inline u64 read_pmccfiltr(void)
{
return read_sysreg(pmccfiltr_el0);
}
static inline void write_pmicfiltr(u64 val)
{
write_sysreg_s(val, SYS_PMICFILTR_EL0);
}
static inline u64 read_pmicfiltr(void)
{
return read_sysreg_s(SYS_PMICFILTR_EL0);
}
static inline void write_pmovsclr(u64 val)
{ {
write_sysreg(val, pmovsclr_el0); write_sysreg(val, pmovsclr_el0);
} }
static inline u32 read_pmovsclr(void) static inline u64 read_pmovsclr(void)
{ {
return read_sysreg(pmovsclr_el0); return read_sysreg(pmovsclr_el0);
} }

View File

@ -1330,12 +1330,12 @@ void kvm_arch_vcpu_load_debug_state_flags(struct kvm_vcpu *vcpu);
void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu); void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu);
#ifdef CONFIG_KVM #ifdef CONFIG_KVM
void kvm_set_pmu_events(u32 set, struct perf_event_attr *attr); void kvm_set_pmu_events(u64 set, struct perf_event_attr *attr);
void kvm_clr_pmu_events(u32 clr); void kvm_clr_pmu_events(u64 clr);
bool kvm_set_pmuserenr(u64 val); bool kvm_set_pmuserenr(u64 val);
#else #else
static inline void kvm_set_pmu_events(u32 set, struct perf_event_attr *attr) {} static inline void kvm_set_pmu_events(u64 set, struct perf_event_attr *attr) {}
static inline void kvm_clr_pmu_events(u32 clr) {} static inline void kvm_clr_pmu_events(u64 clr) {}
static inline bool kvm_set_pmuserenr(u64 val) static inline bool kvm_set_pmuserenr(u64 val)
{ {
return false; return false;

View File

@ -403,7 +403,6 @@
#define SYS_PMCNTENCLR_EL0 sys_reg(3, 3, 9, 12, 2) #define SYS_PMCNTENCLR_EL0 sys_reg(3, 3, 9, 12, 2)
#define SYS_PMOVSCLR_EL0 sys_reg(3, 3, 9, 12, 3) #define SYS_PMOVSCLR_EL0 sys_reg(3, 3, 9, 12, 3)
#define SYS_PMSWINC_EL0 sys_reg(3, 3, 9, 12, 4) #define SYS_PMSWINC_EL0 sys_reg(3, 3, 9, 12, 4)
#define SYS_PMSELR_EL0 sys_reg(3, 3, 9, 12, 5)
#define SYS_PMCEID0_EL0 sys_reg(3, 3, 9, 12, 6) #define SYS_PMCEID0_EL0 sys_reg(3, 3, 9, 12, 6)
#define SYS_PMCEID1_EL0 sys_reg(3, 3, 9, 12, 7) #define SYS_PMCEID1_EL0 sys_reg(3, 3, 9, 12, 7)
#define SYS_PMCCNTR_EL0 sys_reg(3, 3, 9, 13, 0) #define SYS_PMCCNTR_EL0 sys_reg(3, 3, 9, 13, 0)

View File

@ -233,7 +233,7 @@ void kvm_pmu_vcpu_init(struct kvm_vcpu *vcpu)
int i; int i;
struct kvm_pmu *pmu = &vcpu->arch.pmu; struct kvm_pmu *pmu = &vcpu->arch.pmu;
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) for (i = 0; i < KVM_ARMV8_PMU_MAX_COUNTERS; i++)
pmu->pmc[i].idx = i; pmu->pmc[i].idx = i;
} }
@ -260,7 +260,7 @@ void kvm_pmu_vcpu_destroy(struct kvm_vcpu *vcpu)
{ {
int i; int i;
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) for (i = 0; i < KVM_ARMV8_PMU_MAX_COUNTERS; i++)
kvm_pmu_release_perf_event(kvm_vcpu_idx_to_pmc(vcpu, i)); kvm_pmu_release_perf_event(kvm_vcpu_idx_to_pmc(vcpu, i));
irq_work_sync(&vcpu->arch.pmu.overflow_work); irq_work_sync(&vcpu->arch.pmu.overflow_work);
} }
@ -291,7 +291,7 @@ void kvm_pmu_enable_counter_mask(struct kvm_vcpu *vcpu, u64 val)
if (!(kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E) || !val) if (!(kvm_vcpu_read_pmcr(vcpu) & ARMV8_PMU_PMCR_E) || !val)
return; return;
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) { for (i = 0; i < KVM_ARMV8_PMU_MAX_COUNTERS; i++) {
struct kvm_pmc *pmc; struct kvm_pmc *pmc;
if (!(val & BIT(i))) if (!(val & BIT(i)))
@ -323,7 +323,7 @@ void kvm_pmu_disable_counter_mask(struct kvm_vcpu *vcpu, u64 val)
if (!kvm_vcpu_has_pmu(vcpu) || !val) if (!kvm_vcpu_has_pmu(vcpu) || !val)
return; return;
for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) { for (i = 0; i < KVM_ARMV8_PMU_MAX_COUNTERS; i++) {
struct kvm_pmc *pmc; struct kvm_pmc *pmc;
if (!(val & BIT(i))) if (!(val & BIT(i)))
@ -910,10 +910,10 @@ u8 kvm_arm_pmu_get_max_counters(struct kvm *kvm)
struct arm_pmu *arm_pmu = kvm->arch.arm_pmu; struct arm_pmu *arm_pmu = kvm->arch.arm_pmu;
/* /*
* The arm_pmu->num_events considers the cycle counter as well. * The arm_pmu->cntr_mask considers the fixed counter(s) as well.
* Ignore that and return only the general-purpose counters. * Ignore those and return only the general-purpose counters.
*/ */
return arm_pmu->num_events - 1; return bitmap_weight(arm_pmu->cntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS);
} }
static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu) static void kvm_arm_set_pmu(struct kvm *kvm, struct arm_pmu *arm_pmu)

View File

@ -5,6 +5,8 @@
*/ */
#include <linux/kvm_host.h> #include <linux/kvm_host.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <linux/perf/arm_pmu.h>
#include <linux/perf/arm_pmuv3.h>
static DEFINE_PER_CPU(struct kvm_pmu_events, kvm_pmu_events); static DEFINE_PER_CPU(struct kvm_pmu_events, kvm_pmu_events);
@ -35,7 +37,7 @@ struct kvm_pmu_events *kvm_get_pmu_events(void)
* Add events to track that we may want to switch at guest entry/exit * Add events to track that we may want to switch at guest entry/exit
* time. * time.
*/ */
void kvm_set_pmu_events(u32 set, struct perf_event_attr *attr) void kvm_set_pmu_events(u64 set, struct perf_event_attr *attr)
{ {
struct kvm_pmu_events *pmu = kvm_get_pmu_events(); struct kvm_pmu_events *pmu = kvm_get_pmu_events();
@ -51,7 +53,7 @@ void kvm_set_pmu_events(u32 set, struct perf_event_attr *attr)
/* /*
* Stop tracking events * Stop tracking events
*/ */
void kvm_clr_pmu_events(u32 clr) void kvm_clr_pmu_events(u64 clr)
{ {
struct kvm_pmu_events *pmu = kvm_get_pmu_events(); struct kvm_pmu_events *pmu = kvm_get_pmu_events();
@ -62,79 +64,32 @@ void kvm_clr_pmu_events(u32 clr)
pmu->events_guest &= ~clr; pmu->events_guest &= ~clr;
} }
#define PMEVTYPER_READ_CASE(idx) \
case idx: \
return read_sysreg(pmevtyper##idx##_el0)
#define PMEVTYPER_WRITE_CASE(idx) \
case idx: \
write_sysreg(val, pmevtyper##idx##_el0); \
break
#define PMEVTYPER_CASES(readwrite) \
PMEVTYPER_##readwrite##_CASE(0); \
PMEVTYPER_##readwrite##_CASE(1); \
PMEVTYPER_##readwrite##_CASE(2); \
PMEVTYPER_##readwrite##_CASE(3); \
PMEVTYPER_##readwrite##_CASE(4); \
PMEVTYPER_##readwrite##_CASE(5); \
PMEVTYPER_##readwrite##_CASE(6); \
PMEVTYPER_##readwrite##_CASE(7); \
PMEVTYPER_##readwrite##_CASE(8); \
PMEVTYPER_##readwrite##_CASE(9); \
PMEVTYPER_##readwrite##_CASE(10); \
PMEVTYPER_##readwrite##_CASE(11); \
PMEVTYPER_##readwrite##_CASE(12); \
PMEVTYPER_##readwrite##_CASE(13); \
PMEVTYPER_##readwrite##_CASE(14); \
PMEVTYPER_##readwrite##_CASE(15); \
PMEVTYPER_##readwrite##_CASE(16); \
PMEVTYPER_##readwrite##_CASE(17); \
PMEVTYPER_##readwrite##_CASE(18); \
PMEVTYPER_##readwrite##_CASE(19); \
PMEVTYPER_##readwrite##_CASE(20); \
PMEVTYPER_##readwrite##_CASE(21); \
PMEVTYPER_##readwrite##_CASE(22); \
PMEVTYPER_##readwrite##_CASE(23); \
PMEVTYPER_##readwrite##_CASE(24); \
PMEVTYPER_##readwrite##_CASE(25); \
PMEVTYPER_##readwrite##_CASE(26); \
PMEVTYPER_##readwrite##_CASE(27); \
PMEVTYPER_##readwrite##_CASE(28); \
PMEVTYPER_##readwrite##_CASE(29); \
PMEVTYPER_##readwrite##_CASE(30)
/* /*
* Read a value direct from PMEVTYPER<idx> where idx is 0-30 * Read a value direct from PMEVTYPER<idx> where idx is 0-30
* or PMCCFILTR_EL0 where idx is ARMV8_PMU_CYCLE_IDX (31). * or PMxCFILTR_EL0 where idx is 31-32.
*/ */
static u64 kvm_vcpu_pmu_read_evtype_direct(int idx) static u64 kvm_vcpu_pmu_read_evtype_direct(int idx)
{ {
switch (idx) { if (idx == ARMV8_PMU_CYCLE_IDX)
PMEVTYPER_CASES(READ); return read_pmccfiltr();
case ARMV8_PMU_CYCLE_IDX: else if (idx == ARMV8_PMU_INSTR_IDX)
return read_sysreg(pmccfiltr_el0); return read_pmicfiltr();
default:
WARN_ON(1);
}
return 0; return read_pmevtypern(idx);
} }
/* /*
* Write a value direct to PMEVTYPER<idx> where idx is 0-30 * Write a value direct to PMEVTYPER<idx> where idx is 0-30
* or PMCCFILTR_EL0 where idx is ARMV8_PMU_CYCLE_IDX (31). * or PMxCFILTR_EL0 where idx is 31-32.
*/ */
static void kvm_vcpu_pmu_write_evtype_direct(int idx, u32 val) static void kvm_vcpu_pmu_write_evtype_direct(int idx, u32 val)
{ {
switch (idx) { if (idx == ARMV8_PMU_CYCLE_IDX)
PMEVTYPER_CASES(WRITE); write_pmccfiltr(val);
case ARMV8_PMU_CYCLE_IDX: else if (idx == ARMV8_PMU_INSTR_IDX)
write_sysreg(val, pmccfiltr_el0); write_pmicfiltr(val);
break; else
default: write_pmevtypern(idx, val);
WARN_ON(1);
}
} }
/* /*
@ -145,7 +100,7 @@ static void kvm_vcpu_pmu_enable_el0(unsigned long events)
u64 typer; u64 typer;
u32 counter; u32 counter;
for_each_set_bit(counter, &events, 32) { for_each_set_bit(counter, &events, ARMPMU_MAX_HWEVENTS) {
typer = kvm_vcpu_pmu_read_evtype_direct(counter); typer = kvm_vcpu_pmu_read_evtype_direct(counter);
typer &= ~ARMV8_PMU_EXCLUDE_EL0; typer &= ~ARMV8_PMU_EXCLUDE_EL0;
kvm_vcpu_pmu_write_evtype_direct(counter, typer); kvm_vcpu_pmu_write_evtype_direct(counter, typer);
@ -160,7 +115,7 @@ static void kvm_vcpu_pmu_disable_el0(unsigned long events)
u64 typer; u64 typer;
u32 counter; u32 counter;
for_each_set_bit(counter, &events, 32) { for_each_set_bit(counter, &events, ARMPMU_MAX_HWEVENTS) {
typer = kvm_vcpu_pmu_read_evtype_direct(counter); typer = kvm_vcpu_pmu_read_evtype_direct(counter);
typer |= ARMV8_PMU_EXCLUDE_EL0; typer |= ARMV8_PMU_EXCLUDE_EL0;
kvm_vcpu_pmu_write_evtype_direct(counter, typer); kvm_vcpu_pmu_write_evtype_direct(counter, typer);
@ -176,7 +131,7 @@ static void kvm_vcpu_pmu_disable_el0(unsigned long events)
void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu) void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu)
{ {
struct kvm_pmu_events *pmu; struct kvm_pmu_events *pmu;
u32 events_guest, events_host; u64 events_guest, events_host;
if (!kvm_arm_support_pmu_v3() || !has_vhe()) if (!kvm_arm_support_pmu_v3() || !has_vhe())
return; return;
@ -197,7 +152,7 @@ void kvm_vcpu_pmu_restore_guest(struct kvm_vcpu *vcpu)
void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu) void kvm_vcpu_pmu_restore_host(struct kvm_vcpu *vcpu)
{ {
struct kvm_pmu_events *pmu; struct kvm_pmu_events *pmu;
u32 events_guest, events_host; u64 events_guest, events_host;
if (!kvm_arm_support_pmu_v3() || !has_vhe()) if (!kvm_arm_support_pmu_v3() || !has_vhe())
return; return;

View File

@ -18,6 +18,7 @@
#include <linux/printk.h> #include <linux/printk.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/arm_pmuv3.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/cputype.h> #include <asm/cputype.h>
#include <asm/debug-monitors.h> #include <asm/debug-monitors.h>
@ -887,7 +888,7 @@ static u64 reset_pmevtyper(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
static u64 reset_pmselr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r) static u64 reset_pmselr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
{ {
reset_unknown(vcpu, r); reset_unknown(vcpu, r);
__vcpu_sys_reg(vcpu, r->reg) &= ARMV8_PMU_COUNTER_MASK; __vcpu_sys_reg(vcpu, r->reg) &= PMSELR_EL0_SEL_MASK;
return __vcpu_sys_reg(vcpu, r->reg); return __vcpu_sys_reg(vcpu, r->reg);
} }
@ -979,7 +980,7 @@ static bool access_pmselr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
else else
/* return PMSELR.SEL field */ /* return PMSELR.SEL field */
p->regval = __vcpu_sys_reg(vcpu, PMSELR_EL0) p->regval = __vcpu_sys_reg(vcpu, PMSELR_EL0)
& ARMV8_PMU_COUNTER_MASK; & PMSELR_EL0_SEL_MASK;
return true; return true;
} }
@ -1047,8 +1048,8 @@ static bool access_pmu_evcntr(struct kvm_vcpu *vcpu,
if (pmu_access_event_counter_el0_disabled(vcpu)) if (pmu_access_event_counter_el0_disabled(vcpu))
return false; return false;
idx = __vcpu_sys_reg(vcpu, PMSELR_EL0) idx = SYS_FIELD_GET(PMSELR_EL0, SEL,
& ARMV8_PMU_COUNTER_MASK; __vcpu_sys_reg(vcpu, PMSELR_EL0));
} else if (r->Op2 == 0) { } else if (r->Op2 == 0) {
/* PMCCNTR_EL0 */ /* PMCCNTR_EL0 */
if (pmu_access_cycle_counter_el0_disabled(vcpu)) if (pmu_access_cycle_counter_el0_disabled(vcpu))
@ -1098,7 +1099,7 @@ static bool access_pmu_evtyper(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
if (r->CRn == 9 && r->CRm == 13 && r->Op2 == 1) { if (r->CRn == 9 && r->CRm == 13 && r->Op2 == 1) {
/* PMXEVTYPER_EL0 */ /* PMXEVTYPER_EL0 */
idx = __vcpu_sys_reg(vcpu, PMSELR_EL0) & ARMV8_PMU_COUNTER_MASK; idx = SYS_FIELD_GET(PMSELR_EL0, SEL, __vcpu_sys_reg(vcpu, PMSELR_EL0));
reg = PMEVTYPER0_EL0 + idx; reg = PMEVTYPER0_EL0 + idx;
} else if (r->CRn == 14 && (r->CRm & 12) == 12) { } else if (r->CRn == 14 && (r->CRm & 12) == 12) {
idx = ((r->CRm & 3) << 3) | (r->Op2 & 7); idx = ((r->CRm & 3) << 3) | (r->Op2 & 7);

View File

@ -2029,6 +2029,31 @@ Sysreg FAR_EL1 3 0 6 0 0
Field 63:0 ADDR Field 63:0 ADDR
EndSysreg EndSysreg
Sysreg PMICNTR_EL0 3 3 9 4 0
Field 63:0 ICNT
EndSysreg
Sysreg PMICFILTR_EL0 3 3 9 6 0
Res0 63:59
Field 58 SYNC
Field 57:56 VS
Res0 55:32
Field 31 P
Field 30 U
Field 29 NSK
Field 28 NSU
Field 27 NSH
Field 26 M
Res0 25
Field 24 SH
Field 23 T
Field 22 RLK
Field 21 RLU
Field 20 RLH
Res0 19:16
Field 15:0 evtCount
EndSysreg
Sysreg PMSCR_EL1 3 0 9 9 0 Sysreg PMSCR_EL1 3 0 9 9 0
Res0 63:8 Res0 63:8
Field 7:6 PCT Field 7:6 PCT
@ -2153,6 +2178,11 @@ Field 4 P
Field 3:0 ALIGN Field 3:0 ALIGN
EndSysreg EndSysreg
Sysreg PMSELR_EL0 3 3 9 12 5
Res0 63:5
Field 4:0 SEL
EndSysreg
SysregFields CONTEXTIDR_ELx SysregFields CONTEXTIDR_ELx
Res0 63:32 Res0 63:32
Field 31:0 PROCID Field 31:0 PROCID

View File

@ -48,6 +48,13 @@ config ARM_CMN
Support for PMU events monitoring on the Arm CMN-600 Coherent Mesh Support for PMU events monitoring on the Arm CMN-600 Coherent Mesh
Network interconnect. Network interconnect.
config ARM_NI
tristate "Arm NI-700 PMU support"
depends on ARM64 || COMPILE_TEST
help
Support for PMU events monitoring on the Arm NI-700 Network-on-Chip
interconnect and family.
config ARM_PMU config ARM_PMU
depends on ARM || ARM64 depends on ARM || ARM64
bool "ARM PMU framework" bool "ARM PMU framework"

View File

@ -3,6 +3,7 @@ obj-$(CONFIG_ARM_CCI_PMU) += arm-cci.o
obj-$(CONFIG_ARM_CCN) += arm-ccn.o obj-$(CONFIG_ARM_CCN) += arm-ccn.o
obj-$(CONFIG_ARM_CMN) += arm-cmn.o obj-$(CONFIG_ARM_CMN) += arm-cmn.o
obj-$(CONFIG_ARM_DSU_PMU) += arm_dsu_pmu.o obj-$(CONFIG_ARM_DSU_PMU) += arm_dsu_pmu.o
obj-$(CONFIG_ARM_NI) += arm-ni.o
obj-$(CONFIG_ARM_PMU) += arm_pmu.o arm_pmu_platform.o obj-$(CONFIG_ARM_PMU) += arm_pmu.o arm_pmu_platform.o
obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o obj-$(CONFIG_ARM_PMU_ACPI) += arm_pmu_acpi.o
obj-$(CONFIG_ARM_PMUV3) += arm_pmuv3.o obj-$(CONFIG_ARM_PMUV3) += arm_pmuv3.o

View File

@ -400,7 +400,7 @@ static irqreturn_t ali_drw_pmu_isr(int irq_num, void *data)
} }
/* clear common counter intr status */ /* clear common counter intr status */
clr_status = FIELD_PREP(ALI_DRW_PMCOM_CNT_OV_INTR_MASK, 1); clr_status = FIELD_PREP(ALI_DRW_PMCOM_CNT_OV_INTR_MASK, status);
writel(clr_status, writel(clr_status,
drw_pmu->cfg_base + ALI_DRW_PMU_OV_INTR_CLR); drw_pmu->cfg_base + ALI_DRW_PMU_OV_INTR_CLR);
} }

View File

@ -47,46 +47,79 @@
* implementations, we'll have to introduce per cpu-type tables. * implementations, we'll have to introduce per cpu-type tables.
*/ */
enum m1_pmu_events { enum m1_pmu_events {
M1_PMU_PERFCTR_UNKNOWN_01 = 0x01, M1_PMU_PERFCTR_RETIRE_UOP = 0x1,
M1_PMU_PERFCTR_CPU_CYCLES = 0x02, M1_PMU_PERFCTR_CORE_ACTIVE_CYCLE = 0x2,
M1_PMU_PERFCTR_INSTRUCTIONS = 0x8c, M1_PMU_PERFCTR_L1I_TLB_FILL = 0x4,
M1_PMU_PERFCTR_UNKNOWN_8d = 0x8d, M1_PMU_PERFCTR_L1D_TLB_FILL = 0x5,
M1_PMU_PERFCTR_UNKNOWN_8e = 0x8e, M1_PMU_PERFCTR_MMU_TABLE_WALK_INSTRUCTION = 0x7,
M1_PMU_PERFCTR_UNKNOWN_8f = 0x8f, M1_PMU_PERFCTR_MMU_TABLE_WALK_DATA = 0x8,
M1_PMU_PERFCTR_UNKNOWN_90 = 0x90, M1_PMU_PERFCTR_L2_TLB_MISS_INSTRUCTION = 0xa,
M1_PMU_PERFCTR_UNKNOWN_93 = 0x93, M1_PMU_PERFCTR_L2_TLB_MISS_DATA = 0xb,
M1_PMU_PERFCTR_UNKNOWN_94 = 0x94, M1_PMU_PERFCTR_MMU_VIRTUAL_MEMORY_FAULT_NONSPEC = 0xd,
M1_PMU_PERFCTR_UNKNOWN_95 = 0x95, M1_PMU_PERFCTR_SCHEDULE_UOP = 0x52,
M1_PMU_PERFCTR_UNKNOWN_96 = 0x96, M1_PMU_PERFCTR_INTERRUPT_PENDING = 0x6c,
M1_PMU_PERFCTR_UNKNOWN_97 = 0x97, M1_PMU_PERFCTR_MAP_STALL_DISPATCH = 0x70,
M1_PMU_PERFCTR_UNKNOWN_98 = 0x98, M1_PMU_PERFCTR_MAP_REWIND = 0x75,
M1_PMU_PERFCTR_UNKNOWN_99 = 0x99, M1_PMU_PERFCTR_MAP_STALL = 0x76,
M1_PMU_PERFCTR_UNKNOWN_9a = 0x9a, M1_PMU_PERFCTR_MAP_INT_UOP = 0x7c,
M1_PMU_PERFCTR_UNKNOWN_9b = 0x9b, M1_PMU_PERFCTR_MAP_LDST_UOP = 0x7d,
M1_PMU_PERFCTR_UNKNOWN_9c = 0x9c, M1_PMU_PERFCTR_MAP_SIMD_UOP = 0x7e,
M1_PMU_PERFCTR_UNKNOWN_9f = 0x9f, M1_PMU_PERFCTR_FLUSH_RESTART_OTHER_NONSPEC = 0x84,
M1_PMU_PERFCTR_UNKNOWN_bf = 0xbf, M1_PMU_PERFCTR_INST_ALL = 0x8c,
M1_PMU_PERFCTR_UNKNOWN_c0 = 0xc0, M1_PMU_PERFCTR_INST_BRANCH = 0x8d,
M1_PMU_PERFCTR_UNKNOWN_c1 = 0xc1, M1_PMU_PERFCTR_INST_BRANCH_CALL = 0x8e,
M1_PMU_PERFCTR_UNKNOWN_c4 = 0xc4, M1_PMU_PERFCTR_INST_BRANCH_RET = 0x8f,
M1_PMU_PERFCTR_UNKNOWN_c5 = 0xc5, M1_PMU_PERFCTR_INST_BRANCH_TAKEN = 0x90,
M1_PMU_PERFCTR_UNKNOWN_c6 = 0xc6, M1_PMU_PERFCTR_INST_BRANCH_INDIR = 0x93,
M1_PMU_PERFCTR_UNKNOWN_c8 = 0xc8, M1_PMU_PERFCTR_INST_BRANCH_COND = 0x94,
M1_PMU_PERFCTR_UNKNOWN_ca = 0xca, M1_PMU_PERFCTR_INST_INT_LD = 0x95,
M1_PMU_PERFCTR_UNKNOWN_cb = 0xcb, M1_PMU_PERFCTR_INST_INT_ST = 0x96,
M1_PMU_PERFCTR_UNKNOWN_f5 = 0xf5, M1_PMU_PERFCTR_INST_INT_ALU = 0x97,
M1_PMU_PERFCTR_UNKNOWN_f6 = 0xf6, M1_PMU_PERFCTR_INST_SIMD_LD = 0x98,
M1_PMU_PERFCTR_UNKNOWN_f7 = 0xf7, M1_PMU_PERFCTR_INST_SIMD_ST = 0x99,
M1_PMU_PERFCTR_UNKNOWN_f8 = 0xf8, M1_PMU_PERFCTR_INST_SIMD_ALU = 0x9a,
M1_PMU_PERFCTR_UNKNOWN_fd = 0xfd, M1_PMU_PERFCTR_INST_LDST = 0x9b,
M1_PMU_PERFCTR_LAST = M1_PMU_CFG_EVENT, M1_PMU_PERFCTR_INST_BARRIER = 0x9c,
M1_PMU_PERFCTR_UNKNOWN_9f = 0x9f,
M1_PMU_PERFCTR_L1D_TLB_ACCESS = 0xa0,
M1_PMU_PERFCTR_L1D_TLB_MISS = 0xa1,
M1_PMU_PERFCTR_L1D_CACHE_MISS_ST = 0xa2,
M1_PMU_PERFCTR_L1D_CACHE_MISS_LD = 0xa3,
M1_PMU_PERFCTR_LD_UNIT_UOP = 0xa6,
M1_PMU_PERFCTR_ST_UNIT_UOP = 0xa7,
M1_PMU_PERFCTR_L1D_CACHE_WRITEBACK = 0xa8,
M1_PMU_PERFCTR_LDST_X64_UOP = 0xb1,
M1_PMU_PERFCTR_LDST_XPG_UOP = 0xb2,
M1_PMU_PERFCTR_ATOMIC_OR_EXCLUSIVE_SUCC = 0xb3,
M1_PMU_PERFCTR_ATOMIC_OR_EXCLUSIVE_FAIL = 0xb4,
M1_PMU_PERFCTR_L1D_CACHE_MISS_LD_NONSPEC = 0xbf,
M1_PMU_PERFCTR_L1D_CACHE_MISS_ST_NONSPEC = 0xc0,
M1_PMU_PERFCTR_L1D_TLB_MISS_NONSPEC = 0xc1,
M1_PMU_PERFCTR_ST_MEMORY_ORDER_VIOLATION_NONSPEC = 0xc4,
M1_PMU_PERFCTR_BRANCH_COND_MISPRED_NONSPEC = 0xc5,
M1_PMU_PERFCTR_BRANCH_INDIR_MISPRED_NONSPEC = 0xc6,
M1_PMU_PERFCTR_BRANCH_RET_INDIR_MISPRED_NONSPEC = 0xc8,
M1_PMU_PERFCTR_BRANCH_CALL_INDIR_MISPRED_NONSPEC = 0xca,
M1_PMU_PERFCTR_BRANCH_MISPRED_NONSPEC = 0xcb,
M1_PMU_PERFCTR_L1I_TLB_MISS_DEMAND = 0xd4,
M1_PMU_PERFCTR_MAP_DISPATCH_BUBBLE = 0xd6,
M1_PMU_PERFCTR_L1I_CACHE_MISS_DEMAND = 0xdb,
M1_PMU_PERFCTR_FETCH_RESTART = 0xde,
M1_PMU_PERFCTR_ST_NT_UOP = 0xe5,
M1_PMU_PERFCTR_LD_NT_UOP = 0xe6,
M1_PMU_PERFCTR_UNKNOWN_f5 = 0xf5,
M1_PMU_PERFCTR_UNKNOWN_f6 = 0xf6,
M1_PMU_PERFCTR_UNKNOWN_f7 = 0xf7,
M1_PMU_PERFCTR_UNKNOWN_f8 = 0xf8,
M1_PMU_PERFCTR_UNKNOWN_fd = 0xfd,
M1_PMU_PERFCTR_LAST = M1_PMU_CFG_EVENT,
/* /*
* From this point onwards, these are not actual HW events, * From this point onwards, these are not actual HW events,
* but attributes that get stored in hw->config_base. * but attributes that get stored in hw->config_base.
*/ */
M1_PMU_CFG_COUNT_USER = BIT(8), M1_PMU_CFG_COUNT_USER = BIT(8),
M1_PMU_CFG_COUNT_KERNEL = BIT(9), M1_PMU_CFG_COUNT_KERNEL = BIT(9),
}; };
/* /*
@ -96,46 +129,45 @@ enum m1_pmu_events {
* counters had strange affinities. * counters had strange affinities.
*/ */
static const u16 m1_pmu_event_affinity[M1_PMU_PERFCTR_LAST + 1] = { static const u16 m1_pmu_event_affinity[M1_PMU_PERFCTR_LAST + 1] = {
[0 ... M1_PMU_PERFCTR_LAST] = ANY_BUT_0_1, [0 ... M1_PMU_PERFCTR_LAST] = ANY_BUT_0_1,
[M1_PMU_PERFCTR_UNKNOWN_01] = BIT(7), [M1_PMU_PERFCTR_RETIRE_UOP] = BIT(7),
[M1_PMU_PERFCTR_CPU_CYCLES] = ANY_BUT_0_1 | BIT(0), [M1_PMU_PERFCTR_CORE_ACTIVE_CYCLE] = ANY_BUT_0_1 | BIT(0),
[M1_PMU_PERFCTR_INSTRUCTIONS] = BIT(7) | BIT(1), [M1_PMU_PERFCTR_INST_ALL] = BIT(7) | BIT(1),
[M1_PMU_PERFCTR_UNKNOWN_8d] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_BRANCH] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_8e] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_BRANCH_CALL] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_8f] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_BRANCH_RET] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_90] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_BRANCH_TAKEN] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_93] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_BRANCH_INDIR] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_94] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_BRANCH_COND] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_95] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_INT_LD] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_96] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_INT_ST] = BIT(7),
[M1_PMU_PERFCTR_UNKNOWN_97] = BIT(7), [M1_PMU_PERFCTR_INST_INT_ALU] = BIT(7),
[M1_PMU_PERFCTR_UNKNOWN_98] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_SIMD_LD] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_99] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_SIMD_ST] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_9a] = BIT(7), [M1_PMU_PERFCTR_INST_SIMD_ALU] = BIT(7),
[M1_PMU_PERFCTR_UNKNOWN_9b] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_LDST] = BIT(7),
[M1_PMU_PERFCTR_UNKNOWN_9c] = ONLY_5_6_7, [M1_PMU_PERFCTR_INST_BARRIER] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_9f] = BIT(7), [M1_PMU_PERFCTR_UNKNOWN_9f] = BIT(7),
[M1_PMU_PERFCTR_UNKNOWN_bf] = ONLY_5_6_7, [M1_PMU_PERFCTR_L1D_CACHE_MISS_LD_NONSPEC] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_c0] = ONLY_5_6_7, [M1_PMU_PERFCTR_L1D_CACHE_MISS_ST_NONSPEC] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_c1] = ONLY_5_6_7, [M1_PMU_PERFCTR_L1D_TLB_MISS_NONSPEC] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_c4] = ONLY_5_6_7, [M1_PMU_PERFCTR_ST_MEMORY_ORDER_VIOLATION_NONSPEC] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_c5] = ONLY_5_6_7, [M1_PMU_PERFCTR_BRANCH_COND_MISPRED_NONSPEC] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_c6] = ONLY_5_6_7, [M1_PMU_PERFCTR_BRANCH_INDIR_MISPRED_NONSPEC] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_c8] = ONLY_5_6_7, [M1_PMU_PERFCTR_BRANCH_RET_INDIR_MISPRED_NONSPEC] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_ca] = ONLY_5_6_7, [M1_PMU_PERFCTR_BRANCH_CALL_INDIR_MISPRED_NONSPEC] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_cb] = ONLY_5_6_7, [M1_PMU_PERFCTR_BRANCH_MISPRED_NONSPEC] = ONLY_5_6_7,
[M1_PMU_PERFCTR_UNKNOWN_f5] = ONLY_2_4_6, [M1_PMU_PERFCTR_UNKNOWN_f5] = ONLY_2_4_6,
[M1_PMU_PERFCTR_UNKNOWN_f6] = ONLY_2_4_6, [M1_PMU_PERFCTR_UNKNOWN_f6] = ONLY_2_4_6,
[M1_PMU_PERFCTR_UNKNOWN_f7] = ONLY_2_4_6, [M1_PMU_PERFCTR_UNKNOWN_f7] = ONLY_2_4_6,
[M1_PMU_PERFCTR_UNKNOWN_f8] = ONLY_2_TO_7, [M1_PMU_PERFCTR_UNKNOWN_f8] = ONLY_2_TO_7,
[M1_PMU_PERFCTR_UNKNOWN_fd] = ONLY_2_4_6, [M1_PMU_PERFCTR_UNKNOWN_fd] = ONLY_2_4_6,
}; };
static const unsigned m1_pmu_perf_map[PERF_COUNT_HW_MAX] = { static const unsigned m1_pmu_perf_map[PERF_COUNT_HW_MAX] = {
PERF_MAP_ALL_UNSUPPORTED, PERF_MAP_ALL_UNSUPPORTED,
[PERF_COUNT_HW_CPU_CYCLES] = M1_PMU_PERFCTR_CPU_CYCLES, [PERF_COUNT_HW_CPU_CYCLES] = M1_PMU_PERFCTR_CORE_ACTIVE_CYCLE,
[PERF_COUNT_HW_INSTRUCTIONS] = M1_PMU_PERFCTR_INSTRUCTIONS, [PERF_COUNT_HW_INSTRUCTIONS] = M1_PMU_PERFCTR_INST_ALL,
/* No idea about the rest yet */
}; };
/* sysfs definitions */ /* sysfs definitions */
@ -154,8 +186,8 @@ static ssize_t m1_pmu_events_sysfs_show(struct device *dev,
PMU_EVENT_ATTR_ID(name, m1_pmu_events_sysfs_show, config) PMU_EVENT_ATTR_ID(name, m1_pmu_events_sysfs_show, config)
static struct attribute *m1_pmu_event_attrs[] = { static struct attribute *m1_pmu_event_attrs[] = {
M1_PMU_EVENT_ATTR(cycles, M1_PMU_PERFCTR_CPU_CYCLES), M1_PMU_EVENT_ATTR(cycles, M1_PMU_PERFCTR_CORE_ACTIVE_CYCLE),
M1_PMU_EVENT_ATTR(instructions, M1_PMU_PERFCTR_INSTRUCTIONS), M1_PMU_EVENT_ATTR(instructions, M1_PMU_PERFCTR_INST_ALL),
NULL, NULL,
}; };
@ -400,7 +432,7 @@ static irqreturn_t m1_pmu_handle_irq(struct arm_pmu *cpu_pmu)
regs = get_irq_regs(); regs = get_irq_regs();
for (idx = 0; idx < cpu_pmu->num_events; idx++) { for_each_set_bit(idx, cpu_pmu->cntr_mask, M1_PMU_NR_COUNTERS) {
struct perf_event *event = cpuc->events[idx]; struct perf_event *event = cpuc->events[idx];
struct perf_sample_data data; struct perf_sample_data data;
@ -560,7 +592,7 @@ static int m1_pmu_init(struct arm_pmu *cpu_pmu, u32 flags)
cpu_pmu->reset = m1_pmu_reset; cpu_pmu->reset = m1_pmu_reset;
cpu_pmu->set_event_filter = m1_pmu_set_event_filter; cpu_pmu->set_event_filter = m1_pmu_set_event_filter;
cpu_pmu->num_events = M1_PMU_NR_COUNTERS; bitmap_set(cpu_pmu->cntr_mask, 0, M1_PMU_NR_COUNTERS);
cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_EVENTS] = &m1_pmu_events_attr_group; cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_EVENTS] = &m1_pmu_events_attr_group;
cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = &m1_pmu_format_attr_group; cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = &m1_pmu_format_attr_group;
return 0; return 0;

View File

@ -24,14 +24,6 @@
#define CMN_NI_NODE_ID GENMASK_ULL(31, 16) #define CMN_NI_NODE_ID GENMASK_ULL(31, 16)
#define CMN_NI_LOGICAL_ID GENMASK_ULL(47, 32) #define CMN_NI_LOGICAL_ID GENMASK_ULL(47, 32)
#define CMN_NODEID_DEVID(reg) ((reg) & 3)
#define CMN_NODEID_EXT_DEVID(reg) ((reg) & 1)
#define CMN_NODEID_PID(reg) (((reg) >> 2) & 1)
#define CMN_NODEID_EXT_PID(reg) (((reg) >> 1) & 3)
#define CMN_NODEID_1x1_PID(reg) (((reg) >> 2) & 7)
#define CMN_NODEID_X(reg, bits) ((reg) >> (3 + (bits)))
#define CMN_NODEID_Y(reg, bits) (((reg) >> 3) & ((1U << (bits)) - 1))
#define CMN_CHILD_INFO 0x0080 #define CMN_CHILD_INFO 0x0080
#define CMN_CI_CHILD_COUNT GENMASK_ULL(15, 0) #define CMN_CI_CHILD_COUNT GENMASK_ULL(15, 0)
#define CMN_CI_CHILD_PTR_OFFSET GENMASK_ULL(31, 16) #define CMN_CI_CHILD_PTR_OFFSET GENMASK_ULL(31, 16)
@ -43,6 +35,9 @@
#define CMN_MAX_XPS (CMN_MAX_DIMENSION * CMN_MAX_DIMENSION) #define CMN_MAX_XPS (CMN_MAX_DIMENSION * CMN_MAX_DIMENSION)
#define CMN_MAX_DTMS (CMN_MAX_XPS + (CMN_MAX_DIMENSION - 1) * 4) #define CMN_MAX_DTMS (CMN_MAX_XPS + (CMN_MAX_DIMENSION - 1) * 4)
/* Currently XPs are the node type we can have most of; others top out at 128 */
#define CMN_MAX_NODES_PER_EVENT CMN_MAX_XPS
/* The CFG node has various info besides the discovery tree */ /* The CFG node has various info besides the discovery tree */
#define CMN_CFGM_PERIPH_ID_01 0x0008 #define CMN_CFGM_PERIPH_ID_01 0x0008
#define CMN_CFGM_PID0_PART_0 GENMASK_ULL(7, 0) #define CMN_CFGM_PID0_PART_0 GENMASK_ULL(7, 0)
@ -50,24 +45,28 @@
#define CMN_CFGM_PERIPH_ID_23 0x0010 #define CMN_CFGM_PERIPH_ID_23 0x0010
#define CMN_CFGM_PID2_REVISION GENMASK_ULL(7, 4) #define CMN_CFGM_PID2_REVISION GENMASK_ULL(7, 4)
#define CMN_CFGM_INFO_GLOBAL 0x900 #define CMN_CFGM_INFO_GLOBAL 0x0900
#define CMN_INFO_MULTIPLE_DTM_EN BIT_ULL(63) #define CMN_INFO_MULTIPLE_DTM_EN BIT_ULL(63)
#define CMN_INFO_RSP_VC_NUM GENMASK_ULL(53, 52) #define CMN_INFO_RSP_VC_NUM GENMASK_ULL(53, 52)
#define CMN_INFO_DAT_VC_NUM GENMASK_ULL(51, 50) #define CMN_INFO_DAT_VC_NUM GENMASK_ULL(51, 50)
#define CMN_INFO_DEVICE_ISO_ENABLE BIT_ULL(44)
#define CMN_CFGM_INFO_GLOBAL_1 0x908 #define CMN_CFGM_INFO_GLOBAL_1 0x0908
#define CMN_INFO_SNP_VC_NUM GENMASK_ULL(3, 2) #define CMN_INFO_SNP_VC_NUM GENMASK_ULL(3, 2)
#define CMN_INFO_REQ_VC_NUM GENMASK_ULL(1, 0) #define CMN_INFO_REQ_VC_NUM GENMASK_ULL(1, 0)
/* XPs also have some local topology info which has uses too */ /* XPs also have some local topology info which has uses too */
#define CMN_MXP__CONNECT_INFO(p) (0x0008 + 8 * (p)) #define CMN_MXP__CONNECT_INFO(p) (0x0008 + 8 * (p))
#define CMN__CONNECT_INFO_DEVICE_TYPE GENMASK_ULL(4, 0) #define CMN__CONNECT_INFO_DEVICE_TYPE GENMASK_ULL(5, 0)
#define CMN_MAX_PORTS 6 #define CMN_MAX_PORTS 6
#define CI700_CONNECT_INFO_P2_5_OFFSET 0x10 #define CI700_CONNECT_INFO_P2_5_OFFSET 0x10
/* PMU registers occupy the 3rd 4KB page of each node's region */ /* PMU registers occupy the 3rd 4KB page of each node's region */
#define CMN_PMU_OFFSET 0x2000 #define CMN_PMU_OFFSET 0x2000
/* ...except when they don't :( */
#define CMN_S3_DTM_OFFSET 0xa000
#define CMN_S3_PMU_OFFSET 0xd900
/* For most nodes, this is all there is */ /* For most nodes, this is all there is */
#define CMN_PMU_EVENT_SEL 0x000 #define CMN_PMU_EVENT_SEL 0x000
@ -78,7 +77,8 @@
/* Technically this is 4 bits wide on DNs, but we only use 2 there anyway */ /* Technically this is 4 bits wide on DNs, but we only use 2 there anyway */
#define CMN__PMU_OCCUP1_ID GENMASK_ULL(34, 32) #define CMN__PMU_OCCUP1_ID GENMASK_ULL(34, 32)
/* HN-Ps are weird... */ /* Some types are designed to coexist with another device in the same node */
#define CMN_CCLA_PMU_EVENT_SEL 0x008
#define CMN_HNP_PMU_EVENT_SEL 0x008 #define CMN_HNP_PMU_EVENT_SEL 0x008
/* DTMs live in the PMU space of XP registers */ /* DTMs live in the PMU space of XP registers */
@ -123,27 +123,28 @@
/* The DTC node is where the magic happens */ /* The DTC node is where the magic happens */
#define CMN_DT_DTC_CTL 0x0a00 #define CMN_DT_DTC_CTL 0x0a00
#define CMN_DT_DTC_CTL_DT_EN BIT(0) #define CMN_DT_DTC_CTL_DT_EN BIT(0)
#define CMN_DT_DTC_CTL_CG_DISABLE BIT(10)
/* DTC counters are paired in 64-bit registers on a 16-byte stride. Yuck */ /* DTC counters are paired in 64-bit registers on a 16-byte stride. Yuck */
#define _CMN_DT_CNT_REG(n) ((((n) / 2) * 4 + (n) % 2) * 4) #define _CMN_DT_CNT_REG(n) ((((n) / 2) * 4 + (n) % 2) * 4)
#define CMN_DT_PMEVCNT(n) (CMN_PMU_OFFSET + _CMN_DT_CNT_REG(n)) #define CMN_DT_PMEVCNT(dtc, n) ((dtc)->pmu_base + _CMN_DT_CNT_REG(n))
#define CMN_DT_PMCCNTR (CMN_PMU_OFFSET + 0x40) #define CMN_DT_PMCCNTR(dtc) ((dtc)->pmu_base + 0x40)
#define CMN_DT_PMEVCNTSR(n) (CMN_PMU_OFFSET + 0x50 + _CMN_DT_CNT_REG(n)) #define CMN_DT_PMEVCNTSR(dtc, n) ((dtc)->pmu_base + 0x50 + _CMN_DT_CNT_REG(n))
#define CMN_DT_PMCCNTRSR (CMN_PMU_OFFSET + 0x90) #define CMN_DT_PMCCNTRSR(dtc) ((dtc)->pmu_base + 0x90)
#define CMN_DT_PMCR (CMN_PMU_OFFSET + 0x100) #define CMN_DT_PMCR(dtc) ((dtc)->pmu_base + 0x100)
#define CMN_DT_PMCR_PMU_EN BIT(0) #define CMN_DT_PMCR_PMU_EN BIT(0)
#define CMN_DT_PMCR_CNTR_RST BIT(5) #define CMN_DT_PMCR_CNTR_RST BIT(5)
#define CMN_DT_PMCR_OVFL_INTR_EN BIT(6) #define CMN_DT_PMCR_OVFL_INTR_EN BIT(6)
#define CMN_DT_PMOVSR (CMN_PMU_OFFSET + 0x118) #define CMN_DT_PMOVSR(dtc) ((dtc)->pmu_base + 0x118)
#define CMN_DT_PMOVSR_CLR (CMN_PMU_OFFSET + 0x120) #define CMN_DT_PMOVSR_CLR(dtc) ((dtc)->pmu_base + 0x120)
#define CMN_DT_PMSSR (CMN_PMU_OFFSET + 0x128) #define CMN_DT_PMSSR(dtc) ((dtc)->pmu_base + 0x128)
#define CMN_DT_PMSSR_SS_STATUS(n) BIT(n) #define CMN_DT_PMSSR_SS_STATUS(n) BIT(n)
#define CMN_DT_PMSRR (CMN_PMU_OFFSET + 0x130) #define CMN_DT_PMSRR(dtc) ((dtc)->pmu_base + 0x130)
#define CMN_DT_PMSRR_SS_REQ BIT(0) #define CMN_DT_PMSRR_SS_REQ BIT(0)
#define CMN_DT_NUM_COUNTERS 8 #define CMN_DT_NUM_COUNTERS 8
@ -198,10 +199,11 @@ enum cmn_model {
CMN650 = 2, CMN650 = 2,
CMN700 = 4, CMN700 = 4,
CI700 = 8, CI700 = 8,
CMNS3 = 16,
/* ...and then we can use bitmap tricks for commonality */ /* ...and then we can use bitmap tricks for commonality */
CMN_ANY = -1, CMN_ANY = -1,
NOT_CMN600 = -2, NOT_CMN600 = -2,
CMN_650ON = CMN650 | CMN700, CMN_650ON = CMN650 | CMN700 | CMNS3,
}; };
/* Actual part numbers and revision IDs defined by the hardware */ /* Actual part numbers and revision IDs defined by the hardware */
@ -210,6 +212,7 @@ enum cmn_part {
PART_CMN650 = 0x436, PART_CMN650 = 0x436,
PART_CMN700 = 0x43c, PART_CMN700 = 0x43c,
PART_CI700 = 0x43a, PART_CI700 = 0x43a,
PART_CMN_S3 = 0x43e,
}; };
/* CMN-600 r0px shouldn't exist in silicon, thankfully */ /* CMN-600 r0px shouldn't exist in silicon, thankfully */
@ -261,6 +264,7 @@ enum cmn_node_type {
CMN_TYPE_HNS = 0x200, CMN_TYPE_HNS = 0x200,
CMN_TYPE_HNS_MPAM_S, CMN_TYPE_HNS_MPAM_S,
CMN_TYPE_HNS_MPAM_NS, CMN_TYPE_HNS_MPAM_NS,
CMN_TYPE_APB = 0x1000,
/* Not a real node type */ /* Not a real node type */
CMN_TYPE_WP = 0x7770 CMN_TYPE_WP = 0x7770
}; };
@ -280,8 +284,11 @@ struct arm_cmn_node {
u16 id, logid; u16 id, logid;
enum cmn_node_type type; enum cmn_node_type type;
/* XP properties really, but replicated to children for convenience */
u8 dtm; u8 dtm;
s8 dtc; s8 dtc;
u8 portid_bits:4;
u8 deviceid_bits:4;
/* DN/HN-F/CXHA */ /* DN/HN-F/CXHA */
struct { struct {
u8 val : 4; u8 val : 4;
@ -307,8 +314,9 @@ struct arm_cmn_dtm {
struct arm_cmn_dtc { struct arm_cmn_dtc {
void __iomem *base; void __iomem *base;
void __iomem *pmu_base;
int irq; int irq;
int irq_friend; s8 irq_friend;
bool cc_active; bool cc_active;
struct perf_event *counters[CMN_DT_NUM_COUNTERS]; struct perf_event *counters[CMN_DT_NUM_COUNTERS];
@ -357,49 +365,33 @@ struct arm_cmn {
static int arm_cmn_hp_state; static int arm_cmn_hp_state;
struct arm_cmn_nodeid { struct arm_cmn_nodeid {
u8 x;
u8 y;
u8 port; u8 port;
u8 dev; u8 dev;
}; };
static int arm_cmn_xyidbits(const struct arm_cmn *cmn) static int arm_cmn_xyidbits(const struct arm_cmn *cmn)
{ {
return fls((cmn->mesh_x - 1) | (cmn->mesh_y - 1) | 2); return fls((cmn->mesh_x - 1) | (cmn->mesh_y - 1));
} }
static struct arm_cmn_nodeid arm_cmn_nid(const struct arm_cmn *cmn, u16 id) static struct arm_cmn_nodeid arm_cmn_nid(const struct arm_cmn_node *dn)
{ {
struct arm_cmn_nodeid nid; struct arm_cmn_nodeid nid;
if (cmn->num_xps == 1) { nid.dev = dn->id & ((1U << dn->deviceid_bits) - 1);
nid.x = 0; nid.port = (dn->id >> dn->deviceid_bits) & ((1U << dn->portid_bits) - 1);
nid.y = 0;
nid.port = CMN_NODEID_1x1_PID(id);
nid.dev = CMN_NODEID_DEVID(id);
} else {
int bits = arm_cmn_xyidbits(cmn);
nid.x = CMN_NODEID_X(id, bits);
nid.y = CMN_NODEID_Y(id, bits);
if (cmn->ports_used & 0xc) {
nid.port = CMN_NODEID_EXT_PID(id);
nid.dev = CMN_NODEID_EXT_DEVID(id);
} else {
nid.port = CMN_NODEID_PID(id);
nid.dev = CMN_NODEID_DEVID(id);
}
}
return nid; return nid;
} }
static struct arm_cmn_node *arm_cmn_node_to_xp(const struct arm_cmn *cmn, static struct arm_cmn_node *arm_cmn_node_to_xp(const struct arm_cmn *cmn,
const struct arm_cmn_node *dn) const struct arm_cmn_node *dn)
{ {
struct arm_cmn_nodeid nid = arm_cmn_nid(cmn, dn->id); int id = dn->id >> (dn->portid_bits + dn->deviceid_bits);
int xp_idx = cmn->mesh_x * nid.y + nid.x; int bits = arm_cmn_xyidbits(cmn);
int x = id >> bits;
int y = id & ((1U << bits) - 1);
return cmn->xps + xp_idx; return cmn->xps + cmn->mesh_x * y + x;
} }
static struct arm_cmn_node *arm_cmn_node(const struct arm_cmn *cmn, static struct arm_cmn_node *arm_cmn_node(const struct arm_cmn *cmn,
enum cmn_node_type type) enum cmn_node_type type)
@ -423,15 +415,27 @@ static enum cmn_model arm_cmn_model(const struct arm_cmn *cmn)
return CMN700; return CMN700;
case PART_CI700: case PART_CI700:
return CI700; return CI700;
case PART_CMN_S3:
return CMNS3;
default: default:
return 0; return 0;
}; };
} }
static int arm_cmn_pmu_offset(const struct arm_cmn *cmn, const struct arm_cmn_node *dn)
{
if (cmn->part == PART_CMN_S3) {
if (dn->type == CMN_TYPE_XP)
return CMN_S3_DTM_OFFSET;
return CMN_S3_PMU_OFFSET;
}
return CMN_PMU_OFFSET;
}
static u32 arm_cmn_device_connect_info(const struct arm_cmn *cmn, static u32 arm_cmn_device_connect_info(const struct arm_cmn *cmn,
const struct arm_cmn_node *xp, int port) const struct arm_cmn_node *xp, int port)
{ {
int offset = CMN_MXP__CONNECT_INFO(port); int offset = CMN_MXP__CONNECT_INFO(port) - arm_cmn_pmu_offset(cmn, xp);
if (port >= 2) { if (port >= 2) {
if (cmn->part == PART_CMN600 || cmn->part == PART_CMN650) if (cmn->part == PART_CMN600 || cmn->part == PART_CMN650)
@ -444,7 +448,7 @@ static u32 arm_cmn_device_connect_info(const struct arm_cmn *cmn,
offset += CI700_CONNECT_INFO_P2_5_OFFSET; offset += CI700_CONNECT_INFO_P2_5_OFFSET;
} }
return readl_relaxed(xp->pmu_base - CMN_PMU_OFFSET + offset); return readl_relaxed(xp->pmu_base + offset);
} }
static struct dentry *arm_cmn_debugfs; static struct dentry *arm_cmn_debugfs;
@ -478,20 +482,25 @@ static const char *arm_cmn_device_type(u8 type)
case 0x17: return "RN-F_C_E|"; case 0x17: return "RN-F_C_E|";
case 0x18: return " RN-F_E |"; case 0x18: return " RN-F_E |";
case 0x19: return "RN-F_E_E|"; case 0x19: return "RN-F_E_E|";
case 0x1a: return " HN-S |";
case 0x1b: return " LCN |";
case 0x1c: return " MTSX |"; case 0x1c: return " MTSX |";
case 0x1d: return " HN-V |"; case 0x1d: return " HN-V |";
case 0x1e: return " CCG |"; case 0x1e: return " CCG |";
case 0x20: return " RN-F_F |";
case 0x21: return "RN-F_F_E|";
case 0x22: return " SN-F_F |";
default: return " ???? |"; default: return " ???? |";
} }
} }
static void arm_cmn_show_logid(struct seq_file *s, int x, int y, int p, int d) static void arm_cmn_show_logid(struct seq_file *s, const struct arm_cmn_node *xp, int p, int d)
{ {
struct arm_cmn *cmn = s->private; struct arm_cmn *cmn = s->private;
struct arm_cmn_node *dn; struct arm_cmn_node *dn;
u16 id = xp->id | d | (p << xp->deviceid_bits);
for (dn = cmn->dns; dn->type; dn++) { for (dn = cmn->dns; dn->type; dn++) {
struct arm_cmn_nodeid nid = arm_cmn_nid(cmn, dn->id);
int pad = dn->logid < 10; int pad = dn->logid < 10;
if (dn->type == CMN_TYPE_XP) if (dn->type == CMN_TYPE_XP)
@ -500,7 +509,7 @@ static void arm_cmn_show_logid(struct seq_file *s, int x, int y, int p, int d)
if (dn->type < CMN_TYPE_HNI) if (dn->type < CMN_TYPE_HNI)
continue; continue;
if (nid.x != x || nid.y != y || nid.port != p || nid.dev != d) if (dn->id != id)
continue; continue;
seq_printf(s, " %*c#%-*d |", pad + 1, ' ', 3 - pad, dn->logid); seq_printf(s, " %*c#%-*d |", pad + 1, ' ', 3 - pad, dn->logid);
@ -521,6 +530,7 @@ static int arm_cmn_map_show(struct seq_file *s, void *data)
y = cmn->mesh_y; y = cmn->mesh_y;
while (y--) { while (y--) {
int xp_base = cmn->mesh_x * y; int xp_base = cmn->mesh_x * y;
struct arm_cmn_node *xp = cmn->xps + xp_base;
u8 port[CMN_MAX_PORTS][CMN_MAX_DIMENSION]; u8 port[CMN_MAX_PORTS][CMN_MAX_DIMENSION];
for (x = 0; x < cmn->mesh_x; x++) for (x = 0; x < cmn->mesh_x; x++)
@ -528,16 +538,14 @@ static int arm_cmn_map_show(struct seq_file *s, void *data)
seq_printf(s, "\n%-2d |", y); seq_printf(s, "\n%-2d |", y);
for (x = 0; x < cmn->mesh_x; x++) { for (x = 0; x < cmn->mesh_x; x++) {
struct arm_cmn_node *xp = cmn->xps + xp_base + x;
for (p = 0; p < CMN_MAX_PORTS; p++) for (p = 0; p < CMN_MAX_PORTS; p++)
port[p][x] = arm_cmn_device_connect_info(cmn, xp, p); port[p][x] = arm_cmn_device_connect_info(cmn, xp + x, p);
seq_printf(s, " XP #%-3d|", xp_base + x); seq_printf(s, " XP #%-3d|", xp_base + x);
} }
seq_puts(s, "\n |"); seq_puts(s, "\n |");
for (x = 0; x < cmn->mesh_x; x++) { for (x = 0; x < cmn->mesh_x; x++) {
s8 dtc = cmn->xps[xp_base + x].dtc; s8 dtc = xp[x].dtc;
if (dtc < 0) if (dtc < 0)
seq_puts(s, " DTC ?? |"); seq_puts(s, " DTC ?? |");
@ -554,10 +562,10 @@ static int arm_cmn_map_show(struct seq_file *s, void *data)
seq_puts(s, arm_cmn_device_type(port[p][x])); seq_puts(s, arm_cmn_device_type(port[p][x]));
seq_puts(s, "\n 0|"); seq_puts(s, "\n 0|");
for (x = 0; x < cmn->mesh_x; x++) for (x = 0; x < cmn->mesh_x; x++)
arm_cmn_show_logid(s, x, y, p, 0); arm_cmn_show_logid(s, xp + x, p, 0);
seq_puts(s, "\n 1|"); seq_puts(s, "\n 1|");
for (x = 0; x < cmn->mesh_x; x++) for (x = 0; x < cmn->mesh_x; x++)
arm_cmn_show_logid(s, x, y, p, 1); arm_cmn_show_logid(s, xp + x, p, 1);
} }
seq_puts(s, "\n-----+"); seq_puts(s, "\n-----+");
} }
@ -585,7 +593,7 @@ static void arm_cmn_debugfs_init(struct arm_cmn *cmn, int id) {}
struct arm_cmn_hw_event { struct arm_cmn_hw_event {
struct arm_cmn_node *dn; struct arm_cmn_node *dn;
u64 dtm_idx[4]; u64 dtm_idx[DIV_ROUND_UP(CMN_MAX_NODES_PER_EVENT * 2, 64)];
s8 dtc_idx[CMN_MAX_DTCS]; s8 dtc_idx[CMN_MAX_DTCS];
u8 num_dns; u8 num_dns;
u8 dtm_offset; u8 dtm_offset;
@ -599,6 +607,7 @@ struct arm_cmn_hw_event {
bool wide_sel; bool wide_sel;
enum cmn_filter_select filter_sel; enum cmn_filter_select filter_sel;
}; };
static_assert(sizeof(struct arm_cmn_hw_event) <= offsetof(struct hw_perf_event, target));
#define for_each_hw_dn(hw, dn, i) \ #define for_each_hw_dn(hw, dn, i) \
for (i = 0, dn = hw->dn; i < hw->num_dns; i++, dn++) for (i = 0, dn = hw->dn; i < hw->num_dns; i++, dn++)
@ -609,7 +618,6 @@ struct arm_cmn_hw_event {
static struct arm_cmn_hw_event *to_cmn_hw(struct perf_event *event) static struct arm_cmn_hw_event *to_cmn_hw(struct perf_event *event)
{ {
BUILD_BUG_ON(sizeof(struct arm_cmn_hw_event) > offsetof(struct hw_perf_event, target));
return (struct arm_cmn_hw_event *)&event->hw; return (struct arm_cmn_hw_event *)&event->hw;
} }
@ -790,8 +798,8 @@ static umode_t arm_cmn_event_attr_is_visible(struct kobject *kobj,
CMN_EVENT_ATTR(CMN_ANY, cxha_##_name, CMN_TYPE_CXHA, _event) CMN_EVENT_ATTR(CMN_ANY, cxha_##_name, CMN_TYPE_CXHA, _event)
#define CMN_EVENT_CCRA(_name, _event) \ #define CMN_EVENT_CCRA(_name, _event) \
CMN_EVENT_ATTR(CMN_ANY, ccra_##_name, CMN_TYPE_CCRA, _event) CMN_EVENT_ATTR(CMN_ANY, ccra_##_name, CMN_TYPE_CCRA, _event)
#define CMN_EVENT_CCHA(_name, _event) \ #define CMN_EVENT_CCHA(_model, _name, _event) \
CMN_EVENT_ATTR(CMN_ANY, ccha_##_name, CMN_TYPE_CCHA, _event) CMN_EVENT_ATTR(_model, ccha_##_name, CMN_TYPE_CCHA, _event)
#define CMN_EVENT_CCLA(_name, _event) \ #define CMN_EVENT_CCLA(_name, _event) \
CMN_EVENT_ATTR(CMN_ANY, ccla_##_name, CMN_TYPE_CCLA, _event) CMN_EVENT_ATTR(CMN_ANY, ccla_##_name, CMN_TYPE_CCLA, _event)
#define CMN_EVENT_CCLA_RNI(_name, _event) \ #define CMN_EVENT_CCLA_RNI(_name, _event) \
@ -1149,42 +1157,43 @@ static struct attribute *arm_cmn_event_attrs[] = {
CMN_EVENT_CCRA(wdb_alloc, 0x59), CMN_EVENT_CCRA(wdb_alloc, 0x59),
CMN_EVENT_CCRA(ssb_alloc, 0x5a), CMN_EVENT_CCRA(ssb_alloc, 0x5a),
CMN_EVENT_CCHA(rddatbyp, 0x61), CMN_EVENT_CCHA(CMN_ANY, rddatbyp, 0x61),
CMN_EVENT_CCHA(chirsp_up_stall, 0x62), CMN_EVENT_CCHA(CMN_ANY, chirsp_up_stall, 0x62),
CMN_EVENT_CCHA(chidat_up_stall, 0x63), CMN_EVENT_CCHA(CMN_ANY, chidat_up_stall, 0x63),
CMN_EVENT_CCHA(snppcrd_link0_stall, 0x64), CMN_EVENT_CCHA(CMN_ANY, snppcrd_link0_stall, 0x64),
CMN_EVENT_CCHA(snppcrd_link1_stall, 0x65), CMN_EVENT_CCHA(CMN_ANY, snppcrd_link1_stall, 0x65),
CMN_EVENT_CCHA(snppcrd_link2_stall, 0x66), CMN_EVENT_CCHA(CMN_ANY, snppcrd_link2_stall, 0x66),
CMN_EVENT_CCHA(reqtrk_occ, 0x67), CMN_EVENT_CCHA(CMN_ANY, reqtrk_occ, 0x67),
CMN_EVENT_CCHA(rdb_occ, 0x68), CMN_EVENT_CCHA(CMN_ANY, rdb_occ, 0x68),
CMN_EVENT_CCHA(rdbyp_occ, 0x69), CMN_EVENT_CCHA(CMN_ANY, rdbyp_occ, 0x69),
CMN_EVENT_CCHA(wdb_occ, 0x6a), CMN_EVENT_CCHA(CMN_ANY, wdb_occ, 0x6a),
CMN_EVENT_CCHA(snptrk_occ, 0x6b), CMN_EVENT_CCHA(CMN_ANY, snptrk_occ, 0x6b),
CMN_EVENT_CCHA(sdb_occ, 0x6c), CMN_EVENT_CCHA(CMN_ANY, sdb_occ, 0x6c),
CMN_EVENT_CCHA(snphaz_occ, 0x6d), CMN_EVENT_CCHA(CMN_ANY, snphaz_occ, 0x6d),
CMN_EVENT_CCHA(reqtrk_alloc, 0x6e), CMN_EVENT_CCHA(CMN_ANY, reqtrk_alloc, 0x6e),
CMN_EVENT_CCHA(rdb_alloc, 0x6f), CMN_EVENT_CCHA(CMN_ANY, rdb_alloc, 0x6f),
CMN_EVENT_CCHA(rdbyp_alloc, 0x70), CMN_EVENT_CCHA(CMN_ANY, rdbyp_alloc, 0x70),
CMN_EVENT_CCHA(wdb_alloc, 0x71), CMN_EVENT_CCHA(CMN_ANY, wdb_alloc, 0x71),
CMN_EVENT_CCHA(snptrk_alloc, 0x72), CMN_EVENT_CCHA(CMN_ANY, snptrk_alloc, 0x72),
CMN_EVENT_CCHA(sdb_alloc, 0x73), CMN_EVENT_CCHA(CMN_ANY, db_alloc, 0x73),
CMN_EVENT_CCHA(snphaz_alloc, 0x74), CMN_EVENT_CCHA(CMN_ANY, snphaz_alloc, 0x74),
CMN_EVENT_CCHA(pb_rhu_req_occ, 0x75), CMN_EVENT_CCHA(CMN_ANY, pb_rhu_req_occ, 0x75),
CMN_EVENT_CCHA(pb_rhu_req_alloc, 0x76), CMN_EVENT_CCHA(CMN_ANY, pb_rhu_req_alloc, 0x76),
CMN_EVENT_CCHA(pb_rhu_pcie_req_occ, 0x77), CMN_EVENT_CCHA(CMN_ANY, pb_rhu_pcie_req_occ, 0x77),
CMN_EVENT_CCHA(pb_rhu_pcie_req_alloc, 0x78), CMN_EVENT_CCHA(CMN_ANY, pb_rhu_pcie_req_alloc, 0x78),
CMN_EVENT_CCHA(pb_pcie_wr_req_occ, 0x79), CMN_EVENT_CCHA(CMN_ANY, pb_pcie_wr_req_occ, 0x79),
CMN_EVENT_CCHA(pb_pcie_wr_req_alloc, 0x7a), CMN_EVENT_CCHA(CMN_ANY, pb_pcie_wr_req_alloc, 0x7a),
CMN_EVENT_CCHA(pb_pcie_reg_req_occ, 0x7b), CMN_EVENT_CCHA(CMN_ANY, pb_pcie_reg_req_occ, 0x7b),
CMN_EVENT_CCHA(pb_pcie_reg_req_alloc, 0x7c), CMN_EVENT_CCHA(CMN_ANY, pb_pcie_reg_req_alloc, 0x7c),
CMN_EVENT_CCHA(pb_pcie_rsvd_req_occ, 0x7d), CMN_EVENT_CCHA(CMN_ANY, pb_pcie_rsvd_req_occ, 0x7d),
CMN_EVENT_CCHA(pb_pcie_rsvd_req_alloc, 0x7e), CMN_EVENT_CCHA(CMN_ANY, pb_pcie_rsvd_req_alloc, 0x7e),
CMN_EVENT_CCHA(pb_rhu_dat_occ, 0x7f), CMN_EVENT_CCHA(CMN_ANY, pb_rhu_dat_occ, 0x7f),
CMN_EVENT_CCHA(pb_rhu_dat_alloc, 0x80), CMN_EVENT_CCHA(CMN_ANY, pb_rhu_dat_alloc, 0x80),
CMN_EVENT_CCHA(pb_rhu_pcie_dat_occ, 0x81), CMN_EVENT_CCHA(CMN_ANY, pb_rhu_pcie_dat_occ, 0x81),
CMN_EVENT_CCHA(pb_rhu_pcie_dat_alloc, 0x82), CMN_EVENT_CCHA(CMN_ANY, pb_rhu_pcie_dat_alloc, 0x82),
CMN_EVENT_CCHA(pb_pcie_wr_dat_occ, 0x83), CMN_EVENT_CCHA(CMN_ANY, pb_pcie_wr_dat_occ, 0x83),
CMN_EVENT_CCHA(pb_pcie_wr_dat_alloc, 0x84), CMN_EVENT_CCHA(CMN_ANY, pb_pcie_wr_dat_alloc, 0x84),
CMN_EVENT_CCHA(CMNS3, chirsp1_up_stall, 0x85),
CMN_EVENT_CCLA(rx_cxs, 0x21), CMN_EVENT_CCLA(rx_cxs, 0x21),
CMN_EVENT_CCLA(tx_cxs, 0x22), CMN_EVENT_CCLA(tx_cxs, 0x22),
@ -1271,15 +1280,11 @@ static ssize_t arm_cmn_format_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
struct arm_cmn_format_attr *fmt = container_of(attr, typeof(*fmt), attr); struct arm_cmn_format_attr *fmt = container_of(attr, typeof(*fmt), attr);
int lo = __ffs(fmt->field), hi = __fls(fmt->field);
if (lo == hi)
return sysfs_emit(buf, "config:%d\n", lo);
if (!fmt->config) if (!fmt->config)
return sysfs_emit(buf, "config:%d-%d\n", lo, hi); return sysfs_emit(buf, "config:%*pbl\n", 64, &fmt->field);
return sysfs_emit(buf, "config%d:%d-%d\n", fmt->config, lo, hi); return sysfs_emit(buf, "config%d:%*pbl\n", fmt->config, 64, &fmt->field);
} }
#define _CMN_FORMAT_ATTR(_name, _cfg, _fld) \ #define _CMN_FORMAT_ATTR(_name, _cfg, _fld) \
@ -1415,7 +1420,7 @@ static u32 arm_cmn_wp_config(struct perf_event *event, int wp_idx)
static void arm_cmn_set_state(struct arm_cmn *cmn, u32 state) static void arm_cmn_set_state(struct arm_cmn *cmn, u32 state)
{ {
if (!cmn->state) if (!cmn->state)
writel_relaxed(0, cmn->dtc[0].base + CMN_DT_PMCR); writel_relaxed(0, CMN_DT_PMCR(&cmn->dtc[0]));
cmn->state |= state; cmn->state |= state;
} }
@ -1424,7 +1429,7 @@ static void arm_cmn_clear_state(struct arm_cmn *cmn, u32 state)
cmn->state &= ~state; cmn->state &= ~state;
if (!cmn->state) if (!cmn->state)
writel_relaxed(CMN_DT_PMCR_PMU_EN | CMN_DT_PMCR_OVFL_INTR_EN, writel_relaxed(CMN_DT_PMCR_PMU_EN | CMN_DT_PMCR_OVFL_INTR_EN,
cmn->dtc[0].base + CMN_DT_PMCR); CMN_DT_PMCR(&cmn->dtc[0]));
} }
static void arm_cmn_pmu_enable(struct pmu *pmu) static void arm_cmn_pmu_enable(struct pmu *pmu)
@ -1459,18 +1464,19 @@ static u64 arm_cmn_read_dtm(struct arm_cmn *cmn, struct arm_cmn_hw_event *hw,
static u64 arm_cmn_read_cc(struct arm_cmn_dtc *dtc) static u64 arm_cmn_read_cc(struct arm_cmn_dtc *dtc)
{ {
u64 val = readq_relaxed(dtc->base + CMN_DT_PMCCNTR); void __iomem *pmccntr = CMN_DT_PMCCNTR(dtc);
u64 val = readq_relaxed(pmccntr);
writeq_relaxed(CMN_CC_INIT, dtc->base + CMN_DT_PMCCNTR); writeq_relaxed(CMN_CC_INIT, pmccntr);
return (val - CMN_CC_INIT) & ((CMN_CC_INIT << 1) - 1); return (val - CMN_CC_INIT) & ((CMN_CC_INIT << 1) - 1);
} }
static u32 arm_cmn_read_counter(struct arm_cmn_dtc *dtc, int idx) static u32 arm_cmn_read_counter(struct arm_cmn_dtc *dtc, int idx)
{ {
u32 val, pmevcnt = CMN_DT_PMEVCNT(idx); void __iomem *pmevcnt = CMN_DT_PMEVCNT(dtc, idx);
u32 val = readl_relaxed(pmevcnt);
val = readl_relaxed(dtc->base + pmevcnt); writel_relaxed(CMN_COUNTER_INIT, pmevcnt);
writel_relaxed(CMN_COUNTER_INIT, dtc->base + pmevcnt);
return val - CMN_COUNTER_INIT; return val - CMN_COUNTER_INIT;
} }
@ -1481,7 +1487,7 @@ static void arm_cmn_init_counter(struct perf_event *event)
u64 count; u64 count;
for_each_hw_dtc_idx(hw, i, idx) { for_each_hw_dtc_idx(hw, i, idx) {
writel_relaxed(CMN_COUNTER_INIT, cmn->dtc[i].base + CMN_DT_PMEVCNT(idx)); writel_relaxed(CMN_COUNTER_INIT, CMN_DT_PMEVCNT(&cmn->dtc[i], idx));
cmn->dtc[i].counters[idx] = event; cmn->dtc[i].counters[idx] = event;
} }
@ -1564,9 +1570,12 @@ static void arm_cmn_event_start(struct perf_event *event, int flags)
int i; int i;
if (type == CMN_TYPE_DTC) { if (type == CMN_TYPE_DTC) {
i = hw->dtc_idx[0]; struct arm_cmn_dtc *dtc = cmn->dtc + hw->dtc_idx[0];
writeq_relaxed(CMN_CC_INIT, cmn->dtc[i].base + CMN_DT_PMCCNTR);
cmn->dtc[i].cc_active = true; writel_relaxed(CMN_DT_DTC_CTL_DT_EN | CMN_DT_DTC_CTL_CG_DISABLE,
dtc->base + CMN_DT_DTC_CTL);
writeq_relaxed(CMN_CC_INIT, CMN_DT_PMCCNTR(dtc));
dtc->cc_active = true;
} else if (type == CMN_TYPE_WP) { } else if (type == CMN_TYPE_WP) {
u64 val = CMN_EVENT_WP_VAL(event); u64 val = CMN_EVENT_WP_VAL(event);
u64 mask = CMN_EVENT_WP_MASK(event); u64 mask = CMN_EVENT_WP_MASK(event);
@ -1595,8 +1604,10 @@ static void arm_cmn_event_stop(struct perf_event *event, int flags)
int i; int i;
if (type == CMN_TYPE_DTC) { if (type == CMN_TYPE_DTC) {
i = hw->dtc_idx[0]; struct arm_cmn_dtc *dtc = cmn->dtc + hw->dtc_idx[0];
cmn->dtc[i].cc_active = false;
dtc->cc_active = false;
writel_relaxed(CMN_DT_DTC_CTL_DT_EN, dtc->base + CMN_DT_DTC_CTL);
} else if (type == CMN_TYPE_WP) { } else if (type == CMN_TYPE_WP) {
for_each_hw_dn(hw, dn, i) { for_each_hw_dn(hw, dn, i) {
void __iomem *base = dn->pmu_base + CMN_DTM_OFFSET(hw->dtm_offset); void __iomem *base = dn->pmu_base + CMN_DTM_OFFSET(hw->dtm_offset);
@ -1784,7 +1795,8 @@ static int arm_cmn_event_init(struct perf_event *event)
/* ...but the DTM may depend on which port we're watching */ /* ...but the DTM may depend on which port we're watching */
if (cmn->multi_dtm) if (cmn->multi_dtm)
hw->dtm_offset = CMN_EVENT_WP_DEV_SEL(event) / 2; hw->dtm_offset = CMN_EVENT_WP_DEV_SEL(event) / 2;
} else if (type == CMN_TYPE_XP && cmn->part == PART_CMN700) { } else if (type == CMN_TYPE_XP &&
(cmn->part == PART_CMN700 || cmn->part == PART_CMN_S3)) {
hw->wide_sel = true; hw->wide_sel = true;
} }
@ -1815,10 +1827,7 @@ static int arm_cmn_event_init(struct perf_event *event)
} }
if (!hw->num_dns) { if (!hw->num_dns) {
struct arm_cmn_nodeid nid = arm_cmn_nid(cmn, nodeid); dev_dbg(cmn->dev, "invalid node 0x%x type 0x%x\n", nodeid, type);
dev_dbg(cmn->dev, "invalid node 0x%x (%d,%d,%d,%d) type 0x%x\n",
nodeid, nid.x, nid.y, nid.port, nid.dev, type);
return -EINVAL; return -EINVAL;
} }
@ -1921,7 +1930,7 @@ static int arm_cmn_event_add(struct perf_event *event, int flags)
arm_cmn_claim_wp_idx(dtm, event, d, wp_idx, i); arm_cmn_claim_wp_idx(dtm, event, d, wp_idx, i);
writel_relaxed(cfg, dtm->base + CMN_DTM_WPn_CONFIG(wp_idx)); writel_relaxed(cfg, dtm->base + CMN_DTM_WPn_CONFIG(wp_idx));
} else { } else {
struct arm_cmn_nodeid nid = arm_cmn_nid(cmn, dn->id); struct arm_cmn_nodeid nid = arm_cmn_nid(dn);
if (cmn->multi_dtm) if (cmn->multi_dtm)
nid.port %= 2; nid.port %= 2;
@ -2010,7 +2019,7 @@ static int arm_cmn_pmu_online_cpu(unsigned int cpu, struct hlist_node *cpuhp_nod
cmn = hlist_entry_safe(cpuhp_node, struct arm_cmn, cpuhp_node); cmn = hlist_entry_safe(cpuhp_node, struct arm_cmn, cpuhp_node);
node = dev_to_node(cmn->dev); node = dev_to_node(cmn->dev);
if (node != NUMA_NO_NODE && cpu_to_node(cmn->cpu) != node && cpu_to_node(cpu) == node) if (cpu_to_node(cmn->cpu) != node && cpu_to_node(cpu) == node)
arm_cmn_migrate(cmn, cpu); arm_cmn_migrate(cmn, cpu);
return 0; return 0;
} }
@ -2043,7 +2052,7 @@ static irqreturn_t arm_cmn_handle_irq(int irq, void *dev_id)
irqreturn_t ret = IRQ_NONE; irqreturn_t ret = IRQ_NONE;
for (;;) { for (;;) {
u32 status = readl_relaxed(dtc->base + CMN_DT_PMOVSR); u32 status = readl_relaxed(CMN_DT_PMOVSR(dtc));
u64 delta; u64 delta;
int i; int i;
@ -2065,7 +2074,7 @@ static irqreturn_t arm_cmn_handle_irq(int irq, void *dev_id)
} }
} }
writel_relaxed(status, dtc->base + CMN_DT_PMOVSR_CLR); writel_relaxed(status, CMN_DT_PMOVSR_CLR(dtc));
if (!dtc->irq_friend) if (!dtc->irq_friend)
return ret; return ret;
@ -2119,15 +2128,16 @@ static int arm_cmn_init_dtc(struct arm_cmn *cmn, struct arm_cmn_node *dn, int id
{ {
struct arm_cmn_dtc *dtc = cmn->dtc + idx; struct arm_cmn_dtc *dtc = cmn->dtc + idx;
dtc->base = dn->pmu_base - CMN_PMU_OFFSET; dtc->pmu_base = dn->pmu_base;
dtc->base = dtc->pmu_base - arm_cmn_pmu_offset(cmn, dn);
dtc->irq = platform_get_irq(to_platform_device(cmn->dev), idx); dtc->irq = platform_get_irq(to_platform_device(cmn->dev), idx);
if (dtc->irq < 0) if (dtc->irq < 0)
return dtc->irq; return dtc->irq;
writel_relaxed(CMN_DT_DTC_CTL_DT_EN, dtc->base + CMN_DT_DTC_CTL); writel_relaxed(CMN_DT_DTC_CTL_DT_EN, dtc->base + CMN_DT_DTC_CTL);
writel_relaxed(CMN_DT_PMCR_PMU_EN | CMN_DT_PMCR_OVFL_INTR_EN, dtc->base + CMN_DT_PMCR); writel_relaxed(CMN_DT_PMCR_PMU_EN | CMN_DT_PMCR_OVFL_INTR_EN, CMN_DT_PMCR(dtc));
writeq_relaxed(0, dtc->base + CMN_DT_PMCCNTR); writeq_relaxed(0, CMN_DT_PMCCNTR(dtc));
writel_relaxed(0x1ff, dtc->base + CMN_DT_PMOVSR_CLR); writel_relaxed(0x1ff, CMN_DT_PMOVSR_CLR(dtc));
return 0; return 0;
} }
@ -2168,10 +2178,12 @@ static int arm_cmn_init_dtcs(struct arm_cmn *cmn)
continue; continue;
xp = arm_cmn_node_to_xp(cmn, dn); xp = arm_cmn_node_to_xp(cmn, dn);
dn->portid_bits = xp->portid_bits;
dn->deviceid_bits = xp->deviceid_bits;
dn->dtc = xp->dtc; dn->dtc = xp->dtc;
dn->dtm = xp->dtm; dn->dtm = xp->dtm;
if (cmn->multi_dtm) if (cmn->multi_dtm)
dn->dtm += arm_cmn_nid(cmn, dn->id).port / 2; dn->dtm += arm_cmn_nid(dn).port / 2;
if (dn->type == CMN_TYPE_DTC) { if (dn->type == CMN_TYPE_DTC) {
int err = arm_cmn_init_dtc(cmn, dn, dtc_idx++); int err = arm_cmn_init_dtc(cmn, dn, dtc_idx++);
@ -2213,7 +2225,7 @@ static void arm_cmn_init_node_info(struct arm_cmn *cmn, u32 offset, struct arm_c
node->id = FIELD_GET(CMN_NI_NODE_ID, reg); node->id = FIELD_GET(CMN_NI_NODE_ID, reg);
node->logid = FIELD_GET(CMN_NI_LOGICAL_ID, reg); node->logid = FIELD_GET(CMN_NI_LOGICAL_ID, reg);
node->pmu_base = cmn->base + offset + CMN_PMU_OFFSET; node->pmu_base = cmn->base + offset + arm_cmn_pmu_offset(cmn, node);
if (node->type == CMN_TYPE_CFG) if (node->type == CMN_TYPE_CFG)
level = 0; level = 0;
@ -2271,7 +2283,17 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
reg = readl_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_23); reg = readl_relaxed(cfg_region + CMN_CFGM_PERIPH_ID_23);
cmn->rev = FIELD_GET(CMN_CFGM_PID2_REVISION, reg); cmn->rev = FIELD_GET(CMN_CFGM_PID2_REVISION, reg);
/*
* With the device isolation feature, if firmware has neglected to enable
* an XP port then we risk locking up if we try to access anything behind
* it; however we also have no way to tell from Non-Secure whether any
* given port is disabled or not, so the only way to win is not to play...
*/
reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL); reg = readq_relaxed(cfg_region + CMN_CFGM_INFO_GLOBAL);
if (reg & CMN_INFO_DEVICE_ISO_ENABLE) {
dev_err(cmn->dev, "Device isolation enabled, not continuing due to risk of lockup\n");
return -ENODEV;
}
cmn->multi_dtm = reg & CMN_INFO_MULTIPLE_DTM_EN; cmn->multi_dtm = reg & CMN_INFO_MULTIPLE_DTM_EN;
cmn->rsp_vc_num = FIELD_GET(CMN_INFO_RSP_VC_NUM, reg); cmn->rsp_vc_num = FIELD_GET(CMN_INFO_RSP_VC_NUM, reg);
cmn->dat_vc_num = FIELD_GET(CMN_INFO_DAT_VC_NUM, reg); cmn->dat_vc_num = FIELD_GET(CMN_INFO_DAT_VC_NUM, reg);
@ -2341,18 +2363,27 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
arm_cmn_init_dtm(dtm++, xp, 0); arm_cmn_init_dtm(dtm++, xp, 0);
/* /*
* Keeping track of connected ports will let us filter out * Keeping track of connected ports will let us filter out
* unnecessary XP events easily. We can also reliably infer the * unnecessary XP events easily, and also infer the per-XP
* "extra device ports" configuration for the node ID format * part of the node ID format.
* from this, since in that case we will see at least one XP
* with port 2 connected, for the HN-D.
*/ */
for (int p = 0; p < CMN_MAX_PORTS; p++) for (int p = 0; p < CMN_MAX_PORTS; p++)
if (arm_cmn_device_connect_info(cmn, xp, p)) if (arm_cmn_device_connect_info(cmn, xp, p))
xp_ports |= BIT(p); xp_ports |= BIT(p);
if (cmn->multi_dtm && (xp_ports & 0xc)) if (cmn->num_xps == 1) {
xp->portid_bits = 3;
xp->deviceid_bits = 2;
} else if (xp_ports > 0x3) {
xp->portid_bits = 2;
xp->deviceid_bits = 1;
} else {
xp->portid_bits = 1;
xp->deviceid_bits = 2;
}
if (cmn->multi_dtm && (xp_ports > 0x3))
arm_cmn_init_dtm(dtm++, xp, 1); arm_cmn_init_dtm(dtm++, xp, 1);
if (cmn->multi_dtm && (xp_ports & 0x30)) if (cmn->multi_dtm && (xp_ports > 0xf))
arm_cmn_init_dtm(dtm++, xp, 2); arm_cmn_init_dtm(dtm++, xp, 2);
cmn->ports_used |= xp_ports; cmn->ports_used |= xp_ports;
@ -2407,10 +2438,13 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
case CMN_TYPE_CXHA: case CMN_TYPE_CXHA:
case CMN_TYPE_CCRA: case CMN_TYPE_CCRA:
case CMN_TYPE_CCHA: case CMN_TYPE_CCHA:
case CMN_TYPE_CCLA:
case CMN_TYPE_HNS: case CMN_TYPE_HNS:
dn++; dn++;
break; break;
case CMN_TYPE_CCLA:
dn->pmu_base += CMN_CCLA_PMU_EVENT_SEL;
dn++;
break;
/* Nothing to see here */ /* Nothing to see here */
case CMN_TYPE_MPAM_S: case CMN_TYPE_MPAM_S:
case CMN_TYPE_MPAM_NS: case CMN_TYPE_MPAM_NS:
@ -2418,6 +2452,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
case CMN_TYPE_CXLA: case CMN_TYPE_CXLA:
case CMN_TYPE_HNS_MPAM_S: case CMN_TYPE_HNS_MPAM_S:
case CMN_TYPE_HNS_MPAM_NS: case CMN_TYPE_HNS_MPAM_NS:
case CMN_TYPE_APB:
break; break;
/* /*
* Split "optimised" combination nodes into separate * Split "optimised" combination nodes into separate
@ -2428,7 +2463,7 @@ static int arm_cmn_discover(struct arm_cmn *cmn, unsigned int rgn_offset)
case CMN_TYPE_HNP: case CMN_TYPE_HNP:
case CMN_TYPE_CCLA_RNI: case CMN_TYPE_CCLA_RNI:
dn[1] = dn[0]; dn[1] = dn[0];
dn[0].pmu_base += CMN_HNP_PMU_EVENT_SEL; dn[0].pmu_base += CMN_CCLA_PMU_EVENT_SEL;
dn[1].type = arm_cmn_subtype(dn->type); dn[1].type = arm_cmn_subtype(dn->type);
dn += 2; dn += 2;
break; break;
@ -2603,6 +2638,7 @@ static const struct of_device_id arm_cmn_of_match[] = {
{ .compatible = "arm,cmn-600", .data = (void *)PART_CMN600 }, { .compatible = "arm,cmn-600", .data = (void *)PART_CMN600 },
{ .compatible = "arm,cmn-650" }, { .compatible = "arm,cmn-650" },
{ .compatible = "arm,cmn-700" }, { .compatible = "arm,cmn-700" },
{ .compatible = "arm,cmn-s3" },
{ .compatible = "arm,ci-700" }, { .compatible = "arm,ci-700" },
{} {}
}; };

781
drivers/perf/arm-ni.c Normal file
View File

@ -0,0 +1,781 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2022-2024 Arm Limited
// NI-700 Network-on-Chip PMU driver
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/perf_event.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/* Common registers */
#define NI_NODE_TYPE 0x000
#define NI_NODE_TYPE_NODE_ID GENMASK(31, 16)
#define NI_NODE_TYPE_NODE_TYPE GENMASK(15, 0)
#define NI_CHILD_NODE_INFO 0x004
#define NI_CHILD_PTR(n) (0x008 + (n) * 4)
#define NI700_PMUSELA 0x00c
/* Config node */
#define NI_PERIPHERAL_ID0 0xfe0
#define NI_PIDR0_PART_7_0 GENMASK(7, 0)
#define NI_PERIPHERAL_ID1 0xfe4
#define NI_PIDR1_PART_11_8 GENMASK(3, 0)
#define NI_PERIPHERAL_ID2 0xfe8
#define NI_PIDR2_VERSION GENMASK(7, 4)
/* PMU node */
#define NI_PMEVCNTR(n) (0x008 + (n) * 8)
#define NI_PMCCNTR_L 0x0f8
#define NI_PMCCNTR_U 0x0fc
#define NI_PMEVTYPER(n) (0x400 + (n) * 4)
#define NI_PMEVTYPER_NODE_TYPE GENMASK(12, 9)
#define NI_PMEVTYPER_NODE_ID GENMASK(8, 0)
#define NI_PMCNTENSET 0xc00
#define NI_PMCNTENCLR 0xc20
#define NI_PMINTENSET 0xc40
#define NI_PMINTENCLR 0xc60
#define NI_PMOVSCLR 0xc80
#define NI_PMOVSSET 0xcc0
#define NI_PMCFGR 0xe00
#define NI_PMCR 0xe04
#define NI_PMCR_RESET_CCNT BIT(2)
#define NI_PMCR_RESET_EVCNT BIT(1)
#define NI_PMCR_ENABLE BIT(0)
#define NI_NUM_COUNTERS 8
#define NI_CCNT_IDX 31
/* Event attributes */
#define NI_CONFIG_TYPE GENMASK_ULL(15, 0)
#define NI_CONFIG_NODEID GENMASK_ULL(31, 16)
#define NI_CONFIG_EVENTID GENMASK_ULL(47, 32)
#define NI_EVENT_TYPE(event) FIELD_GET(NI_CONFIG_TYPE, (event)->attr.config)
#define NI_EVENT_NODEID(event) FIELD_GET(NI_CONFIG_NODEID, (event)->attr.config)
#define NI_EVENT_EVENTID(event) FIELD_GET(NI_CONFIG_EVENTID, (event)->attr.config)
enum ni_part {
PART_NI_700 = 0x43b,
PART_NI_710AE = 0x43d,
};
enum ni_node_type {
NI_GLOBAL,
NI_VOLTAGE,
NI_POWER,
NI_CLOCK,
NI_ASNI,
NI_AMNI,
NI_PMU,
NI_HSNI,
NI_HMNI,
NI_PMNI,
};
struct arm_ni_node {
void __iomem *base;
enum ni_node_type type;
u16 id;
u32 num_components;
};
struct arm_ni_unit {
void __iomem *pmusela;
enum ni_node_type type;
u16 id;
bool ns;
union {
__le64 pmusel;
u8 event[8];
};
};
struct arm_ni_cd {
void __iomem *pmu_base;
u16 id;
int num_units;
int irq;
int cpu;
struct hlist_node cpuhp_node;
struct pmu pmu;
struct arm_ni_unit *units;
struct perf_event *evcnt[NI_NUM_COUNTERS];
struct perf_event *ccnt;
};
struct arm_ni {
struct device *dev;
void __iomem *base;
enum ni_part part;
int id;
int num_cds;
struct arm_ni_cd cds[] __counted_by(num_cds);
};
#define cd_to_ni(cd) container_of((cd), struct arm_ni, cds[(cd)->id])
#define pmu_to_cd(p) container_of((p), struct arm_ni_cd, pmu)
#define cd_for_each_unit(cd, u) \
for (struct arm_ni_unit *u = cd->units; u < cd->units + cd->num_units; u++)
static int arm_ni_hp_state;
struct arm_ni_event_attr {
struct device_attribute attr;
enum ni_node_type type;
};
#define NI_EVENT_ATTR(_name, _type) \
(&((struct arm_ni_event_attr[]) {{ \
.attr = __ATTR(_name, 0444, arm_ni_event_show, NULL), \
.type = _type, \
}})[0].attr.attr)
static ssize_t arm_ni_event_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct arm_ni_event_attr *eattr = container_of(attr, typeof(*eattr), attr);
if (eattr->type == NI_PMU)
return sysfs_emit(buf, "type=0x%x\n", eattr->type);
return sysfs_emit(buf, "type=0x%x,eventid=?,nodeid=?\n", eattr->type);
}
static umode_t arm_ni_event_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int unused)
{
struct device *dev = kobj_to_dev(kobj);
struct arm_ni_cd *cd = pmu_to_cd(dev_get_drvdata(dev));
struct arm_ni_event_attr *eattr;
eattr = container_of(attr, typeof(*eattr), attr.attr);
cd_for_each_unit(cd, unit) {
if (unit->type == eattr->type && unit->ns)
return attr->mode;
}
return 0;
}
static struct attribute *arm_ni_event_attrs[] = {
NI_EVENT_ATTR(asni, NI_ASNI),
NI_EVENT_ATTR(amni, NI_AMNI),
NI_EVENT_ATTR(cycles, NI_PMU),
NI_EVENT_ATTR(hsni, NI_HSNI),
NI_EVENT_ATTR(hmni, NI_HMNI),
NI_EVENT_ATTR(pmni, NI_PMNI),
NULL
};
static const struct attribute_group arm_ni_event_attrs_group = {
.name = "events",
.attrs = arm_ni_event_attrs,
.is_visible = arm_ni_event_attr_is_visible,
};
struct arm_ni_format_attr {
struct device_attribute attr;
u64 field;
};
#define NI_FORMAT_ATTR(_name, _fld) \
(&((struct arm_ni_format_attr[]) {{ \
.attr = __ATTR(_name, 0444, arm_ni_format_show, NULL), \
.field = _fld, \
}})[0].attr.attr)
static ssize_t arm_ni_format_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct arm_ni_format_attr *fmt = container_of(attr, typeof(*fmt), attr);
return sysfs_emit(buf, "config:%*pbl\n", 64, &fmt->field);
}
static struct attribute *arm_ni_format_attrs[] = {
NI_FORMAT_ATTR(type, NI_CONFIG_TYPE),
NI_FORMAT_ATTR(nodeid, NI_CONFIG_NODEID),
NI_FORMAT_ATTR(eventid, NI_CONFIG_EVENTID),
NULL
};
static const struct attribute_group arm_ni_format_attrs_group = {
.name = "format",
.attrs = arm_ni_format_attrs,
};
static ssize_t arm_ni_cpumask_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct arm_ni_cd *cd = pmu_to_cd(dev_get_drvdata(dev));
return cpumap_print_to_pagebuf(true, buf, cpumask_of(cd->cpu));
}
static struct device_attribute arm_ni_cpumask_attr =
__ATTR(cpumask, 0444, arm_ni_cpumask_show, NULL);
static ssize_t arm_ni_identifier_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct arm_ni *ni = cd_to_ni(pmu_to_cd(dev_get_drvdata(dev)));
u32 reg = readl_relaxed(ni->base + NI_PERIPHERAL_ID2);
int version = FIELD_GET(NI_PIDR2_VERSION, reg);
return sysfs_emit(buf, "%03x%02x\n", ni->part, version);
}
static struct device_attribute arm_ni_identifier_attr =
__ATTR(identifier, 0444, arm_ni_identifier_show, NULL);
static struct attribute *arm_ni_other_attrs[] = {
&arm_ni_cpumask_attr.attr,
&arm_ni_identifier_attr.attr,
NULL
};
static const struct attribute_group arm_ni_other_attr_group = {
.attrs = arm_ni_other_attrs,
NULL
};
static const struct attribute_group *arm_ni_attr_groups[] = {
&arm_ni_event_attrs_group,
&arm_ni_format_attrs_group,
&arm_ni_other_attr_group,
NULL
};
static void arm_ni_pmu_enable(struct pmu *pmu)
{
writel_relaxed(NI_PMCR_ENABLE, pmu_to_cd(pmu)->pmu_base + NI_PMCR);
}
static void arm_ni_pmu_disable(struct pmu *pmu)
{
writel_relaxed(0, pmu_to_cd(pmu)->pmu_base + NI_PMCR);
}
struct arm_ni_val {
unsigned int evcnt;
unsigned int ccnt;
};
static bool arm_ni_val_count_event(struct perf_event *evt, struct arm_ni_val *val)
{
if (is_software_event(evt))
return true;
if (NI_EVENT_TYPE(evt) == NI_PMU) {
val->ccnt++;
return val->ccnt <= 1;
}
val->evcnt++;
return val->evcnt <= NI_NUM_COUNTERS;
}
static int arm_ni_validate_group(struct perf_event *event)
{
struct perf_event *sibling, *leader = event->group_leader;
struct arm_ni_val val = { 0 };
if (leader == event)
return 0;
arm_ni_val_count_event(event, &val);
if (!arm_ni_val_count_event(leader, &val))
return -EINVAL;
for_each_sibling_event(sibling, leader) {
if (!arm_ni_val_count_event(sibling, &val))
return -EINVAL;
}
return 0;
}
static int arm_ni_event_init(struct perf_event *event)
{
struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
if (event->attr.type != event->pmu->type)
return -ENOENT;
if (is_sampling_event(event))
return -EINVAL;
event->cpu = cd->cpu;
if (NI_EVENT_TYPE(event) == NI_PMU)
return arm_ni_validate_group(event);
cd_for_each_unit(cd, unit) {
if (unit->type == NI_EVENT_TYPE(event) &&
unit->id == NI_EVENT_NODEID(event) && unit->ns) {
event->hw.config_base = (unsigned long)unit;
return arm_ni_validate_group(event);
}
}
return -EINVAL;
}
static u64 arm_ni_read_ccnt(struct arm_ni_cd *cd)
{
u64 l, u_old, u_new;
int retries = 3; /* 1st time unlucky, 2nd improbable, 3rd just broken */
u_new = readl_relaxed(cd->pmu_base + NI_PMCCNTR_U);
do {
u_old = u_new;
l = readl_relaxed(cd->pmu_base + NI_PMCCNTR_L);
u_new = readl_relaxed(cd->pmu_base + NI_PMCCNTR_U);
} while (u_new != u_old && --retries);
WARN_ON(!retries);
return (u_new << 32) | l;
}
static void arm_ni_event_read(struct perf_event *event)
{
struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
struct hw_perf_event *hw = &event->hw;
u64 count, prev;
bool ccnt = hw->idx == NI_CCNT_IDX;
do {
prev = local64_read(&hw->prev_count);
if (ccnt)
count = arm_ni_read_ccnt(cd);
else
count = readl_relaxed(cd->pmu_base + NI_PMEVCNTR(hw->idx));
} while (local64_cmpxchg(&hw->prev_count, prev, count) != prev);
count -= prev;
if (!ccnt)
count = (u32)count;
local64_add(count, &event->count);
}
static void arm_ni_event_start(struct perf_event *event, int flags)
{
struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
writel_relaxed(1U << event->hw.idx, cd->pmu_base + NI_PMCNTENSET);
}
static void arm_ni_event_stop(struct perf_event *event, int flags)
{
struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
writel_relaxed(1U << event->hw.idx, cd->pmu_base + NI_PMCNTENCLR);
if (flags & PERF_EF_UPDATE)
arm_ni_event_read(event);
}
static void arm_ni_init_ccnt(struct arm_ni_cd *cd)
{
local64_set(&cd->ccnt->hw.prev_count, S64_MIN);
lo_hi_writeq_relaxed(S64_MIN, cd->pmu_base + NI_PMCCNTR_L);
}
static void arm_ni_init_evcnt(struct arm_ni_cd *cd, int idx)
{
local64_set(&cd->evcnt[idx]->hw.prev_count, S32_MIN);
writel_relaxed(S32_MIN, cd->pmu_base + NI_PMEVCNTR(idx));
}
static int arm_ni_event_add(struct perf_event *event, int flags)
{
struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
struct hw_perf_event *hw = &event->hw;
struct arm_ni_unit *unit;
enum ni_node_type type = NI_EVENT_TYPE(event);
u32 reg;
if (type == NI_PMU) {
if (cd->ccnt)
return -ENOSPC;
hw->idx = NI_CCNT_IDX;
cd->ccnt = event;
arm_ni_init_ccnt(cd);
} else {
hw->idx = 0;
while (cd->evcnt[hw->idx]) {
if (++hw->idx == NI_NUM_COUNTERS)
return -ENOSPC;
}
cd->evcnt[hw->idx] = event;
unit = (void *)hw->config_base;
unit->event[hw->idx] = NI_EVENT_EVENTID(event);
arm_ni_init_evcnt(cd, hw->idx);
lo_hi_writeq_relaxed(le64_to_cpu(unit->pmusel), unit->pmusela);
reg = FIELD_PREP(NI_PMEVTYPER_NODE_TYPE, type) |
FIELD_PREP(NI_PMEVTYPER_NODE_ID, NI_EVENT_NODEID(event));
writel_relaxed(reg, cd->pmu_base + NI_PMEVTYPER(hw->idx));
}
if (flags & PERF_EF_START)
arm_ni_event_start(event, 0);
return 0;
}
static void arm_ni_event_del(struct perf_event *event, int flags)
{
struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
struct hw_perf_event *hw = &event->hw;
arm_ni_event_stop(event, PERF_EF_UPDATE);
if (hw->idx == NI_CCNT_IDX)
cd->ccnt = NULL;
else
cd->evcnt[hw->idx] = NULL;
}
static irqreturn_t arm_ni_handle_irq(int irq, void *dev_id)
{
struct arm_ni_cd *cd = dev_id;
irqreturn_t ret = IRQ_NONE;
u32 reg = readl_relaxed(cd->pmu_base + NI_PMOVSCLR);
if (reg & (1U << NI_CCNT_IDX)) {
ret = IRQ_HANDLED;
if (!(WARN_ON(!cd->ccnt))) {
arm_ni_event_read(cd->ccnt);
arm_ni_init_ccnt(cd);
}
}
for (int i = 0; i < NI_NUM_COUNTERS; i++) {
if (!(reg & (1U << i)))
continue;
ret = IRQ_HANDLED;
if (!(WARN_ON(!cd->evcnt[i]))) {
arm_ni_event_read(cd->evcnt[i]);
arm_ni_init_evcnt(cd, i);
}
}
writel_relaxed(reg, cd->pmu_base + NI_PMOVSCLR);
return ret;
}
static int arm_ni_init_cd(struct arm_ni *ni, struct arm_ni_node *node, u64 res_start)
{
struct arm_ni_cd *cd = ni->cds + node->id;
const char *name;
int err;
cd->id = node->id;
cd->num_units = node->num_components;
cd->units = devm_kcalloc(ni->dev, cd->num_units, sizeof(*(cd->units)), GFP_KERNEL);
if (!cd->units)
return -ENOMEM;
for (int i = 0; i < cd->num_units; i++) {
u32 reg = readl_relaxed(node->base + NI_CHILD_PTR(i));
void __iomem *unit_base = ni->base + reg;
struct arm_ni_unit *unit = cd->units + i;
reg = readl_relaxed(unit_base + NI_NODE_TYPE);
unit->type = FIELD_GET(NI_NODE_TYPE_NODE_TYPE, reg);
unit->id = FIELD_GET(NI_NODE_TYPE_NODE_ID, reg);
switch (unit->type) {
case NI_PMU:
reg = readl_relaxed(unit_base + NI_PMCFGR);
if (!reg) {
dev_info(ni->dev, "No access to PMU %d\n", cd->id);
devm_kfree(ni->dev, cd->units);
return 0;
}
unit->ns = true;
cd->pmu_base = unit_base;
break;
case NI_ASNI:
case NI_AMNI:
case NI_HSNI:
case NI_HMNI:
case NI_PMNI:
unit->pmusela = unit_base + NI700_PMUSELA;
writel_relaxed(1, unit->pmusela);
if (readl_relaxed(unit->pmusela) != 1)
dev_info(ni->dev, "No access to node 0x%04x%04x\n", unit->id, unit->type);
else
unit->ns = true;
break;
default:
/*
* e.g. FMU - thankfully bits 3:2 of FMU_ERR_FR0 are RES0 so
* can't alias any of the leaf node types we're looking for.
*/
dev_dbg(ni->dev, "Mystery node 0x%04x%04x\n", unit->id, unit->type);
break;
}
}
res_start += cd->pmu_base - ni->base;
if (!devm_request_mem_region(ni->dev, res_start, SZ_4K, dev_name(ni->dev))) {
dev_err(ni->dev, "Failed to request PMU region 0x%llx\n", res_start);
return -EBUSY;
}
writel_relaxed(NI_PMCR_RESET_CCNT | NI_PMCR_RESET_EVCNT,
cd->pmu_base + NI_PMCR);
writel_relaxed(U32_MAX, cd->pmu_base + NI_PMCNTENCLR);
writel_relaxed(U32_MAX, cd->pmu_base + NI_PMOVSCLR);
writel_relaxed(U32_MAX, cd->pmu_base + NI_PMINTENSET);
cd->irq = platform_get_irq(to_platform_device(ni->dev), cd->id);
if (cd->irq < 0)
return cd->irq;
err = devm_request_irq(ni->dev, cd->irq, arm_ni_handle_irq,
IRQF_NOBALANCING | IRQF_NO_THREAD,
dev_name(ni->dev), cd);
if (err)
return err;
cd->cpu = cpumask_local_spread(0, dev_to_node(ni->dev));
cd->pmu = (struct pmu) {
.module = THIS_MODULE,
.parent = ni->dev,
.attr_groups = arm_ni_attr_groups,
.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
.task_ctx_nr = perf_invalid_context,
.pmu_enable = arm_ni_pmu_enable,
.pmu_disable = arm_ni_pmu_disable,
.event_init = arm_ni_event_init,
.add = arm_ni_event_add,
.del = arm_ni_event_del,
.start = arm_ni_event_start,
.stop = arm_ni_event_stop,
.read = arm_ni_event_read,
};
name = devm_kasprintf(ni->dev, GFP_KERNEL, "arm_ni_%d_cd_%d", ni->id, cd->id);
if (!name)
return -ENOMEM;
err = cpuhp_state_add_instance_nocalls(arm_ni_hp_state, &cd->cpuhp_node);
if (err)
return err;
err = perf_pmu_register(&cd->pmu, name, -1);
if (err)
cpuhp_state_remove_instance_nocalls(arm_ni_hp_state, &cd->cpuhp_node);
return err;
}
static void arm_ni_probe_domain(void __iomem *base, struct arm_ni_node *node)
{
u32 reg = readl_relaxed(base + NI_NODE_TYPE);
node->base = base;
node->type = FIELD_GET(NI_NODE_TYPE_NODE_TYPE, reg);
node->id = FIELD_GET(NI_NODE_TYPE_NODE_ID, reg);
node->num_components = readl_relaxed(base + NI_CHILD_NODE_INFO);
}
static int arm_ni_probe(struct platform_device *pdev)
{
struct arm_ni_node cfg, vd, pd, cd;
struct arm_ni *ni;
struct resource *res;
void __iomem *base;
static atomic_t id;
int num_cds;
u32 reg, part;
/*
* We want to map the whole configuration space for ease of discovery,
* but the PMU pages are the only ones for which we can honestly claim
* exclusive ownership, so we'll request them explicitly once found.
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!base)
return -ENOMEM;
arm_ni_probe_domain(base, &cfg);
if (cfg.type != NI_GLOBAL)
return -ENODEV;
reg = readl_relaxed(cfg.base + NI_PERIPHERAL_ID0);
part = FIELD_GET(NI_PIDR0_PART_7_0, reg);
reg = readl_relaxed(cfg.base + NI_PERIPHERAL_ID1);
part |= FIELD_GET(NI_PIDR1_PART_11_8, reg) << 8;
switch (part) {
case PART_NI_700:
case PART_NI_710AE:
break;
default:
dev_WARN(&pdev->dev, "Unknown part number: 0x%03x, this may go badly\n", part);
break;
}
num_cds = 0;
for (int v = 0; v < cfg.num_components; v++) {
reg = readl_relaxed(cfg.base + NI_CHILD_PTR(v));
arm_ni_probe_domain(base + reg, &vd);
for (int p = 0; p < vd.num_components; p++) {
reg = readl_relaxed(vd.base + NI_CHILD_PTR(p));
arm_ni_probe_domain(base + reg, &pd);
num_cds += pd.num_components;
}
}
ni = devm_kzalloc(&pdev->dev, struct_size(ni, cds, num_cds), GFP_KERNEL);
if (!ni)
return -ENOMEM;
ni->dev = &pdev->dev;
ni->base = base;
ni->num_cds = num_cds;
ni->part = part;
ni->id = atomic_fetch_inc(&id);
for (int v = 0; v < cfg.num_components; v++) {
reg = readl_relaxed(cfg.base + NI_CHILD_PTR(v));
arm_ni_probe_domain(base + reg, &vd);
for (int p = 0; p < vd.num_components; p++) {
reg = readl_relaxed(vd.base + NI_CHILD_PTR(p));
arm_ni_probe_domain(base + reg, &pd);
for (int c = 0; c < pd.num_components; c++) {
int ret;
reg = readl_relaxed(pd.base + NI_CHILD_PTR(c));
arm_ni_probe_domain(base + reg, &cd);
ret = arm_ni_init_cd(ni, &cd, res->start);
if (ret)
return ret;
}
}
}
return 0;
}
static void arm_ni_remove(struct platform_device *pdev)
{
struct arm_ni *ni = platform_get_drvdata(pdev);
for (int i = 0; i < ni->num_cds; i++) {
struct arm_ni_cd *cd = ni->cds + i;
if (!cd->pmu_base)
continue;
writel_relaxed(0, cd->pmu_base + NI_PMCR);
writel_relaxed(U32_MAX, cd->pmu_base + NI_PMINTENCLR);
perf_pmu_unregister(&cd->pmu);
cpuhp_state_remove_instance_nocalls(arm_ni_hp_state, &cd->cpuhp_node);
}
}
#ifdef CONFIG_OF
static const struct of_device_id arm_ni_of_match[] = {
{ .compatible = "arm,ni-700" },
{}
};
MODULE_DEVICE_TABLE(of, arm_ni_of_match);
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id arm_ni_acpi_match[] = {
{ "ARMHCB70" },
{}
};
MODULE_DEVICE_TABLE(acpi, arm_ni_acpi_match);
#endif
static struct platform_driver arm_ni_driver = {
.driver = {
.name = "arm-ni",
.of_match_table = of_match_ptr(arm_ni_of_match),
.acpi_match_table = ACPI_PTR(arm_ni_acpi_match),
},
.probe = arm_ni_probe,
.remove = arm_ni_remove,
};
static void arm_ni_pmu_migrate(struct arm_ni_cd *cd, unsigned int cpu)
{
perf_pmu_migrate_context(&cd->pmu, cd->cpu, cpu);
irq_set_affinity(cd->irq, cpumask_of(cpu));
cd->cpu = cpu;
}
static int arm_ni_pmu_online_cpu(unsigned int cpu, struct hlist_node *cpuhp_node)
{
struct arm_ni_cd *cd;
int node;
cd = hlist_entry_safe(cpuhp_node, struct arm_ni_cd, cpuhp_node);
node = dev_to_node(cd_to_ni(cd)->dev);
if (cpu_to_node(cd->cpu) != node && cpu_to_node(cpu) == node)
arm_ni_pmu_migrate(cd, cpu);
return 0;
}
static int arm_ni_pmu_offline_cpu(unsigned int cpu, struct hlist_node *cpuhp_node)
{
struct arm_ni_cd *cd;
unsigned int target;
int node;
cd = hlist_entry_safe(cpuhp_node, struct arm_ni_cd, cpuhp_node);
if (cpu != cd->cpu)
return 0;
node = dev_to_node(cd_to_ni(cd)->dev);
target = cpumask_any_and_but(cpumask_of_node(node), cpu_online_mask, cpu);
if (target >= nr_cpu_ids)
target = cpumask_any_but(cpu_online_mask, cpu);
if (target < nr_cpu_ids)
arm_ni_pmu_migrate(cd, target);
return 0;
}
static int __init arm_ni_init(void)
{
int ret;
ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
"perf/arm/ni:online",
arm_ni_pmu_online_cpu,
arm_ni_pmu_offline_cpu);
if (ret < 0)
return ret;
arm_ni_hp_state = ret;
ret = platform_driver_register(&arm_ni_driver);
if (ret)
cpuhp_remove_multi_state(arm_ni_hp_state);
return ret;
}
static void __exit arm_ni_exit(void)
{
platform_driver_unregister(&arm_ni_driver);
cpuhp_remove_multi_state(arm_ni_hp_state);
}
module_init(arm_ni_init);
module_exit(arm_ni_exit);
MODULE_AUTHOR("Robin Murphy <robin.murphy@arm.com>");
MODULE_DESCRIPTION("Arm NI-700 PMU driver");
MODULE_LICENSE("GPL v2");

View File

@ -522,7 +522,7 @@ static void armpmu_enable(struct pmu *pmu)
{ {
struct arm_pmu *armpmu = to_arm_pmu(pmu); struct arm_pmu *armpmu = to_arm_pmu(pmu);
struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events); struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events);
bool enabled = !bitmap_empty(hw_events->used_mask, armpmu->num_events); bool enabled = !bitmap_empty(hw_events->used_mask, ARMPMU_MAX_HWEVENTS);
/* For task-bound events we may be called on other CPUs */ /* For task-bound events we may be called on other CPUs */
if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus)) if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus))
@ -742,7 +742,7 @@ static void cpu_pm_pmu_setup(struct arm_pmu *armpmu, unsigned long cmd)
struct perf_event *event; struct perf_event *event;
int idx; int idx;
for (idx = 0; idx < armpmu->num_events; idx++) { for_each_set_bit(idx, armpmu->cntr_mask, ARMPMU_MAX_HWEVENTS) {
event = hw_events->events[idx]; event = hw_events->events[idx];
if (!event) if (!event)
continue; continue;
@ -772,7 +772,7 @@ static int cpu_pm_pmu_notify(struct notifier_block *b, unsigned long cmd,
{ {
struct arm_pmu *armpmu = container_of(b, struct arm_pmu, cpu_pm_nb); struct arm_pmu *armpmu = container_of(b, struct arm_pmu, cpu_pm_nb);
struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events); struct pmu_hw_events *hw_events = this_cpu_ptr(armpmu->hw_events);
bool enabled = !bitmap_empty(hw_events->used_mask, armpmu->num_events); bool enabled = !bitmap_empty(hw_events->used_mask, ARMPMU_MAX_HWEVENTS);
if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus)) if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus))
return NOTIFY_DONE; return NOTIFY_DONE;
@ -924,8 +924,9 @@ int armpmu_register(struct arm_pmu *pmu)
if (ret) if (ret)
goto out_destroy; goto out_destroy;
pr_info("enabled with %s PMU driver, %d counters available%s\n", pr_info("enabled with %s PMU driver, %d (%*pb) counters available%s\n",
pmu->name, pmu->num_events, pmu->name, bitmap_weight(pmu->cntr_mask, ARMPMU_MAX_HWEVENTS),
ARMPMU_MAX_HWEVENTS, &pmu->cntr_mask,
has_nmi ? ", using NMIs" : ""); has_nmi ? ", using NMIs" : "");
kvm_host_pmu_init(pmu); kvm_host_pmu_init(pmu);

View File

@ -59,7 +59,7 @@ static int pmu_parse_percpu_irq(struct arm_pmu *pmu, int irq)
static bool pmu_has_irq_affinity(struct device_node *node) static bool pmu_has_irq_affinity(struct device_node *node)
{ {
return !!of_find_property(node, "interrupt-affinity", NULL); return of_property_present(node, "interrupt-affinity");
} }
static int pmu_parse_irq_affinity(struct device *dev, int i) static int pmu_parse_irq_affinity(struct device *dev, int i)

View File

@ -451,13 +451,6 @@ static const struct attribute_group armv8_pmuv3_caps_attr_group = {
.attrs = armv8_pmuv3_caps_attrs, .attrs = armv8_pmuv3_caps_attrs,
}; };
/*
* Perf Events' indices
*/
#define ARMV8_IDX_CYCLE_COUNTER 0
#define ARMV8_IDX_COUNTER0 1
#define ARMV8_IDX_CYCLE_COUNTER_USER 32
/* /*
* We unconditionally enable ARMv8.5-PMU long event counter support * We unconditionally enable ARMv8.5-PMU long event counter support
* (64-bit events) where supported. Indicate if this arm_pmu has long * (64-bit events) where supported. Indicate if this arm_pmu has long
@ -489,19 +482,12 @@ static bool armv8pmu_event_is_chained(struct perf_event *event)
return !armv8pmu_event_has_user_read(event) && return !armv8pmu_event_has_user_read(event) &&
armv8pmu_event_is_64bit(event) && armv8pmu_event_is_64bit(event) &&
!armv8pmu_has_long_event(cpu_pmu) && !armv8pmu_has_long_event(cpu_pmu) &&
(idx != ARMV8_IDX_CYCLE_COUNTER); (idx < ARMV8_PMU_MAX_GENERAL_COUNTERS);
} }
/* /*
* ARMv8 low level PMU access * ARMv8 low level PMU access
*/ */
/*
* Perf Event to low level counters mapping
*/
#define ARMV8_IDX_TO_COUNTER(x) \
(((x) - ARMV8_IDX_COUNTER0) & ARMV8_PMU_COUNTER_MASK)
static u64 armv8pmu_pmcr_read(void) static u64 armv8pmu_pmcr_read(void)
{ {
return read_pmcr(); return read_pmcr();
@ -514,21 +500,19 @@ static void armv8pmu_pmcr_write(u64 val)
write_pmcr(val); write_pmcr(val);
} }
static int armv8pmu_has_overflowed(u32 pmovsr) static int armv8pmu_has_overflowed(u64 pmovsr)
{ {
return pmovsr & ARMV8_PMU_OVERFLOWED_MASK; return !!(pmovsr & ARMV8_PMU_OVERFLOWED_MASK);
} }
static int armv8pmu_counter_has_overflowed(u32 pmnc, int idx) static int armv8pmu_counter_has_overflowed(u64 pmnc, int idx)
{ {
return pmnc & BIT(ARMV8_IDX_TO_COUNTER(idx)); return !!(pmnc & BIT(idx));
} }
static u64 armv8pmu_read_evcntr(int idx) static u64 armv8pmu_read_evcntr(int idx)
{ {
u32 counter = ARMV8_IDX_TO_COUNTER(idx); return read_pmevcntrn(idx);
return read_pmevcntrn(counter);
} }
static u64 armv8pmu_read_hw_counter(struct perf_event *event) static u64 armv8pmu_read_hw_counter(struct perf_event *event)
@ -557,7 +541,7 @@ static bool armv8pmu_event_needs_bias(struct perf_event *event)
return false; return false;
if (armv8pmu_has_long_event(cpu_pmu) || if (armv8pmu_has_long_event(cpu_pmu) ||
idx == ARMV8_IDX_CYCLE_COUNTER) idx >= ARMV8_PMU_MAX_GENERAL_COUNTERS)
return true; return true;
return false; return false;
@ -585,8 +569,10 @@ static u64 armv8pmu_read_counter(struct perf_event *event)
int idx = hwc->idx; int idx = hwc->idx;
u64 value; u64 value;
if (idx == ARMV8_IDX_CYCLE_COUNTER) if (idx == ARMV8_PMU_CYCLE_IDX)
value = read_pmccntr(); value = read_pmccntr();
else if (idx == ARMV8_PMU_INSTR_IDX)
value = read_pmicntr();
else else
value = armv8pmu_read_hw_counter(event); value = armv8pmu_read_hw_counter(event);
@ -595,9 +581,7 @@ static u64 armv8pmu_read_counter(struct perf_event *event)
static void armv8pmu_write_evcntr(int idx, u64 value) static void armv8pmu_write_evcntr(int idx, u64 value)
{ {
u32 counter = ARMV8_IDX_TO_COUNTER(idx); write_pmevcntrn(idx, value);
write_pmevcntrn(counter, value);
} }
static void armv8pmu_write_hw_counter(struct perf_event *event, static void armv8pmu_write_hw_counter(struct perf_event *event,
@ -620,15 +604,16 @@ static void armv8pmu_write_counter(struct perf_event *event, u64 value)
value = armv8pmu_bias_long_counter(event, value); value = armv8pmu_bias_long_counter(event, value);
if (idx == ARMV8_IDX_CYCLE_COUNTER) if (idx == ARMV8_PMU_CYCLE_IDX)
write_pmccntr(value); write_pmccntr(value);
else if (idx == ARMV8_PMU_INSTR_IDX)
write_pmicntr(value);
else else
armv8pmu_write_hw_counter(event, value); armv8pmu_write_hw_counter(event, value);
} }
static void armv8pmu_write_evtype(int idx, unsigned long val) static void armv8pmu_write_evtype(int idx, unsigned long val)
{ {
u32 counter = ARMV8_IDX_TO_COUNTER(idx);
unsigned long mask = ARMV8_PMU_EVTYPE_EVENT | unsigned long mask = ARMV8_PMU_EVTYPE_EVENT |
ARMV8_PMU_INCLUDE_EL2 | ARMV8_PMU_INCLUDE_EL2 |
ARMV8_PMU_EXCLUDE_EL0 | ARMV8_PMU_EXCLUDE_EL0 |
@ -638,7 +623,7 @@ static void armv8pmu_write_evtype(int idx, unsigned long val)
mask |= ARMV8_PMU_EVTYPE_TC | ARMV8_PMU_EVTYPE_TH; mask |= ARMV8_PMU_EVTYPE_TC | ARMV8_PMU_EVTYPE_TH;
val &= mask; val &= mask;
write_pmevtypern(counter, val); write_pmevtypern(idx, val);
} }
static void armv8pmu_write_event_type(struct perf_event *event) static void armv8pmu_write_event_type(struct perf_event *event)
@ -658,24 +643,26 @@ static void armv8pmu_write_event_type(struct perf_event *event)
armv8pmu_write_evtype(idx - 1, hwc->config_base); armv8pmu_write_evtype(idx - 1, hwc->config_base);
armv8pmu_write_evtype(idx, chain_evt); armv8pmu_write_evtype(idx, chain_evt);
} else { } else {
if (idx == ARMV8_IDX_CYCLE_COUNTER) if (idx == ARMV8_PMU_CYCLE_IDX)
write_pmccfiltr(hwc->config_base); write_pmccfiltr(hwc->config_base);
else if (idx == ARMV8_PMU_INSTR_IDX)
write_pmicfiltr(hwc->config_base);
else else
armv8pmu_write_evtype(idx, hwc->config_base); armv8pmu_write_evtype(idx, hwc->config_base);
} }
} }
static u32 armv8pmu_event_cnten_mask(struct perf_event *event) static u64 armv8pmu_event_cnten_mask(struct perf_event *event)
{ {
int counter = ARMV8_IDX_TO_COUNTER(event->hw.idx); int counter = event->hw.idx;
u32 mask = BIT(counter); u64 mask = BIT(counter);
if (armv8pmu_event_is_chained(event)) if (armv8pmu_event_is_chained(event))
mask |= BIT(counter - 1); mask |= BIT(counter - 1);
return mask; return mask;
} }
static void armv8pmu_enable_counter(u32 mask) static void armv8pmu_enable_counter(u64 mask)
{ {
/* /*
* Make sure event configuration register writes are visible before we * Make sure event configuration register writes are visible before we
@ -688,7 +675,7 @@ static void armv8pmu_enable_counter(u32 mask)
static void armv8pmu_enable_event_counter(struct perf_event *event) static void armv8pmu_enable_event_counter(struct perf_event *event)
{ {
struct perf_event_attr *attr = &event->attr; struct perf_event_attr *attr = &event->attr;
u32 mask = armv8pmu_event_cnten_mask(event); u64 mask = armv8pmu_event_cnten_mask(event);
kvm_set_pmu_events(mask, attr); kvm_set_pmu_events(mask, attr);
@ -697,7 +684,7 @@ static void armv8pmu_enable_event_counter(struct perf_event *event)
armv8pmu_enable_counter(mask); armv8pmu_enable_counter(mask);
} }
static void armv8pmu_disable_counter(u32 mask) static void armv8pmu_disable_counter(u64 mask)
{ {
write_pmcntenclr(mask); write_pmcntenclr(mask);
/* /*
@ -710,7 +697,7 @@ static void armv8pmu_disable_counter(u32 mask)
static void armv8pmu_disable_event_counter(struct perf_event *event) static void armv8pmu_disable_event_counter(struct perf_event *event)
{ {
struct perf_event_attr *attr = &event->attr; struct perf_event_attr *attr = &event->attr;
u32 mask = armv8pmu_event_cnten_mask(event); u64 mask = armv8pmu_event_cnten_mask(event);
kvm_clr_pmu_events(mask); kvm_clr_pmu_events(mask);
@ -719,18 +706,17 @@ static void armv8pmu_disable_event_counter(struct perf_event *event)
armv8pmu_disable_counter(mask); armv8pmu_disable_counter(mask);
} }
static void armv8pmu_enable_intens(u32 mask) static void armv8pmu_enable_intens(u64 mask)
{ {
write_pmintenset(mask); write_pmintenset(mask);
} }
static void armv8pmu_enable_event_irq(struct perf_event *event) static void armv8pmu_enable_event_irq(struct perf_event *event)
{ {
u32 counter = ARMV8_IDX_TO_COUNTER(event->hw.idx); armv8pmu_enable_intens(BIT(event->hw.idx));
armv8pmu_enable_intens(BIT(counter));
} }
static void armv8pmu_disable_intens(u32 mask) static void armv8pmu_disable_intens(u64 mask)
{ {
write_pmintenclr(mask); write_pmintenclr(mask);
isb(); isb();
@ -741,13 +727,12 @@ static void armv8pmu_disable_intens(u32 mask)
static void armv8pmu_disable_event_irq(struct perf_event *event) static void armv8pmu_disable_event_irq(struct perf_event *event)
{ {
u32 counter = ARMV8_IDX_TO_COUNTER(event->hw.idx); armv8pmu_disable_intens(BIT(event->hw.idx));
armv8pmu_disable_intens(BIT(counter));
} }
static u32 armv8pmu_getreset_flags(void) static u64 armv8pmu_getreset_flags(void)
{ {
u32 value; u64 value;
/* Read */ /* Read */
value = read_pmovsclr(); value = read_pmovsclr();
@ -786,9 +771,12 @@ static void armv8pmu_enable_user_access(struct arm_pmu *cpu_pmu)
struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events); struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
/* Clear any unused counters to avoid leaking their contents */ /* Clear any unused counters to avoid leaking their contents */
for_each_clear_bit(i, cpuc->used_mask, cpu_pmu->num_events) { for_each_andnot_bit(i, cpu_pmu->cntr_mask, cpuc->used_mask,
if (i == ARMV8_IDX_CYCLE_COUNTER) ARMPMU_MAX_HWEVENTS) {
if (i == ARMV8_PMU_CYCLE_IDX)
write_pmccntr(0); write_pmccntr(0);
else if (i == ARMV8_PMU_INSTR_IDX)
write_pmicntr(0);
else else
armv8pmu_write_evcntr(i, 0); armv8pmu_write_evcntr(i, 0);
} }
@ -842,7 +830,7 @@ static void armv8pmu_stop(struct arm_pmu *cpu_pmu)
static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu) static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
{ {
u32 pmovsr; u64 pmovsr;
struct perf_sample_data data; struct perf_sample_data data;
struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events); struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
struct pt_regs *regs; struct pt_regs *regs;
@ -869,7 +857,7 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu)
* to prevent skews in group events. * to prevent skews in group events.
*/ */
armv8pmu_stop(cpu_pmu); armv8pmu_stop(cpu_pmu);
for (idx = 0; idx < cpu_pmu->num_events; ++idx) { for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMPMU_MAX_HWEVENTS) {
struct perf_event *event = cpuc->events[idx]; struct perf_event *event = cpuc->events[idx];
struct hw_perf_event *hwc; struct hw_perf_event *hwc;
@ -908,7 +896,7 @@ static int armv8pmu_get_single_idx(struct pmu_hw_events *cpuc,
{ {
int idx; int idx;
for (idx = ARMV8_IDX_COUNTER0; idx < cpu_pmu->num_events; idx++) { for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS) {
if (!test_and_set_bit(idx, cpuc->used_mask)) if (!test_and_set_bit(idx, cpuc->used_mask))
return idx; return idx;
} }
@ -924,7 +912,9 @@ static int armv8pmu_get_chain_idx(struct pmu_hw_events *cpuc,
* Chaining requires two consecutive event counters, where * Chaining requires two consecutive event counters, where
* the lower idx must be even. * the lower idx must be even.
*/ */
for (idx = ARMV8_IDX_COUNTER0 + 1; idx < cpu_pmu->num_events; idx += 2) { for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMV8_PMU_MAX_GENERAL_COUNTERS) {
if (!(idx & 0x1))
continue;
if (!test_and_set_bit(idx, cpuc->used_mask)) { if (!test_and_set_bit(idx, cpuc->used_mask)) {
/* Check if the preceding even counter is available */ /* Check if the preceding even counter is available */
if (!test_and_set_bit(idx - 1, cpuc->used_mask)) if (!test_and_set_bit(idx - 1, cpuc->used_mask))
@ -946,14 +936,27 @@ static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
/* Always prefer to place a cycle counter into the cycle counter. */ /* Always prefer to place a cycle counter into the cycle counter. */
if ((evtype == ARMV8_PMUV3_PERFCTR_CPU_CYCLES) && if ((evtype == ARMV8_PMUV3_PERFCTR_CPU_CYCLES) &&
!armv8pmu_event_get_threshold(&event->attr)) { !armv8pmu_event_get_threshold(&event->attr)) {
if (!test_and_set_bit(ARMV8_IDX_CYCLE_COUNTER, cpuc->used_mask)) if (!test_and_set_bit(ARMV8_PMU_CYCLE_IDX, cpuc->used_mask))
return ARMV8_IDX_CYCLE_COUNTER; return ARMV8_PMU_CYCLE_IDX;
else if (armv8pmu_event_is_64bit(event) && else if (armv8pmu_event_is_64bit(event) &&
armv8pmu_event_want_user_access(event) && armv8pmu_event_want_user_access(event) &&
!armv8pmu_has_long_event(cpu_pmu)) !armv8pmu_has_long_event(cpu_pmu))
return -EAGAIN; return -EAGAIN;
} }
/*
* Always prefer to place a instruction counter into the instruction counter,
* but don't expose the instruction counter to userspace access as userspace
* may not know how to handle it.
*/
if ((evtype == ARMV8_PMUV3_PERFCTR_INST_RETIRED) &&
!armv8pmu_event_get_threshold(&event->attr) &&
test_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->cntr_mask) &&
!armv8pmu_event_want_user_access(event)) {
if (!test_and_set_bit(ARMV8_PMU_INSTR_IDX, cpuc->used_mask))
return ARMV8_PMU_INSTR_IDX;
}
/* /*
* Otherwise use events counters * Otherwise use events counters
*/ */
@ -978,15 +981,7 @@ static int armv8pmu_user_event_idx(struct perf_event *event)
if (!sysctl_perf_user_access || !armv8pmu_event_has_user_read(event)) if (!sysctl_perf_user_access || !armv8pmu_event_has_user_read(event))
return 0; return 0;
/* return event->hw.idx + 1;
* We remap the cycle counter index to 32 to
* match the offset applied to the rest of
* the counter indices.
*/
if (event->hw.idx == ARMV8_IDX_CYCLE_COUNTER)
return ARMV8_IDX_CYCLE_COUNTER_USER;
return event->hw.idx;
} }
/* /*
@ -1061,14 +1056,16 @@ static int armv8pmu_set_event_filter(struct hw_perf_event *event,
static void armv8pmu_reset(void *info) static void armv8pmu_reset(void *info)
{ {
struct arm_pmu *cpu_pmu = (struct arm_pmu *)info; struct arm_pmu *cpu_pmu = (struct arm_pmu *)info;
u64 pmcr; u64 pmcr, mask;
bitmap_to_arr64(&mask, cpu_pmu->cntr_mask, ARMPMU_MAX_HWEVENTS);
/* The counter and interrupt enable registers are unknown at reset. */ /* The counter and interrupt enable registers are unknown at reset. */
armv8pmu_disable_counter(U32_MAX); armv8pmu_disable_counter(mask);
armv8pmu_disable_intens(U32_MAX); armv8pmu_disable_intens(mask);
/* Clear the counters we flip at guest entry/exit */ /* Clear the counters we flip at guest entry/exit */
kvm_clr_pmu_events(U32_MAX); kvm_clr_pmu_events(mask);
/* /*
* Initialize & Reset PMNC. Request overflow interrupt for * Initialize & Reset PMNC. Request overflow interrupt for
@ -1089,14 +1086,14 @@ static int __armv8_pmuv3_map_event_id(struct arm_pmu *armpmu,
if (event->attr.type == PERF_TYPE_HARDWARE && if (event->attr.type == PERF_TYPE_HARDWARE &&
event->attr.config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS) { event->attr.config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS) {
if (test_bit(ARMV8_PMUV3_PERFCTR_PC_WRITE_RETIRED,
armpmu->pmceid_bitmap))
return ARMV8_PMUV3_PERFCTR_PC_WRITE_RETIRED;
if (test_bit(ARMV8_PMUV3_PERFCTR_BR_RETIRED, if (test_bit(ARMV8_PMUV3_PERFCTR_BR_RETIRED,
armpmu->pmceid_bitmap)) armpmu->pmceid_bitmap))
return ARMV8_PMUV3_PERFCTR_BR_RETIRED; return ARMV8_PMUV3_PERFCTR_BR_RETIRED;
if (test_bit(ARMV8_PMUV3_PERFCTR_PC_WRITE_RETIRED,
armpmu->pmceid_bitmap))
return ARMV8_PMUV3_PERFCTR_PC_WRITE_RETIRED;
return HW_OP_UNSUPPORTED; return HW_OP_UNSUPPORTED;
} }
@ -1211,10 +1208,15 @@ static void __armv8pmu_probe_pmu(void *info)
probe->present = true; probe->present = true;
/* Read the nb of CNTx counters supported from PMNC */ /* Read the nb of CNTx counters supported from PMNC */
cpu_pmu->num_events = FIELD_GET(ARMV8_PMU_PMCR_N, armv8pmu_pmcr_read()); bitmap_set(cpu_pmu->cntr_mask,
0, FIELD_GET(ARMV8_PMU_PMCR_N, armv8pmu_pmcr_read()));
/* Add the CPU cycles counter */ /* Add the CPU cycles counter */
cpu_pmu->num_events += 1; set_bit(ARMV8_PMU_CYCLE_IDX, cpu_pmu->cntr_mask);
/* Add the CPU instructions counter */
if (pmuv3_has_icntr())
set_bit(ARMV8_PMU_INSTR_IDX, cpu_pmu->cntr_mask);
pmceid[0] = pmceid_raw[0] = read_pmceid0(); pmceid[0] = pmceid_raw[0] = read_pmceid0();
pmceid[1] = pmceid_raw[1] = read_pmceid1(); pmceid[1] = pmceid_raw[1] = read_pmceid1();

View File

@ -41,7 +41,7 @@
/* /*
* Cache if the event is allowed to trace Context information. * Cache if the event is allowed to trace Context information.
* This allows us to perform the check, i.e, perfmon_capable(), * This allows us to perform the check, i.e, perf_allow_kernel(),
* in the context of the event owner, once, during the event_init(). * in the context of the event owner, once, during the event_init().
*/ */
#define SPE_PMU_HW_FLAGS_CX 0x00001 #define SPE_PMU_HW_FLAGS_CX 0x00001
@ -50,7 +50,7 @@ static_assert((PERF_EVENT_FLAG_ARCH & SPE_PMU_HW_FLAGS_CX) == SPE_PMU_HW_FLAGS_C
static void set_spe_event_has_cx(struct perf_event *event) static void set_spe_event_has_cx(struct perf_event *event)
{ {
if (IS_ENABLED(CONFIG_PID_IN_CONTEXTIDR) && perfmon_capable()) if (IS_ENABLED(CONFIG_PID_IN_CONTEXTIDR) && !perf_allow_kernel(&event->attr))
event->hw.flags |= SPE_PMU_HW_FLAGS_CX; event->hw.flags |= SPE_PMU_HW_FLAGS_CX;
} }
@ -745,9 +745,8 @@ static int arm_spe_pmu_event_init(struct perf_event *event)
set_spe_event_has_cx(event); set_spe_event_has_cx(event);
reg = arm_spe_event_to_pmscr(event); reg = arm_spe_event_to_pmscr(event);
if (!perfmon_capable() && if (reg & (PMSCR_EL1_PA | PMSCR_EL1_PCT))
(reg & (PMSCR_EL1_PA | PMSCR_EL1_PCT))) return perf_allow_kernel(&event->attr);
return -EACCES;
return 0; return 0;
} }

View File

@ -64,6 +64,7 @@ enum armv6_counters {
ARMV6_CYCLE_COUNTER = 0, ARMV6_CYCLE_COUNTER = 0,
ARMV6_COUNTER0, ARMV6_COUNTER0,
ARMV6_COUNTER1, ARMV6_COUNTER1,
ARMV6_NUM_COUNTERS
}; };
/* /*
@ -254,7 +255,7 @@ armv6pmu_handle_irq(struct arm_pmu *cpu_pmu)
*/ */
armv6_pmcr_write(pmcr); armv6_pmcr_write(pmcr);
for (idx = 0; idx < cpu_pmu->num_events; ++idx) { for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMV6_NUM_COUNTERS) {
struct perf_event *event = cpuc->events[idx]; struct perf_event *event = cpuc->events[idx];
struct hw_perf_event *hwc; struct hw_perf_event *hwc;
@ -391,7 +392,8 @@ static void armv6pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->start = armv6pmu_start; cpu_pmu->start = armv6pmu_start;
cpu_pmu->stop = armv6pmu_stop; cpu_pmu->stop = armv6pmu_stop;
cpu_pmu->map_event = armv6_map_event; cpu_pmu->map_event = armv6_map_event;
cpu_pmu->num_events = 3;
bitmap_set(cpu_pmu->cntr_mask, 0, ARMV6_NUM_COUNTERS);
} }
static int armv6_1136_pmu_init(struct arm_pmu *cpu_pmu) static int armv6_1136_pmu_init(struct arm_pmu *cpu_pmu)

View File

@ -649,24 +649,12 @@ static struct attribute_group armv7_pmuv2_events_attr_group = {
/* /*
* Perf Events' indices * Perf Events' indices
*/ */
#define ARMV7_IDX_CYCLE_COUNTER 0 #define ARMV7_IDX_CYCLE_COUNTER 31
#define ARMV7_IDX_COUNTER0 1 #define ARMV7_IDX_COUNTER_MAX 31
#define ARMV7_IDX_COUNTER_LAST(cpu_pmu) \
(ARMV7_IDX_CYCLE_COUNTER + cpu_pmu->num_events - 1)
#define ARMV7_MAX_COUNTERS 32
#define ARMV7_COUNTER_MASK (ARMV7_MAX_COUNTERS - 1)
/* /*
* ARMv7 low level PMNC access * ARMv7 low level PMNC access
*/ */
/*
* Perf Event to low level counters mapping
*/
#define ARMV7_IDX_TO_COUNTER(x) \
(((x) - ARMV7_IDX_COUNTER0) & ARMV7_COUNTER_MASK)
/* /*
* Per-CPU PMNC: config reg * Per-CPU PMNC: config reg
*/ */
@ -725,19 +713,17 @@ static inline int armv7_pmnc_has_overflowed(u32 pmnc)
static inline int armv7_pmnc_counter_valid(struct arm_pmu *cpu_pmu, int idx) static inline int armv7_pmnc_counter_valid(struct arm_pmu *cpu_pmu, int idx)
{ {
return idx >= ARMV7_IDX_CYCLE_COUNTER && return test_bit(idx, cpu_pmu->cntr_mask);
idx <= ARMV7_IDX_COUNTER_LAST(cpu_pmu);
} }
static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, int idx) static inline int armv7_pmnc_counter_has_overflowed(u32 pmnc, int idx)
{ {
return pmnc & BIT(ARMV7_IDX_TO_COUNTER(idx)); return pmnc & BIT(idx);
} }
static inline void armv7_pmnc_select_counter(int idx) static inline void armv7_pmnc_select_counter(int idx)
{ {
u32 counter = ARMV7_IDX_TO_COUNTER(idx); asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (idx));
asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (counter));
isb(); isb();
} }
@ -787,29 +773,25 @@ static inline void armv7_pmnc_write_evtsel(int idx, u32 val)
static inline void armv7_pmnc_enable_counter(int idx) static inline void armv7_pmnc_enable_counter(int idx)
{ {
u32 counter = ARMV7_IDX_TO_COUNTER(idx); asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (BIT(idx)));
asm volatile("mcr p15, 0, %0, c9, c12, 1" : : "r" (BIT(counter)));
} }
static inline void armv7_pmnc_disable_counter(int idx) static inline void armv7_pmnc_disable_counter(int idx)
{ {
u32 counter = ARMV7_IDX_TO_COUNTER(idx); asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (BIT(idx)));
asm volatile("mcr p15, 0, %0, c9, c12, 2" : : "r" (BIT(counter)));
} }
static inline void armv7_pmnc_enable_intens(int idx) static inline void armv7_pmnc_enable_intens(int idx)
{ {
u32 counter = ARMV7_IDX_TO_COUNTER(idx); asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (BIT(idx)));
asm volatile("mcr p15, 0, %0, c9, c14, 1" : : "r" (BIT(counter)));
} }
static inline void armv7_pmnc_disable_intens(int idx) static inline void armv7_pmnc_disable_intens(int idx)
{ {
u32 counter = ARMV7_IDX_TO_COUNTER(idx); asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (BIT(idx)));
asm volatile("mcr p15, 0, %0, c9, c14, 2" : : "r" (BIT(counter)));
isb(); isb();
/* Clear the overflow flag in case an interrupt is pending. */ /* Clear the overflow flag in case an interrupt is pending. */
asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (BIT(counter))); asm volatile("mcr p15, 0, %0, c9, c12, 3" : : "r" (BIT(idx)));
isb(); isb();
} }
@ -853,15 +835,12 @@ static void armv7_pmnc_dump_regs(struct arm_pmu *cpu_pmu)
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
pr_info("CCNT =0x%08x\n", val); pr_info("CCNT =0x%08x\n", val);
for (cnt = ARMV7_IDX_COUNTER0; for_each_set_bit(cnt, cpu_pmu->cntr_mask, ARMV7_IDX_COUNTER_MAX) {
cnt <= ARMV7_IDX_COUNTER_LAST(cpu_pmu); cnt++) {
armv7_pmnc_select_counter(cnt); armv7_pmnc_select_counter(cnt);
asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));
pr_info("CNT[%d] count =0x%08x\n", pr_info("CNT[%d] count =0x%08x\n", cnt, val);
ARMV7_IDX_TO_COUNTER(cnt), val);
asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val)); asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));
pr_info("CNT[%d] evtsel=0x%08x\n", pr_info("CNT[%d] evtsel=0x%08x\n", cnt, val);
ARMV7_IDX_TO_COUNTER(cnt), val);
} }
} }
#endif #endif
@ -958,7 +937,7 @@ static irqreturn_t armv7pmu_handle_irq(struct arm_pmu *cpu_pmu)
*/ */
regs = get_irq_regs(); regs = get_irq_regs();
for (idx = 0; idx < cpu_pmu->num_events; ++idx) { for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMPMU_MAX_HWEVENTS) {
struct perf_event *event = cpuc->events[idx]; struct perf_event *event = cpuc->events[idx];
struct hw_perf_event *hwc; struct hw_perf_event *hwc;
@ -1027,7 +1006,7 @@ static int armv7pmu_get_event_idx(struct pmu_hw_events *cpuc,
* For anything other than a cycle counter, try and use * For anything other than a cycle counter, try and use
* the events counters * the events counters
*/ */
for (idx = ARMV7_IDX_COUNTER0; idx < cpu_pmu->num_events; ++idx) { for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMV7_IDX_COUNTER_MAX) {
if (!test_and_set_bit(idx, cpuc->used_mask)) if (!test_and_set_bit(idx, cpuc->used_mask))
return idx; return idx;
} }
@ -1073,7 +1052,7 @@ static int armv7pmu_set_event_filter(struct hw_perf_event *event,
static void armv7pmu_reset(void *info) static void armv7pmu_reset(void *info)
{ {
struct arm_pmu *cpu_pmu = (struct arm_pmu *)info; struct arm_pmu *cpu_pmu = (struct arm_pmu *)info;
u32 idx, nb_cnt = cpu_pmu->num_events, val; u32 idx, val;
if (cpu_pmu->secure_access) { if (cpu_pmu->secure_access) {
asm volatile("mrc p15, 0, %0, c1, c1, 1" : "=r" (val)); asm volatile("mrc p15, 0, %0, c1, c1, 1" : "=r" (val));
@ -1082,7 +1061,7 @@ static void armv7pmu_reset(void *info)
} }
/* The counter and interrupt enable registers are unknown at reset. */ /* The counter and interrupt enable registers are unknown at reset. */
for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx) { for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMPMU_MAX_HWEVENTS) {
armv7_pmnc_disable_counter(idx); armv7_pmnc_disable_counter(idx);
armv7_pmnc_disable_intens(idx); armv7_pmnc_disable_intens(idx);
} }
@ -1161,20 +1140,22 @@ static void armv7pmu_init(struct arm_pmu *cpu_pmu)
static void armv7_read_num_pmnc_events(void *info) static void armv7_read_num_pmnc_events(void *info)
{ {
int *nb_cnt = info; int nb_cnt;
struct arm_pmu *cpu_pmu = info;
/* Read the nb of CNTx counters supported from PMNC */ /* Read the nb of CNTx counters supported from PMNC */
*nb_cnt = (armv7_pmnc_read() >> ARMV7_PMNC_N_SHIFT) & ARMV7_PMNC_N_MASK; nb_cnt = (armv7_pmnc_read() >> ARMV7_PMNC_N_SHIFT) & ARMV7_PMNC_N_MASK;
bitmap_set(cpu_pmu->cntr_mask, 0, nb_cnt);
/* Add the CPU cycles counter */ /* Add the CPU cycles counter */
*nb_cnt += 1; set_bit(ARMV7_IDX_CYCLE_COUNTER, cpu_pmu->cntr_mask);
} }
static int armv7_probe_num_events(struct arm_pmu *arm_pmu) static int armv7_probe_num_events(struct arm_pmu *arm_pmu)
{ {
return smp_call_function_any(&arm_pmu->supported_cpus, return smp_call_function_any(&arm_pmu->supported_cpus,
armv7_read_num_pmnc_events, armv7_read_num_pmnc_events,
&arm_pmu->num_events, 1); arm_pmu, 1);
} }
static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu) static int armv7_a8_pmu_init(struct arm_pmu *cpu_pmu)
@ -1524,7 +1505,7 @@ static void krait_pmu_reset(void *info)
{ {
u32 vval, fval; u32 vval, fval;
struct arm_pmu *cpu_pmu = info; struct arm_pmu *cpu_pmu = info;
u32 idx, nb_cnt = cpu_pmu->num_events; u32 idx;
armv7pmu_reset(info); armv7pmu_reset(info);
@ -1538,7 +1519,7 @@ static void krait_pmu_reset(void *info)
venum_post_pmresr(vval, fval); venum_post_pmresr(vval, fval);
/* Reset PMxEVNCTCR to sane default */ /* Reset PMxEVNCTCR to sane default */
for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx) { for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMV7_IDX_COUNTER_MAX) {
armv7_pmnc_select_counter(idx); armv7_pmnc_select_counter(idx);
asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (0)); asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (0));
} }
@ -1562,7 +1543,7 @@ static int krait_event_to_bit(struct perf_event *event, unsigned int region,
* Lower bits are reserved for use by the counters (see * Lower bits are reserved for use by the counters (see
* armv7pmu_get_event_idx() for more info) * armv7pmu_get_event_idx() for more info)
*/ */
bit += ARMV7_IDX_COUNTER_LAST(cpu_pmu) + 1; bit += bitmap_weight(cpu_pmu->cntr_mask, ARMV7_IDX_COUNTER_MAX);
return bit; return bit;
} }
@ -1845,7 +1826,7 @@ static void scorpion_pmu_reset(void *info)
{ {
u32 vval, fval; u32 vval, fval;
struct arm_pmu *cpu_pmu = info; struct arm_pmu *cpu_pmu = info;
u32 idx, nb_cnt = cpu_pmu->num_events; u32 idx;
armv7pmu_reset(info); armv7pmu_reset(info);
@ -1860,7 +1841,7 @@ static void scorpion_pmu_reset(void *info)
venum_post_pmresr(vval, fval); venum_post_pmresr(vval, fval);
/* Reset PMxEVNCTCR to sane default */ /* Reset PMxEVNCTCR to sane default */
for (idx = ARMV7_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx) { for_each_set_bit(idx, cpu_pmu->cntr_mask, ARMV7_IDX_COUNTER_MAX) {
armv7_pmnc_select_counter(idx); armv7_pmnc_select_counter(idx);
asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (0)); asm volatile("mcr p15, 0, %0, c9, c15, 0" : : "r" (0));
} }
@ -1883,7 +1864,7 @@ static int scorpion_event_to_bit(struct perf_event *event, unsigned int region,
* Lower bits are reserved for use by the counters (see * Lower bits are reserved for use by the counters (see
* armv7pmu_get_event_idx() for more info) * armv7pmu_get_event_idx() for more info)
*/ */
bit += ARMV7_IDX_COUNTER_LAST(cpu_pmu) + 1; bit += bitmap_weight(cpu_pmu->cntr_mask, ARMV7_IDX_COUNTER_MAX);
return bit; return bit;
} }

View File

@ -53,6 +53,8 @@ enum xscale_counters {
XSCALE_COUNTER2, XSCALE_COUNTER2,
XSCALE_COUNTER3, XSCALE_COUNTER3,
}; };
#define XSCALE1_NUM_COUNTERS 3
#define XSCALE2_NUM_COUNTERS 5
static const unsigned xscale_perf_map[PERF_COUNT_HW_MAX] = { static const unsigned xscale_perf_map[PERF_COUNT_HW_MAX] = {
PERF_MAP_ALL_UNSUPPORTED, PERF_MAP_ALL_UNSUPPORTED,
@ -168,7 +170,7 @@ xscale1pmu_handle_irq(struct arm_pmu *cpu_pmu)
regs = get_irq_regs(); regs = get_irq_regs();
for (idx = 0; idx < cpu_pmu->num_events; ++idx) { for_each_set_bit(idx, cpu_pmu->cntr_mask, XSCALE1_NUM_COUNTERS) {
struct perf_event *event = cpuc->events[idx]; struct perf_event *event = cpuc->events[idx];
struct hw_perf_event *hwc; struct hw_perf_event *hwc;
@ -364,7 +366,8 @@ static int xscale1pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->start = xscale1pmu_start; cpu_pmu->start = xscale1pmu_start;
cpu_pmu->stop = xscale1pmu_stop; cpu_pmu->stop = xscale1pmu_stop;
cpu_pmu->map_event = xscale_map_event; cpu_pmu->map_event = xscale_map_event;
cpu_pmu->num_events = 3;
bitmap_set(cpu_pmu->cntr_mask, 0, XSCALE1_NUM_COUNTERS);
return 0; return 0;
} }
@ -500,7 +503,7 @@ xscale2pmu_handle_irq(struct arm_pmu *cpu_pmu)
regs = get_irq_regs(); regs = get_irq_regs();
for (idx = 0; idx < cpu_pmu->num_events; ++idx) { for_each_set_bit(idx, cpu_pmu->cntr_mask, XSCALE2_NUM_COUNTERS) {
struct perf_event *event = cpuc->events[idx]; struct perf_event *event = cpuc->events[idx];
struct hw_perf_event *hwc; struct hw_perf_event *hwc;
@ -719,7 +722,8 @@ static int xscale2pmu_init(struct arm_pmu *cpu_pmu)
cpu_pmu->start = xscale2pmu_start; cpu_pmu->start = xscale2pmu_start;
cpu_pmu->stop = xscale2pmu_stop; cpu_pmu->stop = xscale2pmu_stop;
cpu_pmu->map_event = xscale_map_event; cpu_pmu->map_event = xscale_map_event;
cpu_pmu->num_events = 5;
bitmap_set(cpu_pmu->cntr_mask, 0, XSCALE2_NUM_COUNTERS);
return 0; return 0;
} }

View File

@ -107,6 +107,7 @@ struct dwc_pcie_vendor_id {
static const struct dwc_pcie_vendor_id dwc_pcie_vendor_ids[] = { static const struct dwc_pcie_vendor_id dwc_pcie_vendor_ids[] = {
{.vendor_id = PCI_VENDOR_ID_ALIBABA }, {.vendor_id = PCI_VENDOR_ID_ALIBABA },
{.vendor_id = PCI_VENDOR_ID_QCOM },
{} /* terminator */ {} /* terminator */
}; };
@ -556,10 +557,10 @@ static int dwc_pcie_register_dev(struct pci_dev *pdev)
{ {
struct platform_device *plat_dev; struct platform_device *plat_dev;
struct dwc_pcie_dev_info *dev_info; struct dwc_pcie_dev_info *dev_info;
u32 bdf; u32 sbdf;
bdf = PCI_DEVID(pdev->bus->number, pdev->devfn); sbdf = (pci_domain_nr(pdev->bus) << 16) | PCI_DEVID(pdev->bus->number, pdev->devfn);
plat_dev = platform_device_register_data(NULL, "dwc_pcie_pmu", bdf, plat_dev = platform_device_register_data(NULL, "dwc_pcie_pmu", sbdf,
pdev, sizeof(*pdev)); pdev, sizeof(*pdev));
if (IS_ERR(plat_dev)) if (IS_ERR(plat_dev))
@ -611,15 +612,15 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
struct pci_dev *pdev = plat_dev->dev.platform_data; struct pci_dev *pdev = plat_dev->dev.platform_data;
struct dwc_pcie_pmu *pcie_pmu; struct dwc_pcie_pmu *pcie_pmu;
char *name; char *name;
u32 bdf, val; u32 sbdf, val;
u16 vsec; u16 vsec;
int ret; int ret;
vsec = pci_find_vsec_capability(pdev, pdev->vendor, vsec = pci_find_vsec_capability(pdev, pdev->vendor,
DWC_PCIE_VSEC_RAS_DES_ID); DWC_PCIE_VSEC_RAS_DES_ID);
pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val); pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
bdf = PCI_DEVID(pdev->bus->number, pdev->devfn); sbdf = plat_dev->id;
name = devm_kasprintf(&plat_dev->dev, GFP_KERNEL, "dwc_rootport_%x", bdf); name = devm_kasprintf(&plat_dev->dev, GFP_KERNEL, "dwc_rootport_%x", sbdf);
if (!name) if (!name)
return -ENOMEM; return -ENOMEM;
@ -650,7 +651,7 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
ret = cpuhp_state_add_instance(dwc_pcie_pmu_hp_state, ret = cpuhp_state_add_instance(dwc_pcie_pmu_hp_state,
&pcie_pmu->cpuhp_node); &pcie_pmu->cpuhp_node);
if (ret) { if (ret) {
pci_err(pdev, "Error %d registering hotplug @%x\n", ret, bdf); pci_err(pdev, "Error %d registering hotplug @%x\n", ret, sbdf);
return ret; return ret;
} }
@ -663,7 +664,7 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
ret = perf_pmu_register(&pcie_pmu->pmu, name, -1); ret = perf_pmu_register(&pcie_pmu->pmu, name, -1);
if (ret) { if (ret) {
pci_err(pdev, "Error %d registering PMU @%x\n", ret, bdf); pci_err(pdev, "Error %d registering PMU @%x\n", ret, sbdf);
return ret; return ret;
} }
ret = devm_add_action_or_reset(&plat_dev->dev, dwc_pcie_unregister_pmu, ret = devm_add_action_or_reset(&plat_dev->dev, dwc_pcie_unregister_pmu,
@ -726,7 +727,6 @@ static struct platform_driver dwc_pcie_pmu_driver = {
static int __init dwc_pcie_pmu_init(void) static int __init dwc_pcie_pmu_init(void)
{ {
struct pci_dev *pdev = NULL; struct pci_dev *pdev = NULL;
bool found = false;
int ret; int ret;
for_each_pci_dev(pdev) { for_each_pci_dev(pdev) {
@ -738,11 +738,7 @@ static int __init dwc_pcie_pmu_init(void)
pci_dev_put(pdev); pci_dev_put(pdev);
return ret; return ret;
} }
found = true;
} }
if (!found)
return -ENODEV;
ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
"perf/dwc_pcie_pmu:online", "perf/dwc_pcie_pmu:online",

View File

@ -141,6 +141,22 @@ static ssize_t bus_show(struct device *dev, struct device_attribute *attr, char
} }
static DEVICE_ATTR_RO(bus); static DEVICE_ATTR_RO(bus);
static ssize_t bdf_min_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(dev_get_drvdata(dev));
return sysfs_emit(buf, "%#04x\n", pcie_pmu->bdf_min);
}
static DEVICE_ATTR_RO(bdf_min);
static ssize_t bdf_max_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(dev_get_drvdata(dev));
return sysfs_emit(buf, "%#04x\n", pcie_pmu->bdf_max);
}
static DEVICE_ATTR_RO(bdf_max);
static struct hisi_pcie_reg_pair static struct hisi_pcie_reg_pair
hisi_pcie_parse_reg_value(struct hisi_pcie_pmu *pcie_pmu, u32 reg_off) hisi_pcie_parse_reg_value(struct hisi_pcie_pmu *pcie_pmu, u32 reg_off)
{ {
@ -208,7 +224,7 @@ static void hisi_pcie_pmu_writeq(struct hisi_pcie_pmu *pcie_pmu, u32 reg_offset,
static u64 hisi_pcie_pmu_get_event_ctrl_val(struct perf_event *event) static u64 hisi_pcie_pmu_get_event_ctrl_val(struct perf_event *event)
{ {
u64 port, trig_len, thr_len, len_mode; u64 port, trig_len, thr_len, len_mode;
u64 reg = HISI_PCIE_INIT_SET; u64 reg = 0;
/* Config HISI_PCIE_EVENT_CTRL according to event. */ /* Config HISI_PCIE_EVENT_CTRL according to event. */
reg |= FIELD_PREP(HISI_PCIE_EVENT_M, hisi_pcie_get_real_event(event)); reg |= FIELD_PREP(HISI_PCIE_EVENT_M, hisi_pcie_get_real_event(event));
@ -452,10 +468,24 @@ static void hisi_pcie_pmu_set_period(struct perf_event *event)
struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu); struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw; struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx; int idx = hwc->idx;
u64 orig_cnt, cnt;
orig_cnt = hisi_pcie_pmu_read_counter(event);
local64_set(&hwc->prev_count, HISI_PCIE_INIT_VAL); local64_set(&hwc->prev_count, HISI_PCIE_INIT_VAL);
hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_CNT, idx, HISI_PCIE_INIT_VAL); hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_CNT, idx, HISI_PCIE_INIT_VAL);
hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_EXT_CNT, idx, HISI_PCIE_INIT_VAL); hisi_pcie_pmu_writeq(pcie_pmu, HISI_PCIE_EXT_CNT, idx, HISI_PCIE_INIT_VAL);
/*
* The counter maybe unwritable if the target event is unsupported.
* Check this by comparing the counts after setting the period. If
* the counts stay unchanged after setting the period then update
* the hwc->prev_count correctly. Otherwise the final counts user
* get maybe totally wrong.
*/
cnt = hisi_pcie_pmu_read_counter(event);
if (orig_cnt == cnt)
local64_set(&hwc->prev_count, cnt);
} }
static void hisi_pcie_pmu_enable_counter(struct hisi_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc) static void hisi_pcie_pmu_enable_counter(struct hisi_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc)
@ -749,6 +779,8 @@ static const struct attribute_group hisi_pcie_pmu_format_group = {
static struct attribute *hisi_pcie_pmu_bus_attrs[] = { static struct attribute *hisi_pcie_pmu_bus_attrs[] = {
&dev_attr_bus.attr, &dev_attr_bus.attr,
&dev_attr_bdf_max.attr,
&dev_attr_bdf_min.attr,
NULL NULL
}; };

View File

@ -10,7 +10,7 @@
#include <linux/perf_event.h> #include <linux/perf_event.h>
#include <linux/perf/arm_pmuv3.h> #include <linux/perf/arm_pmuv3.h>
#define ARMV8_PMU_CYCLE_IDX (ARMV8_PMU_MAX_COUNTERS - 1) #define KVM_ARMV8_PMU_MAX_COUNTERS 32
#if IS_ENABLED(CONFIG_HW_PERF_EVENTS) && IS_ENABLED(CONFIG_KVM) #if IS_ENABLED(CONFIG_HW_PERF_EVENTS) && IS_ENABLED(CONFIG_KVM)
struct kvm_pmc { struct kvm_pmc {
@ -19,14 +19,14 @@ struct kvm_pmc {
}; };
struct kvm_pmu_events { struct kvm_pmu_events {
u32 events_host; u64 events_host;
u32 events_guest; u64 events_guest;
}; };
struct kvm_pmu { struct kvm_pmu {
struct irq_work overflow_work; struct irq_work overflow_work;
struct kvm_pmu_events events; struct kvm_pmu_events events;
struct kvm_pmc pmc[ARMV8_PMU_MAX_COUNTERS]; struct kvm_pmc pmc[KVM_ARMV8_PMU_MAX_COUNTERS];
int irq_num; int irq_num;
bool created; bool created;
bool irq_level; bool irq_level;

View File

@ -17,10 +17,14 @@
#ifdef CONFIG_ARM_PMU #ifdef CONFIG_ARM_PMU
/* /*
* The ARMv7 CPU PMU supports up to 32 event counters. * The Armv7 and Armv8.8 or less CPU PMU supports up to 32 event counters.
* The Armv8.9/9.4 CPU PMU supports up to 33 event counters.
*/ */
#ifdef CONFIG_ARM
#define ARMPMU_MAX_HWEVENTS 32 #define ARMPMU_MAX_HWEVENTS 32
#else
#define ARMPMU_MAX_HWEVENTS 33
#endif
/* /*
* ARM PMU hw_event flags * ARM PMU hw_event flags
*/ */
@ -96,7 +100,7 @@ struct arm_pmu {
void (*stop)(struct arm_pmu *); void (*stop)(struct arm_pmu *);
void (*reset)(void *); void (*reset)(void *);
int (*map_event)(struct perf_event *event); int (*map_event)(struct perf_event *event);
int num_events; DECLARE_BITMAP(cntr_mask, ARMPMU_MAX_HWEVENTS);
bool secure_access; /* 32-bit ARM only */ bool secure_access; /* 32-bit ARM only */
#define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40 #define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40
DECLARE_BITMAP(pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS); DECLARE_BITMAP(pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS);

View File

@ -6,8 +6,9 @@
#ifndef __PERF_ARM_PMUV3_H #ifndef __PERF_ARM_PMUV3_H
#define __PERF_ARM_PMUV3_H #define __PERF_ARM_PMUV3_H
#define ARMV8_PMU_MAX_COUNTERS 32 #define ARMV8_PMU_MAX_GENERAL_COUNTERS 31
#define ARMV8_PMU_COUNTER_MASK (ARMV8_PMU_MAX_COUNTERS - 1) #define ARMV8_PMU_CYCLE_IDX 31
#define ARMV8_PMU_INSTR_IDX 32 /* Not accessible from AArch32 */
/* /*
* Common architectural and microarchitectural event numbers. * Common architectural and microarchitectural event numbers.
@ -227,8 +228,10 @@
*/ */
#define ARMV8_PMU_OVSR_P GENMASK(30, 0) #define ARMV8_PMU_OVSR_P GENMASK(30, 0)
#define ARMV8_PMU_OVSR_C BIT(31) #define ARMV8_PMU_OVSR_C BIT(31)
#define ARMV8_PMU_OVSR_F BIT_ULL(32) /* arm64 only */
/* Mask for writable bits is both P and C fields */ /* Mask for writable bits is both P and C fields */
#define ARMV8_PMU_OVERFLOWED_MASK (ARMV8_PMU_OVSR_P | ARMV8_PMU_OVSR_C) #define ARMV8_PMU_OVERFLOWED_MASK (ARMV8_PMU_OVSR_P | ARMV8_PMU_OVSR_C | \
ARMV8_PMU_OVSR_F)
/* /*
* PMXEVTYPER: Event selection reg * PMXEVTYPER: Event selection reg

View File

@ -1602,13 +1602,7 @@ static inline int perf_is_paranoid(void)
return sysctl_perf_event_paranoid > -1; return sysctl_perf_event_paranoid > -1;
} }
static inline int perf_allow_kernel(struct perf_event_attr *attr) int perf_allow_kernel(struct perf_event_attr *attr);
{
if (sysctl_perf_event_paranoid > 1 && !perfmon_capable())
return -EACCES;
return security_perf_event_open(attr, PERF_SECURITY_KERNEL);
}
static inline int perf_allow_cpu(struct perf_event_attr *attr) static inline int perf_allow_cpu(struct perf_event_attr *attr)
{ {

View File

@ -13351,6 +13351,15 @@ const struct perf_event_attr *perf_event_attrs(struct perf_event *event)
return &event->attr; return &event->attr;
} }
int perf_allow_kernel(struct perf_event_attr *attr)
{
if (sysctl_perf_event_paranoid > 1 && !perfmon_capable())
return -EACCES;
return security_perf_event_open(attr, PERF_SECURITY_KERNEL);
}
EXPORT_SYMBOL_GPL(perf_allow_kernel);
/* /*
* Inherit an event from parent task to child task. * Inherit an event from parent task to child task.
* *