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
arm_smccc_bus_match(struct device * dev,const struct device_driver * drv)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
arm_smccc_bus_probe(struct device * dev)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
arm_smccc_bus_remove(struct device * dev)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
arm_smccc_bus_uevent(const struct device * dev,struct kobj_uevent_env * env)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
modalias_show(struct device * dev,struct device_attribute * attr,char * buf)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
arm_smccc_driver_register(struct arm_smccc_driver * driver,struct module * owner,const char * mod_name)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
arm_smccc_driver_unregister(struct arm_smccc_driver * driver)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
arm_smccc_release_device(struct device * dev)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
arm_smccc_device_alloc(u32 func_id)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
arm_smccc_device_register(const char * name,u32 func_id)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
arm_smccc_device_unregister(struct arm_smccc_device * smccc_dev)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
arm_smccc_bus_init(void)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