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