Loading...
Note: File does not exist in v4.6.
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
20static DEFINE_IDA(ffa_bus_id);
21
22static 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
47static 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
59static 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
67static 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
75static 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}
83static DEVICE_ATTR_RO(modalias);
84
85static 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}
92static DEVICE_ATTR_RO(partition_id);
93
94static 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}
101static DEVICE_ATTR_RO(uuid);
102
103static 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};
109ATTRIBUTE_GROUPS(ffa_device_attributes);
110
111const 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};
119EXPORT_SYMBOL_GPL(ffa_bus_type);
120
121int 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}
140EXPORT_SYMBOL_GPL(ffa_driver_register);
141
142void ffa_driver_unregister(struct ffa_driver *driver)
143{
144 driver_unregister(&driver->driver);
145}
146EXPORT_SYMBOL_GPL(ffa_driver_unregister);
147
148static 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
156static int __ffa_devices_unregister(struct device *dev, void *data)
157{
158 device_unregister(dev);
159
160 return 0;
161}
162
163static void ffa_devices_unregister(void)
164{
165 bus_for_each_dev(&ffa_bus_type, NULL, NULL,
166 __ffa_devices_unregister);
167}
168
169bool 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
190struct ffa_device *
191ffa_device_register(const struct ffa_partition_info *part_info,
192 const struct ffa_ops *ops)
193{
194 int id, ret;
195 uuid_t uuid;
196 struct device *dev;
197 struct ffa_device *ffa_dev;
198
199 if (!part_info)
200 return NULL;
201
202 id = ida_alloc_min(&ffa_bus_id, 1, GFP_KERNEL);
203 if (id < 0)
204 return NULL;
205
206 ffa_dev = kzalloc(sizeof(*ffa_dev), GFP_KERNEL);
207 if (!ffa_dev) {
208 ida_free(&ffa_bus_id, id);
209 return NULL;
210 }
211
212 dev = &ffa_dev->dev;
213 dev->bus = &ffa_bus_type;
214 dev->release = ffa_release_device;
215 dev_set_name(&ffa_dev->dev, "arm-ffa-%d", id);
216
217 ffa_dev->id = id;
218 ffa_dev->vm_id = part_info->id;
219 ffa_dev->properties = part_info->properties;
220 ffa_dev->ops = ops;
221 import_uuid(&uuid, (u8 *)part_info->uuid);
222 uuid_copy(&ffa_dev->uuid, &uuid);
223
224 ret = device_register(&ffa_dev->dev);
225 if (ret) {
226 dev_err(dev, "unable to register device %s err=%d\n",
227 dev_name(dev), ret);
228 put_device(dev);
229 return NULL;
230 }
231
232 return ffa_dev;
233}
234EXPORT_SYMBOL_GPL(ffa_device_register);
235
236void ffa_device_unregister(struct ffa_device *ffa_dev)
237{
238 if (!ffa_dev)
239 return;
240
241 device_unregister(&ffa_dev->dev);
242}
243EXPORT_SYMBOL_GPL(ffa_device_unregister);
244
245static int __init arm_ffa_bus_init(void)
246{
247 return bus_register(&ffa_bus_type);
248}
249subsys_initcall(arm_ffa_bus_init);
250
251static void __exit arm_ffa_bus_exit(void)
252{
253 ffa_devices_unregister();
254 bus_unregister(&ffa_bus_type);
255 ida_destroy(&ffa_bus_id);
256}
257module_exit(arm_ffa_bus_exit);
258
259MODULE_ALIAS("ffa-core");
260MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
261MODULE_DESCRIPTION("ARM FF-A bus");
262MODULE_LICENSE("GPL");