[PATCH V3 7/7] perf/x86/intel/uncore: Implement global init callback for GNR uncore

From: Zide Chen

Date: Tue Jun 02 2026 - 11:28:43 EST


On Sierra Forest and Clearwater Forest, the FRZ_ALL bit in the global
control register defaults to 0 at boot, but UBOX PMON units do not
work until the global control register is explicitly written with 0
to trigger hardware initialization properly.

Implement the generic uncore_msr_global_init() callback and add it to
gnr_uncore_init[], which is shared by GNR, GRR, SRF, and CWF.

Fixes: 632c4bf6d007 ("perf/x86/intel/uncore: Support Granite Rapids")
Reviewed-by: Dapeng Mi <dapeng1.mi@xxxxxxxxxxxxxxx>
Signed-off-by: Zide Chen <zide.chen@xxxxxxxxx>
---
v3:
- Guard uncore_discovery_pci() with cpus_read_lock() to fix a
theoretical race where CPUs could go offline between
uncore_die_to_cpu() and wrmsrq_on_cpu(). (Sashiko)
- Add Fixes tag. (Dapeng)
v2:
- Propagate return value of wrmsrq_on_cpu() to global_init().
---
arch/x86/events/intel/uncore.c | 13 ++++++++++++-
arch/x86/events/intel/uncore.h | 2 +-
arch/x86/events/intel/uncore_discovery.c | 9 +++++----
3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c
index 4b3a1fa5b41b..7857959c6e82 100644
--- a/arch/x86/events/intel/uncore.c
+++ b/arch/x86/events/intel/uncore.c
@@ -1716,7 +1716,7 @@ static int __init uncore_mmio_init(void)
return ret;
}

-static int uncore_mmio_global_init(u64 ctl)
+static int uncore_mmio_global_init(int die, u64 ctl)
{
void __iomem *io_addr;

@@ -1731,6 +1731,16 @@ static int uncore_mmio_global_init(u64 ctl)
return 0;
}

+static int uncore_msr_global_init(int die, u64 msr)
+{
+ int cpu = uncore_die_to_cpu(die);
+
+ if (cpu == -1)
+ return -ENODEV;
+
+ return wrmsrq_on_cpu(cpu, msr, 0);
+}
+
static const struct uncore_plat_init nhm_uncore_init __initconst = {
.cpu_init = nhm_uncore_cpu_init,
};
@@ -1871,6 +1881,7 @@ static const struct uncore_plat_init gnr_uncore_init __initconst = {
.domain[0].base_is_pci = true,
.domain[0].discovery_base = UNCORE_DISCOVERY_TABLE_DEVICE,
.domain[0].units_ignore = gnr_uncore_units_ignore,
+ .domain[0].global_init = uncore_msr_global_init,
};

static const struct uncore_plat_init dmr_uncore_init __initconst = {
diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h
index 94c68e3417b6..c2e5ccb1d72c 100644
--- a/arch/x86/events/intel/uncore.h
+++ b/arch/x86/events/intel/uncore.h
@@ -53,7 +53,7 @@ struct uncore_discovery_domain {
/* MSR address or PCI device used as the discovery base */
u32 discovery_base;
bool base_is_pci;
- int (*global_init)(u64 ctl);
+ int (*global_init)(int die, u64 ctl);

/* The units in the discovery table should be ignored. */
int *units_ignore;
diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c
index af7e80fee81f..e50776222256 100644
--- a/arch/x86/events/intel/uncore_discovery.c
+++ b/arch/x86/events/intel/uncore_discovery.c
@@ -287,7 +287,7 @@ static int __parse_discovery_table(struct uncore_discovery_domain *domain,
if (!io_addr)
return -ENOMEM;

- if (domain->global_init && domain->global_init(global.ctl)) {
+ if (domain->global_init && domain->global_init(die, global.ctl)) {
ret = -ENODEV;
goto out;
}
@@ -399,7 +399,6 @@ static bool uncore_discovery_msr(struct uncore_discovery_domain *domain)
if (!die_mask)
return false;

- cpus_read_lock();
for_each_online_cpu(cpu) {
die = topology_logical_die_id(cpu);
if (__test_and_set_bit(die, die_mask))
@@ -414,8 +413,6 @@ static bool uncore_discovery_msr(struct uncore_discovery_domain *domain)
__parse_discovery_table(domain, base, die, &parsed);
}

- cpus_read_unlock();
-
kfree(die_mask);
return parsed;
}
@@ -429,10 +426,14 @@ bool uncore_discovery(struct uncore_plat_init *init)
for (i = 0; i < UNCORE_DISCOVERY_DOMAINS; i++) {
domain = &init->domain[i];
if (domain->discovery_base) {
+ cpus_read_lock();
+
if (!domain->base_is_pci)
ret |= uncore_discovery_msr(domain);
else
ret |= uncore_discovery_pci(domain);
+
+ cpus_read_unlock();
}
}

--
2.54.0