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, 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 | |
48 | static 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 | |
56 | static 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 | |
64 | static 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 | |
72 | static 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 | } |
80 | static DEVICE_ATTR_RO(modalias); |
81 | |
82 | static 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 | } |
89 | static DEVICE_ATTR_RO(partition_id); |
90 | |
91 | static 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 | } |
98 | static DEVICE_ATTR_RO(uuid); |
99 | |
100 | static 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 | }; |
106 | ATTRIBUTE_GROUPS(ffa_device_attributes); |
107 | |
108 | const 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 | }; |
116 | EXPORT_SYMBOL_GPL(ffa_bus_type); |
117 | |
118 | int 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 | } |
137 | EXPORT_SYMBOL_GPL(ffa_driver_register); |
138 | |
139 | void ffa_driver_unregister(struct ffa_driver *driver) |
140 | { |
141 | driver_unregister(drv: &driver->driver); |
142 | } |
143 | EXPORT_SYMBOL_GPL(ffa_driver_unregister); |
144 | |
145 | static 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 | |
153 | static int __ffa_devices_unregister(struct device *dev, void *data) |
154 | { |
155 | device_unregister(dev); |
156 | |
157 | return 0; |
158 | } |
159 | |
160 | static void ffa_devices_unregister(void) |
161 | { |
162 | bus_for_each_dev(bus: &ffa_bus_type, NULL, NULL, |
163 | fn: __ffa_devices_unregister); |
164 | } |
165 | |
166 | bool 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 | |
187 | struct 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 | } |
224 | EXPORT_SYMBOL_GPL(ffa_device_register); |
225 | |
226 | void ffa_device_unregister(struct ffa_device *ffa_dev) |
227 | { |
228 | if (!ffa_dev) |
229 | return; |
230 | |
231 | device_unregister(dev: &ffa_dev->dev); |
232 | } |
233 | EXPORT_SYMBOL_GPL(ffa_device_unregister); |
234 | |
235 | int arm_ffa_bus_init(void) |
236 | { |
237 | return bus_register(bus: &ffa_bus_type); |
238 | } |
239 | |
240 | void 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 | |