summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/firmware/smccc/Makefile2
-rw-r--r--drivers/firmware/smccc/bus.c158
-rw-r--r--include/linux/arm-smccc-bus.h48
-rw-r--r--include/linux/device-id/arm_smccc.h19
-rw-r--r--include/linux/mod_devicetable.h1
-rw-r--r--scripts/mod/devicetable-offsets.c3
-rw-r--r--scripts/mod/file2alias.c8
8 files changed, 240 insertions, 1 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index cc3cae2e378b..b8e8b362f932 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24742,7 +24742,9 @@ M: Sudeep Holla <sudeep.holla@kernel.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: drivers/firmware/smccc/
+F: include/linux/arm-smccc-bus.h
F: include/linux/arm-smccc.h
+F: include/linux/device-id/arm_smccc.h
SECURITY CONTACT
M: Security Officers <security@kernel.org>
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..0357a7c48af0
--- /dev/null
+++ b/drivers/firmware/smccc/bus.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Arm Limited
+ */
+
+#include <linux/arm-smccc-bus.h>
+#include <linux/slab.h>
+
+static int arm_smccc_bus_match(struct device *dev,
+ const struct device_driver *drv)
+{
+ const struct arm_smccc_device_id *id_table;
+ const 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->func_id) {
+ if (smccc_dev->func_id == id_table->func_id)
+ 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 "f%08X",
+ smccc_dev->func_id);
+}
+
+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 "f%08X\n",
+ smccc_dev->func_id);
+}
+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 || !driver->id_table)
+ 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);
+
+ kfree(smccc_dev);
+}
+
+static struct device *arm_smccc_device_alloc(u32 func_id)
+{
+ struct arm_smccc_device *smccc_dev;
+
+ smccc_dev = kzalloc_obj(*smccc_dev);
+ if (!smccc_dev)
+ return NULL;
+
+ smccc_dev->func_id = func_id;
+ smccc_dev->dev.bus = &arm_smccc_bus_type;
+ smccc_dev->dev.release = arm_smccc_release_device;
+ device_initialize(&smccc_dev->dev);
+
+ return &smccc_dev->dev;
+}
+
+struct arm_smccc_device *arm_smccc_device_register(const char *name, u32 func_id)
+{
+ int ret;
+
+ if (!name)
+ return ERR_PTR(-EINVAL);
+
+ struct device *dev __free(put_device) = arm_smccc_device_alloc(func_id);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ ret = dev_set_name(dev, "%s", name);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = device_add(dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return to_arm_smccc_device(no_free_ptr(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..05c0c850510e
--- /dev/null
+++ b/include/linux/arm-smccc-bus.h
@@ -0,0 +1,48 @@
+/* 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/device-id/arm_smccc.h>
+#include <linux/module.h>
+
+struct arm_smccc_device {
+ struct device dev;
+ u32 func_id;
+};
+
+#define to_arm_smccc_device(d) container_of_const(d, struct arm_smccc_device, dev)
+
+struct arm_smccc_driver {
+ struct device_driver 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;
+};
+
+#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, u32 func_id);
+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/device-id/arm_smccc.h b/include/linux/device-id/arm_smccc.h
new file mode 100644
index 000000000000..a8579832fc1c
--- /dev/null
+++ b/include/linux/device-id/arm_smccc.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_DEVICE_ID_ARM_SMCCC_H
+#define __LINUX_DEVICE_ID_ARM_SMCCC_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#endif
+
+#define ARM_SMCCC_MODULE_PREFIX "arm_smccc:"
+
+/**
+ * struct arm_smccc_device_id - Arm SMCCC bus device identifier
+ * @func_id: SMCCC function identifier
+ */
+struct arm_smccc_device_id {
+ __u32 func_id;
+};
+
+#endif /* __LINUX_DEVICE_ID_ARM_SMCCC_H */
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index a397213bedac..318c4b5c3451 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -16,6 +16,7 @@
#include "device-id/amba.h"
#include "device-id/ap.h"
#include "device-id/apr.h"
+#include "device-id/arm_smccc.h"
#include "device-id/auxiliary.h"
#include "device-id/bcma.h"
#include "device-id/ccw.h"
diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c
index b4178c42d08f..1492e9fdc7cb 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, func_id);
+
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 8d36c74dec2d..980d7da456bb 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -1349,6 +1349,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(symval, arm_smccc_device_id, func_id);
+
+ module_alias_printf(mod, false, ARM_SMCCC_MODULE_PREFIX "f%08X", func_id);
+}
+
/*
* Looks like: ssam:dNcNtNiNfN
*
@@ -1519,6 +1526,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},