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