Skip to content

Commit 1cdfbc6

Browse files
committed
platform: xuantie: Add custom PMU device for cycle/instret OF
Sscofpmf does not define overflow interrupts for the fixed cycle and instret counters. XuanTie cores expose this via vendor mhpmevent0 (0x7E0) and mhpmevent2 (0x7E1), with the OF/INH bits split into mhpmevent0H (0x7E2) / mhpmevent2H (0x7E3) on RV32 -- mirroring the standard mhpmevent3/3H layout. mxstatus[8] (OFINT) is the per-hart master gate that lets the vendor OF bit raise lcofip. Instead of patching the generic sbi_pmu code path, register a sbi_pmu_device (mirroring the thead c9xx pattern) and layer the vendor OF handling on top of Sscofpmf via hw_counter_enable_irq / hw_counter_disable_irq. The hooks only act on ctr_idx 0 and 2; the programmable counters (ctr_idx >= 3) continue to use the standard Sscofpmf code path unchanged. On RV32 the OF bit is reached via mhpmevent0H/mhpmevent2H, matching what pmu_ctr_enable_irq_hw() does for mhpmevent3H+. Set mxstatus[8] (OFINT) in xuantie_early_init() outside the cold_boot guard so it is enabled per hart on every boot path. Wire it up via QUIRK_XUANTIE_PMU. The xuantie,dummy match enables it alongside the existing quirks, and a new xuantie,pmu compatible is provided for boards that need just this driver. Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
1 parent 29fda62 commit 1cdfbc6

6 files changed

Lines changed: 136 additions & 2 deletions

File tree

include/sbi/riscv_encoding.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,9 @@
656656
#define CSR_MCOUNTINHIBIT 0x320
657657
#define CSR_MCYCLECFG 0x321
658658
#define CSR_MINSTRETCFG 0x322
659+
/* XuanTie vendor: per-counter event regs for fixed mcycle/minstret. */
660+
#define CSR_MHPMEVENT0 0x7E0
661+
#define CSR_MHPMEVENT2 0x7E1
659662
#define CSR_MHPMEVENT3 0x323
660663
#define CSR_MHPMEVENT4 0x324
661664
#define CSR_MHPMEVENT5 0x325
@@ -689,6 +692,9 @@
689692
/* For RV32 */
690693
#define CSR_MCYCLECFGH 0x721
691694
#define CSR_MINSTRETCFGH 0x722
695+
/* XuanTie vendor: high half of mhpmevent0/2 (for OF/INH bits on RV32). */
696+
#define CSR_MHPMEVENT0H 0x7E2
697+
#define CSR_MHPMEVENT2H 0x7E3
692698
#define CSR_MHPMEVENT3H 0x723
693699
#define CSR_MHPMEVENT4H 0x724
694700
#define CSR_MHPMEVENT5H 0x725
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* SPDX-License-Identifier: BSD-2-Clause
3+
*/
4+
5+
#ifndef __RISCV_XUANTIE_PMU_H__
6+
#define __RISCV_XUANTIE_PMU_H__
7+
8+
/*
9+
* XuanTie cores expose Sscofpmf-style OF semantics on the fixed cycle and
10+
* instret counters via vendor per-counter event registers (mhpmevent0/2,
11+
* and the matching high halves on RV32). The CSR addresses live in
12+
* <sbi/riscv_encoding.h> next to the standard mhpmevent CSRs.
13+
*
14+
* mxstatus[8] (OFINT) is the per-hart master gate that lets the vendor
15+
* OF bit raise lcofip; xuantie_pmu_enable_ofint() sets it.
16+
*/
17+
18+
#define MXSTATUS_OFINT BIT(8)
19+
20+
void xuantie_pmu_register_device(void);
21+
void xuantie_pmu_enable_ofint(void);
22+
23+
#endif /* __RISCV_XUANTIE_PMU_H__ */

platform/generic/include/xuantie/xuantie_quirk.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#define QUIRK_XUANTIE_PMC BIT(0)
99
#define QUIRK_XUANTIE_LINK BIT(1)
1010
#define QUIRK_XUANTIE_PMP_EXT BIT(2)
11+
#define QUIRK_XUANTIE_PMU BIT(3)
1112

1213
struct xuantie_generic_quirks {
1314
u32 quirk;

platform/generic/xuantie/objects.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
#
44

55
carray-platform_override_modules-$(CONFIG_PLATFORM_XUANTIE) += xuantie_dummy
6-
platform-objs-$(CONFIG_PLATFORM_XUANTIE) += xuantie/xuantie_dummy.o xuantie/xuantie_pmc.o xuantie/xuantie_link.o xuantie/xuantie_pmp_ext.o
6+
platform-objs-$(CONFIG_PLATFORM_XUANTIE) += xuantie/xuantie_dummy.o xuantie/xuantie_pmc.o xuantie/xuantie_link.o xuantie/xuantie_pmp_ext.o xuantie/xuantie_pmu.o

platform/generic/xuantie/xuantie_dummy.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <xuantie/xuantie_pmc.h>
1515
#include <xuantie/xuantie_link.h>
1616
#include <xuantie/xuantie_pmp_ext.h>
17+
#include <xuantie/xuantie_pmu.h>
1718

1819
static u32 gquirk = 0;
1920

@@ -24,6 +25,10 @@ int xuantie_early_init(bool cold_boot)
2425
xuantie_pmp_ext_cfg();
2526
}
2627

28+
/* mxstatus[8] OFINT is per-hart: enable on every boot path. */
29+
if (gquirk & QUIRK_XUANTIE_PMU)
30+
xuantie_pmu_enable_ofint();
31+
2732
return generic_early_init(cold_boot);
2833
}
2934

@@ -34,6 +39,8 @@ int xuantie_final_init(bool cold_boot)
3439
xuantie_pmc_device_init();
3540
if (gquirk & QUIRK_XUANTIE_LINK)
3641
xuantie_link_pmu_device_init();
42+
if (gquirk & QUIRK_XUANTIE_PMU)
43+
xuantie_pmu_register_device();
3744
}
3845

3946
return generic_final_init(cold_boot);
@@ -52,7 +59,8 @@ static int xuantie_dummy_platform_init(const void *fdt, int nodeoff,
5259
}
5360

5461
static const struct xuantie_generic_quirks xuantie_quirks = {
55-
.quirk = QUIRK_XUANTIE_PMC | QUIRK_XUANTIE_LINK | QUIRK_XUANTIE_PMP_EXT,
62+
.quirk = QUIRK_XUANTIE_PMC | QUIRK_XUANTIE_LINK | QUIRK_XUANTIE_PMP_EXT |
63+
QUIRK_XUANTIE_PMU,
5664
};
5765

5866
static const struct xuantie_generic_quirks xuantie_pmc_quirks = {
@@ -67,10 +75,15 @@ static const struct xuantie_generic_quirks xuantie_pmp_ext_quirks = {
6775
.quirk = QUIRK_XUANTIE_PMP_EXT,
6876
};
6977

78+
static const struct xuantie_generic_quirks xuantie_pmu_quirks = {
79+
.quirk = QUIRK_XUANTIE_PMU,
80+
};
81+
7082
static const struct fdt_match xuantie_dummy_match[] = {
7183
{ .compatible = "xuantie,dummy", .data = &xuantie_quirks },
7284
{ .compatible = "xuantie,pmc", .data = &xuantie_pmc_quirks },
7385
{ .compatible = "xuantie,link", .data = &xuantie_link_quirks },
86+
{ .compatible = "xuantie,pmu", .data = &xuantie_pmu_quirks },
7487
{ .compatible = "riscv-virtio", .data = &xuantie_pmp_ext_quirks }, // qemu debug
7588
{ },
7689
};
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* SPDX-License-Identifier: BSD-2-Clause
3+
*/
4+
5+
#include <sbi/riscv_asm.h>
6+
#include <sbi/riscv_encoding.h>
7+
#include <sbi/sbi_bitops.h>
8+
#include <sbi/sbi_ecall_interface.h>
9+
#include <sbi/sbi_pmu.h>
10+
#include <thead/c9xx_encoding.h>
11+
#include <xuantie/xuantie_pmu.h>
12+
13+
/*
14+
* Sscofpmf does not provide OF interrupts for the fixed cycle/instret
15+
* counters. XuanTie cores back those counters with vendor mhpmevent0/2
16+
* CSRs whose top bits follow the same OF/MINH/...INH layout as the
17+
* standard mhpmevent3+. The generic sbi_pmu_device hooks let us layer
18+
* the vendor OF handling on top of the regular Sscofpmf path: the core
19+
* already calls hw_counter_enable_irq/disable_irq with ctr_idx 0 and 2
20+
* via the fixed-counter fallback in pmu_fixed_ctr_update_inhibit_bits().
21+
*
22+
* Only ctr_idx == 0 (cycle) and ctr_idx == 2 (instret) need vendor
23+
* handling. Other counters are programmable mhpmevent3+ and follow the
24+
* standard Sscofpmf code path unchanged.
25+
*/
26+
27+
static void xuantie_pmu_ctr_enable_irq(uint32_t ctr_idx)
28+
{
29+
if (ctr_idx != 0 && ctr_idx != 2)
30+
return;
31+
32+
/*
33+
* Mirror pmu_ctr_enable_irq_hw(): only clear OF when no lcofip is
34+
* still pending, so we don't race with software that hasn't yet
35+
* handled the previous overflow.
36+
*/
37+
if (csr_read(CSR_MIP) & MIP_LCOFIP)
38+
return;
39+
40+
#if __riscv_xlen == 32
41+
/* OF lives in the H half on RV32; clear via mhpmevent0H/mhpmevent2H. */
42+
if (ctr_idx == 0)
43+
csr_clear(CSR_MHPMEVENT0H, MHPMEVENTH_OF);
44+
else
45+
csr_clear(CSR_MHPMEVENT2H, MHPMEVENTH_OF);
46+
#else
47+
if (ctr_idx == 0)
48+
csr_clear(CSR_MHPMEVENT0, MHPMEVENT_OF);
49+
else
50+
csr_clear(CSR_MHPMEVENT2, MHPMEVENT_OF);
51+
#endif
52+
}
53+
54+
static void xuantie_pmu_ctr_disable_irq(uint32_t ctr_idx)
55+
{
56+
if (ctr_idx != 0 && ctr_idx != 2)
57+
return;
58+
59+
/*
60+
* Setting OF latches the counter so a subsequent overflow cannot
61+
* raise lcofip until enable_irq clears it again. This matches the
62+
* "OF set = disabled" convention used by pmu_update_hw_mhpmevent().
63+
*/
64+
#if __riscv_xlen == 32
65+
if (ctr_idx == 0)
66+
csr_set(CSR_MHPMEVENT0H, MHPMEVENTH_OF);
67+
else
68+
csr_set(CSR_MHPMEVENT2H, MHPMEVENTH_OF);
69+
#else
70+
if (ctr_idx == 0)
71+
csr_set(CSR_MHPMEVENT0, MHPMEVENT_OF);
72+
else
73+
csr_set(CSR_MHPMEVENT2, MHPMEVENT_OF);
74+
#endif
75+
}
76+
77+
static const struct sbi_pmu_device xuantie_pmu_device = {
78+
.name = "xuantie,pmu",
79+
.hw_counter_enable_irq = xuantie_pmu_ctr_enable_irq,
80+
.hw_counter_disable_irq = xuantie_pmu_ctr_disable_irq,
81+
};
82+
83+
void xuantie_pmu_register_device(void)
84+
{
85+
sbi_pmu_set_device(&xuantie_pmu_device);
86+
}
87+
88+
void xuantie_pmu_enable_ofint(void)
89+
{
90+
csr_set(THEAD_C9XX_CSR_MXSTATUS, MXSTATUS_OFINT);
91+
}

0 commit comments

Comments
 (0)