xref: /linux/drivers/firmware/arm_ffa/bus.c (revision 6e7fd890f1d6ac83805409e9c346240de2705584)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2021 ARM Ltd.
4  */
5 
6 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7 
8 #include <linux/arm_ffa.h>
9 #include <linux/device.h>
10 #include <linux/fs.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/slab.h>
14 #include <linux/types.h>
15 
16 #include "common.h"
17 
18 #define SCMI_UEVENT_MODALIAS_FMT	"arm_ffa:%04x:%pUb"
19 
20 static DEFINE_IDA(ffa_bus_id);
21 
22 static int ffa_device_match(struct device *dev, const struct device_driver *drv)
23 {
24 	const struct ffa_device_id *id_table;
25 	struct ffa_device *ffa_dev;
26 
27 	id_table = to_ffa_driver(drv)->id_table;
28 	ffa_dev = to_ffa_dev(dev);
29 
30 	while (!uuid_is_null(&id_table->uuid)) {
31 		/*
32 		 * FF-A v1.0 doesn't provide discovery of UUIDs, just the
33 		 * partition IDs, so match it unconditionally here and handle
34 		 * it via the installed bus notifier during driver binding.
35 		 */
36 		if (uuid_is_null(&ffa_dev->uuid))
37 			return 1;
38 
39 		if (uuid_equal(&ffa_dev->uuid, &id_table->uuid))
40 			return 1;
41 		id_table++;
42 	}
43 
44 	return 0;
45 }
46 
47 static int ffa_device_probe(struct device *dev)
48 {
49 	struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
50 	struct ffa_device *ffa_dev = to_ffa_dev(dev);
51 
52 	/* UUID can be still NULL with FF-A v1.0, so just skip probing them */
53 	if (uuid_is_null(&ffa_dev->uuid))
54 		return -ENODEV;
55 
56 	return ffa_drv->probe(ffa_dev);
57 }
58 
59 static void ffa_device_remove(struct device *dev)
60 {
61 	struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
62 
63 	if (ffa_drv->remove)
64 		ffa_drv->remove(to_ffa_dev(dev));
65 }
66 
67 static int ffa_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
68 {
69 	const struct ffa_device *ffa_dev = to_ffa_dev(dev);
70 
71 	return add_uevent_var(env, "MODALIAS=" SCMI_UEVENT_MODALIAS_FMT,
72 			      ffa_dev->vm_id, &ffa_dev->uuid);
73 }
74 
75 static ssize_t modalias_show(struct device *dev,
76 			     struct device_attribute *attr, char *buf)
77 {
78 	struct ffa_device *ffa_dev = to_ffa_dev(dev);
79 
80 	return sysfs_emit(buf, SCMI_UEVENT_MODALIAS_FMT, ffa_dev->vm_id,
81 			  &ffa_dev->uuid);
82 }
83 static DEVICE_ATTR_RO(modalias);
84 
85 static ssize_t partition_id_show(struct device *dev,
86 				 struct device_attribute *attr, char *buf)
87 {
88 	struct ffa_device *ffa_dev = to_ffa_dev(dev);
89 
90 	return sprintf(buf, "0x%04x\n", ffa_dev->vm_id);
91 }
92 static DEVICE_ATTR_RO(partition_id);
93 
94 static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
95 			 char *buf)
96 {
97 	struct ffa_device *ffa_dev = to_ffa_dev(dev);
98 
99 	return sprintf(buf, "%pUb\n", &ffa_dev->uuid);
100 }
101 static DEVICE_ATTR_RO(uuid);
102 
103 static struct attribute *ffa_device_attributes_attrs[] = {
104 	&dev_attr_partition_id.attr,
105 	&dev_attr_uuid.attr,
106 	&dev_attr_modalias.attr,
107 	NULL,
108 };
109 ATTRIBUTE_GROUPS(ffa_device_attributes);
110 
111 const struct bus_type ffa_bus_type = {
112 	.name		= "arm_ffa",
113 	.match		= ffa_device_match,
114 	.probe		= ffa_device_probe,
115 	.remove		= ffa_device_remove,
116 	.uevent		= ffa_device_uevent,
117 	.dev_groups	= ffa_device_attributes_groups,
118 };
119 EXPORT_SYMBOL_GPL(ffa_bus_type);
120 
121 int ffa_driver_register(struct ffa_driver *driver, struct module *owner,
122 			const char *mod_name)
123 {
124 	int ret;
125 
126 	if (!driver->probe)
127 		return -EINVAL;
128 
129 	driver->driver.bus = &ffa_bus_type;
130 	driver->driver.name = driver->name;
131 	driver->driver.owner = owner;
132 	driver->driver.mod_name = mod_name;
133 
134 	ret = driver_register(&driver->driver);
135 	if (!ret)
136 		pr_debug("registered new ffa driver %s\n", driver->name);
137 
138 	return ret;
139 }
140 EXPORT_SYMBOL_GPL(ffa_driver_register);
141 
142 void ffa_driver_unregister(struct ffa_driver *driver)
143 {
144 	driver_unregister(&driver->driver);
145 }
146 EXPORT_SYMBOL_GPL(ffa_driver_unregister);
147 
148 static void ffa_release_device(struct device *dev)
149 {
150 	struct ffa_device *ffa_dev = to_ffa_dev(dev);
151 
152 	ida_free(&ffa_bus_id, ffa_dev->id);
153 	kfree(ffa_dev);
154 }
155 
156 static int __ffa_devices_unregister(struct device *dev, void *data)
157 {
158 	device_unregister(dev);
159 
160 	return 0;
161 }
162 
163 static void ffa_devices_unregister(void)
164 {
165 	bus_for_each_dev(&ffa_bus_type, NULL, NULL,
166 			 __ffa_devices_unregister);
167 }
168 
169 bool ffa_device_is_valid(struct ffa_device *ffa_dev)
170 {
171 	bool valid = false;
172 	struct device *dev = NULL;
173 	struct ffa_device *tmp_dev;
174 
175 	do {
176 		dev = bus_find_next_device(&ffa_bus_type, dev);
177 		tmp_dev = to_ffa_dev(dev);
178 		if (tmp_dev == ffa_dev) {
179 			valid = true;
180 			break;
181 		}
182 		put_device(dev);
183 	} while (dev);
184 
185 	put_device(dev);
186 
187 	return valid;
188 }
189 
190 struct ffa_device *ffa_device_register(const uuid_t *uuid, int vm_id,
191 				       const struct ffa_ops *ops)
192 {
193 	int id, ret;
194 	struct device *dev;
195 	struct ffa_device *ffa_dev;
196 
197 	id = ida_alloc_min(&ffa_bus_id, 1, GFP_KERNEL);
198 	if (id < 0)
199 		return NULL;
200 
201 	ffa_dev = kzalloc(sizeof(*ffa_dev), GFP_KERNEL);
202 	if (!ffa_dev) {
203 		ida_free(&ffa_bus_id, id);
204 		return NULL;
205 	}
206 
207 	dev = &ffa_dev->dev;
208 	dev->bus = &ffa_bus_type;
209 	dev->release = ffa_release_device;
210 	dev_set_name(&ffa_dev->dev, "arm-ffa-%d", id);
211 
212 	ffa_dev->id = id;
213 	ffa_dev->vm_id = vm_id;
214 	ffa_dev->ops = ops;
215 	uuid_copy(&ffa_dev->uuid, uuid);
216 
217 	ret = device_register(&ffa_dev->dev);
218 	if (ret) {
219 		dev_err(dev, "unable to register device %s err=%d\n",
220 			dev_name(dev), ret);
221 		put_device(dev);
222 		return NULL;
223 	}
224 
225 	return ffa_dev;
226 }
227 EXPORT_SYMBOL_GPL(ffa_device_register);
228 
229 void ffa_device_unregister(struct ffa_device *ffa_dev)
230 {
231 	if (!ffa_dev)
232 		return;
233 
234 	device_unregister(&ffa_dev->dev);
235 }
236 EXPORT_SYMBOL_GPL(ffa_device_unregister);
237 
238 static int __init arm_ffa_bus_init(void)
239 {
240 	return bus_register(&ffa_bus_type);
241 }
242 subsys_initcall(arm_ffa_bus_init);
243 
244 static void __exit arm_ffa_bus_exit(void)
245 {
246 	ffa_devices_unregister();
247 	bus_unregister(&ffa_bus_type);
248 	ida_destroy(&ffa_bus_id);
249 }
250 module_exit(arm_ffa_bus_exit);
251 
252 MODULE_ALIAS("ffa-core");
253 MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
254 MODULE_DESCRIPTION("ARM FF-A bus");
255 MODULE_LICENSE("GPL");
256