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, 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(uuid: &id_table->uuid)) {
31 /*
32 * FF-A v1.0 doesn't provide discovery of UUIDs, just the
33 * partition IDs, so fetch the partitions IDs for this
34 * id_table UUID and assign the UUID to the device if the
35 * partition ID matches
36 */
37 if (uuid_is_null(uuid: &ffa_dev->uuid))
38 ffa_device_match_uuid(ffa_dev, uuid: &id_table->uuid);
39
40 if (uuid_equal(u1: &ffa_dev->uuid, u2: &id_table->uuid))
41 return 1;
42 id_table++;
43 }
44
45 return 0;
46}
47
48static int ffa_device_probe(struct device *dev)
49{
50 struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
51 struct ffa_device *ffa_dev = to_ffa_dev(dev);
52
53 return ffa_drv->probe(ffa_dev);
54}
55
56static void ffa_device_remove(struct device *dev)
57{
58 struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
59
60 if (ffa_drv->remove)
61 ffa_drv->remove(to_ffa_dev(dev));
62}
63
64static int ffa_device_uevent(const struct device *dev, struct kobj_uevent_env *env)
65{
66 const struct ffa_device *ffa_dev = to_ffa_dev(dev);
67
68 return add_uevent_var(env, format: "MODALIAS=" SCMI_UEVENT_MODALIAS_FMT,
69 ffa_dev->vm_id, &ffa_dev->uuid);
70}
71
72static ssize_t modalias_show(struct device *dev,
73 struct device_attribute *attr, char *buf)
74{
75 struct ffa_device *ffa_dev = to_ffa_dev(dev);
76
77 return sysfs_emit(buf, SCMI_UEVENT_MODALIAS_FMT, ffa_dev->vm_id,
78 &ffa_dev->uuid);
79}
80static DEVICE_ATTR_RO(modalias);
81
82static ssize_t partition_id_show(struct device *dev,
83 struct device_attribute *attr, char *buf)
84{
85 struct ffa_device *ffa_dev = to_ffa_dev(dev);
86
87 return sprintf(buf, fmt: "0x%04x\n", ffa_dev->vm_id);
88}
89static DEVICE_ATTR_RO(partition_id);
90
91static ssize_t uuid_show(struct device *dev, struct device_attribute *attr,
92 char *buf)
93{
94 struct ffa_device *ffa_dev = to_ffa_dev(dev);
95
96 return sprintf(buf, fmt: "%pUb\n", &ffa_dev->uuid);
97}
98static DEVICE_ATTR_RO(uuid);
99
100static struct attribute *ffa_device_attributes_attrs[] = {
101 &dev_attr_partition_id.attr,
102 &dev_attr_uuid.attr,
103 &dev_attr_modalias.attr,
104 NULL,
105};
106ATTRIBUTE_GROUPS(ffa_device_attributes);
107
108const struct bus_type ffa_bus_type = {
109 .name = "arm_ffa",
110 .match = ffa_device_match,
111 .probe = ffa_device_probe,
112 .remove = ffa_device_remove,
113 .uevent = ffa_device_uevent,
114 .dev_groups = ffa_device_attributes_groups,
115};
116EXPORT_SYMBOL_GPL(ffa_bus_type);
117
118int ffa_driver_register(struct ffa_driver *driver, struct module *owner,
119 const char *mod_name)
120{
121 int ret;
122
123 if (!driver->probe)
124 return -EINVAL;
125
126 driver->driver.bus = &ffa_bus_type;
127 driver->driver.name = driver->name;
128 driver->driver.owner = owner;
129 driver->driver.mod_name = mod_name;
130
131 ret = driver_register(drv: &driver->driver);
132 if (!ret)
133 pr_debug("registered new ffa driver %s\n", driver->name);
134
135 return ret;
136}
137EXPORT_SYMBOL_GPL(ffa_driver_register);
138
139void ffa_driver_unregister(struct ffa_driver *driver)
140{
141 driver_unregister(drv: &driver->driver);
142}
143EXPORT_SYMBOL_GPL(ffa_driver_unregister);
144
145static void ffa_release_device(struct device *dev)
146{
147 struct ffa_device *ffa_dev = to_ffa_dev(dev);
148
149 ida_free(&ffa_bus_id, id: ffa_dev->id);
150 kfree(objp: ffa_dev);
151}
152
153static int __ffa_devices_unregister(struct device *dev, void *data)
154{
155 device_unregister(dev);
156
157 return 0;
158}
159
160static void ffa_devices_unregister(void)
161{
162 bus_for_each_dev(bus: &ffa_bus_type, NULL, NULL,
163 fn: __ffa_devices_unregister);
164}
165
166bool ffa_device_is_valid(struct ffa_device *ffa_dev)
167{
168 bool valid = false;
169 struct device *dev = NULL;
170 struct ffa_device *tmp_dev;
171
172 do {
173 dev = bus_find_next_device(bus: &ffa_bus_type, cur: dev);
174 tmp_dev = to_ffa_dev(dev);
175 if (tmp_dev == ffa_dev) {
176 valid = true;
177 break;
178 }
179 put_device(dev);
180 } while (dev);
181
182 put_device(dev);
183
184 return valid;
185}
186
187struct ffa_device *ffa_device_register(const uuid_t *uuid, int vm_id,
188 const struct ffa_ops *ops)
189{
190 int id, ret;
191 struct device *dev;
192 struct ffa_device *ffa_dev;
193
194 id = ida_alloc_min(ida: &ffa_bus_id, min: 1, GFP_KERNEL);
195 if (id < 0)
196 return NULL;
197
198 ffa_dev = kzalloc(size: sizeof(*ffa_dev), GFP_KERNEL);
199 if (!ffa_dev) {
200 ida_free(&ffa_bus_id, id);
201 return NULL;
202 }
203
204 dev = &ffa_dev->dev;
205 dev->bus = &ffa_bus_type;
206 dev->release = ffa_release_device;
207 dev_set_name(dev: &ffa_dev->dev, name: "arm-ffa-%d", id);
208
209 ffa_dev->id = id;
210 ffa_dev->vm_id = vm_id;
211 ffa_dev->ops = ops;
212 uuid_copy(dst: &ffa_dev->uuid, src: uuid);
213
214 ret = device_register(dev: &ffa_dev->dev);
215 if (ret) {
216 dev_err(dev, "unable to register device %s err=%d\n",
217 dev_name(dev), ret);
218 put_device(dev);
219 return NULL;
220 }
221
222 return ffa_dev;
223}
224EXPORT_SYMBOL_GPL(ffa_device_register);
225
226void ffa_device_unregister(struct ffa_device *ffa_dev)
227{
228 if (!ffa_dev)
229 return;
230
231 device_unregister(dev: &ffa_dev->dev);
232}
233EXPORT_SYMBOL_GPL(ffa_device_unregister);
234
235int arm_ffa_bus_init(void)
236{
237 return bus_register(bus: &ffa_bus_type);
238}
239
240void arm_ffa_bus_exit(void)
241{
242 ffa_devices_unregister();
243 bus_unregister(bus: &ffa_bus_type);
244 ida_destroy(ida: &ffa_bus_id);
245}
246

source code of linux/drivers/firmware/arm_ffa/bus.c