mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
Pull arm64 updates from Catalin Marinas:
"These are the arm64 updates for 6.19.
The biggest part is the Arm MPAM driver under drivers/resctrl/.
There's a patch touching mm/ to handle spurious faults for huge pmd
(similar to the pte version). The corresponding arm64 part allows us
to avoid the TLB maintenance if a (huge) page is reused after a write
fault. There's EFI refactoring to allow runtime services with
preemption enabled and the rest is the usual perf/PMU updates and
several cleanups/typos.
Summary:
Core features:
- Basic Arm MPAM (Memory system resource Partitioning And Monitoring)
driver under drivers/resctrl/ which makes use of the fs/rectrl/ API
Perf and PMU:
- Avoid cycle counter on multi-threaded CPUs
- Extend CSPMU device probing and add additional filtering support
for NVIDIA implementations
- Add support for the PMUs on the NoC S3 interconnect
- Add additional compatible strings for new Cortex and C1 CPUs
- Add support for data source filtering to the SPE driver
- Add support for i.MX8QM and "DB" PMU in the imx PMU driver
Memory managemennt:
- Avoid broadcast TLBI if page reused in write fault
- Elide TLB invalidation if the old PTE was not valid
- Drop redundant cpu_set_*_tcr_t0sz() macros
- Propagate pgtable_alloc() errors outside of __create_pgd_mapping()
- Propagate return value from __change_memory_common()
ACPI and EFI:
- Call EFI runtime services without disabling preemption
- Remove unused ACPI function
Miscellaneous:
- ptrace support to disable streaming on SME-only systems
- Improve sysreg generation to include a 'Prefix' descriptor
- Replace __ASSEMBLY__ with __ASSEMBLER__
- Align register dumps in the kselftest zt-test
- Remove some no longer used macros/functions
- Various spelling corrections"
* tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux: (94 commits)
arm64/mm: Document why linear map split failure upon vm_reset_perms is not problematic
arm64/pageattr: Propagate return value from __change_memory_common
arm64/sysreg: Remove unused define ARM64_FEATURE_FIELD_BITS
KVM: arm64: selftests: Consider all 7 possible levels of cache
KVM: arm64: selftests: Remove ARM64_FEATURE_FIELD_BITS and its last user
arm64: atomics: lse: Remove unused parameters from ATOMIC_FETCH_OP_AND macros
Documentation/arm64: Fix the typo of register names
ACPI: GTDT: Get rid of acpi_arch_timer_mem_init()
perf: arm_spe: Add support for filtering on data source
perf: Add perf_event_attr::config4
perf/imx_ddr: Add support for PMU in DB (system interconnects)
perf/imx_ddr: Get and enable optional clks
perf/imx_ddr: Move ida_alloc() from ddr_perf_init() to ddr_perf_probe()
dt-bindings: perf: fsl-imx-ddr: Add compatible string for i.MX8QM, i.MX8QXP and i.MX8DXL
arm64: remove duplicate ARCH_HAS_MEM_ENCRYPT
arm64: mm: use untagged address to calculate page index
MAINTAINERS: new entry for MPAM Driver
arm_mpam: Add kunit tests for props_mismatch()
arm_mpam: Add kunit test for bitmap reset
arm_mpam: Add helper to reset saved mbwu state
...
This commit is contained in:
@@ -21,6 +21,11 @@
|
||||
|
||||
#define NI_CHILD_NODE_INFO 0x004
|
||||
#define NI_CHILD_PTR(n) (0x008 + (n) * 4)
|
||||
#define NI_NUM_SUB_FEATURES 0x100
|
||||
#define NI_SUB_FEATURE_TYPE(n) (0x108 + (n) * 8)
|
||||
#define NI_SUB_FEATURE_PTR(n) (0x10c + (n) * 8)
|
||||
|
||||
#define NI_SUB_FEATURE_TYPE_FCU 0x2
|
||||
|
||||
#define NI700_PMUSELA 0x00c
|
||||
|
||||
@@ -33,9 +38,10 @@
|
||||
#define NI_PIDR2_VERSION GENMASK(7, 4)
|
||||
|
||||
/* PMU node */
|
||||
#define NI_PMEVCNTR(n) (0x008 + (n) * 8)
|
||||
#define NI_PMCCNTR_L 0x0f8
|
||||
#define NI_PMCCNTR_U 0x0fc
|
||||
#define NI700_PMEVCNTR(n) (0x008 + (n) * 8)
|
||||
#define NI700_PMCCNTR_L 0x0f8
|
||||
#define NI_PMEVCNTR(n) (0x200 + (n) * 8)
|
||||
#define NI_PMCCNTR_L 0x2f8
|
||||
#define NI_PMEVTYPER(n) (0x400 + (n) * 4)
|
||||
#define NI_PMEVTYPER_NODE_TYPE GENMASK(12, 9)
|
||||
#define NI_PMEVTYPER_NODE_ID GENMASK(8, 0)
|
||||
@@ -66,6 +72,8 @@
|
||||
enum ni_part {
|
||||
PART_NI_700 = 0x43b,
|
||||
PART_NI_710AE = 0x43d,
|
||||
PART_NOC_S3 = 0x43f,
|
||||
PART_SI_L1 = 0x455,
|
||||
};
|
||||
|
||||
enum ni_node_type {
|
||||
@@ -79,6 +87,10 @@ enum ni_node_type {
|
||||
NI_HSNI,
|
||||
NI_HMNI,
|
||||
NI_PMNI,
|
||||
NI_TSNI,
|
||||
NI_TMNI,
|
||||
NI_CMNI = 0x0e,
|
||||
NI_MCN = 0x63,
|
||||
};
|
||||
|
||||
struct arm_ni_node {
|
||||
@@ -179,6 +191,9 @@ static struct attribute *arm_ni_event_attrs[] = {
|
||||
NI_EVENT_ATTR(hsni, NI_HSNI),
|
||||
NI_EVENT_ATTR(hmni, NI_HMNI),
|
||||
NI_EVENT_ATTR(pmni, NI_PMNI),
|
||||
NI_EVENT_ATTR(tsni, NI_TSNI),
|
||||
NI_EVENT_ATTR(tmni, NI_TMNI),
|
||||
NI_EVENT_ATTR(cmni, NI_CMNI),
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -308,9 +323,15 @@ static int arm_ni_validate_group(struct perf_event *event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool arm_ni_is_7xx(const struct arm_ni *ni)
|
||||
{
|
||||
return ni->part == PART_NI_700 || ni->part == PART_NI_710AE;
|
||||
}
|
||||
|
||||
static int arm_ni_event_init(struct perf_event *event)
|
||||
{
|
||||
struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
|
||||
struct arm_ni *ni;
|
||||
|
||||
if (event->attr.type != event->pmu->type)
|
||||
return -ENOENT;
|
||||
@@ -318,7 +339,10 @@ static int arm_ni_event_init(struct perf_event *event)
|
||||
if (is_sampling_event(event))
|
||||
return -EINVAL;
|
||||
|
||||
event->cpu = cd_to_ni(cd)->cpu;
|
||||
ni = cd_to_ni(cd);
|
||||
event->cpu = ni->cpu;
|
||||
event->hw.flags = arm_ni_is_7xx(ni);
|
||||
|
||||
if (NI_EVENT_TYPE(event) == NI_PMU)
|
||||
return arm_ni_validate_group(event);
|
||||
|
||||
@@ -332,16 +356,16 @@ static int arm_ni_event_init(struct perf_event *event)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static u64 arm_ni_read_ccnt(struct arm_ni_cd *cd)
|
||||
static u64 arm_ni_read_ccnt(void __iomem *pmccntr)
|
||||
{
|
||||
u64 l, u_old, u_new;
|
||||
int retries = 3; /* 1st time unlucky, 2nd improbable, 3rd just broken */
|
||||
|
||||
u_new = readl_relaxed(cd->pmu_base + NI_PMCCNTR_U);
|
||||
u_new = readl_relaxed(pmccntr + 4);
|
||||
do {
|
||||
u_old = u_new;
|
||||
l = readl_relaxed(cd->pmu_base + NI_PMCCNTR_L);
|
||||
u_new = readl_relaxed(cd->pmu_base + NI_PMCCNTR_U);
|
||||
l = readl_relaxed(pmccntr);
|
||||
u_new = readl_relaxed(pmccntr + 4);
|
||||
} while (u_new != u_old && --retries);
|
||||
WARN_ON(!retries);
|
||||
|
||||
@@ -350,7 +374,6 @@ static u64 arm_ni_read_ccnt(struct arm_ni_cd *cd)
|
||||
|
||||
static void arm_ni_event_read(struct perf_event *event)
|
||||
{
|
||||
struct arm_ni_cd *cd = pmu_to_cd(event->pmu);
|
||||
struct hw_perf_event *hw = &event->hw;
|
||||
u64 count, prev;
|
||||
bool ccnt = hw->idx == NI_CCNT_IDX;
|
||||
@@ -358,9 +381,9 @@ static void arm_ni_event_read(struct perf_event *event)
|
||||
do {
|
||||
prev = local64_read(&hw->prev_count);
|
||||
if (ccnt)
|
||||
count = arm_ni_read_ccnt(cd);
|
||||
count = arm_ni_read_ccnt((void __iomem *)event->hw.event_base);
|
||||
else
|
||||
count = readl_relaxed(cd->pmu_base + NI_PMEVCNTR(hw->idx));
|
||||
count = readl_relaxed((void __iomem *)event->hw.event_base);
|
||||
} while (local64_cmpxchg(&hw->prev_count, prev, count) != prev);
|
||||
|
||||
count -= prev;
|
||||
@@ -385,16 +408,16 @@ static void arm_ni_event_stop(struct perf_event *event, int flags)
|
||||
arm_ni_event_read(event);
|
||||
}
|
||||
|
||||
static void arm_ni_init_ccnt(struct arm_ni_cd *cd)
|
||||
static void arm_ni_init_ccnt(struct hw_perf_event *hw)
|
||||
{
|
||||
local64_set(&cd->ccnt->hw.prev_count, S64_MIN);
|
||||
lo_hi_writeq_relaxed(S64_MIN, cd->pmu_base + NI_PMCCNTR_L);
|
||||
local64_set(&hw->prev_count, S64_MIN);
|
||||
lo_hi_writeq_relaxed(S64_MIN, (void __iomem *)hw->event_base);
|
||||
}
|
||||
|
||||
static void arm_ni_init_evcnt(struct arm_ni_cd *cd, int idx)
|
||||
static void arm_ni_init_evcnt(struct hw_perf_event *hw)
|
||||
{
|
||||
local64_set(&cd->evcnt[idx]->hw.prev_count, S32_MIN);
|
||||
writel_relaxed(S32_MIN, cd->pmu_base + NI_PMEVCNTR(idx));
|
||||
local64_set(&hw->prev_count, S32_MIN);
|
||||
writel_relaxed(S32_MIN, (void __iomem *)hw->event_base);
|
||||
}
|
||||
|
||||
static int arm_ni_event_add(struct perf_event *event, int flags)
|
||||
@@ -409,8 +432,10 @@ static int arm_ni_event_add(struct perf_event *event, int flags)
|
||||
if (cd->ccnt)
|
||||
return -ENOSPC;
|
||||
hw->idx = NI_CCNT_IDX;
|
||||
hw->event_base = (unsigned long)cd->pmu_base +
|
||||
(hw->flags ? NI700_PMCCNTR_L : NI_PMCCNTR_L);
|
||||
cd->ccnt = event;
|
||||
arm_ni_init_ccnt(cd);
|
||||
arm_ni_init_ccnt(hw);
|
||||
} else {
|
||||
hw->idx = 0;
|
||||
while (cd->evcnt[hw->idx]) {
|
||||
@@ -420,7 +445,9 @@ static int arm_ni_event_add(struct perf_event *event, int flags)
|
||||
cd->evcnt[hw->idx] = event;
|
||||
unit = (void *)hw->config_base;
|
||||
unit->event[hw->idx] = NI_EVENT_EVENTID(event);
|
||||
arm_ni_init_evcnt(cd, hw->idx);
|
||||
hw->event_base = (unsigned long)cd->pmu_base +
|
||||
(hw->flags ? NI700_PMEVCNTR(hw->idx) : NI_PMEVCNTR(hw->idx));
|
||||
arm_ni_init_evcnt(hw);
|
||||
lo_hi_writeq_relaxed(le64_to_cpu(unit->pmusel), unit->pmusela);
|
||||
|
||||
reg = FIELD_PREP(NI_PMEVTYPER_NODE_TYPE, type) |
|
||||
@@ -457,7 +484,7 @@ static irqreturn_t arm_ni_handle_irq(int irq, void *dev_id)
|
||||
ret = IRQ_HANDLED;
|
||||
if (!(WARN_ON(!cd->ccnt))) {
|
||||
arm_ni_event_read(cd->ccnt);
|
||||
arm_ni_init_ccnt(cd);
|
||||
arm_ni_init_ccnt(&cd->ccnt->hw);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < NI_NUM_COUNTERS; i++) {
|
||||
@@ -466,7 +493,7 @@ static irqreturn_t arm_ni_handle_irq(int irq, void *dev_id)
|
||||
ret = IRQ_HANDLED;
|
||||
if (!(WARN_ON(!cd->evcnt[i]))) {
|
||||
arm_ni_event_read(cd->evcnt[i]);
|
||||
arm_ni_init_evcnt(cd, i);
|
||||
arm_ni_init_evcnt(&cd->evcnt[i]->hw);
|
||||
}
|
||||
}
|
||||
writel_relaxed(reg, cd->pmu_base + NI_PMOVSCLR);
|
||||
@@ -476,6 +503,25 @@ static irqreturn_t arm_ni_handle_irq(int irq, void *dev_id)
|
||||
}
|
||||
}
|
||||
|
||||
static void __iomem *arm_ni_get_pmusel(struct arm_ni *ni, void __iomem *unit_base)
|
||||
{
|
||||
u32 type, ptr, num;
|
||||
|
||||
if (arm_ni_is_7xx(ni))
|
||||
return unit_base + NI700_PMUSELA;
|
||||
|
||||
num = readl_relaxed(unit_base + NI_NUM_SUB_FEATURES);
|
||||
for (int i = 0; i < num; i++) {
|
||||
type = readl_relaxed(unit_base + NI_SUB_FEATURE_TYPE(i));
|
||||
if (type != NI_SUB_FEATURE_TYPE_FCU)
|
||||
continue;
|
||||
ptr = readl_relaxed(unit_base + NI_SUB_FEATURE_PTR(i));
|
||||
return ni->base + ptr;
|
||||
}
|
||||
/* Should be impossible */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int arm_ni_init_cd(struct arm_ni *ni, struct arm_ni_node *node, u64 res_start)
|
||||
{
|
||||
struct arm_ni_cd *cd = ni->cds + node->id;
|
||||
@@ -512,13 +558,18 @@ static int arm_ni_init_cd(struct arm_ni *ni, struct arm_ni_node *node, u64 res_s
|
||||
case NI_HSNI:
|
||||
case NI_HMNI:
|
||||
case NI_PMNI:
|
||||
unit->pmusela = unit_base + NI700_PMUSELA;
|
||||
case NI_TSNI:
|
||||
case NI_TMNI:
|
||||
case NI_CMNI:
|
||||
unit->pmusela = arm_ni_get_pmusel(ni, unit_base);
|
||||
writel_relaxed(1, unit->pmusela);
|
||||
if (readl_relaxed(unit->pmusela) != 1)
|
||||
dev_info(ni->dev, "No access to node 0x%04x%04x\n", unit->id, unit->type);
|
||||
else
|
||||
unit->ns = true;
|
||||
break;
|
||||
case NI_MCN:
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* e.g. FMU - thankfully bits 3:2 of FMU_ERR_FR0 are RES0 so
|
||||
@@ -649,6 +700,8 @@ static int arm_ni_probe(struct platform_device *pdev)
|
||||
switch (part) {
|
||||
case PART_NI_700:
|
||||
case PART_NI_710AE:
|
||||
case PART_NOC_S3:
|
||||
case PART_SI_L1:
|
||||
break;
|
||||
default:
|
||||
dev_WARN(&pdev->dev, "Unknown part number: 0x%03x, this may go badly\n", part);
|
||||
|
||||
@@ -322,14 +322,14 @@ static struct arm_cspmu_impl_match impl_match[] = {
|
||||
{
|
||||
.module_name = "nvidia_cspmu",
|
||||
.pmiidr_val = ARM_CSPMU_IMPL_ID_NVIDIA,
|
||||
.pmiidr_mask = ARM_CSPMU_PMIIDR_IMPLEMENTER,
|
||||
.pmiidr_mask = PMIIDR_IMPLEMENTER,
|
||||
.module = NULL,
|
||||
.impl_init_ops = NULL,
|
||||
},
|
||||
{
|
||||
.module_name = "ampere_cspmu",
|
||||
.pmiidr_val = ARM_CSPMU_IMPL_ID_AMPERE,
|
||||
.pmiidr_mask = ARM_CSPMU_PMIIDR_IMPLEMENTER,
|
||||
.pmiidr_mask = PMIIDR_IMPLEMENTER,
|
||||
.module = NULL,
|
||||
.impl_init_ops = NULL,
|
||||
},
|
||||
@@ -351,6 +351,44 @@ static struct arm_cspmu_impl_match *arm_cspmu_impl_match_get(u32 pmiidr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static u32 arm_cspmu_get_pmiidr(struct arm_cspmu *cspmu)
|
||||
{
|
||||
u32 pmiidr, pmpidr;
|
||||
|
||||
pmiidr = readl(cspmu->base0 + PMIIDR);
|
||||
|
||||
if (pmiidr != 0)
|
||||
return pmiidr;
|
||||
|
||||
/* Construct PMIIDR value from PMPIDRs. */
|
||||
|
||||
pmpidr = readl(cspmu->base0 + PMPIDR0);
|
||||
pmiidr |= FIELD_PREP(PMIIDR_PRODUCTID_PART_0,
|
||||
FIELD_GET(PMPIDR0_PART_0, pmpidr));
|
||||
|
||||
pmpidr = readl(cspmu->base0 + PMPIDR1);
|
||||
pmiidr |= FIELD_PREP(PMIIDR_PRODUCTID_PART_1,
|
||||
FIELD_GET(PMPIDR1_PART_1, pmpidr));
|
||||
pmiidr |= FIELD_PREP(PMIIDR_IMPLEMENTER_DES_0,
|
||||
FIELD_GET(PMPIDR1_DES_0, pmpidr));
|
||||
|
||||
pmpidr = readl(cspmu->base0 + PMPIDR2);
|
||||
pmiidr |= FIELD_PREP(PMIIDR_VARIANT,
|
||||
FIELD_GET(PMPIDR2_REVISION, pmpidr));
|
||||
pmiidr |= FIELD_PREP(PMIIDR_IMPLEMENTER_DES_1,
|
||||
FIELD_GET(PMPIDR2_DES_1, pmpidr));
|
||||
|
||||
pmpidr = readl(cspmu->base0 + PMPIDR3);
|
||||
pmiidr |= FIELD_PREP(PMIIDR_REVISION,
|
||||
FIELD_GET(PMPIDR3_REVAND, pmpidr));
|
||||
|
||||
pmpidr = readl(cspmu->base0 + PMPIDR4);
|
||||
pmiidr |= FIELD_PREP(PMIIDR_IMPLEMENTER_DES_2,
|
||||
FIELD_GET(PMPIDR4_DES_2, pmpidr));
|
||||
|
||||
return pmiidr;
|
||||
}
|
||||
|
||||
#define DEFAULT_IMPL_OP(name) .name = arm_cspmu_##name
|
||||
|
||||
static int arm_cspmu_init_impl_ops(struct arm_cspmu *cspmu)
|
||||
@@ -361,7 +399,7 @@ static int arm_cspmu_init_impl_ops(struct arm_cspmu *cspmu)
|
||||
|
||||
/* Start with a default PMU implementation */
|
||||
cspmu->impl.module = THIS_MODULE;
|
||||
cspmu->impl.pmiidr = readl(cspmu->base0 + PMIIDR);
|
||||
cspmu->impl.pmiidr = arm_cspmu_get_pmiidr(cspmu);
|
||||
cspmu->impl.ops = (struct arm_cspmu_impl_ops) {
|
||||
DEFAULT_IMPL_OP(get_event_attrs),
|
||||
DEFAULT_IMPL_OP(get_format_attrs),
|
||||
@@ -815,6 +853,10 @@ static void arm_cspmu_stop(struct perf_event *event, int pmu_flags)
|
||||
return;
|
||||
|
||||
arm_cspmu_disable_counter(cspmu, hwc->idx);
|
||||
|
||||
if (cspmu->impl.ops.reset_ev_filter)
|
||||
cspmu->impl.ops.reset_ev_filter(cspmu, event);
|
||||
|
||||
arm_cspmu_event_update(event);
|
||||
|
||||
hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
|
||||
@@ -1365,8 +1407,10 @@ void arm_cspmu_impl_unregister(const struct arm_cspmu_impl_match *impl_match)
|
||||
|
||||
/* Unbind the driver from all matching backend devices. */
|
||||
while ((dev = driver_find_device(&arm_cspmu_driver.driver, NULL,
|
||||
match, arm_cspmu_match_device)))
|
||||
match, arm_cspmu_match_device))) {
|
||||
device_release_driver(dev);
|
||||
put_device(dev);
|
||||
}
|
||||
|
||||
mutex_lock(&arm_cspmu_lock);
|
||||
|
||||
|
||||
@@ -86,6 +86,11 @@
|
||||
#define PMCFGR 0xE00
|
||||
#define PMCR 0xE04
|
||||
#define PMIIDR 0xE08
|
||||
#define PMPIDR0 0xFE0
|
||||
#define PMPIDR1 0xFE4
|
||||
#define PMPIDR2 0xFE8
|
||||
#define PMPIDR3 0xFEC
|
||||
#define PMPIDR4 0xFD0
|
||||
|
||||
/* PMCFGR register field */
|
||||
#define PMCFGR_NCG GENMASK(31, 28)
|
||||
@@ -115,8 +120,34 @@
|
||||
#define PMCR_E BIT(0)
|
||||
|
||||
/* PMIIDR register field */
|
||||
#define ARM_CSPMU_PMIIDR_IMPLEMENTER GENMASK(11, 0)
|
||||
#define ARM_CSPMU_PMIIDR_PRODUCTID GENMASK(31, 20)
|
||||
#define PMIIDR_IMPLEMENTER GENMASK(11, 0)
|
||||
#define PMIIDR_IMPLEMENTER_DES_0 GENMASK(3, 0)
|
||||
#define PMIIDR_IMPLEMENTER_DES_1 GENMASK(6, 4)
|
||||
#define PMIIDR_IMPLEMENTER_DES_2 GENMASK(11, 8)
|
||||
#define PMIIDR_REVISION GENMASK(15, 12)
|
||||
#define PMIIDR_VARIANT GENMASK(19, 16)
|
||||
#define PMIIDR_PRODUCTID GENMASK(31, 20)
|
||||
#define PMIIDR_PRODUCTID_PART_0 GENMASK(27, 20)
|
||||
#define PMIIDR_PRODUCTID_PART_1 GENMASK(31, 28)
|
||||
|
||||
/* PMPIDR0 register field */
|
||||
#define PMPIDR0_PART_0 GENMASK(7, 0)
|
||||
|
||||
/* PMPIDR1 register field */
|
||||
#define PMPIDR1_DES_0 GENMASK(7, 4)
|
||||
#define PMPIDR1_PART_1 GENMASK(3, 0)
|
||||
|
||||
/* PMPIDR2 register field */
|
||||
#define PMPIDR2_REVISION GENMASK(7, 4)
|
||||
#define PMPIDR2_DES_1 GENMASK(2, 0)
|
||||
|
||||
/* PMPIDR3 register field */
|
||||
#define PMPIDR3_REVAND GENMASK(7, 4)
|
||||
#define PMPIDR3_CMOD GENMASK(3, 0)
|
||||
|
||||
/* PMPIDR4 register field */
|
||||
#define PMPIDR4_SIZE GENMASK(7, 4)
|
||||
#define PMPIDR4_DES_2 GENMASK(3, 0)
|
||||
|
||||
/* JEDEC-assigned JEP106 identification code */
|
||||
#define ARM_CSPMU_IMPL_ID_NVIDIA 0x36B
|
||||
@@ -152,11 +183,13 @@ struct arm_cspmu_impl_ops {
|
||||
bool (*is_cycle_counter_event)(const struct perf_event *event);
|
||||
/* Decode event type/id from configs */
|
||||
u32 (*event_type)(const struct perf_event *event);
|
||||
/* Set event filters */
|
||||
/* Set/reset event filters */
|
||||
void (*set_cc_filter)(struct arm_cspmu *cspmu,
|
||||
const struct perf_event *event);
|
||||
void (*set_ev_filter)(struct arm_cspmu *cspmu,
|
||||
const struct perf_event *event);
|
||||
void (*reset_ev_filter)(struct arm_cspmu *cspmu,
|
||||
const struct perf_event *event);
|
||||
/* Implementation specific event validation */
|
||||
int (*validate_event)(struct arm_cspmu *cspmu,
|
||||
struct perf_event *event);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
#define NV_GENERIC_FILTER_ID_MASK GENMASK_ULL(31, 0)
|
||||
|
||||
#define NV_PRODID_MASK GENMASK(31, 0)
|
||||
#define NV_PRODID_MASK (PMIIDR_PRODUCTID | PMIIDR_VARIANT | PMIIDR_REVISION)
|
||||
|
||||
#define NV_FORMAT_NAME_GENERIC 0
|
||||
|
||||
@@ -40,10 +40,21 @@
|
||||
|
||||
struct nv_cspmu_ctx {
|
||||
const char *name;
|
||||
u32 filter_mask;
|
||||
u32 filter_default_val;
|
||||
|
||||
struct attribute **event_attr;
|
||||
struct attribute **format_attr;
|
||||
|
||||
u32 filter_mask;
|
||||
u32 filter_default_val;
|
||||
u32 filter2_mask;
|
||||
u32 filter2_default_val;
|
||||
|
||||
u32 (*get_filter)(const struct perf_event *event);
|
||||
u32 (*get_filter2)(const struct perf_event *event);
|
||||
|
||||
void *data;
|
||||
|
||||
int (*init_data)(struct arm_cspmu *cspmu);
|
||||
};
|
||||
|
||||
static struct attribute *scf_pmu_event_attrs[] = {
|
||||
@@ -144,6 +155,7 @@ static struct attribute *cnvlink_pmu_format_attrs[] = {
|
||||
static struct attribute *generic_pmu_format_attrs[] = {
|
||||
ARM_CSPMU_FORMAT_EVENT_ATTR,
|
||||
ARM_CSPMU_FORMAT_FILTER_ATTR,
|
||||
ARM_CSPMU_FORMAT_FILTER2_ATTR,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -184,13 +196,36 @@ static u32 nv_cspmu_event_filter(const struct perf_event *event)
|
||||
return filter_val;
|
||||
}
|
||||
|
||||
static u32 nv_cspmu_event_filter2(const struct perf_event *event)
|
||||
{
|
||||
const struct nv_cspmu_ctx *ctx =
|
||||
to_nv_cspmu_ctx(to_arm_cspmu(event->pmu));
|
||||
|
||||
const u32 filter_val = event->attr.config2 & ctx->filter2_mask;
|
||||
|
||||
if (filter_val == 0)
|
||||
return ctx->filter2_default_val;
|
||||
|
||||
return filter_val;
|
||||
}
|
||||
|
||||
static void nv_cspmu_set_ev_filter(struct arm_cspmu *cspmu,
|
||||
const struct perf_event *event)
|
||||
{
|
||||
u32 filter = nv_cspmu_event_filter(event);
|
||||
u32 offset = PMEVFILTR + (4 * event->hw.idx);
|
||||
u32 filter, offset;
|
||||
const struct nv_cspmu_ctx *ctx =
|
||||
to_nv_cspmu_ctx(to_arm_cspmu(event->pmu));
|
||||
offset = 4 * event->hw.idx;
|
||||
|
||||
writel(filter, cspmu->base0 + offset);
|
||||
if (ctx->get_filter) {
|
||||
filter = ctx->get_filter(event);
|
||||
writel(filter, cspmu->base0 + PMEVFILTR + offset);
|
||||
}
|
||||
|
||||
if (ctx->get_filter2) {
|
||||
filter = ctx->get_filter2(event);
|
||||
writel(filter, cspmu->base0 + PMEVFILT2R + offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void nv_cspmu_set_cc_filter(struct arm_cspmu *cspmu,
|
||||
@@ -210,74 +245,120 @@ enum nv_cspmu_name_fmt {
|
||||
struct nv_cspmu_match {
|
||||
u32 prodid;
|
||||
u32 prodid_mask;
|
||||
u64 filter_mask;
|
||||
u32 filter_default_val;
|
||||
const char *name_pattern;
|
||||
enum nv_cspmu_name_fmt name_fmt;
|
||||
struct attribute **event_attr;
|
||||
struct attribute **format_attr;
|
||||
struct nv_cspmu_ctx template_ctx;
|
||||
struct arm_cspmu_impl_ops ops;
|
||||
};
|
||||
|
||||
static const struct nv_cspmu_match nv_cspmu_match[] = {
|
||||
{
|
||||
.prodid = 0x103,
|
||||
.prodid = 0x10300000,
|
||||
.prodid_mask = NV_PRODID_MASK,
|
||||
.filter_mask = NV_PCIE_FILTER_ID_MASK,
|
||||
.filter_default_val = NV_PCIE_FILTER_ID_MASK,
|
||||
.name_pattern = "nvidia_pcie_pmu_%u",
|
||||
.name_fmt = NAME_FMT_SOCKET,
|
||||
.event_attr = mcf_pmu_event_attrs,
|
||||
.format_attr = pcie_pmu_format_attrs
|
||||
.template_ctx = {
|
||||
.event_attr = mcf_pmu_event_attrs,
|
||||
.format_attr = pcie_pmu_format_attrs,
|
||||
.filter_mask = NV_PCIE_FILTER_ID_MASK,
|
||||
.filter_default_val = NV_PCIE_FILTER_ID_MASK,
|
||||
.filter2_mask = 0x0,
|
||||
.filter2_default_val = 0x0,
|
||||
.get_filter = nv_cspmu_event_filter,
|
||||
.get_filter2 = NULL,
|
||||
.data = NULL,
|
||||
.init_data = NULL
|
||||
},
|
||||
},
|
||||
{
|
||||
.prodid = 0x104,
|
||||
.prodid = 0x10400000,
|
||||
.prodid_mask = NV_PRODID_MASK,
|
||||
.filter_mask = NV_NVL_C2C_FILTER_ID_MASK,
|
||||
.filter_default_val = NV_NVL_C2C_FILTER_ID_MASK,
|
||||
.name_pattern = "nvidia_nvlink_c2c1_pmu_%u",
|
||||
.name_fmt = NAME_FMT_SOCKET,
|
||||
.event_attr = mcf_pmu_event_attrs,
|
||||
.format_attr = nvlink_c2c_pmu_format_attrs
|
||||
.template_ctx = {
|
||||
.event_attr = mcf_pmu_event_attrs,
|
||||
.format_attr = nvlink_c2c_pmu_format_attrs,
|
||||
.filter_mask = NV_NVL_C2C_FILTER_ID_MASK,
|
||||
.filter_default_val = NV_NVL_C2C_FILTER_ID_MASK,
|
||||
.filter2_mask = 0x0,
|
||||
.filter2_default_val = 0x0,
|
||||
.get_filter = nv_cspmu_event_filter,
|
||||
.get_filter2 = NULL,
|
||||
.data = NULL,
|
||||
.init_data = NULL
|
||||
},
|
||||
},
|
||||
{
|
||||
.prodid = 0x105,
|
||||
.prodid = 0x10500000,
|
||||
.prodid_mask = NV_PRODID_MASK,
|
||||
.filter_mask = NV_NVL_C2C_FILTER_ID_MASK,
|
||||
.filter_default_val = NV_NVL_C2C_FILTER_ID_MASK,
|
||||
.name_pattern = "nvidia_nvlink_c2c0_pmu_%u",
|
||||
.name_fmt = NAME_FMT_SOCKET,
|
||||
.event_attr = mcf_pmu_event_attrs,
|
||||
.format_attr = nvlink_c2c_pmu_format_attrs
|
||||
.template_ctx = {
|
||||
.event_attr = mcf_pmu_event_attrs,
|
||||
.format_attr = nvlink_c2c_pmu_format_attrs,
|
||||
.filter_mask = NV_NVL_C2C_FILTER_ID_MASK,
|
||||
.filter_default_val = NV_NVL_C2C_FILTER_ID_MASK,
|
||||
.filter2_mask = 0x0,
|
||||
.filter2_default_val = 0x0,
|
||||
.get_filter = nv_cspmu_event_filter,
|
||||
.get_filter2 = NULL,
|
||||
.data = NULL,
|
||||
.init_data = NULL
|
||||
},
|
||||
},
|
||||
{
|
||||
.prodid = 0x106,
|
||||
.prodid = 0x10600000,
|
||||
.prodid_mask = NV_PRODID_MASK,
|
||||
.filter_mask = NV_CNVL_FILTER_ID_MASK,
|
||||
.filter_default_val = NV_CNVL_FILTER_ID_MASK,
|
||||
.name_pattern = "nvidia_cnvlink_pmu_%u",
|
||||
.name_fmt = NAME_FMT_SOCKET,
|
||||
.event_attr = mcf_pmu_event_attrs,
|
||||
.format_attr = cnvlink_pmu_format_attrs
|
||||
.template_ctx = {
|
||||
.event_attr = mcf_pmu_event_attrs,
|
||||
.format_attr = cnvlink_pmu_format_attrs,
|
||||
.filter_mask = NV_CNVL_FILTER_ID_MASK,
|
||||
.filter_default_val = NV_CNVL_FILTER_ID_MASK,
|
||||
.filter2_mask = 0x0,
|
||||
.filter2_default_val = 0x0,
|
||||
.get_filter = nv_cspmu_event_filter,
|
||||
.get_filter2 = NULL,
|
||||
.data = NULL,
|
||||
.init_data = NULL
|
||||
},
|
||||
},
|
||||
{
|
||||
.prodid = 0x2CF,
|
||||
.prodid = 0x2CF00000,
|
||||
.prodid_mask = NV_PRODID_MASK,
|
||||
.filter_mask = 0x0,
|
||||
.filter_default_val = 0x0,
|
||||
.name_pattern = "nvidia_scf_pmu_%u",
|
||||
.name_fmt = NAME_FMT_SOCKET,
|
||||
.event_attr = scf_pmu_event_attrs,
|
||||
.format_attr = scf_pmu_format_attrs
|
||||
.template_ctx = {
|
||||
.event_attr = scf_pmu_event_attrs,
|
||||
.format_attr = scf_pmu_format_attrs,
|
||||
.filter_mask = 0x0,
|
||||
.filter_default_val = 0x0,
|
||||
.filter2_mask = 0x0,
|
||||
.filter2_default_val = 0x0,
|
||||
.get_filter = nv_cspmu_event_filter,
|
||||
.get_filter2 = NULL,
|
||||
.data = NULL,
|
||||
.init_data = NULL
|
||||
},
|
||||
},
|
||||
{
|
||||
.prodid = 0,
|
||||
.prodid_mask = 0,
|
||||
.filter_mask = NV_GENERIC_FILTER_ID_MASK,
|
||||
.filter_default_val = NV_GENERIC_FILTER_ID_MASK,
|
||||
.name_pattern = "nvidia_uncore_pmu_%u",
|
||||
.name_fmt = NAME_FMT_GENERIC,
|
||||
.event_attr = generic_pmu_event_attrs,
|
||||
.format_attr = generic_pmu_format_attrs
|
||||
.template_ctx = {
|
||||
.event_attr = generic_pmu_event_attrs,
|
||||
.format_attr = generic_pmu_format_attrs,
|
||||
.filter_mask = NV_GENERIC_FILTER_ID_MASK,
|
||||
.filter_default_val = NV_GENERIC_FILTER_ID_MASK,
|
||||
.filter2_mask = NV_GENERIC_FILTER_ID_MASK,
|
||||
.filter2_default_val = NV_GENERIC_FILTER_ID_MASK,
|
||||
.get_filter = nv_cspmu_event_filter,
|
||||
.get_filter2 = nv_cspmu_event_filter2,
|
||||
.data = NULL,
|
||||
.init_data = NULL
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -310,9 +391,16 @@ static char *nv_cspmu_format_name(const struct arm_cspmu *cspmu,
|
||||
return name;
|
||||
}
|
||||
|
||||
#define SET_OP(name, impl, match, default_op) \
|
||||
do { \
|
||||
if (match->ops.name) \
|
||||
impl->name = match->ops.name; \
|
||||
else if (default_op != NULL) \
|
||||
impl->name = default_op; \
|
||||
} while (false)
|
||||
|
||||
static int nv_cspmu_init_ops(struct arm_cspmu *cspmu)
|
||||
{
|
||||
u32 prodid;
|
||||
struct nv_cspmu_ctx *ctx;
|
||||
struct device *dev = cspmu->dev;
|
||||
struct arm_cspmu_impl_ops *impl_ops = &cspmu->impl.ops;
|
||||
@@ -322,30 +410,30 @@ static int nv_cspmu_init_ops(struct arm_cspmu *cspmu)
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
prodid = FIELD_GET(ARM_CSPMU_PMIIDR_PRODUCTID, cspmu->impl.pmiidr);
|
||||
|
||||
/* Find matching PMU. */
|
||||
for (; match->prodid; match++) {
|
||||
const u32 prodid_mask = match->prodid_mask;
|
||||
|
||||
if ((match->prodid & prodid_mask) == (prodid & prodid_mask))
|
||||
if ((match->prodid & prodid_mask) ==
|
||||
(cspmu->impl.pmiidr & prodid_mask))
|
||||
break;
|
||||
}
|
||||
|
||||
ctx->name = nv_cspmu_format_name(cspmu, match);
|
||||
ctx->filter_mask = match->filter_mask;
|
||||
ctx->filter_default_val = match->filter_default_val;
|
||||
ctx->event_attr = match->event_attr;
|
||||
ctx->format_attr = match->format_attr;
|
||||
/* Initialize the context with the matched template. */
|
||||
memcpy(ctx, &match->template_ctx, sizeof(struct nv_cspmu_ctx));
|
||||
ctx->name = nv_cspmu_format_name(cspmu, match);
|
||||
|
||||
cspmu->impl.ctx = ctx;
|
||||
|
||||
/* NVIDIA specific callbacks. */
|
||||
impl_ops->set_cc_filter = nv_cspmu_set_cc_filter;
|
||||
impl_ops->set_ev_filter = nv_cspmu_set_ev_filter;
|
||||
impl_ops->get_event_attrs = nv_cspmu_get_event_attrs;
|
||||
impl_ops->get_format_attrs = nv_cspmu_get_format_attrs;
|
||||
impl_ops->get_name = nv_cspmu_get_name;
|
||||
SET_OP(set_cc_filter, impl_ops, match, nv_cspmu_set_cc_filter);
|
||||
SET_OP(set_ev_filter, impl_ops, match, nv_cspmu_set_ev_filter);
|
||||
SET_OP(get_event_attrs, impl_ops, match, nv_cspmu_get_event_attrs);
|
||||
SET_OP(get_format_attrs, impl_ops, match, nv_cspmu_get_format_attrs);
|
||||
SET_OP(get_name, impl_ops, match, nv_cspmu_get_name);
|
||||
|
||||
if (ctx->init_data)
|
||||
return ctx->init_data(cspmu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -928,6 +928,12 @@ int armpmu_register(struct arm_pmu *pmu)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* By this stage we know our supported CPUs on either DT/ACPI platforms,
|
||||
* detect the SMT implementation.
|
||||
*/
|
||||
pmu->has_smt = topology_core_has_smt(cpumask_first(&pmu->supported_cpus));
|
||||
|
||||
if (!pmu->set_event_filter)
|
||||
pmu->pmu.capabilities |= PERF_PMU_CAP_NO_EXCLUDE;
|
||||
|
||||
|
||||
@@ -981,6 +981,7 @@ static int armv8pmu_get_chain_idx(struct pmu_hw_events *cpuc,
|
||||
static bool armv8pmu_can_use_pmccntr(struct pmu_hw_events *cpuc,
|
||||
struct perf_event *event)
|
||||
{
|
||||
struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu);
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
unsigned long evtype = hwc->config_base & ARMV8_PMU_EVTYPE_EVENT;
|
||||
|
||||
@@ -1001,6 +1002,15 @@ 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)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1465,6 +1475,10 @@ static int name##_pmu_init(struct arm_pmu *cpu_pmu) \
|
||||
|
||||
PMUV3_INIT_SIMPLE(armv8_pmuv3)
|
||||
|
||||
PMUV3_INIT_SIMPLE(armv8_c1_nano)
|
||||
PMUV3_INIT_SIMPLE(armv8_c1_premium)
|
||||
PMUV3_INIT_SIMPLE(armv8_c1_pro)
|
||||
PMUV3_INIT_SIMPLE(armv8_c1_ultra)
|
||||
PMUV3_INIT_SIMPLE(armv8_cortex_a34)
|
||||
PMUV3_INIT_SIMPLE(armv8_cortex_a55)
|
||||
PMUV3_INIT_SIMPLE(armv8_cortex_a65)
|
||||
@@ -1472,11 +1486,14 @@ PMUV3_INIT_SIMPLE(armv8_cortex_a75)
|
||||
PMUV3_INIT_SIMPLE(armv8_cortex_a76)
|
||||
PMUV3_INIT_SIMPLE(armv8_cortex_a77)
|
||||
PMUV3_INIT_SIMPLE(armv8_cortex_a78)
|
||||
PMUV3_INIT_SIMPLE(armv9_cortex_a320)
|
||||
PMUV3_INIT_SIMPLE(armv9_cortex_a510)
|
||||
PMUV3_INIT_SIMPLE(armv9_cortex_a520)
|
||||
PMUV3_INIT_SIMPLE(armv9_cortex_a520ae)
|
||||
PMUV3_INIT_SIMPLE(armv9_cortex_a710)
|
||||
PMUV3_INIT_SIMPLE(armv9_cortex_a715)
|
||||
PMUV3_INIT_SIMPLE(armv9_cortex_a720)
|
||||
PMUV3_INIT_SIMPLE(armv9_cortex_a720ae)
|
||||
PMUV3_INIT_SIMPLE(armv9_cortex_a725)
|
||||
PMUV3_INIT_SIMPLE(armv8_cortex_x1)
|
||||
PMUV3_INIT_SIMPLE(armv9_cortex_x2)
|
||||
@@ -1508,6 +1525,10 @@ PMUV3_INIT_MAP_EVENT(armv8_brcm_vulcan, armv8_vulcan_map_event)
|
||||
|
||||
static const struct of_device_id armv8_pmu_of_device_ids[] = {
|
||||
{.compatible = "arm,armv8-pmuv3", .data = armv8_pmuv3_pmu_init},
|
||||
{.compatible = "arm,c1-nano-pmu", .data = armv8_c1_nano_pmu_init},
|
||||
{.compatible = "arm,c1-premium-pmu", .data = armv8_c1_premium_pmu_init},
|
||||
{.compatible = "arm,c1-pro-pmu", .data = armv8_c1_pro_pmu_init},
|
||||
{.compatible = "arm,c1-ultra-pmu", .data = armv8_c1_ultra_pmu_init},
|
||||
{.compatible = "arm,cortex-a34-pmu", .data = armv8_cortex_a34_pmu_init},
|
||||
{.compatible = "arm,cortex-a35-pmu", .data = armv8_cortex_a35_pmu_init},
|
||||
{.compatible = "arm,cortex-a53-pmu", .data = armv8_cortex_a53_pmu_init},
|
||||
@@ -1520,11 +1541,14 @@ static const struct of_device_id armv8_pmu_of_device_ids[] = {
|
||||
{.compatible = "arm,cortex-a76-pmu", .data = armv8_cortex_a76_pmu_init},
|
||||
{.compatible = "arm,cortex-a77-pmu", .data = armv8_cortex_a77_pmu_init},
|
||||
{.compatible = "arm,cortex-a78-pmu", .data = armv8_cortex_a78_pmu_init},
|
||||
{.compatible = "arm,cortex-a320-pmu", .data = armv9_cortex_a320_pmu_init},
|
||||
{.compatible = "arm,cortex-a510-pmu", .data = armv9_cortex_a510_pmu_init},
|
||||
{.compatible = "arm,cortex-a520-pmu", .data = armv9_cortex_a520_pmu_init},
|
||||
{.compatible = "arm,cortex-a520ae-pmu", .data = armv9_cortex_a520ae_pmu_init},
|
||||
{.compatible = "arm,cortex-a710-pmu", .data = armv9_cortex_a710_pmu_init},
|
||||
{.compatible = "arm,cortex-a715-pmu", .data = armv9_cortex_a715_pmu_init},
|
||||
{.compatible = "arm,cortex-a720-pmu", .data = armv9_cortex_a720_pmu_init},
|
||||
{.compatible = "arm,cortex-a720ae-pmu", .data = armv9_cortex_a720ae_pmu_init},
|
||||
{.compatible = "arm,cortex-a725-pmu", .data = armv9_cortex_a725_pmu_init},
|
||||
{.compatible = "arm,cortex-x1-pmu", .data = armv8_cortex_x1_pmu_init},
|
||||
{.compatible = "arm,cortex-x2-pmu", .data = armv9_cortex_x2_pmu_init},
|
||||
|
||||
@@ -87,6 +87,7 @@ struct arm_spe_pmu {
|
||||
#define SPE_PMU_FEAT_INV_FILT_EVT (1UL << 6)
|
||||
#define SPE_PMU_FEAT_DISCARD (1UL << 7)
|
||||
#define SPE_PMU_FEAT_EFT (1UL << 8)
|
||||
#define SPE_PMU_FEAT_FDS (1UL << 9)
|
||||
#define SPE_PMU_FEAT_DEV_PROBED (1UL << 63)
|
||||
u64 features;
|
||||
|
||||
@@ -252,6 +253,10 @@ static const struct attribute_group arm_spe_pmu_cap_group = {
|
||||
#define ATTR_CFG_FLD_inv_event_filter_LO 0
|
||||
#define ATTR_CFG_FLD_inv_event_filter_HI 63
|
||||
|
||||
#define ATTR_CFG_FLD_inv_data_src_filter_CFG config4 /* inverse of PMSDSFR_EL1 */
|
||||
#define ATTR_CFG_FLD_inv_data_src_filter_LO 0
|
||||
#define ATTR_CFG_FLD_inv_data_src_filter_HI 63
|
||||
|
||||
GEN_PMU_FORMAT_ATTR(ts_enable);
|
||||
GEN_PMU_FORMAT_ATTR(pa_enable);
|
||||
GEN_PMU_FORMAT_ATTR(pct_enable);
|
||||
@@ -268,6 +273,7 @@ GEN_PMU_FORMAT_ATTR(float_filter);
|
||||
GEN_PMU_FORMAT_ATTR(float_filter_mask);
|
||||
GEN_PMU_FORMAT_ATTR(event_filter);
|
||||
GEN_PMU_FORMAT_ATTR(inv_event_filter);
|
||||
GEN_PMU_FORMAT_ATTR(inv_data_src_filter);
|
||||
GEN_PMU_FORMAT_ATTR(min_latency);
|
||||
GEN_PMU_FORMAT_ATTR(discard);
|
||||
|
||||
@@ -288,6 +294,7 @@ static struct attribute *arm_spe_pmu_formats_attr[] = {
|
||||
&format_attr_float_filter_mask.attr,
|
||||
&format_attr_event_filter.attr,
|
||||
&format_attr_inv_event_filter.attr,
|
||||
&format_attr_inv_data_src_filter.attr,
|
||||
&format_attr_min_latency.attr,
|
||||
&format_attr_discard.attr,
|
||||
NULL,
|
||||
@@ -306,6 +313,10 @@ static umode_t arm_spe_pmu_format_attr_is_visible(struct kobject *kobj,
|
||||
if (attr == &format_attr_inv_event_filter.attr && !(spe_pmu->features & SPE_PMU_FEAT_INV_FILT_EVT))
|
||||
return 0;
|
||||
|
||||
if (attr == &format_attr_inv_data_src_filter.attr &&
|
||||
!(spe_pmu->features & SPE_PMU_FEAT_FDS))
|
||||
return 0;
|
||||
|
||||
if ((attr == &format_attr_branch_filter_mask.attr ||
|
||||
attr == &format_attr_load_filter_mask.attr ||
|
||||
attr == &format_attr_store_filter_mask.attr ||
|
||||
@@ -430,6 +441,9 @@ static u64 arm_spe_event_to_pmsfcr(struct perf_event *event)
|
||||
if (ATTR_CFG_GET_FLD(attr, inv_event_filter))
|
||||
reg |= PMSFCR_EL1_FnE;
|
||||
|
||||
if (ATTR_CFG_GET_FLD(attr, inv_data_src_filter))
|
||||
reg |= PMSFCR_EL1_FDS;
|
||||
|
||||
if (ATTR_CFG_GET_FLD(attr, min_latency))
|
||||
reg |= PMSFCR_EL1_FL;
|
||||
|
||||
@@ -454,6 +468,17 @@ static u64 arm_spe_event_to_pmslatfr(struct perf_event *event)
|
||||
return FIELD_PREP(PMSLATFR_EL1_MINLAT, ATTR_CFG_GET_FLD(attr, min_latency));
|
||||
}
|
||||
|
||||
static u64 arm_spe_event_to_pmsdsfr(struct perf_event *event)
|
||||
{
|
||||
struct perf_event_attr *attr = &event->attr;
|
||||
|
||||
/*
|
||||
* Data src filter is inverted so that the default value of 0 is
|
||||
* equivalent to no filtering.
|
||||
*/
|
||||
return ~ATTR_CFG_GET_FLD(attr, inv_data_src_filter);
|
||||
}
|
||||
|
||||
static void arm_spe_pmu_pad_buf(struct perf_output_handle *handle, int len)
|
||||
{
|
||||
struct arm_spe_pmu_buf *buf = perf_get_aux(handle);
|
||||
@@ -791,6 +816,10 @@ static int arm_spe_pmu_event_init(struct perf_event *event)
|
||||
if (arm_spe_event_to_pmsnevfr(event) & spe_pmu->pmsevfr_res0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (arm_spe_event_to_pmsdsfr(event) != U64_MAX &&
|
||||
!(spe_pmu->features & SPE_PMU_FEAT_FDS))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (attr->exclude_idle)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -866,6 +895,11 @@ static void arm_spe_pmu_start(struct perf_event *event, int flags)
|
||||
write_sysreg_s(reg, SYS_PMSNEVFR_EL1);
|
||||
}
|
||||
|
||||
if (spe_pmu->features & SPE_PMU_FEAT_FDS) {
|
||||
reg = arm_spe_event_to_pmsdsfr(event);
|
||||
write_sysreg_s(reg, SYS_PMSDSFR_EL1);
|
||||
}
|
||||
|
||||
reg = arm_spe_event_to_pmslatfr(event);
|
||||
write_sysreg_s(reg, SYS_PMSLATFR_EL1);
|
||||
|
||||
@@ -1125,6 +1159,9 @@ static void __arm_spe_pmu_dev_probe(void *info)
|
||||
if (FIELD_GET(PMSIDR_EL1_EFT, reg))
|
||||
spe_pmu->features |= SPE_PMU_FEAT_EFT;
|
||||
|
||||
if (FIELD_GET(PMSIDR_EL1_FDS, reg))
|
||||
spe_pmu->features |= SPE_PMU_FEAT_FDS;
|
||||
|
||||
/* This field has a spaced out encoding, so just use a look-up */
|
||||
fld = FIELD_GET(PMSIDR_EL1_INTERVAL, reg);
|
||||
switch (fld) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
@@ -52,18 +53,27 @@
|
||||
#define to_ddr_pmu(p) container_of(p, struct ddr_pmu, pmu)
|
||||
|
||||
#define DDR_PERF_DEV_NAME "imx8_ddr"
|
||||
#define DB_PERF_DEV_NAME "imx8_db"
|
||||
#define DDR_CPUHP_CB_NAME DDR_PERF_DEV_NAME "_perf_pmu"
|
||||
|
||||
static DEFINE_IDA(ddr_ida);
|
||||
static DEFINE_IDA(db_ida);
|
||||
|
||||
/* DDR Perf hardware feature */
|
||||
#define DDR_CAP_AXI_ID_FILTER 0x1 /* support AXI ID filter */
|
||||
#define DDR_CAP_AXI_ID_FILTER_ENHANCED 0x3 /* support enhanced AXI ID filter */
|
||||
#define DDR_CAP_AXI_ID_PORT_CHANNEL_FILTER 0x4 /* support AXI ID PORT CHANNEL filter */
|
||||
|
||||
/* Perf type */
|
||||
enum fsl_ddr_type {
|
||||
DDR_PERF_TYPE = 0, /* ddr Perf (default) */
|
||||
DB_PERF_TYPE, /* db Perf */
|
||||
};
|
||||
|
||||
struct fsl_ddr_devtype_data {
|
||||
unsigned int quirks; /* quirks needed for different DDR Perf core */
|
||||
const char *identifier; /* system PMU identifier for userspace */
|
||||
enum fsl_ddr_type type; /* types of Perf, ddr or db */
|
||||
};
|
||||
|
||||
static const struct fsl_ddr_devtype_data imx8_devtype_data;
|
||||
@@ -97,6 +107,12 @@ static const struct fsl_ddr_devtype_data imx8dxl_devtype_data = {
|
||||
.identifier = "i.MX8DXL",
|
||||
};
|
||||
|
||||
static const struct fsl_ddr_devtype_data imx8dxl_db_devtype_data = {
|
||||
.quirks = DDR_CAP_AXI_ID_PORT_CHANNEL_FILTER,
|
||||
.identifier = "i.MX8DXL",
|
||||
.type = DB_PERF_TYPE,
|
||||
};
|
||||
|
||||
static const struct of_device_id imx_ddr_pmu_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx8-ddr-pmu", .data = &imx8_devtype_data},
|
||||
{ .compatible = "fsl,imx8m-ddr-pmu", .data = &imx8m_devtype_data},
|
||||
@@ -105,6 +121,7 @@ static const struct of_device_id imx_ddr_pmu_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx8mn-ddr-pmu", .data = &imx8mn_devtype_data},
|
||||
{ .compatible = "fsl,imx8mp-ddr-pmu", .data = &imx8mp_devtype_data},
|
||||
{ .compatible = "fsl,imx8dxl-ddr-pmu", .data = &imx8dxl_devtype_data},
|
||||
{ .compatible = "fsl,imx8dxl-db-pmu", .data = &imx8dxl_db_devtype_data},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_ddr_pmu_dt_ids);
|
||||
@@ -284,9 +301,37 @@ static struct attribute *ddr_perf_events_attrs[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const int ddr_perf_db_visible_event_list[] = {
|
||||
EVENT_CYCLES_ID,
|
||||
0x41,
|
||||
0x42,
|
||||
};
|
||||
|
||||
static umode_t ddr_perf_events_attrs_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct ddr_pmu *pmu = dev_get_drvdata(dev);
|
||||
struct perf_pmu_events_attr *pmu_attr;
|
||||
unsigned int i;
|
||||
|
||||
pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr.attr);
|
||||
|
||||
if (pmu->devtype_data->type == DDR_PERF_TYPE)
|
||||
return attr->mode;
|
||||
|
||||
/* DB Type */
|
||||
for (i = 0; i < ARRAY_SIZE(ddr_perf_db_visible_event_list); i++)
|
||||
if (pmu_attr->id == ddr_perf_db_visible_event_list[i])
|
||||
return attr->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct attribute_group ddr_perf_events_attr_group = {
|
||||
.name = "events",
|
||||
.attrs = ddr_perf_events_attrs,
|
||||
.is_visible = ddr_perf_events_attrs_is_visible,
|
||||
};
|
||||
|
||||
PMU_FORMAT_ATTR(event, "config:0-7");
|
||||
@@ -645,8 +690,8 @@ static void ddr_perf_pmu_disable(struct pmu *pmu)
|
||||
{
|
||||
}
|
||||
|
||||
static int ddr_perf_init(struct ddr_pmu *pmu, void __iomem *base,
|
||||
struct device *dev)
|
||||
static void ddr_perf_init(struct ddr_pmu *pmu, void __iomem *base,
|
||||
struct device *dev)
|
||||
{
|
||||
*pmu = (struct ddr_pmu) {
|
||||
.pmu = (struct pmu) {
|
||||
@@ -667,9 +712,6 @@ static int ddr_perf_init(struct ddr_pmu *pmu, void __iomem *base,
|
||||
.base = base,
|
||||
.dev = dev,
|
||||
};
|
||||
|
||||
pmu->id = ida_alloc(&ddr_ida, GFP_KERNEL);
|
||||
return pmu->id;
|
||||
}
|
||||
|
||||
static irqreturn_t ddr_perf_irq_handler(int irq, void *p)
|
||||
@@ -735,10 +777,13 @@ static int ddr_perf_offline_cpu(unsigned int cpu, struct hlist_node *node)
|
||||
|
||||
static int ddr_perf_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct clk_bulk_data *clks;
|
||||
struct ddr_pmu *pmu;
|
||||
struct device_node *np;
|
||||
void __iomem *base;
|
||||
struct ida *ida;
|
||||
char *name;
|
||||
int nclks;
|
||||
int num;
|
||||
int ret;
|
||||
int irq;
|
||||
@@ -753,19 +798,33 @@ static int ddr_perf_probe(struct platform_device *pdev)
|
||||
if (!pmu)
|
||||
return -ENOMEM;
|
||||
|
||||
num = ddr_perf_init(pmu, base, &pdev->dev);
|
||||
ddr_perf_init(pmu, base, &pdev->dev);
|
||||
|
||||
platform_set_drvdata(pdev, pmu);
|
||||
|
||||
name = devm_kasprintf(&pdev->dev, GFP_KERNEL, DDR_PERF_DEV_NAME "%d",
|
||||
num);
|
||||
if (!name) {
|
||||
ret = -ENOMEM;
|
||||
goto cpuhp_state_err;
|
||||
}
|
||||
nclks = devm_clk_bulk_get_all_enabled(&pdev->dev, &clks);
|
||||
if (nclks < 0)
|
||||
return dev_err_probe(&pdev->dev, nclks, "Failure get clks\n");
|
||||
|
||||
pmu->devtype_data = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
ida = pmu->devtype_data->type == DDR_PERF_TYPE ? &ddr_ida : &db_ida;
|
||||
num = ida_alloc(ida, GFP_KERNEL);
|
||||
if (num < 0)
|
||||
return num;
|
||||
|
||||
pmu->id = num;
|
||||
|
||||
if (pmu->devtype_data->type == DDR_PERF_TYPE)
|
||||
name = devm_kasprintf(&pdev->dev, GFP_KERNEL, DDR_PERF_DEV_NAME "%d", num);
|
||||
else
|
||||
name = devm_kasprintf(&pdev->dev, GFP_KERNEL, DB_PERF_DEV_NAME "%d", num);
|
||||
|
||||
if (!name) {
|
||||
ret = -ENOMEM;
|
||||
goto idr_free;
|
||||
}
|
||||
|
||||
pmu->cpu = raw_smp_processor_id();
|
||||
ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN,
|
||||
DDR_CPUHP_CB_NAME,
|
||||
@@ -774,7 +833,7 @@ static int ddr_perf_probe(struct platform_device *pdev)
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "cpuhp_setup_state_multi failed\n");
|
||||
goto cpuhp_state_err;
|
||||
goto idr_free;
|
||||
}
|
||||
|
||||
pmu->cpuhp_state = ret;
|
||||
@@ -821,8 +880,8 @@ ddr_perf_err:
|
||||
cpuhp_state_remove_instance_nocalls(pmu->cpuhp_state, &pmu->node);
|
||||
cpuhp_instance_err:
|
||||
cpuhp_remove_multi_state(pmu->cpuhp_state);
|
||||
cpuhp_state_err:
|
||||
ida_free(&ddr_ida, pmu->id);
|
||||
idr_free:
|
||||
ida_free(ida, pmu->id);
|
||||
dev_warn(&pdev->dev, "i.MX8 DDR Perf PMU failed (%d), disabled\n", ret);
|
||||
return ret;
|
||||
}
|
||||
@@ -836,7 +895,11 @@ static void ddr_perf_remove(struct platform_device *pdev)
|
||||
|
||||
perf_pmu_unregister(&pmu->pmu);
|
||||
|
||||
ida_free(&ddr_ida, pmu->id);
|
||||
if (pmu->devtype_data->type == DDR_PERF_TYPE)
|
||||
ida_free(&ddr_ida, pmu->id);
|
||||
else
|
||||
ida_free(&db_ida, pmu->id);
|
||||
|
||||
}
|
||||
|
||||
static struct platform_driver imx_ddr_pmu_driver = {
|
||||
|
||||
Reference in New Issue
Block a user