1e7818584SSudeep Holla // SPDX-License-Identifier: GPL-2.0
2e7818584SSudeep Holla /*
3e7818584SSudeep Holla * Copyright (C) 2021 ARM Ltd.
4e7818584SSudeep Holla */
5e7818584SSudeep Holla
6e7818584SSudeep Holla #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7e7818584SSudeep Holla
8e7818584SSudeep Holla #include <linux/arm_ffa.h>
9e7818584SSudeep Holla #include <linux/device.h>
10e7818584SSudeep Holla #include <linux/fs.h>
11e7818584SSudeep Holla #include <linux/kernel.h>
12e7818584SSudeep Holla #include <linux/module.h>
13e7818584SSudeep Holla #include <linux/slab.h>
14e7818584SSudeep Holla #include <linux/types.h>
15e7818584SSudeep Holla
163bbfe987SSudeep Holla #include "common.h"
173bbfe987SSudeep Holla
1822779149SSudeep Holla #define SCMI_UEVENT_MODALIAS_FMT "arm_ffa:%04x:%pUb"
1922779149SSudeep Holla
2019b87664SSudeep Holla static DEFINE_IDA(ffa_bus_id);
2119b87664SSudeep Holla
ffa_device_match(struct device * dev,const struct device_driver * drv)22*d69d8048SGreg Kroah-Hartman static int ffa_device_match(struct device *dev, const struct device_driver *drv)
23e7818584SSudeep Holla {
24e7818584SSudeep Holla const struct ffa_device_id *id_table;
25e7818584SSudeep Holla struct ffa_device *ffa_dev;
26e7818584SSudeep Holla
27e7818584SSudeep Holla id_table = to_ffa_driver(drv)->id_table;
28e7818584SSudeep Holla ffa_dev = to_ffa_dev(dev);
29e7818584SSudeep Holla
30e7818584SSudeep Holla while (!uuid_is_null(&id_table->uuid)) {
31d0c0bce8SSudeep Holla /*
32d0c0bce8SSudeep Holla * FF-A v1.0 doesn't provide discovery of UUIDs, just the
339dd15934SSudeep Holla * partition IDs, so match it unconditionally here and handle
349dd15934SSudeep Holla * it via the installed bus notifier during driver binding.
35d0c0bce8SSudeep Holla */
36d0c0bce8SSudeep Holla if (uuid_is_null(&ffa_dev->uuid))
379dd15934SSudeep Holla return 1;
38d0c0bce8SSudeep Holla
39e7818584SSudeep Holla if (uuid_equal(&ffa_dev->uuid, &id_table->uuid))
40e7818584SSudeep Holla return 1;
41e7818584SSudeep Holla id_table++;
42e7818584SSudeep Holla }
43e7818584SSudeep Holla
44e7818584SSudeep Holla return 0;
45e7818584SSudeep Holla }
46e7818584SSudeep Holla
ffa_device_probe(struct device * dev)47e7818584SSudeep Holla static int ffa_device_probe(struct device *dev)
48e7818584SSudeep Holla {
49e7818584SSudeep Holla struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
50e7818584SSudeep Holla struct ffa_device *ffa_dev = to_ffa_dev(dev);
51e7818584SSudeep Holla
529dd15934SSudeep Holla /* UUID can be still NULL with FF-A v1.0, so just skip probing them */
539dd15934SSudeep Holla if (uuid_is_null(&ffa_dev->uuid))
549dd15934SSudeep Holla return -ENODEV;
559dd15934SSudeep Holla
56e7818584SSudeep Holla return ffa_drv->probe(ffa_dev);
57e7818584SSudeep Holla }
58e7818584SSudeep Holla
ffa_device_remove(struct device * dev)59244f5d59SSudeep Holla static void ffa_device_remove(struct device *dev)
60244f5d59SSudeep Holla {
61244f5d59SSudeep Holla struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
62244f5d59SSudeep Holla
63b71b5524SSudeep Holla if (ffa_drv->remove)
64244f5d59SSudeep Holla ffa_drv->remove(to_ffa_dev(dev));
65244f5d59SSudeep Holla }
66244f5d59SSudeep Holla
ffa_device_uevent(const struct device * dev,struct kobj_uevent_env * env)672a81ada3SGreg Kroah-Hartman static int ffa_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
68e7818584SSudeep Holla {
692a81ada3SGreg Kroah-Hartman const struct ffa_device *ffa_dev = to_ffa_dev(dev);
70e7818584SSudeep Holla
7122779149SSudeep Holla return add_uevent_var(env, "MODALIAS=" SCMI_UEVENT_MODALIAS_FMT,
72e7818584SSudeep Holla ffa_dev->vm_id, &ffa_dev->uuid);
73e7818584SSudeep Holla }
74e7818584SSudeep Holla
modalias_show(struct device * dev,struct device_attribute * attr,char * buf)7522779149SSudeep Holla static ssize_t modalias_show(struct device *dev,
7622779149SSudeep Holla struct device_attribute *attr, char *buf)
7722779149SSudeep Holla {
7822779149SSudeep Holla struct ffa_device *ffa_dev = to_ffa_dev(dev);
7922779149SSudeep Holla
8022779149SSudeep Holla return sysfs_emit(buf, SCMI_UEVENT_MODALIAS_FMT, ffa_dev->vm_id,
8122779149SSudeep Holla &ffa_dev->uuid);
8222779149SSudeep Holla }
8322779149SSudeep Holla static DEVICE_ATTR_RO(modalias);
8422779149SSudeep Holla
partition_id_show(struct device * dev,struct device_attribute * attr,char * buf)85e7818584SSudeep Holla static ssize_t partition_id_show(struct device *dev,
86e7818584SSudeep Holla struct device_attribute *attr, char *buf)
87e7818584SSudeep Holla {
88e7818584SSudeep Holla struct ffa_device *ffa_dev = to_ffa_dev(dev);
89e7818584SSudeep Holla
90e7818584SSudeep Holla return sprintf(buf, "0x%04x\n", ffa_dev->vm_id);
91e7818584SSudeep Holla }
92e7818584SSudeep Holla static DEVICE_ATTR_RO(partition_id);
93e7818584SSudeep Holla
uuid_show(struct device * dev,struct device_attribute * attr,char * buf)94e7818584SSudeep Holla static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
95e7818584SSudeep Holla char *buf)
96e7818584SSudeep Holla {
97e7818584SSudeep Holla struct ffa_device *ffa_dev = to_ffa_dev(dev);
98e7818584SSudeep Holla
99e7818584SSudeep Holla return sprintf(buf, "%pUb\n", &ffa_dev->uuid);
100e7818584SSudeep Holla }
101e7818584SSudeep Holla static DEVICE_ATTR_RO(uuid);
102e7818584SSudeep Holla
103e7818584SSudeep Holla static struct attribute *ffa_device_attributes_attrs[] = {
104e7818584SSudeep Holla &dev_attr_partition_id.attr,
105e7818584SSudeep Holla &dev_attr_uuid.attr,
10622779149SSudeep Holla &dev_attr_modalias.attr,
107e7818584SSudeep Holla NULL,
108e7818584SSudeep Holla };
109e7818584SSudeep Holla ATTRIBUTE_GROUPS(ffa_device_attributes);
110e7818584SSudeep Holla
111989e8661SRicardo B. Marliere const struct bus_type ffa_bus_type = {
112e7818584SSudeep Holla .name = "arm_ffa",
113e7818584SSudeep Holla .match = ffa_device_match,
114e7818584SSudeep Holla .probe = ffa_device_probe,
115244f5d59SSudeep Holla .remove = ffa_device_remove,
116e7818584SSudeep Holla .uevent = ffa_device_uevent,
117e7818584SSudeep Holla .dev_groups = ffa_device_attributes_groups,
118e7818584SSudeep Holla };
119e7818584SSudeep Holla EXPORT_SYMBOL_GPL(ffa_bus_type);
120e7818584SSudeep Holla
ffa_driver_register(struct ffa_driver * driver,struct module * owner,const char * mod_name)121e7818584SSudeep Holla int ffa_driver_register(struct ffa_driver *driver, struct module *owner,
122e7818584SSudeep Holla const char *mod_name)
123e7818584SSudeep Holla {
124e7818584SSudeep Holla int ret;
125e7818584SSudeep Holla
12692743071SUwe Kleine-König if (!driver->probe)
12792743071SUwe Kleine-König return -EINVAL;
12892743071SUwe Kleine-König
129e7818584SSudeep Holla driver->driver.bus = &ffa_bus_type;
130e7818584SSudeep Holla driver->driver.name = driver->name;
131e7818584SSudeep Holla driver->driver.owner = owner;
132e7818584SSudeep Holla driver->driver.mod_name = mod_name;
133e7818584SSudeep Holla
134e7818584SSudeep Holla ret = driver_register(&driver->driver);
135e7818584SSudeep Holla if (!ret)
136e7818584SSudeep Holla pr_debug("registered new ffa driver %s\n", driver->name);
137e7818584SSudeep Holla
138e7818584SSudeep Holla return ret;
139e7818584SSudeep Holla }
140e7818584SSudeep Holla EXPORT_SYMBOL_GPL(ffa_driver_register);
141e7818584SSudeep Holla
ffa_driver_unregister(struct ffa_driver * driver)142e7818584SSudeep Holla void ffa_driver_unregister(struct ffa_driver *driver)
143e7818584SSudeep Holla {
144e7818584SSudeep Holla driver_unregister(&driver->driver);
145e7818584SSudeep Holla }
146e7818584SSudeep Holla EXPORT_SYMBOL_GPL(ffa_driver_unregister);
147e7818584SSudeep Holla
ffa_release_device(struct device * dev)148e7818584SSudeep Holla static void ffa_release_device(struct device *dev)
149e7818584SSudeep Holla {
150e7818584SSudeep Holla struct ffa_device *ffa_dev = to_ffa_dev(dev);
151e7818584SSudeep Holla
15219b87664SSudeep Holla ida_free(&ffa_bus_id, ffa_dev->id);
153e7818584SSudeep Holla kfree(ffa_dev);
154e7818584SSudeep Holla }
155e7818584SSudeep Holla
__ffa_devices_unregister(struct device * dev,void * data)156e7818584SSudeep Holla static int __ffa_devices_unregister(struct device *dev, void *data)
157e7818584SSudeep Holla {
158eb7b52e6SSudeep Holla device_unregister(dev);
159e7818584SSudeep Holla
160e7818584SSudeep Holla return 0;
161e7818584SSudeep Holla }
162e7818584SSudeep Holla
ffa_devices_unregister(void)163e7818584SSudeep Holla static void ffa_devices_unregister(void)
164e7818584SSudeep Holla {
165e7818584SSudeep Holla bus_for_each_dev(&ffa_bus_type, NULL, NULL,
166e7818584SSudeep Holla __ffa_devices_unregister);
167e7818584SSudeep Holla }
168e7818584SSudeep Holla
ffa_device_is_valid(struct ffa_device * ffa_dev)169e7818584SSudeep Holla bool ffa_device_is_valid(struct ffa_device *ffa_dev)
170e7818584SSudeep Holla {
171e7818584SSudeep Holla bool valid = false;
172e7818584SSudeep Holla struct device *dev = NULL;
173e7818584SSudeep Holla struct ffa_device *tmp_dev;
174e7818584SSudeep Holla
175e7818584SSudeep Holla do {
176e7818584SSudeep Holla dev = bus_find_next_device(&ffa_bus_type, dev);
177e7818584SSudeep Holla tmp_dev = to_ffa_dev(dev);
178e7818584SSudeep Holla if (tmp_dev == ffa_dev) {
179e7818584SSudeep Holla valid = true;
180e7818584SSudeep Holla break;
181e7818584SSudeep Holla }
182e7818584SSudeep Holla put_device(dev);
183e7818584SSudeep Holla } while (dev);
184e7818584SSudeep Holla
185e7818584SSudeep Holla put_device(dev);
186e7818584SSudeep Holla
187e7818584SSudeep Holla return valid;
188e7818584SSudeep Holla }
189e7818584SSudeep Holla
ffa_device_register(const uuid_t * uuid,int vm_id,const struct ffa_ops * ops)190d01387fcSSudeep Holla struct ffa_device *ffa_device_register(const uuid_t *uuid, int vm_id,
1917aa7a979SSudeep Holla const struct ffa_ops *ops)
192e7818584SSudeep Holla {
19319b87664SSudeep Holla int id, ret;
194e7818584SSudeep Holla struct device *dev;
195e7818584SSudeep Holla struct ffa_device *ffa_dev;
196e7818584SSudeep Holla
19719b87664SSudeep Holla id = ida_alloc_min(&ffa_bus_id, 1, GFP_KERNEL);
19819b87664SSudeep Holla if (id < 0)
199e7818584SSudeep Holla return NULL;
200e7818584SSudeep Holla
20119b87664SSudeep Holla ffa_dev = kzalloc(sizeof(*ffa_dev), GFP_KERNEL);
20219b87664SSudeep Holla if (!ffa_dev) {
20319b87664SSudeep Holla ida_free(&ffa_bus_id, id);
20419b87664SSudeep Holla return NULL;
20519b87664SSudeep Holla }
20619b87664SSudeep Holla
207e7818584SSudeep Holla dev = &ffa_dev->dev;
208e7818584SSudeep Holla dev->bus = &ffa_bus_type;
209e7818584SSudeep Holla dev->release = ffa_release_device;
21019b87664SSudeep Holla dev_set_name(&ffa_dev->dev, "arm-ffa-%d", id);
211e7818584SSudeep Holla
2127d0bc636SSudeep Holla ffa_dev->id = id;
213e7818584SSudeep Holla ffa_dev->vm_id = vm_id;
214d01387fcSSudeep Holla ffa_dev->ops = ops;
215e7818584SSudeep Holla uuid_copy(&ffa_dev->uuid, uuid);
216e7818584SSudeep Holla
217e7818584SSudeep Holla ret = device_register(&ffa_dev->dev);
218e7818584SSudeep Holla if (ret) {
219e7818584SSudeep Holla dev_err(dev, "unable to register device %s err=%d\n",
220e7818584SSudeep Holla dev_name(dev), ret);
221e7818584SSudeep Holla put_device(dev);
222e7818584SSudeep Holla return NULL;
223e7818584SSudeep Holla }
224e7818584SSudeep Holla
225e7818584SSudeep Holla return ffa_dev;
226e7818584SSudeep Holla }
227e7818584SSudeep Holla EXPORT_SYMBOL_GPL(ffa_device_register);
228e7818584SSudeep Holla
ffa_device_unregister(struct ffa_device * ffa_dev)229e7818584SSudeep Holla void ffa_device_unregister(struct ffa_device *ffa_dev)
230e7818584SSudeep Holla {
231e7818584SSudeep Holla if (!ffa_dev)
232e7818584SSudeep Holla return;
233e7818584SSudeep Holla
234e7818584SSudeep Holla device_unregister(&ffa_dev->dev);
235e7818584SSudeep Holla }
236e7818584SSudeep Holla EXPORT_SYMBOL_GPL(ffa_device_unregister);
237e7818584SSudeep Holla
arm_ffa_bus_init(void)23818c250bdSSudeep Holla static int __init arm_ffa_bus_init(void)
239e7818584SSudeep Holla {
240e7818584SSudeep Holla return bus_register(&ffa_bus_type);
241e7818584SSudeep Holla }
24218c250bdSSudeep Holla subsys_initcall(arm_ffa_bus_init);
243e7818584SSudeep Holla
arm_ffa_bus_exit(void)24418c250bdSSudeep Holla static void __exit arm_ffa_bus_exit(void)
245e7818584SSudeep Holla {
246e7818584SSudeep Holla ffa_devices_unregister();
247e7818584SSudeep Holla bus_unregister(&ffa_bus_type);
24819b87664SSudeep Holla ida_destroy(&ffa_bus_id);
249e7818584SSudeep Holla }
25018c250bdSSudeep Holla module_exit(arm_ffa_bus_exit);
25118c250bdSSudeep Holla
25218c250bdSSudeep Holla MODULE_ALIAS("ffa-core");
25318c250bdSSudeep Holla MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
25418c250bdSSudeep Holla MODULE_DESCRIPTION("ARM FF-A bus");
25518c250bdSSudeep Holla MODULE_LICENSE("GPL");
256