xref: /linux/drivers/firmware/smccc/bus.c (revision 303bff3637d13f0ecbb280c7cb403470c440a2a6)
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