// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2026 Arm Limited */ #include #include static int arm_smccc_bus_match(struct device *dev, const struct device_driver *drv) { const struct arm_smccc_device_id *id_table; const struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev); id_table = to_arm_smccc_driver(drv)->id_table; if (!id_table) return 0; while (id_table->func_id) { if (smccc_dev->func_id == id_table->func_id) return 1; id_table++; } return 0; } static int arm_smccc_bus_probe(struct device *dev) { struct arm_smccc_driver *smccc_drv = to_arm_smccc_driver(dev->driver); return smccc_drv->probe(to_arm_smccc_device(dev)); } static void arm_smccc_bus_remove(struct device *dev) { struct arm_smccc_driver *smcc_drv = to_arm_smccc_driver(dev->driver); if (smcc_drv->remove) smcc_drv->remove(to_arm_smccc_device(dev)); } static int arm_smccc_bus_uevent(const struct device *dev, struct kobj_uevent_env *env) { const struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev); return add_uevent_var(env, "MODALIAS=" ARM_SMCCC_MODULE_PREFIX "f%08X", smccc_dev->func_id); } static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev); return sysfs_emit(buf, ARM_SMCCC_MODULE_PREFIX "f%08X\n", smccc_dev->func_id); } static DEVICE_ATTR_RO(modalias); static struct attribute *arm_smccc_device_attrs[] = { &dev_attr_modalias.attr, NULL, }; ATTRIBUTE_GROUPS(arm_smccc_device); const struct bus_type arm_smccc_bus_type = { .name = "arm_smccc", .match = arm_smccc_bus_match, .probe = arm_smccc_bus_probe, .remove = arm_smccc_bus_remove, .uevent = arm_smccc_bus_uevent, .dev_groups = arm_smccc_device_groups, }; EXPORT_SYMBOL_GPL(arm_smccc_bus_type); int arm_smccc_driver_register(struct arm_smccc_driver *driver, struct module *owner, const char *mod_name) { if (!driver->probe || !driver->id_table) return -EINVAL; driver->driver.bus = &arm_smccc_bus_type; driver->driver.name = driver->name; driver->driver.owner = owner; driver->driver.mod_name = mod_name; return driver_register(&driver->driver); } EXPORT_SYMBOL_GPL(arm_smccc_driver_register); void arm_smccc_driver_unregister(struct arm_smccc_driver *driver) { driver_unregister(&driver->driver); } EXPORT_SYMBOL_GPL(arm_smccc_driver_unregister); static void arm_smccc_release_device(struct device *dev) { struct arm_smccc_device *smccc_dev = to_arm_smccc_device(dev); kfree(smccc_dev); } static struct device *arm_smccc_device_alloc(u32 func_id) { struct arm_smccc_device *smccc_dev; smccc_dev = kzalloc_obj(*smccc_dev); if (!smccc_dev) return NULL; smccc_dev->func_id = func_id; smccc_dev->dev.bus = &arm_smccc_bus_type; smccc_dev->dev.release = arm_smccc_release_device; device_initialize(&smccc_dev->dev); return &smccc_dev->dev; } struct arm_smccc_device *arm_smccc_device_register(const char *name, u32 func_id) { int ret; if (!name) return ERR_PTR(-EINVAL); struct device *dev __free(put_device) = arm_smccc_device_alloc(func_id); if (!dev) return ERR_PTR(-ENOMEM); ret = dev_set_name(dev, "%s", name); if (ret) return ERR_PTR(ret); ret = device_add(dev); if (ret) return ERR_PTR(ret); return to_arm_smccc_device(no_free_ptr(dev)); } EXPORT_SYMBOL_GPL(arm_smccc_device_register); void arm_smccc_device_unregister(struct arm_smccc_device *smccc_dev) { if (!smccc_dev) return; device_unregister(&smccc_dev->dev); } EXPORT_SYMBOL_GPL(arm_smccc_device_unregister); static int __init arm_smccc_bus_init(void) { return bus_register(&arm_smccc_bus_type); } subsys_initcall(arm_smccc_bus_init);