From: Jonathan Cameron <Jonathan.Cameron@huawei.com>
To: "Clément Le Goffic" <clement.legoffic@foss.st.com>
Cc: Will Deacon <will@kernel.org>,
Mark Rutland <mark.rutland@arm.com>,
"Rob Herring" <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
Maxime Coquelin <mcoquelin.stm32@gmail.com>,
Alexandre Torgue <alexandre.torgue@foss.st.com>,
Philipp Zabel <p.zabel@pengutronix.de>,
Jonathan Corbet <corbet@lwn.net>,
"Gatien Chevallier" <gatien.chevallier@foss.st.com>,
Michael Turquette <mturquette@baylibre.com>,
Stephen Boyd <sboyd@kernel.org>,
"Gabriel Fernandez" <gabriel.fernandez@foss.st.com>,
Krzysztof Kozlowski <krzk@kernel.org>,
Le Goffic <legoffic.clement@gmail.com>,
<linux-arm-kernel@lists.infradead.org>,
<linux-perf-users@vger.kernel.org>, <devicetree@vger.kernel.org>,
<linux-stm32@st-md-mailman.stormreply.com>,
<linux-kernel@vger.kernel.org>, <linux-doc@vger.kernel.org>,
<linux-clk@vger.kernel.org>
Subject: Re: [PATCH v2 09/16] perf: stm32: introduce DDRPERFM driver
Date: Fri, 11 Jul 2025 17:04:15 +0100 [thread overview]
Message-ID: <20250711170415.00001901@huawei.com> (raw)
In-Reply-To: <20250711-ddrperfm-upstream-v2-9-cdece720348f@foss.st.com>
On Fri, 11 Jul 2025 16:49:01 +0200
Clément Le Goffic <clement.legoffic@foss.st.com> wrote:
> Introduce the driver for the DDR Performance Monitor available on
> STM32MPU SoC.
>
> On STM32MP2 platforms, the DDRPERFM allows to monitor up to 8 DDR events
> that come from the DDR Controller such as read or write events.
>
> On STM32MP1 platforms, the DDRPERFM cannot monitor any event on any
> counter, there is a notion of set of events.
> Events from different sets cannot be monitored at the same time.
> The first chosen event selects the set.
> The set is coded in the first two bytes of the config value which is on 4
> bytes.
>
> On STM32MP25x series, the DDRPERFM clock is shared with the DDR controller
> and may be secured by bootloaders.
> Access controllers allow to check access to a resource. Use the access
> controller defined in the devicetree to know about the access to the
> DDRPERFM clock.
>
> Signed-off-by: Clément Le Goffic <clement.legoffic@foss.st.com>
Hi Clément,
A quick drive by review as it's Friday afternoon and I was curious..
Mostly superficial stuff. I didn't look closely at the perf logic.
Jonathan
> diff --git a/drivers/perf/stm32_ddr_pmu.c b/drivers/perf/stm32_ddr_pmu.c
> new file mode 100644
> index 000000000000..1be5bbe12978
> --- /dev/null
> +++ b/drivers/perf/stm32_ddr_pmu.c
> @@ -0,0 +1,910 @@
> +#define EVENT_NUMBER(group, index) (((group) << 8) | (index))
> +#define GROUP_VALUE(event_number) ((event_number) >> 8)
> +#define EVENT_INDEX(event_number) ((event_number) & 0xFF)
Prefix these macro names with something driver specific. They are
very likely to clash with something in a header in future otherwise.
> +
> +enum stm32_ddr_pmu_memory_type {
> + STM32_DDR_PMU_LPDDR4,
> + STM32_DDR_PMU_LPDDR3,
> + STM32_DDR_PMU_DDR4,
> + STM32_DDR_PMU_DDR3
This should have a trailing comma as might well be more
added in future if this IP gets used in more devices.
> +};
>
> +
> +static const struct attribute_group *stm32_ddr_pmu_attr_groups_mp2[] = {
> + &stm32_ddr_pmu_events_attrs_group_mp2,
> + &stm32_ddr_pmu_format_attr_group,
> + NULL,
No comma needed on terminating entries.
> +};
> +
> +static int stm32_ddr_pmu_device_probe(struct platform_device *pdev)
> +{
> + struct stm32_firewall firewall;
> + struct stm32_ddr_pmu *pmu;
> + struct reset_control *rst;
> + struct resource *res;
> + int ret;
> +
> + pmu = devm_kzalloc(&pdev->dev, struct_size(pmu, counters, MP2_CNT_NB), GFP_KERNEL);
> + if (!pmu)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, pmu);
> + pmu->dev = &pdev->dev;
> +
> + pmu->cfg = device_get_match_data(&pdev->dev);
> +
> + pmu->membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
> + if (IS_ERR(pmu->membase))
> + return PTR_ERR(pmu->membase);
> +
> + if (of_property_present(pmu->dev->of_node, "access-controllers")) {
> + ret = stm32_firewall_get_firewall(pmu->dev->of_node, &firewall, 1);
> + if (ret)
> + return dev_err_probe(pmu->dev, ret, "Failed to get firewall\n");
> + ret = stm32_firewall_grant_access_by_id(&firewall, firewall.firewall_id);
> + if (ret)
> + return dev_err_probe(pmu->dev, ret, "Failed to grant access\n");
> + }
> +
> + pmu->clk = devm_clk_get_optional_prepared(pmu->dev, NULL);
Given there are quite a few uses of pmu->dev, maybe worth a local
struct device *dev = &pdev->dev; at the top and use dev to replace all these.
> + if (IS_ERR(pmu->clk))
> + return dev_err_probe(pmu->dev, PTR_ERR(pmu->clk), "Failed to get prepare clock\n");
> +
> + clk_enable(pmu->clk);
> +
> + rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
You mix and match between pdev->dev, and pmu->dev. Good to pick one or use local
variable as suggested above.
> + if (IS_ERR(rst)) {
> + clk_disable_unprepare(pmu->clk);
Given use of _prepared() get above. This doesn't look right - the unprepare
should be handled by devm unwinding. clk_disable()
> + return dev_err_probe(&pdev->dev, PTR_ERR(rst), "Failed to get reset\n");
> + }
> +
> + reset_control_assert(rst);
> + reset_control_deassert(rst);
> +
> + pmu->poll_period = ms_to_ktime(POLL_MS);
> + hrtimer_setup(&pmu->hrtimer, stm32_ddr_pmu_poll, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
> +
> + for (int i = 0; i < MP2_CNT_NB; i++)
> + INIT_LIST_HEAD(&pmu->counters[i]);
> +
> + pmu->selected_set = -1;
> +
> + pmu->pmu = (struct pmu) {
> + .task_ctx_nr = perf_invalid_context,
> + .start = stm32_ddr_pmu_event_start,
> + .stop = stm32_ddr_pmu_event_stop,
> + .add = stm32_ddr_pmu_event_add,
> + .del = stm32_ddr_pmu_event_del,
> + .read = stm32_ddr_pmu_event_read,
> + .event_init = stm32_ddr_pmu_event_init,
> + .attr_groups = pmu->cfg->attribute,
> + .module = THIS_MODULE,
> + };
> +
> + ret = perf_pmu_register(&pmu->pmu, DRIVER_NAME, -1);
Calling this exposes user interfaces etc. Does it really make sense to
do that and then write another register? I'd normally expect this
last in probe.
> + if (ret) {
> + clk_disable_unprepare(pmu->clk);
As above.
> + return dev_err_probe(&pdev->dev, ret,
> + "Couldn't register DDRPERFM driver as a PMU\n");
> + }
> +
> + if (pmu->cfg->regs->dram_inf.reg) {
> + ret = stm32_ddr_pmu_get_memory_type(pmu);
> + if (ret) {
> + perf_pmu_unregister(&pmu->pmu);
> + clk_disable_unprepare(pmu->clk);
> + return dev_err_probe(&pdev->dev, ret, "Failed to get memory type\n");
> + }
> +
> + writel_relaxed(pmu->dram_type, pmu->membase + pmu->cfg->regs->dram_inf.reg);
> + }
> +
> + clk_disable(pmu->clk);
> +
> + return 0;
> +}
> +static const struct stm32_ddr_pmu_regspec stm32_ddr_pmu_regspec_mp2 = {
> + .stop = { DDRPERFM_CTRL, CTRL_STOP },
> + .start = { DDRPERFM_CTRL, CTRL_START },
> + .status = { DDRPERFM_MP2_STATUS, MP2_STATUS_BUSY },
> + .clear_cnt = { DDRPERFM_CLR, MP2_CLR_CNT},
> + .clear_time = { DDRPERFM_CLR, MP2_CLR_TIME},
Spaces before } are missing
There are a few others above that I'll not mention directly.
> + .cfg0 = { DDRPERFM_MP2_CFG0 },
> + .cfg1 = { DDRPERFM_MP2_CFG1 },
> + .enable = { DDRPERFM_MP2_CFG5 },
> + .dram_inf = { DDRPERFM_MP2_DRAMINF },
> + .counter_time = { DDRPERFM_MP2_TCNT },
> + .counter_evt = {
> + { DDRPERFM_MP2_EVCNT(0) },
Somewhat unusual formatting though neat I guess so fine if you
really like it!.
.counter_evt = {
{ DDRPERFM_MP2_EVCNT(0) },
would be what I'd normally expect.
> + { DDRPERFM_MP2_EVCNT(1) },
> + { DDRPERFM_MP2_EVCNT(2) },
> + { DDRPERFM_MP2_EVCNT(3) },
> + { DDRPERFM_MP2_EVCNT(4) },
> + { DDRPERFM_MP2_EVCNT(5) },
> + { DDRPERFM_MP2_EVCNT(6) },
> + { DDRPERFM_MP2_EVCNT(7) },
> + },
> +};
> +
> +static const struct stm32_ddr_pmu_cfg stm32_ddr_pmu_cfg_mp1 = {
> + .regs = &stm32_ddr_pmu_regspec_mp1,
> + .attribute = stm32_ddr_pmu_attr_groups_mp1,
> + .counters_nb = MP1_CNT_NB,
> + .evt_counters_nb = MP1_CNT_NB - 1, /* Time counter is not an event counter */
> + .time_cnt_idx = MP1_TIME_CNT_IDX,
> + .get_counter = stm32_ddr_pmu_get_event_counter_mp1,
> +};
> +
> +static const struct stm32_ddr_pmu_cfg stm32_ddr_pmu_cfg_mp2 = {
> + .regs = &stm32_ddr_pmu_regspec_mp2,
> + .attribute = stm32_ddr_pmu_attr_groups_mp2,
> + .counters_nb = MP2_CNT_NB,
> + .evt_counters_nb = MP2_CNT_NB - 1, /* Time counter is an event counter */
> + .time_cnt_idx = MP2_TIME_CNT_IDX,
> + .get_counter = stm32_ddr_pmu_get_event_counter_mp2,
> +};
> +
> +static const struct dev_pm_ops stm32_ddr_pmu_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(NULL, stm32_ddr_pmu_device_resume)
> +};
static DEFINE_SIMPLE_DEV_PM_OPS() looks appropriate here.
> +
> +static const struct of_device_id stm32_ddr_pmu_of_match[] = {
> + {
> + .compatible = "st,stm32mp131-ddr-pmu",
> + .data = &stm32_ddr_pmu_cfg_mp1
> + },
> + {
> + .compatible = "st,stm32mp251-ddr-pmu",
> + .data = &stm32_ddr_pmu_cfg_mp2
> + },
> + { },
No comma need after terminating entry. Nice to make it hard
to accidentally add entries after one of these!
> +};
> +MODULE_DEVICE_TABLE(of, stm32_ddr_pmu_of_match);
> +
> +static struct platform_driver stm32_ddr_pmu_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + .pm = pm_sleep_ptr(&stm32_ddr_pmu_pm_ops),
> + .of_match_table = stm32_ddr_pmu_of_match,
> + },
> + .probe = stm32_ddr_pmu_device_probe,
> + .remove = stm32_ddr_pmu_device_remove,
> +};
> +
> +module_platform_driver(stm32_ddr_pmu_driver);
> +
> +MODULE_AUTHOR("Clément Le Goffic");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 DDR performance monitor driver");
> +MODULE_LICENSE("GPL");
>
next prev parent reply other threads:[~2025-07-11 16:48 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-07-11 14:48 [PATCH v2 00/16] Introduce STM32 DDR PMU for STM32MP platforms Clément Le Goffic
2025-07-11 14:48 ` [PATCH v2 01/16] bus: firewall: move stm32_firewall header file in include folder Clément Le Goffic
2025-07-11 14:48 ` [PATCH v2 02/16] dt-bindings: stm32: stm32mp25: add `access-controller-cell` property Clément Le Goffic
2025-07-15 3:17 ` Rob Herring
2025-07-15 7:37 ` Gatien CHEVALLIER
2025-07-15 8:19 ` Krzysztof Kozlowski
2025-07-15 8:40 ` Gatien CHEVALLIER
2025-07-15 11:47 ` Clement LE GOFFIC
2025-07-11 14:48 ` [PATCH v2 03/16] clk: stm32mp25: add firewall grant_access ops Clément Le Goffic
2025-07-11 14:48 ` [PATCH v2 04/16] arm64: dts: st: set rcc as an access-controller Clément Le Goffic
2025-07-11 14:48 ` [PATCH v2 05/16] dt-bindings: memory: add jedec,ddr[3-4]-channel binding Clément Le Goffic
2025-07-21 20:09 ` Rob Herring
2025-07-22 7:35 ` Clement LE GOFFIC
2025-07-11 14:48 ` [PATCH v2 06/16] arm64: dts: st: add LPDDR channel to stm32mp257f-dk board Clément Le Goffic
2025-07-15 3:20 ` Rob Herring
2025-07-15 8:32 ` Clement LE GOFFIC
2025-07-15 15:02 ` Rob Herring
2025-07-21 15:44 ` Clement LE GOFFIC
2025-07-11 14:48 ` [PATCH v2 07/16] arm64: dts: st: add DDR channel to stm32mp257f-ev1 board Clément Le Goffic
2025-07-11 14:49 ` [PATCH v2 08/16] dt-bindings: perf: stm32: introduce DDRPERFM dt-bindings Clément Le Goffic
2025-07-11 14:49 ` [PATCH v2 09/16] perf: stm32: introduce DDRPERFM driver Clément Le Goffic
2025-07-11 16:04 ` Jonathan Cameron [this message]
2025-07-15 9:49 ` Clement LE GOFFIC
2025-07-14 19:39 ` Dan Carpenter
2025-07-11 14:49 ` [PATCH v2 10/16] Documentation: perf: stm32: add ddrperfm support Clément Le Goffic
2025-07-11 14:49 ` [PATCH v2 11/16] MAINTAINERS: add myself as STM32 DDR PMU maintainer Clément Le Goffic
2025-07-11 14:49 ` [PATCH v2 12/16] ARM: dts: stm32: add ddrperfm on stm32mp131 Clément Le Goffic
2025-07-11 14:49 ` [PATCH v2 13/16] ARM: dts: stm32: add ddrperfm on stm32mp151 Clément Le Goffic
2025-07-11 14:49 ` [PATCH v2 14/16] arm64: dts: st: add ddrperfm on stm32mp251 Clément Le Goffic
2025-07-11 14:49 ` [PATCH v2 15/16] arm64: dts: st: support ddrperfm on stm32mp257f-dk Clément Le Goffic
2025-07-11 14:49 ` [PATCH v2 16/16] arm64: dts: st: support ddrperfm on stm32mp257f-ev1 Clément Le Goffic
2025-07-14 15:24 ` [PATCH v2 00/16] Introduce STM32 DDR PMU for STM32MP platforms Rob Herring (Arm)
-- strict thread matches above, loose matches on Subject: below --
2025-07-12 13:25 [PATCH v2 09/16] perf: stm32: introduce DDRPERFM driver kernel test robot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250711170415.00001901@huawei.com \
--to=jonathan.cameron@huawei.com \
--cc=alexandre.torgue@foss.st.com \
--cc=clement.legoffic@foss.st.com \
--cc=conor+dt@kernel.org \
--cc=corbet@lwn.net \
--cc=devicetree@vger.kernel.org \
--cc=gabriel.fernandez@foss.st.com \
--cc=gatien.chevallier@foss.st.com \
--cc=krzk+dt@kernel.org \
--cc=krzk@kernel.org \
--cc=legoffic.clement@gmail.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=linux-stm32@st-md-mailman.stormreply.com \
--cc=mark.rutland@arm.com \
--cc=mcoquelin.stm32@gmail.com \
--cc=mturquette@baylibre.com \
--cc=p.zabel@pengutronix.de \
--cc=robh@kernel.org \
--cc=sboyd@kernel.org \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.