* Re: [PATCH v7 15/15] arm64: mm: Unmap kernel data/bss entirely from the linear map
From: Ard Biesheuvel @ 2026-06-09 6:31 UTC (permalink / raw)
To: Marek Szyprowski, Ard Biesheuvel, linux-arm-kernel
Cc: linux-kernel, Will Deacon, Catalin Marinas, Mark Rutland,
Ryan Roberts, Anshuman Khandual, Kevin Brodsky, Liz Prucka,
Seth Jenkins, Kees Cook, Mike Rapoport, David Hildenbrand,
Andrew Morton, Jann Horn, linux-mm, linux-hardening, linuxppc-dev,
linux-sh
In-Reply-To: <6a9c0f55-fe98-4063-864b-8f7e1f4fefd7@samsung.com>
On Tue, 9 Jun 2026, at 08:28, Marek Szyprowski wrote:
> On 09.06.2026 08:22, Marek Szyprowski wrote:
>> On 29.05.2026 17:02, Ard Biesheuvel wrote:
>>> From: Ard Biesheuvel <ardb@kernel.org>
>>>
>>> The linear aliases of the kernel text and rodata are also mapped
>>> read-only in the linear map. Given that the contents of these regions
>>> are mostly identical to the version in the loadable image, mapping them
>>> read-only and leaving their contents visible is a reasonable hardening
>>> measure.
>>>
>>> Data and bss, however, are now also mapped read-only but the contents of
>>> these regions are more likely to contain data that we'd rather not leak.
>>> So let's unmap these entirely in the linear map when the kernel is
>>> running normally.
>>>
>>> When going into hibernation or waking up from it, these regions need to
>>> be mapped, so map the region initially, and toggle the valid bit so
>>> map/unmap the region as needed.
>>>
>>> Doing so is required because pages covering the kernel image are marked
>>> as PageReserved, and therefore disregarded for snapshotting by the
>>> hibernate logic unless they are mapped.
>>>
>>> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
>> This commit landed in yesterday's linux-next as commit 63e0b6a5b693
>> ("arm64: mm: Unmap kernel data/bss entirely from the linear map").
>> In my tests I found that it breaks booting of RaspberryPi3 and
>> RaspberryPi4 boards with the following kernel panic:
> One more comment - reverting 63e0b6a5b693 and 53205d56212c (dependent
> change) on top of next-20260608 fixes this issue.
>
Thanks for the report, and for the confirmation that those reverts fix
the issue - this was reported here as well:
https://lore.kernel.org/all/aicVyebkEMs6w6UV@sirena.co.uk/
^ permalink raw reply
* Re: [PATCH v7 15/15] arm64: mm: Unmap kernel data/bss entirely from the linear map
From: Marek Szyprowski @ 2026-06-09 6:28 UTC (permalink / raw)
To: Ard Biesheuvel, linux-arm-kernel
Cc: linux-kernel, will, catalin.marinas, mark.rutland, Ard Biesheuvel,
Ryan Roberts, Anshuman Khandual, Kevin Brodsky, Liz Prucka,
Seth Jenkins, Kees Cook, Mike Rapoport, David Hildenbrand,
Andrew Morton, Jann Horn, linux-mm, linux-hardening, linuxppc-dev,
linux-sh
In-Reply-To: <a1b27e97-182c-485d-a448-56c19c5de2c2@samsung.com>
On 09.06.2026 08:22, Marek Szyprowski wrote:
> On 29.05.2026 17:02, Ard Biesheuvel wrote:
>> From: Ard Biesheuvel <ardb@kernel.org>
>>
>> The linear aliases of the kernel text and rodata are also mapped
>> read-only in the linear map. Given that the contents of these regions
>> are mostly identical to the version in the loadable image, mapping them
>> read-only and leaving their contents visible is a reasonable hardening
>> measure.
>>
>> Data and bss, however, are now also mapped read-only but the contents of
>> these regions are more likely to contain data that we'd rather not leak.
>> So let's unmap these entirely in the linear map when the kernel is
>> running normally.
>>
>> When going into hibernation or waking up from it, these regions need to
>> be mapped, so map the region initially, and toggle the valid bit so
>> map/unmap the region as needed.
>>
>> Doing so is required because pages covering the kernel image are marked
>> as PageReserved, and therefore disregarded for snapshotting by the
>> hibernate logic unless they are mapped.
>>
>> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> This commit landed in yesterday's linux-next as commit 63e0b6a5b693
> ("arm64: mm: Unmap kernel data/bss entirely from the linear map").
> In my tests I found that it breaks booting of RaspberryPi3 and
> RaspberryPi4 boards with the following kernel panic:
One more comment - reverting 63e0b6a5b693 and 53205d56212c (dependent
change) on top of next-20260608 fixes this issue.
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply
* RE: [PATCH v2 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss clock controller
From: Joakim Zhang @ 2026-06-09 6:27 UTC (permalink / raw)
To: Krzysztof Kozlowski, mturquette@baylibre.com, sboyd@kernel.org,
bmasney@redhat.com, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, p.zabel@pengutronix.de, Gary Yang
Cc: cix-kernel-upstream, linux-clk@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <6c87641d-d505-44ff-a994-eeabf55f4c73@kernel.org>
Hi Krzysztof,
> -----Original Message-----
> From: Krzysztof Kozlowski <krzk@kernel.org>
> Sent: Friday, June 5, 2026 5:24 PM
> To: Joakim Zhang <joakim.zhang@cixtech.com>; mturquette@baylibre.com;
> sboyd@kernel.org; bmasney@redhat.com; robh@kernel.org;
> krzk+dt@kernel.org; conor+dt@kernel.org; p.zabel@pengutronix.de; Gary Yang
> <gary.yang@cixtech.com>
> Cc: cix-kernel-upstream <cix-kernel-upstream@cixtech.com>; linux-
> clk@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org;
> linux-arm-kernel@lists.infradead.org
> Subject: Re: [PATCH v2 3/5] dt-bindings: clock: cix,sky1-audss-clock: add audss
> clock controller
>
> EXTERNAL EMAIL
>
> On 05/06/2026 05:22, joakim.zhang@cixtech.com wrote:
> > +description: |
> > + Clock provider for the Cix Sky1 audio subsystem (AUDSS).
> > +
> > + This node is a child of a cix,sky1-audss-system-control MFD/syscon
> > + node (see cix,sky1-system-control.yaml). It does not have a reg
> > + property; clock mux, divider and gate fields are accessed through the parent
> register block.
> > +
> > + Software reset lines for AUDSS blocks are exposed on the parent
> > + syscon via #reset-cells. Reset indices are defined in
> > + include/dt-bindings/reset/cix,sky1-audss-system-control.h.
> > +
> > + Six SoC-level reference clocks listed in clocks/clock-names feed
> > + the AUDSS clock tree. The provider exposes the internal AUDSS
> > + clocks to other devices via #clock-cells; indices are defined in cix,sky1-
> audss.h.
> > +
> > +properties:
> > + compatible:
> > + const: cix,sky1-audss-clock
> > +
> > + '#clock-cells':
> > + const: 1
> > + description:
> > + Clock indices are defined in include/dt-bindings/clock/cix,sky1-audss.h.
> > +
> > + clocks:
> > + minItems: 6
>
> Drop
OK
> > + maxItems: 6
> > + description:
> > + Six SoC-level audio reference clocks that feed the audio subsystem,
> > + in the same order as clock-names.
> > +
> > + clock-names:
> > + items:
> > + - const: audio_clk0
> > + - const: audio_clk1
> > + - const: audio_clk2
> > + - const: audio_clk3
> > + - const: audio_clk4
> > + - const: audio_clk5
>
> Pretty pointless names. Names matching indexes have no benefits, drop all of
> them and instead list items in "clocks" with description.
Yes, you are right, I will describe these more meaningful.
> > +
> > + resets:
> > + maxItems: 1
> > + description: Audio subsystem NoC (or bus) reset line.
> > +
> > + power-domains:
> > + maxItems: 1
> > + description: Audio subsystem power domain.
>
> So the clock part has power domain but reset part does not? This is odd.
> Especially that parent is audss (right?) and here you describe that this is audss
> poer domain.
>
> Same question about resets.
The reset and power domain takes effect on the entire subsystem, i.e., audss can be accessed only after powered on and reset released, including the CRU registers which contains clock/reset/control bits for all device within the audss.
Because the reset controller probe does not access the hardware, while the clock controller does, so at that time, the power domain and reset were placed in the clock driver. At present, it does not seem very reasonable either.
Linking the "reset" and "power domain" to the parent node requires us to ensure the order of the probes. We need to perform deferred probes within the child nodes until the parent node has been probed.
Do you have any good suggestions? I can also serve as a reference. Thanks
> > +
> > +required:
> > + - compatible
> > + - '#clock-cells'
> > + - clocks
> > + - clock-names
> > + - resets
> > + - power-domains
> > +
> > +additionalProperties: false
> > +
> > +examples:
> > + - |
> > + #include <dt-bindings/clock/cix,sky1.h>
> > + #include <dt-bindings/reset/cix,sky1-audss-system-control.h>
> > + #include <dt-bindings/reset/cix,sky1-s5-system-control.h>
> > +
> > + audss_syscon: system-controller@7110000 {
> > + compatible = "cix,sky1-audss-system-control", "simple-mfd", "syscon";
> > + reg = <0x7110000 0x10000>;
> > + #reset-cells = <1>;
>
> Drop parent node.
OK
> > +
> > + audss_clk: clock-controller {
> > + compatible = "cix,sky1-audss-clock";
> > + power-domains = <&smc_devpd 0>;
> > + #clock-cells = <1>;
> > + clocks = <&scmi_clk CLK_TREE_AUDIO_CLK0>, <&scmi_clk
> CLK_TREE_AUDIO_CLK1>,
> > + <&scmi_clk CLK_TREE_AUDIO_CLK2>, <&scmi_clk
> CLK_TREE_AUDIO_CLK3>,
> > + <&scmi_clk CLK_TREE_AUDIO_CLK4>, <&scmi_clk
> CLK_TREE_AUDIO_CLK5>;
> > + clock-names = "audio_clk0", "audio_clk1", "audio_clk2",
> > + "audio_clk3", "audio_clk4", "audio_clk5";
> > + resets = <&src SKY1_AUDIO_HIFI5_NOC_RESET_N>;
> > + };
> > + };
>
>
>
> > +#define CLK_MCLK4 40
> > +
> > +#define AUDSS_MAX_CLKS 41
>
> Drop
OK
Thanks,
Joakim
^ permalink raw reply
* RE: [PATCH v2 1/5] dt-bindings: soc: cix,sky1-system-control: add audss system control
From: Joakim Zhang @ 2026-06-09 6:25 UTC (permalink / raw)
To: Krzysztof Kozlowski, mturquette@baylibre.com, sboyd@kernel.org,
bmasney@redhat.com, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, p.zabel@pengutronix.de, Gary Yang
Cc: cix-kernel-upstream, linux-clk@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <b005d5cc-3b9b-47a6-b6fe-91ace2db4089@kernel.org>
Hi Krzysztof,
> -----Original Message-----
> From: Krzysztof Kozlowski <krzk@kernel.org>
> Sent: Friday, June 5, 2026 5:21 PM
> To: Joakim Zhang <joakim.zhang@cixtech.com>; mturquette@baylibre.com;
> sboyd@kernel.org; bmasney@redhat.com; robh@kernel.org;
> krzk+dt@kernel.org; conor+dt@kernel.org; p.zabel@pengutronix.de; Gary Yang
> <gary.yang@cixtech.com>
> Cc: cix-kernel-upstream <cix-kernel-upstream@cixtech.com>; linux-
> clk@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org;
> linux-arm-kernel@lists.infradead.org
> Subject: Re: [PATCH v2 1/5] dt-bindings: soc: cix,sky1-system-control: add audss
> system control
>
> EXTERNAL EMAIL
>
> On 05/06/2026 11:18, Krzysztof Kozlowski wrote:
> > On 05/06/2026 05:22, joakim.zhang@cixtech.com wrote:
> >> compatible:
> >> - items:
> >> - - enum:
> >> - - cix,sky1-system-control
> >> - - cix,sky1-s5-system-control
> >> - - const: syscon
> >> + oneOf:
> >> + - items:
> >> + - enum:
> >> + - cix,sky1-system-control
> >> + - cix,sky1-s5-system-control
> >> + - const: syscon
> >> + - items:
> >> + - const: cix,sky1-audss-system-control
> >> + - const: simple-mfd
> >> + - const: syscon
> >>
> >> reg:
> >> maxItems: 1
> >> @@ -27,6 +32,11 @@ properties:
> >> '#reset-cells':
> >> const: 1
> >>
> >> + clock-controller:
> >> + $ref: /schemas/clock/cix,sky1-audss-clock.yaml#
> >> + description:
> >> + AUDSS internal clock provider (cix,sky1-audss-system-control only).
> >
> > Are you sure this patch builds? Your cover letter should explain
> > merging
>
> I am sure it does not...
>
> I recommend switching to compatible-style of defining subnodes in parent
> schema which would decouple patches.
>
> example:
> https://elixir.bootlin.com/linux/v7.1-
> rc6/source/Documentation/devicetree/bindings/display/msm/qcom,sm8750-
> mdss.yaml#L41
Thank you for your guidance. I will study it further.
Joakim
^ permalink raw reply
* RE: [PATCH v2 1/5] dt-bindings: soc: cix,sky1-system-control: add audss system control
From: Joakim Zhang @ 2026-06-09 6:25 UTC (permalink / raw)
To: Krzysztof Kozlowski, mturquette@baylibre.com, sboyd@kernel.org,
bmasney@redhat.com, robh@kernel.org, krzk+dt@kernel.org,
conor+dt@kernel.org, p.zabel@pengutronix.de, Gary Yang
Cc: cix-kernel-upstream, linux-clk@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <17528d9a-738c-48fe-ac24-b8d90875a74f@kernel.org>
Hi Krzysztof,
> -----Original Message-----
> From: Krzysztof Kozlowski <krzk@kernel.org>
> Sent: Friday, June 5, 2026 5:18 PM
> To: Joakim Zhang <joakim.zhang@cixtech.com>; mturquette@baylibre.com;
> sboyd@kernel.org; bmasney@redhat.com; robh@kernel.org;
> krzk+dt@kernel.org; conor+dt@kernel.org; p.zabel@pengutronix.de; Gary Yang
> <gary.yang@cixtech.com>
> Cc: cix-kernel-upstream <cix-kernel-upstream@cixtech.com>; linux-
> clk@vger.kernel.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org;
> linux-arm-kernel@lists.infradead.org
> Subject: Re: [PATCH v2 1/5] dt-bindings: soc: cix,sky1-system-control: add audss
> system control
>
> EXTERNAL EMAIL
>
> On 05/06/2026 05:22, joakim.zhang@cixtech.com wrote:
> > compatible:
> > - items:
> > - - enum:
> > - - cix,sky1-system-control
> > - - cix,sky1-s5-system-control
> > - - const: syscon
> > + oneOf:
> > + - items:
> > + - enum:
> > + - cix,sky1-system-control
> > + - cix,sky1-s5-system-control
> > + - const: syscon
> > + - items:
> > + - const: cix,sky1-audss-system-control
> > + - const: simple-mfd
> > + - const: syscon
> >
> > reg:
> > maxItems: 1
> > @@ -27,6 +32,11 @@ properties:
> > '#reset-cells':
> > const: 1
> >
> > + clock-controller:
> > + $ref: /schemas/clock/cix,sky1-audss-clock.yaml#
> > + description:
> > + AUDSS internal clock provider (cix,sky1-audss-system-control only).
>
> Are you sure this patch builds? Your cover letter should explain merging
> dependencies/strategy/constraints in the first chapter. You start with THE MOST
> important information.
yes, I build yaml with below cmd:
make -j8 ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/soc/cix/cix,sky1-system-control.yaml
make -j8 ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/clock/cix,sky1-audss-clock.yaml
make -j8 ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- dt_binding_check CHECK_DTBS=y W=1 cix/sky1-orion-o6.dtb
It's also possible that I compiled the entire patch set after it was completed. This is incorrect because there are dependencies involved. I will check it again carefully.
> You need to disallow node for other variants.
OK
>
> > +
> > required:
> > - compatible
> > - reg
> > @@ -40,3 +50,22 @@ examples:
> > reg = <0x4160000 0x100>;
> > #reset-cells = <1>;
> > };
> > + - |
> > + #include <dt-bindings/reset/cix,sky1-audss-system-control.h>
> > +
> > + audss_syscon: system-controller@7110000 {
> > + compatible = "cix,sky1-audss-system-control", "simple-mfd", "syscon";
> > + reg = <0x7110000 0x10000>;
> > + #reset-cells = <1>;
> > +
> > + clock-controller {
> > + compatible = "cix,sky1-audss-clock";
> > + power-domains = <&smc_devpd 0>;
> > + #clock-cells = <1>;
> > + clocks = <&scmi_clk 0>, <&scmi_clk 1>, <&scmi_clk 2>,
> > + <&scmi_clk 3>, <&scmi_clk 4>, <&scmi_clk 5>;
> > + clock-names = "audio_clk0", "audio_clk1", "audio_clk2",
> > + "audio_clk3", "audio_clk4", "audio_clk5";
> > + resets = <&src 0>;
> > + };
> > + };
> > diff --git a/include/dt-bindings/reset/cix,sky1-audss-system-control.h
> > b/include/dt-bindings/reset/cix,sky1-audss-system-control.h
> > new file mode 100644
> > index 000000000000..2ebc5c4f10cd
> > --- /dev/null
> > +++ b/include/dt-bindings/reset/cix,sky1-audss-system-control.h
> > @@ -0,0 +1,27 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
> > +/*
> > + * Copyright 2026 Cix Technology Group Co., Ltd.
> > + */
> > +#ifndef DT_BINDING_RESET_CIX_SKY1_AUDSS_SYSTEM_CONTROL_H
> > +#define DT_BINDING_RESET_CIX_SKY1_AUDSS_SYSTEM_CONTROL_H
> > +
> > +#define AUDSS_I2S0_SW_RST_N 0
>
> Most likely _N is redundant here. Consumers will ignore it completely and this is
> binding used by consumers, not by reset controller.
OK
> > +#define AUDSS_I2S1_SW_RST_N 1
> > +#define AUDSS_I2S2_SW_RST_N 2
> > +#define AUDSS_I2S3_SW_RST_N 3
> > +#define AUDSS_I2S4_SW_RST_N 4
> > +#define AUDSS_I2S5_SW_RST_N 5
> > +#define AUDSS_I2S6_SW_RST_N 6
> > +#define AUDSS_I2S7_SW_RST_N 7
> > +#define AUDSS_I2S8_SW_RST_N 8
> > +#define AUDSS_I2S9_SW_RST_N 9
> > +#define AUDSS_WDT_SW_RST_N 10
> > +#define AUDSS_TIMER_SW_RST_N 11
> > +#define AUDSS_MB0_SW_RST_N 12
> > +#define AUDSS_MB1_SW_RST_N 13
> > +#define AUDSS_HDA_SW_RST_N 14
> > +#define AUDSS_DMAC_SW_RST_N 15
> > +
> > +#define SKY1_AUDSS_SW_RESET_NUM 16
>
> Drop, not a binding.
OK
Thanks,
Joakim
^ permalink raw reply
* [PATCH 2/2] pmdomain: imx93-blk-ctrl: Extract PHY as shared domain for DSI/CSI
From: Guoniu Zhou @ 2026-06-09 6:26 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Ulf Hansson,
Peng Fan, Shawn Guo
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, linux-pm,
Guoniu Zhou, stable
In-Reply-To: <20260609-pm_imx93-v1-0-d06c004b0f51@oss.nxp.com>
The MIPI DSI and CSI domains share control bits for clock and reset, which
can lead to incorrect behavior if one domain disables the shared resource
while the other is still active.
To fix the issue, introduce a shared MIPI PHY power domain to own the
common resources and make DSI and CSI its subdomains. This ensures the
shared bits are properly managed and not disabled while still in use.
Fixes: e9aa77d413c9 ("soc: imx: add i.MX93 media blk ctrl driver")
Cc: stable@vger.kernel.org
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
---
drivers/pmdomain/imx/imx93-blk-ctrl.c | 60 +++++++++++++++++++++++++++++++++--
1 file changed, 58 insertions(+), 2 deletions(-)
diff --git a/drivers/pmdomain/imx/imx93-blk-ctrl.c b/drivers/pmdomain/imx/imx93-blk-ctrl.c
index 1afc78b034fa..243ce939ba68 100644
--- a/drivers/pmdomain/imx/imx93-blk-ctrl.c
+++ b/drivers/pmdomain/imx/imx93-blk-ctrl.c
@@ -48,6 +48,8 @@
#define PRIO(X) (X)
+#define BLK_CTRL_NO_PARENT UINT_MAX
+
struct imx93_blk_ctrl_domain;
struct imx93_blk_ctrl {
@@ -68,12 +70,18 @@ struct imx93_blk_ctrl_qos {
u32 cfg_prio;
};
+struct imx93_blk_ctrl_subdomain_link {
+ struct generic_pm_domain *parent;
+ struct generic_pm_domain *subdomain;
+};
+
struct imx93_blk_ctrl_domain_data {
const char *name;
const char * const *clk_names;
int num_clks;
u32 rst_mask;
u32 clk_mask;
+ u32 parent;
int num_qos;
struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS];
};
@@ -203,6 +211,13 @@ static void imx93_release_pm_genpd(void *data)
pm_genpd_remove(genpd);
}
+static void imx93_release_subdomain(void *data)
+{
+ struct imx93_blk_ctrl_subdomain_link *link = data;
+
+ pm_genpd_remove_subdomain(link->parent, link->subdomain);
+}
+
static struct lock_class_key blk_ctrl_genpd_lock_class;
static int imx93_blk_ctrl_probe(struct platform_device *pdev)
@@ -302,6 +317,34 @@ static int imx93_blk_ctrl_probe(struct platform_device *pdev)
bc->onecell_data.domains[i] = &domain->genpd;
}
+ for (i = 0; i < bc_data->num_domains; i++) {
+ struct imx93_blk_ctrl_domain *domain = &bc->domains[i];
+ const struct imx93_blk_ctrl_domain_data *data = domain->data;
+ struct imx93_blk_ctrl_subdomain_link *link;
+
+ if (bc_data->skip_mask & BIT(i) ||
+ data->parent == BLK_CTRL_NO_PARENT)
+ continue;
+
+ link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL);
+ if (!link)
+ return -ENOMEM;
+
+ link->parent = &bc->domains[data->parent].genpd;
+ link->subdomain = &domain->genpd;
+
+ ret = pm_genpd_add_subdomain(&bc->domains[data->parent].genpd,
+ &domain->genpd);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add subdomain %s\n",
+ domain->genpd.name);
+
+ ret = devm_add_action_or_reset(dev, imx93_release_subdomain, link);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to add subdomain release callback\n");
+ }
+
ret = devm_pm_runtime_enable(dev);
if (ret)
return dev_err_probe(dev, ret, "failed to enable pm-runtime\n");
@@ -326,8 +369,9 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
.name = "mediablk-mipi-dsi",
.clk_names = (const char *[]){ "dsi" },
.num_clks = 1,
- .rst_mask = BIT(11) | BIT(12),
- .clk_mask = BIT(11) | BIT(12),
+ .rst_mask = BIT(11),
+ .clk_mask = BIT(11),
+ .parent = IMX93_MEDIABLK_PD_MIPI_PHY,
},
[IMX93_MEDIABLK_PD_MIPI_CSI] = {
.name = "mediablk-mipi-csi",
@@ -335,6 +379,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
.num_clks = 2,
.rst_mask = BIT(9) | BIT(10),
.clk_mask = BIT(9) | BIT(10),
+ .parent = IMX93_MEDIABLK_PD_MIPI_PHY,
},
[IMX93_MEDIABLK_PD_PXP] = {
.name = "mediablk-pxp",
@@ -342,6 +387,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
.num_clks = 1,
.rst_mask = BIT(7) | BIT(8),
.clk_mask = BIT(7) | BIT(8),
+ .parent = BLK_CTRL_NO_PARENT,
.num_qos = 2,
.qos = {
{
@@ -363,6 +409,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
.num_clks = 2,
.rst_mask = BIT(4) | BIT(5) | BIT(6),
.clk_mask = BIT(4) | BIT(5) | BIT(6),
+ .parent = BLK_CTRL_NO_PARENT,
.num_qos = 1,
.qos = {
{
@@ -379,6 +426,7 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
.num_clks = 1,
.rst_mask = BIT(2) | BIT(3),
.clk_mask = BIT(2) | BIT(3),
+ .parent = BLK_CTRL_NO_PARENT,
.num_qos = 4,
.qos = {
{
@@ -404,6 +452,14 @@ static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[]
}
}
},
+ [IMX93_MEDIABLK_PD_MIPI_PHY] = {
+ .name = "mediablk-mipi-phy",
+ .clk_names = NULL,
+ .num_clks = 0,
+ .rst_mask = BIT(12),
+ .clk_mask = BIT(12),
+ .parent = BLK_CTRL_NO_PARENT,
+ },
};
static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = {
--
2.34.1
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: power: imx93: Add MIPI PHY power domain
From: Guoniu Zhou @ 2026-06-09 6:26 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Ulf Hansson,
Peng Fan, Shawn Guo
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, linux-pm,
Guoniu Zhou
In-Reply-To: <20260609-pm_imx93-v1-0-d06c004b0f51@oss.nxp.com>
Add MIPI PHY power domain for shared PHY resources used by both
MIPI DSI and CSI blocks.
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
---
include/dt-bindings/power/fsl,imx93-power.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/dt-bindings/power/fsl,imx93-power.h b/include/dt-bindings/power/fsl,imx93-power.h
index 17f9f015bf7d..071221fe5c57 100644
--- a/include/dt-bindings/power/fsl,imx93-power.h
+++ b/include/dt-bindings/power/fsl,imx93-power.h
@@ -11,5 +11,6 @@
#define IMX93_MEDIABLK_PD_PXP 2
#define IMX93_MEDIABLK_PD_LCDIF 3
#define IMX93_MEDIABLK_PD_ISI 4
+#define IMX93_MEDIABLK_PD_MIPI_PHY 5
#endif
--
2.34.1
^ permalink raw reply related
* [PATCH 0/2] pmdomain: imx93: Fix shared MIPI PHY resource management
From: Guoniu Zhou @ 2026-06-09 6:26 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Ulf Hansson,
Peng Fan, Shawn Guo
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, linux-pm,
Guoniu Zhou, stable
The i.MX93 MIPI DSI and CSI domains share control bits for clock and
reset in the media block controller. This creates a resource conflict
where one domain can inadvertently disable shared resources while the
other domain is still active, leading to system instability.
This series fixes the issue by introducing a dedicated MIPI PHY power
domain that owns the shared clock and reset control bits. The DSI and
CSI domains are then made subdomains of this PHY domain, ensuring proper
reference counting and preventing premature resource shutdown.
Tested on i.MX93 EVK with concurrent DSI and CSI operations.
Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
---
Guoniu Zhou (2):
dt-bindings: power: imx93: Add MIPI PHY power domain
pmdomain: imx93-blk-ctrl: Extract PHY as shared domain for DSI/CSI
drivers/pmdomain/imx/imx93-blk-ctrl.c | 60 ++++++++++++++++++++++++++++-
include/dt-bindings/power/fsl,imx93-power.h | 1 +
2 files changed, 59 insertions(+), 2 deletions(-)
---
base-commit: 3b7a18a34e8d3b14c7c926f033488a0350de9759
change-id: 20260608-pm_imx93-6ccc1aa11932
Best regards,
--
Guoniu Zhou <guoniu.zhou@oss.nxp.com>
^ permalink raw reply
* Re: [PATCH v7 15/15] arm64: mm: Unmap kernel data/bss entirely from the linear map
From: Marek Szyprowski @ 2026-06-09 6:22 UTC (permalink / raw)
To: Ard Biesheuvel, linux-arm-kernel
Cc: linux-kernel, will, catalin.marinas, mark.rutland, Ard Biesheuvel,
Ryan Roberts, Anshuman Khandual, Kevin Brodsky, Liz Prucka,
Seth Jenkins, Kees Cook, Mike Rapoport, David Hildenbrand,
Andrew Morton, Jann Horn, linux-mm, linux-hardening, linuxppc-dev,
linux-sh
In-Reply-To: <20260529150150.1670604-32-ardb+git@google.com>
Dear All,
On 29.05.2026 17:02, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> The linear aliases of the kernel text and rodata are also mapped
> read-only in the linear map. Given that the contents of these regions
> are mostly identical to the version in the loadable image, mapping them
> read-only and leaving their contents visible is a reasonable hardening
> measure.
>
> Data and bss, however, are now also mapped read-only but the contents of
> these regions are more likely to contain data that we'd rather not leak.
> So let's unmap these entirely in the linear map when the kernel is
> running normally.
>
> When going into hibernation or waking up from it, these regions need to
> be mapped, so map the region initially, and toggle the valid bit so
> map/unmap the region as needed.
>
> Doing so is required because pages covering the kernel image are marked
> as PageReserved, and therefore disregarded for snapshotting by the
> hibernate logic unless they are mapped.
>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
This commit landed in yesterday's linux-next as commit 63e0b6a5b693
("arm64: mm: Unmap kernel data/bss entirely from the linear map").
In my tests I found that it breaks booting of RaspberryPi3 and
RaspberryPi4 boards with the following kernel panic:
kvm [1]: nv: 570 coarse grained trap handlers
kvm [1]: nv: 710 fine grained trap handlers
kvm [1]: IPA Size Limit: 40 bits
Unable to handle kernel paging request at virtual address ffff000003a23000
Mem abort info:
ESR = 0x0000000096000147
EC = 0x25: DABT (current EL), IL = 32 bits
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x07: level 3 translation fault
Data abort info:
ISV = 0, ISS = 0x00000147, ISS2 = 0x00000000
CM = 1, WnR = 1, TnD = 0, TagAccess = 0
GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000002609000
[ffff000003a23000] pgd=0000000000000000, p4d=180000003b3ff403, pud=180000003b3fe403, pmd=180000003b3e6403, pte=00e8000003a23f06
Internal error: Oops: 0000000096000147 [#1] SMP
Modules linked in:
CPU: 3 UID: 0 PID: 1 Comm: swapper/0 Not tainted 7.1.0-rc1+ #16768 PREEMPT
Hardware name: Raspberry Pi 3 Model B (DT)
pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
pc : dcache_clean_inval_poc+0x24/0x48
lr : kvm_arm_init+0xa8c/0x165c
sp : ffff8000844bbd00
...
Call trace:
dcache_clean_inval_poc+0x24/0x48 (P)
do_one_initcall+0x68/0x4f4
kernel_init_freeable+0x24c/0x360
kernel_init+0x24/0x1dc
ret_from_fork+0x10/0x20
Code: 9ac32042 d1000443 8a230000 d503201f (d50b7e20)
---[ end trace 0000000000000000 ]---
Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
SMP: stopping secondary CPUs
Kernel Offset: disabled
CPU features: 0x00000000,03000008,00040000,0400421b
Memory Limit: none
---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b ]---
> ---
> arch/arm64/mm/mmu.c | 45 ++++++++++++++++++--
> 1 file changed, 41 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
> index 7b18dc2f1721..07a6fa210171 100644
> --- a/arch/arm64/mm/mmu.c
> +++ b/arch/arm64/mm/mmu.c
> @@ -24,6 +24,7 @@
> #include <linux/mm.h>
> #include <linux/vmalloc.h>
> #include <linux/set_memory.h>
> +#include <linux/suspend.h>
> #include <linux/kfence.h>
> #include <linux/pkeys.h>
> #include <linux/mm_inline.h>
> @@ -1056,6 +1057,29 @@ static void __init __map_memblock(phys_addr_t start, phys_addr_t end,
> end - start, prot, early_pgtable_alloc, flags);
> }
>
> +static void mark_linear_data_alias_valid(bool valid)
> +{
> + set_memory_valid((unsigned long)lm_alias(__init_end),
> + (unsigned long)(__bss_stop - __init_end) / PAGE_SIZE,
> + valid);
> +}
> +
> +static int arm64_hibernate_pm_notify(struct notifier_block *nb,
> + unsigned long mode, void *unused)
> +{
> + switch (mode) {
> + default:
> + break;
> + case PM_POST_HIBERNATION:
> + mark_linear_data_alias_valid(false);
> + break;
> + case PM_HIBERNATION_PREPARE:
> + mark_linear_data_alias_valid(true);
> + break;
> + }
> + return 0;
> +}
> +
> void __init mark_linear_text_alias_ro(void)
> {
> /*
> @@ -1064,6 +1088,21 @@ void __init mark_linear_text_alias_ro(void)
> update_mapping_prot(__pa_symbol(_text), (unsigned long)lm_alias(_text),
> (unsigned long)__init_begin - (unsigned long)_text,
> PAGE_KERNEL_RO);
> +
> + /*
> + * Register a PM notifier to remap the linear alias of data/bss as
> + * valid read-only before hibernation. This is needed because the
> + * snapshot logic disregards PageReserved pages (such as the ones
> + * covering the kernel image) unless they are mapped in the linear
> + * map.
> + */
> + if (IS_ENABLED(CONFIG_HIBERNATION)) {
> + static struct notifier_block nb = {
> + .notifier_call = arm64_hibernate_pm_notify
> + };
> +
> + register_pm_notifier(&nb);
> + }
> }
>
> #ifdef CONFIG_KFENCE
> @@ -1193,10 +1232,8 @@ static void __init map_mem(void)
> flags);
> }
>
> - /* Map the kernel data/bss read-only in the linear map */
> - __map_memblock(init_end, kernel_end, PAGE_KERNEL_RO, flags);
> - flush_tlb_kernel_range((unsigned long)lm_alias(__init_end),
> - (unsigned long)lm_alias(__bss_stop));
> + /* Map the kernel data/bss as invalid in the linear map */
> + mark_linear_data_alias_valid(false);
> }
>
> void mark_rodata_ro(void)
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply
* [PATCH v6 21/21] TEST(do-not-upstream): fake qemu vendor JSON + mapfile entry for CounterIDMask path
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@meta.com>
arch/riscv/qemu/virt/events.json: fake-json-{any,ctr3,ctr34,ctr6} with EventCode
+ CounterIDMask; mapfile.csv: 0x0-0x0-0x0 -> qemu/virt. Exercises jevents
CounterIDMask -> counterid_mask= -> config2 -> cdeleg counter allocation.
Signed-off-by: Atish Patra <atishp@meta.com>
---
tools/perf/pmu-events/arch/riscv/mapfile.csv | 1 +
.../pmu-events/arch/riscv/qemu/virt/events.json | 26 ++++++++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/tools/perf/pmu-events/arch/riscv/mapfile.csv b/tools/perf/pmu-events/arch/riscv/mapfile.csv
index 87cfb0e0849f..3533a8c0253f 100644
--- a/tools/perf/pmu-events/arch/riscv/mapfile.csv
+++ b/tools/perf/pmu-events/arch/riscv/mapfile.csv
@@ -24,3 +24,4 @@
0x602-0x3-0x0,v1,openhwgroup/cva6,core
0x67e-0x80000000db0000[89]0-0x[[:xdigit:]]+,v1,starfive/dubhe-80,core
0x31e-0x8000000000008a45-0x[[:xdigit:]]+,v1,andes/ax45,core
+0x0-0x0-0x0,v1,qemu/virt,core
diff --git a/tools/perf/pmu-events/arch/riscv/qemu/virt/events.json b/tools/perf/pmu-events/arch/riscv/qemu/virt/events.json
new file mode 100644
index 000000000000..294c4ed645f6
--- /dev/null
+++ b/tools/perf/pmu-events/arch/riscv/qemu/virt/events.json
@@ -0,0 +1,26 @@
+[
+ {
+ "EventName": "fake-json-any",
+ "EventCode": "0xF10",
+ "CounterIDMask": "0xFFFFFFF8",
+ "BriefDescription": "FAKE json event (any hpmcounter 3-31) - QEMU does not model 0xF10"
+ },
+ {
+ "EventName": "fake-json-ctr3",
+ "EventCode": "0xF11",
+ "CounterIDMask": "0x8",
+ "BriefDescription": "FAKE json event constrained to hpmcounter3"
+ },
+ {
+ "EventName": "fake-json-ctr34",
+ "EventCode": "0xF12",
+ "CounterIDMask": "0x18",
+ "BriefDescription": "FAKE json event constrained to hpmcounter3,4"
+ },
+ {
+ "EventName": "fake-json-ctr6",
+ "EventCode": "0xF13",
+ "CounterIDMask": "0x40",
+ "BriefDescription": "FAKE json event constrained to hpmcounter6 (out of a small pmu-mask)"
+ }
+]
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 20/21] TEST(do-not-upstream): fake qemu-virt PMU events for cdeleg counter-mask testing
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@meta.com>
Adds fake-any/fake-ctr3/fake-ctr34 (event codes 0xF0x QEMU doesn't model) with
counterid_masks, to exercise the counter-delegation allocation + counter-mask
constraint in QEMU (events read 0 = allocated/programmed, vs 'not supported').
Signed-off-by: Atish Patra <atishp@meta.com>
---
drivers/perf/riscv_pmu_sbi.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 74acac54328e..3c0829c0a42a 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -476,6 +476,12 @@ RVPMU_EVENT_CMASK_ATTR(instructions, instructions, 0x02, 0xFFFFFFF8);
RVPMU_EVENT_CMASK_ATTR(dTLB-load-misses, dTLB_load_miss, 0x10019, 0xFFFFFFF8);
RVPMU_EVENT_CMASK_ATTR(dTLB-store-misses, dTLB_store_miss, 0x1001B, 0xFFFFFFF8);
RVPMU_EVENT_CMASK_ATTR(iTLB-load-misses, iTLB_load_miss, 0x10021, 0xFFFFFFF8);
+/*
+ * FAKE events for cdeleg mechanism testing: event codes QEMU does NOT model.
+ */
+RVPMU_EVENT_CMASK_ATTR(fake-any, fake_any, 0xF00, 0xFFFFFFF8);
+RVPMU_EVENT_CMASK_ATTR(fake-ctr3, fake_ctr3, 0xF01, 0x8);
+RVPMU_EVENT_CMASK_ATTR(fake-ctr34, fake_ctr34, 0xF02, 0x18);
static struct attribute *qemu_virt_event_group[] = {
RVPMU_EVENT_ATTR_PTR(cycles),
@@ -483,6 +489,9 @@ static struct attribute *qemu_virt_event_group[] = {
RVPMU_EVENT_ATTR_PTR(dTLB_load_miss),
RVPMU_EVENT_ATTR_PTR(dTLB_store_miss),
RVPMU_EVENT_ATTR_PTR(iTLB_load_miss),
+ RVPMU_EVENT_ATTR_PTR(fake_any),
+ RVPMU_EVENT_ATTR_PTR(fake_ctr3),
+ RVPMU_EVENT_ATTR_PTR(fake_ctr34),
NULL,
};
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 19/21] tools/perf: Add RISC-V CounterIDMask event field
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
Counter delegation lets supervisor mode choose the hpmcounter for an event,
but the hardware may only allow a given event on a subset of counters. Add a
RISC-V specific "CounterIDMask" json event field, handled like the other
arch-specific entries in event_fields[], that carries the allowed-counter
bitmask through to the driver's existing counterid_mask (config2:0-31) format.
The value is the bitmask directly so no counter-list to bitmask conversion is
needed, and because the field is RISC-V specific it is a no-op for every other
architecture's events (unlike the shared "Counter" field).
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
tools/perf/pmu-events/jevents.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 457fce7a5982..c1ed8a05c9a4 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -396,6 +396,7 @@ class JsonEvent:
('EnAllSlices', 'enallslices='),
('SliceId', 'sliceid='),
('ThreadMask', 'threadmask='),
+ ('CounterIDMask', 'counterid_mask='),
]
for key, value in event_fields:
if key in jd and not is_zero(jd[key]):
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 18/21] tools/perf: Support event code for arch standard events
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
RISC-V relies on the event encoding from the json file. That includes
arch standard events. If event code is present, event is already updated
with correct encoding. No need to update it again which results in losing
the event encoding.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
tools/perf/pmu-events/arch/riscv/arch-standard.json | 10 ++++++++++
tools/perf/pmu-events/jevents.py | 6 +++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/tools/perf/pmu-events/arch/riscv/arch-standard.json b/tools/perf/pmu-events/arch/riscv/arch-standard.json
new file mode 100644
index 000000000000..96e21f088558
--- /dev/null
+++ b/tools/perf/pmu-events/arch/riscv/arch-standard.json
@@ -0,0 +1,10 @@
+[
+ {
+ "EventName": "cycles",
+ "BriefDescription": "cycle executed"
+ },
+ {
+ "EventName": "instructions",
+ "BriefDescription": "instruction retired"
+ }
+]
diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py
index 3a1bcdcdc685..457fce7a5982 100755
--- a/tools/perf/pmu-events/jevents.py
+++ b/tools/perf/pmu-events/jevents.py
@@ -413,7 +413,11 @@ class JsonEvent:
self.long_desc = None
if arch_std:
if arch_std.lower() in _arch_std_events:
- event = _arch_std_events[arch_std.lower()].event
+ # If the JSON event already specified an event code, the encoding has
+ # been set above; don't overwrite it with the arch standard event or
+ # the event encoding would be lost.
+ if not eventcode:
+ event = _arch_std_events[arch_std.lower()].event
# Copy from the architecture standard event to self for undefined fields.
for attr, value in _arch_std_events[arch_std.lower()].__dict__.items():
if hasattr(self, attr) and not getattr(self, attr):
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 11/21] RISC-V: perf: Modify the counter discovery mechanism
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
If both counter delegation and SBI PMU is present, the counter
delegation will be used for hardware pmu counters while the SBI PMU
will be used for firmware counters. Thus, the driver has to probe
the counters info via SBI PMU to distinguish the firmware counters.
The hybrid scheme also requires improvements of the informational
logging messages to indicate the user about underlying interface
used for each use case.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
drivers/perf/riscv_pmu_sbi.c | 131 ++++++++++++++++++++++++++++++++-----------
1 file changed, 97 insertions(+), 34 deletions(-)
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 7f21c16003f0..57ab15beab3e 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -67,6 +67,20 @@ static bool sbi_v3_available;
static DEFINE_STATIC_KEY_FALSE(sbi_pmu_snapshot_available);
#define sbi_pmu_snapshot_available() \
static_branch_unlikely(&sbi_pmu_snapshot_available)
+static DEFINE_STATIC_KEY_FALSE(riscv_pmu_sbi_available);
+static DEFINE_STATIC_KEY_FALSE(riscv_pmu_cdeleg_available);
+
+/* Avoid unnecessary code patching in the one time booting path*/
+#define riscv_pmu_cdeleg_available_boot() \
+ static_key_enabled(&riscv_pmu_cdeleg_available)
+#define riscv_pmu_sbi_available_boot() \
+ static_key_enabled(&riscv_pmu_sbi_available)
+
+/* Perform a runtime code patching with static key */
+#define riscv_pmu_cdeleg_available() \
+ static_branch_unlikely(&riscv_pmu_cdeleg_available)
+#define riscv_pmu_sbi_available() \
+ static_branch_likely(&riscv_pmu_sbi_available)
static struct attribute *riscv_arch_formats_attr[] = {
&format_attr_event.attr,
@@ -89,7 +103,8 @@ static int sysctl_perf_user_access __read_mostly = SYSCTL_USER_ACCESS;
/*
* This structure is SBI specific but counter delegation also require counter
- * width, csr mapping. Reuse it for now.
+ * width, csr mapping. Reuse it for now we can have firmware counters for
+ * platfroms with counter delegation support.
* RISC-V doesn't have heterogeneous harts yet. This need to be part of
* per_cpu in case of harts with different pmu counters
*/
@@ -101,6 +116,8 @@ static unsigned int riscv_pmu_irq;
/* Cache the available counters in a bitmask */
static unsigned long cmask;
+/* Cache the available firmware counters in another bitmask */
+static unsigned long firmware_cmask;
static int sbi_pmu_event_find_cache(u64 config);
struct sbi_pmu_event_data {
@@ -868,34 +885,38 @@ static int rvpmu_sbi_find_num_ctrs(void)
return sbi_err_map_linux_errno(ret.error);
}
-static int rvpmu_sbi_get_ctrinfo(int nctr, unsigned long *mask)
+static u32 rvpmu_deleg_find_ctrs(void)
+{
+ /* TODO */
+ return 0;
+}
+
+static int rvpmu_sbi_get_ctrinfo(u32 nsbi_ctr, u32 *num_fw_ctr, u32 *num_hw_ctr)
{
struct sbiret ret;
- int i, num_hw_ctr = 0, num_fw_ctr = 0;
+ int i;
union sbi_pmu_ctr_info cinfo;
- pmu_ctr_list = kzalloc_objs(*pmu_ctr_list, nctr);
- if (!pmu_ctr_list)
- return -ENOMEM;
-
- for (i = 0; i < nctr; i++) {
+ for (i = 0; i < nsbi_ctr; i++) {
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_GET_INFO, i, 0, 0, 0, 0, 0);
if (ret.error)
/* The logical counter ids are not expected to be contiguous */
continue;
- *mask |= BIT(i);
-
cinfo.value = ret.value;
- if (cinfo.type == SBI_PMU_CTR_TYPE_FW)
- num_fw_ctr++;
- else
- num_hw_ctr++;
- pmu_ctr_list[i].value = cinfo.value;
+ if (cinfo.type == SBI_PMU_CTR_TYPE_FW) {
+ /* Track firmware counters in a different mask */
+ firmware_cmask |= BIT(i);
+ pmu_ctr_list[i].value = cinfo.value;
+ *num_fw_ctr = *num_fw_ctr + 1;
+ } else if (cinfo.type == SBI_PMU_CTR_TYPE_HW &&
+ !riscv_pmu_cdeleg_available_boot()) {
+ *num_hw_ctr = *num_hw_ctr + 1;
+ cmask |= BIT(i);
+ pmu_ctr_list[i].value = cinfo.value;
+ }
}
- pr_info("%d firmware and %d hardware counters\n", num_fw_ctr, num_hw_ctr);
-
return 0;
}
@@ -1159,16 +1180,42 @@ static void rvpmu_ctr_stop(struct perf_event *event, unsigned long flag)
/* TODO: Counter delegation implementation */
}
-static int rvpmu_find_num_ctrs(void)
+static int rvpmu_find_ctrs(void)
{
- return rvpmu_sbi_find_num_ctrs();
- /* TODO: Counter delegation implementation */
-}
+ u32 num_sbi_counters = 0, num_deleg_counters = 0;
+ u32 num_hw_ctr = 0, num_fw_ctr = 0, num_ctr = 0;
+ /*
+ * We don't know how many firmware counters are available. Just allocate
+ * for maximum counters the driver can support. The default is 64 anyways.
+ */
+ pmu_ctr_list = kcalloc(RISCV_MAX_COUNTERS, sizeof(*pmu_ctr_list),
+ GFP_KERNEL);
+ if (!pmu_ctr_list)
+ return -ENOMEM;
-static int rvpmu_get_ctrinfo(int nctr, unsigned long *mask)
-{
- return rvpmu_sbi_get_ctrinfo(nctr, mask);
- /* TODO: Counter delegation implementation */
+ if (riscv_pmu_cdeleg_available_boot())
+ num_deleg_counters = rvpmu_deleg_find_ctrs();
+
+ /* This is required for firmware counters even if the above is true */
+ if (riscv_pmu_sbi_available_boot())
+ num_sbi_counters = rvpmu_sbi_find_num_ctrs();
+
+ if (num_sbi_counters > RISCV_MAX_COUNTERS || num_deleg_counters > RISCV_MAX_COUNTERS)
+ return -ENOSPC;
+
+ /* cache all the information about counters now */
+ if (riscv_pmu_sbi_available_boot())
+ rvpmu_sbi_get_ctrinfo(num_sbi_counters, &num_fw_ctr, &num_hw_ctr);
+
+ if (riscv_pmu_cdeleg_available_boot()) {
+ pr_info("%u firmware and %u hardware counters\n", num_fw_ctr, num_deleg_counters);
+ num_ctr = num_fw_ctr + num_deleg_counters;
+ } else {
+ pr_info("%u firmware and %u hardware counters\n", num_fw_ctr, num_hw_ctr);
+ num_ctr = num_sbi_counters;
+ }
+
+ return num_ctr;
}
static int rvpmu_event_map(struct perf_event *event, u64 *econfig)
@@ -1469,12 +1516,21 @@ static int rvpmu_device_probe(struct platform_device *pdev)
int ret = -ENODEV;
int num_counters;
- pr_info("SBI PMU extension is available\n");
+ if (riscv_pmu_cdeleg_available_boot()) {
+ pr_info("hpmcounters will use the counter delegation ISA extension\n");
+ if (riscv_pmu_sbi_available_boot())
+ pr_info("Firmware counters will use SBI PMU extension\n");
+ else
+ pr_info("Firmware counters will not be available as SBI PMU extension is not present\n");
+ } else if (riscv_pmu_sbi_available_boot()) {
+ pr_info("Both hpmcounters and firmware counters will use SBI PMU extension\n");
+ }
+
pmu = riscv_pmu_alloc();
if (!pmu)
return -ENOMEM;
- num_counters = rvpmu_find_num_ctrs();
+ num_counters = rvpmu_find_ctrs();
if (num_counters < 0) {
pr_err("SBI PMU extension doesn't provide any counters\n");
goto out_free;
@@ -1486,9 +1542,6 @@ static int rvpmu_device_probe(struct platform_device *pdev)
pr_info("SBI returned more than maximum number of counters. Limiting the number of counters to %d\n", num_counters);
}
- /* cache all the information about counters now */
- if (rvpmu_get_ctrinfo(num_counters, &cmask))
- goto out_free;
ret = rvpmu_setup_irqs(pmu, pdev);
if (ret < 0) {
@@ -1578,13 +1631,23 @@ static int __init rvpmu_devinit(void)
int ret;
struct platform_device *pdev;
- if (sbi_spec_version < sbi_mk_version(0, 3) ||
- !sbi_probe_extension(SBI_EXT_PMU)) {
- return 0;
- }
+ if (sbi_spec_version >= sbi_mk_version(0, 3) &&
+ sbi_probe_extension(SBI_EXT_PMU))
+ static_branch_enable(&riscv_pmu_sbi_available);
if (sbi_spec_version >= sbi_mk_version(2, 0))
sbi_v2_available = true;
+ /*
+ * We need all three extensions to be present to access the counters
+ * in S-mode via Supervisor Counter delegation.
+ */
+ if (riscv_isa_extension_available(NULL, SSCCFG) &&
+ riscv_isa_extension_available(NULL, SMCDELEG) &&
+ riscv_isa_extension_available(NULL, SSCSRIND))
+ static_branch_enable(&riscv_pmu_cdeleg_available);
+
+ if (!(riscv_pmu_sbi_available_boot() || riscv_pmu_cdeleg_available_boot()))
+ return 0;
if (sbi_spec_version >= sbi_mk_version(3, 0))
sbi_v3_available = true;
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 14/21] RISC-V: perf: Skip PMU SBI extension when not implemented
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Charlie Jenkins <charlie@rivosinc.com>
When the PMU SBI extension is not implemented, sbi_v2_available should
not be set to true. The SBI implementation for counter config matching
and firmware counter read should also be skipped when the SBI extension
is not implemented.
Signed-off-by: Atish Patra <atishp@meta.com>
Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
---
drivers/perf/riscv_pmu_sbi.c | 48 ++++++++++++++++++++++++++------------------
1 file changed, 28 insertions(+), 20 deletions(-)
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 1f16df9d0dd0..5bfcd3821f57 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -479,27 +479,31 @@ static void rvpmu_sbi_check_event(struct sbi_pmu_event_data *edata)
}
}
-static void rvpmu_sbi_check_std_events(struct work_struct *work)
+static void rvpmu_check_std_events(struct work_struct *work)
{
int ret;
- if (sbi_v3_available) {
- ret = pmu_sbi_check_event_info();
- if (ret)
- pr_err("pmu_sbi_check_event_info failed with error %d\n", ret);
- return;
- }
+ if (riscv_pmu_sbi_available()) {
+ if (sbi_v3_available) {
+ ret = pmu_sbi_check_event_info();
+ if (ret)
+ pr_err("pmu_sbi_check_event_info failed with error %d\n", ret);
+ return;
+ }
- for (int i = 0; i < ARRAY_SIZE(pmu_hw_event_sbi_map); i++)
- rvpmu_sbi_check_event(&pmu_hw_event_sbi_map[i]);
+ for (int i = 0; i < ARRAY_SIZE(pmu_hw_event_sbi_map); i++)
+ rvpmu_sbi_check_event(&pmu_hw_event_sbi_map[i]);
- for (int i = 0; i < ARRAY_SIZE(pmu_cache_event_sbi_map); i++)
- for (int j = 0; j < ARRAY_SIZE(pmu_cache_event_sbi_map[i]); j++)
- for (int k = 0; k < ARRAY_SIZE(pmu_cache_event_sbi_map[i][j]); k++)
- rvpmu_sbi_check_event(&pmu_cache_event_sbi_map[i][j][k]);
+ for (int i = 0; i < ARRAY_SIZE(pmu_cache_event_sbi_map); i++)
+ for (int j = 0; j < ARRAY_SIZE(pmu_cache_event_sbi_map[i]); j++)
+ for (int k = 0; k < ARRAY_SIZE(pmu_cache_event_sbi_map[i][j]); k++)
+ rvpmu_sbi_check_event(&pmu_cache_event_sbi_map[i][j][k]);
+ } else {
+ DO_ONCE_LITE_IF(1, pr_info, "Boot time config matching not required for smcdeleg\n");
+ }
}
-static DECLARE_WORK(check_std_events_work, rvpmu_sbi_check_std_events);
+static DECLARE_WORK(check_std_events_work, rvpmu_check_std_events);
static ssize_t rvpmu_format_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -692,6 +696,9 @@ static int rvpmu_sbi_ctr_get_idx(struct perf_event *event)
cflags = rvpmu_sbi_get_filter_flags(event);
+ if (!riscv_pmu_sbi_available())
+ return -ENOENT;
+
/*
* In legacy mode, we have to force the fixed counters for those events
* but not in the user access mode as we want to use the other counters
@@ -967,7 +974,7 @@ static u64 rvpmu_ctr_read(struct perf_event *event)
return val;
}
- if (pmu_sbi_is_fw_event(event)) {
+ if (pmu_sbi_is_fw_event(event) && riscv_pmu_sbi_available()) {
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_FW_READ,
hwc->idx, 0, 0, 0, 0, 0);
if (ret.error)
@@ -2045,12 +2052,13 @@ static int __init rvpmu_devinit(void)
int ret;
struct platform_device *pdev;
- if (sbi_spec_version >= sbi_mk_version(0, 3) &&
- sbi_probe_extension(SBI_EXT_PMU))
- static_branch_enable(&riscv_pmu_sbi_available);
+ if (sbi_probe_extension(SBI_EXT_PMU)) {
+ if (sbi_spec_version >= sbi_mk_version(0, 3))
+ static_branch_enable(&riscv_pmu_sbi_available);
+ if (sbi_spec_version >= sbi_mk_version(2, 0))
+ sbi_v2_available = true;
+ }
- if (sbi_spec_version >= sbi_mk_version(2, 0))
- sbi_v2_available = true;
/*
* We need all three extensions to be present to access the counters
* in S-mode via Supervisor Counter delegation.
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 09/21] dt-bindings: riscv: add Counter delegation ISA extensions description
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
Add description for the Smcdeleg/Ssccfg extension.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
.../devicetree/bindings/riscv/extensions.yaml | 45 ++++++++++++++++++++++
1 file changed, 45 insertions(+)
diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
index ece3edccee42..2845e8e2999a 100644
--- a/Documentation/devicetree/bindings/riscv/extensions.yaml
+++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
@@ -181,6 +181,13 @@ properties:
changes to interrupts as frozen at commit ccbddab ("Merge pull
request #42 from riscv/jhauser-2023-RC4") of riscv-aia.
+ - const: smcdeleg
+ description: |
+ The standard Smcdeleg supervisor-level extension for the machine mode
+ to delegate the hpmcounters to supervisor mode so that they are
+ directly accessible in the supervisor mode as ratified in the
+ 20240213 version of the privileged ISA specification.
+
- const: smcsrind
description: |
The standard Smcsrind supervisor-level extension extends the
@@ -228,6 +235,14 @@ properties:
behavioural changes to interrupts as frozen at commit ccbddab
("Merge pull request #42 from riscv/jhauser-2023-RC4") of riscv-aia.
+ - const: ssccfg
+ description: |
+ The standard Ssccfg supervisor-level extension for configuring
+ the delegated hpmcounters to be accessible directly in supervisor
+ mode as ratified in the 20240213 version of the privileged ISA
+ specification. This extension depends on Sscsrind, Smcdeleg, Sscofpmf,
+ Smcntrpmf, Zihpm, Zicntr extensions.
+
- const: ssccptr
description: |
The standard Ssccptr extension for main memory (cacheability and
@@ -1135,6 +1150,36 @@ properties:
allOf:
- const: zilsd
- const: zca
+ # Smcdeleg depends on Sscsrind, Zihpm, Zicntr
+ - if:
+ contains:
+ const: smcdeleg
+ then:
+ allOf:
+ - contains:
+ const: sscsrind
+ - contains:
+ const: zihpm
+ - contains:
+ const: zicntr
+ # Ssccfg depends on Smcdeleg, Sscsrind, Zihpm, Zicntr, Sscofpmf, Smcntrpmf
+ - if:
+ contains:
+ const: ssccfg
+ then:
+ allOf:
+ - contains:
+ const: smcdeleg
+ - contains:
+ const: sscsrind
+ - contains:
+ const: sscofpmf
+ - contains:
+ const: smcntrpmf
+ - contains:
+ const: zihpm
+ - contains:
+ const: zicntr
allOf:
# Zcf extension does not exist on rv64
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 16/21] RISC-V: perf: Add legacy event encodings via sysfs
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
Define sysfs details for the legacy events so that any tool can
parse these to understand the minimum set of legacy events
supported by the platform. The sysfs entry will describe both event
encoding and corresponding counter map so that an perf event can be
programmed accordingly.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
drivers/perf/riscv_pmu_sbi.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 4b4f151a0744..00b84b28117a 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -130,7 +130,20 @@ static struct attribute_group riscv_cdeleg_pmu_format_group = {
.attrs = riscv_cdeleg_pmu_formats_attr,
};
+#define RVPMU_EVENT_ATTR_RESOLVE(m) #m
+#define RVPMU_EVENT_CMASK_ATTR(_name, _var, config, mask) \
+ PMU_EVENT_ATTR_STRING(_name, rvpmu_event_attr_##_var, \
+ "event=" RVPMU_EVENT_ATTR_RESOLVE(config) \
+ ",counterid_mask=" RVPMU_EVENT_ATTR_RESOLVE(mask))
+
+#define RVPMU_EVENT_ATTR_PTR(name) (&rvpmu_event_attr_##name.attr.attr)
+
+static struct attribute_group riscv_cdeleg_pmu_event_group __ro_after_init = {
+ .name = "events",
+};
+
static const struct attribute_group *riscv_cdeleg_pmu_attr_groups[] = {
+ &riscv_cdeleg_pmu_event_group,
&riscv_cdeleg_pmu_format_group,
NULL,
};
@@ -431,11 +444,14 @@ struct riscv_vendor_pmu_events {
const struct riscv_pmu_event *hw_event_map;
const struct riscv_pmu_event (*cache_event_map)[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_COUNT_HW_CACHE_RESULT_MAX];
+ struct attribute **attrs_events;
};
-#define RISCV_VENDOR_PMU_EVENTS(_vendorid, _archid, _implid, _hw_event_map, _cache_event_map) \
+#define RISCV_VENDOR_PMU_EVENTS(_vendorid, _archid, _implid, _hw_event_map, \
+ _cache_event_map, _attrs) \
{ .vendorid = _vendorid, .archid = _archid, .implid = _implid, \
- .hw_event_map = _hw_event_map, .cache_event_map = _cache_event_map },
+ .hw_event_map = _hw_event_map, .cache_event_map = _cache_event_map, \
+ .attrs_events = _attrs },
static struct riscv_vendor_pmu_events pmu_vendor_events_table[] = {
};
@@ -457,6 +473,8 @@ static void rvpmu_vendor_register_events(void)
pmu_vendor_events_table[i].archid == arch_id) {
current_pmu_hw_event_map = pmu_vendor_events_table[i].hw_event_map;
current_pmu_cache_event_map = pmu_vendor_events_table[i].cache_event_map;
+ riscv_cdeleg_pmu_event_group.attrs =
+ pmu_vendor_events_table[i].attrs_events;
break;
}
}
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 13/21] RISC-V: perf: Implement supervisor counter delegation support
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
There are few new RISC-V ISA exensions (ssccfg, sscsrind, smcntrpmf) which
allows the hpmcounter/hpmevents to be programmed directly from S-mode. The
implementation detects the ISA extension at runtime and uses them if
available instead of SBI PMU extension. SBI PMU extension will still be
used for firmware counters if the user requests it.
The current linux driver relies on event encoding defined by SBI PMU
specification for standard perf events. However, there are no standard
event encoding available in the ISA. In the future, we may want to
decouple the counter delegation and SBI PMU completely. In that case,
counter delegation supported platforms must rely on the event encoding
defined in the perf json file or in the pmu driver.
For firmware events, it will continue to use the SBI PMU encoding as
one can not support firmware event without SBI PMU.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
arch/riscv/include/asm/csr.h | 1 +
drivers/perf/riscv_pmu_sbi.c | 574 +++++++++++++++++++++++++++++++++--------
include/linux/perf/riscv_pmu.h | 3 +
3 files changed, 473 insertions(+), 105 deletions(-)
diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index 26cb78dee2fd..25ebf853bfef 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -266,6 +266,7 @@
#endif
#define SISELECT_SSCCFG_BASE 0x40
+#define HPMEVENT_MASK GENMASK_ULL(63, 56)
/* mseccfg bits */
#define MSECCFG_PMM ENVCFG_PMM
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 46a25979e95e..1f16df9d0dd0 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -27,6 +27,8 @@
#include <asm/cpufeature.h>
#include <asm/vendor_extensions.h>
#include <asm/vendor_extensions/andes.h>
+#include <asm/hwcap.h>
+#include <asm/csr_ind.h>
#define ALT_SBI_PMU_OVERFLOW(__ovl) \
asm volatile(ALTERNATIVE_2( \
@@ -59,7 +61,20 @@ asm volatile(ALTERNATIVE( \
#define PERF_EVENT_FLAG_USER_ACCESS BIT(SYSCTL_USER_ACCESS)
#define PERF_EVENT_FLAG_LEGACY BIT(SYSCTL_LEGACY)
-PMU_FORMAT_ATTR(event, "config:0-55");
+#define RVPMU_SBI_PMU_FORMAT_ATTR "config:0-47"
+#define RVPMU_CDELEG_PMU_FORMAT_ATTR "config:0-55"
+
+static ssize_t __maybe_unused rvpmu_format_show(struct device *dev, struct device_attribute *attr,
+ char *buf);
+
+#define RVPMU_ATTR_ENTRY(_name, _func, _config) ( \
+ &((struct dev_ext_attribute[]) { \
+ { __ATTR(_name, 0444, _func, NULL), (void *)_config } \
+ })[0].attr.attr)
+
+#define RVPMU_FORMAT_ATTR_ENTRY(_name, _config) \
+ RVPMU_ATTR_ENTRY(_name, rvpmu_format_show, (char *)_config)
+
PMU_FORMAT_ATTR(firmware, "config:62-63");
static bool sbi_v2_available;
@@ -67,7 +82,11 @@ static bool sbi_v3_available;
static DEFINE_STATIC_KEY_FALSE(sbi_pmu_snapshot_available);
#define sbi_pmu_snapshot_available() \
static_branch_unlikely(&sbi_pmu_snapshot_available)
+
static DEFINE_STATIC_KEY_FALSE(riscv_pmu_sbi_available);
+#define riscv_pmu_sbi_available() \
+ static_branch_likely(&riscv_pmu_sbi_available)
+
static DEFINE_STATIC_KEY_FALSE(riscv_pmu_cdeleg_available);
/* Avoid unnecessary code patching in the one time booting path*/
@@ -82,19 +101,35 @@ static DEFINE_STATIC_KEY_FALSE(riscv_pmu_cdeleg_available);
#define riscv_pmu_sbi_available() \
static_branch_likely(&riscv_pmu_sbi_available)
-static struct attribute *riscv_arch_formats_attr[] = {
- &format_attr_event.attr,
+static struct attribute *riscv_sbi_pmu_formats_attr[] = {
+ RVPMU_FORMAT_ATTR_ENTRY(event, RVPMU_SBI_PMU_FORMAT_ATTR),
+ &format_attr_firmware.attr,
+ NULL,
+};
+
+static struct attribute_group riscv_sbi_pmu_format_group = {
+ .name = "format",
+ .attrs = riscv_sbi_pmu_formats_attr,
+};
+
+static const struct attribute_group *riscv_sbi_pmu_attr_groups[] = {
+ &riscv_sbi_pmu_format_group,
+ NULL,
+};
+
+static struct attribute *riscv_cdeleg_pmu_formats_attr[] = {
+ RVPMU_FORMAT_ATTR_ENTRY(event, RVPMU_CDELEG_PMU_FORMAT_ATTR),
&format_attr_firmware.attr,
NULL,
};
-static struct attribute_group riscv_pmu_format_group = {
+static struct attribute_group riscv_cdeleg_pmu_format_group = {
.name = "format",
- .attrs = riscv_arch_formats_attr,
+ .attrs = riscv_cdeleg_pmu_formats_attr,
};
-static const struct attribute_group *riscv_pmu_attr_groups[] = {
- &riscv_pmu_format_group,
+static const struct attribute_group *riscv_cdeleg_pmu_attr_groups[] = {
+ &riscv_cdeleg_pmu_format_group,
NULL,
};
@@ -466,6 +501,14 @@ static void rvpmu_sbi_check_std_events(struct work_struct *work)
static DECLARE_WORK(check_std_events_work, rvpmu_sbi_check_std_events);
+static ssize_t rvpmu_format_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dev_ext_attribute *eattr = container_of(attr,
+ struct dev_ext_attribute, attr);
+ return sysfs_emit(buf, "%s\n", (char *)eattr->var);
+}
+
static int rvpmu_ctr_get_width(int idx)
{
return pmu_ctr_list[idx].width;
@@ -583,6 +626,38 @@ static uint8_t rvpmu_csr_index(struct perf_event *event)
return pmu_ctr_list[event->hw.idx].csr - CSR_CYCLE;
}
+static uint64_t get_deleg_priv_filter_bits(struct perf_event *event)
+{
+ u64 priv_filter_bits = 0;
+ bool guest_events = false;
+
+ if (event->attr.config1 & RISCV_PMU_CONFIG1_GUEST_EVENTS)
+ guest_events = true;
+ if (event->attr.exclude_kernel)
+ priv_filter_bits |= guest_events ? HPMEVENT_VSINH : HPMEVENT_SINH;
+ if (event->attr.exclude_user)
+ priv_filter_bits |= guest_events ? HPMEVENT_VUINH : HPMEVENT_UINH;
+ if (guest_events && event->attr.exclude_hv)
+ priv_filter_bits |= HPMEVENT_SINH;
+ if (event->attr.exclude_host)
+ priv_filter_bits |= HPMEVENT_UINH | HPMEVENT_SINH;
+ if (event->attr.exclude_guest)
+ priv_filter_bits |= HPMEVENT_VSINH | HPMEVENT_VUINH;
+
+ return priv_filter_bits;
+}
+
+static bool pmu_sbi_is_fw_event(struct perf_event *event)
+{
+ u32 type = event->attr.type;
+ u64 config = event->attr.config;
+
+ if (type == PERF_TYPE_RAW && ((config >> 63) == 1))
+ return true;
+ else
+ return false;
+}
+
static unsigned long rvpmu_sbi_get_filter_flags(struct perf_event *event)
{
unsigned long cflags = 0;
@@ -611,7 +686,8 @@ static int rvpmu_sbi_ctr_get_idx(struct perf_event *event)
struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events);
struct sbiret ret;
int idx;
- uint64_t cbase = 0, cmask = rvpmu->cmask;
+ u64 cbase = 0;
+ unsigned long ctr_mask = rvpmu->cmask;
unsigned long cflags = 0;
cflags = rvpmu_sbi_get_filter_flags(event);
@@ -624,21 +700,23 @@ static int rvpmu_sbi_ctr_get_idx(struct perf_event *event)
if ((hwc->flags & PERF_EVENT_FLAG_LEGACY) && (event->attr.type == PERF_TYPE_HARDWARE)) {
if (event->attr.config == PERF_COUNT_HW_CPU_CYCLES) {
cflags |= SBI_PMU_CFG_FLAG_SKIP_MATCH;
- cmask = 1;
+ ctr_mask = 1;
} else if (event->attr.config == PERF_COUNT_HW_INSTRUCTIONS) {
cflags |= SBI_PMU_CFG_FLAG_SKIP_MATCH;
- cmask = BIT(CSR_INSTRET - CSR_CYCLE);
+ ctr_mask = BIT(CSR_INSTRET - CSR_CYCLE);
}
+ } else if (pmu_sbi_is_fw_event(event)) {
+ ctr_mask = firmware_cmask;
}
/* retrieve the available counter index */
#if defined(CONFIG_32BIT)
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase,
- cmask, cflags, hwc->event_base, hwc->config,
+ ctr_mask, cflags, hwc->event_base, hwc->config,
hwc->config >> 32);
#else
ret = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, cbase,
- cmask, cflags, hwc->event_base, hwc->config, 0);
+ ctr_mask, cflags, hwc->event_base, hwc->config, 0);
#endif
if (ret.error) {
pr_debug("Not able to find a counter for event %lx config %llx\n",
@@ -647,7 +725,7 @@ static int rvpmu_sbi_ctr_get_idx(struct perf_event *event)
}
idx = ret.value;
- if (!test_bit(idx, &rvpmu->cmask) || !pmu_ctr_list[idx].value)
+ if (!test_bit(idx, &ctr_mask) || !pmu_ctr_list[idx].value)
return -ENOENT;
/* Additional sanity check for the counter id */
@@ -697,29 +775,96 @@ static int sbi_pmu_event_find_cache(u64 config)
return ret;
}
-static bool pmu_sbi_is_fw_event(struct perf_event *event)
+static int rvpmu_sbi_event_map(struct perf_event *event, u64 *econfig)
{
u32 type = event->attr.type;
u64 config = event->attr.config;
- if ((type == PERF_TYPE_RAW) && ((config >> 63) == 1))
- return true;
- else
- return false;
+ /*
+ * Ensure we are finished checking standard hardware events for
+ * validity before allowing userspace to configure any events.
+ */
+ flush_work(&check_std_events_work);
+
+ return riscv_pmu_get_event_info(type, config, econfig);
}
-static int rvpmu_sbi_event_map(struct perf_event *event, u64 *econfig)
+static int cdeleg_pmu_event_find_cache(u64 config, u64 *eventid, uint32_t *counter_mask)
+{
+ unsigned int cache_type, cache_op, cache_result;
+
+ if (!current_pmu_cache_event_map)
+ return -ENOENT;
+
+ cache_type = (config >> 0) & 0xff;
+ if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
+ return -EINVAL;
+
+ cache_op = (config >> 8) & 0xff;
+ if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX)
+ return -EINVAL;
+
+ cache_result = (config >> 16) & 0xff;
+ if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
+ return -EINVAL;
+
+ if (eventid)
+ *eventid = current_pmu_cache_event_map[cache_type][cache_op]
+ [cache_result].event_id;
+ if (counter_mask)
+ *counter_mask = current_pmu_cache_event_map[cache_type][cache_op]
+ [cache_result].counter_mask;
+
+ return 0;
+}
+
+static int rvpmu_cdeleg_event_map(struct perf_event *event, u64 *econfig)
{
u32 type = event->attr.type;
u64 config = event->attr.config;
+ int ret = 0;
/*
- * Ensure we are finished checking standard hardware events for
- * validity before allowing userspace to configure any events.
+ * There are two ways standard perf events can be mapped to platform specific
+ * encoding.
+ * 1. The vendor may specify the encodings in the driver.
+ * 2. The Perf tool for RISC-V may remap the standard perf event to platform
+ * specific encoding.
+ *
+ * As RISC-V ISA doesn't define any standard event encoding. Thus, perf tool allows
+ * vendor to define it via json file. The encoding defined in the json will override
+ * the perf legacy encoding. However, some user may want to run performance
+ * monitoring without perf tool as well. That's why, vendors may specify the event
+ * encoding in the driver as well if they want to support that use case too.
+ * If an encoding is defined in the json, it will be encoded as a raw event.
*/
- flush_work(&check_std_events_work);
- return riscv_pmu_get_event_info(type, config, econfig);
+ switch (type) {
+ case PERF_TYPE_HARDWARE:
+ if (config >= PERF_COUNT_HW_MAX)
+ return -EINVAL;
+ if (!current_pmu_hw_event_map)
+ return -ENOENT;
+
+ *econfig = current_pmu_hw_event_map[config].event_id;
+ if (*econfig == HW_OP_UNSUPPORTED)
+ ret = -ENOENT;
+ break;
+ case PERF_TYPE_HW_CACHE:
+ ret = cdeleg_pmu_event_find_cache(config, econfig, NULL);
+ if (*econfig == HW_OP_UNSUPPORTED)
+ ret = -ENOENT;
+ break;
+ case PERF_TYPE_RAW:
+ *econfig = config & RISCV_PMU_DELEG_RAW_EVENT_MASK;
+ break;
+ default:
+ ret = -ENOENT;
+ break;
+ }
+
+ /* event_base is not used for counter delegation */
+ return ret;
}
static void pmu_sbi_snapshot_free(struct riscv_pmu *pmu)
@@ -805,7 +950,7 @@ static int pmu_sbi_snapshot_setup(struct riscv_pmu *pmu, int cpu)
return 0;
}
-static u64 rvpmu_sbi_ctr_read(struct perf_event *event)
+static u64 rvpmu_ctr_read(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx;
@@ -882,10 +1027,6 @@ static void rvpmu_sbi_ctr_start(struct perf_event *event, u64 ival)
if (ret.error && (ret.error != SBI_ERR_ALREADY_STARTED))
pr_err("Starting counter idx %d failed with error %d\n",
hwc->idx, sbi_err_map_linux_errno(ret.error));
-
- if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
- (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
- rvpmu_set_scounteren((void *)event);
}
static void rvpmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
@@ -896,10 +1037,6 @@ static void rvpmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr;
- if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
- (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
- rvpmu_reset_scounteren((void *)event);
-
if (sbi_pmu_snapshot_available())
flag |= SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT;
@@ -935,12 +1072,6 @@ static int rvpmu_sbi_find_num_ctrs(void)
return sbi_err_map_linux_errno(ret.error);
}
-static u32 rvpmu_deleg_find_ctrs(void)
-{
- /* TODO */
- return 0;
-}
-
static int rvpmu_sbi_get_ctrinfo(u32 nsbi_ctr, u32 *num_fw_ctr, u32 *num_hw_ctr)
{
struct sbiret ret;
@@ -1018,55 +1149,75 @@ static inline void rvpmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
}
}
-/*
- * This function starts all the used counters in two step approach.
- * Any counter that did not overflow can be start in a single step
- * while the overflowed counters need to be started with updated initialization
- * value.
- */
-static inline void rvpmu_sbi_start_ovf_ctrs_sbi(struct cpu_hw_events *cpu_hw_evt,
- u64 ctr_ovf_mask)
+static void rvpmu_deleg_ctr_start_mask(unsigned long mask)
{
- int idx = 0, i;
- struct perf_event *event;
- unsigned long flag = SBI_PMU_START_FLAG_SET_INIT_VALUE;
- unsigned long ctr_start_mask = 0;
- uint64_t max_period;
- struct hw_perf_event *hwc;
- u64 init_val = 0;
+ unsigned long scountinhibit_val = 0;
- for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) {
- ctr_start_mask = cpu_hw_evt->used_hw_ctrs[i] & ~ctr_ovf_mask;
- /* Start all the counters that did not overflow in a single shot */
- if (ctr_start_mask) {
- sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, i * BITS_PER_LONG,
- ctr_start_mask, 0, 0, 0, 0);
- }
- }
+ scountinhibit_val = csr_read(CSR_SCOUNTINHIBIT);
+ scountinhibit_val &= ~mask;
+
+ csr_write(CSR_SCOUNTINHIBIT, scountinhibit_val);
+}
+
+static void rvpmu_deleg_ctr_enable_irq(struct perf_event *event)
+{
+ unsigned long hpmevent_curr;
+ unsigned long of_mask;
+ struct hw_perf_event *hwc = &event->hw;
+ int counter_idx = hwc->idx;
+ unsigned long sip_val = csr_read(CSR_SIP);
+
+ if (!is_sampling_event(event) || (sip_val & SIP_LCOFIP))
+ return;
- /* Reinitialize and start all the counter that overflowed */
- while (ctr_ovf_mask) {
- if (ctr_ovf_mask & 0x01) {
- event = cpu_hw_evt->events[idx];
- hwc = &event->hw;
- max_period = riscv_pmu_ctr_get_width_mask(event);
- init_val = local64_read(&hwc->prev_count) & max_period;
#if defined(CONFIG_32BIT)
- sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1,
- flag, init_val, init_val >> 32, 0);
+ hpmevent_curr = csr_ind_read(CSR_SIREG5, SISELECT_SSCCFG_BASE, counter_idx);
+ of_mask = (u32)~HPMEVENTH_OF;
#else
- sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, idx, 1,
- flag, init_val, 0, 0);
+ hpmevent_curr = csr_ind_read(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx);
+ of_mask = ~HPMEVENT_OF;
#endif
- perf_event_update_userpage(event);
- }
- ctr_ovf_mask = ctr_ovf_mask >> 1;
- idx++;
- }
+
+ hpmevent_curr &= of_mask;
+#if defined(CONFIG_32BIT)
+ csr_ind_write(CSR_SIREG4, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_curr);
+#else
+ csr_ind_write(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_curr);
+#endif
+}
+
+static void rvpmu_deleg_ctr_start(struct perf_event *event, u64 ival)
+{
+ unsigned long scountinhibit_val = 0;
+ struct hw_perf_event *hwc = &event->hw;
+
+#if defined(CONFIG_32BIT)
+ csr_ind_write(CSR_SIREG, SISELECT_SSCCFG_BASE, hwc->idx, ival & 0xFFFFFFFF);
+ csr_ind_write(CSR_SIREG4, SISELECT_SSCCFG_BASE, hwc->idx, ival >> BITS_PER_LONG);
+#else
+ csr_ind_write(CSR_SIREG, SISELECT_SSCCFG_BASE, hwc->idx, ival);
+#endif
+
+ rvpmu_deleg_ctr_enable_irq(event);
+
+ scountinhibit_val = csr_read(CSR_SCOUNTINHIBIT);
+ scountinhibit_val &= ~(1 << hwc->idx);
+
+ csr_write(CSR_SCOUNTINHIBIT, scountinhibit_val);
+}
+
+static void rvpmu_deleg_ctr_stop_mask(unsigned long mask)
+{
+ unsigned long scountinhibit_val = 0;
+
+ scountinhibit_val = csr_read(CSR_SCOUNTINHIBIT);
+ scountinhibit_val |= mask;
+
+ csr_write(CSR_SCOUNTINHIBIT, scountinhibit_val);
}
-static inline void rvpmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_evt,
- u64 ctr_ovf_mask)
+static void rvpmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_evt,
+ u64 ctr_ovf_mask)
{
int i, idx = 0;
struct perf_event *event;
@@ -1100,15 +1251,53 @@ static inline void rvpmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_h
}
}
-static void rvpmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
- u64 ctr_ovf_mask)
+/*
+ * This function starts all the used counters in two step approach.
+ * Any counter that did not overflow can be start in a single step
+ * while the overflowed counters need to be started with updated initialization
+ * value.
+ */
+static void rvpmu_start_overflow_mask(struct riscv_pmu *pmu, u64 ctr_ovf_mask)
{
+ int idx = 0, i;
+ struct perf_event *event;
+ unsigned long ctr_start_mask = 0;
+ u64 max_period, init_val = 0;
+ struct hw_perf_event *hwc;
struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
if (sbi_pmu_snapshot_available())
- rvpmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
- else
- rvpmu_sbi_start_ovf_ctrs_sbi(cpu_hw_evt, ctr_ovf_mask);
+ return rvpmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
+
+ /* Start all the counters that did not overflow */
+ if (riscv_pmu_cdeleg_available()) {
+ ctr_start_mask = cpu_hw_evt->used_hw_ctrs[0] & ~ctr_ovf_mask;
+ rvpmu_deleg_ctr_start_mask(ctr_start_mask);
+ } else {
+ for (i = 0; i < BITS_TO_LONGS(RISCV_MAX_COUNTERS); i++) {
+ ctr_start_mask = cpu_hw_evt->used_hw_ctrs[i] & ~ctr_ovf_mask;
+ /* Start all the counters that did not overflow in a single shot */
+ sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, i * BITS_PER_LONG,
+ ctr_start_mask, 0, 0, 0, 0);
+ }
+ }
+
+ /* Reinitialize and start all the counter that overflowed */
+ while (ctr_ovf_mask) {
+ if (ctr_ovf_mask & 0x01) {
+ event = cpu_hw_evt->events[idx];
+ hwc = &event->hw;
+ max_period = riscv_pmu_ctr_get_width_mask(event);
+ init_val = local64_read(&hwc->prev_count) & max_period;
+ if (riscv_pmu_cdeleg_available())
+ rvpmu_deleg_ctr_start(event, init_val);
+ else
+ rvpmu_sbi_ctr_start(event, init_val);
+ perf_event_update_userpage(event);
+ }
+ ctr_ovf_mask = ctr_ovf_mask >> 1;
+ idx++;
+ }
}
static irqreturn_t rvpmu_ovf_handler(int irq, void *dev)
@@ -1143,10 +1332,18 @@ static irqreturn_t rvpmu_ovf_handler(int irq, void *dev)
}
pmu = to_riscv_pmu(event->pmu);
- rvpmu_sbi_stop_hw_ctrs(pmu);
+ if (riscv_pmu_cdeleg_available())
+ rvpmu_deleg_ctr_stop_mask(cpu_hw_evt->used_hw_ctrs[0]);
+ else
+ rvpmu_sbi_stop_hw_ctrs(pmu);
- /* Overflow status register should only be read after counter are stopped */
- if (sbi_pmu_snapshot_available())
+ /*
+ * Overflow status register should only be read after counter are stopped.
+ * In counter delegation mode the overflows are reported in scountovf, not
+ * in the SBI snapshot area, so read the CSR directly even when an SBI PMU
+ * snapshot is also available.
+ */
+ if (sbi_pmu_snapshot_available() && !riscv_pmu_cdeleg_available())
overflow = sdata->ctr_overflow_mask;
else
ALT_SBI_PMU_OVERFLOW(overflow);
@@ -1212,25 +1409,182 @@ static irqreturn_t rvpmu_ovf_handler(int irq, void *dev)
hw_evt->state = 0;
}
- rvpmu_sbi_start_overflow_mask(pmu, overflowed_ctrs);
+ rvpmu_start_overflow_mask(pmu, overflowed_ctrs);
perf_sample_event_took(sched_clock() - start_clock);
return IRQ_HANDLED;
}
+static int get_deleg_hw_ctr_width(int counter_offset)
+{
+ unsigned long hpm_warl;
+ int num_bits;
+
+ if (counter_offset < 3 || counter_offset > 31)
+ return 0;
+
+ hpm_warl = csr_ind_warl(CSR_SIREG, SISELECT_SSCCFG_BASE, counter_offset, -1);
+ num_bits = __fls(hpm_warl);
+
+#if defined(CONFIG_32BIT)
+ /*
+ * The low half contributes a full BITS_PER_LONG bits when the counter is
+ * wider than 32 bits; the high half's __fls() gives the remaining width.
+ */
+ hpm_warl = csr_ind_warl(CSR_SIREG4, SISELECT_SSCCFG_BASE, counter_offset, -1);
+ if (hpm_warl)
+ num_bits = BITS_PER_LONG + __fls(hpm_warl);
+#endif
+ return num_bits;
+}
+
+static int rvpmu_deleg_find_ctrs(void)
+{
+ int i, num_hw_ctr = 0;
+ union sbi_pmu_ctr_info cinfo;
+ unsigned long scountinhibit_old = 0;
+
+ /* Do a WARL write/read to detect which hpmcounters have been delegated */
+ scountinhibit_old = csr_read(CSR_SCOUNTINHIBIT);
+ csr_write(CSR_SCOUNTINHIBIT, -1);
+ cmask = csr_read(CSR_SCOUNTINHIBIT);
+
+ csr_write(CSR_SCOUNTINHIBIT, scountinhibit_old);
+
+ for_each_set_bit(i, &cmask, RISCV_MAX_HW_COUNTERS) {
+ if (unlikely(i == 1))
+ continue; /* This should never happen as TM is read only */
+ cinfo.value = 0;
+ cinfo.type = SBI_PMU_CTR_TYPE_HW;
+ /*
+ * If counter delegation is enabled, the csr stored to the cinfo will
+ * be a virtual counter that the delegation attempts to read.
+ */
+ cinfo.csr = CSR_CYCLE + i;
+ if (i == 0 || i == 2)
+ cinfo.width = 63;
+ else
+ cinfo.width = get_deleg_hw_ctr_width(i);
+
+ num_hw_ctr++;
+ pmu_ctr_list[i].value = cinfo.value;
+ }
+
+ return num_hw_ctr;
+}
+
+static int get_deleg_fixed_hw_idx(struct cpu_hw_events *cpuc, struct perf_event *event)
+{
+ return -EINVAL;
+}
+
+static int get_deleg_next_hpm_hw_idx(struct cpu_hw_events *cpuc, struct perf_event *event)
+{
+ unsigned long hw_ctr_mask = 0;
+
+ /*
+ * TODO: Treat every hpmcounter can monitor every event for now.
+ * The event to counter mapping should come from the json file.
+ * The mapping should also tell if sampling is supported or not.
+ */
+
+ /* Select only hpmcounters */
+ hw_ctr_mask = cmask & (~0x7);
+ hw_ctr_mask &= ~(cpuc->used_hw_ctrs[0]);
+ return __ffs(hw_ctr_mask);
+}
+
+static void update_deleg_hpmevent(int counter_idx, uint64_t event_value, uint64_t filter_bits)
+{
+ u64 hpmevent_value = 0;
+
+ /* OF bit should be enable during the start if sampling is requested */
+ hpmevent_value = (event_value & ~HPMEVENT_MASK) | filter_bits | HPMEVENT_OF;
+#if defined(CONFIG_32BIT)
+ csr_ind_write(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_value & 0xFFFFFFFF);
+ if (riscv_isa_extension_available(NULL, SSCOFPMF))
+ csr_ind_write(CSR_SIREG5, SISELECT_SSCCFG_BASE, counter_idx,
+ hpmevent_value >> BITS_PER_LONG);
+#else
+ csr_ind_write(CSR_SIREG2, SISELECT_SSCCFG_BASE, counter_idx, hpmevent_value);
+#endif
+}
+
+static int rvpmu_deleg_ctr_get_idx(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+ struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
+ struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events);
+ unsigned long hw_ctr_max_id;
+ u64 priv_filter;
+ int idx;
+
+ /*
+ * TODO: We should not rely on SBI Perf encoding to check if the event
+ * is a fixed one or not.
+ */
+ if (!is_sampling_event(event)) {
+ idx = get_deleg_fixed_hw_idx(cpuc, event);
+ if (idx == 0 || idx == 2) {
+ /* Priv mode filter bits are only available if smcntrpmf is present */
+ if (riscv_isa_extension_available(NULL, SMCNTRPMF))
+ goto found_idx;
+ else
+ goto skip_update;
+ }
+ }
+
+ hw_ctr_max_id = __fls(cmask);
+ idx = get_deleg_next_hpm_hw_idx(cpuc, event);
+ if (idx < 3 || idx > hw_ctr_max_id)
+ goto out_err;
+found_idx:
+ priv_filter = get_deleg_priv_filter_bits(event);
+ update_deleg_hpmevent(idx, hwc->config, priv_filter);
+skip_update:
+ if (!test_and_set_bit(idx, cpuc->used_hw_ctrs))
+ return idx;
+out_err:
+ return -ENOENT;
+}
+
static void rvpmu_ctr_start(struct perf_event *event, u64 ival)
{
- rvpmu_sbi_ctr_start(event, ival);
- /* TODO: Counter delegation implementation */
+ struct hw_perf_event *hwc = &event->hw;
+
+ if (riscv_pmu_cdeleg_available() && !pmu_sbi_is_fw_event(event))
+ rvpmu_deleg_ctr_start(event, ival);
+ else
+ rvpmu_sbi_ctr_start(event, ival);
+
+ if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
+ (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
+ rvpmu_set_scounteren((void *)event);
}
static void rvpmu_ctr_stop(struct perf_event *event, unsigned long flag)
{
- rvpmu_sbi_ctr_stop(event, flag);
- /* TODO: Counter delegation implementation */
+ struct hw_perf_event *hwc = &event->hw;
+
+ if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
+ (hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
+ rvpmu_reset_scounteren((void *)event);
+
+ if (riscv_pmu_cdeleg_available() && !pmu_sbi_is_fw_event(event)) {
+ /*
+ * The counter is already stopped. No need to stop again. Counter
+ * mapping will be reset in clear_idx function.
+ */
+ if (flag != RISCV_PMU_STOP_FLAG_RESET)
+ rvpmu_deleg_ctr_stop_mask((1 << hwc->idx));
+ else
+ update_deleg_hpmevent(hwc->idx, 0, 0);
+ } else {
+ rvpmu_sbi_ctr_stop(event, flag);
+ }
}
-static int rvpmu_find_ctrs(void)
+static u32 rvpmu_find_ctrs(void)
{
u32 num_sbi_counters = 0, num_deleg_counters = 0;
u32 num_hw_ctr = 0, num_fw_ctr = 0, num_ctr = 0;
@@ -1270,20 +1624,18 @@ static int rvpmu_find_ctrs(void)
static int rvpmu_event_map(struct perf_event *event, u64 *econfig)
{
- return rvpmu_sbi_event_map(event, econfig);
- /* TODO: Counter delegation implementation */
+ if (riscv_pmu_cdeleg_available() && !pmu_sbi_is_fw_event(event))
+ return rvpmu_cdeleg_event_map(event, econfig);
+ else
+ return rvpmu_sbi_event_map(event, econfig);
}
static int rvpmu_ctr_get_idx(struct perf_event *event)
{
- return rvpmu_sbi_ctr_get_idx(event);
- /* TODO: Counter delegation implementation */
-}
-
-static u64 rvpmu_ctr_read(struct perf_event *event)
-{
- return rvpmu_sbi_ctr_read(event);
- /* TODO: Counter delegation implementation */
+ if (riscv_pmu_cdeleg_available() && !pmu_sbi_is_fw_event(event))
+ return rvpmu_deleg_ctr_get_idx(event);
+ else
+ return rvpmu_sbi_ctr_get_idx(event);
}
static int rvpmu_starting_cpu(unsigned int cpu, struct hlist_node *node)
@@ -1301,7 +1653,16 @@ static int rvpmu_starting_cpu(unsigned int cpu, struct hlist_node *node)
csr_write(CSR_SCOUNTEREN, 0x2);
/* Stop all the counters so that they can be enabled from perf */
- rvpmu_sbi_stop_all(pmu);
+ if (riscv_pmu_cdeleg_available()) {
+ rvpmu_deleg_ctr_stop_mask(cmask);
+ if (riscv_pmu_sbi_available()) {
+ /* Stop the firmware counters as well */
+ sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP, 0, firmware_cmask,
+ 0, 0, 0, 0);
+ }
+ } else {
+ rvpmu_sbi_stop_all(pmu);
+ }
if (riscv_pmu_use_irq) {
cpu_hw_evt->irq = riscv_pmu_irq;
@@ -1600,8 +1961,11 @@ static int rvpmu_device_probe(struct platform_device *pdev)
pmu->pmu.capabilities |= PERF_PMU_CAP_NO_EXCLUDE;
}
- pmu->pmu.attr_groups = riscv_pmu_attr_groups;
pmu->pmu.parent = &pdev->dev;
+ if (riscv_pmu_cdeleg_available_boot())
+ pmu->pmu.attr_groups = riscv_cdeleg_pmu_attr_groups;
+ else
+ pmu->pmu.attr_groups = riscv_sbi_pmu_attr_groups;
pmu->cmask = cmask;
pmu->ctr_start = rvpmu_ctr_start;
pmu->ctr_stop = rvpmu_ctr_stop;
diff --git a/include/linux/perf/riscv_pmu.h b/include/linux/perf/riscv_pmu.h
index 6c75106989b6..f6a710c83a4c 100644
--- a/include/linux/perf/riscv_pmu.h
+++ b/include/linux/perf/riscv_pmu.h
@@ -20,6 +20,7 @@
*/
#define RISCV_MAX_COUNTERS 64
+#define RISCV_MAX_HW_COUNTERS 32
#define RISCV_OP_UNSUPP (-EOPNOTSUPP)
#define RISCV_PMU_SBI_PDEV_NAME "riscv-pmu-sbi"
#define RISCV_PMU_LEGACY_PDEV_NAME "riscv-pmu-legacy"
@@ -28,6 +29,8 @@
#define RISCV_PMU_CONFIG1_GUEST_EVENTS 0x1
+#define RISCV_PMU_DELEG_RAW_EVENT_MASK GENMASK_ULL(55, 0)
+
#define HW_OP_UNSUPPORTED 0xFFFF
#define CACHE_OP_UNSUPPORTED 0xFFFF
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 12/21] RISC-V: perf: Add a mechanism to defined legacy event encoding
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
RISC-V ISA doesn't define any standard event encodings or specify
any event to counter mapping. Thus, event encoding information
and corresponding counter mapping fot those events needs to be
provided in the driver for each vendor.
Add a framework to support that. The individual platform events
will be added later.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
drivers/perf/riscv_pmu_sbi.c | 54 +++++++++++++++++++++++++++++++++++++++++-
include/linux/perf/riscv_pmu.h | 13 ++++++++++
2 files changed, 66 insertions(+), 1 deletion(-)
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 57ab15beab3e..46a25979e95e 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -379,6 +379,56 @@ static int pmu_sbi_check_event_info(void)
return result;
}
+/*
+ * Vendor specific PMU events.
+ */
+struct riscv_pmu_event {
+ u64 event_id;
+ u32 counter_mask;
+};
+
+struct riscv_vendor_pmu_events {
+ unsigned long vendorid;
+ unsigned long archid;
+ unsigned long implid;
+ const struct riscv_pmu_event *hw_event_map;
+ const struct riscv_pmu_event (*cache_event_map)[PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX];
+};
+
+#define RISCV_VENDOR_PMU_EVENTS(_vendorid, _archid, _implid, _hw_event_map, _cache_event_map) \
+ { .vendorid = _vendorid, .archid = _archid, .implid = _implid, \
+ .hw_event_map = _hw_event_map, .cache_event_map = _cache_event_map },
+
+static struct riscv_vendor_pmu_events pmu_vendor_events_table[] = {
+};
+
+static const struct riscv_pmu_event *current_pmu_hw_event_map;
+static const struct riscv_pmu_event (*current_pmu_cache_event_map)[PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX];
+
+static void rvpmu_vendor_register_events(void)
+{
+ int cpu = raw_smp_processor_id();
+ unsigned long vendor_id = riscv_cached_mvendorid(cpu);
+ unsigned long impl_id = riscv_cached_mimpid(cpu);
+ unsigned long arch_id = riscv_cached_marchid(cpu);
+
+ for (int i = 0; i < ARRAY_SIZE(pmu_vendor_events_table); i++) {
+ if (pmu_vendor_events_table[i].vendorid == vendor_id &&
+ pmu_vendor_events_table[i].implid == impl_id &&
+ pmu_vendor_events_table[i].archid == arch_id) {
+ current_pmu_hw_event_map = pmu_vendor_events_table[i].hw_event_map;
+ current_pmu_cache_event_map = pmu_vendor_events_table[i].cache_event_map;
+ break;
+ }
+ }
+
+ if (!current_pmu_hw_event_map || !current_pmu_cache_event_map) {
+ pr_info("No default PMU events found\n");
+ }
+}
+
static void rvpmu_sbi_check_event(struct sbi_pmu_event_data *edata)
{
struct sbiret ret;
@@ -1643,8 +1693,10 @@ static int __init rvpmu_devinit(void)
*/
if (riscv_isa_extension_available(NULL, SSCCFG) &&
riscv_isa_extension_available(NULL, SMCDELEG) &&
- riscv_isa_extension_available(NULL, SSCSRIND))
+ riscv_isa_extension_available(NULL, SSCSRIND)) {
static_branch_enable(&riscv_pmu_cdeleg_available);
+ rvpmu_vendor_register_events();
+ }
if (!(riscv_pmu_sbi_available_boot() || riscv_pmu_cdeleg_available_boot()))
return 0;
diff --git a/include/linux/perf/riscv_pmu.h b/include/linux/perf/riscv_pmu.h
index f82a28040594..6c75106989b6 100644
--- a/include/linux/perf/riscv_pmu.h
+++ b/include/linux/perf/riscv_pmu.h
@@ -28,6 +28,19 @@
#define RISCV_PMU_CONFIG1_GUEST_EVENTS 0x1
+#define HW_OP_UNSUPPORTED 0xFFFF
+#define CACHE_OP_UNSUPPORTED 0xFFFF
+
+#define PERF_MAP_ALL_UNSUPPORTED \
+ [0 ... PERF_COUNT_HW_MAX - 1] = {HW_OP_UNSUPPORTED, 0x0}
+
+#define PERF_CACHE_MAP_ALL_UNSUPPORTED \
+[0 ... C(MAX) - 1] = { \
+ [0 ... C(OP_MAX) - 1] = { \
+ [0 ... C(RESULT_MAX) - 1] = {CACHE_OP_UNSUPPORTED, 0x0} \
+ }, \
+}
+
struct cpu_hw_events {
/* currently enabled events */
int n_events;
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 10/21] RISC-V: perf: Restructure the SBI PMU code
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
With Ssccfg/Smcdeleg, supervisor mode can program and access the hpmcounters
and events directly, without the SBI PMU extension. The SBI PMU extension is
still required for firmware counters. Restructure the existing SBI PMU code so
the hpmcounter/event helpers can be shared between the SBI and the counter
delegation paths that follow.
The driver, file, module and Kconfig names are intentionally kept unchanged to
avoid backport churn and userspace breakage (module listings, udev rules,
cmdline options).
No functional change intended.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
drivers/perf/Kconfig | 14 ++-
drivers/perf/riscv_pmu_sbi.c | 238 +++++++++++++++++++++++++------------------
2 files changed, 150 insertions(+), 102 deletions(-)
diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index ab90932fc2d0..3245bb2969e1 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -97,13 +97,17 @@ config RISCV_PMU_LEGACY
config RISCV_PMU_SBI
depends on RISCV_PMU && RISCV_SBI
- bool "RISC-V PMU based on SBI PMU extension"
+ bool "RISC-V PMU based on SBI PMU extension and/or counter delegation"
default y
help
- Say y if you want to use the CPU performance monitor
- using SBI PMU extension on RISC-V based systems. This option provides
- full perf feature support i.e. counter overflow, privilege mode
- filtering, counter configuration.
+ Say y if you want to use the CPU performance monitor on RISC-V based
+ systems. This single driver supports both hardware counter access
+ mechanisms: it uses the counter delegation (Smcdeleg/Ssccfg) ISA
+ extension to program and read the hpmcounters directly in supervisor
+ mode when available, and uses the SBI PMU extension for firmware
+ counters and when counter delegation is not present. This option
+ provides full perf feature support i.e. counter overflow, privilege
+ mode filtering, counter configuration.
config STARFIVE_STARLINK_PMU
depends on ARCH_STARFIVE || COMPILE_TEST
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 385af5e6e6d0..7f21c16003f0 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -88,6 +88,8 @@ static const struct attribute_group *riscv_pmu_attr_groups[] = {
static int sysctl_perf_user_access __read_mostly = SYSCTL_USER_ACCESS;
/*
+ * This structure is SBI specific but counter delegation also require counter
+ * width, csr mapping. Reuse it for now.
* RISC-V doesn't have heterogeneous harts yet. This need to be part of
* per_cpu in case of harts with different pmu counters
*/
@@ -100,7 +102,7 @@ static unsigned int riscv_pmu_irq;
/* Cache the available counters in a bitmask */
static unsigned long cmask;
-static int pmu_event_find_cache(u64 config);
+static int sbi_pmu_event_find_cache(u64 config);
struct sbi_pmu_event_data {
union {
union {
@@ -121,7 +123,7 @@ struct sbi_pmu_event_data {
};
};
-static struct sbi_pmu_event_data pmu_hw_event_map[] = {
+static struct sbi_pmu_event_data pmu_hw_event_sbi_map[] = {
[PERF_COUNT_HW_CPU_CYCLES] = {.hw_gen_event = {
SBI_PMU_HW_CPU_CYCLES,
SBI_PMU_EVENT_TYPE_HW, 0}},
@@ -155,7 +157,7 @@ static struct sbi_pmu_event_data pmu_hw_event_map[] = {
};
#define C(x) PERF_COUNT_HW_CACHE_##x
-static struct sbi_pmu_event_data pmu_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
+static struct sbi_pmu_event_data pmu_cache_event_sbi_map[PERF_COUNT_HW_CACHE_MAX]
[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
[C(L1D)] = {
@@ -302,7 +304,7 @@ static struct sbi_pmu_event_data pmu_cache_event_map[PERF_COUNT_HW_CACHE_MAX]
static int pmu_sbi_check_event_info(void)
{
- int num_events = ARRAY_SIZE(pmu_hw_event_map) + PERF_COUNT_HW_CACHE_MAX *
+ int num_events = ARRAY_SIZE(pmu_hw_event_sbi_map) + PERF_COUNT_HW_CACHE_MAX *
PERF_COUNT_HW_CACHE_OP_MAX * PERF_COUNT_HW_CACHE_RESULT_MAX;
struct riscv_pmu_event_info *event_info_shmem;
phys_addr_t base_addr;
@@ -313,14 +315,14 @@ static int pmu_sbi_check_event_info(void)
if (!event_info_shmem)
return -ENOMEM;
- for (i = 0; i < ARRAY_SIZE(pmu_hw_event_map); i++)
- event_info_shmem[count++].event_idx = pmu_hw_event_map[i].event_idx;
+ for (i = 0; i < ARRAY_SIZE(pmu_hw_event_sbi_map); i++)
+ event_info_shmem[count++].event_idx = pmu_hw_event_sbi_map[i].event_idx;
- for (i = 0; i < ARRAY_SIZE(pmu_cache_event_map); i++) {
- for (j = 0; j < ARRAY_SIZE(pmu_cache_event_map[i]); j++) {
- for (k = 0; k < ARRAY_SIZE(pmu_cache_event_map[i][j]); k++)
+ for (i = 0; i < ARRAY_SIZE(pmu_cache_event_sbi_map); i++) {
+ for (j = 0; j < ARRAY_SIZE(pmu_cache_event_sbi_map[i]); j++) {
+ for (k = 0; k < ARRAY_SIZE(pmu_cache_event_sbi_map[i][j]); k++)
event_info_shmem[count++].event_idx =
- pmu_cache_event_map[i][j][k].event_idx;
+ pmu_cache_event_sbi_map[i][j][k].event_idx;
}
}
@@ -336,19 +338,19 @@ static int pmu_sbi_check_event_info(void)
goto free_mem;
}
- for (i = 0; i < ARRAY_SIZE(pmu_hw_event_map); i++) {
+ for (i = 0; i < ARRAY_SIZE(pmu_hw_event_sbi_map); i++) {
if (!(event_info_shmem[i].output & RISCV_PMU_EVENT_INFO_OUTPUT_MASK))
- pmu_hw_event_map[i].event_idx = -ENOENT;
+ pmu_hw_event_sbi_map[i].event_idx = -ENOENT;
}
- count = ARRAY_SIZE(pmu_hw_event_map);
+ count = ARRAY_SIZE(pmu_hw_event_sbi_map);
- for (i = 0; i < ARRAY_SIZE(pmu_cache_event_map); i++) {
- for (j = 0; j < ARRAY_SIZE(pmu_cache_event_map[i]); j++) {
- for (k = 0; k < ARRAY_SIZE(pmu_cache_event_map[i][j]); k++) {
+ for (i = 0; i < ARRAY_SIZE(pmu_cache_event_sbi_map); i++) {
+ for (j = 0; j < ARRAY_SIZE(pmu_cache_event_sbi_map[i]); j++) {
+ for (k = 0; k < ARRAY_SIZE(pmu_cache_event_sbi_map[i][j]); k++) {
if (!(event_info_shmem[count].output &
RISCV_PMU_EVENT_INFO_OUTPUT_MASK))
- pmu_cache_event_map[i][j][k].event_idx = -ENOENT;
+ pmu_cache_event_sbi_map[i][j][k].event_idx = -ENOENT;
count++;
}
}
@@ -360,7 +362,7 @@ static int pmu_sbi_check_event_info(void)
return result;
}
-static void pmu_sbi_check_event(struct sbi_pmu_event_data *edata)
+static void rvpmu_sbi_check_event(struct sbi_pmu_event_data *edata)
{
struct sbiret ret;
@@ -375,7 +377,7 @@ static void pmu_sbi_check_event(struct sbi_pmu_event_data *edata)
}
}
-static void pmu_sbi_check_std_events(struct work_struct *work)
+static void rvpmu_sbi_check_std_events(struct work_struct *work)
{
int ret;
@@ -386,23 +388,23 @@ static void pmu_sbi_check_std_events(struct work_struct *work)
return;
}
- for (int i = 0; i < ARRAY_SIZE(pmu_hw_event_map); i++)
- pmu_sbi_check_event(&pmu_hw_event_map[i]);
+ for (int i = 0; i < ARRAY_SIZE(pmu_hw_event_sbi_map); i++)
+ rvpmu_sbi_check_event(&pmu_hw_event_sbi_map[i]);
- for (int i = 0; i < ARRAY_SIZE(pmu_cache_event_map); i++)
- for (int j = 0; j < ARRAY_SIZE(pmu_cache_event_map[i]); j++)
- for (int k = 0; k < ARRAY_SIZE(pmu_cache_event_map[i][j]); k++)
- pmu_sbi_check_event(&pmu_cache_event_map[i][j][k]);
+ for (int i = 0; i < ARRAY_SIZE(pmu_cache_event_sbi_map); i++)
+ for (int j = 0; j < ARRAY_SIZE(pmu_cache_event_sbi_map[i]); j++)
+ for (int k = 0; k < ARRAY_SIZE(pmu_cache_event_sbi_map[i][j]); k++)
+ rvpmu_sbi_check_event(&pmu_cache_event_sbi_map[i][j][k]);
}
-static DECLARE_WORK(check_std_events_work, pmu_sbi_check_std_events);
+static DECLARE_WORK(check_std_events_work, rvpmu_sbi_check_std_events);
-static int pmu_sbi_ctr_get_width(int idx)
+static int rvpmu_ctr_get_width(int idx)
{
return pmu_ctr_list[idx].width;
}
-static bool pmu_sbi_ctr_is_fw(int cidx)
+static bool rvpmu_ctr_is_fw(int cidx)
{
union sbi_pmu_ctr_info *info;
@@ -421,10 +423,10 @@ int riscv_pmu_get_event_info(u32 type, u64 config, u64 *econfig)
case PERF_TYPE_HARDWARE:
if (config >= PERF_COUNT_HW_MAX)
return -EINVAL;
- ret = pmu_hw_event_map[config].event_idx;
+ ret = pmu_hw_event_sbi_map[config].event_idx;
break;
case PERF_TYPE_HW_CACHE:
- ret = pmu_event_find_cache(config);
+ ret = sbi_pmu_event_find_cache(config);
break;
case PERF_TYPE_RAW:
/*
@@ -509,12 +511,12 @@ int riscv_pmu_get_hpm_info(u32 *hw_ctr_width, u32 *num_hw_ctr)
}
EXPORT_SYMBOL_GPL(riscv_pmu_get_hpm_info);
-static uint8_t pmu_sbi_csr_index(struct perf_event *event)
+static uint8_t rvpmu_csr_index(struct perf_event *event)
{
return pmu_ctr_list[event->hw.idx].csr - CSR_CYCLE;
}
-static unsigned long pmu_sbi_get_filter_flags(struct perf_event *event)
+static unsigned long rvpmu_sbi_get_filter_flags(struct perf_event *event)
{
unsigned long cflags = 0;
bool guest_events = false;
@@ -535,7 +537,7 @@ static unsigned long pmu_sbi_get_filter_flags(struct perf_event *event)
return cflags;
}
-static int pmu_sbi_ctr_get_idx(struct perf_event *event)
+static int rvpmu_sbi_ctr_get_idx(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
struct riscv_pmu *rvpmu = to_riscv_pmu(event->pmu);
@@ -545,7 +547,7 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event)
uint64_t cbase = 0, cmask = rvpmu->cmask;
unsigned long cflags = 0;
- cflags = pmu_sbi_get_filter_flags(event);
+ cflags = rvpmu_sbi_get_filter_flags(event);
/*
* In legacy mode, we have to force the fixed counters for those events
@@ -582,7 +584,7 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event)
return -ENOENT;
/* Additional sanity check for the counter id */
- if (pmu_sbi_ctr_is_fw(idx)) {
+ if (rvpmu_ctr_is_fw(idx)) {
if (!test_and_set_bit(idx, cpuc->used_fw_ctrs))
return idx;
} else {
@@ -593,7 +595,7 @@ static int pmu_sbi_ctr_get_idx(struct perf_event *event)
return -ENOENT;
}
-static void pmu_sbi_ctr_clear_idx(struct perf_event *event)
+static void rvpmu_ctr_clear_idx(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
@@ -601,13 +603,13 @@ static void pmu_sbi_ctr_clear_idx(struct perf_event *event)
struct cpu_hw_events *cpuc = this_cpu_ptr(rvpmu->hw_events);
int idx = hwc->idx;
- if (pmu_sbi_ctr_is_fw(idx))
+ if (rvpmu_ctr_is_fw(idx))
clear_bit(idx, cpuc->used_fw_ctrs);
else
clear_bit(idx, cpuc->used_hw_ctrs);
}
-static int pmu_event_find_cache(u64 config)
+static int sbi_pmu_event_find_cache(u64 config)
{
unsigned int cache_type, cache_op, cache_result, ret;
@@ -623,7 +625,7 @@ static int pmu_event_find_cache(u64 config)
if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
return -EINVAL;
- ret = pmu_cache_event_map[cache_type][cache_op][cache_result].event_idx;
+ ret = pmu_cache_event_sbi_map[cache_type][cache_op][cache_result].event_idx;
return ret;
}
@@ -639,7 +641,7 @@ static bool pmu_sbi_is_fw_event(struct perf_event *event)
return false;
}
-static int pmu_sbi_event_map(struct perf_event *event, u64 *econfig)
+static int rvpmu_sbi_event_map(struct perf_event *event, u64 *econfig)
{
u32 type = event->attr.type;
u64 config = event->attr.config;
@@ -736,7 +738,7 @@ static int pmu_sbi_snapshot_setup(struct riscv_pmu *pmu, int cpu)
return 0;
}
-static u64 pmu_sbi_ctr_read(struct perf_event *event)
+static u64 rvpmu_sbi_ctr_read(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
int idx = hwc->idx;
@@ -778,25 +780,25 @@ static u64 pmu_sbi_ctr_read(struct perf_event *event)
return val;
}
-static void pmu_sbi_set_scounteren(void *arg)
+static void rvpmu_set_scounteren(void *arg)
{
struct perf_event *event = (struct perf_event *)arg;
if (event->hw.idx != -1)
csr_write(CSR_SCOUNTEREN,
- csr_read(CSR_SCOUNTEREN) | BIT(pmu_sbi_csr_index(event)));
+ csr_read(CSR_SCOUNTEREN) | BIT(rvpmu_csr_index(event)));
}
-static void pmu_sbi_reset_scounteren(void *arg)
+static void rvpmu_reset_scounteren(void *arg)
{
struct perf_event *event = (struct perf_event *)arg;
if (event->hw.idx != -1)
csr_write(CSR_SCOUNTEREN,
- csr_read(CSR_SCOUNTEREN) & ~BIT(pmu_sbi_csr_index(event)));
+ csr_read(CSR_SCOUNTEREN) & ~BIT(rvpmu_csr_index(event)));
}
-static void pmu_sbi_ctr_start(struct perf_event *event, u64 ival)
+static void rvpmu_sbi_ctr_start(struct perf_event *event, u64 ival)
{
struct sbiret ret;
struct hw_perf_event *hwc = &event->hw;
@@ -816,10 +818,10 @@ static void pmu_sbi_ctr_start(struct perf_event *event, u64 ival)
if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
(hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
- pmu_sbi_set_scounteren((void *)event);
+ rvpmu_set_scounteren((void *)event);
}
-static void pmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
+static void rvpmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
{
struct sbiret ret;
struct hw_perf_event *hwc = &event->hw;
@@ -829,7 +831,7 @@ static void pmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
if ((hwc->flags & PERF_EVENT_FLAG_USER_ACCESS) &&
(hwc->flags & PERF_EVENT_FLAG_USER_READ_CNT))
- pmu_sbi_reset_scounteren((void *)event);
+ rvpmu_reset_scounteren((void *)event);
if (sbi_pmu_snapshot_available())
flag |= SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT;
@@ -855,7 +857,7 @@ static void pmu_sbi_ctr_stop(struct perf_event *event, unsigned long flag)
}
}
-static int pmu_sbi_find_num_ctrs(void)
+static int rvpmu_sbi_find_num_ctrs(void)
{
struct sbiret ret;
@@ -866,7 +868,7 @@ static int pmu_sbi_find_num_ctrs(void)
return sbi_err_map_linux_errno(ret.error);
}
-static int pmu_sbi_get_ctrinfo(int nctr, unsigned long *mask)
+static int rvpmu_sbi_get_ctrinfo(int nctr, unsigned long *mask)
{
struct sbiret ret;
int i, num_hw_ctr = 0, num_fw_ctr = 0;
@@ -897,7 +899,7 @@ static int pmu_sbi_get_ctrinfo(int nctr, unsigned long *mask)
return 0;
}
-static inline void pmu_sbi_stop_all(struct riscv_pmu *pmu)
+static inline void rvpmu_sbi_stop_all(struct riscv_pmu *pmu)
{
/*
* No need to check the error because we are disabling all the counters
@@ -907,7 +909,7 @@ static inline void pmu_sbi_stop_all(struct riscv_pmu *pmu)
0, pmu->cmask, SBI_PMU_STOP_FLAG_RESET, 0, 0, 0);
}
-static inline void pmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
+static inline void rvpmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
{
struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
struct riscv_pmu_snapshot_data *sdata = cpu_hw_evt->snapshot_addr;
@@ -951,8 +953,8 @@ static inline void pmu_sbi_stop_hw_ctrs(struct riscv_pmu *pmu)
* while the overflowed counters need to be started with updated initialization
* value.
*/
-static inline void pmu_sbi_start_ovf_ctrs_sbi(struct cpu_hw_events *cpu_hw_evt,
- u64 ctr_ovf_mask)
+static inline void rvpmu_sbi_start_ovf_ctrs_sbi(struct cpu_hw_events *cpu_hw_evt,
+ u64 ctr_ovf_mask)
{
int idx = 0, i;
struct perf_event *event;
@@ -992,8 +994,8 @@ static inline void pmu_sbi_start_ovf_ctrs_sbi(struct cpu_hw_events *cpu_hw_evt,
}
}
-static inline void pmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_evt,
- u64 ctr_ovf_mask)
+static inline void rvpmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_evt,
+ u64 ctr_ovf_mask)
{
int i, idx = 0;
struct perf_event *event;
@@ -1027,18 +1029,18 @@ static inline void pmu_sbi_start_ovf_ctrs_snapshot(struct cpu_hw_events *cpu_hw_
}
}
-static void pmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
- u64 ctr_ovf_mask)
+static void rvpmu_sbi_start_overflow_mask(struct riscv_pmu *pmu,
+ u64 ctr_ovf_mask)
{
struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
if (sbi_pmu_snapshot_available())
- pmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
+ rvpmu_sbi_start_ovf_ctrs_snapshot(cpu_hw_evt, ctr_ovf_mask);
else
- pmu_sbi_start_ovf_ctrs_sbi(cpu_hw_evt, ctr_ovf_mask);
+ rvpmu_sbi_start_ovf_ctrs_sbi(cpu_hw_evt, ctr_ovf_mask);
}
-static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
+static irqreturn_t rvpmu_ovf_handler(int irq, void *dev)
{
struct perf_sample_data data;
struct pt_regs *regs;
@@ -1070,7 +1072,7 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
}
pmu = to_riscv_pmu(event->pmu);
- pmu_sbi_stop_hw_ctrs(pmu);
+ rvpmu_sbi_stop_hw_ctrs(pmu);
/* Overflow status register should only be read after counter are stopped */
if (sbi_pmu_snapshot_available())
@@ -1139,13 +1141,55 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev)
hw_evt->state = 0;
}
- pmu_sbi_start_overflow_mask(pmu, overflowed_ctrs);
+ rvpmu_sbi_start_overflow_mask(pmu, overflowed_ctrs);
perf_sample_event_took(sched_clock() - start_clock);
return IRQ_HANDLED;
}
-static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node)
+static void rvpmu_ctr_start(struct perf_event *event, u64 ival)
+{
+ rvpmu_sbi_ctr_start(event, ival);
+ /* TODO: Counter delegation implementation */
+}
+
+static void rvpmu_ctr_stop(struct perf_event *event, unsigned long flag)
+{
+ rvpmu_sbi_ctr_stop(event, flag);
+ /* TODO: Counter delegation implementation */
+}
+
+static int rvpmu_find_num_ctrs(void)
+{
+ return rvpmu_sbi_find_num_ctrs();
+ /* TODO: Counter delegation implementation */
+}
+
+static int rvpmu_get_ctrinfo(int nctr, unsigned long *mask)
+{
+ return rvpmu_sbi_get_ctrinfo(nctr, mask);
+ /* TODO: Counter delegation implementation */
+}
+
+static int rvpmu_event_map(struct perf_event *event, u64 *econfig)
+{
+ return rvpmu_sbi_event_map(event, econfig);
+ /* TODO: Counter delegation implementation */
+}
+
+static int rvpmu_ctr_get_idx(struct perf_event *event)
+{
+ return rvpmu_sbi_ctr_get_idx(event);
+ /* TODO: Counter delegation implementation */
+}
+
+static u64 rvpmu_ctr_read(struct perf_event *event)
+{
+ return rvpmu_sbi_ctr_read(event);
+ /* TODO: Counter delegation implementation */
+}
+
+static int rvpmu_starting_cpu(unsigned int cpu, struct hlist_node *node)
{
struct riscv_pmu *pmu = hlist_entry_safe(node, struct riscv_pmu, node);
struct cpu_hw_events *cpu_hw_evt = this_cpu_ptr(pmu->hw_events);
@@ -1160,7 +1204,7 @@ static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node)
csr_write(CSR_SCOUNTEREN, 0x2);
/* Stop all the counters so that they can be enabled from perf */
- pmu_sbi_stop_all(pmu);
+ rvpmu_sbi_stop_all(pmu);
if (riscv_pmu_use_irq) {
cpu_hw_evt->irq = riscv_pmu_irq;
@@ -1174,7 +1218,7 @@ static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node)
return 0;
}
-static int pmu_sbi_dying_cpu(unsigned int cpu, struct hlist_node *node)
+static int rvpmu_dying_cpu(unsigned int cpu, struct hlist_node *node)
{
if (riscv_pmu_use_irq) {
disable_percpu_irq(riscv_pmu_irq);
@@ -1189,7 +1233,7 @@ static int pmu_sbi_dying_cpu(unsigned int cpu, struct hlist_node *node)
return 0;
}
-static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pdev)
+static int rvpmu_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pdev)
{
int ret;
struct cpu_hw_events __percpu *hw_events = pmu->hw_events;
@@ -1229,7 +1273,7 @@ static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pde
return -ENODEV;
}
- ret = request_percpu_irq(riscv_pmu_irq, pmu_sbi_ovf_handler, "riscv-pmu", hw_events);
+ ret = request_percpu_irq(riscv_pmu_irq, rvpmu_ovf_handler, "riscv-pmu", hw_events);
if (ret) {
pr_err("registering percpu irq failed [%d]\n", ret);
return ret;
@@ -1305,7 +1349,7 @@ static void riscv_pmu_destroy(struct riscv_pmu *pmu)
cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
}
-static void pmu_sbi_event_init(struct perf_event *event)
+static void rvpmu_event_init(struct perf_event *event)
{
/*
* The permissions are set at event_init so that we do not depend
@@ -1319,7 +1363,7 @@ static void pmu_sbi_event_init(struct perf_event *event)
event->hw.flags |= PERF_EVENT_FLAG_LEGACY;
}
-static void pmu_sbi_event_mapped(struct perf_event *event, struct mm_struct *mm)
+static void rvpmu_event_mapped(struct perf_event *event, struct mm_struct *mm)
{
if (event->hw.flags & PERF_EVENT_FLAG_NO_USER_ACCESS)
return;
@@ -1347,14 +1391,14 @@ static void pmu_sbi_event_mapped(struct perf_event *event, struct mm_struct *mm)
* that it is possible to do so to avoid any race.
* And we must notify all cpus here because threads that currently run
* on other cpus will try to directly access the counter too without
- * calling pmu_sbi_ctr_start.
+ * calling rvpmu_sbi_ctr_start.
*/
if (event->hw.flags & PERF_EVENT_FLAG_USER_ACCESS)
on_each_cpu_mask(mm_cpumask(mm),
- pmu_sbi_set_scounteren, (void *)event, 1);
+ rvpmu_set_scounteren, (void *)event, 1);
}
-static void pmu_sbi_event_unmapped(struct perf_event *event, struct mm_struct *mm)
+static void rvpmu_event_unmapped(struct perf_event *event, struct mm_struct *mm)
{
if (event->hw.flags & PERF_EVENT_FLAG_NO_USER_ACCESS)
return;
@@ -1376,7 +1420,7 @@ static void pmu_sbi_event_unmapped(struct perf_event *event, struct mm_struct *m
if (event->hw.flags & PERF_EVENT_FLAG_USER_ACCESS)
on_each_cpu_mask(mm_cpumask(mm),
- pmu_sbi_reset_scounteren, (void *)event, 1);
+ rvpmu_reset_scounteren, (void *)event, 1);
}
static void riscv_pmu_update_counter_access(void *info)
@@ -1419,7 +1463,7 @@ static const struct ctl_table sbi_pmu_sysctl_table[] = {
},
};
-static int pmu_sbi_device_probe(struct platform_device *pdev)
+static int rvpmu_device_probe(struct platform_device *pdev)
{
struct riscv_pmu *pmu = NULL;
int ret = -ENODEV;
@@ -1430,7 +1474,7 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
if (!pmu)
return -ENOMEM;
- num_counters = pmu_sbi_find_num_ctrs();
+ num_counters = rvpmu_find_num_ctrs();
if (num_counters < 0) {
pr_err("SBI PMU extension doesn't provide any counters\n");
goto out_free;
@@ -1443,10 +1487,10 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
}
/* cache all the information about counters now */
- if (pmu_sbi_get_ctrinfo(num_counters, &cmask))
+ if (rvpmu_get_ctrinfo(num_counters, &cmask))
goto out_free;
- ret = pmu_sbi_setup_irqs(pmu, pdev);
+ ret = rvpmu_setup_irqs(pmu, pdev);
if (ret < 0) {
pr_info("Perf sampling/filtering is not supported as sscof extension is not available\n");
pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
@@ -1456,17 +1500,17 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
pmu->pmu.attr_groups = riscv_pmu_attr_groups;
pmu->pmu.parent = &pdev->dev;
pmu->cmask = cmask;
- pmu->ctr_start = pmu_sbi_ctr_start;
- pmu->ctr_stop = pmu_sbi_ctr_stop;
- pmu->event_map = pmu_sbi_event_map;
- pmu->ctr_get_idx = pmu_sbi_ctr_get_idx;
- pmu->ctr_get_width = pmu_sbi_ctr_get_width;
- pmu->ctr_clear_idx = pmu_sbi_ctr_clear_idx;
- pmu->ctr_read = pmu_sbi_ctr_read;
- pmu->event_init = pmu_sbi_event_init;
- pmu->event_mapped = pmu_sbi_event_mapped;
- pmu->event_unmapped = pmu_sbi_event_unmapped;
- pmu->csr_index = pmu_sbi_csr_index;
+ pmu->ctr_start = rvpmu_ctr_start;
+ pmu->ctr_stop = rvpmu_ctr_stop;
+ pmu->event_map = rvpmu_event_map;
+ pmu->ctr_get_idx = rvpmu_ctr_get_idx;
+ pmu->ctr_get_width = rvpmu_ctr_get_width;
+ pmu->ctr_clear_idx = rvpmu_ctr_clear_idx;
+ pmu->ctr_read = rvpmu_ctr_read;
+ pmu->event_init = rvpmu_event_init;
+ pmu->event_mapped = rvpmu_event_mapped;
+ pmu->event_unmapped = rvpmu_event_unmapped;
+ pmu->csr_index = rvpmu_csr_index;
ret = riscv_pm_pmu_register(pmu);
if (ret)
@@ -1522,14 +1566,14 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
return ret;
}
-static struct platform_driver pmu_sbi_driver = {
- .probe = pmu_sbi_device_probe,
+static struct platform_driver rvpmu_driver = {
+ .probe = rvpmu_device_probe,
.driver = {
.name = RISCV_PMU_SBI_PDEV_NAME,
},
};
-static int __init pmu_sbi_devinit(void)
+static int __init rvpmu_devinit(void)
{
int ret;
struct platform_device *pdev;
@@ -1547,20 +1591,20 @@ static int __init pmu_sbi_devinit(void)
ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_RISCV_STARTING,
"perf/riscv/pmu:starting",
- pmu_sbi_starting_cpu, pmu_sbi_dying_cpu);
+ rvpmu_starting_cpu, rvpmu_dying_cpu);
if (ret) {
pr_err("CPU hotplug notifier could not be registered: %d\n",
ret);
return ret;
}
- ret = platform_driver_register(&pmu_sbi_driver);
+ ret = platform_driver_register(&rvpmu_driver);
if (ret)
return ret;
pdev = platform_device_register_simple(RISCV_PMU_SBI_PDEV_NAME, -1, NULL, 0);
if (IS_ERR(pdev)) {
- platform_driver_unregister(&pmu_sbi_driver);
+ platform_driver_unregister(&rvpmu_driver);
return PTR_ERR(pdev);
}
@@ -1569,4 +1613,4 @@ static int __init pmu_sbi_devinit(void)
return ret;
}
-device_initcall(pmu_sbi_devinit)
+device_initcall(rvpmu_devinit)
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 07/21] RISC-V: Add Sscfg extension CSR definition
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Kaiwen Xue <kaiwenx@rivosinc.com>
This adds the scountinhibit CSR definition and S-mode accessible hpmevent
bits defined by smcdeleg/ssccfg. scountinhibit allows S-mode to start/stop
counters directly from S-mode without invoking SBI calls to M-mode. It is
also used to figure out the counters delegated to S-mode by the M-mode as
well.
Signed-off-by: Kaiwen Xue <kaiwenx@rivosinc.com>
Reviewed-by: Clément Léger <cleger@rivosinc.com>
---
arch/riscv/include/asm/csr.h | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index b4551a6cf7cb..26cb78dee2fd 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -241,6 +241,31 @@
#define SMSTATEEN0_HSENVCFG (_ULL(1) << SMSTATEEN0_HSENVCFG_SHIFT)
#define SMSTATEEN0_SSTATEEN0_SHIFT 63
#define SMSTATEEN0_SSTATEEN0 (_ULL(1) << SMSTATEEN0_SSTATEEN0_SHIFT)
+/* HPMEVENT bits. These are accessible in S-mode via Smcdeleg/Ssccfg */
+#ifdef CONFIG_64BIT
+#define HPMEVENT_OF (BIT_ULL(63))
+#define HPMEVENT_MINH (BIT_ULL(62))
+#define HPMEVENT_SINH (BIT_ULL(61))
+#define HPMEVENT_UINH (BIT_ULL(60))
+#define HPMEVENT_VSINH (BIT_ULL(59))
+#define HPMEVENT_VUINH (BIT_ULL(58))
+#else
+#define HPMEVENTH_OF (BIT_ULL(31))
+#define HPMEVENTH_MINH (BIT_ULL(30))
+#define HPMEVENTH_SINH (BIT_ULL(29))
+#define HPMEVENTH_UINH (BIT_ULL(28))
+#define HPMEVENTH_VSINH (BIT_ULL(27))
+#define HPMEVENTH_VUINH (BIT_ULL(26))
+
+#define HPMEVENT_OF (HPMEVENTH_OF << 32)
+#define HPMEVENT_MINH (HPMEVENTH_MINH << 32)
+#define HPMEVENT_SINH (HPMEVENTH_SINH << 32)
+#define HPMEVENT_UINH (HPMEVENTH_UINH << 32)
+#define HPMEVENT_VSINH (HPMEVENTH_VSINH << 32)
+#define HPMEVENT_VUINH (HPMEVENTH_VUINH << 32)
+#endif
+
+#define SISELECT_SSCCFG_BASE 0x40
/* mseccfg bits */
#define MSECCFG_PMM ENVCFG_PMM
@@ -322,6 +347,7 @@
#define CSR_SCOUNTEREN 0x106
#define CSR_SENVCFG 0x10a
#define CSR_SSTATEEN0 0x10c
+#define CSR_SCOUNTINHIBIT 0x120
#define CSR_SSCRATCH 0x140
#define CSR_SEPC 0x141
#define CSR_SCAUSE 0x142
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 06/21] dt-bindings: riscv: add Smcntrpmf ISA extension description
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
Add the description for Smcntrpmf ISA extension
Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
Documentation/devicetree/bindings/riscv/extensions.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
index 4be557dc215d..ece3edccee42 100644
--- a/Documentation/devicetree/bindings/riscv/extensions.yaml
+++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
@@ -189,6 +189,12 @@ properties:
mechanism in M-mode as ratified in the 20240326 version of the
privileged ISA specification.
+ - const: smcntrpmf
+ description: |
+ The standard Smcntrpmf supervisor-level extension for the machine mode
+ to enable privilege mode filtering for cycle and instret counters as
+ ratified in the 20240326 version of the privileged ISA specification.
+
- const: smmpm
description: |
The standard Smmpm extension for M-mode pointer masking as
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 05/21] RISC-V: Add Smcntrpmf extension parsing
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
Smcntrpmf extension allows M-mode to enable privilege mode filtering
for cycle/instret counters. However, the cyclecfg/instretcfg CSRs are
only available only in Ssccfg only Smcntrpmf is present.
That's why, kernel needs to detect presence of Smcntrpmf extension and
enable privilege mode filtering for cycle/instret counters.
Reviewed-by: Clément Léger <cleger@rivosinc.com>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
arch/riscv/include/asm/hwcap.h | 1 +
arch/riscv/kernel/cpufeature.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index d4a7b90e2d78..51ad55b9677a 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -114,6 +114,7 @@
#define RISCV_ISA_EXT_ZICFISS 105
#define RISCV_ISA_EXT_SSCSRIND 106
#define RISCV_ISA_EXT_SMCSRIND 107
+#define RISCV_ISA_EXT_SMCNTRPMF 108
#define RISCV_ISA_EXT_XLINUXENVCFG 127
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 3fa0a563fb21..1452521d740a 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -576,6 +576,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
__RISCV_ISA_EXT_BUNDLE_VALIDATE(zvksg, riscv_zvksg_bundled_exts, riscv_ext_vector_crypto_validate),
__RISCV_ISA_EXT_DATA_VALIDATE(zvkt, RISCV_ISA_EXT_ZVKT, riscv_ext_vector_crypto_validate),
__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
+ __RISCV_ISA_EXT_DATA(smcntrpmf, RISCV_ISA_EXT_SMCNTRPMF),
__RISCV_ISA_EXT_DATA(smcsrind, RISCV_ISA_EXT_SMCSRIND),
__RISCV_ISA_EXT_DATA(smmpm, RISCV_ISA_EXT_SMMPM),
__RISCV_ISA_EXT_SUPERSET(smnpm, RISCV_ISA_EXT_SMNPM, riscv_xlinuxenvcfg_exts),
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 02/21] RISC-V: Add Sxcsrind ISA extension definition and parsing
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
The S[m|s]csrind extension extends the indirect CSR access mechanism
defined in Smaia/Ssaia extensions.
This patch just enables the definition and parsing.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
arch/riscv/include/asm/hwcap.h | 4 ++++
arch/riscv/kernel/cpufeature.c | 2 ++
2 files changed, 6 insertions(+)
diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index 7ef8e5f55c8d..d4a7b90e2d78 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -112,6 +112,8 @@
#define RISCV_ISA_EXT_ZCLSD 103
#define RISCV_ISA_EXT_ZICFILP 104
#define RISCV_ISA_EXT_ZICFISS 105
+#define RISCV_ISA_EXT_SSCSRIND 106
+#define RISCV_ISA_EXT_SMCSRIND 107
#define RISCV_ISA_EXT_XLINUXENVCFG 127
@@ -121,9 +123,11 @@
#ifdef CONFIG_RISCV_M_MODE
#define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SMAIA
#define RISCV_ISA_EXT_SUPM RISCV_ISA_EXT_SMNPM
+#define RISCV_ISA_EXT_SxCSRIND RISCV_ISA_EXT_SMCSRIND
#else
#define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SSAIA
#define RISCV_ISA_EXT_SUPM RISCV_ISA_EXT_SSNPM
+#define RISCV_ISA_EXT_SxCSRIND RISCV_ISA_EXT_SSCSRIND
#endif
#endif /* _ASM_RISCV_HWCAP_H */
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index f46aa5602d74..3fa0a563fb21 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -576,11 +576,13 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
__RISCV_ISA_EXT_BUNDLE_VALIDATE(zvksg, riscv_zvksg_bundled_exts, riscv_ext_vector_crypto_validate),
__RISCV_ISA_EXT_DATA_VALIDATE(zvkt, RISCV_ISA_EXT_ZVKT, riscv_ext_vector_crypto_validate),
__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
+ __RISCV_ISA_EXT_DATA(smcsrind, RISCV_ISA_EXT_SMCSRIND),
__RISCV_ISA_EXT_DATA(smmpm, RISCV_ISA_EXT_SMMPM),
__RISCV_ISA_EXT_SUPERSET(smnpm, RISCV_ISA_EXT_SMNPM, riscv_xlinuxenvcfg_exts),
__RISCV_ISA_EXT_DATA(smstateen, RISCV_ISA_EXT_SMSTATEEN),
__RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),
__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
+ __RISCV_ISA_EXT_DATA(sscsrind, RISCV_ISA_EXT_SSCSRIND),
__RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts),
__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
__RISCV_ISA_EXT_DATA(svade, RISCV_ISA_EXT_SVADE),
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v6 04/21] RISC-V: Define indirect CSR access helpers
From: Atish Patra @ 2026-06-09 6:01 UTC (permalink / raw)
To: James Clark, Rob Herring, Atish Patra, Arnaldo Carvalho de Melo,
Jiri Olsa, Will Deacon, Mark Rutland, Anup Patel, Namhyung Kim,
Paul Walmsley, Krzysztof Kozlowski, Ian Rogers
Cc: linux-riscv, linux-kernel, linux-perf-users, Conor Dooley,
devicetree, linux-arm-kernel
In-Reply-To: <20260608-counter_delegation-v6-0-285b72ed65a9@meta.com>
From: Atish Patra <atishp@rivosinc.com>
The indriect CSR requires multiple instructions to read/write CSR.
Add a few helper functions for ease of usage.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
arch/riscv/include/asm/csr_ind.h | 44 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/arch/riscv/include/asm/csr_ind.h b/arch/riscv/include/asm/csr_ind.h
new file mode 100644
index 000000000000..6fd7d44dc640
--- /dev/null
+++ b/arch/riscv/include/asm/csr_ind.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Rivos Inc.
+ */
+
+#ifndef _ASM_RISCV_CSR_IND_H
+#define _ASM_RISCV_CSR_IND_H
+
+#include <linux/irqflags.h>
+
+#include <asm/csr.h>
+
+#define csr_ind_read(iregcsr, iselbase, iseloff) ({ \
+ unsigned long __value = 0; \
+ unsigned long __flags; \
+ local_irq_save(__flags); \
+ csr_write(CSR_ISELECT, (iselbase) + (iseloff)); \
+ __value = csr_read(iregcsr); \
+ local_irq_restore(__flags); \
+ __value; \
+})
+
+#define csr_ind_write(iregcsr, iselbase, iseloff, value) ({ \
+ unsigned long __flags; \
+ local_irq_save(__flags); \
+ csr_write(CSR_ISELECT, (iselbase) + (iseloff)); \
+ csr_write(iregcsr, (value)); \
+ local_irq_restore(__flags); \
+})
+
+#define csr_ind_warl(iregcsr, iselbase, iseloff, warl_val) ({ \
+ unsigned long __old_val = 0, __value = 0; \
+ unsigned long __flags; \
+ local_irq_save(__flags); \
+ csr_write(CSR_ISELECT, (iselbase) + (iseloff)); \
+ __old_val = csr_read(iregcsr); \
+ csr_write(iregcsr, (warl_val)); \
+ __value = csr_read(iregcsr); \
+ csr_write(iregcsr, __old_val); \
+ local_irq_restore(__flags); \
+ __value; \
+})
+
+#endif
--
2.53.0-Meta
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox