* [PATCH v7 2/6] firmware: hwrng: arm_smccc_trng: Register as an SMCCC device
From: Aneesh Kumar K.V (Arm) @ 2026-06-11 13:04 UTC (permalink / raw)
To: linux-coco, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Greg KH, Jeremy Linton,
Jonathan Cameron, Lorenzo Pieralisi, Mark Rutland, Sudeep Holla,
Will Deacon, Steven Price, Suzuki K Poulose, Andre Przywara
In-Reply-To: <20260611130429.295516-1-aneesh.kumar@kernel.org>
The SMCCC TRNG interface is a firmware-provided SMCCC service rather than a
standalone platform device. Now that the SMCCC core has an SMCCC bus,
create an arm-smccc-trng device for the discovered TRNG service and convert
the hwrng driver to an SMCCC driver.
The SMCCC id table preserves module autoloading for systems where the TRNG
driver is built as a module.
The sysfs device path changes from the old smccc_trng platform-device path
to an arm-smccc device path. No known userspace dependency on the old path
was found; a Debian Code Search lookup for the existing platform-device
name/path did not find any users.
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
arch/arm64/include/asm/archrandom.h | 2 +-
drivers/char/hw_random/arm_smccc_trng.c | 32 +++++++++-----
drivers/firmware/smccc/smccc.c | 58 +++++++++++++++++++++----
3 files changed, 71 insertions(+), 21 deletions(-)
diff --git a/arch/arm64/include/asm/archrandom.h b/arch/arm64/include/asm/archrandom.h
index 8babfbe31f95..7605dd81bd1e 100644
--- a/arch/arm64/include/asm/archrandom.h
+++ b/arch/arm64/include/asm/archrandom.h
@@ -12,7 +12,7 @@
extern bool smccc_trng_available;
-static inline bool __init smccc_probe_trng(void)
+static inline bool smccc_probe_trng(void)
{
struct arm_smccc_res res;
diff --git a/drivers/char/hw_random/arm_smccc_trng.c b/drivers/char/hw_random/arm_smccc_trng.c
index dcb8e7f37f25..8f7f9d830cf2 100644
--- a/drivers/char/hw_random/arm_smccc_trng.c
+++ b/drivers/char/hw_random/arm_smccc_trng.c
@@ -16,8 +16,10 @@
#include <linux/device.h>
#include <linux/hw_random.h>
#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/arm-smccc.h>
+#include <linux/arm-smccc-bus.h>
+
+#include <asm/archrandom.h>
#ifdef CONFIG_ARM64
#define ARM_SMCCC_TRNG_RND ARM_SMCCC_TRNG_RND64
@@ -94,29 +96,37 @@ static int smccc_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
return copied;
}
-static int smccc_trng_probe(struct platform_device *pdev)
+static int smccc_trng_probe(struct arm_smccc_device *sdev)
{
struct hwrng *trng;
- trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
+ /* validate the minimum version requirement */
+ if (!smccc_probe_trng())
+ return -ENODEV;
+
+ trng = devm_kzalloc(&sdev->dev, sizeof(*trng), GFP_KERNEL);
if (!trng)
return -ENOMEM;
trng->name = "smccc_trng";
trng->read = smccc_trng_read;
- return devm_hwrng_register(&pdev->dev, trng);
+ return devm_hwrng_register(&sdev->dev, trng);
}
-static struct platform_driver smccc_trng_driver = {
- .driver = {
- .name = "smccc_trng",
- },
- .probe = smccc_trng_probe,
+static const struct arm_smccc_device_id smccc_trng_id_table[] = {
+ { .name = "arm-smccc-trng" },
+ {}
+};
+MODULE_DEVICE_TABLE(arm_smccc, smccc_trng_id_table);
+
+static struct arm_smccc_driver smccc_trng_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = smccc_trng_probe,
+ .id_table = smccc_trng_id_table,
};
-module_platform_driver(smccc_trng_driver);
+module_arm_smccc_driver(smccc_trng_driver);
-MODULE_ALIAS("platform:smccc_trng");
MODULE_AUTHOR("Andre Przywara");
MODULE_DESCRIPTION("Arm SMCCC TRNG firmware interface support");
MODULE_LICENSE("GPL");
diff --git a/drivers/firmware/smccc/smccc.c b/drivers/firmware/smccc/smccc.c
index bdee057db2fd..a47696f3a5de 100644
--- a/drivers/firmware/smccc/smccc.c
+++ b/drivers/firmware/smccc/smccc.c
@@ -9,7 +9,8 @@
#include <linux/init.h>
#include <linux/arm-smccc.h>
#include <linux/kernel.h>
-#include <linux/platform_device.h>
+#include <linux/arm-smccc-bus.h>
+
#include <asm/archrandom.h>
static u32 smccc_version = ARM_SMCCC_VERSION_1_0;
@@ -81,16 +82,55 @@ bool arm_smccc_hypervisor_has_uuid(const uuid_t *hyp_uuid)
}
EXPORT_SYMBOL_GPL(arm_smccc_hypervisor_has_uuid);
+struct smccc_device_info {
+ u32 func_id;
+ bool requires_smc;
+ const char *device_name;
+};
+
+static const struct smccc_device_info smccc_devices[] __initconst = {
+ {
+ .func_id = ARM_SMCCC_TRNG_VERSION,
+ .requires_smc = false,
+ .device_name = "arm-smccc-trng",
+ },
+};
+
+static bool __init smccc_probe_smccc_device(const struct smccc_device_info *smccc_dev)
+{
+ unsigned long ret;
+ struct arm_smccc_res res;
+
+ if (smccc_conduit == SMCCC_CONDUIT_NONE)
+ return false;
+
+ if (smccc_dev->requires_smc && smccc_conduit != SMCCC_CONDUIT_SMC)
+ return false;
+
+ arm_smccc_1_1_invoke(smccc_dev->func_id, &res);
+ ret = res.a0;
+
+ if ((s32)ret == SMCCC_RET_NOT_SUPPORTED)
+ return false;
+
+ return true;
+}
+
static int __init smccc_devices_init(void)
{
- struct platform_device *pdev;
-
- if (smccc_trng_available) {
- pdev = platform_device_register_simple("smccc_trng", -1,
- NULL, 0);
- if (IS_ERR(pdev))
- pr_err("smccc_trng: could not register device: %ld\n",
- PTR_ERR(pdev));
+ struct arm_smccc_device *sdev;
+ const struct smccc_device_info *smccc_dev;
+
+ for (int i = 0; i < ARRAY_SIZE(smccc_devices); i++) {
+ smccc_dev = &smccc_devices[i];
+
+ if (!smccc_probe_smccc_device(smccc_dev))
+ continue;
+
+ sdev = arm_smccc_device_register(smccc_dev->device_name);
+ if (IS_ERR(sdev))
+ pr_err("%s: could not register device: %ld\n",
+ smccc_dev->device_name, PTR_ERR(sdev));
}
return 0;
--
2.43.0
^ permalink raw reply related
* [PATCH v7 1/6] firmware: smccc: Add an Arm SMCCC bus
From: Aneesh Kumar K.V (Arm) @ 2026-06-11 13:04 UTC (permalink / raw)
To: linux-coco, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Greg KH, Jeremy Linton,
Jonathan Cameron, Lorenzo Pieralisi, Mark Rutland, Sudeep Holla,
Will Deacon, Steven Price, Suzuki K Poulose, Andre Przywara
In-Reply-To: <20260611130429.295516-1-aneesh.kumar@kernel.org>
SMCCC-discovered firmware services are currently represented by separate
platform devices, such as smccc_trng and arm-cca-dev. Those devices do not
represent independent DT/ACPI-described platform resources; they are
features of the SMCCC firmware interface.
Add an Arm SMCCC bus for services discovered through the SMCCC firmware
interface. The bus provides SMCCC device and driver registration helpers,
name-based matching, modalias generation, and a sysfs modalias attribute so
SMCCC service drivers can bind to discovered firmware services and autoload
as modules.
Follow-up changes can then register SMCCC firmware services as arm-smccc
devices instead of creating independent per-feature platform devices.
Based on arm_ffa code
Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@kernel.org>
---
drivers/firmware/smccc/Makefile | 2 +-
drivers/firmware/smccc/bus.c | 164 ++++++++++++++++++++++++++++++
include/linux/arm-smccc-bus.h | 49 +++++++++
include/linux/mod_devicetable.h | 13 +++
scripts/mod/devicetable-offsets.c | 3 +
scripts/mod/file2alias.c | 8 ++
6 files changed, 238 insertions(+), 1 deletion(-)
create mode 100644 drivers/firmware/smccc/bus.c
create mode 100644 include/linux/arm-smccc-bus.h
diff --git a/drivers/firmware/smccc/Makefile b/drivers/firmware/smccc/Makefile
index 40d19144a860..68bbff1407b8 100644
--- a/drivers/firmware/smccc/Makefile
+++ b/drivers/firmware/smccc/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
#
-obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smccc.o kvm_guest.o
+obj-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += bus.o smccc.o kvm_guest.o
obj-$(CONFIG_ARM_SMCCC_SOC_ID) += soc_id.o
diff --git a/drivers/firmware/smccc/bus.c b/drivers/firmware/smccc/bus.c
new file mode 100644
index 000000000000..fe7e893130ce
--- /dev/null
+++ b/drivers/firmware/smccc/bus.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Arm Limited
+ */
+
+#include <linux/arm-smccc-bus.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+
+static DEFINE_IDA(arm_smccc_bus_id);
+
+static int arm_smccc_bus_match(struct device *dev,
+ const struct device_driver *drv)
+{
+ const struct arm_smccc_device_id *id_table;
+ struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
+
+ id_table = to_arm_smccc_driver(drv)->id_table;
+ if (!id_table)
+ return 0;
+
+ while (id_table->name[0]) {
+ if (!strcmp(smccc_dev->name, id_table->name))
+ return 1;
+ id_table++;
+ }
+
+ return 0;
+}
+
+static int arm_smccc_bus_probe(struct device *dev)
+{
+ struct arm_smccc_driver *smccc_drv = to_arm_smccc_driver(dev->driver);
+
+ return smccc_drv->probe(to_arm_smccc_device(dev));
+}
+
+static void arm_smccc_bus_remove(struct device *dev)
+{
+ struct arm_smccc_driver *smcc_drv = to_arm_smccc_driver(dev->driver);
+
+ if (smcc_drv->remove)
+ smcc_drv->remove(to_arm_smccc_device(dev));
+}
+
+static int arm_smccc_bus_uevent(const struct device *dev,
+ struct kobj_uevent_env *env)
+{
+ const struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
+
+ return add_uevent_var(env, "MODALIAS=" ARM_SMCCC_MODULE_PREFIX "%s",
+ smccc_dev->name);
+}
+
+static ssize_t modalias_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
+
+ return sysfs_emit(buf, ARM_SMCCC_MODULE_PREFIX "%s\n", smccc_dev->name);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *arm_smccc_device_attrs[] = {
+ &dev_attr_modalias.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(arm_smccc_device);
+
+const struct bus_type arm_smccc_bus_type = {
+ .name = "arm_smccc",
+ .match = arm_smccc_bus_match,
+ .probe = arm_smccc_bus_probe,
+ .remove = arm_smccc_bus_remove,
+ .uevent = arm_smccc_bus_uevent,
+ .dev_groups = arm_smccc_device_groups,
+};
+EXPORT_SYMBOL_GPL(arm_smccc_bus_type);
+
+int arm_smccc_driver_register(struct arm_smccc_driver *driver,
+ struct module *owner, const char *mod_name)
+{
+ if (!driver->probe)
+ return -EINVAL;
+
+ driver->driver.bus = &arm_smccc_bus_type;
+ driver->driver.name = driver->name;
+ driver->driver.owner = owner;
+ driver->driver.mod_name = mod_name;
+
+ return driver_register(&driver->driver);
+}
+EXPORT_SYMBOL_GPL(arm_smccc_driver_register);
+
+void arm_smccc_driver_unregister(struct arm_smccc_driver *driver)
+{
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL_GPL(arm_smccc_driver_unregister);
+
+static void arm_smccc_release_device(struct device *dev)
+{
+ struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev);
+
+ ida_free(&arm_smccc_bus_id, smccc_dev->id);
+ kfree(smccc_dev);
+}
+
+struct arm_smccc_device *arm_smccc_device_register(const char *name)
+{
+ struct arm_smccc_device *smccc_dev;
+ int id, ret;
+
+ id = ida_alloc_min(&arm_smccc_bus_id, 1, GFP_KERNEL);
+ if (id < 0)
+ return ERR_PTR(id);
+
+ smccc_dev = kzalloc_obj(*smccc_dev);
+ if (!smccc_dev) {
+ ida_free(&arm_smccc_bus_id, id);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ smccc_dev->id = id;
+ if (strscpy(smccc_dev->name, name) < 0) {
+ kfree(smccc_dev);
+ ida_free(&arm_smccc_bus_id, id);
+ return ERR_PTR(-EINVAL);
+ }
+ smccc_dev->dev.bus = &arm_smccc_bus_type;
+ smccc_dev->dev.release = arm_smccc_release_device;
+
+ ret = dev_set_name(&smccc_dev->dev, "%s-%d", smccc_dev->name, id);
+ if (ret) {
+ kfree(smccc_dev);
+ ida_free(&arm_smccc_bus_id, id);
+ return ERR_PTR(ret);
+ }
+
+ ret = device_register(&smccc_dev->dev);
+ if (ret) {
+ put_device(&smccc_dev->dev);
+ return ERR_PTR(ret);
+ }
+
+ return smccc_dev;
+}
+EXPORT_SYMBOL_GPL(arm_smccc_device_register);
+
+void arm_smccc_device_unregister(struct arm_smccc_device *smccc_dev)
+{
+ if (!smccc_dev)
+ return;
+
+ device_unregister(&smccc_dev->dev);
+}
+EXPORT_SYMBOL_GPL(arm_smccc_device_unregister);
+
+static int __init arm_smccc_bus_init(void)
+{
+ return bus_register(&arm_smccc_bus_type);
+}
+subsys_initcall(arm_smccc_bus_init);
+
diff --git a/include/linux/arm-smccc-bus.h b/include/linux/arm-smccc-bus.h
new file mode 100644
index 000000000000..188891441e57
--- /dev/null
+++ b/include/linux/arm-smccc-bus.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2026 Arm Limited
+ */
+#ifndef __LINUX_ARM_SMCCC_BUS_H
+#define __LINUX_ARM_SMCCC_BUS_H
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+
+struct arm_smccc_device {
+ int id;
+ char name[ARM_SMCCC_NAME_SIZE];
+ struct device dev;
+};
+
+#define to_arm_smccc_device(d) container_of(d, struct arm_smccc_device, dev)
+
+struct arm_smccc_driver {
+ const char *name;
+ int (*probe)(struct arm_smccc_device *sdev);
+ void (*remove)(struct arm_smccc_device *sdev);
+ const struct arm_smccc_device_id *id_table;
+
+ struct device_driver driver;
+};
+
+#define to_arm_smccc_driver(d) \
+ container_of_const(d, struct arm_smccc_driver, driver)
+
+int arm_smccc_driver_register(struct arm_smccc_driver *driver,
+ struct module *owner, const char *mod_name);
+void arm_smccc_driver_unregister(struct arm_smccc_driver *driver);
+struct arm_smccc_device *arm_smccc_device_register(const char *name);
+void arm_smccc_device_unregister(struct arm_smccc_device *smcc_dev);
+
+#define arm_smccc_register(driver) \
+ arm_smccc_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
+#define arm_smccc_unregister(driver) \
+ arm_smccc_driver_unregister(driver)
+
+#define module_arm_smccc_driver(__arm_smccc_driver) \
+ module_driver(__arm_smccc_driver, arm_smccc_register, \
+ arm_smccc_unregister)
+
+extern const struct bus_type arm_smccc_bus_type;
+
+#endif /* __LINUX_ARM_SMCCC_BUS_H */
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 23ff24080dfd..c9cee8c5a0b2 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -876,6 +876,19 @@ struct auxiliary_device_id {
kernel_ulong_t driver_data;
};
+#define ARM_SMCCC_NAME_SIZE 40
+#define ARM_SMCCC_MODULE_PREFIX "arm_smccc:"
+
+/**
+ * struct arm_smccc_device_id - Arm SMCCC bus device identifier
+ * @name: SMCCC device name
+ * @driver_data: driver data
+ */
+struct arm_smccc_device_id {
+ char name[ARM_SMCCC_NAME_SIZE];
+ kernel_ulong_t driver_data;
+};
+
/* Surface System Aggregator Module */
#define SSAM_MATCH_TARGET 0x1
diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c
index b4178c42d08f..a485011ff137 100644
--- a/scripts/mod/devicetable-offsets.c
+++ b/scripts/mod/devicetable-offsets.c
@@ -254,6 +254,9 @@ int main(void)
DEVID(auxiliary_device_id);
DEVID_FIELD(auxiliary_device_id, name);
+ DEVID(arm_smccc_device_id);
+ DEVID_FIELD(arm_smccc_device_id, name);
+
DEVID(ssam_device_id);
DEVID_FIELD(ssam_device_id, match_flags);
DEVID_FIELD(ssam_device_id, domain);
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 2ad87a74bb03..92d3917f27cc 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -1323,6 +1323,13 @@ static void do_auxiliary_entry(struct module *mod, void *symval)
module_alias_printf(mod, false, AUXILIARY_MODULE_PREFIX "%s", *name);
}
+static void do_arm_smccc_entry(struct module *mod, void *symval)
+{
+ DEF_FIELD_ADDR(symval, arm_smccc_device_id, name);
+
+ module_alias_printf(mod, false, ARM_SMCCC_MODULE_PREFIX "%s", *name);
+}
+
/*
* Looks like: ssam:dNcNtNiNfN
*
@@ -1493,6 +1500,7 @@ static const struct devtable devtable[] = {
{"mhi", SIZE_mhi_device_id, do_mhi_entry},
{"mhi_ep", SIZE_mhi_device_id, do_mhi_ep_entry},
{"auxiliary", SIZE_auxiliary_device_id, do_auxiliary_entry},
+ {"arm_smccc", SIZE_arm_smccc_device_id, do_arm_smccc_entry},
{"ssam", SIZE_ssam_device_id, do_ssam_entry},
{"dfl", SIZE_dfl_device_id, do_dfl_entry},
{"ishtp", SIZE_ishtp_device_id, do_ishtp_entry},
--
2.43.0
^ permalink raw reply related
* [PATCH v7 0/6] Switch Arm SMCCC firmware services to an SMCCC bus
From: Aneesh Kumar K.V (Arm) @ 2026-06-11 13:04 UTC (permalink / raw)
To: linux-coco, linux-arm-kernel, linux-kernel
Cc: Aneesh Kumar K.V (Arm), Catalin Marinas, Greg KH, Jeremy Linton,
Jonathan Cameron, Lorenzo Pieralisi, Mark Rutland, Sudeep Holla,
Will Deacon, Steven Price, Suzuki K Poulose, Andre Przywara
As discussed here:
https://lore.kernel.org/all/20250728135216.48084-12-aneesh.kumar@kernel.org
The earlier CCA guest support used an arm-cca-dev platform device as a pure
software anchor for the TSM class device. That platform device did not
correspond to a DT/ACPI described device, MMIO range, interrupt, or other
platform resource; it existed only to make the CCA guest driver bind and to
place the resulting TSM device in the driver model. The same pattern also
exists for smccc_trng. Creating separate platform devices for such
SMCCC-discovered features is misleading, because those features are not
independent platform devices.
This series adds an Arm SMCCC bus for services discovered through the SMCCC
firmware interface. The bus provides SMCCC device and driver registration
helpers, name-based matching, uevent modalias generation, and a sysfs modalias
attribute. SMCCC service drivers can use MODULE_DEVICE_TABLE(arm_smccc, ...)
to emit arm_smccc:<name> aliases, allowing userspace to autoload service
drivers when the SMCCC core registers matching firmware-service devices.
The series then moves SMCCC TRNG and the Arm CCA guest RSI service off the
platform bus. When the SMCCC core discovers the corresponding firmware
service, it registers an arm-smccc device for that service. The hwrng
arm_smccc_trng driver and the Arm CCA guest TSM provider are converted to
SMCCC drivers that bind to those discovered devices.
The old arm-cca-dev platform device has also been used by userspace as a Realm
guest indicator. Removing it without a replacement would leave userspace
depending on an internal driver-binding device. This series therefore adds
/sys/firmware/cca/realm_guest as a stable, architecture-provided ABI for
detecting whether the kernel is running as an Arm CCA Realm guest, and then
removes the dummy arm-cca-dev platform-device registration.
Changes since v6:
* Move SMCCC bus-related code to bus.c.
* Remove CONFIG_ARM64 #ifdefs and switch device creation to use the generic function-ID support framework.
* Move version-specific checks and other conditionals to the device driver probe routines.
* Move RSI definitions to include/linux/arm-smccc-rsi.h.
* Split the file and variable renames into a separate patch.
Changes from v5:
https://lore.kernel.org/all/20260514094030.42495-1-aneesh.kumar@kernel.org
* Replace the arm-smccc platform-device plus auxiliary-child model with a
dedicated Arm SMCCC bus.
* Add SMCCC module alias support so SMCCC service drivers can use
MODULE_DEVICE_TABLE(arm_smccc, ...) and autoload through arm_smccc:<name>
aliases.
* Convert smccc_trng from a platform driver to an SMCCC driver.
* Convert the Arm CCA guest TSM provider from the arm-cca-dev platform device
to an SMCCC driver bound to the discovered RSI service.
* Add /sys/firmware/cca/realm_guest before removing the old arm-cca-dev dummy
platform device.
Changes from v4:
https://lore.kernel.org/all/20260427061615.905018-1-aneesh.kumar@kernel.org
* Add /sys/firmware/cca/realm_guest for detecting realm guest
* Convert smccc_trng to auxiliary device from platform device
Changes from v3:
https://lore.kernel.org/all/20260309100507.2303361-1-aneesh.kumar@kernel.org
* Rebased onto the latest kernel
* Drop pr_fmt() from drivers/firmware/smccc/rmm.c
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Greg KH <gregkh@linuxfoundation.org>
Cc: Jeremy Linton <jeremy.linton@arm.com>
Cc: Jonathan Cameron <jic23@kernel.org>
Cc: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Steven Price <steven.price@arm.com>
Cc: Suzuki K Poulose <Suzuki.Poulose@arm.com>
Cc: Andre Przywara <andre.przywara@arm.com>
Aneesh Kumar K.V (Arm) (6):
firmware: smccc: Add an Arm SMCCC bus
firmware: hwrng: arm_smccc_trng: Register as an SMCCC device
firmware: smccc: Move RSI definitions to include/linux
virt: coco: arm-cca-guest: Rename TSM report source file
firmware: smccc: arm-cca-guest: Bind the TSM provider to an SMCCC
device
coco: guest: arm64: Replace dummy CCA device with sysfs ABI
Documentation/ABI/testing/sysfs-firmware-cca | 10 ++
arch/arm64/include/asm/archrandom.h | 2 +-
arch/arm64/include/asm/rsi.h | 2 -
arch/arm64/include/asm/rsi_cmds.h | 74 +-------
arch/arm64/kernel/rsi.c | 39 +++--
drivers/char/hw_random/arm_smccc_trng.c | 32 ++--
drivers/firmware/smccc/Makefile | 2 +-
drivers/firmware/smccc/bus.c | 164 ++++++++++++++++++
drivers/firmware/smccc/smccc.c | 65 ++++++-
drivers/virt/coco/arm-cca-guest/Kconfig | 1 +
drivers/virt/coco/arm-cca-guest/Makefile | 2 +
.../{arm-cca-guest.c => arm-cca.c} | 62 +++----
drivers/virt/coco/arm-cca-guest/rsi.h | 84 +++++++++
include/linux/arm-smccc-bus.h | 49 ++++++
.../linux/arm-smccc-rsi.h | 8 +-
include/linux/mod_devicetable.h | 13 ++
scripts/mod/devicetable-offsets.c | 3 +
scripts/mod/file2alias.c | 8 +
18 files changed, 480 insertions(+), 140 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-firmware-cca
create mode 100644 drivers/firmware/smccc/bus.c
rename drivers/virt/coco/arm-cca-guest/{arm-cca-guest.c => arm-cca.c} (85%)
create mode 100644 drivers/virt/coco/arm-cca-guest/rsi.h
create mode 100644 include/linux/arm-smccc-bus.h
rename arch/arm64/include/asm/rsi_smc.h => include/linux/arm-smccc-rsi.h (97%)
base-commit: ddd664bbff63e09e7a7f9acae9c43605d4cf185f
--
2.43.0
^ permalink raw reply
* [PATCH/RFC 9/9] arm64: dts: renesas: ironhide: Switch to pure SCMI
From: Geert Uytterhoeven @ 2026-06-11 13:02 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Brian Masney,
Ulf Hansson
Cc: arm-scmi, linux-arm-kernel, devicetree, linux-clk, linux-pm,
linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <cover.1781171705.git.geert+renesas@glider.be>
Switch from CPG/MDLC remapping to pure SCMI:
- Add SCMI IDs for selected devices, supporting SCP FW SDK v4.28,
v4.31, and v4.32,
- Enable SCMI clock domain support,
- Replace clocks, power-domains, and resets properties by pure SCMI
references.
Note that the user must uncomment the line that defines
R_CAR_X5H_SCP_FW_SDK_VERSION to the SCP FW SDK version being used.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
This patch is included as a PoC.
This fails to apply, as the SCMI protocol subnodes are not added in this
series.
---
.../boot/dts/renesas/r8a78000-ironhide.dts | 44 +++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
index 00b5a010b7247722..f4093c359b21379e 100644
--- a/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
+++ b/arch/arm64/boot/dts/renesas/r8a78000-ironhide.dts
@@ -9,6 +9,45 @@
#include <dt-bindings/soc/renesas,r8a78000-mfis.h>
#include "r8a78000.dtsi"
+#define V4_28 4028
+#define V4_31 4031 // Supports v4.31 and v4.32
+
+//#define R_CAR_X5H_SCP_FW_SDK_VERSION V4_28
+#define R_CAR_X5H_SCP_FW_SDK_VERSION V4_31
+
+#if R_CAR_X5H_SCP_FW_SDK_VERSION == V4_28
+
+#define CLK_SGD4_PERW_BUS 1661
+#define MDLC_HSCIF0 228
+#define MDLC_HSCIF1 229
+#define MDLC_SCIF0 209
+#define MDLC_SCIF1 210
+#define MDLC_UFS1 203
+#define RESET_HSCIF0 228
+#define RESET_HSCIF1 229
+#define RESET_SCIF0 209
+#define RESET_SCIF1 210
+#define RESET_UFS1 203
+
+#elif R_CAR_X5H_SCP_FW_SDK_VERSION == V4_31
+
+#define CLK_SGD4_PERW_BUS 1657
+#define MDLC_HSCIF0 224
+#define MDLC_HSCIF1 225
+#define MDLC_SCIF0 205
+#define MDLC_SCIF1 206
+#define MDLC_UFS1 199
+#define RESET_HSCIF0 224
+#define RESET_HSCIF1 225
+#define RESET_SCIF0 205
+#define RESET_SCIF1 206
+#define RESET_UFS1 199
+
+#endif
+
+#define PD_APL 257
+#define PD_UFS1 13
+
/ {
model = "Renesas Ironhide board based on r8a78000";
compatible = "renesas,ironhide", "renesas,r8a78000";
@@ -35,6 +74,7 @@ scmi: scmi {
scmi_devpd: protocol@11 {
reg = <0x11>;
#power-domain-cells = <1>;
+ arm,clock-domain = <&scmi_clk>;
};
scmi_sys: protocol@12 {
@@ -138,6 +178,10 @@ &extalr_clk {
};
&hscif0 {
+ clocks = <&scmi_clk MDLC_HSCIF0>, <&scmi_clk CLK_SGD4_PERW_BUS>,
+ <&scif_clk>;
+ power-domains = <&scmi_devpd PD_APL>;
+ resets = <&scmi_reset RESET_HSCIF0>;
uart-has-rtscts;
status = "okay";
};
--
2.43.0
^ permalink raw reply related
* [PATCH/RFC 8/9] firmware: arm_scmi: quirk: Handle power management clocks on R-Car X5H
From: Geert Uytterhoeven @ 2026-06-11 13:02 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Brian Masney,
Ulf Hansson
Cc: arm-scmi, linux-arm-kernel, devicetree, linux-clk, linux-pm,
linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <cover.1781171705.git.geert+renesas@glider.be>
On Renesas R-Car X5H, power management of on-SoC modules is handled
through two methods: module power control and module clock gating. The
former is exposed as an SCMI power domain, the latter as an SCMI clock.
Currently the SCMI clock protocol does not support advertizing
power-management clocks yet. Add quirks to mark all module clocks
suitable for power management, reusing the existing clock_rcar_x5h_4_28
and clock_rcar_x5h_4_31 quirk keys.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
This patch is included as a PoC.
This does not build as the quirk keys are not added in this series.
---
drivers/firmware/arm_scmi/clock.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 42e666a628c732e5..c2648f9f283f2488 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -355,6 +355,18 @@ scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id,
return ret;
}
+#define QUIRK_RCAR_X5H_4_28_PM_CLK \
+ ({ \
+ if (clk_id <= 818 /* Last MDLC clock MDLC_GPIODM3 */) \
+ clk->pm_clk = true; \
+ })
+
+#define QUIRK_RCAR_X5H_4_31_PM_CLK \
+ ({ \
+ if (clk_id <= 814 /* Last MDLC clock MDLC_GPIODM3 */) \
+ clk->pm_clk = true; \
+ })
+
static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
u32 clk_id, struct clock_info *cinfo)
{
@@ -410,6 +422,9 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
if (SUPPORTS_EXTENDED_CONFIG(attributes))
clk->extended_config = true;
}
+
+ SCMI_QUIRK(clock_rcar_x5h_4_28, QUIRK_RCAR_X5H_4_28_PM_CLK);
+ SCMI_QUIRK(clock_rcar_x5h_4_31, QUIRK_RCAR_X5H_4_31_PM_CLK);
}
return ret;
--
2.43.0
^ permalink raw reply related
* [PATCH/RFC 7/9] pmdomain: arm: scmi: Add clock domain support
From: Geert Uytterhoeven @ 2026-06-11 13:02 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Brian Masney,
Ulf Hansson
Cc: arm-scmi, linux-arm-kernel, devicetree, linux-clk, linux-pm,
linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <cover.1781171705.git.geert+renesas@glider.be>
PM domain consumer devices may also part of a clock domain.
Add support for managing power management clocks in a clock domain
through Runtime PM.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
drivers/pmdomain/arm/Kconfig | 1 +
drivers/pmdomain/arm/scmi_pm_domain.c | 81 ++++++++++++++++++++++++++-
2 files changed, 81 insertions(+), 1 deletion(-)
diff --git a/drivers/pmdomain/arm/Kconfig b/drivers/pmdomain/arm/Kconfig
index afed10d382ad7f66..11c4db47c1eadab0 100644
--- a/drivers/pmdomain/arm/Kconfig
+++ b/drivers/pmdomain/arm/Kconfig
@@ -14,6 +14,7 @@ config ARM_SCMI_PERF_DOMAIN
config ARM_SCMI_POWER_DOMAIN
tristate "SCMI power domain driver"
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
+ depends on COMMON_CLK_SCMI || !COMMON_CLK_SCMI
default ARM_SCMI_PROTOCOL
select PM_GENERIC_DOMAINS if PM
help
diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c
index 8e67f971c707e121..838917d3b236e3aa 100644
--- a/drivers/pmdomain/arm/scmi_pm_domain.c
+++ b/drivers/pmdomain/arm/scmi_pm_domain.c
@@ -5,9 +5,12 @@
* Copyright (C) 2018-2021 ARM Ltd.
*/
+#include <linux/clk.h>
+#include <linux/clk/scmi.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/pm_clock.h>
#include <linux/pm_domain.h>
#include <linux/scmi_protocol.h>
@@ -16,6 +19,7 @@ static const struct scmi_power_proto_ops *power_ops;
struct scmi_pm_domain {
struct generic_pm_domain genpd;
const struct scmi_protocol_handle *ph;
+ struct device_node *clock_domain;
const char *name;
u32 domain;
};
@@ -39,6 +43,67 @@ static int scmi_pd_power_off(struct generic_pm_domain *domain)
return scmi_pd_power(domain, SCMI_POWER_STATE_GENERIC_OFF);
}
+static int scmi_pd_attach_dev(struct generic_pm_domain *domain,
+ struct device *dev)
+{
+ struct scmi_pm_domain *pd = to_scmi_pd(domain);
+ struct device_node *np = dev->of_node;
+ struct of_phandle_args clkspec;
+ bool once = true;
+ struct clk *clk;
+ int ret;
+
+ for (int i = 0;
+ !of_parse_phandle_with_args(np, "clocks", "#clock-cells", i, &clkspec);
+ i++) {
+ if (clkspec.np != pd->clock_domain || clkspec.args_count != 1) {
+ of_node_put(clkspec.np);
+ continue;
+ }
+
+ clk = of_clk_get_from_provider(&clkspec);
+ of_node_put(clkspec.np);
+ if (!clk)
+ continue;
+
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ clk = NULL;
+ goto fail;
+ }
+
+ if (!scmi_clk_is_pm_clk(clk)) {
+ clk_put(clk);
+ continue;
+ }
+
+ if (once) {
+ once = false;
+ ret = pm_clk_create(dev);
+ if (ret)
+ goto fail;
+ }
+
+ ret = pm_clk_add_clk(dev, clk);
+ if (ret)
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ pm_clk_destroy(dev);
+ clk_put(clk);
+ return ret;
+}
+
+static void scmi_pd_detach_dev(struct generic_pm_domain *domain,
+ struct device *dev)
+{
+ if (!pm_clk_no_clocks(dev))
+ pm_clk_destroy(dev);
+}
+
static int scmi_pm_domain_probe(struct scmi_device *sdev)
{
int num_domains, i, ret;
@@ -48,6 +113,7 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
struct genpd_onecell_data *scmi_pd_data;
struct generic_pm_domain **domains;
const struct scmi_handle *handle = sdev->handle;
+ struct device_node *clock_domain;
struct scmi_protocol_handle *ph;
if (!handle)
@@ -75,6 +141,8 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
if (!domains)
return -ENOMEM;
+ clock_domain = of_parse_phandle(np, "arm,clock-domain", 0);
+
for (i = 0; i < num_domains; i++, scmi_pd++) {
const struct scmi_power_domain_info *info;
u32 state;
@@ -106,6 +174,12 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
scmi_pd->genpd.power_on = scmi_pd_power_on;
scmi_pd->genpd.flags = GENPD_FLAG_ACTIVE_WAKEUP |
info->genpd_flags;
+ if (clock_domain) {
+ scmi_pd->clock_domain = of_node_get(clock_domain);
+ scmi_pd->genpd.attach_dev = scmi_pd_attach_dev;
+ scmi_pd->genpd.detach_dev = scmi_pd_detach_dev;
+ scmi_pd->genpd.flags |= GENPD_FLAG_PM_CLK;
+ }
pm_genpd_init(&scmi_pd->genpd, NULL,
state == SCMI_POWER_STATE_GENERIC_OFF);
@@ -113,6 +187,8 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
domains[i] = &scmi_pd->genpd;
}
+ of_node_put(clock_domain);
+
scmi_pd_data->domains = domains;
scmi_pd_data->num_domains = num_domains;
@@ -134,8 +210,10 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
return 0;
err_rm_genpds:
- for (i = num_domains - 1; i >= 0; i--)
+ for (i = num_domains - 1; i >= 0; i--) {
pm_genpd_remove(domains[i]);
+ of_node_put(to_scmi_pd(domains[i])->clock_domain);
+ }
return ret;
}
@@ -158,6 +236,7 @@ static void scmi_pm_domain_remove(struct scmi_device *sdev)
if (!scmi_pd_data->domains[i])
continue;
pm_genpd_remove(scmi_pd_data->domains[i]);
+ of_node_put(to_scmi_pd(scmi_pd_data->domains[i])->clock_domain);
}
}
--
2.43.0
^ permalink raw reply related
* [PATCH/RFC 6/9] dt-bindings: firmware: arm,scmi: Document arm,clock-domain
From: Geert Uytterhoeven @ 2026-06-11 13:02 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Brian Masney,
Ulf Hansson
Cc: arm-scmi, linux-arm-kernel, devicetree, linux-clk, linux-pm,
linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <cover.1781171705.git.geert+renesas@glider.be>
Power management of on-SoC modules is typically handled through two
methods: module power control and module clock gating. The former is
exposed as an SCMI power domain, the latter as an SCMI clock.
Document the new arm,clock-domain property, to link the SCMI power domain
provider to the clock domain provider that is responsible for module
clock gating.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Documentation/devicetree/bindings/firmware/arm,scmi.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
index d06cca9273c483c0..18e4da3884c4c12e 100644
--- a/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
+++ b/Documentation/devicetree/bindings/firmware/arm,scmi.yaml
@@ -174,6 +174,13 @@ properties:
'#power-domain-cells':
const: 1
+ arm,clock-domain:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ When PM domain consumer devices are also part of a clock domain, this
+ property should be a reference to the SCMI clock protocol node for
+ the power management clocks in the clock domain.
+
required:
- '#power-domain-cells'
--
2.43.0
^ permalink raw reply related
* [PATCH/RFC 5/9] clk: scmi: Add scmi_clk_is_pm_clk()
From: Geert Uytterhoeven @ 2026-06-11 13:02 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Brian Masney,
Ulf Hansson
Cc: arm-scmi, linux-arm-kernel, devicetree, linux-clk, linux-pm,
linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <cover.1781171705.git.geert+renesas@glider.be>
Add a helper for querying if an SCMI clock can be used for
power-management.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
drivers/clk/clk-scmi.c | 9 +++++++++
include/linux/clk/scmi.h | 17 +++++++++++++++++
2 files changed, 26 insertions(+)
create mode 100644 include/linux/clk/scmi.h
diff --git a/drivers/clk/clk-scmi.c b/drivers/clk/clk-scmi.c
index 7c562559ad8bb47f..20e6da1859290b75 100644
--- a/drivers/clk/clk-scmi.c
+++ b/drivers/clk/clk-scmi.c
@@ -7,6 +7,7 @@
#include <linux/bits.h>
#include <linux/clk-provider.h>
+#include <linux/clk/scmi.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/of.h>
@@ -364,6 +365,14 @@ scmi_clk_ops_select(struct scmi_clk *sclk, bool atomic_capable,
return ops;
}
+bool scmi_clk_is_pm_clk(struct clk *clk)
+{
+ struct clk_hw *hw = __clk_get_hw(clk);
+
+ return hw && to_scmi_clk(hw)->info->pm_clk;
+}
+EXPORT_SYMBOL_GPL(scmi_clk_is_pm_clk);
+
static int scmi_clocks_probe(struct scmi_device *sdev)
{
int idx, count, err;
diff --git a/include/linux/clk/scmi.h b/include/linux/clk/scmi.h
new file mode 100644
index 0000000000000000..12c338598d09296f
--- /dev/null
+++ b/include/linux/clk/scmi.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __LINUX_CLK_SCMI_H_
+#define __LINUX_CLK_SCMI_H_
+
+#include <linux/types.h>
+
+struct clk;
+
+#if IS_ENABLED(CONFIG_COMMON_CLK_SCMI)
+bool scmi_clk_is_pm_clk(struct clk *clk);
+#else
+static inline bool scmi_clk_is_pm_clk(struct clk *clk) { return false; }
+#endif
+
+#endif /* __LINUX_CLK_SCMI_H_ */
--
2.43.0
^ permalink raw reply related
* [PATCH/RFC 4/9] firmware: arm_scmi: Add a flag for power-management clocks
From: Geert Uytterhoeven @ 2026-06-11 13:02 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Brian Masney,
Ulf Hansson
Cc: arm-scmi, linux-arm-kernel, devicetree, linux-clk, linux-pm,
linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <cover.1781171705.git.geert+renesas@glider.be>
Power management of on-SoC modules is typically handled through two
methods: module power control and module clock gating. The former is
exposed as an SCMI power domain, the latter as an SCMI clock.
Add a flag to indicate if a clock is intended for power-management of a
hardware module.
As the SCMI clock protocol does not support advertizing power-management
clocks yet, this flag can only be set by a quirk.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
include/linux/scmi_protocol.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 1d55374bc8cdcc72..c98e0add25f0c6c6 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -55,6 +55,7 @@ struct scmi_clock_info {
bool rate_ctrl_forbidden;
bool parent_ctrl_forbidden;
bool extended_config;
+ bool pm_clk;
u64 min_rate;
u64 max_rate;
int num_parents;
--
2.43.0
^ permalink raw reply related
* [PATCH/RFC 3/9] pmdomain: arm: scmi: Add always-on support
From: Geert Uytterhoeven @ 2026-06-11 13:02 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Brian Masney,
Ulf Hansson
Cc: arm-scmi, linux-arm-kernel, devicetree, linux-clk, linux-pm,
linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <cover.1781171705.git.geert+renesas@glider.be>
Extend the PM domain flags with the flags advertized by the SCMI core.
For now this is limited to GENPD_FLAG_ALWAYS_ON.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
Questions:
- Should the power_ops->state_get() call be skipped in case of an
always-on domain, and state just be force to
SCMI_POWER_STATE_GENERIC_ON instead?
- Should the power_ops->state_set() call be skipped in case of an
always-on domain? Or might this cause issues with some firmware
implementations?
Skipping these calls may avoid adding quirk code later...
---
drivers/pmdomain/arm/scmi_pm_domain.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c
index 965592e828741b11..8e67f971c707e121 100644
--- a/drivers/pmdomain/arm/scmi_pm_domain.c
+++ b/drivers/pmdomain/arm/scmi_pm_domain.c
@@ -104,7 +104,8 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
scmi_pd->genpd.name = scmi_pd->name;
scmi_pd->genpd.power_off = scmi_pd_power_off;
scmi_pd->genpd.power_on = scmi_pd_power_on;
- scmi_pd->genpd.flags = GENPD_FLAG_ACTIVE_WAKEUP;
+ scmi_pd->genpd.flags = GENPD_FLAG_ACTIVE_WAKEUP |
+ info->genpd_flags;
pm_genpd_init(&scmi_pd->genpd, NULL,
state == SCMI_POWER_STATE_GENERIC_OFF);
--
2.43.0
^ permalink raw reply related
* [PATCH/RFC 2/9] firmware: arm_scmi: Advertize always-on power domains
From: Geert Uytterhoeven @ 2026-06-11 13:02 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Brian Masney,
Ulf Hansson
Cc: arm-scmi, linux-arm-kernel, devicetree, linux-clk, linux-pm,
linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <cover.1781171705.git.geert+renesas@glider.be>
A power domains indicates in its attribute flags if it supports setting
its power state synchronously and/or asynchronously. If none of them is
supported, it must be an always-on power domain.
Make this information available to SCMI protocol drivers, so they can
make use of it.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
drivers/firmware/arm_scmi/power.c | 4 ++++
include/linux/scmi_protocol.h | 1 +
2 files changed, 5 insertions(+)
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index a00f7c298efb74f9..11ca8c9965110b5a 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -8,6 +8,7 @@
#define pr_fmt(fmt) "SCMI Notifications POWER - " fmt
#include <linux/module.h>
+#include <linux/pm_domain.h>
#include <linux/scmi_protocol.h>
#include "protocols.h"
@@ -147,6 +148,9 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
SCMI_MAX_STR_SIZE);
}
+ if (!ret && !dom_info->state_set_async && !dom_info->state_set_sync)
+ dom_info->info.genpd_flags |= GENPD_FLAG_ALWAYS_ON;
+
return ret;
}
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 1c17515ba45d1fd4..1d55374bc8cdcc72 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -193,6 +193,7 @@ struct scmi_perf_proto_ops {
struct scmi_power_domain_info {
char name[SCMI_MAX_STR_SIZE];
+ unsigned int genpd_flags;
};
/**
--
2.43.0
^ permalink raw reply related
* [PATCH/RFC 1/9] firmware: arm_scmi: Replace scmi_power_proto_ops.name_get() by .info_get()
From: Geert Uytterhoeven @ 2026-06-11 13:02 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Brian Masney,
Ulf Hansson
Cc: arm-scmi, linux-arm-kernel, devicetree, linux-clk, linux-pm,
linux-renesas-soc, linux-kernel, Geert Uytterhoeven
In-Reply-To: <cover.1781171705.git.geert+renesas@glider.be>
The SCMI power domain protocol operations structure does not provide a
.info_get() method, unlike most other protocols. Instead, it provides
a .name_get() method.
Replace the .name_get() method by the .info_get() method, to increase
uniformity, and to prepare for returning more information.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
drivers/firmware/arm_scmi/power.c | 17 ++++++++---------
drivers/pmdomain/arm/scmi_pm_domain.c | 9 ++++++++-
include/linux/scmi_protocol.h | 10 +++++++---
3 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c
index 28ef63a4ecc2e1df..a00f7c298efb74f9 100644
--- a/drivers/firmware/arm_scmi/power.c
+++ b/drivers/firmware/arm_scmi/power.c
@@ -63,7 +63,7 @@ struct power_dom_info {
bool state_set_sync;
bool state_set_async;
bool state_set_notify;
- char name[SCMI_MAX_STR_SIZE];
+ struct scmi_power_domain_info info;
};
struct scmi_power_info {
@@ -132,7 +132,7 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
SUPPORTS_STATE_SET_NOTIFY(flags);
dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
- strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
+ strscpy(dom_info->info.name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
}
ph->xops->xfer_put(ph, t);
@@ -143,7 +143,7 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
SUPPORTS_EXTENDED_NAMES(flags)) {
ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET,
- domain, NULL, dom_info->name,
+ domain, NULL, dom_info->info.name,
SCMI_MAX_STR_SIZE);
}
@@ -199,23 +199,22 @@ static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph)
return pi->num_domains;
}
-static const char *
-scmi_power_name_get(const struct scmi_protocol_handle *ph,
- u32 domain)
+static const struct scmi_power_domain_info *
+scmi_power_info_get(const struct scmi_protocol_handle *ph, u32 domain)
{
struct scmi_power_info *pi = ph->get_priv(ph);
struct power_dom_info *dom;
if (domain >= pi->num_domains)
- return "unknown";
+ return NULL;
dom = pi->dom_info + domain;
- return dom->name;
+ return &dom->info;
}
static const struct scmi_power_proto_ops power_proto_ops = {
.num_domains_get = scmi_power_num_domains_get,
- .name_get = scmi_power_name_get,
+ .info_get = scmi_power_info_get,
.state_set = scmi_power_state_set,
.state_get = scmi_power_state_get,
};
diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c
index 3d73aef21d2f9942..965592e828741b11 100644
--- a/drivers/pmdomain/arm/scmi_pm_domain.c
+++ b/drivers/pmdomain/arm/scmi_pm_domain.c
@@ -76,8 +76,15 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
return -ENOMEM;
for (i = 0; i < num_domains; i++, scmi_pd++) {
+ const struct scmi_power_domain_info *info;
u32 state;
+ info = power_ops->info_get(ph, i);
+ if (!info) {
+ dev_warn(dev, "failed to get info for domain %d\n", i);
+ continue;
+ }
+
if (power_ops->state_get(ph, i, &state)) {
dev_warn(dev, "failed to get state for domain %d\n", i);
continue;
@@ -93,7 +100,7 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
scmi_pd->domain = i;
scmi_pd->ph = ph;
- scmi_pd->name = power_ops->name_get(ph, i);
+ scmi_pd->name = info->name;
scmi_pd->genpd.name = scmi_pd->name;
scmi_pd->genpd.power_off = scmi_pd_power_off;
scmi_pd->genpd.power_on = scmi_pd_power_on;
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 5ab73b1ab9aa4fa8..1c17515ba45d1fd4 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -191,19 +191,23 @@ struct scmi_perf_proto_ops {
enum scmi_power_scale (*power_scale_get)(const struct scmi_protocol_handle *ph);
};
+struct scmi_power_domain_info {
+ char name[SCMI_MAX_STR_SIZE];
+};
+
/**
* struct scmi_power_proto_ops - represents the various operations provided
* by SCMI Power Protocol
*
* @num_domains_get: get the count of power domains provided by SCMI
- * @name_get: gets the name of a power domain
+ * @info_get: gets the information of the specified power domain
* @state_set: sets the power state of a power domain
* @state_get: gets the power state of a power domain
*/
struct scmi_power_proto_ops {
int (*num_domains_get)(const struct scmi_protocol_handle *ph);
- const char *(*name_get)(const struct scmi_protocol_handle *ph,
- u32 domain);
+ const struct scmi_power_domain_info __must_check *(*info_get)
+ (const struct scmi_protocol_handle *ph, u32 domain);
#define SCMI_POWER_STATE_TYPE_SHIFT 30
#define SCMI_POWER_STATE_ID_MASK (BIT(28) - 1)
#define SCMI_POWER_STATE_PARAM(type, id) \
--
2.43.0
^ permalink raw reply related
* [PATCH/RFC 0/9] R-Car X5H Ironhide pure SCMI proof-of-concept
From: Geert Uytterhoeven @ 2026-06-11 13:02 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Michael Turquette, Stephen Boyd, Brian Masney,
Ulf Hansson
Cc: arm-scmi, linux-arm-kernel, devicetree, linux-clk, linux-pm,
linux-renesas-soc, linux-kernel, Geert Uytterhoeven
Hi all,
As promised[1], I tried handling all issues with current R-Car X5H
Ironhide SCP FW SDK v4.28, v4.31, and v4.32 as SCMI quirks. This helped
identifying missing features in the SCMI drivers and/or protocol:
- The SCMI PM domain driver does not support always-on domains.
This limits the ability of the Linux Power Management core to
optimize its decisions by taking into account which PM domains
cannot be disabled,
- The SCMI protocol does not support clock domains.
- Power management of on-SoC modules is typically handled through two
methods: module power control and module clock gating. The former
can be exposed as an SCMI power domain, the latter as an SCMI clock.
Currently, the SCMI clock protocol does not support advertizing
whether a clock is intended for power-management of a hardware
module, so such clocks can not be managed automatically through
Runtime PM. To solve this in general, the SCMI CLOCK_ATTRIBUTES
could be extended with a new flag in the returned attributes (which
is not done by this series).
Series overvies:
- Patch 1 is a preparatory refactoring,
- Patches 2-3 add support for always-on power areas,
- Patches 4-7 add support for clock domains,
- Patch 8 adds an SCMI quirk to advertize power-management clocks on
R-Car X5H,
- Patch 9 switches the (still minimal) Ironhide DTS to SCMI (this
needs manual configuration for the actual SCP firmware version, as
the SCMI domain IDs differ).
Note that other SCMI quirks than patch 8 are not included in this
series[2], as IMHO the original issues must be fixed in the SCP firmware
instead.
While the result works, and we might get stable SCMI domain IDs
(eventually), I still prefer the approach taking by the CPG/MDLC
remapping driver series[3], as:
- It describes in DT the actual hardware (which is needed for U-Boot
IPL),
- It does not depend on SCMI domain IDs defined by firmware that is
still under active development,
- It lets us keep the DTB stable, while SCMI domain IDs may change,
depending on system partitioning (i.e. software policy),
- It allows us to support (in the Renesas LTS tree, not upstream)
firmware versions that already exist, but need quirks for proper
operation.
Thanks for your comments!
[1] "Re: [PATCH/RFC 05/14] firmware: arm_scmi: Add scmi_get_base_info()"
https://lore.kernel.org/CAMuHMdWJvMH+a1RqozbaCxxH_8M569JcruTFa8PW+87FysnjHw@mail.gmail.com
[2] List of SCMI quirks which are not included in this series, but are
needed to boot on R-Car X5H Ironhide:
firmware: arm_scmi: quirk: Handle critical clocks on R-Car X5H
firmware: arm_scmi: quirk: Handle bad power domains on R-Car X5H
firmware: arm_scmi: quirk: Handle bad clocks on R-Car X5H
firmware: arm_scmi: quirk: Handle wrong clock rates on R-Car X5H
firmware: arm_scmi: Add support for retrieving rates from another clock
firmware: arm_scmi: quirk: Handle zero clock rates on R-Car X5H
firmware: arm_scmi: quirk: Add always-on power domains on R-Car X5H
firmware: arm_scmi: quirk: Handle broken HSCIF0 reset on R-Car X5H
With diffstat:
drivers/firmware/arm_scmi/clock.c | 2555 +++++++++++++++++++++++++++-
drivers/firmware/arm_scmi/power.c | 83 +
drivers/firmware/arm_scmi/quirks.c | 12 +
drivers/firmware/arm_scmi/quirks.h | 4 +
drivers/firmware/arm_scmi/reset.c | 9 +
5 files changed, 2655 insertions(+), 8 deletions(-)
[3] "[PATCH/RFC 00/14] R-Car X5H Ironhide SCMI CPG/MDLC remapping"
https://lore.kernel.org/cover.1776793163.git.geert+renesas@glider.be
Geert Uytterhoeven (9):
firmware: arm_scmi: Replace scmi_power_proto_ops.name_get() by
.info_get()
firmware: arm_scmi: Advertize always-on power domains
pmdomain: arm: scmi: Add always-on support
firmware: arm_scmi: Add a flag for power-management clocks
clk: scmi: Add scmi_clk_is_pm_clk()
dt-bindings: firmware: arm,scmi: Document arm,clock-domain
pmdomain: arm: scmi: Add clock domain support
firmware: arm_scmi: quirk: Handle power management clocks on R-Car X5H
arm64: dts: renesas: ironhide: Switch to pure SCMI
.../bindings/firmware/arm,scmi.yaml | 7 ++
.../boot/dts/renesas/r8a78000-ironhide.dts | 44 +++++++++
drivers/clk/clk-scmi.c | 9 ++
drivers/firmware/arm_scmi/clock.c | 15 +++
drivers/firmware/arm_scmi/power.c | 21 +++--
drivers/pmdomain/arm/Kconfig | 1 +
drivers/pmdomain/arm/scmi_pm_domain.c | 93 ++++++++++++++++++-
include/linux/clk/scmi.h | 17 ++++
include/linux/scmi_protocol.h | 12 ++-
9 files changed, 204 insertions(+), 15 deletions(-)
create mode 100644 include/linux/clk/scmi.h
--
2.43.0
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [RFC PATCH 5/6] arm64: execmem: enable EXECMEM_ROX_CACHE on supported CPUs
From: Adrian Barnaś @ 2026-06-11 13:01 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-mm, Adrian Barnaś, Catalin Marinas, Will Deacon,
Ryan Roberts, David Hildenbrand, Mike Rapoport (Microsoft),
Ard Biesheuvel, Christoph Lameter, Yang Shi, Brendan Jackman
In-Reply-To: <20260611130144.1385343-1-abarnas@google.com>
Enable EXECMEM_ROX_CACHE support for ARM64 systems that implement
the bbml2_no_abort CPU feature.
Using the ROX cache brings a performance boost by reducing linear region
fragmentation caused by strict memory permissions (e.g., W^X enforcement).
Grouping executable code (which is read-only in the linear region alias)
into PMD-sized block mappings reduces TLB pressure and page table size.
This is only enabled on systems with bbml2_no_abort, as splitting
these large blocks to make pages writable during module loading would
otherwise risk triggering TLB Conflict Aborts.
Signed-off-by: Adrian Barnaś <abarnas@google.com>
---
arch/arm64/Kconfig | 1 +
arch/arm64/mm/init.c | 22 +++++++++++++++++++++-
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 38dba5f7e4d2..79c347ab841e 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -285,6 +285,7 @@ config ARM64
select USER_STACKTRACE_SUPPORT
select VDSO_GETRANDOM
select VMAP_STACK
+ select ARCH_HAS_EXECMEM_ROX
help
ARM 64-bit (AArch64) Linux support.
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 71aa745e0bef..8269d7747b84 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -420,6 +420,12 @@ void execmem_fill_trapping_insns(void *ptr, size_t size)
flush_icache_range((unsigned long)ptr, (unsigned long)ptr + size);
}
+
+#define MODULE_TEXT_FLAG EXECMEM_ROX_CACHE
+#define MODULE_TEXT_PGPROT PAGE_KERNEL_ROX
+#else
+#define MODULE_TEXT_FLAG (0)
+#define MODULE_TEXT_PGPROT PAGE_KERNEL
#endif
static u64 module_direct_base __ro_after_init = 0;
@@ -511,6 +517,8 @@ struct execmem_info __init *execmem_arch_setup(void)
{
unsigned long fallback_start = 0, fallback_end = 0;
unsigned long start = 0, end = 0;
+ enum execmem_range_flags module_text_flags = 0;
+ pgprot_t module_text_pgprot = PAGE_KERNEL;
module_init_limits();
@@ -531,12 +539,24 @@ struct execmem_info __init *execmem_arch_setup(void)
end = module_plt_base + SZ_2G;
}
+ /*
+ * The ROX Cache requires bbml2_no_abort because it uses large block
+ * mappings. On systems without this guarantee, splitting these blocks
+ * to make pages writable for module loading can trigger TLB Conflict
+ * Aborts.
+ */
+ if (system_supports_bbml2_noabort()) {
+ module_text_flags = MODULE_TEXT_FLAG;
+ module_text_pgprot = MODULE_TEXT_PGPROT;
+ }
+
execmem_info = (struct execmem_info){
.ranges = {
[EXECMEM_MODULE_TEXT] = {
.start = start,
.end = end,
- .pgprot = PAGE_KERNEL,
+ .flags = module_text_flags,
+ .pgprot = module_text_pgprot,
.alignment = 1,
.fallback_start = fallback_start,
.fallback_end = fallback_end,
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [RFC PATCH 6/6] arm64: mm: support PMD page coalescing in the linear map
From: Adrian Barnaś @ 2026-06-11 13:01 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-mm, Adrian Barnaś, Catalin Marinas, Will Deacon,
Ryan Roberts, David Hildenbrand, Mike Rapoport (Microsoft),
Ard Biesheuvel, Christoph Lameter, Yang Shi, Brendan Jackman
In-Reply-To: <20260611130144.1385343-1-abarnas@google.com>
Implement PMD block coalescing to merge fragmented linear mapping regions
back into huge pages when restoring the read-only attribute.
When memory allocated with VM_ALLOW_HUGE_VMAP (such as for the execmem
ROX cache) has its permissions modified, the PMD block mapping is split
into individual PTEs. Without this change, when that memory have its RO
attribute subsequently cleared and set the mapping remains permanently
fragmented into 4K pages.
Signed-off-by: Adrian Barnaś <abarnas@google.com>
---
arch/arm64/include/asm/mmu.h | 1 +
arch/arm64/mm/mmu.c | 95 ++++++++++++++++++++++++++++++++++++
arch/arm64/mm/pageattr.c | 7 +++
3 files changed, 103 insertions(+)
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index 137a173df1ff..19158bacb2df 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -80,6 +80,7 @@ extern void *fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot);
extern void mark_linear_text_alias_ro(void);
extern int split_kernel_leaf_mapping(unsigned long start, unsigned long end);
extern void linear_map_maybe_split_to_ptes(void);
+void try_collapse_kernel_pmd(unsigned long addr);
/*
* This check is triggered during the early boot before the cpufeature
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index a6a00accf4f9..d74226fa1c9b 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -769,6 +769,101 @@ static inline bool force_pte_mapping(void)
static DEFINE_MUTEX(pgtable_split_lock);
+static inline bool __pte_can_be_collapsed(pte_t pte, unsigned long pfn, pgprot_t prot)
+{
+ if (!pte_valid(pte))
+ return false;
+ if (pte_pfn(pte) != pfn)
+ return false;
+ if ((pgprot_val(pte_pgprot(pte)) & ~PTE_CONT) != pgprot_val(prot))
+ return false;
+
+ return true;
+}
+
+static void __try_collapse_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr)
+{
+ pte_t *ptep;
+ pte_t first_pte;
+ unsigned long pfn;
+ pgprot_t prot;
+ int i;
+
+ ptep = (pte_t *)pmd_page_vaddr(pmd);
+ first_pte = __ptep_get(ptep);
+
+ if (!pte_valid(first_pte))
+ return;
+
+ prot = pte_pgprot(first_pte);
+ prot = __pgprot(pgprot_val(prot) & ~PTE_CONT);
+ pfn = pte_pfn(first_pte);
+
+ if (!IS_ALIGNED(pfn, PMD_SIZE >> PAGE_SHIFT))
+ return;
+
+ for (i = 1; i < PTRS_PER_PTE; i++) {
+ if (!__pte_can_be_collapsed(__ptep_get(ptep + i), pfn + i, prot))
+ return;
+ }
+
+ set_pmd(pmdp, pmd_mkhuge(pfn_pmd(pfn, prot)));
+
+ __flush_tlb_kernel_pgtable(addr);
+
+ if (static_branch_unlikely(&arm64_ptdump_lock_key)) {
+ mmap_read_lock(&init_mm);
+ mmap_read_unlock(&init_mm);
+ }
+
+ pte_free_kernel(NULL, ptep);
+}
+
+void try_collapse_kernel_pmd(unsigned long addr)
+{
+ pgd_t *pgdp;
+ p4d_t *p4dp;
+ pud_t *pudp;
+ pmd_t *pmdp;
+ pmd_t pmd;
+
+ /*
+ * collapse_pmd expects exact address of block to be collapsed
+ */
+ if (WARN_ON(ALIGN_DOWN(addr, PMD_SIZE) != addr))
+ return;
+
+ mutex_lock(&pgtable_split_lock);
+
+ pgdp = pgd_offset_k(addr);
+ if (pgd_none(READ_ONCE(*pgdp)))
+ goto out;
+
+ p4dp = p4d_offset(pgdp, addr);
+ if (p4d_none(READ_ONCE(*p4dp)))
+ goto out;
+
+ pudp = pud_offset(p4dp, addr);
+ if (pud_none(READ_ONCE(*pudp)))
+ goto out;
+
+ if (pud_leaf(READ_ONCE(*pudp)))
+ goto out;
+
+ pmdp = pmd_offset(pudp, addr);
+ pmd = pmdp_get(pmdp);
+
+ if (!pmd_table(pmd))
+ goto out;
+
+ lazy_mmu_mode_enable();
+ __try_collapse_pmd(pmdp, pmd, addr);
+ lazy_mmu_mode_disable();
+
+out:
+ mutex_unlock(&pgtable_split_lock);
+}
+
int split_kernel_leaf_mapping(unsigned long start, unsigned long end)
{
int ret;
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index eaefdf90b0d5..11e0b60264c3 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -200,6 +200,13 @@ static int change_memory_common(unsigned long addr, int numpages,
if (ret)
return ret;
}
+ /*
+ * When setting a read-only flag on the linear region, the memory
+ * may have been backed by a PMD before being split. Try to
+ * collapse it back into a PMD to restore huge page performance.
+ */
+ if (pgprot_val(set_mask) == PTE_RDONLY && area->flags & VM_ALLOW_HUGE_VMAP)
+ try_collapse_kernel_pmd((u64)page_address(area->pages[0]));
}
/*
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [RFC PATCH 4/6] arm64: mm: add helper to fill execmem with trapping instructions
From: Adrian Barnaś @ 2026-06-11 13:01 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-mm, Adrian Barnaś, Catalin Marinas, Will Deacon,
Ryan Roberts, David Hildenbrand, Mike Rapoport (Microsoft),
Ard Biesheuvel, Christoph Lameter, Yang Shi, Brendan Jackman
In-Reply-To: <20260611130144.1385343-1-abarnas@google.com>
Implement the architecture-specific execmem_fill_trapping_insns() helper
to poison executable memory regions.
When CONFIG_ARCH_HAS_EXECMEM_ROX is enabled, the execmem subsystem
requires a way to fill unused or freed executable memory with
architecture-specific trapping instructions. This implementation fills
the specified region with AARCH64_BREAK_FAULT instructions and flushes
the icache to ensure the traps are immediately visible to execution.
Signed-off-by: Adrian Barnaś <abarnas@google.com>
---
arch/arm64/mm/init.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index c673a9a839dd..71aa745e0bef 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -408,6 +408,20 @@ void dump_mem_limit(void)
}
#ifdef CONFIG_EXECMEM
+
+#ifdef CONFIG_ARCH_HAS_EXECMEM_ROX
+void execmem_fill_trapping_insns(void *ptr, size_t size)
+{
+ int nr_inst = size / AARCH64_INSN_SIZE;
+ __le32 *updptr = ptr;
+
+ for (int i = 0; i < nr_inst; i++)
+ updptr[i] = cpu_to_le32(AARCH64_BREAK_FAULT);
+
+ flush_icache_range((unsigned long)ptr, (unsigned long)ptr + size);
+}
+#endif
+
static u64 module_direct_base __ro_after_init = 0;
static u64 module_plt_base __ro_after_init = 0;
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [RFC PATCH 3/6] arm64: mm: fix restoring linear map permissions on execmem cache clean
From: Adrian Barnaś @ 2026-06-11 13:01 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-mm, Adrian Barnaś, Catalin Marinas, Will Deacon,
Ryan Roberts, David Hildenbrand, Mike Rapoport (Microsoft),
Ard Biesheuvel, Christoph Lameter, Yang Shi, Brendan Jackman
In-Reply-To: <20260611130144.1385343-1-abarnas@google.com>
Strip the read-only attribute from the selected memory range when
restoring the linear map after an execmem cache clean.
An execmem cache clean is performed when a cache block becomes empty
after unloading a module. When making the memory valid again, the linear
memory alias must also have its read-only attribute cleared.
Without this change, the linear memory alias remains read-only even
after the execmem cache block itself is freed, which prevents subsequent
allocations from writing to that memory.
Signed-off-by: Adrian Barnaś <abarnas@google.com>
---
arch/arm64/mm/pageattr.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index 88720bbba892..eaefdf90b0d5 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -239,6 +239,13 @@ int set_memory_x(unsigned long addr, int numpages)
__pgprot(PTE_PXN));
}
+static int set_memory_default(unsigned long addr, int numpages)
+{
+ return __change_memory_common(addr, PAGE_SIZE * numpages,
+ __pgprot(PTE_VALID),
+ __pgprot(PTE_RDONLY));
+}
+
int set_memory_valid(unsigned long addr, int numpages, int enable)
{
if (enable)
@@ -362,7 +369,15 @@ int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid)
if (!can_set_direct_map())
return 0;
- return set_memory_valid(addr, nr, valid);
+ /*
+ * Execmem cache uses this function to reset permissions on linear mapping
+ * when freeing unused cache block. On x86 it makes memory RW which is
+ * desirable. On ARM64 set_memory_valid() just change valid bit which
+ * leave direct mapping read-only so use set_memory_default instead.
+ */
+
+ return valid ? set_memory_default(addr, nr) :
+ set_memory_valid(addr, nr, false);
}
#ifdef CONFIG_DEBUG_PAGEALLOC
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [RFC PATCH 2/6] arm64: mm: allow huge vmap permission adjustments with bbml2_no_abort
From: Adrian Barnaś @ 2026-06-11 13:01 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-mm, Adrian Barnaś, Catalin Marinas, Will Deacon,
Ryan Roberts, David Hildenbrand, Mike Rapoport (Microsoft),
Ard Biesheuvel, Christoph Lameter, Yang Shi, Brendan Jackman
In-Reply-To: <20260611130144.1385343-1-abarnas@google.com>
Remove the protection against huge vmap permission adjustments on
systems that support the bbml2_no_abort CPU feature.
Splitting live kernel VA section mappings into page mappings was
restricted because it could cause TLB Conflict Aborts. This forced
permission adjustments on memory allocated with VM_ALLOW_HUGE_VMAP to be
rejected, resulting in performance drops (e.g., when enforcing rodata=on
disables huge mappings).
The bbml2_no_abort feature (which mirrors the architectural guarantees of
FEAT_BBML3) ensures that changing between table and block sizes without
following a break-before-make sequence will not generate a TLB Conflict
Abort. This hardware guarantee makes it safe to allow dynamic permission
adjustments on huge vmap regions.
Signed-off-by: Adrian Barnaś <abarnas@google.com>
---
arch/arm64/mm/pageattr.c | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index 358d1dc9a576..88720bbba892 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -157,23 +157,29 @@ static int change_memory_common(unsigned long addr, int numpages,
}
/*
- * Kernel VA mappings are always live, and splitting live section
- * mappings into page mappings may cause TLB conflicts. This means
- * we have to ensure that changing the permission bits of the range
- * we are operating on does not result in such splitting.
- *
* Let's restrict ourselves to mappings created by vmalloc (or vmap).
- * Disallow VM_ALLOW_HUGE_VMAP mappings to guarantee that only page
- * mappings are updated and splitting is never needed.
*
* So check whether the [addr, addr + size) interval is entirely
* covered by precisely one VM area that has the VM_ALLOC flag set.
*/
area = find_vm_area((void *)addr);
+
if (!area ||
((unsigned long)kasan_reset_tag((void *)end) >
(unsigned long)kasan_reset_tag(area->addr) + area->size) ||
- ((area->flags & (VM_ALLOC | VM_ALLOW_HUGE_VMAP)) != VM_ALLOC))
+ !(area->flags & VM_ALLOC))
+ return -EINVAL;
+
+ /*
+ * Kernel VA mappings are always live, and splitting live section
+ * mappings into page mappings may cause TLB conflicts if bbml2_noabort
+ * is not present.
+ *
+ * While bbml2_noabort is not present disallow VM_ALLOW_HUGE_VMAP mappings
+ * to guarantee that only page mappings are updated and splitting is not
+ * needed.
+ */
+ if (!system_supports_bbml2_noabort() && (area->flags & (VM_ALLOW_HUGE_VMAP)))
return -EINVAL;
if (!numpages)
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [RFC PATCH 1/6] arm64: mm: explicitly declare module and ftrace execmem regions
From: Adrian Barnaś @ 2026-06-11 13:01 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-mm, Adrian Barnaś, Catalin Marinas, Will Deacon,
Ryan Roberts, David Hildenbrand, Mike Rapoport (Microsoft),
Ard Biesheuvel, Christoph Lameter, Yang Shi, Brendan Jackman
In-Reply-To: <20260611130144.1385343-1-abarnas@google.com>
Replace the reliance on the EXECMEM_DEFAULT fallback by explicitly defining
the execution memory (execmem) regions for MODULE_TEXT, MODULE_DATA, and
FTRACE in execmem_arch_setup().
Signed-off-by: Adrian Barnaś <abarnas@google.com>
---
arch/arm64/mm/init.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 96711b8578fd..c673a9a839dd 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -519,7 +519,7 @@ struct execmem_info __init *execmem_arch_setup(void)
execmem_info = (struct execmem_info){
.ranges = {
- [EXECMEM_DEFAULT] = {
+ [EXECMEM_MODULE_TEXT] = {
.start = start,
.end = end,
.pgprot = PAGE_KERNEL,
@@ -533,12 +533,28 @@ struct execmem_info __init *execmem_arch_setup(void)
.pgprot = PAGE_KERNEL_ROX,
.alignment = 1,
},
+ [EXECMEM_FTRACE] = {
+ .start = VMALLOC_START,
+ .end = VMALLOC_END,
+ .pgprot = PAGE_KERNEL,
+ .alignment = 1,
+ .fallback_start = fallback_start,
+ .fallback_end = fallback_end,
+ },
[EXECMEM_BPF] = {
.start = VMALLOC_START,
.end = VMALLOC_END,
.pgprot = PAGE_KERNEL,
.alignment = 1,
},
+ [EXECMEM_MODULE_DATA] = {
+ .start = start,
+ .end = end,
+ .pgprot = PAGE_KERNEL,
+ .alignment = 1,
+ .fallback_start = fallback_start,
+ .fallback_end = fallback_end,
+ },
},
};
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply related
* [RFC PATCH 0/6] arm64: mm: Introducing ROX CACHE to ARM64 systems with bbml2 no abort
From: Adrian Barnaś @ 2026-06-11 13:01 UTC (permalink / raw)
To: linux-arm-kernel
Cc: linux-mm, Adrian Barnaś, Catalin Marinas, Will Deacon,
Ryan Roberts, David Hildenbrand, Mike Rapoport (Microsoft),
Ard Biesheuvel, Christoph Lameter, Yang Shi, Brendan Jackman
Hi All,
I would like to propose the introduction of the EXECMEM_ROX_CACHE feature
for ARM64.
When the `rodata=on` kernel parameter is set, all executable and read-only
aliases in the linear map are forced to be read-only. Originally, this
forced PTE-level mappings across the entire linear map, causing a
significant performance regression (greater than 10%) for some workloads
due to increased TLB miss rates and deeper page table walk.
The `feat_bbml2_no_abort` (FEAT_BBML3 in ARMv9.7) feature can be
utilized to mitigate this regression. Because we can split memory
mappings without triggering TLB conflict aborts, kernel memory permission
adjustments become possible after early boot without forcing PTE-level
mappings across the entire linear map.
However, when applying read-only permissions to kernel module section the
linear map can still became fragmented due to the scattered physical layout
of the underlying pages. To address this, EXECMEM_ROX_CACHE, which was
initially enabled on the x86 architecture [1] for this purpose, can be
used on ARM64 as well.
EXECMEM_ROX_CACHE works by preallocating PMD-sized contiguous blocks to
act as a cache for .module.text memory. These blocks are initially
poisoned and made read-only-execute (which simultaneously makes the
linear alias of this region read-only). When loading a .module.text
section into memory, the requested cache region is made RW, the bytes
are copied, and ROX permissions are restored.
To take full advantage of this approach, after restoring RO permissions
on the PMD-sized linear alias block, the PTE mappings are coalesced back
into a single PMD entry.
Testing on an Android device running 6.18 based kernel with rodata=on
shows an average 20% reduction of level 3 page table entries for the
linear mapping.
This implementation currently works around some limitations of the
`set_memory_xx` API, which might be relevant when considering the
refactoring proposed here [2]:
* Because the execmem_cache operates outside the linear map, its
permissions could theoretically remain untouched (poisoned and RO)
until the cache block is fully emptied and freed. However, we currently
lack an API to interact exclusively with the vmalloc area (e.g., setting
it to RW) without simultaneously setting the linear alias to RW.
* Additionally, set_direct_map_valid() has somewhat confusing semantics.
It is used while "cleaning" a cache block (after confirming it is
entirely empty). The linear map region should be returned to its default
state to restore writability, but set_valid might just set the "valid"
attribute (as is the case for ARM64, which I have temporarily addressed
here with a workaround).
I would be glad to hear your feedback on these changes.
Best regards,
Adrian
[1]: https://lore.kernel.org/all/20241023162711.2579610-1-rppt@kernel.org/
[2]: https://lore.kernel.org/all/20260219175113.618562-1-jackmanb@google.com/
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: Ryan Roberts <ryan.roberts@arm.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: "Mike Rapoport (Microsoft)" <rppt@kernel.org>
Cc: Ard Biesheuvel <ardb@kernel.org>
Cc: Christoph Lameter <cl@gentwo.org>
Cc: Yang Shi <yang@os.amperecomputing.com>
Cc: Brendan Jackman <jackmanb@google.com>
--->8
Adrian Barnaś (6):
arm64: mm: explicitly declare module and ftrace execmem regions
arm64: mm: allow huge vmap permission adjustments with bbml2_no_abort
arm64: mm: fix restoring linear map permissions on execmem cache clean
arm64: mm: add helper to fill execmem with trapping instructions
arm64: execmem: enable EXECMEM_ROX_CACHE on supported CPUs
arm64: mm: support PMD page coalescing in the linear map
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/mmu.h | 1 +
arch/arm64/mm/init.c | 54 +++++++++++++++++++-
arch/arm64/mm/mmu.c | 95 ++++++++++++++++++++++++++++++++++++
arch/arm64/mm/pageattr.c | 46 +++++++++++++----
5 files changed, 186 insertions(+), 11 deletions(-)
--
2.54.0.1136.gdb2ca164c4-goog
^ permalink raw reply
* Re: [PATCH v3 2/2] clk: amlogic: Add A9 AO clock controller driver
From: Jian Hu @ 2026-06-11 13:01 UTC (permalink / raw)
To: Jerome Brunet, Jian Hu via B4 Relay
Cc: Neil Armstrong, Michael Turquette, Stephen Boyd, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Xianwei Zhao, Kevin Hilman,
Martin Blumenstingl, linux-amlogic, linux-clk, devicetree,
linux-kernel, linux-arm-kernel
In-Reply-To: <1jjys6fuhz.fsf@starbuckisacylon.baylibre.com>
On 6/10/2026 8:30 PM, Jerome Brunet wrote:
> [ EXTERNAL EMAIL ]
>
> On mer. 10 juin 2026 at 16:23, Jian Hu via B4 Relay <devnull+jian.hu.amlogic.com@kernel.org> wrote:
>
>> From: Jian Hu <jian.hu@amlogic.com>
>>
>> Add the Always-on clock controller driver for the Amlogic A9 SoC family.
>>
>> Signed-off-by: Jian Hu <jian.hu@amlogic.com>
>> ---
>> drivers/clk/meson/Kconfig | 13 ++
>> drivers/clk/meson/Makefile | 1 +
>> drivers/clk/meson/a9-aoclk.c | 431 +++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 445 insertions(+)
>>
>> diff --git a/drivers/clk/meson/Kconfig b/drivers/clk/meson/Kconfig
>> index cf8cf3f9e4ee..b71299898197 100644
>> --- a/drivers/clk/meson/Kconfig
>> +++ b/drivers/clk/meson/Kconfig
>> @@ -132,6 +132,19 @@ config COMMON_CLK_A1_PERIPHERALS
>> device, A1 SoC Family. Say Y if you want A1 Peripherals clock
>> controller to work.
>>
>> +config COMMON_CLK_A9_AO
>> + tristate "Amlogic A9 SoC AO clock controller support"
>> + depends on ARM64 || COMPILE_TEST
>> + default ARCH_MESON
>> + select COMMON_CLK_MESON_REGMAP
>> + select COMMON_CLK_MESON_CLKC_UTILS
>> + select COMMON_CLK_MESON_DUALDIV
>> + imply COMMON_CLK_SCMI
>> + help
>> + Support for the AO clock controller on Amlogic A311Y3 based
>> + device, AKA A9.
>> + Say Y if you want A9 AO clock controller to work.
>> +
>> config COMMON_CLK_C3_PLL
>> tristate "Amlogic C3 PLL clock controller"
>> depends on ARM64
>> diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile
>> index c6719694a242..f89d027c282c 100644
>> --- a/drivers/clk/meson/Makefile
>> +++ b/drivers/clk/meson/Makefile
>> @@ -19,6 +19,7 @@ obj-$(CONFIG_COMMON_CLK_AXG) += axg.o axg-aoclk.o
>> obj-$(CONFIG_COMMON_CLK_AXG_AUDIO) += axg-audio.o
>> obj-$(CONFIG_COMMON_CLK_A1_PLL) += a1-pll.o
>> obj-$(CONFIG_COMMON_CLK_A1_PERIPHERALS) += a1-peripherals.o
>> +obj-$(CONFIG_COMMON_CLK_A9_AO) += a9-aoclk.o
>> obj-$(CONFIG_COMMON_CLK_C3_PLL) += c3-pll.o
>> obj-$(CONFIG_COMMON_CLK_C3_PERIPHERALS) += c3-peripherals.o
>> obj-$(CONFIG_COMMON_CLK_GXBB) += gxbb.o gxbb-aoclk.o
>> diff --git a/drivers/clk/meson/a9-aoclk.c b/drivers/clk/meson/a9-aoclk.c
>> new file mode 100644
>> index 000000000000..dd9fd8d24702
>> --- /dev/null
>> +++ b/drivers/clk/meson/a9-aoclk.c
>> @@ -0,0 +1,431 @@
>> +// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
>> +/*
>> + * Copyright (C) 2026 Amlogic, Inc. All rights reserved
>> + */
>> +
>> +#include <dt-bindings/clock/amlogic,a9-aoclkc.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/platform_device.h>
>> +#include "clk-regmap.h"
>> +#include "clk-dualdiv.h"
>> +#include "meson-clkc-utils.h"
>> +
>> +#define AO_OSCIN_CTRL 0x00
>> +#define AO_SYS_CLK0 0x04
>> +#define AO_PWM_CLK_A_CTRL 0x1c
>> +#define AO_PWM_CLK_B_CTRL 0x20
>> +#define AO_PWM_CLK_C_CTRL 0x24
>> +#define AO_PWM_CLK_D_CTRL 0x28
>> +#define AO_PWM_CLK_E_CTRL 0x2c
>> +#define AO_PWM_CLK_F_CTRL 0x30
>> +#define AO_PWM_CLK_G_CTRL 0x34
>> +#define AO_CEC_CTRL0 0x38
>> +#define AO_CEC_CTRL1 0x3c
>> +#define AO_RTC_BY_OSCIN_CTRL0 0x50
>> +#define AO_RTC_BY_OSCIN_CTRL1 0x54
>> +
>> +#define A9_COMP_SEL(_name, _reg, _shift, _mask, _pdata) \
>> + MESON_COMP_SEL(a9_ao_, _name, _reg, _shift, _mask, _pdata, NULL, 0, 0)
>> +
>> +#define A9_COMP_DIV(_name, _reg, _shift, _width) \
>> + MESON_COMP_DIV(a9_ao_, _name, _reg, _shift, _width, 0, CLK_SET_RATE_PARENT)
>> +
>> +#define A9_COMP_GATE(_name, _reg, _bit) \
>> + MESON_COMP_GATE(a9_ao_, _name, _reg, _bit, CLK_SET_RATE_PARENT)
>> +
>> +static struct clk_regmap a9_ao_xtal_in = {
>> + .data = &(struct clk_regmap_gate_data){
>> + .offset = AO_OSCIN_CTRL,
>> + .bit_idx = 3,
>> + },
>> + .hw.init = &(struct clk_init_data) {
>> + .name = "ao_xtal_in",
>> + .ops = &clk_regmap_gate_ops,
>> + .parent_data = &(const struct clk_parent_data) {
>> + .fw_name = "xtal",
>> + },
>> + .num_parents = 1,
>> + /*
>> + * ao_sys can select different clock sources. One possible clock path is:
>> + * ao_xtal_in->ao_xtal->ao_sys-> ao sys gate clocks
>> + *
>> + * ao_xtal_in is in the parent chain of AO sys gate clocks.
>> + * Since some downstream clocks are marked CLK_IS_CRITICAL,
>> + * ao_xtal_in must remain enabled and is therefore marked
>> + * CLK_IS_CRITICAL as well.
>> + */
>> + .flags = CLK_IS_CRITICAL,
> Please allow some time for me to reply before reposting.
> See my answer on v2.
>
Sorry for reposting too quickly.
I'll allow more time for review feedback before sending the next revision.
I've seen your reply on v2 and will drop this flag in the next revision.
>> + },
>> +};
>> +
>> +static struct clk_regmap a9_ao_xtal = {
>> + .data = &(struct clk_regmap_mux_data) {
>> + .offset = AO_OSCIN_CTRL,
>> + .mask = 0x1,
>> + .shift = 0,
>> + },
>> + /* ext_32k is from external PAD, do not automatically reparent */
>> + .hw.init = CLK_HW_INIT_PARENTS_DATA("ao_xtal",
>> + ((const struct clk_parent_data []) {
>> + { .hw = &a9_ao_xtal_in.hw },
>> + { .fw_name = "ext_32k" }
>> + }), &clk_regmap_mux_ops, CLK_SET_RATE_NO_REPARENT),
> I hope my view on this is clear as well.
> Let me know if it isn't
>
Understood. I will drop all CLK_HW_INIT* macros and revert to explicit
struct clk_init_data initializers for the A9 clock controllers.
[ ... ]
> --
> Jerome
--
Jian
^ permalink raw reply
* [PATCH] wifi: mt76: fix airoha_npu dependency tracking
From: Arnd Bergmann @ 2026-06-11 12:58 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: Arnd Bergmann, Shayne Chen, Sean Wang, Rex Lu, linux-wireless,
linux-kernel, linux-arm-kernel, linux-mediatek
From: Arnd Bergmann <arnd@arndb.de>
There is a new build failure with MT7996E=m MT76_CORE=y and NET_AIROHA_NPU=m:
ld.lld: error: undefined symbol: airoha_npu_get
ld.lld: error: undefined symbol: airoha_npu_put
>>> referenced by npu.c
>>> drivers/net/wireless/mediatek/mt76/npu.o:(mt76_npu_init) in archive vmlinux.a
Fix this by reworking the dependency for the MT7996_NPU to only
allow enabling that when mt76_core can link against the npu driver.
To make sure this gets caught more easily in the future when additional
mt76 variants need the same dependency, also turn CONFIG_MT76_NPU into
a tristate symbol that has the same dependency.
Fixes: 7fb554b1b623 ("wifi: mt76: Introduce the NPU generic layer")
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
drivers/net/wireless/mediatek/mt76/Kconfig | 4 ++--
drivers/net/wireless/mediatek/mt76/Makefile | 6 +++++-
drivers/net/wireless/mediatek/mt76/mt76.h | 2 +-
drivers/net/wireless/mediatek/mt76/mt7996/Kconfig | 2 +-
4 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig
index 502303622a53..d941e67a222d 100644
--- a/drivers/net/wireless/mediatek/mt76/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/Kconfig
@@ -38,8 +38,8 @@ config MT792x_USB
select MT76_USB
config MT76_NPU
- bool
- depends on MT76_CORE
+ tristate
+ depends on NET_AIROHA_NPU=y || MT76=NET_AIROHA_NPU
source "drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig"
source "drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig"
diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile
index 1d42adfe8030..cacdd2b13d05 100644
--- a/drivers/net/wireless/mediatek/mt76/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/Makefile
@@ -12,7 +12,11 @@ mt76-y := \
mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \
tx.o agg-rx.o mcu.o wed.o scan.o channel.o
-mt76-$(CONFIG_MT76_NPU) += npu.o
+ifdef CONFIG_MT76_NPU
+# CONFIG_MT76_NPU is tristate to simplify dependency tracking,
+# but it behaves as a bool symbol here.
+mt76-y += npu.o
+endif
mt76-$(CONFIG_PCI) += pci.o
mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 07955555f84d..60bd155cc7d5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -1647,7 +1647,7 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);
-#ifdef CONFIG_MT76_NPU
+#if IS_ENABLED(CONFIG_MT76_NPU)
void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb,
u32 info);
int mt76_npu_dma_add_buf(struct mt76_phy *phy, struct mt76_queue *q,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
index 5503d03bf62c..5742bce12fbb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
@@ -16,6 +16,6 @@ config MT7996E
config MT7996_NPU
bool "MT7996 (PCIe) NPU support"
depends on MT7996E
- depends on NET_AIROHA_NPU=y || MT7996E=NET_AIROHA_NPU
+ depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU
select MT76_NPU
default n
--
2.39.5
^ permalink raw reply related
* Re: [RFC PATCH v2 1/3] mm/huge_memory: make persistent huge zero folio read-only
From: Lance Yang @ 2026-06-11 12:50 UTC (permalink / raw)
To: David Hildenbrand (Arm)
Cc: akpm, xueyuan.chen21, linux-mm, linux-kernel, linux-arm-kernel,
x86, catalin.marinas, will, tglx, mingo, bp, dave.hansen, luto,
peterz, hpa, ljs, liam, vbabka, rppt, surenb, mhocko, ziy,
baolin.wang, npache, ryan.roberts, dev.jain, baohua, yang, jannh,
dave.hansen
In-Reply-To: <d943a03e-bf28-4086-8907-5a24665fb2f5@kernel.org>
On 2026/6/11 20:21, David Hildenbrand (Arm) wrote:
> On 6/11/26 13:58, Lance Yang wrote:
>>
>> On Thu, Jun 11, 2026 at 01:28:58PM +0200, David Hildenbrand (Arm) wrote:
>>> On 6/10/26 04:15, Lance Yang wrote:
>>>>
>>>>
>>>> Right, this came from the RFC v1 discussion[1]. David preferred a page-
>>>> range helper for possible future non-folio callers, not something folio-
>>>> only.
>>>>
>>>> Of course, we could also add a folio wrapper on top of that if needed :)
>>>
>>> Best to document that as part of the patch description: we don't really expect
>>> to have a lot of read-only folios in the near future (zero page is rather
>>> special; maybe it won't even be a folio in the future).
>>
>> Ah, good to know, thanks. Will spell that out in RFC v3.
>>
>> Maybe something like this?
>>
>> The huge zero page is pretty special case, and maybe it won't even be a
>> folio in the future. Since read-only folios are unlikely to become a
>> common thing, a page-range helper is the cleaner fit.
>
> Right. And if read-only folios in FSes become a real thing, we can always add
> infrastructure for that.
Got it, that draws the line nicely :P
^ permalink raw reply
* Re: [PATCH RFC 0/2] pinctrl: Add support gpiod_to_irq
From: Linus Walleij @ 2026-06-11 12:51 UTC (permalink / raw)
To: xianwei.zhao
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Neil Armstrong,
Kevin Hilman, Jerome Brunet, Martin Blumenstingl, linux-amlogic,
linux-gpio, devicetree, linux-kernel, linux-arm-kernel
In-Reply-To: <20260611-gpio-to-irq-v1-0-12201716f23f@amlogic.com>
Hi Xianwei,
thanks for your patches!
On Thu, Jun 11, 2026 at 9:54 AM Xianwei Zhao via B4 Relay
<devnull+xianwei.zhao.amlogic.com@kernel.org> wrote:
> Some users need to obtain an IRQ directly from a GPIO descriptor through gpiod_to_irq().
> Add the required DT binding and implementation to support this use case.
> Since this introduces a new DT property, the property is kept optional to
> maintain compatibility with existing SoCs and DTS files.
To me it looks like you have just re-implemented hierarchical
irqs.
Look into the section "Infrastructure helpers for GPIO irqchips"
in Documentation/driver-api/gpio/driver.rst, especially towards
the end.
Solve this by using GPIOLIB_IRQCHIP and a custom
child_to_parent_hwirq() callback to translate the GPIO into
an IRQ.
To just implement gpiod_to_irq() without any irqchip abstraction
is also broken: you can't force all users to just use this way
to get an IRQ it's excessively restricting.
Add
interrupt-controller: true
"#interrupt-cells":
const: 2
to the pinctrl node as well so that DT users can simply request
the IRQ from the irqchip inside of the pin controller. It will
be hierarchical and lightweight but an irqchip nevertheless.
The GPIOLIB_IRQCHIP approach will help you to get this
right.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH v5 08/10] ACPI: APEI: share GHES CPER helpers
From: Ahmed Tiba @ 2026-06-11 12:42 UTC (permalink / raw)
To: Jonathan Cameron
Cc: will, xueshuai, saket.dumbre, mchehab, dave, djbw, bp, tony.luck,
guohanjun, lenb, skhan, vishal.l.verma, rafael, corbet, ira.weiny,
dave.jiang, krzk+dt, robh, catalin.marinas, alison.schofield,
conor+dt, linux-arm-kernel, Michael.Zhao2, linux-doc,
linux-kernel, linux-cxl, Dmitry.Lamerov, devicetree, linux-acpi,
linux-edac, acpica-devel
In-Reply-To: <20260529173229.18843384@jic23-huawei>
On 29/05/2026 17:32, Jonathan Cameron wrote:
> On Fri, 29 May 2026 10:50:48 +0100
> Ahmed Tiba <ahmed.tiba@arm.com> wrote:
>
>> Wire GHES up to the helper routines in ghes_cper.c and remove the local
>> copies from ghes.c. This keeps the control flow identical while letting
>> the helpers be shared with other firmware-first providers.
>>
>> Signed-off-by: Ahmed Tiba <ahmed.tiba@arm.com>
> Mostly looks fine. The one bit that rather makes this exercise of breaking
> out generic code look dodgy is the ifdefs in the generic file.
>
As below.
>
>> ---
>> drivers/acpi/apei/ghes.c | 416 +--------------------------------------
>> drivers/acpi/apei/ghes_cper.c | 438 +++++++++++++++++++++++++++++++++++++++++-
>> include/acpi/ghes_cper.h | 20 ++
>> 3 files changed, 459 insertions(+), 415 deletions(-)
>>
>> diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
>> index 85be2ebf4d3e..f85b97c4db4c 100644
>> --- a/drivers/acpi/apei/ghes.c
>> +++ b/drivers/acpi/apei/ghes.c
>
>>
>> static void __ghes_panic(struct ghes *ghes,
>> diff --git a/drivers/acpi/apei/ghes_cper.c b/drivers/acpi/apei/ghes_cper.c
>> index d7a666a163c3..0ff9d06eb78f 100644
>> --- a/drivers/acpi/apei/ghes_cper.c
>> +++ b/drivers/acpi/apei/ghes_cper.c
>> @@ -13,22 +13,32 @@
>
>>
>> #include "apei-internal.h"
>>
>> +ATOMIC_NOTIFIER_HEAD(ghes_report_chain);
>> +
>> +#ifndef CONFIG_ACPI_APEI
>> +void __weak arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) { }
>> +#endif
> This is non obvious enough that the reasoning for a new weak function should be mentioned in
> the patch description. Why not stub it in include/acpi/apei.h?
>
Agreed. I should have explained that in the changelog.
The weak arch_apei_report_mem_error() fallback was only meant to keep
the shared helper buildable when GHES_CPER_HELPERS is enabled without
ACPI_APEI, while preserving the current GHES behaviour when ACPI_APEI
is enabled.
I kept it local because this fallback is only needed by this helper
split and I did not want to widen the APEI header API for that.
>> +
>> static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
>> static atomic_t ghes_estatus_cache_alloced;
>
>> +void __ghes_print_estatus(const char *pfx,
>> + const struct acpi_hest_generic *generic,
>> + const struct acpi_hest_generic_status *estatus)
>> +{
>> + static atomic_t seqno;
>> + unsigned int curr_seqno;
>> + char pfx_seq[64];
>> +
>> + if (!pfx) {
>> + if (ghes_severity(estatus->error_severity) <=
>> + GHES_SEV_CORRECTED)
>> + pfx = KERN_WARNING;
>> + else
>> + pfx = KERN_ERR;
>> + }
>> + curr_seqno = atomic_inc_return(&seqno);
>> + snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno);
>> + printk("%sHardware error from APEI Generic Hardware Error Source: %d\n",
>> + pfx_seq, generic->header.source_id);
>> + cper_estatus_print(pfx_seq, estatus);
>> +}
>> +
>> +int ghes_print_estatus(const char *pfx,
>> + const struct acpi_hest_generic *generic,
>> + const struct acpi_hest_generic_status *estatus)
>> +{
>> + /* Not more than 2 messages every 5 seconds */
>> + static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5 * HZ, 2);
>> + static DEFINE_RATELIMIT_STATE(ratelimit_uncorrected, 5 * HZ, 2);
>> + struct ratelimit_state *ratelimit;
>> +
>> + if (ghes_severity(estatus->error_severity) <= GHES_SEV_CORRECTED)
>> + ratelimit = &ratelimit_corrected;
>> + else
>> + ratelimit = &ratelimit_uncorrected;
>> + if (__ratelimit(ratelimit)) {
>> + __ghes_print_estatus(pfx, generic, estatus);
>> + return 1;
>> + }
>> + return 0;
>> +}
>> +
>> +#ifdef CONFIG_ACPI_APEI
>
> So after the effort to break the the generic stuff we end up with non generic
> bits in the broken out file? Is there no way to avoid this?
>
The intent here was not to create a new generic estatus core
but to keep the existing GHES control flow and lift the CPER helper flow
for reuse by the DT provider.
Splitting the remaining ACPI GHES specific read/clear path back out into
ghes.c would break that flow across files again. The CONFIG_ACPI_APEI
guard keeps that ACPI specific piece local while the DT side reuses the
same CPER parsing and status-dispatch path.
Best regards,
Ahmed
^ 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