1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2026 Arm Limited 4 */ 5 6 #include <linux/arm-smccc-bus.h> 7 #include <linux/slab.h> 8 9 static int arm_smccc_bus_match(struct device *dev, 10 const struct device_driver *drv) 11 { 12 const struct arm_smccc_device_id *id_table; 13 const struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev); 14 15 id_table = to_arm_smccc_driver(drv)->id_table; 16 if (!id_table) 17 return 0; 18 19 while (id_table->func_id) { 20 if (smccc_dev->func_id == id_table->func_id) 21 return 1; 22 id_table++; 23 } 24 25 return 0; 26 } 27 28 static int arm_smccc_bus_probe(struct device *dev) 29 { 30 struct arm_smccc_driver *smccc_drv = to_arm_smccc_driver(dev->driver); 31 32 return smccc_drv->probe(to_arm_smccc_device(dev)); 33 } 34 35 static void arm_smccc_bus_remove(struct device *dev) 36 { 37 struct arm_smccc_driver *smcc_drv = to_arm_smccc_driver(dev->driver); 38 39 if (smcc_drv->remove) 40 smcc_drv->remove(to_arm_smccc_device(dev)); 41 } 42 43 static int arm_smccc_bus_uevent(const struct device *dev, 44 struct kobj_uevent_env *env) 45 { 46 const struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev); 47 48 return add_uevent_var(env, "MODALIAS=" ARM_SMCCC_MODULE_PREFIX "f%08X", 49 smccc_dev->func_id); 50 } 51 52 static ssize_t modalias_show(struct device *dev, 53 struct device_attribute *attr, char *buf) 54 { 55 struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev); 56 57 return sysfs_emit(buf, ARM_SMCCC_MODULE_PREFIX "f%08X\n", 58 smccc_dev->func_id); 59 } 60 static DEVICE_ATTR_RO(modalias); 61 62 static struct attribute *arm_smccc_device_attrs[] = { 63 &dev_attr_modalias.attr, 64 NULL, 65 }; 66 ATTRIBUTE_GROUPS(arm_smccc_device); 67 68 const struct bus_type arm_smccc_bus_type = { 69 .name = "arm_smccc", 70 .match = arm_smccc_bus_match, 71 .probe = arm_smccc_bus_probe, 72 .remove = arm_smccc_bus_remove, 73 .uevent = arm_smccc_bus_uevent, 74 .dev_groups = arm_smccc_device_groups, 75 }; 76 EXPORT_SYMBOL_GPL(arm_smccc_bus_type); 77 78 int arm_smccc_driver_register(struct arm_smccc_driver *driver, 79 struct module *owner, const char *mod_name) 80 { 81 if (!driver->probe || !driver->id_table) 82 return -EINVAL; 83 84 driver->driver.bus = &arm_smccc_bus_type; 85 driver->driver.name = driver->name; 86 driver->driver.owner = owner; 87 driver->driver.mod_name = mod_name; 88 89 return driver_register(&driver->driver); 90 } 91 EXPORT_SYMBOL_GPL(arm_smccc_driver_register); 92 93 void arm_smccc_driver_unregister(struct arm_smccc_driver *driver) 94 { 95 driver_unregister(&driver->driver); 96 } 97 EXPORT_SYMBOL_GPL(arm_smccc_driver_unregister); 98 99 static void arm_smccc_release_device(struct device *dev) 100 { 101 struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev); 102 103 kfree(smccc_dev); 104 } 105 106 static struct device *arm_smccc_device_alloc(u32 func_id) 107 { 108 struct arm_smccc_device *smccc_dev; 109 110 smccc_dev = kzalloc_obj(*smccc_dev); 111 if (!smccc_dev) 112 return NULL; 113 114 smccc_dev->func_id = func_id; 115 smccc_dev->dev.bus = &arm_smccc_bus_type; 116 smccc_dev->dev.release = arm_smccc_release_device; 117 device_initialize(&smccc_dev->dev); 118 119 return &smccc_dev->dev; 120 } 121 122 struct arm_smccc_device *arm_smccc_device_register(const char *name, u32 func_id) 123 { 124 int ret; 125 126 if (!name) 127 return ERR_PTR(-EINVAL); 128 129 struct device *dev __free(put_device) = arm_smccc_device_alloc(func_id); 130 if (!dev) 131 return ERR_PTR(-ENOMEM); 132 133 ret = dev_set_name(dev, "%s", name); 134 if (ret) 135 return ERR_PTR(ret); 136 137 ret = device_add(dev); 138 if (ret) 139 return ERR_PTR(ret); 140 141 return to_arm_smccc_device(no_free_ptr(dev)); 142 } 143 EXPORT_SYMBOL_GPL(arm_smccc_device_register); 144 145 void arm_smccc_device_unregister(struct arm_smccc_device *smccc_dev) 146 { 147 if (!smccc_dev) 148 return; 149 150 device_unregister(&smccc_dev->dev); 151 } 152 EXPORT_SYMBOL_GPL(arm_smccc_device_unregister); 153 154 static int __init arm_smccc_bus_init(void) 155 { 156 return bus_register(&arm_smccc_bus_type); 157 } 158 subsys_initcall(arm_smccc_bus_init); 159