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:
Linus Torvalds
2025-12-02 17:03:55 -08:00
150 changed files with 5997 additions and 711 deletions

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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},

View File

@@ -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) {

View File

@@ -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 = {