* [PATCH v4 0/4] Enable sysfs module symlink for more built-in drivers
From: Shashank Balaji @ 2026-04-27 2:41 UTC (permalink / raw)
To: Suzuki K Poulose, James Clark, Alexander Shishkin,
Maxime Coquelin, Alexandre Torgue, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Richard Cochran, Jonathan Corbet,
Shuah Khan, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Mike Leach, Leo Yan, Mike Leach
Cc: Rahul Bukte, Shashank Balaji, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules
In-Reply-To: <20260422-acpi_mod_name-v3-0-a184eff9ff6f@sony.com>
struct device_driver's mod_name is not set by a number of bus' driver registration
functions. Without that, built-in drivers don't have the module symlink in sysfs.
We want this to go from unbound driver name -> module name -> kernel config name.
This is useful on embedded platforms to minimize kernel config, reduce kernel size,
and reduce boot time.
In order to achieve this, mod_name has to be set to KBUILD_MODNAME, and this has
to be done for all buses which don't yet do this.
Here are some treewide stats:
- 110 registration functions across all bus types
- 20 of them set mod_name
- Remaining 90 do not set mod_name:
1. 36 functions under pattern 1:
They have a __register function + register macro. KBUILD_MODNAME needs to
be passed and the function needs to take mod_name as input.
2. 42 functions under pattern 2:
These have no macro wrapper. They need a double-underscore rename + macro
wrapper to make them similar to pattern 1.
3. Remaining 12 do not have such a clean registration interface. More analysis
is required.
We plan to start with pattern 1, since it's the easiest category of changes.
Within that, for now we're only sending the platform patch. If we get the go-ahead
on that, we'll send the remaining ones.
Patch 3 depends on patches 1 and 2.
Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
---
Changes in v4:
- Initialize module_kset in do_basic_setup() before do_initcalls() (Gary)
- Add commit body to the documentation patch (Greg)
- Link to v3: https://patch.msgid.link/20260422-acpi_mod_name-v3-0-a184eff9ff6f@sony.com
Changes in v3:
- Initialize module_kset on-demand (Greg)
- Make coresight driver registration happen through a macro (Greg)
- Split up the patch adding mod_name to platform driver registrations (Greg)
- Link to v2: https://patch.msgid.link/20260421-acpi_mod_name-v2-0-e73f9310dad3@sony.com
Changes in v2:
- Drop acpi patch, send platform instead (Rafael)
- Link to v1: https://patch.msgid.link/20260416-acpi_mod_name-v1-0-1a4d96fd86c9@sony.com
To: Suzuki K Poulose <suzuki.poulose@arm.com>
To: Mike Leach <mike.leach@linaro.org>
To: James Clark <james.clark@linaro.org>
To: Alexander Shishkin <alexander.shishkin@linux.intel.com>
To: Maxime Coquelin <mcoquelin.stm32@gmail.com>
To: Alexandre Torgue <alexandre.torgue@foss.st.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
To: "Rafael J. Wysocki" <rafael@kernel.org>
To: Danilo Krummrich <dakr@kernel.org>
To: Miguel Ojeda <ojeda@kernel.org>
To: Boqun Feng <boqun@kernel.org>
To: Gary Guo <gary@garyguo.net>
To: Björn Roy Baron <bjorn3_gh@protonmail.com>
To: Benno Lossin <lossin@kernel.org>
To: Andreas Hindborg <a.hindborg@kernel.org>
To: Alice Ryhl <aliceryhl@google.com>
To: Trevor Gross <tmgross@umich.edu>
To: Richard Cochran <richardcochran@gmail.com>
To: Jonathan Corbet <corbet@lwn.net>
To: Shuah Khan <skhan@linuxfoundation.org>
To: Luis Chamberlain <mcgrof@kernel.org>
To: Petr Pavlu <petr.pavlu@suse.com>
To: Daniel Gomez <da.gomez@kernel.org>
To: Sami Tolvanen <samitolvanen@google.com>
To: Aaron Tomlin <atomlin@atomlin.com>
To: Mike Leach <mike.leach@arm.com>
To: Leo Yan <leo.yan@arm.com>
Cc: linux-kernel@vger.kernel.org
Cc: coresight@lists.linaro.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: driver-core@lists.linux.dev
Cc: rust-for-linux@vger.kernel.org
Cc: linux-doc@vger.kernel.org
Cc: Shashank Balaji <shashank.mahadasyam@sony.com>
Cc: Rahul Bukte <rahul.bukte@sony.com>
Cc: Daniel Palmer <daniel.palmer@sony.com>
Cc: Tim Bird <tim.bird@sony.com>
Cc: linux-modules@vger.kernel.org
---
Shashank Balaji (4):
kernel: param: initialize module_kset before do_initcalls()
coresight: pass THIS_MODULE implicitly through a macro
driver core: platform: set mod_name in driver registration
docs: driver-api: add mod_name argument to __platform_register_drivers()
Documentation/driver-api/driver-model/platform.rst | 3 ++-
drivers/base/platform.c | 21 ++++++++++++++-------
drivers/hwtracing/coresight/coresight-catu.c | 2 +-
drivers/hwtracing/coresight/coresight-core.c | 9 +++++----
drivers/hwtracing/coresight/coresight-cpu-debug.c | 3 +--
drivers/hwtracing/coresight/coresight-funnel.c | 3 +--
drivers/hwtracing/coresight/coresight-replicator.c | 3 +--
drivers/hwtracing/coresight/coresight-stm.c | 2 +-
drivers/hwtracing/coresight/coresight-tmc-core.c | 2 +-
drivers/hwtracing/coresight/coresight-tnoc.c | 2 +-
drivers/hwtracing/coresight/coresight-tpdm.c | 3 +--
drivers/hwtracing/coresight/coresight-tpiu.c | 2 +-
include/linux/coresight.h | 7 +++++--
include/linux/module.h | 4 ++++
include/linux/platform_device.h | 17 +++++++++--------
init/main.c | 1 +
kernel/params.c | 21 +++++++++------------
rust/kernel/platform.rs | 4 +++-
18 files changed, 61 insertions(+), 48 deletions(-)
---
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
change-id: 20260416-acpi_mod_name-f645a76e337b
Best regards,
--
Shashank Balaji <shashank.mahadasyam@sony.com>
^ permalink raw reply
* [PATCH v4 4/4] docs: driver-api: add mod_name argument to __platform_register_drivers()
From: Shashank Balaji @ 2026-04-27 2:41 UTC (permalink / raw)
To: Suzuki K Poulose, James Clark, Alexander Shishkin,
Maxime Coquelin, Alexandre Torgue, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Richard Cochran, Jonathan Corbet,
Shuah Khan, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Mike Leach, Leo Yan, Mike Leach
Cc: Rahul Bukte, Shashank Balaji, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules
In-Reply-To: <20260427-acpi_mod_name-v4-0-22b42240c9bf@sony.com>
Sync the function signature of __platform_register_driver() between the code and
the doc.
Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
---
Documentation/driver-api/driver-model/platform.rst | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/Documentation/driver-api/driver-model/platform.rst b/Documentation/driver-api/driver-model/platform.rst
index cf5ff48d3115..9673470bded2 100644
--- a/Documentation/driver-api/driver-model/platform.rst
+++ b/Documentation/driver-api/driver-model/platform.rst
@@ -70,7 +70,8 @@ Kernel modules can be composed of several platform drivers. The platform core
provides helpers to register and unregister an array of drivers::
int __platform_register_drivers(struct platform_driver * const *drivers,
- unsigned int count, struct module *owner);
+ unsigned int count, struct module *owner,
+ const char *mod_name);
void platform_unregister_drivers(struct platform_driver * const *drivers,
unsigned int count);
--
2.43.0
^ permalink raw reply related
* [PATCH v4 2/4] coresight: pass THIS_MODULE implicitly through a macro
From: Shashank Balaji @ 2026-04-27 2:41 UTC (permalink / raw)
To: Suzuki K Poulose, James Clark, Alexander Shishkin,
Maxime Coquelin, Alexandre Torgue, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Richard Cochran, Jonathan Corbet,
Shuah Khan, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Mike Leach, Leo Yan, Mike Leach
Cc: Rahul Bukte, Shashank Balaji, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules
In-Reply-To: <20260427-acpi_mod_name-v4-0-22b42240c9bf@sony.com>
Rename coresight_init_driver() to __coresight_init_driver() and replace
it with a macro wrapper that passes THIS_MODULE implicitly. This is in line with
what other buses do.
Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
---
drivers/hwtracing/coresight/coresight-catu.c | 2 +-
drivers/hwtracing/coresight/coresight-core.c | 4 ++--
drivers/hwtracing/coresight/coresight-cpu-debug.c | 3 +--
drivers/hwtracing/coresight/coresight-funnel.c | 3 +--
drivers/hwtracing/coresight/coresight-replicator.c | 3 +--
drivers/hwtracing/coresight/coresight-stm.c | 2 +-
drivers/hwtracing/coresight/coresight-tmc-core.c | 2 +-
drivers/hwtracing/coresight/coresight-tnoc.c | 2 +-
drivers/hwtracing/coresight/coresight-tpdm.c | 3 +--
drivers/hwtracing/coresight/coresight-tpiu.c | 2 +-
include/linux/coresight.h | 4 +++-
11 files changed, 14 insertions(+), 16 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c
index ce71dcddfca2..0c8698c8fc5e 100644
--- a/drivers/hwtracing/coresight/coresight-catu.c
+++ b/drivers/hwtracing/coresight/coresight-catu.c
@@ -706,7 +706,7 @@ static int __init catu_init(void)
{
int ret;
- ret = coresight_init_driver("catu", &catu_driver, &catu_platform_driver, THIS_MODULE);
+ ret = coresight_init_driver("catu", &catu_driver, &catu_platform_driver);
tmc_etr_set_catu_ops(&etr_catu_buf_ops);
return ret;
}
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 46f247f73cf6..4ebcbd95b7fb 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -1694,7 +1694,7 @@ static void __exit coresight_exit(void)
module_init(coresight_init);
module_exit(coresight_exit);
-int coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
+int __coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
struct platform_driver *pdev_drv, struct module *owner)
{
int ret;
@@ -1713,7 +1713,7 @@ int coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
amba_driver_unregister(amba_drv);
return ret;
}
-EXPORT_SYMBOL_GPL(coresight_init_driver);
+EXPORT_SYMBOL_GPL(__coresight_init_driver);
void coresight_remove_driver(struct amba_driver *amba_drv,
struct platform_driver *pdev_drv)
diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c
index 629614278e46..3a806c1d50ea 100644
--- a/drivers/hwtracing/coresight/coresight-cpu-debug.c
+++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c
@@ -757,8 +757,7 @@ static struct platform_driver debug_platform_driver = {
static int __init debug_init(void)
{
- return coresight_init_driver("debug", &debug_driver, &debug_platform_driver,
- THIS_MODULE);
+ return coresight_init_driver("debug", &debug_driver, &debug_platform_driver);
}
static void __exit debug_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
index 3f56ceccd8c9..0abc11f0690c 100644
--- a/drivers/hwtracing/coresight/coresight-funnel.c
+++ b/drivers/hwtracing/coresight/coresight-funnel.c
@@ -412,8 +412,7 @@ static struct amba_driver dynamic_funnel_driver = {
static int __init funnel_init(void)
{
- return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver,
- THIS_MODULE);
+ return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver);
}
static void __exit funnel_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c
index 07fc04f53b88..2f382de357ee 100644
--- a/drivers/hwtracing/coresight/coresight-replicator.c
+++ b/drivers/hwtracing/coresight/coresight-replicator.c
@@ -418,8 +418,7 @@ static struct amba_driver dynamic_replicator_driver = {
static int __init replicator_init(void)
{
- return coresight_init_driver("replicator", &dynamic_replicator_driver, &replicator_driver,
- THIS_MODULE);
+ return coresight_init_driver("replicator", &dynamic_replicator_driver, &replicator_driver);
}
static void __exit replicator_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c
index aca6cec7885a..4e860519a73f 100644
--- a/drivers/hwtracing/coresight/coresight-stm.c
+++ b/drivers/hwtracing/coresight/coresight-stm.c
@@ -1050,7 +1050,7 @@ static struct platform_driver stm_platform_driver = {
static int __init stm_init(void)
{
- return coresight_init_driver("stm", &stm_driver, &stm_platform_driver, THIS_MODULE);
+ return coresight_init_driver("stm", &stm_driver, &stm_platform_driver);
}
static void __exit stm_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
index c89fe996af23..bc5a133ada3e 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-core.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
@@ -1046,7 +1046,7 @@ static struct platform_driver tmc_platform_driver = {
static int __init tmc_init(void)
{
- return coresight_init_driver("tmc", &tmc_driver, &tmc_platform_driver, THIS_MODULE);
+ return coresight_init_driver("tmc", &tmc_driver, &tmc_platform_driver);
}
static void __exit tmc_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-tnoc.c b/drivers/hwtracing/coresight/coresight-tnoc.c
index 96a25877b824..9e8de4323d28 100644
--- a/drivers/hwtracing/coresight/coresight-tnoc.c
+++ b/drivers/hwtracing/coresight/coresight-tnoc.c
@@ -344,7 +344,7 @@ static struct platform_driver itnoc_driver = {
static int __init tnoc_init(void)
{
- return coresight_init_driver("tnoc", &trace_noc_driver, &itnoc_driver, THIS_MODULE);
+ return coresight_init_driver("tnoc", &trace_noc_driver, &itnoc_driver);
}
static void __exit tnoc_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c
index eaf7210af648..8464edbba2d4 100644
--- a/drivers/hwtracing/coresight/coresight-tpdm.c
+++ b/drivers/hwtracing/coresight/coresight-tpdm.c
@@ -1563,8 +1563,7 @@ static struct platform_driver static_tpdm_driver = {
static int __init tpdm_init(void)
{
- return coresight_init_driver("tpdm", &dynamic_tpdm_driver, &static_tpdm_driver,
- THIS_MODULE);
+ return coresight_init_driver("tpdm", &dynamic_tpdm_driver, &static_tpdm_driver);
}
static void __exit tpdm_exit(void)
diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
index b8560b140e0f..7b029d2eb389 100644
--- a/drivers/hwtracing/coresight/coresight-tpiu.c
+++ b/drivers/hwtracing/coresight/coresight-tpiu.c
@@ -310,7 +310,7 @@ static struct platform_driver tpiu_platform_driver = {
static int __init tpiu_init(void)
{
- return coresight_init_driver("tpiu", &tpiu_driver, &tpiu_platform_driver, THIS_MODULE);
+ return coresight_init_driver("tpiu", &tpiu_driver, &tpiu_platform_driver);
}
static void __exit tpiu_exit(void)
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 2131febebee9..b9ec5f195907 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -691,7 +691,9 @@ coresight_find_output_type(struct coresight_platform_data *pdata,
enum coresight_dev_type type,
union coresight_dev_subtype subtype);
-int coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
+#define coresight_init_driver(drv, amba_drv, pdev_drv) \
+ __coresight_init_driver(drv, amba_drv, pdev_drv, THIS_MODULE)
+int __coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
struct platform_driver *pdev_drv, struct module *owner);
void coresight_remove_driver(struct amba_driver *amba_drv,
--
2.43.0
^ permalink raw reply related
* [PATCH v4 1/4] kernel: param: initialize module_kset before do_initcalls()
From: Shashank Balaji @ 2026-04-27 2:41 UTC (permalink / raw)
To: Suzuki K Poulose, James Clark, Alexander Shishkin,
Maxime Coquelin, Alexandre Torgue, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Richard Cochran, Jonathan Corbet,
Shuah Khan, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Mike Leach, Leo Yan, Mike Leach
Cc: Rahul Bukte, Shashank Balaji, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules
In-Reply-To: <20260427-acpi_mod_name-v4-0-22b42240c9bf@sony.com>
module_kset is initialized in param_sysfs_init(), a subsys_initcall. A number
of platform drivers register themselves prior to subsys_initcalls
(tegra194_cbb_driver registers in a pure_initcall, for example). With an
upcoming patch ("driver core: platform: set mod_name in driver registration")
that sets their mod_name in struct device_driver, lookup_or_create_module_kobject()
will be called for those drivers, which calls kset_find_obj(module_kset, mod_name).
This causes a null deref because module_kset isn't alive yet.
Fix this by initializing module_kset in do_basic_setup() before do_initcalls().
Modernize the pr_warn while we're at it.
Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Suggested-by: Gary Guo <gary@garyguo.net>
Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
---
include/linux/module.h | 4 ++++
init/main.c | 1 +
kernel/params.c | 21 +++++++++------------
3 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/include/linux/module.h b/include/linux/module.h
index 7566815fabbe..6478596e8f9f 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -886,6 +886,10 @@ static inline void module_for_each_mod(int(*func)(struct module *mod, void *data
#ifdef CONFIG_SYSFS
extern struct kset *module_kset;
extern const struct kobj_type module_ktype;
+
+void param_sysfs_init(void);
+#else
+static inline void param_sysfs_init(void) {}
#endif /* CONFIG_SYSFS */
#define symbol_request(x) try_then_request_module(symbol_get(x), "symbol:" #x)
diff --git a/init/main.c b/init/main.c
index 96f93bb06c49..01552c6b62ff 100644
--- a/init/main.c
+++ b/init/main.c
@@ -1486,6 +1486,7 @@ static void __init do_basic_setup(void)
ksysfs_init();
driver_init();
init_irq_proc();
+ param_sysfs_init();
do_ctors();
do_initcalls();
}
diff --git a/kernel/params.c b/kernel/params.c
index 74d620bc2521..d1e3934fb3a7 100644
--- a/kernel/params.c
+++ b/kernel/params.c
@@ -942,22 +942,19 @@ const struct kobj_type module_ktype = {
/*
* param_sysfs_init - create "module" kset
*
- * This must be done before the initramfs is unpacked and
- * request_module() thus becomes possible, because otherwise the
- * module load would fail in mod_sysfs_init.
+ * Must run before:
+ * - do_initcalls(): some drivers register during initcalls and rely on
+ * module_kset existing for their sysfs module symlink.
+ * - rootfs_initcall (initramfs unpack): request_module() becomes possible.
+ * But if module_kset is null, module load would fail in mod_sysfs_init(),
+ * causing request_module() to fail.
*/
-static int __init param_sysfs_init(void)
+void __init param_sysfs_init(void)
{
module_kset = kset_create_and_add("module", &module_uevent_ops, NULL);
- if (!module_kset) {
- printk(KERN_WARNING "%s (%d): error creating kset\n",
- __FILE__, __LINE__);
- return -ENOMEM;
- }
-
- return 0;
+ if (!module_kset)
+ pr_warn("Error creating module kset\n");
}
-subsys_initcall(param_sysfs_init);
/*
* param_sysfs_builtin_init - add sysfs version and parameter
--
2.43.0
^ permalink raw reply related
* [PATCH v4 3/4] driver core: platform: set mod_name in driver registration
From: Shashank Balaji @ 2026-04-27 2:41 UTC (permalink / raw)
To: Suzuki K Poulose, James Clark, Alexander Shishkin,
Maxime Coquelin, Alexandre Torgue, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Alice Ryhl, Trevor Gross, Richard Cochran, Jonathan Corbet,
Shuah Khan, Luis Chamberlain, Petr Pavlu, Daniel Gomez,
Sami Tolvanen, Aaron Tomlin, Mike Leach, Leo Yan, Mike Leach
Cc: Rahul Bukte, Shashank Balaji, linux-kernel, coresight,
linux-arm-kernel, driver-core, rust-for-linux, linux-doc,
Daniel Palmer, Tim Bird, linux-modules
In-Reply-To: <20260427-acpi_mod_name-v4-0-22b42240c9bf@sony.com>
Pass KBUILD_MODNAME through the driver registration macro so that
the driver core can create the module symlink in sysfs for built-in
drivers, and fixup all callers.
The Rust platform adapter is updated to pass the module name through to the new
parameter.
Tested on qemu with:
- x86 defconfig + CONFIG_RUST
- arm64 defconfig + CONFIG_RUST + CONFIG_CORESIGHT stuff
Examples after this patch:
/sys/bus/platform/drivers/...
coresight-itnoc/module -> coresight_tnoc
coresight-static-tpdm/module -> coresight_tpdm
coresight-catu-platform/module -> coresight_catu
serial8250/module -> 8250
acpi-ged/module -> acpi
vmclock/module -> ptp_vmclock
Co-developed-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Rahul Bukte <rahul.bukte@sony.com>
Signed-off-by: Shashank Balaji <shashank.mahadasyam@sony.com>
---
This patch depends on patches 1 and 2.
---
drivers/base/platform.c | 21 ++++++++++++++-------
drivers/hwtracing/coresight/coresight-core.c | 5 +++--
include/linux/coresight.h | 5 +++--
include/linux/platform_device.h | 17 +++++++++--------
rust/kernel/platform.rs | 4 +++-
5 files changed, 32 insertions(+), 20 deletions(-)
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 75b4698d0e58..2b0cc0889386 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -901,11 +901,14 @@ EXPORT_SYMBOL_GPL(platform_device_register_full);
* __platform_driver_register - register a driver for platform-level devices
* @drv: platform driver structure
* @owner: owning module/driver
+ * @mod_name: module name string
*/
-int __platform_driver_register(struct platform_driver *drv, struct module *owner)
+int __platform_driver_register(struct platform_driver *drv, struct module *owner,
+ const char *mod_name)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
+ drv->driver.mod_name = mod_name;
return driver_register(&drv->driver);
}
@@ -938,6 +941,7 @@ static int is_bound_to_driver(struct device *dev, void *driver)
* @drv: platform driver structure
* @probe: the driver probe routine, probably from an __init section
* @module: module which will be the owner of the driver
+ * @mod_name: module name string
*
* Use this instead of platform_driver_register() when you know the device
* is not hotpluggable and has already been registered, and you want to
@@ -955,7 +959,8 @@ static int is_bound_to_driver(struct device *dev, void *driver)
*/
int __init_or_module __platform_driver_probe(struct platform_driver *drv,
int (*probe)(struct platform_device *),
- struct module *module)
+ struct module *module,
+ const char *mod_name)
{
int retval;
@@ -983,7 +988,7 @@ int __init_or_module __platform_driver_probe(struct platform_driver *drv,
/* temporary section violation during probe() */
drv->probe = probe;
- retval = __platform_driver_register(drv, module);
+ retval = __platform_driver_register(drv, module, mod_name);
if (retval)
return retval;
@@ -1011,6 +1016,7 @@ EXPORT_SYMBOL_GPL(__platform_driver_probe);
* @data: platform specific data for this platform device
* @size: size of platform specific data
* @module: module which will be the owner of the driver
+ * @mod_name: module name string
*
* Use this in legacy-style modules that probe hardware directly and
* register a single platform device and corresponding platform driver.
@@ -1021,7 +1027,7 @@ struct platform_device * __init_or_module
__platform_create_bundle(struct platform_driver *driver,
int (*probe)(struct platform_device *),
struct resource *res, unsigned int n_res,
- const void *data, size_t size, struct module *module)
+ const void *data, size_t size, struct module *module, const char *mod_name)
{
struct platform_device *pdev;
int error;
@@ -1044,7 +1050,7 @@ __platform_create_bundle(struct platform_driver *driver,
if (error)
goto err_pdev_put;
- error = __platform_driver_probe(driver, probe, module);
+ error = __platform_driver_probe(driver, probe, module, mod_name);
if (error)
goto err_pdev_del;
@@ -1064,6 +1070,7 @@ EXPORT_SYMBOL_GPL(__platform_create_bundle);
* @drivers: an array of drivers to register
* @count: the number of drivers to register
* @owner: module owning the drivers
+ * @mod_name: module name string
*
* Registers platform drivers specified by an array. On failure to register a
* driver, all previously registered drivers will be unregistered. Callers of
@@ -1073,7 +1080,7 @@ EXPORT_SYMBOL_GPL(__platform_create_bundle);
* Returns: 0 on success or a negative error code on failure.
*/
int __platform_register_drivers(struct platform_driver * const *drivers,
- unsigned int count, struct module *owner)
+ unsigned int count, struct module *owner, const char *mod_name)
{
unsigned int i;
int err;
@@ -1081,7 +1088,7 @@ int __platform_register_drivers(struct platform_driver * const *drivers,
for (i = 0; i < count; i++) {
pr_debug("registering platform driver %ps\n", drivers[i]);
- err = __platform_driver_register(drivers[i], owner);
+ err = __platform_driver_register(drivers[i], owner, mod_name);
if (err < 0) {
pr_err("failed to register platform driver %ps: %d\n",
drivers[i], err);
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 4ebcbd95b7fb..9c4fdef76833 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -1695,7 +1695,8 @@ module_init(coresight_init);
module_exit(coresight_exit);
int __coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
- struct platform_driver *pdev_drv, struct module *owner)
+ struct platform_driver *pdev_drv, struct module *owner,
+ const char *mod_name)
{
int ret;
@@ -1705,7 +1706,7 @@ int __coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
return ret;
}
- ret = __platform_driver_register(pdev_drv, owner);
+ ret = __platform_driver_register(pdev_drv, owner, mod_name);
if (!ret)
return 0;
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index b9ec5f195907..d7ae0527d039 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -692,9 +692,10 @@ coresight_find_output_type(struct coresight_platform_data *pdata,
union coresight_dev_subtype subtype);
#define coresight_init_driver(drv, amba_drv, pdev_drv) \
- __coresight_init_driver(drv, amba_drv, pdev_drv, THIS_MODULE)
+ __coresight_init_driver(drv, amba_drv, pdev_drv, THIS_MODULE, KBUILD_MODNAME)
int __coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
- struct platform_driver *pdev_drv, struct module *owner);
+ struct platform_driver *pdev_drv, struct module *owner,
+ const char *mod_name);
void coresight_remove_driver(struct amba_driver *amba_drv,
struct platform_driver *pdev_drv);
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index 975400a472e3..26e6a43358e2 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -293,18 +293,19 @@ struct platform_driver {
* use a macro to avoid include chaining to get THIS_MODULE
*/
#define platform_driver_register(drv) \
- __platform_driver_register(drv, THIS_MODULE)
+ __platform_driver_register(drv, THIS_MODULE, KBUILD_MODNAME)
extern int __platform_driver_register(struct platform_driver *,
- struct module *);
+ struct module *, const char *mod_name);
extern void platform_driver_unregister(struct platform_driver *);
/* non-hotpluggable platform devices may use this so that probe() and
* its support may live in __init sections, conserving runtime memory.
*/
#define platform_driver_probe(drv, probe) \
- __platform_driver_probe(drv, probe, THIS_MODULE)
+ __platform_driver_probe(drv, probe, THIS_MODULE, KBUILD_MODNAME)
extern int __platform_driver_probe(struct platform_driver *driver,
- int (*probe)(struct platform_device *), struct module *module);
+ int (*probe)(struct platform_device *), struct module *module,
+ const char *mod_name);
static inline void *platform_get_drvdata(const struct platform_device *pdev)
{
@@ -368,19 +369,19 @@ static int __init __platform_driver##_init(void) \
device_initcall(__platform_driver##_init); \
#define platform_create_bundle(driver, probe, res, n_res, data, size) \
- __platform_create_bundle(driver, probe, res, n_res, data, size, THIS_MODULE)
+ __platform_create_bundle(driver, probe, res, n_res, data, size, THIS_MODULE, KBUILD_MODNAME)
extern struct platform_device *__platform_create_bundle(
struct platform_driver *driver, int (*probe)(struct platform_device *),
struct resource *res, unsigned int n_res,
- const void *data, size_t size, struct module *module);
+ const void *data, size_t size, struct module *module, const char *mod_name);
int __platform_register_drivers(struct platform_driver * const *drivers,
- unsigned int count, struct module *owner);
+ unsigned int count, struct module *owner, const char *mod_name);
void platform_unregister_drivers(struct platform_driver * const *drivers,
unsigned int count);
#define platform_register_drivers(drivers, count) \
- __platform_register_drivers(drivers, count, THIS_MODULE)
+ __platform_register_drivers(drivers, count, THIS_MODULE, KBUILD_MODNAME)
#ifdef CONFIG_SUSPEND
extern int platform_pm_suspend(struct device *dev);
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 8917d4ee499f..2d626eecc450 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -82,7 +82,9 @@ unsafe fn register(
}
// SAFETY: `pdrv` is guaranteed to be a valid `DriverType`.
- to_result(unsafe { bindings::__platform_driver_register(pdrv.get(), module.0) })
+ to_result(unsafe {
+ bindings::__platform_driver_register(pdrv.get(), module.0, name.as_char_ptr())
+ })
}
unsafe fn unregister(pdrv: &Opaque<Self::DriverType>) {
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v8 1/4] coresight: cti: Convert trigger usage fields to dynamic bitmaps and arrays
From: Jie Gan @ 2026-04-27 2:47 UTC (permalink / raw)
To: Yingchao Deng, Suzuki K Poulose, Mike Leach, James Clark, Leo Yan,
Alexander Shishkin
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
quic_yingdeng, Jinlong Mao, Tingwei Zhang
In-Reply-To: <1c99a162-475e-4d6c-af85-a16322d31476@oss.qualcomm.com>
On 4/27/2026 9:48 AM, Jie Gan wrote:
>
>
> On 4/26/2026 5:44 PM, Yingchao Deng wrote:
>> Replace the fixed-size u32 fields in the cti_config and cti_trig_grp
>> structure with dynamically allocated bitmaps and arrays. This allows
>> memory to be allocated based on the actual number of triggers during
>> probe
>> time, reducing memory footprint and improving scalability for platforms
>> with varying trigger counts.
>>
>> Signed-off-by: Yingchao Deng <yingchao.deng@oss.qualcomm.com>
>> ---
>> drivers/hwtracing/coresight/coresight-cti-core.c | 59 ++++++++++++
>> +++++-----
>> .../hwtracing/coresight/coresight-cti-platform.c | 26 +++++++---
>> drivers/hwtracing/coresight/coresight-cti-sysfs.c | 14 ++---
>> drivers/hwtracing/coresight/coresight-cti.h | 12 ++---
>> 4 files changed, 76 insertions(+), 35 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/
>> drivers/hwtracing/coresight/coresight-cti-core.c
>> index 2f4c9362709a..4e7d12bd2d3e 100644
>> --- a/drivers/hwtracing/coresight/coresight-cti-core.c
>> +++ b/drivers/hwtracing/coresight/coresight-cti-core.c
>> @@ -161,8 +161,8 @@ void cti_write_intack(struct device *dev, u32 ackval)
>> /* DEVID[19:16] - number of CTM channels */
>> #define CTI_DEVID_CTMCHANNELS(devid_val) ((int) BMVAL(devid_val, 16,
>> 19))
>> -static void cti_set_default_config(struct device *dev,
>> - struct cti_drvdata *drvdata)
>> +static int cti_set_default_config(struct device *dev,
>> + struct cti_drvdata *drvdata)
>> {
>> struct cti_config *config = &drvdata->config;
>> u32 devid;
>> @@ -181,6 +181,26 @@ static void cti_set_default_config(struct device
>> *dev,
>> config->nr_trig_max = CTIINOUTEN_MAX;
>> }
>> + config->trig_in_use = devm_bitmap_zalloc(dev, config-
>> >nr_trig_max, GFP_KERNEL);
>> + if (!config->trig_in_use)
>> + return -ENOMEM;
>> +
>> + config->trig_out_use = devm_bitmap_zalloc(dev, config-
>> >nr_trig_max, GFP_KERNEL);
>> + if (!config->trig_out_use)
>> + return -ENOMEM;
>> +
>> + config->trig_out_filter = devm_bitmap_zalloc(dev, config-
>> >nr_trig_max, GFP_KERNEL);
>> + if (!config->trig_out_filter)
>> + return -ENOMEM;
>> +
>> + config->ctiinen = devm_kcalloc(dev, config->nr_trig_max,
>> sizeof(u32), GFP_KERNEL);
>> + if (!config->ctiinen)
>> + return -ENOMEM;
>> +
>> + config->ctiouten = devm_kcalloc(dev, config->nr_trig_max,
>> sizeof(u32), GFP_KERNEL);
>> + if (!config->ctiouten)
>> + return -ENOMEM;
>> +
>> config->nr_ctm_channels = CTI_DEVID_CTMCHANNELS(devid);
>> /* Most regs default to 0 as zalloc'ed except...*/
>> @@ -189,6 +209,7 @@ static void cti_set_default_config(struct device
>> *dev,
>> config->enable_req_count = 0;
>> config->asicctl_impl = !!FIELD_GET(GENMASK(4, 0), devid);
>> + return 0;
>> }
>> /*
>> @@ -219,8 +240,10 @@ int cti_add_connection_entry(struct device *dev,
>> struct cti_drvdata *drvdata,
>> cti_dev->nr_trig_con++;
>> /* add connection usage bit info to overall info */
>> - drvdata->config.trig_in_use |= tc->con_in->used_mask;
>> - drvdata->config.trig_out_use |= tc->con_out->used_mask;
>> + bitmap_or(drvdata->config.trig_in_use, drvdata->config.trig_in_use,
>> + tc->con_in->used_mask, drvdata->config.nr_trig_max);
>> + bitmap_or(drvdata->config.trig_out_use, drvdata-
>> >config.trig_out_use,
>> + tc->con_out->used_mask, drvdata->config.nr_trig_max);
>> return 0;
>> }
>> @@ -231,6 +254,8 @@ struct cti_trig_con *cti_allocate_trig_con(struct
>> device *dev, int in_sigs,
>> {
>> struct cti_trig_con *tc = NULL;
>> struct cti_trig_grp *in = NULL, *out = NULL;
>> + struct cti_drvdata *drvdata = dev_get_drvdata(dev);
>> + int n_trigs = drvdata->config.nr_trig_max;
>> tc = devm_kzalloc(dev, sizeof(struct cti_trig_con), GFP_KERNEL);
>> if (!tc)
>> @@ -242,12 +267,20 @@ struct cti_trig_con
>> *cti_allocate_trig_con(struct device *dev, int in_sigs,
>> if (!in)
>> return NULL;
>> + in->used_mask = devm_bitmap_zalloc(dev, n_trigs, GFP_KERNEL);
>> + if (!in->used_mask)
>> + return NULL;
>> +
>> out = devm_kzalloc(dev,
>> offsetof(struct cti_trig_grp, sig_types[out_sigs]),
>> GFP_KERNEL);
>> if (!out)
>> return NULL;
>> + out->used_mask = devm_bitmap_zalloc(dev, n_trigs, GFP_KERNEL);
>> + if (!out->used_mask)
>> + return NULL;
>> +
>> tc->con_in = in;
>> tc->con_out = out;
>> tc->con_in->nr_sigs = in_sigs;
>> @@ -263,7 +296,6 @@ int cti_add_default_connection(struct device *dev,
>> struct cti_drvdata *drvdata)
>> {
>> int ret = 0;
>> int n_trigs = drvdata->config.nr_trig_max;
>> - u32 n_trig_mask = GENMASK(n_trigs - 1, 0);
>> struct cti_trig_con *tc = NULL;
>> /*
>> @@ -274,8 +306,8 @@ int cti_add_default_connection(struct device *dev,
>> struct cti_drvdata *drvdata)
>> if (!tc)
>> return -ENOMEM;
>> - tc->con_in->used_mask = n_trig_mask;
>> - tc->con_out->used_mask = n_trig_mask;
>> + bitmap_fill(tc->con_in->used_mask, n_trigs);
>> + bitmap_fill(tc->con_out->used_mask, n_trigs);
>> ret = cti_add_connection_entry(dev, drvdata, tc, NULL, "default");
>> return ret;
>> }
>> @@ -288,7 +320,6 @@ int cti_channel_trig_op(struct device *dev, enum
>> cti_chan_op op,
>> {
>> struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
>> struct cti_config *config = &drvdata->config;
>> - u32 trig_bitmask;
>> u32 chan_bitmask;
>> u32 reg_value;
>> int reg_offset;
>> @@ -298,18 +329,16 @@ int cti_channel_trig_op(struct device *dev, enum
>> cti_chan_op op,
>> (trigger_idx >= config->nr_trig_max))
>> return -EINVAL;
>> - trig_bitmask = BIT(trigger_idx);
>> -
>> /* ensure registered triggers and not out filtered */
>> if (direction == CTI_TRIG_IN) {
>> - if (!(trig_bitmask & config->trig_in_use))
>> + if (!(test_bit(trigger_idx, config->trig_in_use)))
>> return -EINVAL;
>> } else {
>> - if (!(trig_bitmask & config->trig_out_use))
>> + if (!(test_bit(trigger_idx, config->trig_out_use)))
>> return -EINVAL;
>> if ((config->trig_filter_enable) &&
>> - (config->trig_out_filter & trig_bitmask))
>> + test_bit(trigger_idx, config->trig_out_filter))
>> return -EINVAL;
>> }
>> @@ -687,7 +716,9 @@ static int cti_probe(struct amba_device *adev,
>> const struct amba_id *id)
>> raw_spin_lock_init(&drvdata->spinlock);
>> /* initialise CTI driver config values */
>> - cti_set_default_config(dev, drvdata);
>> + ret = cti_set_default_config(dev, drvdata);
>> + if (ret)
>> + return ret;
>> pdata = coresight_cti_get_platform_data(dev);
>> if (IS_ERR(pdata)) {
>> diff --git a/drivers/hwtracing/coresight/coresight-cti-platform.c b/
>> drivers/hwtracing/coresight/coresight-cti-platform.c
>> index 4eff96f48594..557debbc8ca4 100644
>> --- a/drivers/hwtracing/coresight/coresight-cti-platform.c
>> +++ b/drivers/hwtracing/coresight/coresight-cti-platform.c
>> @@ -136,8 +136,8 @@ static int
>> cti_plat_create_v8_etm_connection(struct device *dev,
>> goto create_v8_etm_out;
>> /* build connection data */
>> - tc->con_in->used_mask = 0xF0; /* sigs <4,5,6,7> */
>> - tc->con_out->used_mask = 0xF0; /* sigs <4,5,6,7> */
>> + bitmap_set(tc->con_in->used_mask, 4, 4); /* sigs <4,5,6,7> */
>> + bitmap_set(tc->con_out->used_mask, 4, 4); /* sigs <4,5,6,7> */
>> /*
>> * The EXTOUT type signals from the ETM are connected to a set
>> of input
>> @@ -194,10 +194,10 @@ static int cti_plat_create_v8_connections(struct
>> device *dev,
>> goto of_create_v8_out;
>> /* Set the v8 PE CTI connection data */
>> - tc->con_in->used_mask = 0x3; /* sigs <0 1> */
>> + bitmap_set(tc->con_in->used_mask, 0, 2); /* sigs <0 1> */
>> tc->con_in->sig_types[0] = PE_DBGTRIGGER;
>> tc->con_in->sig_types[1] = PE_PMUIRQ;
>> - tc->con_out->used_mask = 0x7; /* sigs <0 1 2 > */
>> + bitmap_set(tc->con_out->used_mask, 0, 3); /* sigs <0 1 2 > */
>> tc->con_out->sig_types[0] = PE_EDBGREQ;
>> tc->con_out->sig_types[1] = PE_DBGRESTART;
>> tc->con_out->sig_types[2] = PE_CTIIRQ;
>> @@ -213,7 +213,7 @@ static int cti_plat_create_v8_connections(struct
>> device *dev,
>> goto of_create_v8_out;
>> /* filter pe_edbgreq - PE trigout sig <0> */
>> - drvdata->config.trig_out_filter |= 0x1;
>> + set_bit(0, drvdata->config.trig_out_filter);
>> of_create_v8_out:
>> return ret;
>> @@ -257,7 +257,7 @@ static int cti_plat_read_trig_group(struct
>> cti_trig_grp *tgrp,
>> if (!err) {
>> /* set the signal usage mask */
>> for (idx = 0; idx < tgrp->nr_sigs; idx++)
>> - tgrp->used_mask |= BIT(values[idx]);
>> + set_bit(values[idx], tgrp->used_mask);
>> }
>> kfree(values);
>> @@ -316,23 +316,33 @@ static int cti_plat_process_filter_sigs(struct
>> cti_drvdata *drvdata,
>> {
>> struct cti_trig_grp *tg = NULL;
>> int err = 0, nr_filter_sigs;
>> + int nr_trigs = drvdata->config.nr_trig_max;
>> nr_filter_sigs = cti_plat_count_sig_elements(fwnode,
>> CTI_DT_FILTER_OUT_SIGS);
>> if (nr_filter_sigs == 0)
>> return 0;
>> - if (nr_filter_sigs > drvdata->config.nr_trig_max)
>> + if (nr_filter_sigs > nr_trigs)
>> return -EINVAL;
>> tg = kzalloc_obj(*tg);
>> if (!tg)
>> return -ENOMEM;
>> + tg->used_mask = bitmap_zalloc(nr_trigs, GFP_KERNEL);
>> + if (!tg->used_mask) {
>> + kfree(tg);
>> + return -ENOMEM;
>> + }
>> +
>> err = cti_plat_read_trig_group(tg, fwnode, CTI_DT_FILTER_OUT_SIGS);
>> if (!err)
>> - drvdata->config.trig_out_filter |= tg->used_mask;
>> + bitmap_or(drvdata->config.trig_out_filter,
>> + drvdata->config.trig_out_filter,
>> + tg->used_mask, nr_trigs);
>
> The error may be silently ignored when a memory allocation error
> occured. I think it's better to add a log print to tell user what happened.
My fault here, please ignore this comment.
This error will be handled by probe fail process.
Thanks,
Jie
>
> Thanks,
> Jie
>
>> + bitmap_free(tg->used_mask);
>> kfree(tg);
>> return err;
>> }
>> diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/
>> drivers/hwtracing/coresight/coresight-cti-sysfs.c
>> index 3fe2c916d228..2bbfa405cb6b 100644
>> --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c
>> +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
>> @@ -719,12 +719,12 @@ static ssize_t trigout_filtered_show(struct
>> device *dev,
>> struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
>> struct cti_config *cfg = &drvdata->config;
>> int nr_trig_max = cfg->nr_trig_max;
>> - unsigned long mask = cfg->trig_out_filter;
>> + unsigned long *mask = cfg->trig_out_filter;
>> - if (mask == 0)
>> + if (bitmap_empty(mask, nr_trig_max))
>> return 0;
>> - return sysfs_emit(buf, "%*pbl\n", nr_trig_max, &mask);
>> + return sysfs_emit(buf, "%*pbl\n", nr_trig_max, mask);
>> }
>> static DEVICE_ATTR_RO(trigout_filtered);
>> @@ -931,9 +931,9 @@ static ssize_t trigin_sig_show(struct device *dev,
>> struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
>> struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
>> struct cti_config *cfg = &drvdata->config;
>> - unsigned long mask = con->con_in->used_mask;
>> + unsigned long *mask = con->con_in->used_mask;
>> - return sysfs_emit(buf, "%*pbl\n", cfg->nr_trig_max, &mask);
>> + return sysfs_emit(buf, "%*pbl\n", cfg->nr_trig_max, mask);
>> }
>> static ssize_t trigout_sig_show(struct device *dev,
>> @@ -945,9 +945,9 @@ static ssize_t trigout_sig_show(struct device *dev,
>> struct cti_trig_con *con = (struct cti_trig_con *)ext_attr->var;
>> struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
>> struct cti_config *cfg = &drvdata->config;
>> - unsigned long mask = con->con_out->used_mask;
>> + unsigned long *mask = con->con_out->used_mask;
>> - return sysfs_emit(buf, "%*pbl\n", cfg->nr_trig_max, &mask);
>> + return sysfs_emit(buf, "%*pbl\n", cfg->nr_trig_max, mask);
>> }
>> /* convert a sig type id to a name */
>> diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/
>> hwtracing/coresight/coresight-cti.h
>> index c5f9e79fabc6..ef079fc18b72 100644
>> --- a/drivers/hwtracing/coresight/coresight-cti.h
>> +++ b/drivers/hwtracing/coresight/coresight-cti.h
>> @@ -68,7 +68,7 @@ struct fwnode_handle;
>> */
>> struct cti_trig_grp {
>> int nr_sigs;
>> - u32 used_mask;
>> + unsigned long *used_mask;
>> int sig_types[];
>> };
>> @@ -145,17 +145,17 @@ struct cti_config {
>> int enable_req_count;
>> /* registered triggers and filtering */
>> - u32 trig_in_use;
>> - u32 trig_out_use;
>> - u32 trig_out_filter;
>> + unsigned long *trig_in_use;
>> + unsigned long *trig_out_use;
>> + unsigned long *trig_out_filter;
>> bool trig_filter_enable;
>> u8 xtrig_rchan_sel;
>> /* cti cross trig programmable regs */
>> u32 ctiappset;
>> u8 ctiinout_sel;
>> - u32 ctiinen[CTIINOUTEN_MAX];
>> - u32 ctiouten[CTIINOUTEN_MAX];
>> + u32 *ctiinen;
>> + u32 *ctiouten;
>> u32 ctigate;
>> u32 asicctl;
>> };
>>
>
^ permalink raw reply
* RE: [PATCH v1 1/3] dt-bindings: PCI: imx6q-pcie: Add intr, aer and pme interrupts
From: Hongxing Zhu @ 2026-04-27 3:17 UTC (permalink / raw)
To: Conor Dooley
Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
bhelgaas@google.com, Frank Li, l.stach@pengutronix.de,
lpieralisi@kernel.org, kwilczynski@kernel.org, mani@kernel.org,
s.hauer@pengutronix.de, kernel@pengutronix.de, festevam@gmail.com,
linux-pci@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
devicetree@vger.kernel.org, imx@lists.linux.dev,
linux-kernel@vger.kernel.org
In-Reply-To: <20260424-sinless-unfiled-d1087a894da5@spud>
> -----Original Message-----
> From: Conor Dooley <conor@kernel.org>
> Sent: Saturday, April 25, 2026 1:06 AM
> To: Hongxing Zhu <hongxing.zhu@nxp.com>
> Cc: robh@kernel.org; krzk+dt@kernel.org; conor+dt@kernel.org;
> bhelgaas@google.com; Frank Li <frank.li@nxp.com>; l.stach@pengutronix.de;
> lpieralisi@kernel.org; kwilczynski@kernel.org; mani@kernel.org;
> s.hauer@pengutronix.de; kernel@pengutronix.de; festevam@gmail.com; linux-
> pci@vger.kernel.org; linux-arm-kernel@lists.infradead.org;
> devicetree@vger.kernel.org; imx@lists.linux.dev; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v1 1/3] dt-bindings: PCI: imx6q-pcie: Add intr, aer and pme
> interrupts
>
> On Fri, Apr 24, 2026 at 10:57:33AM +0800, Richard Zhu wrote:
> > Add optional 'intr', 'aer', and 'pme' interrupt entries to the i.MX6Q
> > PCIe binding to support PCIe event-based interrupts for general
> > controller events, Advanced Error Reporting, and Power Management
> > Events respectively.
> >
> > Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
> > ---
>
> This binding supports lots of devices. Do they all have these additional interrupts?
Currently, only i.MX95 PCIe has these dedicated SPI interrupts. The earlier
SoCs in this binding (i.MX6Q/6SX/7D/8MQ/8MM/8MP, etc.) do not expose these as
separate interrupt lines.
I can constrain these three interrupt entries to be valid only for the i.MX95
variant using conditional schemas. Would that be acceptable?
Best Regards
Richard Zhu
>
> > Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml | 6 ++++++
> > 1 file changed, 6 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
> > b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
> > index 9d1349855b422..badc7fcbd556c 100644
> > --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
> > +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
> > @@ -58,12 +58,18 @@ properties:
> > items:
> > - description: builtin MSI controller.
> > - description: builtin DMA controller.
> > + - description: PCIe event interrupt.
> > + - description: builtin AER SPI standalone interrupter line.
> > + - description: builtin PME SPI standalone interrupter line.
> >
> > interrupt-names:
> > minItems: 1
> > items:
> > - const: msi
> > - const: dma
> > + - const: intr
> > + - const: aer
> > + - const: pme
> >
> > reset-gpio:
> > description: Should specify the GPIO for controlling the PCI bus
> > device
> > --
> > 2.37.1
> >
^ permalink raw reply
* RE: [PATCH v1 1/3] dt-bindings: PCI: imx6q-pcie: Add intr, aer and pme interrupts
From: Hongxing Zhu @ 2026-04-27 3:17 UTC (permalink / raw)
To: Krzysztof Kozlowski, Conor Dooley
Cc: robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
bhelgaas@google.com, Frank Li, l.stach@pengutronix.de,
lpieralisi@kernel.org, kwilczynski@kernel.org, mani@kernel.org,
s.hauer@pengutronix.de, kernel@pengutronix.de, festevam@gmail.com,
linux-pci@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
devicetree@vger.kernel.org, imx@lists.linux.dev,
linux-kernel@vger.kernel.org
In-Reply-To: <20260425-agile-impala-of-unity-cadced@quoll>
> -----Original Message-----
> From: Krzysztof Kozlowski <krzk@kernel.org>
> Sent: Saturday, April 25, 2026 6:00 PM
> To: Conor Dooley <conor@kernel.org>
> Cc: Hongxing Zhu <hongxing.zhu@nxp.com>; robh@kernel.org;
> krzk+dt@kernel.org; conor+dt@kernel.org; bhelgaas@google.com; Frank Li
> <frank.li@nxp.com>; l.stach@pengutronix.de; lpieralisi@kernel.org;
> kwilczynski@kernel.org; mani@kernel.org; s.hauer@pengutronix.de;
> kernel@pengutronix.de; festevam@gmail.com; linux-pci@vger.kernel.org; linux-
> arm-kernel@lists.infradead.org; devicetree@vger.kernel.org;
> imx@lists.linux.dev; linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v1 1/3] dt-bindings: PCI: imx6q-pcie: Add intr, aer and pme
> interrupts
>
> On Fri, Apr 24, 2026 at 06:06:18PM +0100, Conor Dooley wrote:
> > On Fri, Apr 24, 2026 at 10:57:33AM +0800, Richard Zhu wrote:
> > > Add optional 'intr', 'aer', and 'pme' interrupt entries to the
> > > i.MX6Q PCIe binding to support PCIe event-based interrupts for
> > > general controller events, Advanced Error Reporting, and Power
> > > Management Events respectively.
> > >
> > > Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
> > > ---
> >
> > This binding supports lots of devices. Do they all have these
> > additional interrupts?
>
> Yep. Commit msg says only i.MX6Q...
>
Sorry for the confusion caused by the file name. These standalone SPI
interrupts are specific to i.MX95 PCIe only, not the other devices covered by
this binding.
Best Regards
Richard Zhu
> Best regards,
> Krzysztof
>
^ permalink raw reply
* Re: [PATCH v8 2/4] coresight: cti: encode trigger register index in register offsets
From: Yingchao Deng (Consultant) @ 2026-04-27 3:36 UTC (permalink / raw)
To: Jie Gan, Yingchao Deng, Suzuki K Poulose, Mike Leach, James Clark,
Leo Yan, Alexander Shishkin
Cc: coresight, linux-arm-kernel, linux-kernel, linux-arm-msm,
Jinlong Mao, Tingwei Zhang
In-Reply-To: <11376a1b-923d-4bee-bdc6-fecea43a256d@oss.qualcomm.com>
On 4/27/2026 10:22 AM, Jie Gan wrote:
>
>
> On 4/26/2026 5:44 PM, Yingchao Deng wrote:
>> Introduce a small encoding to carry the register index together with the
>> base offset in a single u32, and use a common helper to compute the
>> final
>> MMIO address. This refactors register access to be based on the encoded
>> (reg, nr) pair, reducing duplicated arithmetic and making it easier to
>> support variants that bank or relocate trigger-indexed registers.
>>
>> Signed-off-by: Yingchao Deng <yingchao.deng@oss.qualcomm.com>
>> ---
>> drivers/hwtracing/coresight/coresight-cti-core.c | 31
>> +++++++++++++++--------
>> drivers/hwtracing/coresight/coresight-cti-sysfs.c | 4 +--
>> drivers/hwtracing/coresight/coresight-cti.h | 16 ++++++++++--
>> 3 files changed, 36 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c
>> b/drivers/hwtracing/coresight/coresight-cti-core.c
>> index 4e7d12bd2d3e..c4cbeb64365b 100644
>> --- a/drivers/hwtracing/coresight/coresight-cti-core.c
>> +++ b/drivers/hwtracing/coresight/coresight-cti-core.c
>> @@ -42,6 +42,14 @@ static DEFINE_MUTEX(ect_mutex);
>> #define csdev_to_cti_drvdata(csdev) \
>> dev_get_drvdata(csdev->dev.parent)
>> +static void __iomem *cti_reg_addr(struct cti_drvdata *drvdata, int
>> reg)
>
> u32 reg would be better.
>
>> +{
>> + u32 offset = CTI_REG_CLR_NR(reg);
>
> No functional error but a little bit tricky here.
>
> CTI_REG_CLR_NR(reg) will produce a offset for the bits[0:23], but in
> the comment, you mentioned the base register offset ranges from [0:11].
>
> With my understanding, all CTI register offsets fall within the range
> b 0 to 0XFAC, that's why we have bits[0:11]?
>
> Thanks,
> Jie
Thanks for the review.
While current CoreSight components fit within a single 4KB
block, IHI0029 states that a component can occupy up to 64MB (16,384
x 4KB blocks), requiring up to 26 bits for the offset. I will change
CTI_REG_NR_MASK to GENMASK(31, 28) to avoid any potential conflict
with bits[24:25].
Thanks,
Yingchao
>> + u32 nr = CTI_REG_GET_NR(reg);
>> +
>> + return drvdata->base + offset + sizeof(u32) * nr;
>> +}
>> +
>> /* write set of regs to hardware - call with spinlock claimed */
>> void cti_write_all_hw_regs(struct cti_drvdata *drvdata)
>> {
>> @@ -55,16 +63,17 @@ void cti_write_all_hw_regs(struct cti_drvdata
>> *drvdata)
>> /* write the CTI trigger registers */
>> for (i = 0; i < config->nr_trig_max; i++) {
>> - writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
>> + writel_relaxed(config->ctiinen[i],
>> + cti_reg_addr(drvdata, CTI_REG_SET_NR(CTIINEN, i)));
>> writel_relaxed(config->ctiouten[i],
>> - drvdata->base + CTIOUTEN(i));
>> + cti_reg_addr(drvdata, CTI_REG_SET_NR(CTIOUTEN, i)));
>> }
>> /* other regs */
>> - writel_relaxed(config->ctigate, drvdata->base + CTIGATE);
>> + writel_relaxed(config->ctigate, cti_reg_addr(drvdata, CTIGATE));
>> if (config->asicctl_impl)
>> - writel_relaxed(config->asicctl, drvdata->base + ASICCTL);
>> - writel_relaxed(config->ctiappset, drvdata->base + CTIAPPSET);
>> + writel_relaxed(config->asicctl, cti_reg_addr(drvdata,
>> ASICCTL));
>> + writel_relaxed(config->ctiappset, cti_reg_addr(drvdata,
>> CTIAPPSET));
>> /* re-enable CTI */
>> writel_relaxed(1, drvdata->base + CTICONTROL);
>> @@ -127,7 +136,7 @@ u32 cti_read_single_reg(struct cti_drvdata
>> *drvdata, int offset)
>> int val;
>> CS_UNLOCK(drvdata->base);
>> - val = readl_relaxed(drvdata->base + offset);
>> + val = readl_relaxed(cti_reg_addr(drvdata, offset));
>> CS_LOCK(drvdata->base);
>> return val;
>> @@ -136,7 +145,7 @@ u32 cti_read_single_reg(struct cti_drvdata
>> *drvdata, int offset)
>> void cti_write_single_reg(struct cti_drvdata *drvdata, int offset,
>> u32 value)
>> {
>> CS_UNLOCK(drvdata->base);
>> - writel_relaxed(value, drvdata->base + offset);
>> + writel_relaxed(value, cti_reg_addr(drvdata, offset));
>> CS_LOCK(drvdata->base);
>> }
>> @@ -344,8 +353,7 @@ int cti_channel_trig_op(struct device *dev,
>> enum cti_chan_op op,
>> /* update the local register values */
>> chan_bitmask = BIT(channel_idx);
>> - reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
>> - CTIOUTEN(trigger_idx));
>> + reg_offset = (direction == CTI_TRIG_IN ? CTIINEN : CTIOUTEN);
>> guard(raw_spinlock_irqsave)(&drvdata->spinlock);
>> @@ -365,8 +373,9 @@ int cti_channel_trig_op(struct device *dev,
>> enum cti_chan_op op,
>> /* write through if enabled */
>> if (cti_is_active(config))
>> - cti_write_single_reg(drvdata, reg_offset, reg_value);
>> -
>> + cti_write_single_reg(drvdata,
>> + CTI_REG_SET_NR(reg_offset, trigger_idx),
>> + reg_value);
>> return 0;
>> }
>> diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c
>> b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
>> index 2bbfa405cb6b..8b70e7e38ea3 100644
>> --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c
>> +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c
>> @@ -386,7 +386,7 @@ static ssize_t inen_store(struct device *dev,
>> /* write through if enabled */
>> if (cti_is_active(config))
>> - cti_write_single_reg(drvdata, CTIINEN(index), val);
>> + cti_write_single_reg(drvdata, CTI_REG_SET_NR(CTIINEN,
>> index), val);
>> return size;
>> }
>> @@ -427,7 +427,7 @@ static ssize_t outen_store(struct device *dev,
>> /* write through if enabled */
>> if (cti_is_active(config))
>> - cti_write_single_reg(drvdata, CTIOUTEN(index), val);
>> + cti_write_single_reg(drvdata, CTI_REG_SET_NR(CTIOUTEN,
>> index), val);
>> return size;
>> }
>> diff --git a/drivers/hwtracing/coresight/coresight-cti.h
>> b/drivers/hwtracing/coresight/coresight-cti.h
>> index ef079fc18b72..dd1ba44518c4 100644
>> --- a/drivers/hwtracing/coresight/coresight-cti.h
>> +++ b/drivers/hwtracing/coresight/coresight-cti.h
>> @@ -7,6 +7,7 @@
>> #ifndef _CORESIGHT_CORESIGHT_CTI_H
>> #define _CORESIGHT_CORESIGHT_CTI_H
>> +#include <linux/bitfield.h>
>> #include <linux/coresight.h>
>> #include <linux/device.h>
>> #include <linux/list.h>
>> @@ -30,8 +31,8 @@ struct fwnode_handle;
>> #define CTIAPPSET 0x014
>> #define CTIAPPCLEAR 0x018
>> #define CTIAPPPULSE 0x01C
>> -#define CTIINEN(n) (0x020 + (4 * n))
>> -#define CTIOUTEN(n) (0x0A0 + (4 * n))
>> +#define CTIINEN 0x020
>> +#define CTIOUTEN 0x0A0
>> #define CTITRIGINSTATUS 0x130
>> #define CTITRIGOUTSTATUS 0x134
>> #define CTICHINSTATUS 0x138
>> @@ -59,6 +60,17 @@ struct fwnode_handle;
>> */
>> #define CTIINOUTEN_MAX 32
>> +/*
>> + * Encode CTI register offset and register index in one u32:
>> + * - bits[0:11] : base register offset (0x000 to 0xFFF)
>> + * - bits[24:31] : register index (nr)
>> + */
>> +#define CTI_REG_NR_MASK GENMASK(31, 24)
>> +#define CTI_REG_GET_NR(reg) FIELD_GET(CTI_REG_NR_MASK, (reg))
>> +#define CTI_REG_SET_NR_CONST(reg, nr) ((reg) |
>> FIELD_PREP_CONST(CTI_REG_NR_MASK, (nr)))
>> +#define CTI_REG_SET_NR(reg, nr) ((reg) |
>> FIELD_PREP(CTI_REG_NR_MASK, (nr)))
>> +#define CTI_REG_CLR_NR(reg) ((reg) & (~CTI_REG_NR_MASK))
>> +
>> /**
>> * Group of related trigger signals
>> *
>>
>
^ permalink raw reply
* [PATCH 0/3] wifi: mt76: remove mediatek,mtd-eeprom
From: Rosen Penev @ 2026-04-27 3:44 UTC (permalink / raw)
To: devicetree
Cc: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Johannes Berg, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Thomas Bogendoerfer, open list:MEDIATEK MT76 WIRELESS LAN DRIVER,
open list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support, open list:MIPS
mediatek,mtd-eeprom predates nvmem which is used everywhere to grab
eeprom and mac-address data from MTD devices.
Transition the one place to nvmem and remove the binding to discourage
use.
Rosen Penev (3):
MIPS: dts: ralink: gardena_smart_gateway_mt7688: use nvmem for EEPROM
dt-bindings: net: wireless: mt76: remove mediatek,mtd-eeprom
wifi: mt76: remove mt76_get_of_data_from_mtd
.../bindings/net/wireless/mediatek,mt76.yaml | 19 +---
.../ralink/gardena_smart_gateway_mt7688.dts | 17 +++-
drivers/net/wireless/mediatek/mt76/eeprom.c | 87 -------------------
drivers/net/wireless/mediatek/mt76/mt76.h | 1 -
.../wireless/mediatek/mt76/mt7915/eeprom.c | 4 -
5 files changed, 17 insertions(+), 111 deletions(-)
--
2.54.0
^ permalink raw reply
* [PATCH 1/3] MIPS: dts: ralink: gardena_smart_gateway_mt7688: use nvmem for EEPROM
From: Rosen Penev @ 2026-04-27 3:44 UTC (permalink / raw)
To: devicetree
Cc: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Johannes Berg, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Thomas Bogendoerfer, open list:MEDIATEK MT76 WIRELESS LAN DRIVER,
open list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support, open list:MIPS
In-Reply-To: <20260427034427.881389-1-rosenp@gmail.com>
mediatek,mtd-eeprom is a deprecated binding for extracting data on MTD
devices which has been replaced by NVMEM.
The latter is already in wide use with mt76. As this is the only user,
transition to NVMEM.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
.../dts/ralink/gardena_smart_gateway_mt7688.dts | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/arch/mips/boot/dts/ralink/gardena_smart_gateway_mt7688.dts b/arch/mips/boot/dts/ralink/gardena_smart_gateway_mt7688.dts
index 0bfb1dde9764..a8a8efbaf527 100644
--- a/arch/mips/boot/dts/ralink/gardena_smart_gateway_mt7688.dts
+++ b/arch/mips/boot/dts/ralink/gardena_smart_gateway_mt7688.dts
@@ -155,10 +155,20 @@ partition@b0000 {
reg = <0xb0000 0x10000>;
};
- factory: partition@c0000 {
+ partition@c0000 {
label = "factory";
reg = <0xc0000 0x10000>;
read-only;
+
+ nvmem-layout {
+ compatible = "fixed-layout";
+ #address-cells = <1>;
+ #nvmem-cell-cells = <1>;
+
+ eeprom_factory_0: eeprom@0 {
+ reg = <0x0 0x400>;
+ };
+ };
};
};
};
@@ -201,5 +211,8 @@ &watchdog {
&wmac {
status = "okay";
- mediatek,mtd-eeprom = <&factory 0x0000>;
+
+ nvmem-cells = <&eeprom_factory_0>;
+ nvmem-cell-names = "eeprom"
+
};
--
2.54.0
^ permalink raw reply related
* [PATCH 2/3] dt-bindings: net: wireless: mt76: remove mediatek,mtd-eeprom
From: Rosen Penev @ 2026-04-27 3:44 UTC (permalink / raw)
To: devicetree
Cc: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Johannes Berg, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Thomas Bogendoerfer, open list:MEDIATEK MT76 WIRELESS LAN DRIVER,
open list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support, open list:MIPS
In-Reply-To: <20260427034427.881389-1-rosenp@gmail.com>
mediatek,mtd-eeprom is a widely unused binding that predates and has
been replaced by NVMEM. As there are no users, remove it.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
.../bindings/net/wireless/mediatek,mt76.yaml | 19 ++-----------------
1 file changed, 2 insertions(+), 17 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
index ae6b97cdc44b..482c22cd6627 100644
--- a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
@@ -87,21 +87,6 @@ properties:
description:
EEPROM data embedded as array.
- mediatek,mtd-eeprom:
- $ref: /schemas/types.yaml#/definitions/phandle-array
- items:
- - items:
- - description: phandle to MTD partition
- - description: offset containing EEPROM data
- description:
- Phandle to a MTD partition + offset containing EEPROM data
- deprecated: true
-
- big-endian:
- $ref: /schemas/types.yaml#/definitions/flag
- description:
- Specify if the radio eeprom partition is written in big-endian
-
mediatek,eeprom-merge-otp:
type: boolean
description:
@@ -314,8 +299,8 @@ examples:
compatible = "mediatek,mt76";
reg = <0x0000 0 0 0 0>;
ieee80211-freq-limit = <5000000 6000000>;
- mediatek,mtd-eeprom = <&factory 0x8000>;
- big-endian;
+ nvmem-cells = <&eeprom>;
+ nvmem-cell-names = "eeprom";
led {
led-sources = <2>;
--
2.54.0
^ permalink raw reply related
* [PATCH 3/3] wifi: mt76: remove mt76_get_of_data_from_mtd
From: Rosen Penev @ 2026-04-27 3:44 UTC (permalink / raw)
To: devicetree
Cc: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Johannes Berg, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
Thomas Bogendoerfer, open list:MEDIATEK MT76 WIRELESS LAN DRIVER,
open list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support, open list:MIPS
In-Reply-To: <20260427034427.881389-1-rosenp@gmail.com>
mt76_get_of_data_from_mtd has been replaced by
mt76_get_of_data_from_nvmem in all usages.
Remove it to prevent people from using the deprecated
mediatek,mtd-eeprom binding.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/wireless/mediatek/mt76/eeprom.c | 87 -------------------
drivers/net/wireless/mediatek/mt76/mt76.h | 1 -
.../wireless/mediatek/mt76/mt7915/eeprom.c | 4 -
3 files changed, 92 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
index afdb73661866..092804323d81 100644
--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -35,89 +35,6 @@ static int mt76_get_of_eeprom_data(struct mt76_dev *dev, void *eep, int len)
return 0;
}
-int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len)
-{
-#ifdef CONFIG_MTD
- struct device_node *np = dev->dev->of_node;
- struct mtd_info *mtd;
- const __be32 *list;
- const char *part;
- phandle phandle;
- size_t retlen;
- int size;
- int ret;
-
- list = of_get_property(np, "mediatek,mtd-eeprom", &size);
- if (!list)
- return -ENOENT;
-
- phandle = be32_to_cpup(list++);
- if (!phandle)
- return -ENOENT;
-
- np = of_find_node_by_phandle(phandle);
- if (!np)
- return -EINVAL;
-
- part = of_get_property(np, "label", NULL);
- if (!part)
- part = np->name;
-
- mtd = get_mtd_device_nm(part);
- if (IS_ERR(mtd)) {
- ret = PTR_ERR(mtd);
- goto out_put_node;
- }
-
- if (size <= sizeof(*list)) {
- ret = -EINVAL;
- goto out_put_node;
- }
-
- offset += be32_to_cpup(list);
- ret = mtd_read(mtd, offset, len, &retlen, eep);
- put_mtd_device(mtd);
- if (mtd_is_bitflip(ret))
- ret = 0;
- if (ret) {
- dev_err(dev->dev, "reading EEPROM from mtd %s failed: %i\n",
- part, ret);
- goto out_put_node;
- }
-
- if (retlen < len) {
- ret = -EINVAL;
- goto out_put_node;
- }
-
- if (of_property_read_bool(dev->dev->of_node, "big-endian")) {
- u8 *data = (u8 *)eep;
- int i;
-
- /* convert eeprom data in Little Endian */
- for (i = 0; i < round_down(len, 2); i += 2)
- put_unaligned_le16(get_unaligned_be16(&data[i]),
- &data[i]);
- }
-
-#ifdef CONFIG_NL80211_TESTMODE
- dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
- if (!dev->test_mtd.name) {
- ret = -ENOMEM;
- goto out_put_node;
- }
- dev->test_mtd.offset = offset;
-#endif
-
-out_put_node:
- of_node_put(np);
- return ret;
-#else
- return -ENOENT;
-#endif
-}
-EXPORT_SYMBOL_GPL(mt76_get_of_data_from_mtd);
-
int mt76_get_of_data_from_nvmem(struct mt76_dev *dev, void *eep,
const char *cell_name, int len)
{
@@ -163,10 +80,6 @@ static int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int len)
if (!ret)
return 0;
- ret = mt76_get_of_data_from_mtd(dev, eep, 0, len);
- if (!ret)
- return 0;
-
return mt76_get_of_data_from_nvmem(dev, eep, "eeprom", len);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 527bef97e122..f447ecac664d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -1339,7 +1339,6 @@ void mt76_seq_puts_array(struct seq_file *file, const char *str,
int mt76_eeprom_init(struct mt76_dev *dev, int len);
int mt76_eeprom_override(struct mt76_phy *phy);
-int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len);
int mt76_get_of_data_from_nvmem(struct mt76_dev *dev, void *eep,
const char *cell_name, int len);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
index eb92cbf1a284..c24e1276700b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
@@ -29,10 +29,6 @@ static int mt7915_eeprom_load_precal(struct mt7915_dev *dev)
offs = is_mt7915(&dev->mt76) ? MT_EE_PRECAL : MT_EE_PRECAL_V2;
- ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size);
- if (!ret)
- return ret;
-
ret = mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size);
if (!ret)
return ret;
--
2.54.0
^ permalink raw reply related
* [PATCH 1/1] scsi: ufs: remove ucd_rsp_dma_addr and ucd_prdt_dma_addr from ufshcd_lrb
From: ed.tsai @ 2026-04-27 3:58 UTC (permalink / raw)
To: bvanassche, Alim Akhtar, Avri Altman, James E.J. Bottomley,
Martin K. Petersen, Matthias Brugger, AngeloGioacchino Del Regno
Cc: linux-kernel, linux-arm-kernel, linux-mediatek, wsd_upstream,
peter.wang, alice.chao, naomi.chu, chun-hung.wu, Ed Tsai, stable,
linux-scsi
From: Ed Tsai <ed.tsai@mediatek.com>
The offsets stored in utp_transfer_req_desc are in double words on
hosts without UFSHCD_QUIRK_PRDT_BYTE_GRAN, using them directly to
compute ucd_rsp_dma_addr and ucd_prdt_dma_addr results in incorrect
DMA addresses.
Since these fields are only used for error logging, remove them from
struct ufshcd_lrb and compute directly in ufshcd_print_tr() using
offsetof(struct utp_transfer_cmd_desc, ...) instead.
Fixes: d5130c5a0932 ("scsi: ufs: Use pre-calculated offsets in ufshcd_init_lrb()")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/all/20260424063603.382328-2-ed.tsai@mediatek.com/
Signed-off-by: Ed Tsai <ed.tsai@mediatek.com>
---
drivers/ufs/core/ufshcd.c | 10 ++++------
include/ufs/ufshcd.h | 4 ----
2 files changed, 4 insertions(+), 10 deletions(-)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index 4805e40ed4d7..02fa61322e77 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -621,7 +621,8 @@ static void ufshcd_print_tr(struct ufs_hba *hba, struct scsi_cmnd *cmd,
ufshcd_hex_dump("UPIU REQ: ", lrbp->ucd_req_ptr,
sizeof(struct utp_upiu_req));
dev_err(hba->dev, "UPIU[%d] - Response UPIU phys@0x%llx\n", tag,
- (u64)lrbp->ucd_rsp_dma_addr);
+ (u64)(lrbp->ucd_req_dma_addr +
+ offsetof(struct utp_transfer_cmd_desc, response_upiu)));
ufshcd_hex_dump("UPIU RSP: ", lrbp->ucd_rsp_ptr,
sizeof(struct utp_upiu_rsp));
@@ -633,7 +634,8 @@ static void ufshcd_print_tr(struct ufs_hba *hba, struct scsi_cmnd *cmd,
dev_err(hba->dev,
"UPIU[%d] - PRDT - %d entries phys@0x%llx\n",
tag, prdt_length,
- (u64)lrbp->ucd_prdt_dma_addr);
+ (u64)(lrbp->ucd_req_dma_addr +
+ offsetof(struct utp_transfer_cmd_desc, prd_table)));
if (pr_prdt)
ufshcd_hex_dump("UPIU PRDT: ", lrbp->ucd_prdt_ptr,
@@ -2971,8 +2973,6 @@ static void ufshcd_init_lrb(struct ufs_hba *hba, struct scsi_cmnd *cmd)
struct utp_transfer_req_desc *utrdlp = hba->utrdl_base_addr;
dma_addr_t cmd_desc_element_addr =
hba->ucdl_dma_addr + i * ufshcd_get_ucd_size(hba);
- u16 response_offset = le16_to_cpu(utrdlp[i].response_upiu_offset);
- u16 prdt_offset = le16_to_cpu(utrdlp[i].prd_table_offset);
struct ufshcd_lrb *lrb = scsi_cmd_priv(cmd);
lrb->utr_descriptor_ptr = utrdlp + i;
@@ -2981,9 +2981,7 @@ static void ufshcd_init_lrb(struct ufs_hba *hba, struct scsi_cmnd *cmd)
lrb->ucd_req_ptr = (struct utp_upiu_req *)cmd_descp->command_upiu;
lrb->ucd_req_dma_addr = cmd_desc_element_addr;
lrb->ucd_rsp_ptr = (struct utp_upiu_rsp *)cmd_descp->response_upiu;
- lrb->ucd_rsp_dma_addr = cmd_desc_element_addr + response_offset;
lrb->ucd_prdt_ptr = (struct ufshcd_sg_entry *)cmd_descp->prd_table;
- lrb->ucd_prdt_dma_addr = cmd_desc_element_addr + prdt_offset;
}
static void __ufshcd_setup_cmd(struct ufs_hba *hba, struct scsi_cmnd *cmd,
diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
index cfbc75d8df83..8cb845534e63 100644
--- a/include/ufs/ufshcd.h
+++ b/include/ufs/ufshcd.h
@@ -158,8 +158,6 @@ struct ufs_pm_lvl_states {
* @ucd_rsp_ptr: Response UPIU address for this command
* @ucd_prdt_ptr: PRDT address of the command
* @utrd_dma_addr: UTRD dma address for debug
- * @ucd_prdt_dma_addr: PRDT dma address for debug
- * @ucd_rsp_dma_addr: UPIU response dma address for debug
* @ucd_req_dma_addr: UPIU request dma address for debug
* @scsi_status: SCSI status of the command
* @command_type: SCSI, UFS, Query.
@@ -182,8 +180,6 @@ struct ufshcd_lrb {
dma_addr_t utrd_dma_addr;
dma_addr_t ucd_req_dma_addr;
- dma_addr_t ucd_rsp_dma_addr;
- dma_addr_t ucd_prdt_dma_addr;
int scsi_status;
--
2.45.2
^ permalink raw reply related
* [PATCH v3] arm64: defconfig: Enable J721E and Keystone PCIe drivers for TI SoCs
From: Aksh Garg @ 2026-04-27 4:44 UTC (permalink / raw)
To: krzysztof.kozlowski, bjorn.andersson, geert, dmitry.baryshkov,
arnd, ebiggers, michal.simek, luca.weiss, sven,
kuninori.morimoto.gx, shijie, linux-arm-kernel
Cc: linux-kernel, s-vadapalli, danishanwar
Enable the J721E PCIe endpoint driver used by TI's J721E, J7200, J721S2,
J722S, J742S2, J784S4, AM64, AM68, and AM69 SoCs.
Enable the Keystone PCIe driver for host and endpoint mode used by TI's
AM65 SoC.
Signed-off-by: Aksh Garg <a-garg7@ti.com>
Reviewed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
---
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes from v1 to v2:
- Squashed the individial patches [1] and [2].
Link to v2: https://lore.kernel.org/all/20260317063902.262392-1-a-garg7@ti.com/
[1] - https://lore.kernel.org/all/20260223104609.876613-1-a-garg7@ti.com/
[2] - https://lore.kernel.org/all/20260223104650.876632-1-a-garg7@ti.com/
arch/arm64/configs/defconfig | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index d905a0777f93..d22a6bcbdaaf 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -230,6 +230,8 @@ CONFIG_PCIE_BRCMSTB=m
CONFIG_PCI_HOST_THUNDER_PEM=y
CONFIG_PCI_HOST_THUNDER_ECAM=y
CONFIG_PCI_HOST_GENERIC=y
+CONFIG_PCI_KEYSTONE_HOST=m
+CONFIG_PCI_KEYSTONE_EP=m
CONFIG_PCIE_MEDIATEK_GEN3=m
CONFIG_PCI_TEGRA=y
CONFIG_PCIE_RCAR_HOST=y
@@ -242,6 +244,7 @@ CONFIG_PCIE_XILINX_DMA_PL=y
CONFIG_PCIE_XILINX_NWL=y
CONFIG_PCIE_XILINX_CPM=y
CONFIG_PCI_J721E_HOST=m
+CONFIG_PCI_J721E_EP=m
CONFIG_PCI_IMX6_HOST=y
CONFIG_PCI_LAYERSCAPE=y
CONFIG_PCI_HISI=y
--
2.34.1
^ permalink raw reply related
* [PATCH v3 0/4] PCI: Add DOE support for endpoint
From: Aksh Garg @ 2026-04-27 5:17 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, s-vadapalli, danishanwar, srk,
a-garg7
This patch series introduces the framework for supporting the Data
Object Exchange (DOE) feature for PCIe endpoint devices. Please refer
to the documentation added in patch 4 for details on the feature and
implementation architecture.
The implementation provides a common framework for all PCIe endpoint
controllers, not specific to any particular SoC vendor.
This patch series is the non-RFC version of the RFC series at
https://lore.kernel.org/all/20260213123603.420941-1-a-garg7@ti.com/
The changes since v1 are documented in the respective patch description.
Changes from v2 to v3:
- Rebased on 7.1-rc1.
v2: https://lore.kernel.org/all/20260401073022.215805-1-a-garg7@ti.com/
Aksh Garg (4):
PCI/DOE: Move common definitions to the header file
PCI: endpoint: Add DOE mailbox support for endpoint functions
PCI: endpoint: Add API for DOE initialization and setup in EPC core
Documentation: PCI: Add documentation for DOE endpoint support
Documentation/PCI/endpoint/index.rst | 1 +
.../PCI/endpoint/pci-endpoint-doe.rst | 318 ++++++++++
drivers/pci/doe.c | 11 -
drivers/pci/endpoint/Kconfig | 14 +
drivers/pci/endpoint/Makefile | 1 +
drivers/pci/endpoint/pci-ep-doe.c | 552 ++++++++++++++++++
drivers/pci/endpoint/pci-epc-core.c | 71 +++
drivers/pci/pci.h | 47 ++
include/linux/pci-doe.h | 8 +
include/linux/pci-epc.h | 24 +
10 files changed, 1036 insertions(+), 11 deletions(-)
create mode 100644 Documentation/PCI/endpoint/pci-endpoint-doe.rst
create mode 100644 drivers/pci/endpoint/pci-ep-doe.c
--
2.34.1
^ permalink raw reply
* [PATCH v3 1/4] PCI/DOE: Move common definitions to the header file
From: Aksh Garg @ 2026-04-27 5:17 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, s-vadapalli, danishanwar, srk,
a-garg7
In-Reply-To: <20260427051725.223704-1-a-garg7@ti.com>
Move common macros and structures from drivers/pci/doe.c to
drivers/pci/pci.h to allow reuse across root complex and
endpoint DOE implementations.
PCI_DOE_MAX_LENGTH macro can be used outside the PCI core as well,
hence move the macro to include/linux/pci-doe.h.
These changes prepare the groundwork for the DOE endpoint implementation
that will reuse these common definitions.
Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes since v1:
- Moved the common macros that need not be visible outside the PCI core
to drivers/pci/pci.h instead to include/linux/pci-doe.h as suggested
by Lukas Wunner
- Removed the redundant empty inlines guarded with CONFIG_PCI_DOE in
include/linux/pci-doe.h.
v2: https://lore.kernel.org/all/20260401073022.215805-2-a-garg7@ti.com/
v1: https://lore.kernel.org/all/20260213123603.420941-3-a-garg7@ti.com/
drivers/pci/doe.c | 11 -----------
drivers/pci/pci.h | 9 +++++++++
include/linux/pci-doe.h | 3 +++
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c
index 7b41da4ec11a..e8d9e95644b3 100644
--- a/drivers/pci/doe.c
+++ b/drivers/pci/doe.c
@@ -28,12 +28,6 @@
#define PCI_DOE_TIMEOUT HZ
#define PCI_DOE_POLL_INTERVAL (PCI_DOE_TIMEOUT / 128)
-#define PCI_DOE_FLAG_CANCEL 0
-#define PCI_DOE_FLAG_DEAD 1
-
-/* Max data object length is 2^18 dwords */
-#define PCI_DOE_MAX_LENGTH (1 << 18)
-
/**
* struct pci_doe_mb - State for a single DOE mailbox
*
@@ -63,11 +57,6 @@ struct pci_doe_mb {
#endif
};
-struct pci_doe_feature {
- u16 vid;
- u8 type;
-};
-
/**
* struct pci_doe_task - represents a single query/response
*
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 4a14f88e543a..5844deee2b5f 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -683,6 +683,15 @@ struct pci_sriov {
bool drivers_autoprobe; /* Auto probing of VFs by driver */
};
+/* DOE Mailbox state flags */
+#define PCI_DOE_FLAG_CANCEL 0
+#define PCI_DOE_FLAG_DEAD 1
+
+struct pci_doe_feature {
+ u16 vid;
+ u8 type;
+};
+
#ifdef CONFIG_PCI_DOE
void pci_doe_init(struct pci_dev *pdev);
void pci_doe_destroy(struct pci_dev *pdev);
diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
index bd4346a7c4e7..abb9b7ae8029 100644
--- a/include/linux/pci-doe.h
+++ b/include/linux/pci-doe.h
@@ -19,6 +19,9 @@ struct pci_doe_mb;
#define PCI_DOE_FEATURE_CMA 1
#define PCI_DOE_FEATURE_SSESSION 2
+/* Max data object length is 2^18 dwords */
+#define PCI_DOE_MAX_LENGTH (1 << 18)
+
struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
u8 type);
--
2.34.1
^ permalink raw reply related
* [PATCH v3 2/4] PCI: endpoint: Add DOE mailbox support for endpoint functions
From: Aksh Garg @ 2026-04-27 5:17 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, s-vadapalli, danishanwar, srk,
a-garg7
In-Reply-To: <20260427051725.223704-1-a-garg7@ti.com>
DOE (Data Object Exchange) is a standard PCIe extended capability
feature introduced in the Data Object Exchange (DOE) ECN for
PCIe r5.0. It provides a communication mechanism primarily used for
implementing PCIe security features such as device authentication, and
secure link establishment. Think of DOE as a sophisticated mailbox
system built into PCIe. The root complex can send structured requests
to the endpoint device through DOE mailboxes, and the endpoint device
responds with appropriate data.
Add the DOE support for PCIe endpoint devices, enabling endpoint
functions to process the DOE requests from the host. The implementation
provides framework APIs for EPC core driver and controller drivers to
register mailboxes, and request processing with workqueues ensuring
sequential handling per mailbox, and parallel handling across mailboxes.
The Discovery protocol is handled internally by the DOE core.
This implementation complements the existing DOE implementation for
root complex in drivers/pci/doe.c.
Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes since v1:
- Moved the DOE-EP core file to drivers/pci/endpoint/pci-ep-doe.c, and
corresponding Kconfig and Makefile to match the existing naming scheme,
as suggested by Niklas Cassel.
- Renamed the config from PCI_DOE_EP to PCI_ENDPOINT_DOE
- Moved the function declarations that need not be visible outside the
PCI core to drivers/pci/pci.h instead to include/linux/pci-doe.h as
suggested by Lukas Wunner
- Converted from synchronous to asynchronous request processing:
* Removed wait_for_completion() from pci_ep_doe_process_request()
* Function returns immediately after queuing to workqueue, hence
removed private data for completion in the task structure
* Added completion callback as an additional argument to
pci_ep_doe_process_request(), which takes the response and status
parameters as arguments (along with other required arguments), hence
removed task_status in the task structure
* Created a typedef pci_ep_doe_complete_t for completion callback
* Removed the pci_ep_doe_task_complete() function, as it would not be
required anymore with these changes
* Moved from INIT_WORK_ONSTACK() to INIT_WORK(), to initialize the work
on heap instead of stack
* signal_task_complete() now invokes the completion callback, once the
protocol handler completes its task
- Changed from dynamic xarray-based protocol registration to static array:
* Removed the register/unregister protocol APIs
* Replaced the dynamic xarray with static array of struct pci_doe_protocol
* Added discovery protocol to static array, instead of treating it specially,
hence removed the special handling for Discovery protocol in
doe_ep_task_work()
* Updated pci_ep_doe_handle_discovery() and pci_ep_doe_find_protocol()
accordingly.
- Memory Management:
* DOE core frees request buffer in signal_task_complete()
or during error handling
* pci_ep_doe_process_request() defines response_pl and response_pl_sz
as NULL and 0 respectively, whose pointer is passed to the protocol
handler, hence removed the arguments void **response, size_t *response_sz
to this function.
- Task structure refactoring:
* Response buffer: void **response_pl to void *response_pl
* Response size: size_t *response_pl_sz to size_t response_pl_sz
* Changed the completion callback to type pci_ep_doe_complete_t
* Removed void *private and int task_status
- Updated documentation comments of the functions according to the changes
v2: https://lore.kernel.org/all/20260401073022.215805-3-a-garg7@ti.com/
v1: https://lore.kernel.org/all/20260213123603.420941-4-a-garg7@ti.com/
drivers/pci/endpoint/Kconfig | 14 +
drivers/pci/endpoint/Makefile | 1 +
drivers/pci/endpoint/pci-ep-doe.c | 552 ++++++++++++++++++++++++++++++
drivers/pci/pci.h | 38 ++
include/linux/pci-doe.h | 5 +
include/linux/pci-epc.h | 3 +
6 files changed, 613 insertions(+)
create mode 100644 drivers/pci/endpoint/pci-ep-doe.c
diff --git a/drivers/pci/endpoint/Kconfig b/drivers/pci/endpoint/Kconfig
index 8dad291be8b8..15ae16aaa58f 100644
--- a/drivers/pci/endpoint/Kconfig
+++ b/drivers/pci/endpoint/Kconfig
@@ -36,6 +36,20 @@ config PCI_ENDPOINT_MSI_DOORBELL
doorbell. The RC can trigger doorbell in EP by writing data to a
dedicated BAR, which the EP maps to the controller's message address.
+config PCI_ENDPOINT_DOE
+ bool "PCI Endpoint Data Object Exchange (DOE) support"
+ depends on PCI_ENDPOINT
+ help
+ This enables support for Data Object Exchange (DOE) protocol
+ on PCI Endpoint controllers. It provides a communication
+ mechanism through mailboxes, primarily used for PCIe security
+ features.
+
+ Say Y here if you want be able to communicate using PCIe DOE
+ mailboxes.
+
+ If unsure, say N.
+
source "drivers/pci/endpoint/functions/Kconfig"
endmenu
diff --git a/drivers/pci/endpoint/Makefile b/drivers/pci/endpoint/Makefile
index b4869d52053a..1fa176b6792b 100644
--- a/drivers/pci/endpoint/Makefile
+++ b/drivers/pci/endpoint/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_PCI_ENDPOINT_CONFIGFS) += pci-ep-cfs.o
obj-$(CONFIG_PCI_ENDPOINT) += pci-epc-core.o pci-epf-core.o\
pci-epc-mem.o functions/
obj-$(CONFIG_PCI_ENDPOINT_MSI_DOORBELL) += pci-ep-msi.o
+obj-$(CONFIG_PCI_ENDPOINT_DOE) += pci-ep-doe.o
diff --git a/drivers/pci/endpoint/pci-ep-doe.c b/drivers/pci/endpoint/pci-ep-doe.c
new file mode 100644
index 000000000000..ded0290b15ed
--- /dev/null
+++ b/drivers/pci/endpoint/pci-ep-doe.c
@@ -0,0 +1,552 @@
+// SPDX-License-Identifier: GPL-2.0-only or MIT
+/*
+ * Data Object Exchange for PCIe Endpoint
+ * PCIe r7.0, sec 6.30 DOE
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com
+ * Aksh Garg <a-garg7@ti.com>
+ * Siddharth Vadapalli <s-vadapalli@ti.com>
+ */
+
+#define dev_fmt(fmt) "DOE EP: " fmt
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/pci-epc.h>
+#include <linux/pci-doe.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/xarray.h>
+
+#include "../pci.h"
+
+/* Forward declaration of discovery protocol handler */
+static int pci_ep_doe_handle_discovery(const void *request, size_t request_sz,
+ void **response, size_t *response_sz);
+
+/**
+ * struct pci_doe_protocol - DOE protocol handler entry
+ * @vid: Vendor ID
+ * @type: Protocol type
+ * @handler: Handler function pointer
+ */
+struct pci_doe_protocol {
+ u16 vid;
+ u8 type;
+ pci_doe_protocol_handler_t handler;
+};
+
+/**
+ * struct pci_ep_doe_mb - State for a single DOE mailbox on EP
+ *
+ * This state is used to manage a single DOE mailbox capability on the
+ * endpoint side.
+ *
+ * @epc: PCI endpoint controller this mailbox belongs to
+ * @func_no: Physical function number of the function this mailbox belongs to
+ * @cap_offset: Capability offset
+ * @work_queue: Queue of work items
+ * @flags: Bit array of PCI_DOE_FLAG_* flags
+ */
+struct pci_ep_doe_mb {
+ struct pci_epc *epc;
+ u8 func_no;
+ u16 cap_offset;
+ struct workqueue_struct *work_queue;
+ unsigned long flags;
+};
+
+/**
+ * struct pci_ep_doe_task - Represents a single DOE request/response task
+ *
+ * @feat: DOE feature (vendor ID and type)
+ * @request_pl: Request payload
+ * @request_pl_sz: Size of request payload in bytes
+ * @response_pl: Response buffer
+ * @response_pl_sz: Size of response buffer in bytes
+ * @complete: Completion callback
+ * @work: Work structure for workqueue
+ * @doe_mb: DOE mailbox handling this task
+ */
+struct pci_ep_doe_task {
+ struct pci_doe_feature feat;
+ const void *request_pl;
+ size_t request_pl_sz;
+ void *response_pl;
+ size_t response_pl_sz;
+ pci_ep_doe_complete_t complete;
+
+ /* Initialized by pci_ep_doe_submit_task() */
+ struct work_struct work;
+ struct pci_ep_doe_mb *doe_mb;
+};
+
+/*
+ * Global registry of protocol handlers.
+ * When a new DOE protocol, library is added, add an entry to this array.
+ */
+static const struct pci_doe_protocol pci_doe_protocols[] = {
+ {
+ .vid = PCI_VENDOR_ID_PCI_SIG,
+ .type = PCI_DOE_FEATURE_DISCOVERY,
+ .handler = pci_ep_doe_handle_discovery,
+ },
+};
+
+/*
+ * Combines function number and capability offset into a unique lookup key
+ * for storing/retrieving DOE mailboxes in an xarray.
+ */
+#define PCI_DOE_MB_KEY(func, offset) \
+ (((unsigned long)(func) << 16) | (offset))
+#define PCI_DOE_PROTOCOL_COUNT ARRAY_SIZE(pci_doe_protocols)
+
+/**
+ * pci_ep_doe_init() - Initialize the DOE framework for a controller in EP mode
+ * @epc: PCI endpoint controller
+ *
+ * Initialize the DOE framework data structures. This only initializes
+ * the xarray that will hold the mailboxes.
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+int pci_ep_doe_init(struct pci_epc *epc)
+{
+ if (!epc)
+ return -EINVAL;
+
+ xa_init(&epc->doe_mbs);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_init);
+
+/**
+ * pci_ep_doe_add_mailbox() - Add a DOE mailbox for a physical function
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: Offset of the DOE capability
+ *
+ * Create and register a DOE mailbox for the specified physical function
+ * and capability offset.
+ *
+ * EPC core driver calls this for each DOE capability discovered in the config
+ * space of each endpoint function through an API. The API is invoked by the
+ * controller driver during initialization if DOE support is available.
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset)
+{
+ struct pci_ep_doe_mb *doe_mb;
+ unsigned long key;
+ int ret;
+
+ if (!epc)
+ return -EINVAL;
+
+ doe_mb = kzalloc_obj(*doe_mb, GFP_KERNEL);
+ if (!doe_mb)
+ return -ENOMEM;
+
+ doe_mb->epc = epc;
+ doe_mb->func_no = func_no;
+ doe_mb->cap_offset = cap_offset;
+
+ doe_mb->work_queue = alloc_ordered_workqueue("pci_ep_doe[%s:pf%d:offset%x]", 0,
+ dev_name(&epc->dev),
+ func_no, cap_offset);
+ if (!doe_mb->work_queue) {
+ dev_err(epc->dev.parent,
+ "[pf%d:offset%x] failed to allocate work queue\n",
+ func_no, cap_offset);
+ ret = -ENOMEM;
+ goto err_free;
+ }
+
+ /* Add to xarray with composite key */
+ key = PCI_DOE_MB_KEY(func_no, cap_offset);
+ ret = xa_insert(&epc->doe_mbs, key, doe_mb, GFP_KERNEL);
+ if (ret) {
+ dev_err(epc->dev.parent,
+ "[pf%d:offset%x] failed to insert mailbox: %d\n",
+ func_no, cap_offset, ret);
+ goto err_destroy;
+ }
+
+ dev_dbg(epc->dev.parent,
+ "DOE mailbox added: pf%d offset 0x%x\n",
+ func_no, cap_offset);
+
+ return 0;
+
+err_destroy:
+ destroy_workqueue(doe_mb->work_queue);
+err_free:
+ kfree(doe_mb);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_add_mailbox);
+
+/**
+ * pci_ep_doe_cancel_tasks() - Cancel all pending tasks
+ * @doe_mb: DOE mailbox
+ *
+ * Cancel all pending tasks in the mailbox. Mark the mailbox as dead
+ * so no new tasks can be submitted.
+ */
+static void pci_ep_doe_cancel_tasks(struct pci_ep_doe_mb *doe_mb)
+{
+ if (!doe_mb)
+ return;
+
+ /* Mark the mailbox as dead */
+ set_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags);
+
+ /* Stop all pending work items from starting */
+ set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
+}
+
+/**
+ * pci_ep_doe_get_mailbox() - Get DOE mailbox by function and offset
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: Offset of the DOE capability
+ *
+ * Internal helper to look up a DOE mailbox by its function number and
+ * capability offset.
+ *
+ * RETURNS: Pointer to the mailbox or NULL if not found
+ */
+static struct pci_ep_doe_mb *pci_ep_doe_get_mailbox(struct pci_epc *epc,
+ u8 func_no, u16 cap_offset)
+{
+ unsigned long key;
+
+ if (!epc)
+ return NULL;
+
+ key = PCI_DOE_MB_KEY(func_no, cap_offset);
+ return xa_load(&epc->doe_mbs, key);
+}
+
+/**
+ * pci_ep_doe_find_protocol() - Find protocol handler in static array
+ * @vendor: Vendor ID
+ * @type: Protocol type
+ *
+ * Look up a protocol handler in the static protocol array by matching vendor ID
+ * and protocol type.
+ *
+ * RETURNS: Handler function pointer or NULL if not found
+ */
+static pci_doe_protocol_handler_t pci_ep_doe_find_protocol(u16 vendor, u8 type)
+{
+ int i;
+
+ /* Search static protocol array */
+ for (i = 0; i < PCI_DOE_PROTOCOL_COUNT; i++) {
+ if (pci_doe_protocols[i].vid == vendor &&
+ pci_doe_protocols[i].type == type)
+ return pci_doe_protocols[i].handler;
+ }
+
+ return NULL;
+}
+
+/**
+ * pci_ep_doe_handle_discovery() - Handle Discovery protocol request
+ * @request: Request payload
+ * @request_sz: Request size
+ * @response: Output pointer for response buffer
+ * @response_sz: Output pointer for response size
+ *
+ * Handle the DOE Discovery protocol. The request contains an index specifying
+ * which protocol to query. This function creates a response containing the
+ * vendor ID and protocol type for the requested index, along with the next
+ * index value for further discovery:
+ *
+ * - next_index = 0: Signals this is the last protocol supported
+ * - next_index = n (non-zero): Signals more protocols available,
+ * query index n next
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+static int pci_ep_doe_handle_discovery(const void *request, size_t request_sz,
+ void **response, size_t *response_sz)
+{
+ struct pci_doe_protocol protocol;
+ u8 requested_index, next_index;
+ u32 *response_pl;
+ u32 request_pl;
+ u16 vendor;
+ u8 type;
+
+ if (request_sz != sizeof(u32))
+ return -EINVAL;
+
+ request_pl = *(u32 *)request;
+ requested_index = FIELD_GET(PCI_DOE_DATA_OBJECT_DISC_REQ_3_INDEX, request_pl);
+
+ if (requested_index >= PCI_DOE_PROTOCOL_COUNT)
+ return -EINVAL;
+
+ /* Get protocol from array at requested_index */
+ protocol = pci_doe_protocols[requested_index];
+ vendor = protocol.vid;
+ type = protocol.type;
+
+ /* Calculate next index */
+ next_index = (requested_index + 1 < PCI_DOE_PROTOCOL_COUNT) ? requested_index + 1 : 0;
+
+ response_pl = kzalloc_obj(*response_pl, GFP_KERNEL);
+ if (!response_pl)
+ return -ENOMEM;
+
+ /* Build response */
+ *response_pl = FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID, vendor) |
+ FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_TYPE, type) |
+ FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX, next_index);
+
+ *response = response_pl;
+ *response_sz = sizeof(*response_pl);
+
+ return 0;
+}
+
+static void signal_task_complete(struct pci_ep_doe_task *task, int status)
+{
+ kfree(task->request_pl);
+ task->complete(task->doe_mb->func_no, task->doe_mb->cap_offset, status,
+ task->feat.vid, task->feat.type,
+ task->response_pl, task->response_pl_sz);
+ kfree(task);
+}
+
+/**
+ * doe_ep_task_work() - Work function for processing DOE EP tasks
+ * @work: Work structure
+ *
+ * Process a DOE request by calling the appropriate protocol handler.
+ */
+static void doe_ep_task_work(struct work_struct *work)
+{
+ struct pci_ep_doe_task *task = container_of(work, struct pci_ep_doe_task,
+ work);
+ struct pci_ep_doe_mb *doe_mb = task->doe_mb;
+ pci_doe_protocol_handler_t handler;
+ int rc;
+
+ if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags)) {
+ signal_task_complete(task, -EIO);
+ return;
+ }
+
+ /* Check if request was aborted */
+ if (test_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags)) {
+ signal_task_complete(task, -ECANCELED);
+ return;
+ }
+
+ /* Find protocol handler in the array */
+ handler = pci_ep_doe_find_protocol(task->feat.vid, task->feat.type);
+ if (!handler) {
+ dev_warn(doe_mb->epc->dev.parent,
+ "[%d:%x] Unsupported protocol VID=%04x TYPE=%02x\n",
+ doe_mb->func_no, doe_mb->cap_offset,
+ task->feat.vid, task->feat.type);
+ signal_task_complete(task, -EOPNOTSUPP);
+ return;
+ }
+
+ /* Call protocol handler */
+ rc = handler(task->request_pl, task->request_pl_sz,
+ &task->response_pl, &task->response_pl_sz);
+
+ signal_task_complete(task, rc);
+}
+
+/**
+ * pci_ep_doe_submit_task() - Submit a task to be processed
+ * @doe_mb: DOE mailbox
+ * @task: Task to submit
+ *
+ * Submit a DOE task to the workqueue for asynchronous processing.
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+static int pci_ep_doe_submit_task(struct pci_ep_doe_mb *doe_mb,
+ struct pci_ep_doe_task *task)
+{
+ if (test_bit(PCI_DOE_FLAG_DEAD, &doe_mb->flags))
+ return -EIO;
+
+ task->doe_mb = doe_mb;
+ INIT_WORK(&task->work, doe_ep_task_work);
+ queue_work(doe_mb->work_queue, &task->work);
+ return 0;
+}
+
+/**
+ * pci_ep_doe_process_request() - Process DOE request on endpoint
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: DOE capability offset
+ * @vendor: Vendor ID from request header
+ * @type: Protocol type from request header
+ * @request: Request payload in CPU-native format
+ * @request_sz: Size of request payload (bytes)
+ * @complete: Callback to invoke upon completion
+ *
+ * Asynchronously process a DOE request received on the endpoint. The request
+ * payload should not include the DOE header (vendor/type/length). The protocol
+ * handler will allocate the response buffer, which the caller (controller driver)
+ * must free after use.
+ *
+ * This function returns immediately after queuing the request. The completion
+ * callback will be invoked asynchronously from workqueue context once the
+ * request is processed. The callback receives the function number and capability
+ * offset to identify the mailbox, along with a status code (0 on success, -errno
+ * on failure), and other required arguments.
+ *
+ * As per DOE specification, a mailbox processes one request at a time.
+ * Therefore, this function will never be called concurrently for the same
+ * mailbox by different callers.
+ *
+ * The caller is responsible for the conversion of the received DOE request
+ * with le32_to_cpu() before calling this function.
+ * Similarly, it is responsible for converting the response payload with
+ * cpu_to_le32() before sending it back over the DOE mailbox.
+ *
+ * The caller is also responsible for ensuring that the request size
+ * is within the limits defined by PCI_DOE_MAX_LENGTH.
+ *
+ * RETURNS: 0 if the request was successfully queued, -errno on failure
+ */
+int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
+ u16 vendor, u8 type, const void *request, size_t request_sz,
+ pci_ep_doe_complete_t complete)
+{
+ struct pci_ep_doe_mb *doe_mb;
+ struct pci_ep_doe_task *task;
+ int rc;
+
+ doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
+ if (!doe_mb) {
+ kfree(request);
+ return -ENODEV;
+ }
+
+ task = kzalloc_obj(*task, GFP_KERNEL);
+ if (!task) {
+ kfree(request);
+ return -ENOMEM;
+ }
+
+ task->feat.vid = vendor;
+ task->feat.type = type;
+ task->request_pl = request;
+ task->request_pl_sz = request_sz;
+ task->response_pl = NULL;
+ task->response_pl_sz = 0;
+ task->complete = complete;
+
+ rc = pci_ep_doe_submit_task(doe_mb, task);
+ if (rc) {
+ kfree(request);
+ kfree(task);
+ return rc;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_process_request);
+
+/**
+ * pci_ep_doe_abort() - Abort DOE operations on a mailbox
+ * @epc: PCI endpoint controller
+ * @func_no: Physical function number
+ * @cap_offset: DOE capability offset
+ *
+ * Abort all queued and wait for in-flight DOE operations to complete for the
+ * specified mailbox. This function is called by the EP controller driver
+ * when the RC sets the ABORT bit in the DOE Control register.
+ *
+ * The function will:
+ *
+ * - Set CANCEL flag to prevent new requests in the queue from starting
+ * - Wait for the currently executing handler to complete (cannot interrupt)
+ * - Flush the workqueue to wait for all requests to be handled appropriately
+ * - Clear CANCEL flag to prepare for new requests
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset)
+{
+ struct pci_ep_doe_mb *doe_mb;
+
+ if (!epc)
+ return -EINVAL;
+
+ doe_mb = pci_ep_doe_get_mailbox(epc, func_no, cap_offset);
+ if (!doe_mb)
+ return -ENODEV;
+
+ /* Set CANCEL flag - worker will abort queued requests */
+ set_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
+ flush_workqueue(doe_mb->work_queue);
+
+ /* Clear CANCEL flag - mailbox ready for new requests */
+ clear_bit(PCI_DOE_FLAG_CANCEL, &doe_mb->flags);
+
+ dev_dbg(epc->dev.parent,
+ "DOE mailbox aborted: PF%d offset 0x%x\n",
+ func_no, cap_offset);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_abort);
+
+/**
+ * pci_ep_doe_destroy_mb() - Destroy a single DOE mailbox
+ * @doe_mb: DOE mailbox to destroy
+ *
+ * Internal function to destroy a mailbox and free its resources.
+ */
+static void pci_ep_doe_destroy_mb(struct pci_ep_doe_mb *doe_mb)
+{
+ if (!doe_mb)
+ return;
+
+ pci_ep_doe_cancel_tasks(doe_mb);
+
+ if (doe_mb->work_queue)
+ destroy_workqueue(doe_mb->work_queue);
+
+ kfree(doe_mb);
+}
+
+/**
+ * pci_ep_doe_destroy() - Destroy all DOE mailboxes
+ * @epc: PCI endpoint controller
+ *
+ * Destroy all DOE mailboxes and free associated resources.
+ *
+ * The EPC core driver calls this through an API, invoked by the controller
+ * driver during controller cleanup to free all DOE resources,
+ * if DOE support is available.
+ */
+void pci_ep_doe_destroy(struct pci_epc *epc)
+{
+ struct pci_ep_doe_mb *doe_mb;
+ unsigned long index;
+
+ if (!epc)
+ return;
+
+ xa_for_each(&epc->doe_mbs, index, doe_mb)
+ pci_ep_doe_destroy_mb(doe_mb);
+
+ xa_destroy(&epc->doe_mbs);
+}
+EXPORT_SYMBOL_GPL(pci_ep_doe_destroy);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 5844deee2b5f..f7766cbedbaf 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -692,6 +692,12 @@ struct pci_doe_feature {
u8 type;
};
+struct pci_epc;
+
+typedef void (*pci_ep_doe_complete_t)(u8 func_no, u16 cap_offset, int status,
+ u16 vendor, u8 type, void *response_pl,
+ size_t response_pl_sz);
+
#ifdef CONFIG_PCI_DOE
void pci_doe_init(struct pci_dev *pdev);
void pci_doe_destroy(struct pci_dev *pdev);
@@ -702,6 +708,38 @@ static inline void pci_doe_destroy(struct pci_dev *pdev) { }
static inline void pci_doe_disconnected(struct pci_dev *pdev) { }
#endif
+#ifdef CONFIG_PCI_ENDPOINT_DOE
+int pci_ep_doe_init(struct pci_epc *epc);
+int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no, u16 cap_offset);
+int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no, u16 cap_offset,
+ u16 vendor, u8 type, const void *request,
+ size_t request_sz, pci_ep_doe_complete_t complete);
+int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset);
+void pci_ep_doe_destroy(struct pci_epc *epc);
+#else
+static inline int pci_ep_doe_init(struct pci_epc *epc) { return -EOPNOTSUPP; }
+static inline int pci_ep_doe_add_mailbox(struct pci_epc *epc, u8 func_no,
+ u16 cap_offset)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pci_ep_doe_process_request(struct pci_epc *epc, u8 func_no,
+ u16 cap_offset, u16 vendor, u8 type,
+ const void *request, size_t request_sz,
+ pci_ep_doe_complete_t complete)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pci_ep_doe_abort(struct pci_epc *epc, u8 func_no, u16 cap_offset)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void pci_ep_doe_destroy(struct pci_epc *epc) { }
+#endif
+
#ifdef CONFIG_PCI_NPEM
void pci_npem_create(struct pci_dev *dev);
void pci_npem_remove(struct pci_dev *dev);
diff --git a/include/linux/pci-doe.h b/include/linux/pci-doe.h
index abb9b7ae8029..c46e42f3ce78 100644
--- a/include/linux/pci-doe.h
+++ b/include/linux/pci-doe.h
@@ -22,6 +22,11 @@ struct pci_doe_mb;
/* Max data object length is 2^18 dwords */
#define PCI_DOE_MAX_LENGTH (1 << 18)
+typedef int (*pci_doe_protocol_handler_t)(const void *request,
+ size_t request_sz,
+ void **response,
+ size_t *response_sz);
+
struct pci_doe_mb *pci_find_doe_mailbox(struct pci_dev *pdev, u16 vendor,
u8 type);
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 1eca1264815b..dd26294c8175 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -182,6 +182,9 @@ struct pci_epc {
unsigned long function_num_map;
int domain_nr;
bool init_complete;
+#ifdef CONFIG_PCI_ENDPOINT_DOE
+ struct xarray doe_mbs;
+#endif
};
/**
--
2.34.1
^ permalink raw reply related
* [PATCH v3 4/4] Documentation: PCI: Add documentation for DOE endpoint support
From: Aksh Garg @ 2026-04-27 5:17 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, s-vadapalli, danishanwar, srk,
a-garg7
In-Reply-To: <20260427051725.223704-1-a-garg7@ti.com>
Document the architecture and implementation details for the Data Object
Exchange (DOE) framework for PCIe Endpoint devices.
Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes since v1:
- Squashed the patches [1] and [2], and moved the documentation file
to Documentation/PCI/endpoint/pci-endpoint-doe.rst to match the existing
naming scheme, as suggested by Niklas Cassel
- Updated the documentation as per the design and implementaion changes
made to previous patches in this series:
* Updated for static protocol array instead of dynamic registration
* Documented asynchronous callback model
* Updated request/response flow with new callback signature
* Updated memory ownership: DOE core frees request, driver frees response
* Updated initialization and cleanup sections for new APIs
v2: https://lore.kernel.org/all/20260401073022.215805-5-a-garg7@ti.com/
v1: [1] https://lore.kernel.org/all/20260213123603.420941-2-a-garg7@ti.com/
[2] https://lore.kernel.org/all/20260213123603.420941-5-a-garg7@ti.com/
Documentation/PCI/endpoint/index.rst | 1 +
.../PCI/endpoint/pci-endpoint-doe.rst | 318 ++++++++++++++++++
2 files changed, 319 insertions(+)
create mode 100644 Documentation/PCI/endpoint/pci-endpoint-doe.rst
diff --git a/Documentation/PCI/endpoint/index.rst b/Documentation/PCI/endpoint/index.rst
index dd1f62e731c9..7c03d5abd2ef 100644
--- a/Documentation/PCI/endpoint/index.rst
+++ b/Documentation/PCI/endpoint/index.rst
@@ -9,6 +9,7 @@ PCI Endpoint Framework
pci-endpoint
pci-endpoint-cfs
+ pci-endpoint-doe
pci-test-function
pci-test-howto
pci-ntb-function
diff --git a/Documentation/PCI/endpoint/pci-endpoint-doe.rst b/Documentation/PCI/endpoint/pci-endpoint-doe.rst
new file mode 100644
index 000000000000..03b7a69516f3
--- /dev/null
+++ b/Documentation/PCI/endpoint/pci-endpoint-doe.rst
@@ -0,0 +1,318 @@
+.. SPDX-License-Identifier: GPL-2.0-only or MIT
+
+.. include:: <isonum.txt>
+
+=============================================
+Data Object Exchange (DOE) for PCIe Endpoint
+=============================================
+
+:Copyright: |copy| 2026 Texas Instruments Incorporated
+:Author: Aksh Garg <a-garg7@ti.com>
+:Co-Author: Siddharth Vadapalli <s-vadapalli@ti.com>
+
+Overview
+========
+
+DOE (Data Object Exchange) is a standard PCIe extended capability feature
+introduced in the Data Object Exchange (DOE) ECN for PCIe r5.0. It is an optional
+mechanism for system firmware/software running on root complex (host) to perform
+:ref:`data object <data-object-term>` exchanges with an endpoint function. Each
+data object is uniquely identified by the Vendor ID of the vendor publishing the
+data object definition and a Data Object Type value assigned by that vendor.
+
+Think of DOE as a sophisticated mailbox system built into PCIe. The root complex
+can send structured requests to the endpoint device through DOE mailboxes, and
+the endpoint device responds with appropriate data. DOE mailboxes are implemented
+as PCIe Extended Capabilities in endpoint devices, allowing multiple mailboxes
+per function, each potentially supporting different data object protocols.
+
+The DOE support for root complex devices has already been implemented in
+``drivers/pci/doe.c``.
+
+How DOE Works
+=============
+
+The DOE mailbox operates through a simple request-response model:
+
+1. **Host sends request**: The root complex writes a data object (vendor ID, type,
+ and payload) to the DOE write mailbox register (one DWORD at a time) of the
+ endpoint function's config space and sets the GO bit in the DOE Status register
+ to indicate that a request is ready for processing.
+2. **Endpoint processes**: The endpoint function reads the request from DOE write
+ mailbox register, sets the BUSY bit in the DOE Status register, identifies the
+ protocol of the data object, and executes the appropriate handler.
+3. **Endpoint responds**: The endpoint function writes the response data object to the
+ DOE read mailbox register (one DWORD at a time), and sets the READY bit in the DOE
+ Status register to indicate that the response is ready. If an error occurs during
+ request processing (such as unsupported protocol or handler failure), the endpoint
+ sets the ERROR bit in the DOE Status register instead of the READY bit.
+4. **Host reads response**: The root complex retrieves the response data from the DOE read
+ mailbox register once the READY bit is set in the DOE Status register, and then writes
+ any value to this register to indicate a successful read. If the ERROR bit was set,
+ the root complex discards the response and performs error handling as needed.
+
+Each mailbox operates independently and can handle one transaction at a time. The
+DOE specification supports data objects of size up to 256KB (2\ :sup:`18` dwords).
+
+For complete DOE capability details, refer to `PCI Express Base Specification Revision 7.0,
+Section 6.30 - Data Object Exchange (DOE)`.
+
+Key Terminologies
+=================
+
+.. _data-object-term:
+
+**Data Object**
+ A structured, vendor-defined, or standard-defined message exchanged between
+ root complex and endpoint function via DOE capability registers in configuration
+ space of the function.
+
+**Mailbox**
+ A DOE capability on the endpoint device, where each physical function can have
+ multiple mailboxes.
+
+**Protocol**
+ A specific type of DOE communication data object identified by a Vendor ID and Type.
+
+**Handler**
+ A function that processes DOE requests of a specific protocol and generates responses.
+
+Architecture of DOE Implementation for Endpoint
+===============================================
+
+.. code-block:: text
+
+ +------------------+
+ | |
+ | Root Complex |
+ | |
+ +--------^---------+
+ |
+ | Config space access
+ | over PCIe link
+ |
+ +----------v-----------+
+ | |
+ | PCIe Controller |
+ | as Endpoint |
+ | |
+ | +-----------------+ |
+ | | DOE Mailbox | |
+ | +-------^---------+ |
+ +----------|-----------+
+ +-----------|---------------------------------------------------------------+
+ | | +--------------------+ |
+ | +---------v--------+ Allocate | +--------------+ | |
+ | | |-------------------------------->| Request | | |
+ | | EP Controller | +--->| Buffer | | |
+ | | Driver | Free | | +--------------+ | |
+ | | |--------------------------+ | | | |
+ | +--------^---------+ | | | | |
+ | | | | | | |
+ | | | | | | |
+ | | pci_ep_doe_process_request() | | | | |
+ | | | | | | |
+ | +--------v---------+ Free | | | | |
+ | | |----------------------------+ | DDR | |
+ | | DOE EP Core |<----+ | | | |
+ | | (doe-ep.c) | | Discovery | | | |
+ | | |-----+ Protocol Handler | | | |
+ | +--------^---------+ | | | |
+ | | | | | |
+ | | protocol_handler() | | | |
+ | | | | | |
+ | +--------v---------+ | | | |
+ | | | | | +--------------+ | |
+ | | Protocol Handler | +----->| Response | | |
+ | | Module |-------------------------------->| Buffer | | |
+ | | (CMA/SPDM/Other) | Allocate | +--------------+ | |
+ | | | | | |
+ | +------------------+ | | |
+ | +--------------------+ |
+ +---------------------------------------------------------------------------+
+
+Initialization and Cleanup
+--------------------------
+
+**Framework Initialization and DOE Setup**
+
+The EPC core provides the ``pci_epc_doe_setup(epc)`` API for centralized DOE
+mailbox discovery and registration. The controller driver calls this API during
+its probe sequence if DOE is supported.
+
+This API performs the following steps:
+
+1. Calls ``pci_ep_doe_init(epc)``, which initializes the xarray data structure
+ (a resizable array data structure defined in linux) named ``doe_mbs`` that
+ stores metadata of DOE mailboxes for the controller in ``struct pci_epc``.
+2. Discovers all DOE capabilities in the endpoint function's configuration space
+ for each function. For each discovered DOE capability, calls
+ ``pci_ep_doe_add_mailbox(epc, func_no, cap_offset)`` to register the mailbox.
+
+Each DOE mailbox structure created by ``pci_ep_doe_add_mailbox()`` gets an
+ordered workqueue allocated for processing DOE requests sequentially for that
+mailbox, enabling concurrent request handling across different mailboxes. Each
+mailbox is uniquely identified by the combination of physical function number
+and capability offset for that controller.
+
+**Cleanup**
+
+The EPC core provides the ``pci_epc_doe_destroy(epc)`` API for centralized DOE
+cleanup. The controller driver calls this API during its remove sequence
+if DOE is supported.
+
+This API calls ``pci_ep_doe_destroy(epc)``, which destroys all registered
+mailboxes, cancels any pending tasks, flushes and destroys the workqueues,
+and frees all memory allocated to the mailboxes.
+
+Protocol Handler Support
+------------------------
+
+Protocol implementations (such as CMA, SPDM, or vendor-specific protocols) are
+supported through a static array of protocol handlers.
+
+When a new DOE protocol library is introduced, its handler function is added to
+the static ``pci_doe_protocols`` array in ``drivers/pci/endpoint/pci-ep-doe.c``.
+The discovery protocol (VID = 0x0001 (PCI-SIG vendor ID), Type = 0x00 (discovery
+protocol)) is included in this static array and handled internally by the
+DOE EP core.
+
+Request Handling
+----------------
+
+The complete flow of a DOE request from the root complex to the response:
+
+**Step 1: Root Complex → EP Controller Driver**
+
+The root complex writes a DOE request (Vendor ID, Type, and Payload) to the
+DOE write mailbox register in the endpoint function's configuration space and sets
+the GO bit in the DOE Control register, indicating that the request is ready for
+processing.
+
+**Step 2: EP Controller Driver → DOE EP Core**
+
+The controller driver reads the request header to determine the data object
+length. Based on this length field, it allocates a request buffer in memory
+(DDR) of the appropriate size. The driver then reads the complete request
+payload from the DOE write mailbox register and converts the data from
+little-endian format (the format followed in the PCIe transactions over the
+link) to CPU-native format using ``le32_to_cpu()``. The driver defines a
+completion callback function with signature ``void (*complete)(u8 func_no,
+u16 cap_offset, int status, u16 vendor, u8 type, void *response_pl,
+size_t response_pl_sz)`` to be invoked when the request processing completes.
+The driver then calls ``pci_ep_doe_process_request(epc, func_no, cap_offset,
+vendor, type, request, request_sz, complete)`` to hand off the request to the
+DOE EP core. This function returns immediately after queuing the work
+(without blocking), and the driver sets the BUSY bit in the DOE Status register.
+
+**Step 3: DOE EP Core Processing**
+
+The DOE EP core creates a task structure and submits it to the mailbox's ordered
+workqueue. This ensures that requests for each mailbox are processed
+sequentially, one at a time, as required by the DOE specification. It looks up
+the protocol handler based on the Vendor ID and Type from the request header,
+and executes the handler function.
+
+**Step 4: Protocol Handler Execution**
+
+The workqueue executes the task by calling the registered protocol handler:
+``handler(request, request_sz, &response, &response_sz)``. The handler processes
+the request, allocates a response buffer in memory (DDR), builds the response
+data, and returns the response pointer and size. For the discovery protocol,
+the DOE EP core handles this directly without invoking an external handler.
+
+**Step 5: DOE EP Core → EP Controller Driver**
+
+After the protocol handler completes, the DOE EP core frees the request buffer,
+and invokes the completion callback provided by the controller driver asynchronously.
+The callback receives the function number, capability offset (to identify the mailbox),
+status code indicating the result of request processing, vendor ID and type of the data
+object, the response buffer, and its size.
+
+**Step 6: EP Controller Driver → Root Complex**
+
+The controller driver converts the response from CPU-native format to
+little-endian format using ``cpu_to_le32()``, writes the response to DOE read
+mailbox register, and sets the READY bit in the DOE Status register. The root
+complex then reads the response from the read mailbox register. Finally, the controller
+driver frees the response buffer (which the handler allocated).
+
+Asynchronous Request Processing
+-------------------------------
+
+The DOE-EP framework implements asynchronous request processing because an
+endpoint function can have multiple instances of DOE mailboxes, and requests may
+be interleaved across these mailboxes. Request processing of one mailbox should
+not result in blocking request processing of other mailboxes. Hence, requests
+on each mailbox need to be handled in parallel for optimization.
+
+For the EP controller driver to handle requests on multiple mailboxes in
+parallel, ``pci_ep_doe_process_request()`` must be asynchronous. The function
+returns immediately after submitting the request to the mailbox's workqueue,
+without waiting for the request to complete. A completion callback provided by
+the controller driver is invoked asynchronously when request processing
+finishes. This asynchronous design enables concurrent processing of requests
+across different mailboxes.
+
+Abort Handling
+--------------
+
+The DOE specification allows the root complex to abort ongoing DOE operations
+by setting the ABORT bit in the DOE Control register.
+
+**Trigger**
+
+When the root complex sets the ABORT bit, the EP controller driver detects this
+condition (typically in an interrupt handler or register polling routine). The
+action taken depends on the timing of the abort:
+
+- **ABORT during request transfer**: If the ABORT bit is set while the root complex
+ is still transferring the request to the mailbox registers, the controller driver
+ discards the request and no call to ``pci_ep_doe_abort()`` is needed.
+
+- **ABORT after request submission**: If the ABORT bit is set after the request
+ has been fully received and submitted to the DOE EP core via
+ ``pci_ep_doe_process_request()``, the controller driver must call
+ ``pci_ep_doe_abort(epc, func_no, cap_offset)`` for the affected mailbox to
+ perform abort sequence in the DOE EP core.
+
+**Abort Sequence**
+
+The abort function performs the following actions:
+
+1. Sets the CANCEL flag on the mailbox to prevent queued requests from starting
+2. Flushes the workqueue to wait for any currently executing handler to complete
+ (handlers cannot be interrupted mid-execution)
+3. Clears the CANCEL flag to allow the mailbox to accept new requests
+
+Queued requests that have not started execution will be aborted with an error
+status. The currently executing request will complete normally, and the controller
+will reject the response if it arrives after the abort sequence has been triggered.
+
+.. note::
+ Independent of when the ABORT bit is triggered, the controller driver must
+ clear the ERROR, BUSY, and READY bits in the DOE Status register after
+ completing the abort operation to reset the mailbox to an idle state.
+
+Error Handling
+--------------
+
+Errors can occur during DOE request processing for various reasons, such as
+unsupported protocols, handler failures, or memory allocation failures.
+
+**Error Detection**
+
+When an error occurs during DOE request processing, the DOE EP core propagates this error
+back to the controller driver either through the ``pci_ep_doe_process_request()`` return value,
+or the status code passed to the completion callback.
+
+**Error Response**
+
+When the controller driver receives an error code, it sets the ERROR bit in the DOE Status
+register instead of writing a response to the read mailbox register, and frees the buffers.
+
+API Reference
+=============
+
+.. kernel-doc:: drivers/pci/endpoint/pci-ep-doe.c
+ :export:
--
2.34.1
^ permalink raw reply related
* [PATCH v3 3/4] PCI: endpoint: Add API for DOE initialization and setup in EPC core
From: Aksh Garg @ 2026-04-27 5:17 UTC (permalink / raw)
To: linux-pci, linux-doc, mani, kwilczynski, bhelgaas, corbet, kishon,
skhan, lukas, cassel, alistair
Cc: linux-arm-kernel, linux-kernel, s-vadapalli, danishanwar, srk,
a-garg7
In-Reply-To: <20260427051725.223704-1-a-garg7@ti.com>
Add pci_epc_setup_doe() API in EPC core driver to initialize and setup
the DOE framework for an endpoint controller. The API discovers the DOE
capabilities (extended capability ID 0x2E), and registers each discovered
DOE mailbox for all the functions in the endpoint controller. This API
should be invoked by the controller driver during probe based on the
doe_capable feature.
Add pci_epc_destroy_doe() API in EPC core driver for cleanup of DOE
resources, which should be invoked by the controller driver during
controller cleanup based on the doe_capable feature.
Co-developed-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Siddharth Vadapalli <s-vadapalli@ti.com>
Signed-off-by: Aksh Garg <a-garg7@ti.com>
---
Changes from v2 to v3:
- Rebased on 7.1-rc1.
Changes since v1:
- New patch added to v2 (not present in v1)
v2: https://lore.kernel.org/all/20260401073022.215805-4-a-garg7@ti.com/
This patch is introduced based on the feedback provided by Manivannan
Sadhasivam at [1].
[1]: https://lore.kernel.org/all/p57x6jleaim5w7t2k3v7tioujnaxuovfpj5euop5ogefvw23se@y5fw3che5p5d/
drivers/pci/endpoint/pci-epc-core.c | 71 +++++++++++++++++++++++++++++
include/linux/pci-epc.h | 21 +++++++++
2 files changed, 92 insertions(+)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 6c3c58185fc5..5a95a07b7d3a 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -14,6 +14,8 @@
#include <linux/pci-epf.h>
#include <linux/pci-ep-cfs.h>
+#include "../pci.h"
+
static const struct class pci_epc_class = {
.name = "pci_epc",
};
@@ -548,6 +550,75 @@ void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
}
EXPORT_SYMBOL_GPL(pci_epc_mem_unmap);
+/**
+ * pci_epc_doe_setup() - Setup and discover DOE mailboxes for all functions
+ * @epc: the EPC device on which DOE mailboxes has to be setup
+ *
+ * Discover DOE (Data Object Exchange) capabilities for all physical functions
+ * in the endpoint controller and register DOE mailboxes.
+ *
+ * This API should be called by the controller driver during initialization
+ * if DOE support is available (indicated by doe_capable in pci_epc_features).
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+int pci_epc_doe_setup(struct pci_epc *epc)
+{
+ u16 cap_offset = 0;
+ u8 func_no;
+ int ret;
+
+ if (!epc || !epc->ops || !epc->ops->find_ext_capability)
+ return -EINVAL;
+
+ /* Initialize DOE framework for this controller */
+ ret = pci_ep_doe_init(epc);
+ if (ret)
+ return ret;
+
+ /* Discover DOE capabilities for all functions */
+ for (func_no = 0; func_no < epc->max_functions; func_no++) {
+ while ((cap_offset = epc->ops->find_ext_capability(epc, func_no, 0,
+ cap_offset,
+ PCI_EXT_CAP_ID_DOE))) {
+ /* Register this DOE mailbox */
+ ret = pci_ep_doe_add_mailbox(epc, func_no, cap_offset);
+ if (ret) {
+ dev_err(&epc->dev,
+ "[pf%d:offset %x] failed to add DOE mailbox\n",
+ func_no, cap_offset);
+ }
+ }
+ }
+
+ dev_dbg(&epc->dev, "DOE mailboxes setup complete\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_epc_doe_setup);
+
+/**
+ * pci_epc_doe_destroy() - Destroy and cleanup DOE mailboxes
+ * @epc: the EPC device on which DOE mailboxes has to be destroyed
+ *
+ * Destroy all DOE mailboxes registered on this endpoint controller and
+ * free associated resources.
+ *
+ * This API should be called by the controller driver during controller cleanup
+ * if DOE support is available (indicated by doe_capable in pci_epc_features).
+ *
+ * RETURNS: 0 on success, -errno on failure
+ */
+int pci_epc_doe_destroy(struct pci_epc *epc)
+{
+ if (!epc)
+ return -EINVAL;
+
+ pci_ep_doe_destroy(epc);
+ dev_dbg(&epc->dev, "DOE mailboxes destroyed\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_epc_doe_destroy);
+
/**
* pci_epc_clear_bar() - reset the BAR
* @epc: the EPC device for which the BAR has to be cleared
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index dd26294c8175..7b0f258ef330 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -84,6 +84,8 @@ struct pci_epc_map {
* @start: ops to start the PCI link
* @stop: ops to stop the PCI link
* @get_features: ops to get the features supported by the EPC
+ * @find_ext_capability: ops to find extended capability offset for a function
+ * in endpoint controller
* @owner: the module owner containing the ops
*/
struct pci_epc_ops {
@@ -115,6 +117,8 @@ struct pci_epc_ops {
void (*stop)(struct pci_epc *epc);
const struct pci_epc_features* (*get_features)(struct pci_epc *epc,
u8 func_no, u8 vfunc_no);
+ u16 (*find_ext_capability)(struct pci_epc *epc, u8 func_no,
+ u8 vfunc_no, u16 start, u8 cap);
struct module *owner;
};
@@ -270,6 +274,7 @@ struct pci_epc_bar_desc {
* @msi_capable: indicate if the endpoint function has MSI capability
* @msix_capable: indicate if the endpoint function has MSI-X capability
* @intx_capable: indicate if the endpoint can raise INTx interrupts
+ * @doe_capable: indicate if the endpoint function has DOE capability
* @bar: array specifying the hardware description for each BAR
* @align: alignment size required for BAR buffer allocation
*/
@@ -280,6 +285,7 @@ struct pci_epc_features {
unsigned int msi_capable : 1;
unsigned int msix_capable : 1;
unsigned int intx_capable : 1;
+ unsigned int doe_capable : 1;
struct pci_epc_bar_desc bar[PCI_STD_NUM_BARS];
size_t align;
};
@@ -368,6 +374,21 @@ int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epc_map *map);
+#ifdef CONFIG_PCI_ENDPOINT_DOE
+int pci_epc_doe_setup(struct pci_epc *epc);
+int pci_epc_doe_destroy(struct pci_epc *epc);
+#else
+static inline int pci_epc_doe_setup(struct pci_epc *epc)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pci_epc_doe_destroy(struct pci_epc *epc)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
#else
static inline void pci_epc_init_notify(struct pci_epc *epc)
{
--
2.34.1
^ permalink raw reply related
* [PATCH wireless-next] wifi: mt76: fix of_get_mac_address error handling
From: Rosen Penev @ 2026-04-27 5:17 UTC (permalink / raw)
To: linux-wireless
Cc: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
open list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support
Check return value instead of is_valid_ether_addr. The latter is handled
by the former.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/wireless/mediatek/mt76/eeprom.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
index 93d91264687f..0f6ccf6ed53d 100644
--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -93,7 +93,7 @@ mt76_eeprom_override(struct mt76_phy *phy)
if (err == -EPROBE_DEFER)
return err;
- if (!is_valid_ether_addr(phy->macaddr)) {
+ if (err) {
eth_random_addr(phy->macaddr);
dev_info(dev->dev,
"Invalid MAC address, using random address %pM\n",
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] KVM: arm64: Wake-up from WFI when iqrchip is in userspace
From: Yao Yuan @ 2026-04-27 5:31 UTC (permalink / raw)
To: Marc Zyngier
Cc: kvmarm, kvm, linux-arm-kernel, Joey Gouly, Suzuki K Poulose,
Oliver Upton, Zenghui Yu
In-Reply-To: <87jytwbz2d.wl-maz@kernel.org>
On Fri, Apr 24, 2026 at 08:24:42AM +0800, Marc Zyngier wrote:
> On Fri, 24 Apr 2026 07:33:02 +0100,
> Yao Yuan <yaoyuan@linux.alibaba.com> wrote:
> >
> > On Thu, Apr 23, 2026 at 05:36:07PM +0800, Marc Zyngier wrote:
> > > It appears that there is nothing in the wake-up path that
> > > evaluates whether the in-kernel interrupts are pending unless
> > > we have a vgic.
> > >
> > > This means that the userspace irqchip support has been broken for
> > > about four years, and nobody noticed. It was also broken before
> > > as we wouldn't wake-up on a PMU interrupt, but hey, who cares...
> > >
> > > It is probably time to remove the feature altogether, because it
> > > was a terrible idea 10 years ago, and it still is.
> > >
> > > Fixes: b57de4ffd7c6d ("KVM: arm64: Simplify kvm_cpu_has_pending_timer()")
> > > Signed-off-by: Marc Zyngier <maz@kernel.org>
> > > ---
> > > arch/arm64/kvm/arm.c | 4 ++++
> > > 1 file changed, 4 insertions(+)
> > >
> > > diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> > > index 176cbe8baad30..8bb2c7422cc8b 100644
> > > --- a/arch/arm64/kvm/arm.c
> > > +++ b/arch/arm64/kvm/arm.c
> > > @@ -824,6 +824,10 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
> > > {
> > > bool irq_lines = *vcpu_hcr(v) & (HCR_VI | HCR_VF | HCR_VSE);
> > >
> >
> > Hi Marc,
> >
> > > + irq_lines |= (!irqchip_in_kernel(v->kvm) &&
> > > + (kvm_timer_should_notify_user(v) ||
> > > + kvm_pmu_should_notify_user(v)));
> >
> > How about a new helper like 'kvm_should_notify_us_irqchip()' ?
> > We can replace the same part at beginning of kvm_vcpu_exit_request() and
> > here w/ unlikely().
>
> I'd rather not introduce a helper, for two reasons:
>
> - this needs to be backported all the way to 5.19, because that's how
> far it has been broken. So keeping it small and localised is far
> better than introducing a helper that will make the backport less
> obvious.
Agree on this point very much!
>
> - I have patches to remove the other calls to kvm_*_notify_user() as a
> simplification of this utterly stupid feature.
OK.
>
> Finally, and while I agree that this could take an unlikely()
> qualifier, a much better course of action would be to have a separate
> patch that moves the qualifier to the predicate itself.
I got it, thanks for your such detail explanation!
>
> Thanks,
>
> M.
>
> --
> Jazz isn't dead. It just smells funny.
^ permalink raw reply
* RE: [PATCH v2 02/15] Drivers: hv: Move hv_vp_assist_page to common files
From: Michael Kelley @ 2026-04-27 5:37 UTC (permalink / raw)
To: Naman Jain, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Catalin Marinas, Will Deacon,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
x86@kernel.org, H . Peter Anvin, Arnd Bergmann, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Michael Kelley
Cc: Marc Zyngier, Timothy Hayes, Lorenzo Pieralisi, Sascha Bischoff,
mrigendrachaubey, linux-hyperv@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
linux-riscv@lists.infradead.org, vdso@mailbox.org,
ssengar@linux.microsoft.com
In-Reply-To: <20260423124206.2410879-3-namjain@linux.microsoft.com>
From: Naman Jain <namjain@linux.microsoft.com> Sent: Thursday, April 23, 2026 5:42 AM
>
> Move the logic to initialize and export hv_vp_assist_page from x86
> architecture code to Hyper-V common code to allow it to be used for
> upcoming arm64 support in MSHV_VTL driver.
> Note: This change also improves error handling - if VP assist page
> allocation fails, hyperv_init() now returns early instead of
> continuing with partial initialization.
>
> Signed-off-by: Roman Kisel <romank@linux.microsoft.com>
> Reviewed-by: Roman Kisel <vdso@mailbox.org>
> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
> ---
> arch/x86/hyperv/hv_init.c | 88 +-----------------------------
> arch/x86/include/asm/mshyperv.h | 14 -----
> drivers/hv/hv_common.c | 94 ++++++++++++++++++++++++++++++++-
> include/asm-generic/mshyperv.h | 16 ++++++
> include/hyperv/hvgdk_mini.h | 6 ++-
> 5 files changed, 115 insertions(+), 103 deletions(-)
>
> diff --git a/arch/x86/hyperv/hv_init.c b/arch/x86/hyperv/hv_init.c
> index 323adc93f2dc..75a98b5e451b 100644
> --- a/arch/x86/hyperv/hv_init.c
> +++ b/arch/x86/hyperv/hv_init.c
> @@ -81,9 +81,6 @@ union hv_ghcb * __percpu *hv_ghcb_pg;
> /* Storage to save the hypercall page temporarily for hibernation */
> static void *hv_hypercall_pg_saved;
>
> -struct hv_vp_assist_page **hv_vp_assist_page;
> -EXPORT_SYMBOL_GPL(hv_vp_assist_page);
> -
> static int hyperv_init_ghcb(void)
> {
> u64 ghcb_gpa;
> @@ -117,59 +114,12 @@ static int hyperv_init_ghcb(void)
>
> static int hv_cpu_init(unsigned int cpu)
> {
> - union hv_vp_assist_msr_contents msr = { 0 };
> - struct hv_vp_assist_page **hvp;
> int ret;
>
> ret = hv_common_cpu_init(cpu);
> if (ret)
> return ret;
>
> - if (!hv_vp_assist_page)
> - return 0;
> -
> - hvp = &hv_vp_assist_page[cpu];
> - if (hv_root_partition()) {
> - /*
> - * For root partition we get the hypervisor provided VP assist
> - * page, instead of allocating a new page.
> - */
> - rdmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
> - *hvp = memremap(msr.pfn << HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT,
> - PAGE_SIZE, MEMREMAP_WB);
> - } else {
> - /*
> - * The VP assist page is an "overlay" page (see Hyper-V TLFS's
> - * Section 5.2.1 "GPA Overlay Pages"). Here it must be zeroed
> - * out to make sure we always write the EOI MSR in
> - * hv_apic_eoi_write() *after* the EOI optimization is disabled
> - * in hv_cpu_die(), otherwise a CPU may not be stopped in the
> - * case of CPU offlining and the VM will hang.
> - */
> - if (!*hvp) {
> - *hvp = __vmalloc(PAGE_SIZE, GFP_KERNEL | __GFP_ZERO);
> -
> - /*
> - * Hyper-V should never specify a VM that is a Confidential
> - * VM and also running in the root partition. Root partition
> - * is blocked to run in Confidential VM. So only decrypt assist
> - * page in non-root partition here.
> - */
> - if (*hvp && !ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
> - WARN_ON_ONCE(set_memory_decrypted((unsigned long)(*hvp), 1));
> - memset(*hvp, 0, PAGE_SIZE);
> - }
> - }
> -
> - if (*hvp)
> - msr.pfn = vmalloc_to_pfn(*hvp);
> -
> - }
> - if (!WARN_ON(!(*hvp))) {
> - msr.enable = 1;
> - wrmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
> - }
> -
> /* Allow Hyper-V stimer vector to be injected from Hypervisor. */
> if (ms_hyperv.misc_features & HV_STIMER_DIRECT_MODE_AVAILABLE)
> apic_update_vector(cpu, HYPERV_STIMER0_VECTOR, true);
> @@ -286,23 +236,6 @@ static int hv_cpu_die(unsigned int cpu)
>
> hv_common_cpu_die(cpu);
>
> - if (hv_vp_assist_page && hv_vp_assist_page[cpu]) {
> - union hv_vp_assist_msr_contents msr = { 0 };
> - if (hv_root_partition()) {
> - /*
> - * For root partition the VP assist page is mapped to
> - * hypervisor provided page, and thus we unmap the
> - * page here and nullify it, so that in future we have
> - * correct page address mapped in hv_cpu_init.
> - */
> - memunmap(hv_vp_assist_page[cpu]);
> - hv_vp_assist_page[cpu] = NULL;
> - rdmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
> - msr.enable = 0;
> - }
> - wrmsrq(HV_X64_MSR_VP_ASSIST_PAGE, msr.as_uint64);
> - }
> -
> if (hv_reenlightenment_cb == NULL)
> return 0;
>
> @@ -460,21 +393,6 @@ void __init hyperv_init(void)
> if (hv_common_init())
> return;
>
> - /*
> - * The VP assist page is useless to a TDX guest: the only use we
> - * would have for it is lazy EOI, which can not be used with TDX.
> - */
> - if (hv_isolation_type_tdx())
> - hv_vp_assist_page = NULL;
> - else
> - hv_vp_assist_page = kzalloc_objs(*hv_vp_assist_page, nr_cpu_ids);
> - if (!hv_vp_assist_page) {
> - ms_hyperv.hints &= ~HV_X64_ENLIGHTENED_VMCS_RECOMMENDED;
> -
> - if (!hv_isolation_type_tdx())
> - goto common_free;
> - }
> -
> if (ms_hyperv.paravisor_present && hv_isolation_type_snp()) {
> /* Negotiate GHCB Version. */
> if (!hv_ghcb_negotiate_protocol())
> @@ -483,7 +401,7 @@ void __init hyperv_init(void)
>
> hv_ghcb_pg = alloc_percpu(union hv_ghcb *);
> if (!hv_ghcb_pg)
> - goto free_vp_assist_page;
> + goto free_ghcb_page;
Seems like this should be "goto common_free". The allocation of
hv_ghcb_pg has failed, so going to a label where hv_ghcb_pg is
freed seems redundant. It works since free_percpu() checks for
a NULL argument, but it's a bit unexpected since the common_free
label is already there.
> }
>
> cpuhp = cpuhp_setup_state(CPUHP_AP_HYPERV_ONLINE, "x86/hyperv_init:online",
> @@ -613,10 +531,6 @@ void __init hyperv_init(void)
> cpuhp_remove_state(CPUHP_AP_HYPERV_ONLINE);
> free_ghcb_page:
> free_percpu(hv_ghcb_pg);
> -free_vp_assist_page:
> - kfree(hv_vp_assist_page);
> - hv_vp_assist_page = NULL;
> -common_free:
> hv_common_free();
> }
>
> diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
> index f64393e853ee..95b452387969 100644
> --- a/arch/x86/include/asm/mshyperv.h
> +++ b/arch/x86/include/asm/mshyperv.h
> @@ -155,16 +155,6 @@ static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)
> return _hv_do_fast_hypercall16(control, input1, input2);
> }
>
> -extern struct hv_vp_assist_page **hv_vp_assist_page;
> -
> -static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
> -{
> - if (!hv_vp_assist_page)
> - return NULL;
> -
> - return hv_vp_assist_page[cpu];
> -}
> -
> void __init hyperv_init(void);
> void hyperv_setup_mmu_ops(void);
> void set_hv_tscchange_cb(void (*cb)(void));
> @@ -254,10 +244,6 @@ static inline void hyperv_setup_mmu_ops(void) {}
> static inline void set_hv_tscchange_cb(void (*cb)(void)) {}
> static inline void clear_hv_tscchange_cb(void) {}
> static inline void hyperv_stop_tsc_emulation(void) {};
> -static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
> -{
> - return NULL;
> -}
> static inline int hyperv_flush_guest_mapping(u64 as) { return -1; }
> static inline int hyperv_flush_guest_mapping_range(u64 as,
> hyperv_fill_flush_list_func fill_func, void *data)
> diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
> index 6b67ac616789..e8633bc51d56 100644
> --- a/drivers/hv/hv_common.c
> +++ b/drivers/hv/hv_common.c
> @@ -28,7 +28,11 @@
> #include <linux/slab.h>
> #include <linux/dma-map-ops.h>
> #include <linux/set_memory.h>
> +#include <linux/vmalloc.h>
> +#include <linux/io.h>
> +#include <linux/hyperv.h>
> #include <hyperv/hvhdk.h>
> +#include <hyperv/hvgdk.h>
> #include <asm/mshyperv.h>
>
> u64 hv_current_partition_id = HV_PARTITION_ID_SELF;
> @@ -78,6 +82,8 @@ static struct ctl_table_header *hv_ctl_table_hdr;
> u8 * __percpu *hv_synic_eventring_tail;
> EXPORT_SYMBOL_GPL(hv_synic_eventring_tail);
>
> +struct hv_vp_assist_page **hv_vp_assist_page;
> +EXPORT_SYMBOL_GPL(hv_vp_assist_page);
> /*
> * Hyper-V specific initialization and shutdown code that is
> * common across all architectures. Called from architecture
> @@ -92,6 +98,9 @@ void __init hv_common_free(void)
> if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE)
> hv_kmsg_dump_unregister();
>
> + kfree(hv_vp_assist_page);
> + hv_vp_assist_page = NULL;
> +
> kfree(hv_vp_index);
> hv_vp_index = NULL;
>
> @@ -394,6 +403,23 @@ int __init hv_common_init(void)
> for (i = 0; i < nr_cpu_ids; i++)
> hv_vp_index[i] = VP_INVAL;
>
> + /*
> + * The VP assist page is useless to a TDX guest: the only use we
> + * would have for it is lazy EOI, which can not be used with TDX.
> + */
> + if (hv_isolation_type_tdx()) {
> + hv_vp_assist_page = NULL;
> +#ifdef CONFIG_X86_64
> + ms_hyperv.hints &= ~HV_X64_ENLIGHTENED_VMCS_RECOMMENDED;
> +#endif
I realize that this #ifdef went away for the reason I flagged in v1 of
this patch set, but it's back again for a different reason.
Let me suggest another approach. hv_common_init() is called from
both the x86/64 and arm64 hyperv_init() functions. Immediately after
the call to hv_common_init() in the x86/64 hyperv_init(), test
hv_vp_assist_page for NULL and clear
HV_X64_ENLIGHTENED_VMCS_RECOMMENDED if it is. No #ifdef is
needed, and x86/64 specific hackery stays under arch/x86 instead of
being in common code.
> + } else {
> + hv_vp_assist_page = kzalloc_objs(*hv_vp_assist_page, nr_cpu_ids);
> + if (!hv_vp_assist_page) {
> + hv_common_free();
> + return -ENOMEM;
> + }
> + }
> +
> return 0;
> }
>
> @@ -471,6 +497,8 @@ void __init ms_hyperv_late_init(void)
>
> int hv_common_cpu_init(unsigned int cpu)
> {
> + union hv_vp_assist_msr_contents msr = { 0 };
> + struct hv_vp_assist_page **hvp;
> void **inputarg, **outputarg;
> u8 **synic_eventring_tail;
> u64 msr_vp_index;
> @@ -539,7 +567,53 @@ int hv_common_cpu_init(unsigned int cpu)
> sizeof(u8), flags);
> /* No need to unwind any of the above on failure here */
> if (unlikely(!*synic_eventring_tail))
> - ret = -ENOMEM;
> + return -ENOMEM;
> + }
> +
> + if (!hv_vp_assist_page)
> + return ret;
> +
> + hvp = &hv_vp_assist_page[cpu];
> + if (hv_root_partition()) {
> + /*
> + * For root partition we get the hypervisor provided VP assist
> + * page, instead of allocating a new page.
> + */
> + msr.as_uint64 = hv_get_msr(HV_MSR_VP_ASSIST_PAGE);
> + *hvp = memremap(msr.pfn << HV_VP_ASSIST_PAGE_ADDRESS_SHIFT,
> + HV_HYP_PAGE_SIZE, MEMREMAP_WB);
> + } else {
> + /*
> + * The VP assist page is an "overlay" page (see Hyper-V TLFS's
> + * Section 5.2.1 "GPA Overlay Pages"). Here it must be zeroed
> + * out to make sure that on x86/x64, we always write the EOI MSR in
> + * hv_apic_eoi_write() *after* the EOI optimization is disabled
> + * in hv_cpu_die(), otherwise a CPU may not be stopped in the
> + * case of CPU offlining and the VM will hang.
> + */
> + if (!*hvp) {
> + *hvp = __vmalloc(HV_HYP_PAGE_SIZE, flags | __GFP_ZERO);
> +
> + /*
> + * Hyper-V should never specify a VM that is a Confidential
> + * VM and also running in the root partition. Root partition
> + * is blocked to run in Confidential VM. So only decrypt assist
> + * page in non-root partition here.
> + */
> + if (*hvp &&
> + !ms_hyperv.paravisor_present &&
> + hv_isolation_type_snp()) {
> + WARN_ON_ONCE(set_memory_decrypted((unsigned long)(*hvp), 1));
> + memset(*hvp, 0, HV_HYP_PAGE_SIZE);
> + }
> + }
> +
> + if (*hvp)
> + msr.pfn = page_to_hvpfn(vmalloc_to_page(*hvp));
Your Patch 0 changelog mentions adding a comment about vmalloc_to_pfn(), which
I didn't see anywhere. I'm not sure what that comment would say, so maybe it
became unnecessary.
> + }
> + if (!WARN_ON(!(*hvp))) {
> + msr.enable = 1;
> + hv_set_msr(HV_MSR_VP_ASSIST_PAGE, msr.as_uint64);
> }
>
> return ret;
> @@ -566,6 +640,24 @@ int hv_common_cpu_die(unsigned int cpu)
> *synic_eventring_tail = NULL;
> }
>
> + if (hv_vp_assist_page && hv_vp_assist_page[cpu]) {
> + union hv_vp_assist_msr_contents msr = { 0 };
> +
> + if (hv_root_partition()) {
> + /*
> + * For root partition the VP assist page is mapped to
> + * hypervisor provided page, and thus we unmap the
> + * page here and nullify it, so that in future we have
> + * correct page address mapped in hv_cpu_init.
> + */
> + memunmap(hv_vp_assist_page[cpu]);
> + hv_vp_assist_page[cpu] = NULL;
> + msr.as_uint64 = hv_get_msr(HV_MSR_VP_ASSIST_PAGE);
> + msr.enable = 0;
> + }
> + hv_set_msr(HV_MSR_VP_ASSIST_PAGE, msr.as_uint64);
> + }
> +
> return 0;
> }
>
> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
> index d37b68238c97..2810aa05dc73 100644
> --- a/include/asm-generic/mshyperv.h
> +++ b/include/asm-generic/mshyperv.h
> @@ -25,6 +25,7 @@
> #include <linux/nmi.h>
> #include <asm/ptrace.h>
> #include <hyperv/hvhdk.h>
> +#include <hyperv/hvgdk.h>
>
> #define VTPM_BASE_ADDRESS 0xfed40000
>
> @@ -299,6 +300,16 @@ do { \
> #define hv_status_debug(status, fmt, ...) \
> hv_status_printk(debug, status, fmt, ##__VA_ARGS__)
>
> +extern struct hv_vp_assist_page **hv_vp_assist_page;
> +
> +static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
> +{
> + if (!hv_vp_assist_page)
> + return NULL;
> +
> + return hv_vp_assist_page[cpu];
> +}
> +
> const char *hv_result_to_string(u64 hv_status);
> int hv_result_to_errno(u64 status);
> void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die);
> @@ -327,6 +338,11 @@ static inline enum hv_isolation_type hv_get_isolation_type(void)
> {
> return HV_ISOLATION_TYPE_NONE;
> }
> +
> +static inline struct hv_vp_assist_page *hv_get_vp_assist_page(unsigned int cpu)
> +{
> + return NULL;
> +}
> #endif /* CONFIG_HYPERV */
>
> #if IS_ENABLED(CONFIG_MSHV_ROOT)
> diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
> index 056ef7b6b360..c72d04cd5ae4 100644
> --- a/include/hyperv/hvgdk_mini.h
> +++ b/include/hyperv/hvgdk_mini.h
> @@ -149,6 +149,7 @@ struct hv_u128 {
> #define HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT 12
Can this X64 specific definition of the shift be eliminated entirely,
and a single common definition for x86/64 and arm64 be used?
As I understand it, the MSR layout is the same on both architectures.
The one gotcha is that kvm_hv_set_msr() would need to be updated.
HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_MASK defined below isn't
used anywhere, so it could go away too. (The KVM selftest usage has
its own definition.)
I realize these are changes to a source code file that is derived from
Windows, and I'm not sure of the guidelines for such changes. So maybe
these suggestions have to be ignored ....
> #define HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_MASK \
> (~((1ull << HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT) - 1))
> +#define HV_MSR_VP_ASSIST_PAGE (HV_X64_MSR_VP_ASSIST_PAGE)
This is the correct file for this #define, but it should be placed down around
line 1148 or so with the other HV_MSR_* definitions in terms of HV_X64_MSR_*
>
> /* Hyper-V Enlightened VMCS version mask in nested features CPUID */
> #define HV_X64_ENLIGHTENED_VMCS_VERSION 0xff
> @@ -410,6 +411,7 @@ union hv_x64_msr_hypercall_contents {
> #if defined(CONFIG_ARM64)
> #define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE BIT(8)
> #define HV_STIMER_DIRECT_MODE_AVAILABLE BIT(13)
> +#define HV_VP_ASSIST_PAGE_ADDRESS_SHIFT 12
> #endif /* CONFIG_ARM64 */
>
> #if defined(CONFIG_X86)
> @@ -1163,6 +1165,8 @@ enum hv_register_name {
> #define HV_MSR_STIMER0_CONFIG (HV_X64_MSR_STIMER0_CONFIG)
> #define HV_MSR_STIMER0_COUNT (HV_X64_MSR_STIMER0_COUNT)
>
> +#define HV_VP_ASSIST_PAGE_ADDRESS_SHIFT HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT
> +
> #elif defined(CONFIG_ARM64) /* CONFIG_X86 */
>
> #define HV_MSR_CRASH_P0 (HV_REGISTER_GUEST_CRASH_P0)
> @@ -1185,7 +1189,7 @@ enum hv_register_name {
>
> #define HV_MSR_STIMER0_CONFIG (HV_REGISTER_STIMER0_CONFIG)
> #define HV_MSR_STIMER0_COUNT (HV_REGISTER_STIMER0_COUNT)
> -
> +#define HV_MSR_VP_ASSIST_PAGE (HV_REGISTER_VP_ASSIST_PAGE)
Nit: This definition is slightly mis-aligned. It has spaces where there
should be a tab to match the similar definitions above it.
> #endif /* CONFIG_ARM64 */
>
> union hv_explicit_suspend_register {
> --
> 2.43.0
>
^ permalink raw reply
* RE: [PATCH v2 03/15] Drivers: hv: Move vmbus_handler to common code
From: Michael Kelley @ 2026-04-27 5:38 UTC (permalink / raw)
To: Naman Jain, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Catalin Marinas, Will Deacon,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
x86@kernel.org, H . Peter Anvin, Arnd Bergmann, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Michael Kelley
Cc: Marc Zyngier, Timothy Hayes, Lorenzo Pieralisi, Sascha Bischoff,
mrigendrachaubey, linux-hyperv@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
linux-riscv@lists.infradead.org, vdso@mailbox.org,
ssengar@linux.microsoft.com
In-Reply-To: <20260423124206.2410879-4-namjain@linux.microsoft.com>
From: Naman Jain <namjain@linux.microsoft.com> Sent: Thursday, April 23, 2026 5:42 AM
>
> Move the vmbus_handler global variable and hv_setup_vmbus_handler()/
> hv_remove_vmbus_handler() from arch/x86 to drivers/hv/hv_common.c.
>
> hv_setup_vmbus_handler() is called unconditionally in vmbus_bus_init()
> and works for both x86 (sysvec handler) and arm64 (vmbus_percpu_isr).
>
> This eliminates the need for separate percpu vmbus handler setup
> functions and __weak stubs, that are needed for adding ARM64 support
> in MSHV_VTL driver where we need to set a custom per-cpu vmbus handler.
>
> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
> ---
> arch/x86/kernel/cpu/mshyperv.c | 12 ------------
> drivers/hv/hv_common.c | 9 +++++++--
> drivers/hv/vmbus_drv.c | 17 +++++++++--------
> include/asm-generic/mshyperv.h | 1 +
> 4 files changed, 17 insertions(+), 22 deletions(-)
>
> diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
> index 89a2eb8a0722..68706ff5880e 100644
> --- a/arch/x86/kernel/cpu/mshyperv.c
> +++ b/arch/x86/kernel/cpu/mshyperv.c
> @@ -145,7 +145,6 @@ void hv_set_msr(unsigned int reg, u64 value)
> EXPORT_SYMBOL_GPL(hv_set_msr);
>
> static void (*mshv_handler)(void);
> -static void (*vmbus_handler)(void);
> static void (*hv_stimer0_handler)(void);
> static void (*hv_kexec_handler)(void);
> static void (*hv_crash_handler)(struct pt_regs *regs);
> @@ -172,17 +171,6 @@ void hv_setup_mshv_handler(void (*handler)(void))
> mshv_handler = handler;
> }
>
> -void hv_setup_vmbus_handler(void (*handler)(void))
> -{
> - vmbus_handler = handler;
> -}
> -
> -void hv_remove_vmbus_handler(void)
> -{
> - /* We have no way to deallocate the interrupt gate */
> - vmbus_handler = NULL;
> -}
> -
> /*
> * Routines to do per-architecture handling of stimer0
> * interrupts when in Direct Mode
> diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
> index e8633bc51d56..eb7b0028b45d 100644
> --- a/drivers/hv/hv_common.c
> +++ b/drivers/hv/hv_common.c
> @@ -758,13 +758,18 @@ bool __weak hv_isolation_type_tdx(void)
> }
> EXPORT_SYMBOL_GPL(hv_isolation_type_tdx);
>
> -void __weak hv_setup_vmbus_handler(void (*handler)(void))
> +void (*vmbus_handler)(void);
> +EXPORT_SYMBOL_GPL(vmbus_handler);
> +
> +void hv_setup_vmbus_handler(void (*handler)(void))
> {
> + vmbus_handler = handler;
> }
> EXPORT_SYMBOL_GPL(hv_setup_vmbus_handler);
>
> -void __weak hv_remove_vmbus_handler(void)
> +void hv_remove_vmbus_handler(void)
> {
> + vmbus_handler = NULL;
> }
> EXPORT_SYMBOL_GPL(hv_remove_vmbus_handler);
I'd suggest moving hv_setup_vmbus_handler() and
hv_remove_vmbus_handler() above or below the group
of __weak stubs in this source code file. There's a comment
describing the purpose of these __weak functions, and
intermixing these two functions that are no longer __weak
produces something of a jumble.
>
> diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
> index bc4fc1951ae1..052ca8b11cee 100644
> --- a/drivers/hv/vmbus_drv.c
> +++ b/drivers/hv/vmbus_drv.c
> @@ -1415,7 +1415,8 @@ EXPORT_SYMBOL_FOR_MODULES(vmbus_isr, "mshv_vtl");
>
> static irqreturn_t vmbus_percpu_isr(int irq, void *dev_id)
> {
> - vmbus_isr();
> + if (vmbus_handler)
> + vmbus_handler();
Is it necessary to test vmbus_handler first? From what I can
see, it is always set before the per-cpu interrupt is setup.
> return IRQ_HANDLED;
> }
>
> @@ -1517,8 +1518,10 @@ static int vmbus_bus_init(void)
> vmbus_irq_initialized = true;
> }
>
> + hv_setup_vmbus_handler(vmbus_isr);
> +
> if (vmbus_irq == -1) {
> - hv_setup_vmbus_handler(vmbus_isr);
> + /* x86: sysvec handler uses vmbus_handler directly */
> } else {
> ret = request_percpu_irq(vmbus_irq, vmbus_percpu_isr,
> "Hyper-V VMbus", &vmbus_evt);
> @@ -1553,9 +1556,8 @@ static int vmbus_bus_init(void)
> return 0;
>
> err_connect:
> - if (vmbus_irq == -1)
> - hv_remove_vmbus_handler();
> - else
> + hv_remove_vmbus_handler();
> + if (vmbus_irq != -1)
> free_percpu_irq(vmbus_irq, &vmbus_evt);
These operations should be reordered so they are the inverse
of how they are setup. I.e., free_percpu_irq() first, then remove
the VMBus handler. That's just good standard practice unless
there's a specific reason to do the cleanup ordering differently. In
fact, hv_remove_vmbus_handler() needs to be moved down
to the err_setup label so it's done if request_percpu_irq()
fails.
> err_setup:
> if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) {
> @@ -3026,9 +3028,8 @@ static void __exit vmbus_exit(void)
> vmbus_connection.conn_state = DISCONNECTED;
> hv_stimer_global_cleanup();
> vmbus_disconnect();
> - if (vmbus_irq == -1)
> - hv_remove_vmbus_handler();
> - else
> + hv_remove_vmbus_handler();
> + if (vmbus_irq != -1)
> free_percpu_irq(vmbus_irq, &vmbus_evt);
Ordering should be changed here as well so it is the inverse
of how things are set up.
> if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) {
> smpboot_unregister_percpu_thread(&vmbus_irq_threads);
> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
> index 2810aa05dc73..db183c8cfb95 100644
> --- a/include/asm-generic/mshyperv.h
> +++ b/include/asm-generic/mshyperv.h
> @@ -179,6 +179,7 @@ static inline u64 hv_generate_guest_id(u64 kernel_version)
>
> int hv_get_hypervisor_version(union hv_hypervisor_version_info *info);
>
> +extern void (*vmbus_handler)(void);
> void hv_setup_vmbus_handler(void (*handler)(void));
> void hv_remove_vmbus_handler(void);
> void hv_setup_stimer0_handler(void (*handler)(void));
> --
> 2.43.0
>
^ permalink raw reply
* RE: [PATCH v2 07/15] arm64: hyperv: Add support for mshv_vtl_return_call
From: Michael Kelley @ 2026-04-27 5:38 UTC (permalink / raw)
To: Naman Jain, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Long Li, Catalin Marinas, Will Deacon,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
x86@kernel.org, H . Peter Anvin, Arnd Bergmann, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Michael Kelley
Cc: Marc Zyngier, Timothy Hayes, Lorenzo Pieralisi, Sascha Bischoff,
mrigendrachaubey, linux-hyperv@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org,
linux-riscv@lists.infradead.org, vdso@mailbox.org,
ssengar@linux.microsoft.com
In-Reply-To: <20260423124206.2410879-8-namjain@linux.microsoft.com>
From: Naman Jain <namjain@linux.microsoft.com> Sent: Thursday, April 23, 2026 5:42 AM
>
> Add the arm64 variant of mshv_vtl_return_call() to support the MSHV_VTL
> driver on arm64. This function enables the transition between Virtual
> Trust Levels (VTLs) in MSHV_VTL when the kernel acts as a paravisor.
>
> Signed-off-by: Roman Kisel <romank@linux.microsoft.com>
> Reviewed-by: Roman Kisel <vdso@mailbox.org>
> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
> ---
> arch/arm64/hyperv/Makefile | 1 +
> arch/arm64/hyperv/hv_vtl.c | 158 ++++++++++++++++++++++++++++++
> arch/arm64/include/asm/mshyperv.h | 13 +++
> arch/x86/include/asm/mshyperv.h | 2 -
> drivers/hv/mshv_vtl.h | 3 +
> include/asm-generic/mshyperv.h | 2 +
> 6 files changed, 177 insertions(+), 2 deletions(-)
> create mode 100644 arch/arm64/hyperv/hv_vtl.c
>
[snip]
> diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h
> index 585b23a26f1b..9eb0e5999f29 100644
> --- a/arch/arm64/include/asm/mshyperv.h
> +++ b/arch/arm64/include/asm/mshyperv.h
> @@ -60,6 +60,18 @@ static inline u64 hv_get_non_nested_msr(unsigned int reg)
> ARM_SMCCC_SMC_64, \
> ARM_SMCCC_OWNER_VENDOR_HYP, \
> HV_SMCCC_FUNC_NUMBER)
> +
> +struct mshv_vtl_cpu_context {
> +/*
> + * x18 is managed by the hypervisor. It won't be reloaded from this array.
> + * It is included here for convenience in array indexing.
> + * 'rsvd' field serves as alignment padding so q[] starts at offset 32*8=256.
> + */
> + __u64 x[31];
> + __u64 rsvd;
> + __uint128_t q[32];
> +};
> +
> #ifdef CONFIG_HYPERV_VTL_MODE
> /*
> * Get/Set the register. If the function returns `1`, that must be done via
> @@ -69,6 +81,7 @@ static inline int hv_vtl_get_set_reg(struct hv_register_assoc *regs,
> bool set, b
> {
> return 1;
> }
> +
This appears to be a spurious blank line being added since there
are no other changes in the vicinity.
> #endif
>
> #include <asm-generic/mshyperv.h>
> diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
> index 08278547b84c..b4d80c9a673a 100644
> --- a/arch/x86/include/asm/mshyperv.h
> +++ b/arch/x86/include/asm/mshyperv.h
> @@ -286,7 +286,6 @@ struct mshv_vtl_cpu_context {
> #ifdef CONFIG_HYPERV_VTL_MODE
> void __init hv_vtl_init_platform(void);
> int __init hv_vtl_early_init(void);
> -void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0);
> void mshv_vtl_return_call_init(u64 vtl_return_offset);
> void mshv_vtl_return_hypercall(void);
> void __mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0);
> @@ -294,7 +293,6 @@ int hv_vtl_get_set_reg(struct hv_register_assoc *regs, bool set,
> bool shared);
> #else
> static inline void __init hv_vtl_init_platform(void) {}
> static inline int __init hv_vtl_early_init(void) { return 0; }
> -static inline void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) {}
> static inline void mshv_vtl_return_call_init(u64 vtl_return_offset) {}
> static inline void mshv_vtl_return_hypercall(void) {}
> static inline void __mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) {}
> diff --git a/drivers/hv/mshv_vtl.h b/drivers/hv/mshv_vtl.h
> index a6eea52f7aa2..103f07371f3f 100644
> --- a/drivers/hv/mshv_vtl.h
> +++ b/drivers/hv/mshv_vtl.h
> @@ -22,4 +22,7 @@ struct mshv_vtl_run {
> char vtl_ret_actions[MSHV_MAX_RUN_MSG_SIZE];
> };
>
> +static_assert(sizeof(struct mshv_vtl_cpu_context) <= 1024,
> + "struct mshv_vtl_cpu_context exceeds reserved space in struct
> mshv_vtl_run");
> +
> #endif /* _MSHV_VTL_H */
> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
> index db183c8cfb95..8cdf2a9fbdfb 100644
> --- a/include/asm-generic/mshyperv.h
> +++ b/include/asm-generic/mshyperv.h
> @@ -396,8 +396,10 @@ static inline int hv_deposit_memory(u64 partition_id, u64 status)
>
> #if IS_ENABLED(CONFIG_HYPERV_VTL_MODE)
> u8 __init get_vtl(void);
> +void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0);
> #else
> static inline u8 get_vtl(void) { return 0; }
> +static inline void mshv_vtl_return_call(struct mshv_vtl_cpu_context *vtl0) {}
Is this stub needed? Maybe I missed something, but it looks to me like none
of the code that calls this gets built unless CONFIG_HYPERV_VTL_MODE is set.
See further comments about stubs in Patch 8 of this series.
> #endif
>
> #endif
> --
> 2.43.0
>
^ permalink raw reply
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