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

* for-next/perf: (27 commits)
  perf: arm_pmuv3: Zero initialize hw_id branch stack field
  perf/arm-cmn: Support CMN S3 r2
  perf/arm-cmn: Plumb in new filter types
  perf/arm-cmn: Refactor event filter data
  perf/arm-cmn: Refactor event filter programming
  perf/arm-cmn: Rename filter variables for clarity
  perf/arm_cspmu: Support 64-bit programmers' model
  drivers/perf: hisi: Remove redundant dev_err()/dev_err_probe()
  perf: arm_cspmu: Remove redundant dev_err()
  perf: Remove redundant dev_err()/dev_err_probe()
  perf: arm_pmu_acpi: Get rid of the edge-triggered interrupt oddity
  perf/arm_cspmu: Make IRQ more optional
  perf/arm_cspmu: Improve sub-module error reporting
  perf/arm_cspmu: Improve APMT-based PMU naming
  ACPI/APMT: Use stable device ID
  perf/cxlpmu: Support Channel/Rank/Bank filter
  perf/cxlpmu: Add missing CXL 4.0 events
  perf/cxlpmu: Fix 64-bit write to 32-bit HDM filter register
  perf/dwc_pcie: Support narrowed time-based counter for long time monitoring
  perf/dwc_pcie: Add support for Picoheart vendor devices
  ...
This commit is contained in:
Will Deacon 2026-08-14 10:16:11 +00:00
commit de5e5f9b99
26 changed files with 1079 additions and 303 deletions

View File

@ -4,16 +4,22 @@
$id: http://devicetree.org/schemas/perf/marvell-cn10k-ddr.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Marvell CN10K DDR performance monitor
title: Marvell CN10K / CN20K DDR performance monitor
description:
Performance Monitoring Unit (PMU) for the DDR controller on Marvell
CN10K and CN20K SoCs. The block is accessed via a dedicated MMIO region.
maintainers:
- Bharat Bhushan <bbhushan2@marvell.com>
- Geetha sowjanya <gakula@marvell.com>
properties:
compatible:
items:
- enum:
- marvell,cn10k-ddr-pmu
- marvell,cn20k-ddr-pmu
reg:
maxItems: 1

View File

@ -76,10 +76,12 @@ static int __init apmt_add_platform_device(struct acpi_apmt_node *node,
struct fwnode_handle *fwnode)
{
struct platform_device *pdev;
int ret, count;
int ret, count, uid = node->id & INT_MAX;
struct resource res[DEV_MAX_RESOURCE_COUNT];
pdev = platform_device_alloc(DEV_NAME, PLATFORM_DEVID_AUTO);
if (uid != node->id)
pr_warn("Unexpectedly large UID 0x%x, truncated to 0x%x\n", node->id, uid);
pdev = platform_device_alloc(DEV_NAME, uid);
if (!pdev)
return -ENOMEM;

View File

@ -449,11 +449,8 @@ static struct ali_drw_pmu_irq *__ali_drw_pmu_init_irq(struct platform_device
*/
ret = devm_request_irq(&pdev->dev, irq_num, ali_drw_pmu_isr,
IRQF_SHARED, dev_name(&pdev->dev), irq);
if (ret < 0) {
dev_err(&pdev->dev,
"Fail to request IRQ:%d ret:%d\n", irq_num, ret);
if (ret < 0)
goto out_free;
}
ret = irq_set_affinity_hint(irq_num, cpumask_of(irq->cpu));
if (ret)

File diff suppressed because it is too large Load Diff

View File

@ -78,13 +78,13 @@ static struct acpi_apmt_node *arm_cspmu_apmt_node(struct device *dev)
}
/*
* In CoreSight PMU architecture, all of the MMIO registers are 32-bit except
* counter register. The counter register can be implemented as 32-bit or 64-bit
* register depending on the value of PMCFGR.SIZE field. For 64-bit access,
* single-copy 64-bit atomic support is implementation defined. APMT node flag
* is used to identify if the PMU supports 64-bit single copy atomic. If 64-bit
* single copy atomic is not supported, the driver treats the register as a pair
* of 32-bit register.
* With FEAT_CSPMU_EXT32, all of the MMIO registers are 32-bit except the
* counter registers, which are either 32-bit or 64-bit depending on the value
* of PMCFGR.SIZE. It is implementation-defined whether single-copy-atomic
* 64-bit accesses are supported, so we rely on a firmware flag to identify
* that, and otherwise treat a 64-bit counter as a non-atomic pair of 32-bit
* registers. With FEAT_CSPMU_EXT64, everything is 64-bit, but we may still
* have to deal with atomicity being broken.
*/
/*
@ -173,13 +173,30 @@ arm_cspmu_event_attr_is_visible(struct kobject *kobj,
eattr = container_of(attr, typeof(*eattr), attr.attr);
/* Hide cycle event if not supported */
if (!supports_cycle_counter(cspmu) &&
if ((cspmu->has_ext64 || !supports_cycle_counter(cspmu)) &&
eattr->id == ARM_CSPMU_EVT_CYCLES_DEFAULT)
return 0;
return attr->mode;
}
ssize_t arm_cspmu_default_format_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct perf_pmu_events_attr *fmt = container_of(attr, typeof(*fmt), attr);
struct arm_cspmu *cspmu = to_arm_cspmu(dev_get_drvdata(dev));
u64 field = cspmu->has_ext64 ? U64_MAX : U32_MAX;
DECLARE_BITMAP(bits, 64) = { BITMAP_FROM_U64(field) };
if (!fmt->id) {
set_bit(32, bits); /* For 32-bit "cycles" event */
return sysfs_emit(buf, "config:%*pbl\n", 64, bits);
}
return sysfs_emit(buf, "config%lld:%*pbl\n", fmt->id, 64, bits);
}
EXPORT_SYMBOL_GPL(arm_cspmu_default_format_show);
static struct attribute *arm_cspmu_format_attrs[] = {
ARM_CSPMU_FORMAT_EVENT_ATTR,
ARM_CSPMU_FORMAT_FILTER_ATTR,
@ -198,9 +215,9 @@ arm_cspmu_get_format_attrs(const struct arm_cspmu *cspmu)
return attrs;
}
static u32 arm_cspmu_event_type(const struct perf_event *event)
static u64 arm_cspmu_event_type(const struct perf_event *event)
{
return event->attr.config & ARM_CSPMU_EVENT_MASK;
return event->attr.config;
}
static bool arm_cspmu_is_cycle_counter_event(const struct perf_event *event)
@ -208,6 +225,16 @@ static bool arm_cspmu_is_cycle_counter_event(const struct perf_event *event)
return (event->attr.config == ARM_CSPMU_EVT_CYCLES_DEFAULT);
}
static u64 arm_cspmu_filter(const struct perf_event *event)
{
return event->attr.config1;
}
static u64 arm_cspmu_filter2(const struct perf_event *event)
{
return event->attr.config2;
}
static ssize_t arm_cspmu_identifier_show(struct device *dev,
struct device_attribute *attr,
char *page)
@ -250,38 +277,43 @@ static const char *arm_cspmu_get_name(const struct arm_cspmu *cspmu)
struct device *dev;
struct acpi_apmt_node *apmt_node;
u8 pmu_type;
char *name;
char acpi_hid_string[ACPI_ID_LEN] = { 0 };
static atomic_t pmu_idx[ACPI_APMT_NODE_TYPE_COUNT] = { 0 };
static atomic_t pmu_idx;
u32 id;
dev = cspmu->dev;
apmt_node = arm_cspmu_apmt_node(dev);
if (!apmt_node)
return devm_kasprintf(dev, GFP_KERNEL, PMUNAME "_%u",
atomic_fetch_inc(&pmu_idx[0]));
atomic_fetch_inc(&pmu_idx));
pmu_type = apmt_node->type;
if (pmu_type >= ACPI_APMT_NODE_TYPE_COUNT) {
switch (pmu_type) {
default:
dev_err(dev, "unsupported PMU type-%u\n", pmu_type);
return NULL;
}
if (pmu_type == ACPI_APMT_NODE_TYPE_ACPI) {
case ACPI_APMT_NODE_TYPE_ACPI:
memcpy(acpi_hid_string,
&apmt_node->inst_primary,
sizeof(apmt_node->inst_primary));
name = devm_kasprintf(dev, GFP_KERNEL, "%s_%s_%s_%u", PMUNAME,
return devm_kasprintf(dev, GFP_KERNEL, "%s_%s_%s_%u", PMUNAME,
arm_cspmu_type_str[pmu_type],
acpi_hid_string,
apmt_node->inst_secondary);
} else {
name = devm_kasprintf(dev, GFP_KERNEL, "%s_%s_%d", PMUNAME,
arm_cspmu_type_str[pmu_type],
atomic_fetch_inc(&pmu_idx[pmu_type]));
case ACPI_APMT_NODE_TYPE_MC:
id = apmt_node->id;
break;
case ACPI_APMT_NODE_TYPE_SMMU:
case ACPI_APMT_NODE_TYPE_PCIE_ROOT:
id = apmt_node->inst_primary;
break;
case ACPI_APMT_NODE_TYPE_CACHE:
id = apmt_node->inst_secondary;
break;
}
return name;
return devm_kasprintf(dev, GFP_KERNEL, "%s_%s_%u", PMUNAME,
arm_cspmu_type_str[pmu_type], id);
}
static ssize_t arm_cspmu_cpumask_show(struct device *dev,
@ -412,6 +444,17 @@ static int arm_cspmu_init_impl_ops(struct arm_cspmu *cspmu)
DEFAULT_IMPL_OP(event_attr_is_visible),
};
/*
* With 64-bit events, since our default "cycles" encoding won't work,
* and the architecture recommends against implementing it anyway, we
* choose to effectively ignore FEAT_CSPMU_CCNTR, unless a vendor
* module really wants to provide its own encoding and ops.
*/
if (cspmu->has_ext64) {
cspmu->impl.ops.is_cycle_counter_event = NULL;
cspmu->impl.ops.set_cc_filter = NULL;
}
/* Firmware may override implementer/product ID from PMIIDR */
if (apmt_node && apmt_node->impl_id)
cspmu->impl.pmiidr = apmt_node->impl_id;
@ -432,13 +475,15 @@ static int arm_cspmu_init_impl_ops(struct arm_cspmu *cspmu)
if (ret)
module_put(match->module);
} else {
WARN(1, "arm_cspmu failed to get module: %s\n",
dev_WARN(cspmu->dev, "Failed to get module: %s\n",
match->module_name);
ret = -EINVAL;
}
} else {
request_module_nowait(match->module_name);
ret = -EPROBE_DEFER;
ret = dev_err_probe(cspmu->dev, -EPROBE_DEFER,
"Waiting for module %s to load\n",
match->module_name);
}
mutex_unlock(&arm_cspmu_lock);
@ -511,19 +556,24 @@ static int arm_cspmu_alloc_attr_groups(struct arm_cspmu *cspmu)
return 0;
}
static inline int arm_cspmu_pmcr(struct arm_cspmu *cspmu)
{
return cspmu->has_ext64 ? PMCR_64 : PMCR;
}
static inline void arm_cspmu_reset_counters(struct arm_cspmu *cspmu)
{
writel(PMCR_C | PMCR_P, cspmu->base0 + PMCR);
writel(PMCR_C | PMCR_P, cspmu->base0 + arm_cspmu_pmcr(cspmu));
}
static inline void arm_cspmu_start_counters(struct arm_cspmu *cspmu)
{
writel(PMCR_E, cspmu->base0 + PMCR);
writel(PMCR_E, cspmu->base0 + arm_cspmu_pmcr(cspmu));
}
static inline void arm_cspmu_stop_counters(struct arm_cspmu *cspmu)
{
writel(0, cspmu->base0 + PMCR);
writel(0, cspmu->base0 + arm_cspmu_pmcr(cspmu));
}
static void arm_cspmu_enable(struct pmu *pmu)
@ -554,7 +604,8 @@ static int arm_cspmu_get_event_idx(struct arm_cspmu_hw_events *hw_events,
struct arm_cspmu *cspmu = to_arm_cspmu(event->pmu);
if (supports_cycle_counter(cspmu)) {
if (cspmu->impl.ops.is_cycle_counter_event(event)) {
if (cspmu->impl.ops.is_cycle_counter_event &&
cspmu->impl.ops.is_cycle_counter_event(event)) {
/* Search for available cycle counter. */
if (test_and_set_bit(cspmu->cycle_counter_logical_idx,
hw_events->used_ctrs))
@ -797,26 +848,33 @@ static void arm_cspmu_event_update(struct perf_event *event)
static inline void arm_cspmu_set_event(struct arm_cspmu *cspmu,
struct hw_perf_event *hwc)
{
u32 offset = PMEVTYPER + (4 * hwc->idx);
writel(hwc->config, cspmu->base0 + offset);
if (cspmu->has_ext64)
writeq(hwc->config, cspmu->base0 + PMEVTYPER + (8 * hwc->idx));
else
writel(hwc->config, cspmu->base0 + PMEVTYPER + (4 * hwc->idx));
}
static void arm_cspmu_set_ev_filter(struct arm_cspmu *cspmu,
const struct perf_event *event)
{
u32 filter = event->attr.config1 & ARM_CSPMU_FILTER_MASK;
u32 filter2 = event->attr.config2 & ARM_CSPMU_FILTER_MASK;
u32 offset = 4 * event->hw.idx;
u64 filter = arm_cspmu_filter(event);
u64 filter2 = arm_cspmu_filter2(event);
int n = event->hw.idx;
writel(filter, cspmu->base0 + PMEVFILTR + offset);
writel(filter2, cspmu->base0 + PMEVFILT2R + offset);
if (cspmu->has_ext64) {
writeq(filter, cspmu->base0 + PMEVFILTR + (8 * n));
writeq(filter2, cspmu->base0 + PMEVFILT2R + (8 * n));
} else {
writel(filter, cspmu->base0 + PMEVFILTR + (4 * n));
writel(filter2, cspmu->base0 + PMEVFILT2R + (4 * n));
}
}
/* Note we deliberately don't expect 64-bit filters here; see init_impl_ops */
static void arm_cspmu_set_cc_filter(struct arm_cspmu *cspmu,
const struct perf_event *event)
{
u32 filter = event->attr.config1 & ARM_CSPMU_FILTER_MASK;
u32 filter = arm_cspmu_filter(event);
writel(filter, cspmu->base0 + PMCCFILTR);
}
@ -969,6 +1027,30 @@ static int arm_cspmu_init_mmio(struct arm_cspmu *cspmu)
}
}
/*
* We can infer FEAT_CSPMU_EXT64 from PMCNTEN, or hope that anything
* that failed to get that right has at least implemented the optional
* PMDEVARCH correctly...
*
* Note that architecturally, has_ext64 *should* imply has_atomic_dword,
* but enough implementations have ignored that already that we'll just
* have to still rely on the firmware flag.
*/
writel(~0U, cspmu->base0 + PMCNTENCLR);
writel(~0U, cspmu->base0 + PMCNTEN);
if (readl(cspmu->base0 + PMCNTENCLR)) {
cspmu->has_ext64 = true;
writel(0, cspmu->base0 + PMCNTEN);
} else {
u32 reg = readl(cspmu->base0 + PMDEVARCH);
if (reg & ARM_CSPMU_PMDEVARCH_PRESENT) {
reg &= ARM_CSPMU_PMDEVARCH_ARCHPART;
if (reg == 0xaf4 || reg == 0xaf5)
cspmu->has_ext64 = true;
}
}
cspmu->pmcfgr = readl(cspmu->base0 + PMCFGR);
cspmu->num_logical_ctrs = FIELD_GET(PMCFGR_N, cspmu->pmcfgr) + 1;
@ -1070,10 +1152,8 @@ static int arm_cspmu_request_irq(struct arm_cspmu *cspmu)
ret = devm_request_irq(dev, irq, arm_cspmu_handle_irq,
IRQF_NOBALANCING | IRQF_NO_THREAD, dev_name(dev),
cspmu);
if (ret) {
dev_err(dev, "Could not request IRQ %d\n", irq);
if (ret)
return ret;
}
cspmu->irq = irq;
@ -1249,8 +1329,11 @@ static int arm_cspmu_device_probe(struct platform_device *pdev)
return ret;
ret = arm_cspmu_request_irq(cspmu);
if (ret)
return ret;
if (ret) {
if (counter_size(cspmu) < 64)
return ret;
dev_info(cspmu->dev, "Continuing without IRQ\n");
}
ret = arm_cspmu_get_cpus(cspmu);
if (ret)

View File

@ -35,21 +35,15 @@
PMU_EVENT_ATTR_ID(_name, arm_cspmu_sysfs_event_show, _config)
/* Default event id mask */
#define ARM_CSPMU_EVENT_MASK GENMASK_ULL(63, 0)
/* Default filter value mask */
#define ARM_CSPMU_FILTER_MASK GENMASK_ULL(63, 0)
/* Default event format */
#define ARM_CSPMU_FORMAT_EVENT_ATTR \
ARM_CSPMU_FORMAT_ATTR(event, "config:0-32")
PMU_EVENT_ATTR_ID(event, arm_cspmu_default_format_show, 0)
/* Default filter format */
#define ARM_CSPMU_FORMAT_FILTER_ATTR \
ARM_CSPMU_FORMAT_ATTR(filter, "config1:0-31")
PMU_EVENT_ATTR_ID(filter, arm_cspmu_default_format_show, 1)
#define ARM_CSPMU_FORMAT_FILTER2_ATTR \
ARM_CSPMU_FORMAT_ATTR(filter2, "config2:0-31")
PMU_EVENT_ATTR_ID(filter2, arm_cspmu_default_format_show, 2)
/*
* This is the default event number for cycle count, if supported, since the
@ -78,6 +72,7 @@
#define PMEVFILT2R 0x800
#define PMEVFILTR 0xA00
#define PMCNTENSET 0xC00
#define PMCNTEN 0xC10
#define PMCNTENCLR 0xC20
#define PMINTENSET 0xC40
#define PMINTENCLR 0xC60
@ -87,6 +82,8 @@
#define PMCFGR 0xE00
#define PMCR 0xE04
#define PMIIDR 0xE08
#define PMCR_64 0xE10
#define PMDEVARCH 0xFBC
#define PMPIDR0 0xFE0
#define PMPIDR1 0xFE4
#define PMPIDR2 0xFE8
@ -154,6 +151,10 @@
#define ARM_CSPMU_IMPL_ID_NVIDIA 0x36B
#define ARM_CSPMU_IMPL_ID_AMPERE 0xA16
/* PMDEVARCH */
#define ARM_CSPMU_PMDEVARCH_PRESENT BIT(20)
#define ARM_CSPMU_PMDEVARCH_ARCHPART GENMASK(11, 0)
struct arm_cspmu;
/* This tracks the events assigned to each counter in the PMU. */
@ -183,7 +184,7 @@ struct arm_cspmu_impl_ops {
/* Check if the event corresponds to cycle count event */
bool (*is_cycle_counter_event)(const struct perf_event *event);
/* Decode event type/id from configs */
u32 (*event_type)(const struct perf_event *event);
u64 (*event_type)(const struct perf_event *event);
/* Set/reset event filters */
void (*set_cc_filter)(struct arm_cspmu *cspmu,
const struct perf_event *event);
@ -234,6 +235,7 @@ struct arm_cspmu {
int irq;
bool has_atomic_dword;
bool has_ext64;
u32 pmcfgr;
u32 num_logical_ctrs;
u32 num_set_clr_reg;
@ -250,6 +252,9 @@ ssize_t arm_cspmu_sysfs_event_show(struct device *dev,
struct device_attribute *attr,
char *buf);
ssize_t arm_cspmu_default_format_show(struct device *dev,
struct device_attribute *attr, char *buf);
/* Register vendor backend. */
int arm_cspmu_impl_register(const struct arm_cspmu_impl_match *impl_match);

View File

@ -762,7 +762,7 @@ static void pcie_tgt_pmu_reset_ev_filter(struct arm_cspmu *cspmu,
pcie_tgt_pmu_config_addr_filter(cspmu, false, base, mask, idx);
}
static u32 pcie_tgt_pmu_event_type(const struct perf_event *event)
static u64 pcie_tgt_pmu_event_type(const struct perf_event *event)
{
return event->attr.config & NV_PCIE_TGT_EV_TYPE_MASK;
}

View File

@ -931,8 +931,13 @@ int armpmu_register(struct arm_pmu *pmu)
/*
* By this stage we know our supported CPUs on either DT/ACPI platforms,
* detect the SMT implementation.
* On SMT CPUs, the PMCCNTR_EL0 increments from the processor clock rather
* than the PE clock (ARM DDI0487 L.b D13.1.3) which means it'll continue
* counting on a WFI PE if one of its SMT sibling is not idle on a
* multi-threaded implementation. So don't use it on SMT cores.
*/
pmu->has_smt = topology_core_has_smt(cpumask_first(&pmu->supported_cpus));
pmu->avoid_pmccntr |=
topology_core_has_smt(cpumask_first(&pmu->supported_cpus));
if (!pmu->set_event_filter)
pmu->pmu.capabilities |= PERF_PMU_CAP_NO_EXCLUDE;

View File

@ -22,7 +22,7 @@ static DEFINE_PER_CPU(int, pmu_irqs);
static int arm_pmu_acpi_register_irq(int cpu)
{
struct acpi_madt_generic_interrupt *gicc;
int gsi, trigger;
int gsi;
gicc = acpi_cpu_get_madt_gicc(cpu);
@ -38,11 +38,6 @@ static int arm_pmu_acpi_register_irq(int cpu)
if (!gsi)
return 0;
if (gicc->flags & ACPI_MADT_PERFORMANCE_IRQ_MODE)
trigger = ACPI_EDGE_SENSITIVE;
else
trigger = ACPI_LEVEL_SENSITIVE;
/*
* Helpfully, the MADT GICC doesn't have a polarity flag for the
* "performance interrupt". Luckily, on compliant GICs the polarity is
@ -53,8 +48,12 @@ static int arm_pmu_acpi_register_irq(int cpu)
* may not match the real polarity, but that should not matter.
*
* Other interrupt controllers are not supported with ACPI.
*
* The spec also indicates that the PMU interrupt can be edge
* triggered, which doesn't make any sense (SW needs to clear the
* interrupt condition for the level to drop). Ignore the silly flag.
*/
return acpi_register_gsi(NULL, gsi, trigger, ACPI_ACTIVE_HIGH);
return acpi_register_gsi(NULL, gsi, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_HIGH);
}
static void arm_pmu_acpi_unregister_irq(int cpu)

View File

@ -8,6 +8,7 @@
* This code is based heavily on the ARMv7 perf event code.
*/
#include <asm/cputype.h>
#include <asm/irq_regs.h>
#include <asm/perf_event.h>
#include <asm/virt.h>
@ -795,6 +796,7 @@ static void armv8pmu_disable_user_access(void)
static void armv8pmu_enable_user_access(struct arm_pmu *cpu_pmu)
{
int i;
u64 userenr = ARMV8_PMU_USERENR_ER | ARMV8_PMU_USERENR_UEN;
struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events);
if (is_pmuv3p9(cpu_pmu->pmuver)) {
@ -817,7 +819,10 @@ static void armv8pmu_enable_user_access(struct arm_pmu *cpu_pmu)
}
}
update_pmuserenr(ARMV8_PMU_USERENR_ER | ARMV8_PMU_USERENR_CR | ARMV8_PMU_USERENR_UEN);
if (!cpu_pmu->avoid_pmccntr)
userenr |= ARMV8_PMU_USERENR_CR;
update_pmuserenr(userenr);
}
static void armv8pmu_enable_event(struct perf_event *event)
@ -1002,13 +1007,7 @@ static bool armv8pmu_can_use_pmccntr(struct pmu_hw_events *cpuc,
if (has_branch_stack(event))
return false;
/*
* The PMCCNTR_EL0 increments from the processor clock rather than
* the PE clock (ARM DDI0487 L.b D13.1.3) which means it'll continue
* counting on a WFI PE if one of its SMT sibling is not idle on a
* multi-threaded implementation. So don't use it on SMT cores.
*/
if (cpu_pmu->has_smt)
if (cpu_pmu->avoid_pmccntr)
return false;
return true;
@ -1250,7 +1249,8 @@ static int __armv8_pmuv3_map_event(struct perf_event *event,
if (!(event->attach_state & PERF_ATTACH_TASK))
return -EINVAL;
if (armv8pmu_event_is_64bit(event) &&
(hw_event_id != ARMV8_PMUV3_PERFCTR_CPU_CYCLES) &&
(hw_event_id != ARMV8_PMUV3_PERFCTR_CPU_CYCLES ||
armpmu->avoid_pmccntr) &&
!armv8pmu_has_long_event(armpmu))
return -EOPNOTSUPP;
@ -1299,6 +1299,45 @@ static int armv8_vulcan_map_event(struct perf_event *event)
&armv8_vulcan_perf_cache_map);
}
#ifdef CONFIG_ARM64
/*
* List of CPUs that should avoid using PMCCNTR_EL0.
*/
static struct midr_range armv8pmu_avoid_pmccntr_cpus[] = {
/*
* NVIDIA Olympus may expose different WFI/WFE behaviour between the
* PMCCNTR_EL0 and the CPU_CYCLES event on programmable counters.
* While the CPU is in WFI/WFE state, the PMCCNTR_EL0 may still increment
* but the programmable counter may not. This is an implementation specific
* behavior and not an erratum. Perf assumes those two paths are
* interchangeable, so avoid using PMCCNTR_EL0 for CPU_CYCLES event.
*
* From ARM DDI0487 D14.4:
* It is IMPLEMENTATION SPECIFIC whether CPU_CYCLES and PMCCNTR count
* when the PE is in WFI or WFE state, even if the clocks are not stopped.
*
* From ARM DDI0487 D24.5.2:
* All counters are subject to any changes in clock frequency, including
* clock stopping caused by the WFI and WFE instructions.
* This means that it is CONSTRAINED UNPREDICTABLE whether or not
* PMCCNTR_EL0 continues to increment when clocks are stopped by WFI and
* WFE instructions.
*/
MIDR_ALL_VERSIONS(MIDR_NVIDIA_OLYMPUS),
{}
};
static bool armv8pmu_is_in_avoid_pmccntr_cpus(void)
{
return is_midr_in_range_list(armv8pmu_avoid_pmccntr_cpus);
}
#else
static bool armv8pmu_is_in_avoid_pmccntr_cpus(void)
{
return false;
}
#endif
struct armv8pmu_probe_info {
struct arm_pmu *pmu;
bool present;
@ -1348,6 +1387,13 @@ static void __armv8pmu_probe_pmu(void *info)
else
cpu_pmu->reg_pmmir = 0;
/*
* On some CPUs, PMCCNTR_EL0 does not match the behavior of CPU_CYCLES
* programmable counter, so avoid routing cycles through PMCCNTR_EL0 to
* prevent inconsistency in the results.
*/
cpu_pmu->avoid_pmccntr |= armv8pmu_is_in_avoid_pmccntr_cpus();
brbe_probe(cpu_pmu);
}
@ -1361,7 +1407,7 @@ static int branch_records_alloc(struct arm_pmu *armpmu)
struct pmu_hw_events *events_cpu;
events_cpu = per_cpu_ptr(armpmu->hw_events, cpu);
events_cpu->branch_stack = kmalloc(size, GFP_KERNEL);
events_cpu->branch_stack = kzalloc(size, GFP_KERNEL);
if (!events_cpu->branch_stack)
return -ENOMEM;
}

View File

@ -577,7 +577,7 @@ static u64 __arm_spe_pmu_next_off(struct perf_output_handle *handle)
* the page boundary following it. Keep the tail boundary if
* that's lower.
*/
if (handle->wakeup < (handle->head + handle->size) && head <= wakeup)
if ((handle->wakeup - handle->head) < handle->size && head <= wakeup)
limit = min(limit, round_up(wakeup, PAGE_SIZE));
if (limit > head)

View File

@ -77,6 +77,10 @@
#define CXL_PMU_GID_S2M_NDR 0x0024
#define CXL_PMU_GID_S2M_DRS 0x0025
#define CXL_PMU_GID_DDR 0x8000
#define CXL_PMU_GID_QUEUE_OCC 0x8001
#define CXL_PMU_GID_QUEUE_RESID 0x8002
#define CXL_PMU_GID_RETRY_EVENTS 0x8003
#define CXL_PMU_GID_THROTTLE 0x8004
static int cxl_pmu_cpuhp_state_num;
@ -106,6 +110,7 @@ struct cxl_pmu_info {
int on_cpu;
struct hlist_node node;
bool filter_hdm;
bool filter_crb;
int irq;
};
@ -142,6 +147,8 @@ static int cxl_pmu_parse_caps(struct device *dev, struct cxl_pmu_info *info)
info->num_event_capabilities = FIELD_GET(CXL_PMU_CAP_NUM_EVN_CAP_REG_SUP_MSK, val) + 1;
info->filter_hdm = FIELD_GET(CXL_PMU_CAP_FILTERS_SUP_MSK, val) & CXL_PMU_FILTER_HDM;
info->filter_crb = FIELD_GET(CXL_PMU_CAP_FILTERS_SUP_MSK, val) &
CXL_PMU_FILTER_CHAN_RANK_BANK;
if (FIELD_GET(CXL_PMU_CAP_INT, val))
info->irq = FIELD_GET(CXL_PMU_CAP_MSI_N_MSK, val);
else
@ -225,6 +232,8 @@ enum {
cxl_pmu_edge_attr,
cxl_pmu_hdm_filter_en_attr,
cxl_pmu_hdm_attr,
cxl_pmu_crb_filter_en_attr,
cxl_pmu_crb_attr,
};
static struct attribute *cxl_pmu_format_attr[] = {
@ -236,6 +245,8 @@ static struct attribute *cxl_pmu_format_attr[] = {
[cxl_pmu_edge_attr] = CXL_PMU_FORMAT_ATTR(edge, "config1:17"),
[cxl_pmu_hdm_filter_en_attr] = CXL_PMU_FORMAT_ATTR(hdm_filter_en, "config1:18"),
[cxl_pmu_hdm_attr] = CXL_PMU_FORMAT_ATTR(hdm, "config2:0-15"),
[cxl_pmu_crb_filter_en_attr] = CXL_PMU_FORMAT_ATTR(crb_filter_en, "config1:19"),
[cxl_pmu_crb_attr] = CXL_PMU_FORMAT_ATTR(crb, "config2:32-63"),
NULL
};
@ -246,7 +257,9 @@ static struct attribute *cxl_pmu_format_attr[] = {
#define CXL_PMU_ATTR_CONFIG1_INVERT_MSK BIT(16)
#define CXL_PMU_ATTR_CONFIG1_EDGE_MSK BIT(17)
#define CXL_PMU_ATTR_CONFIG1_FILTER_EN_MSK BIT(18)
#define CXL_PMU_ATTR_CONFIG1_CRB_FILTER_EN_MSK BIT(19)
#define CXL_PMU_ATTR_CONFIG2_HDM_MSK GENMASK(15, 0)
#define CXL_PMU_ATTR_CONFIG2_CRB_MSK GENMASK_ULL(63, 32)
static umode_t cxl_pmu_format_is_visible(struct kobject *kobj,
struct attribute *attr, int a)
@ -263,6 +276,11 @@ static umode_t cxl_pmu_format_is_visible(struct kobject *kobj,
attr == cxl_pmu_format_attr[cxl_pmu_hdm_attr]))
return 0;
if (!info->filter_crb &&
(attr == cxl_pmu_format_attr[cxl_pmu_crb_filter_en_attr] ||
attr == cxl_pmu_format_attr[cxl_pmu_crb_attr]))
return 0;
return attr->mode;
}
@ -319,6 +337,17 @@ static u16 cxl_pmu_config2_get_hdm_decoder(struct perf_event *event)
return FIELD_GET(CXL_PMU_ATTR_CONFIG2_HDM_MSK, event->attr.config2);
}
static u16 cxl_pmu_config1_crb_filter_en(struct perf_event *event)
{
return FIELD_GET(CXL_PMU_ATTR_CONFIG1_CRB_FILTER_EN_MSK,
event->attr.config1);
}
static u32 cxl_pmu_config2_get_crb(struct perf_event *event)
{
return FIELD_GET(CXL_PMU_ATTR_CONFIG2_CRB_MSK, event->attr.config2);
}
static ssize_t cxl_pmu_event_sysfs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -385,13 +414,23 @@ static struct attribute *cxl_pmu_event_attrs[] = {
CXL_PMU_EVENT_CXL_ATTR(m2s_req_memwrfwd, CXL_PMU_GID_M2S_REQ, BIT(4)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_memrdtee, CXL_PMU_GID_M2S_REQ, BIT(5)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_memrddatatee, CXL_PMU_GID_M2S_REQ, BIT(6)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_meminvtee, CXL_PMU_GID_M2S_REQ, BIT(7)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_memspecrd, CXL_PMU_GID_M2S_REQ, BIT(8)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_meminvnt, CXL_PMU_GID_M2S_REQ, BIT(9)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_memcleanevict, CXL_PMU_GID_M2S_REQ, BIT(10)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_meminvptee, CXL_PMU_GID_M2S_REQ, BIT(11)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_memspecrdtee, CXL_PMU_GID_M2S_REQ, BIT(12)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_teupdate, CXL_PMU_GID_M2S_REQ, BIT(13)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_memclnevcttee, CXL_PMU_GID_M2S_REQ, BIT(14)),
CXL_PMU_EVENT_CXL_ATTR(m2s_req_memclnevctu, CXL_PMU_GID_M2S_REQ, BIT(15)),
/* CXL rev 3.0 Table 3-35 M2S RwD Memory Opcodes */
CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memwr, CXL_PMU_GID_M2S_RWD, BIT(1)),
CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memwrptl, CXL_PMU_GID_M2S_RWD, BIT(2)),
CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_biconflict, CXL_PMU_GID_M2S_RWD, BIT(4)),
CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memrdfill, CXL_PMU_GID_M2S_RWD, BIT(5)),
CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memwrtee, CXL_PMU_GID_M2S_RWD, BIT(9)),
CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memwrptltee, CXL_PMU_GID_M2S_RWD, BIT(10)),
CXL_PMU_EVENT_CXL_ATTR(m2s_rwd_memrdfilltee, CXL_PMU_GID_M2S_RWD, BIT(13)),
/* CXL rev 3.0 Table 3-38 M2S BIRsp Memory Opcodes */
CXL_PMU_EVENT_CXL_ATTR(m2s_birsp_i, CXL_PMU_GID_M2S_BIRSP, BIT(0)),
CXL_PMU_EVENT_CXL_ATTR(m2s_birsp_s, CXL_PMU_GID_M2S_BIRSP, BIT(1)),
@ -406,15 +445,25 @@ static struct attribute *cxl_pmu_event_attrs[] = {
CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_curblk, CXL_PMU_GID_S2M_BISNP, BIT(4)),
CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_datblk, CXL_PMU_GID_S2M_BISNP, BIT(5)),
CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_invblk, CXL_PMU_GID_S2M_BISNP, BIT(6)),
CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_curtee, CXL_PMU_GID_S2M_BISNP, BIT(8)),
CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_datatee, CXL_PMU_GID_S2M_BISNP, BIT(9)),
CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_invtee, CXL_PMU_GID_S2M_BISNP, BIT(10)),
CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_curblktee, CXL_PMU_GID_S2M_BISNP, BIT(12)),
CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_datablktee, CXL_PMU_GID_S2M_BISNP, BIT(13)),
CXL_PMU_EVENT_CXL_ATTR(s2m_bisnp_invblktee, CXL_PMU_GID_S2M_BISNP, BIT(14)),
/* CXL rev 3.1 Table 3-50 S2M NDR Opcodes */
CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmp, CXL_PMU_GID_S2M_NDR, BIT(0)),
CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmps, CXL_PMU_GID_S2M_NDR, BIT(1)),
CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmpe, CXL_PMU_GID_S2M_NDR, BIT(2)),
CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmpm, CXL_PMU_GID_S2M_NDR, BIT(3)),
CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_biconflictack, CXL_PMU_GID_S2M_NDR, BIT(4)),
CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmptee, CXL_PMU_GID_S2M_NDR, BIT(5)),
CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmptee_s, CXL_PMU_GID_S2M_NDR, BIT(6)),
CXL_PMU_EVENT_CXL_ATTR(s2m_ndr_cmptee_e, CXL_PMU_GID_S2M_NDR, BIT(7)),
/* CXL rev 3.0 Table 3-46 S2M DRS opcodes */
CXL_PMU_EVENT_CXL_ATTR(s2m_drs_memdata, CXL_PMU_GID_S2M_DRS, BIT(0)),
CXL_PMU_EVENT_CXL_ATTR(s2m_drs_memdatanxm, CXL_PMU_GID_S2M_DRS, BIT(1)),
CXL_PMU_EVENT_CXL_ATTR(s2m_drs_memdatatee, CXL_PMU_GID_S2M_DRS, BIT(2)),
/* CXL rev 3.0 Table 13-5 directly lists these */
CXL_PMU_EVENT_CXL_ATTR(ddr_act, CXL_PMU_GID_DDR, BIT(0)),
CXL_PMU_EVENT_CXL_ATTR(ddr_pre, CXL_PMU_GID_DDR, BIT(1)),
@ -423,6 +472,32 @@ static struct attribute *cxl_pmu_event_attrs[] = {
CXL_PMU_EVENT_CXL_ATTR(ddr_refresh, CXL_PMU_GID_DDR, BIT(4)),
CXL_PMU_EVENT_CXL_ATTR(ddr_selfrefreshent, CXL_PMU_GID_DDR, BIT(5)),
CXL_PMU_EVENT_CXL_ATTR(ddr_rfm, CXL_PMU_GID_DDR, BIT(6)),
/* CXL 4.0 Table 13-5 DDR add-on events opcodes */
CXL_PMU_EVENT_CXL_ATTR(ddr_cas_rd_ap, CXL_PMU_GID_DDR, BIT(7)),
CXL_PMU_EVENT_CXL_ATTR(ddr_cas_wr_ap, CXL_PMU_GID_DDR, BIT(8)),
CXL_PMU_EVENT_CXL_ATTR(ddr_refresh_all_banks, CXL_PMU_GID_DDR, BIT(9)),
CXL_PMU_EVENT_CXL_ATTR(ddr_refresh_same_bank, CXL_PMU_GID_DDR, BIT(10)),
CXL_PMU_EVENT_CXL_ATTR(ddr_pwrdn_entry, CXL_PMU_GID_DDR, BIT(11)),
CXL_PMU_EVENT_CXL_ATTR(ddr_pwrdn_exit, CXL_PMU_GID_DDR, BIT(12)),
CXL_PMU_EVENT_CXL_ATTR(ddr_rd_wr_ddr_bus_switching, CXL_PMU_GID_DDR, BIT(13)),
CXL_PMU_EVENT_CXL_ATTR(ddr_incoming_rd_req, CXL_PMU_GID_DDR, BIT(14)),
CXL_PMU_EVENT_CXL_ATTR(ddr_incoming_wr_req, CXL_PMU_GID_DDR, BIT(15)),
/* CXL 4.0 Table 13-5 QUEUE OCCUPANCY events opcodes */
CXL_PMU_EVENT_CXL_ATTR(rd_queue_occ, CXL_PMU_GID_QUEUE_OCC, BIT(0)),
CXL_PMU_EVENT_CXL_ATTR(wr_queue_occ, CXL_PMU_GID_QUEUE_OCC, BIT(1)),
CXL_PMU_EVENT_CXL_ATTR(rd_wr_merged_queue_occ, CXL_PMU_GID_QUEUE_OCC, BIT(2)),
CXL_PMU_EVENT_CXL_ATTR(pwrdn_event, CXL_PMU_GID_QUEUE_OCC, BIT(3)),
/* CXL 4.0 Table 13-5 QUEUE RESIDENCY events opcodes */
CXL_PMU_EVENT_CXL_ATTR(mc_rd_resid_cnt, CXL_PMU_GID_QUEUE_RESID, BIT(0)),
CXL_PMU_EVENT_CXL_ATTR(mc_wr_resid_cnt, CXL_PMU_GID_QUEUE_RESID, BIT(1)),
/* CXL 4.0 Table 13-5 RETRY events opcodes */
CXL_PMU_EVENT_CXL_ATTR(retry_event_trig_by_rd_crc, CXL_PMU_GID_RETRY_EVENTS, BIT(0)),
CXL_PMU_EVENT_CXL_ATTR(retry_event_trig_by_wr_crc, CXL_PMU_GID_RETRY_EVENTS, BIT(1)),
CXL_PMU_EVENT_CXL_ATTR(retry_event_trig_by_ca_parity, CXL_PMU_GID_RETRY_EVENTS, BIT(2)),
CXL_PMU_EVENT_CXL_ATTR(retry_event_trig_by_ecc, CXL_PMU_GID_RETRY_EVENTS, BIT(3)),
/* CXL 4.0 Table 13-5 THROTTLE events opcodes */
CXL_PMU_EVENT_CXL_ATTR(thermal_throttle_event, CXL_PMU_GID_THROTTLE, BIT(0)),
CXL_PMU_EVENT_CXL_ATTR(power_throttle_event, CXL_PMU_GID_THROTTLE, BIT(1)),
NULL
};
@ -571,6 +646,36 @@ static int cxl_pmu_event_init(struct perf_event *event)
return -EOPNOTSUPP;
/* TODO: Validation of any filter */
if (cxl_pmu_config1_crb_filter_en(event)) {
if (!info->filter_crb)
return -EINVAL;
/* event group IDs are scoped by the CXL vendor ID */
if (cxl_pmu_config_get_vid(event) != PCI_VENDOR_ID_CXL)
return -EINVAL;
/*
* CRB filtering (Filter ID 1) is only valid for the DDR
* Interface, Queue Occupancy, Queue Residency and Retry
* event groups (CXL 4.0 Table 13-5).
*/
switch (cxl_pmu_config_get_gid(event)) {
case CXL_PMU_GID_DDR:
case CXL_PMU_GID_QUEUE_OCC:
case CXL_PMU_GID_QUEUE_RESID:
case CXL_PMU_GID_RETRY_EVENTS:
break;
default:
return -EINVAL;
}
/*
* Filtering while counting multiple events is
* undefined behavior.
*/
if (hweight32(cxl_pmu_config_get_mask(event)) > 1)
return -EINVAL;
}
/*
* Verify that it is possible to count what was requested. Either must
* be a fixed counter that is a precise match or a configurable counter
@ -627,15 +732,23 @@ static void cxl_pmu_event_start(struct perf_event *event, int flags)
hwc->state = 0;
/*
* Currently only hdm filter control is implemented, this code will
* want generalizing when more filters are added.
* Filter ID=0: HDM decoder filter
* Filter ID=1: Channel/Rank/Bank (CRB) filter
*/
if (info->filter_hdm) {
if (cxl_pmu_config1_hdm_filter_en(event))
cfg = cxl_pmu_config2_get_hdm_decoder(event);
else
cfg = GENMASK(31, 0); /* No filtering if 0xFFFF_FFFF */
writeq(cfg, base + CXL_PMU_FILTER_CFG_REG(hwc->idx, 0));
writel(cfg, base + CXL_PMU_FILTER_CFG_REG(hwc->idx, 0));
}
if (info->filter_crb) {
if (cxl_pmu_config1_crb_filter_en(event))
cfg = cxl_pmu_config2_get_crb(event);
else
cfg = GENMASK(31, 0); /* no filtering if 0xFFFF_FFFF */
writel(cfg, base + CXL_PMU_FILTER_CFG_REG(hwc->idx, 1));
}
cfg = readq(base + CXL_PMU_COUNTER_CFG_REG(hwc->idx));

View File

@ -11,6 +11,7 @@
#include <linux/cpumask.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/hrtimer.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/pcie-dwc.h>
@ -83,6 +84,7 @@ enum dwc_pcie_event_type {
#define DWC_PCIE_LANE_EVENT_MAX_PERIOD GENMASK_ULL(31, 0)
#define DWC_PCIE_MAX_PERIOD GENMASK_ULL(63, 0)
#define DWC_PCIE_PMU_TIMER_PERIOD_NS (2 * NSEC_PER_SEC)
struct dwc_pcie_pmu {
struct pmu pmu;
@ -93,6 +95,8 @@ struct dwc_pcie_pmu {
/* Groups #6 and #7 */
DECLARE_BITMAP(lane_events, 2 * DWC_PCIE_LANE_MAX_EVENTS_PER_GROUP);
struct perf_event *time_based_event;
bool timer_enable;
struct hrtimer hrtimer;
struct hlist_node cpuhp_node;
int on_cpu;
@ -354,6 +358,26 @@ static u64 dwc_pcie_pmu_read_time_based_counter(struct perf_event *event)
return val;
}
static void dwc_pcie_pmu_reset_time_based_counter(struct perf_event *event)
{
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
struct hw_perf_event *hwc = &event->hw;
u64 prev;
dwc_pcie_pmu_time_based_event_enable(pcie_pmu, false);
/*
* The hardware counter is reset to zero when disabled. Synchronize
* prev_count so that the next event_update() computes the correct
* delta against the new counter baseline.
*/
do {
prev = local64_read(&hwc->prev_count);
} while (local64_cmpxchg(&hwc->prev_count, prev, 0) != prev);
dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true);
}
static void dwc_pcie_pmu_event_update(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
@ -429,6 +453,26 @@ static int dwc_pcie_pmu_validate_group(struct perf_event *event)
return 0;
}
static enum hrtimer_restart dwc_pcie_pmu_hrtimer_callback(struct hrtimer *hrtimer)
{
struct dwc_pcie_pmu *pcie_pmu = container_of(hrtimer, struct dwc_pcie_pmu, hrtimer);
struct perf_event *event = pcie_pmu->time_based_event;
struct hw_perf_event *hwc;
if (!event)
return HRTIMER_NORESTART;
hwc = &event->hw;
if (hwc->state & PERF_HES_STOPPED)
return HRTIMER_NORESTART;
dwc_pcie_pmu_event_update(event);
dwc_pcie_pmu_reset_time_based_counter(event);
hrtimer_forward_now(hrtimer, ns_to_ktime(DWC_PCIE_PMU_TIMER_PERIOD_NS));
return HRTIMER_RESTART;
}
static int dwc_pcie_pmu_event_init(struct perf_event *event)
{
struct dwc_pcie_pmu *pcie_pmu = to_dwc_pcie_pmu(event->pmu);
@ -478,10 +522,15 @@ static void dwc_pcie_pmu_event_start(struct perf_event *event, int flags)
hwc->state = 0;
local64_set(&hwc->prev_count, 0);
if (type == DWC_PCIE_LANE_EVENT)
if (type == DWC_PCIE_LANE_EVENT) {
dwc_pcie_pmu_lane_event_enable(pcie_pmu, event, true);
else if (type == DWC_PCIE_TIME_BASE_EVENT)
} else if (type == DWC_PCIE_TIME_BASE_EVENT) {
dwc_pcie_pmu_time_based_event_enable(pcie_pmu, true);
if (pcie_pmu->timer_enable)
hrtimer_start(&pcie_pmu->hrtimer,
ns_to_ktime(DWC_PCIE_PMU_TIMER_PERIOD_NS),
HRTIMER_MODE_REL_PINNED_HARD);
}
}
static void dwc_pcie_pmu_event_stop(struct perf_event *event, int flags)
@ -495,11 +544,15 @@ static void dwc_pcie_pmu_event_stop(struct perf_event *event, int flags)
dwc_pcie_pmu_event_update(event);
if (type == DWC_PCIE_LANE_EVENT)
if (type == DWC_PCIE_LANE_EVENT) {
dwc_pcie_pmu_lane_event_enable(pcie_pmu, event, false);
else if (type == DWC_PCIE_TIME_BASE_EVENT)
} else if (type == DWC_PCIE_TIME_BASE_EVENT) {
dwc_pcie_pmu_time_based_event_enable(pcie_pmu, false);
if (pcie_pmu->timer_enable)
hrtimer_cancel(&pcie_pmu->hrtimer);
}
hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
}
@ -726,6 +779,16 @@ static int dwc_pcie_pmu_probe(struct platform_device *plat_dev)
pcie_pmu->ras_des_offset = vsec;
pcie_pmu->nr_lanes = pcie_get_width_cap(pdev);
pcie_pmu->on_cpu = -1;
hrtimer_setup(&pcie_pmu->hrtimer, dwc_pcie_pmu_hrtimer_callback,
CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED_HARD);
/*
* Use timer for updating time-based counts on platforms known
* to have narrowed counter.
*/
if (pdev->vendor == PCI_VENDOR_ID_PICOHEART)
pcie_pmu->timer_enable = true;
pcie_pmu->pmu = (struct pmu){
.name = name,
.parent = &plat_dev->dev,

View File

@ -858,10 +858,8 @@ static int ddr_perf_probe(struct platform_device *pdev)
IRQF_NOBALANCING | IRQF_NO_THREAD,
DDR_CPUHP_CB_NAME,
pmu);
if (ret < 0) {
dev_err(&pdev->dev, "Request irq failed: %d", ret);
if (ret < 0)
goto ddr_perf_err;
}
pmu->irq = irq;
ret = irq_set_affinity(pmu->irq, cpumask_of(pmu->cpu));

View File

@ -830,10 +830,8 @@ static int ddr_perf_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, irq, ddr_perf_irq_handler,
IRQF_NOBALANCING | IRQF_NO_THREAD,
DDR_CPUHP_CB_NAME, pmu);
if (ret < 0) {
dev_err(&pdev->dev, "Request irq failed: %d", ret);
if (ret < 0)
goto ddr_perf_err;
}
pmu->irq = irq;
ret = irq_set_affinity(pmu->irq, cpumask_of(pmu->cpu));

View File

@ -526,7 +526,7 @@ static int fujitsu_uncore_pmu_probe(struct platform_device *pdev)
IRQF_NOBALANCING | IRQF_NO_THREAD,
name, uncorepmu);
if (ret)
return dev_err_probe(dev, ret, "Failed to request IRQ:%d\n", irq);
return ret;
ret = irq_set_affinity(irq, cpumask_of(uncorepmu->cpu));
if (ret)

View File

@ -604,8 +604,7 @@ static int hisi_l3c_pmu_init_ext(struct hisi_pmu *l3c_pmu, struct platform_devic
IRQF_NOBALANCING | IRQF_NO_THREAD,
irqname, l3c_pmu);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"Fail to request EXT IRQ: %d.\n", irq);
return ret;
hisi_l3c_pmu->ext_irq[i] = irq;
}

View File

@ -192,11 +192,8 @@ int hisi_uncore_pmu_init_irq(struct hisi_pmu *hisi_pmu,
ret = devm_request_irq(&pdev->dev, irq, hisi_uncore_pmu_isr,
IRQF_NOBALANCING | IRQF_NO_THREAD,
dev_name(&pdev->dev), hisi_pmu);
if (ret < 0) {
dev_err(&pdev->dev,
"Fail to request IRQ: %d ret: %d.\n", irq, ret);
if (ret < 0)
return ret;
}
hisi_pmu->irq = irq;

View File

@ -13,31 +13,43 @@
#include <linux/hrtimer.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/bits.h>
/* SoC variant flags for struct ddr_pmu_platform_data (mutually exclusive in pdata) */
#define IS_CN10K BIT(0)
#define IS_ODY BIT(1)
#define IS_CN20K BIT(2)
/* Performance Counters Operating Mode Control Registers */
#define CN10K_DDRC_PERF_CNT_OP_MODE_CTRL 0x8020
#define ODY_DDRC_PERF_CNT_OP_MODE_CTRL 0x20020
#define CN20K_DDRC_PERF_CNT_OP_MODE_CTRL 0x20000
#define OP_MODE_CTRL_VAL_MANUAL 0x1
/* Performance Counters Start Operation Control Registers */
#define CN10K_DDRC_PERF_CNT_START_OP_CTRL 0x8028
#define ODY_DDRC_PERF_CNT_START_OP_CTRL 0x200A0
#define CN20K_DDRC_PERF_CNT_START_OP_CTRL 0x20080
#define START_OP_CTRL_VAL_START 0x1ULL
#define START_OP_CTRL_VAL_ACTIVE 0x2
/* Performance Counters End Operation Control Registers */
#define CN10K_DDRC_PERF_CNT_END_OP_CTRL 0x8030
#define ODY_DDRC_PERF_CNT_END_OP_CTRL 0x200E0
#define CN20K_DDRC_PERF_CNT_END_OP_CTRL 0x200C0
#define END_OP_CTRL_VAL_END 0x1ULL
/* Performance Counters End Status Registers */
#define CN10K_DDRC_PERF_CNT_END_STATUS 0x8038
#define ODY_DDRC_PERF_CNT_END_STATUS 0x20120
#define CN20K_DDRC_PERF_CNT_END_STATUS 0x20100
#define END_STATUS_VAL_END_TIMER_MODE_END 0x1
/* Performance Counters Configuration Registers */
#define CN10K_DDRC_PERF_CFG_BASE 0x8040
#define ODY_DDRC_PERF_CFG_BASE 0x20160
#define CN20K_DDRC_PERF_CFG_BASE 0x20140
#define CN20K_DDRC_PERF_CFG1_BASE 0x20180
/* 8 Generic event counter + 2 fixed event counters */
#define DDRC_PERF_NUM_GEN_COUNTERS 8
@ -61,6 +73,24 @@
* DO NOT change these event-id numbers, they are used to
* program event bitmap in h/w.
*/
/* CN20K specific events */
#define EVENT_PERF_OP_IS_RD16 61
#define EVENT_PERF_OP_IS_RD32 60
#define EVENT_PERF_OP_IS_WR16 59
#define EVENT_PERF_OP_IS_WR32 58
#define EVENT_OP_IS_ENTER_DSM 44
#define EVENT_OP_IS_RFM 43
#define EVENT_CN20K_OP_IS_ZQLATCH 62
#define EVENT_CN20K_OP_IS_ZQSTART 63
#define EVENT_CN20K_OP_IS_TCR_MRR 50
#define EVENT_CN20K_OP_IS_DQSOSC_MRR 49
#define EVENT_CN20K_OP_IS_DQSOSC_MPC 48
#define EVENT_CN20K_VISIBLE_WIN_LIMIT_REACHED_WR 47
#define EVENT_CN20K_VISIBLE_WIN_LIMIT_REACHED_RD 46
#define EVENT_DFI_CMD_IS_RETRY 61
#define EVENT_RD_UC_ECC_ERROR 60
#define EVENT_RD_CRC_ERROR 59
@ -87,6 +117,9 @@
#define EVENT_OP_IS_SPEC_REF 41
#define EVENT_OP_IS_CRIT_REF 40
#define EVENT_OP_IS_REFRESH 39
#define EVENT_OP_IS_CAS_WCK_SUS 38
#define EVENT_OP_IS_CAS_WS_OFF 37
#define EVENT_OP_IS_CAS_WS 36
#define EVENT_OP_IS_ENTER_MPSM 35
#define EVENT_OP_IS_ENTER_POWERDOWN 31
#define EVENT_OP_IS_ENTER_SELFREF 27
@ -183,8 +216,8 @@ struct ddr_pmu_platform_data {
u64 cnt_freerun_clr;
u64 cnt_value_wr_op;
u64 cnt_value_rd_op;
bool is_cn10k;
bool is_ody;
u64 cfg1_base;
unsigned int silicon_flags; /* IS_CN10K, IS_ODY, or IS_CN20K */
};
static ssize_t cn10k_ddr_pmu_event_show(struct device *dev,
@ -336,6 +369,80 @@ static struct attribute *odyssey_ddr_perf_events_attrs[] = {
NULL
};
static struct attribute *cn20k_ddr_perf_events_attrs[] = {
/* Programmable */
CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rd_or_wr_access, EVENT_HIF_RD_OR_WR),
CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_wr_access, EVENT_HIF_WR),
CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rd_access, EVENT_HIF_RD),
CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_rmw_access, EVENT_HIF_RMW),
CN10K_DDR_PMU_EVENT_ATTR(ddr_hif_pri_rdaccess, EVENT_HIF_HI_PRI_RD),
CN10K_DDR_PMU_EVENT_ATTR(ddr_rd_bypass_access, EVENT_READ_BYPASS),
CN10K_DDR_PMU_EVENT_ATTR(ddr_act_bypass_access, EVENT_ACT_BYPASS),
CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_wr_data_access,
EVENT_DFI_WR_DATA_CYCLES),
CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_rd_data_access,
EVENT_DFI_RD_DATA_CYCLES),
CN10K_DDR_PMU_EVENT_ATTR(ddr_hpri_sched_rd_crit_access,
EVENT_HPR_XACT_WHEN_CRITICAL),
CN10K_DDR_PMU_EVENT_ATTR(ddr_lpri_sched_rd_crit_access,
EVENT_LPR_XACT_WHEN_CRITICAL),
CN10K_DDR_PMU_EVENT_ATTR(ddr_wr_trxn_crit_access,
EVENT_WR_XACT_WHEN_CRITICAL),
CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_active_access, EVENT_OP_IS_ACTIVATE),
CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_rd_or_wr_access,
EVENT_OP_IS_RD_OR_WR),
CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_rd_active_access,
EVENT_OP_IS_RD_ACTIVATE),
CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_read, EVENT_OP_IS_RD),
CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_write, EVENT_OP_IS_WR),
CN10K_DDR_PMU_EVENT_ATTR(ddr_cam_mwr, EVENT_OP_IS_MWR),
CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge, EVENT_OP_IS_PRECHARGE),
CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge_for_rdwr,
EVENT_PRECHARGE_FOR_RDWR),
CN10K_DDR_PMU_EVENT_ATTR(ddr_precharge_for_other,
EVENT_PRECHARGE_FOR_OTHER),
CN10K_DDR_PMU_EVENT_ATTR(ddr_rdwr_transitions, EVENT_RDWR_TRANSITIONS),
CN10K_DDR_PMU_EVENT_ATTR(ddr_write_combine, EVENT_WRITE_COMBINE),
CN10K_DDR_PMU_EVENT_ATTR(ddr_war_hazard, EVENT_WAR_HAZARD),
CN10K_DDR_PMU_EVENT_ATTR(ddr_raw_hazard, EVENT_RAW_HAZARD),
CN10K_DDR_PMU_EVENT_ATTR(ddr_waw_hazard, EVENT_WAW_HAZARD),
CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_selfref, EVENT_OP_IS_ENTER_SELFREF),
CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_powerdown,
EVENT_OP_IS_ENTER_POWERDOWN),
CN10K_DDR_PMU_EVENT_ATTR(ddr_cas_ws, EVENT_OP_IS_CAS_WS),
CN10K_DDR_PMU_EVENT_ATTR(ddr_cas_ws_off, EVENT_OP_IS_CAS_WS_OFF),
CN10K_DDR_PMU_EVENT_ATTR(ddr_cas_wck_sus, EVENT_OP_IS_CAS_WCK_SUS),
CN10K_DDR_PMU_EVENT_ATTR(ddr_refresh, EVENT_OP_IS_REFRESH),
CN10K_DDR_PMU_EVENT_ATTR(ddr_crit_ref, EVENT_OP_IS_CRIT_REF),
CN10K_DDR_PMU_EVENT_ATTR(ddr_spec_ref, EVENT_OP_IS_SPEC_REF),
CN10K_DDR_PMU_EVENT_ATTR(ddr_load_mode, EVENT_OP_IS_LOAD_MODE),
CN10K_DDR_PMU_EVENT_ATTR(ddr_rfm, EVENT_OP_IS_RFM),
CN10K_DDR_PMU_EVENT_ATTR(ddr_enter_dsm, EVENT_OP_IS_ENTER_DSM),
CN10K_DDR_PMU_EVENT_ATTR(ddr_dfi_cycles, EVENT_DFI_CYCLES),
CN10K_DDR_PMU_EVENT_ATTR(ddr_win_limit_reached_rd,
EVENT_CN20K_VISIBLE_WIN_LIMIT_REACHED_RD),
CN10K_DDR_PMU_EVENT_ATTR(ddr_win_limit_reached_wr,
EVENT_CN20K_VISIBLE_WIN_LIMIT_REACHED_WR),
CN10K_DDR_PMU_EVENT_ATTR(ddr_dqsosc_mpc, EVENT_CN20K_OP_IS_DQSOSC_MPC),
CN10K_DDR_PMU_EVENT_ATTR(ddr_dqsosc_mrr, EVENT_CN20K_OP_IS_DQSOSC_MRR),
CN10K_DDR_PMU_EVENT_ATTR(ddr_tcr_mrr, EVENT_CN20K_OP_IS_TCR_MRR),
CN10K_DDR_PMU_EVENT_ATTR(ddr_zqstart, EVENT_CN20K_OP_IS_ZQSTART),
CN10K_DDR_PMU_EVENT_ATTR(ddr_zqlatch, EVENT_CN20K_OP_IS_ZQLATCH),
CN10K_DDR_PMU_EVENT_ATTR(ddr_read16, EVENT_PERF_OP_IS_RD16),
CN10K_DDR_PMU_EVENT_ATTR(ddr_read32, EVENT_PERF_OP_IS_RD32),
CN10K_DDR_PMU_EVENT_ATTR(ddr_write16, EVENT_PERF_OP_IS_WR16),
CN10K_DDR_PMU_EVENT_ATTR(ddr_write32, EVENT_PERF_OP_IS_WR32),
/* Free run event counters */
CN10K_DDR_PMU_EVENT_ATTR(ddr_ddr_reads, EVENT_DDR_READS),
CN10K_DDR_PMU_EVENT_ATTR(ddr_ddr_writes, EVENT_DDR_WRITES),
NULL
};
static struct attribute_group cn20k_ddr_perf_events_attr_group = {
.name = "events",
.attrs = cn20k_ddr_perf_events_attrs,
};
static struct attribute_group odyssey_ddr_perf_events_attr_group = {
.name = "events",
.attrs = odyssey_ddr_perf_events_attrs,
@ -393,6 +500,13 @@ static const struct attribute_group *odyssey_attr_groups[] = {
NULL
};
static const struct attribute_group *cn20k_attr_groups[] = {
&cn20k_ddr_perf_events_attr_group,
&cn10k_ddr_perf_format_attr_group,
&cn10k_ddr_perf_cpumask_attr_group,
NULL
};
/* Default poll timeout is 100 sec, which is very sufficient for
* 48 bit counter incremented max at 5.6 GT/s, which may take many
* hours to overflow.
@ -411,14 +525,38 @@ static int ddr_perf_get_event_bitmap(int eventid, u64 *event_bitmap,
int err = 0;
switch (eventid) {
case EVENT_CN20K_OP_IS_ZQLATCH ... EVENT_CN20K_OP_IS_ZQSTART:
if (ddr_pmu->p_data->silicon_flags & IS_CN20K) {
*event_bitmap = (1ULL << (eventid - 42));
break;
}
err = -EINVAL;
break;
case EVENT_DFI_PARITY_POISON ...EVENT_DFI_CMD_IS_RETRY:
if (!ddr_pmu->p_data->is_ody) {
/*
* 58..61: CN20K perf width events share numeric IDs with Odyssey
* DFI events; same 1ULL << (eventid - 1) bitmap on both paths.
*/
if (eventid >= EVENT_PERF_OP_IS_WR32 &&
eventid <= EVENT_PERF_OP_IS_RD16) {
if (ddr_pmu->p_data->silicon_flags & IS_CN20K) {
*event_bitmap = (1ULL << (eventid - 1));
break;
}
if (!(ddr_pmu->p_data->silicon_flags & IS_ODY)) {
err = -EINVAL;
break;
}
*event_bitmap = (1ULL << (eventid - 1));
break;
}
if (!(ddr_pmu->p_data->silicon_flags & IS_ODY)) {
err = -EINVAL;
break;
}
fallthrough;
case EVENT_HIF_RD_OR_WR ... EVENT_WAW_HAZARD:
case EVENT_OP_IS_REFRESH ... EVENT_OP_IS_ZQLATCH:
case EVENT_OP_IS_CAS_WS ... EVENT_OP_IS_ZQLATCH:
*event_bitmap = (1ULL << (eventid - 1));
break;
case EVENT_OP_IS_ENTER_SELFREF:
@ -524,9 +662,9 @@ static void cn10k_ddr_perf_counter_enable(struct cn10k_ddr_pmu *pmu,
int counter, bool enable)
{
const struct ddr_pmu_platform_data *p_data = pmu->p_data;
unsigned int silicon_flags = pmu->p_data->silicon_flags;
u64 ctrl_reg = pmu->p_data->cnt_op_mode_ctrl;
const struct ddr_pmu_ops *ops = pmu->ops;
bool is_ody = pmu->p_data->is_ody;
u32 reg;
u64 val;
@ -546,7 +684,7 @@ static void cn10k_ddr_perf_counter_enable(struct cn10k_ddr_pmu *pmu,
writeq_relaxed(val, pmu->base + reg);
if (is_ody) {
if ((silicon_flags & IS_ODY) || (silicon_flags & IS_CN20K)) {
if (enable) {
/*
* Setup the PMU counter to work in
@ -621,6 +759,7 @@ static int cn10k_ddr_perf_event_add(struct perf_event *event, int flags)
{
struct cn10k_ddr_pmu *pmu = to_cn10k_ddr_pmu(event->pmu);
const struct ddr_pmu_platform_data *p_data = pmu->p_data;
unsigned int silicon_flags = pmu->p_data->silicon_flags;
const struct ddr_pmu_ops *ops = pmu->ops;
struct hw_perf_event *hwc = &event->hw;
u8 config = event->attr.config;
@ -642,10 +781,27 @@ static int cn10k_ddr_perf_event_add(struct perf_event *event, int flags)
if (counter < DDRC_PERF_NUM_GEN_COUNTERS) {
/* Generic counters, configure event id */
reg_offset = DDRC_PERF_CFG(p_data->cfg_base, counter);
ret = ddr_perf_get_event_bitmap(config, &val, pmu);
if (ret)
return ret;
goto err_free_counter;
if (silicon_flags & IS_CN20K) {
if (config == EVENT_CN20K_OP_IS_ZQSTART ||
config == EVENT_CN20K_OP_IS_ZQLATCH) {
/* ZQ lives in CFG1; clear stale event mask in CFG0 */
writeq_relaxed(0, pmu->base +
DDRC_PERF_CFG(p_data->cfg_base,
counter));
reg_offset = DDRC_PERF_CFG(p_data->cfg1_base,
counter);
} else {
/* Clear CFG1 so a prior ZQ select cannot linger */
writeq_relaxed(0, pmu->base +
DDRC_PERF_CFG(p_data->cfg1_base,
counter));
}
}
writeq_relaxed(val, pmu->base + reg_offset);
} else {
/* fixed event counter, clear counter value */
@ -661,6 +817,14 @@ static int cn10k_ddr_perf_event_add(struct perf_event *event, int flags)
cn10k_ddr_perf_event_start(event, flags);
return 0;
err_free_counter:
if (pmu->active_events == 1)
hrtimer_cancel(&pmu->hrtimer);
pmu->active_events--;
cn10k_ddr_perf_free_counter(pmu, counter);
hwc->idx = -1;
return ret;
}
static void cn10k_ddr_perf_event_stop(struct perf_event *event, int flags)
@ -952,7 +1116,25 @@ static const struct ddr_pmu_platform_data cn10k_ddr_pmu_pdata = {
.cnt_freerun_clr = 0,
.cnt_value_wr_op = CN10K_DDRC_PERF_CNT_VALUE_WR_OP,
.cnt_value_rd_op = CN10K_DDRC_PERF_CNT_VALUE_RD_OP,
.is_cn10k = TRUE,
.silicon_flags = IS_CN10K,
};
static const struct ddr_pmu_platform_data cn20k_ddr_pmu_pdata = {
.counter_overflow_val = 0,
.counter_max_val = GENMASK_ULL(63, 0),
.cnt_base = ODY_DDRC_PERF_CNT_VALUE_BASE,
.cfg_base = CN20K_DDRC_PERF_CFG_BASE,
.cfg1_base = CN20K_DDRC_PERF_CFG1_BASE,
.cnt_op_mode_ctrl = CN20K_DDRC_PERF_CNT_OP_MODE_CTRL,
.cnt_start_op_ctrl = CN20K_DDRC_PERF_CNT_START_OP_CTRL,
.cnt_end_op_ctrl = CN20K_DDRC_PERF_CNT_END_OP_CTRL,
.cnt_end_status = CN20K_DDRC_PERF_CNT_END_STATUS,
.cnt_freerun_en = 0,
.cnt_freerun_ctrl = ODY_DDRC_PERF_CNT_FREERUN_CTRL,
.cnt_freerun_clr = ODY_DDRC_PERF_CNT_FREERUN_CLR,
.cnt_value_wr_op = ODY_DDRC_PERF_CNT_VALUE_WR_OP,
.cnt_value_rd_op = ODY_DDRC_PERF_CNT_VALUE_RD_OP,
.silicon_flags = IS_CN20K,
};
#endif
@ -979,7 +1161,7 @@ static const struct ddr_pmu_platform_data odyssey_ddr_pmu_pdata = {
.cnt_freerun_clr = ODY_DDRC_PERF_CNT_FREERUN_CLR,
.cnt_value_wr_op = ODY_DDRC_PERF_CNT_VALUE_WR_OP,
.cnt_value_rd_op = ODY_DDRC_PERF_CNT_VALUE_RD_OP,
.is_ody = TRUE,
.silicon_flags = IS_ODY,
};
#endif
@ -989,8 +1171,7 @@ static int cn10k_ddr_perf_probe(struct platform_device *pdev)
struct cn10k_ddr_pmu *ddr_pmu;
struct resource *res;
void __iomem *base;
bool is_cn10k;
bool is_ody;
unsigned int silicon_flags;
char *name;
int ret;
@ -1014,10 +1195,9 @@ static int cn10k_ddr_perf_probe(struct platform_device *pdev)
ddr_pmu->base = base;
ddr_pmu->p_data = dev_data;
is_cn10k = ddr_pmu->p_data->is_cn10k;
is_ody = ddr_pmu->p_data->is_ody;
silicon_flags = ddr_pmu->p_data->silicon_flags;
if (is_cn10k) {
if (silicon_flags & IS_CN10K) {
ddr_pmu->ops = &ddr_pmu_ops;
/* Setup the PMU counter to work in manual mode */
writeq_relaxed(OP_MODE_CTRL_VAL_MANUAL, ddr_pmu->base +
@ -1039,7 +1219,7 @@ static int cn10k_ddr_perf_probe(struct platform_device *pdev)
};
}
if (is_ody) {
if (silicon_flags & IS_ODY) {
ddr_pmu->ops = &ddr_pmu_ody_ops;
ddr_pmu->pmu = (struct pmu) {
@ -1056,6 +1236,22 @@ static int cn10k_ddr_perf_probe(struct platform_device *pdev)
};
}
if (silicon_flags & IS_CN20K) {
ddr_pmu->ops = &ddr_pmu_ody_ops;
ddr_pmu->pmu = (struct pmu) {
.module = THIS_MODULE,
.capabilities = PERF_PMU_CAP_NO_EXCLUDE,
.task_ctx_nr = perf_invalid_context,
.attr_groups = cn20k_attr_groups,
.event_init = cn10k_ddr_perf_event_init,
.add = cn10k_ddr_perf_event_add,
.del = cn10k_ddr_perf_event_del,
.start = cn10k_ddr_perf_event_start,
.stop = cn10k_ddr_perf_event_stop,
.read = cn10k_ddr_perf_event_update,
};
}
/* Choose this cpu to collect perf data */
ddr_pmu->cpu = raw_smp_processor_id();
@ -1088,6 +1284,12 @@ static void cn10k_ddr_perf_remove(struct platform_device *pdev)
{
struct cn10k_ddr_pmu *ddr_pmu = platform_get_drvdata(pdev);
/*
* Cancel the poll timer before further teardown so the handler
* cannot run after this function returns.
*/
hrtimer_cancel(&ddr_pmu->hrtimer);
cpuhp_state_remove_instance_nocalls(
CPUHP_AP_PERF_ARM_MARVELL_CN10K_DDR_ONLINE,
&ddr_pmu->node);
@ -1098,6 +1300,7 @@ static void cn10k_ddr_perf_remove(struct platform_device *pdev)
#ifdef CONFIG_OF
static const struct of_device_id cn10k_ddr_pmu_of_match[] = {
{ .compatible = "marvell,cn10k-ddr-pmu", .data = &cn10k_ddr_pmu_pdata },
{ .compatible = "marvell,cn20k-ddr-pmu", .data = &cn20k_ddr_pmu_pdata },
{ },
};
MODULE_DEVICE_TABLE(of, cn10k_ddr_pmu_of_match);
@ -1107,6 +1310,7 @@ MODULE_DEVICE_TABLE(of, cn10k_ddr_pmu_of_match);
static const struct acpi_device_id cn10k_ddr_pmu_acpi_match[] = {
{"MRVL000A", (kernel_ulong_t)&cn10k_ddr_pmu_pdata },
{"MRVL000C", (kernel_ulong_t)&odyssey_ddr_pmu_pdata},
{"MRVL000B", (kernel_ulong_t)&cn20k_ddr_pmu_pdata},
{},
};
MODULE_DEVICE_TABLE(acpi, cn10k_ddr_pmu_acpi_match);

View File

@ -869,11 +869,8 @@ static int l2_cache_pmu_probe_cluster(struct device *dev, void *data)
IRQF_NOBALANCING | IRQF_NO_THREAD |
IRQF_NO_AUTOEN,
"l2-cache-pmu", cluster);
if (err) {
dev_err(&pdev->dev,
"Unable to request IRQ%d for L2 PMU counters\n", irq);
if (err)
return err;
}
dev_info(&pdev->dev,
"Registered L2 cache PMU cluster %lld\n", fw_cluster_id);

View File

@ -767,11 +767,8 @@ static int qcom_l3_cache_pmu_probe(struct platform_device *pdev)
ret = devm_request_irq(&pdev->dev, ret, qcom_l3_cache__handle_irq, 0,
name, l3pmu);
if (ret) {
dev_err(&pdev->dev, "Request for IRQ failed for slice @%pa\n",
&memrc->start);
if (ret)
return ret;
}
/* Add this instance to the list used by the offline callback */
ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_QCOM_L3_ONLINE, &l3pmu->node);

View File

@ -435,7 +435,7 @@ static int starlink_setup_irqs(struct starlink_pmu *starlink_pmu,
ret = devm_request_irq(&pdev->dev, irq, starlink_pmu_handle_irq,
0, STARLINK_PMU_PDEV_NAME, starlink_pmu);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Failed to request IRQ\n");
return ret;
starlink_pmu->irq = irq;

View File

@ -1876,10 +1876,8 @@ static int xgene_pmu_probe(struct platform_device *pdev)
rc = devm_request_irq(&pdev->dev, irq, xgene_pmu_isr,
IRQF_NOBALANCING | IRQF_NO_THREAD,
dev_name(&pdev->dev), xgene_pmu);
if (rc) {
dev_err(&pdev->dev, "Could not request IRQ %d\n", irq);
if (rc)
return rc;
}
xgene_pmu->irq = irq;

View File

@ -2640,6 +2640,8 @@
#define PCI_VENDOR_ID_SUNIX 0x1fd4
#define PCI_DEVICE_ID_SUNIX_1999 0x1999
#define PCI_VENDOR_ID_PICOHEART 0x20fa
#define PCI_VENDOR_ID_HINT 0x3388
#define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013

View File

@ -26,6 +26,8 @@ static const struct dwc_pcie_vsec_id dwc_pcie_rasdes_vsec_ids[] = {
.vsec_id = 0x02, .vsec_rev = 0x4 },
{ .vendor_id = PCI_VENDOR_ID_AMPERE,
.vsec_id = 0x02, .vsec_rev = 0x4 },
{ .vendor_id = PCI_VENDOR_ID_PICOHEART,
.vsec_id = 0x02, .vsec_rev = 0x4 },
{ .vendor_id = PCI_VENDOR_ID_QCOM,
.vsec_id = 0x02, .vsec_rev = 0x4 },
{ .vendor_id = PCI_VENDOR_ID_ROCKCHIP,

View File

@ -119,7 +119,7 @@ struct arm_pmu {
/* PMUv3 only */
int pmuver;
bool has_smt;
bool avoid_pmccntr;
u64 reg_pmmir;
u64 reg_brbidr;
#define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40