mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
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:
commit
de5e5f9b99
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user