summaryrefslogtreecommitdiff
path: root/drivers/firmware/arm_ffa
diff options
context:
space:
mode:
authorSudeep Holla <sudeep.holla@arm.com>2021-05-21 18:10:29 +0300
committerSudeep Holla <sudeep.holla@arm.com>2021-05-27 00:36:46 +0300
commite781858488b918e30a6ff28e9eab6058b787e3b3 (patch)
treeaa4e4256df744f2af06d7a8a5c7b06a13513d7b6 /drivers/firmware/arm_ffa
parent3fdc0cb59d97f87e2cc708d424f1538e31744286 (diff)
downloadlinux-e781858488b918e30a6ff28e9eab6058b787e3b3.tar.xz
firmware: arm_ffa: Add initial FFA bus support for device enumeration
The Arm FF for Armv8-A specification has concept of endpoints or partitions. In the Normal world, a partition could be a VM when the Virtualization extension is enabled or the kernel itself. In order to handle multiple partitions, we can create a FFA device for each such partition on a dedicated FFA bus. Similarly, different drivers requiring FFA transport can be registered on the same bus. We can match the device and drivers using UUID. This is mostly for the in-kernel users with FFA drivers. Link: https://lore.kernel.org/r/20210521151033.181846-2-sudeep.holla@arm.com Tested-by: Jens Wiklander <jens.wiklander@linaro.org> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
Diffstat (limited to 'drivers/firmware/arm_ffa')
-rw-r--r--drivers/firmware/arm_ffa/Kconfig16
-rw-r--r--drivers/firmware/arm_ffa/Makefile4
-rw-r--r--drivers/firmware/arm_ffa/bus.c207
3 files changed, 227 insertions, 0 deletions
diff --git a/drivers/firmware/arm_ffa/Kconfig b/drivers/firmware/arm_ffa/Kconfig
new file mode 100644
index 000000000000..261a3660650a
--- /dev/null
+++ b/drivers/firmware/arm_ffa/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config ARM_FFA_TRANSPORT
+ tristate "Arm Firmware Framework for Armv8-A"
+ depends on OF
+ depends on ARM64
+ default n
+ help
+ This Firmware Framework(FF) for Arm A-profile processors describes
+ interfaces that standardize communication between the various
+ software images which includes communication between images in
+ the Secure world and Normal world. It also leverages the
+ virtualization extension to isolate software images provided
+ by an ecosystem of vendors from each other.
+
+ This driver provides interface for all the client drivers making
+ use of the features offered by ARM FF-A.
diff --git a/drivers/firmware/arm_ffa/Makefile b/drivers/firmware/arm_ffa/Makefile
new file mode 100644
index 000000000000..bfe4323a8784
--- /dev/null
+++ b/drivers/firmware/arm_ffa/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+ffa-bus-y = bus.o
+ffa-module-objs := $(ffa-bus-y)
+obj-$(CONFIG_ARM_FFA_TRANSPORT) = ffa-module.o
diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c
new file mode 100644
index 000000000000..a17874bed946
--- /dev/null
+++ b/drivers/firmware/arm_ffa/bus.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 ARM Ltd.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/arm_ffa.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+static int ffa_device_match(struct device *dev, struct device_driver *drv)
+{
+ const struct ffa_device_id *id_table;
+ struct ffa_device *ffa_dev;
+
+ id_table = to_ffa_driver(drv)->id_table;
+ ffa_dev = to_ffa_dev(dev);
+
+ while (!uuid_is_null(&id_table->uuid)) {
+ if (uuid_equal(&ffa_dev->uuid, &id_table->uuid))
+ return 1;
+ id_table++;
+ }
+
+ return 0;
+}
+
+static int ffa_device_probe(struct device *dev)
+{
+ struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
+ struct ffa_device *ffa_dev = to_ffa_dev(dev);
+
+ if (!ffa_device_match(dev, dev->driver))
+ return -ENODEV;
+
+ return ffa_drv->probe(ffa_dev);
+}
+
+static int ffa_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct ffa_device *ffa_dev = to_ffa_dev(dev);
+
+ return add_uevent_var(env, "MODALIAS=arm_ffa:%04x:%pUb",
+ ffa_dev->vm_id, &ffa_dev->uuid);
+}
+
+static ssize_t partition_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ffa_device *ffa_dev = to_ffa_dev(dev);
+
+ return sprintf(buf, "0x%04x\n", ffa_dev->vm_id);
+}
+static DEVICE_ATTR_RO(partition_id);
+
+static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ffa_device *ffa_dev = to_ffa_dev(dev);
+
+ return sprintf(buf, "%pUb\n", &ffa_dev->uuid);
+}
+static DEVICE_ATTR_RO(uuid);
+
+static struct attribute *ffa_device_attributes_attrs[] = {
+ &dev_attr_partition_id.attr,
+ &dev_attr_uuid.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ffa_device_attributes);
+
+struct bus_type ffa_bus_type = {
+ .name = "arm_ffa",
+ .match = ffa_device_match,
+ .probe = ffa_device_probe,
+ .uevent = ffa_device_uevent,
+ .dev_groups = ffa_device_attributes_groups,
+};
+EXPORT_SYMBOL_GPL(ffa_bus_type);
+
+int ffa_driver_register(struct ffa_driver *driver, struct module *owner,
+ const char *mod_name)
+{
+ int ret;
+
+ driver->driver.bus = &ffa_bus_type;
+ driver->driver.name = driver->name;
+ driver->driver.owner = owner;
+ driver->driver.mod_name = mod_name;
+
+ ret = driver_register(&driver->driver);
+ if (!ret)
+ pr_debug("registered new ffa driver %s\n", driver->name);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ffa_driver_register);
+
+void ffa_driver_unregister(struct ffa_driver *driver)
+{
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL_GPL(ffa_driver_unregister);
+
+static void ffa_release_device(struct device *dev)
+{
+ struct ffa_device *ffa_dev = to_ffa_dev(dev);
+
+ kfree(ffa_dev);
+}
+
+static int __ffa_devices_unregister(struct device *dev, void *data)
+{
+ ffa_release_device(dev);
+
+ return 0;
+}
+
+static void ffa_devices_unregister(void)
+{
+ bus_for_each_dev(&ffa_bus_type, NULL, NULL,
+ __ffa_devices_unregister);
+}
+
+bool ffa_device_is_valid(struct ffa_device *ffa_dev)
+{
+ bool valid = false;
+ struct device *dev = NULL;
+ struct ffa_device *tmp_dev;
+
+ do {
+ dev = bus_find_next_device(&ffa_bus_type, dev);
+ tmp_dev = to_ffa_dev(dev);
+ if (tmp_dev == ffa_dev) {
+ valid = true;
+ break;
+ }
+ put_device(dev);
+ } while (dev);
+
+ put_device(dev);
+
+ return valid;
+}
+
+struct ffa_device *ffa_device_register(const uuid_t *uuid, int vm_id)
+{
+ int ret;
+ struct device *dev;
+ struct ffa_device *ffa_dev;
+
+ ffa_dev = kzalloc(sizeof(*ffa_dev), GFP_KERNEL);
+ if (!ffa_dev)
+ return NULL;
+
+ dev = &ffa_dev->dev;
+ dev->bus = &ffa_bus_type;
+ dev->release = ffa_release_device;
+ dev_set_name(&ffa_dev->dev, "arm-ffa-%04x", vm_id);
+
+ ffa_dev->vm_id = vm_id;
+ uuid_copy(&ffa_dev->uuid, uuid);
+
+ ret = device_register(&ffa_dev->dev);
+ if (ret) {
+ dev_err(dev, "unable to register device %s err=%d\n",
+ dev_name(dev), ret);
+ put_device(dev);
+ return NULL;
+ }
+
+ return ffa_dev;
+}
+EXPORT_SYMBOL_GPL(ffa_device_register);
+
+void ffa_device_unregister(struct ffa_device *ffa_dev)
+{
+ if (!ffa_dev)
+ return;
+
+ device_unregister(&ffa_dev->dev);
+}
+EXPORT_SYMBOL_GPL(ffa_device_unregister);
+
+static int __init arm_ffa_bus_init(void)
+{
+ return bus_register(&ffa_bus_type);
+}
+module_init(arm_ffa_bus_init);
+
+static void __exit arm_ffa_bus_exit(void)
+{
+ ffa_devices_unregister();
+ bus_unregister(&ffa_bus_type);
+}
+
+module_exit(arm_ffa_bus_exit);
+
+MODULE_ALIAS("arm-ffa-bus");
+MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
+MODULE_DESCRIPTION("Arm FF-A bus driver");
+MODULE_LICENSE("GPL v2");