aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSudeep Holla <sudeep.holla@arm.com>2017-10-30 14:33:30 -0400
committerSudeep Holla <sudeep.holla@arm.com>2018-02-28 11:37:57 -0500
commit933c504424a2bc784fdb4cd5c318049d55da20e0 (patch)
treec3c92b9123d7eca9941720b4c368928febae4292
parentb6f20ff8bd94ad34032804a60bab5ee56752007e (diff)
firmware: arm_scmi: add scmi protocol bus to enumerate protocol devices
The SCMI specification encompasses various protocols. However, not every protocol has to be present on a given platform/implementation as not every protocol is relevant for it. Furthermore, the platform chooses which protocols it exposes to a given agent. The only protocol that must be implemented is the base protocol. The base protocol is used by an agent to discover which protocols are available to it. In order to enumerate the discovered implemented protocols, this patch adds support for a separate scmi protocol bus. It also adds mechanism to register support for different protocols. Cc: Arnd Bergmann <arnd@arndb.de> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
-rw-r--r--drivers/firmware/arm_scmi/Makefile3
-rw-r--r--drivers/firmware/arm_scmi/bus.c221
-rw-r--r--drivers/firmware/arm_scmi/common.h1
-rw-r--r--include/linux/scmi_protocol.h64
4 files changed, 288 insertions, 1 deletions
diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
index 5d9c7ef35f0f..5f4ec2613db6 100644
--- a/drivers/firmware/arm_scmi/Makefile
+++ b/drivers/firmware/arm_scmi/Makefile
@@ -1,3 +1,4 @@
1obj-y = scmi-driver.o scmi-protocols.o 1obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o
2scmi-bus-y = bus.o
2scmi-driver-y = driver.o 3scmi-driver-y = driver.o
3scmi-protocols-y = base.o 4scmi-protocols-y = base.o
diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
new file mode 100644
index 000000000000..f2760a596c28
--- /dev/null
+++ b/drivers/firmware/arm_scmi/bus.c
@@ -0,0 +1,221 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * System Control and Management Interface (SCMI) Message Protocol bus layer
4 *
5 * Copyright (C) 2018 ARM Ltd.
6 */
7
8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9
10#include <linux/types.h>
11#include <linux/module.h>
12#include <linux/kernel.h>
13#include <linux/slab.h>
14#include <linux/device.h>
15
16#include "common.h"
17
18static DEFINE_IDA(scmi_bus_id);
19static DEFINE_IDR(scmi_protocols);
20static DEFINE_SPINLOCK(protocol_lock);
21
22static const struct scmi_device_id *
23scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
24{
25 const struct scmi_device_id *id = scmi_drv->id_table;
26
27 if (!id)
28 return NULL;
29
30 for (; id->protocol_id; id++)
31 if (id->protocol_id == scmi_dev->protocol_id)
32 return id;
33
34 return NULL;
35}
36
37static int scmi_dev_match(struct device *dev, struct device_driver *drv)
38{
39 struct scmi_driver *scmi_drv = to_scmi_driver(drv);
40 struct scmi_device *scmi_dev = to_scmi_dev(dev);
41 const struct scmi_device_id *id;
42
43 id = scmi_dev_match_id(scmi_dev, scmi_drv);
44 if (id)
45 return 1;
46
47 return 0;
48}
49
50static int scmi_protocol_init(int protocol_id, struct scmi_handle *handle)
51{
52 scmi_prot_init_fn_t fn = idr_find(&scmi_protocols, protocol_id);
53
54 if (unlikely(!fn))
55 return -EINVAL;
56 return fn(handle);
57}
58
59static int scmi_dev_probe(struct device *dev)
60{
61 struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
62 struct scmi_device *scmi_dev = to_scmi_dev(dev);
63 const struct scmi_device_id *id;
64 int ret;
65
66 id = scmi_dev_match_id(scmi_dev, scmi_drv);
67 if (!id)
68 return -ENODEV;
69
70 if (!scmi_dev->handle)
71 return -EPROBE_DEFER;
72
73 ret = scmi_protocol_init(scmi_dev->protocol_id, scmi_dev->handle);
74 if (ret)
75 return ret;
76
77 return scmi_drv->probe(scmi_dev);
78}
79
80static int scmi_dev_remove(struct device *dev)
81{
82 struct scmi_driver *scmi_drv = to_scmi_driver(dev->driver);
83 struct scmi_device *scmi_dev = to_scmi_dev(dev);
84
85 if (scmi_drv->remove)
86 scmi_drv->remove(scmi_dev);
87
88 return 0;
89}
90
91static struct bus_type scmi_bus_type = {
92 .name = "scmi_protocol",
93 .match = scmi_dev_match,
94 .probe = scmi_dev_probe,
95 .remove = scmi_dev_remove,
96};
97
98int scmi_driver_register(struct scmi_driver *driver, struct module *owner,
99 const char *mod_name)
100{
101 int retval;
102
103 driver->driver.bus = &scmi_bus_type;
104 driver->driver.name = driver->name;
105 driver->driver.owner = owner;
106 driver->driver.mod_name = mod_name;
107
108 retval = driver_register(&driver->driver);
109 if (!retval)
110 pr_debug("registered new scmi driver %s\n", driver->name);
111
112 return retval;
113}
114EXPORT_SYMBOL_GPL(scmi_driver_register);
115
116void scmi_driver_unregister(struct scmi_driver *driver)
117{
118 driver_unregister(&driver->driver);
119}
120EXPORT_SYMBOL_GPL(scmi_driver_unregister);
121
122struct scmi_device *
123scmi_device_create(struct device_node *np, struct device *parent, int protocol)
124{
125 int id, retval;
126 struct scmi_device *scmi_dev;
127
128 id = ida_simple_get(&scmi_bus_id, 1, 0, GFP_KERNEL);
129 if (id < 0)
130 return NULL;
131
132 scmi_dev = kzalloc(sizeof(*scmi_dev), GFP_KERNEL);
133 if (!scmi_dev)
134 goto no_mem;
135
136 scmi_dev->id = id;
137 scmi_dev->protocol_id = protocol;
138 scmi_dev->dev.parent = parent;
139 scmi_dev->dev.of_node = np;
140 scmi_dev->dev.bus = &scmi_bus_type;
141 dev_set_name(&scmi_dev->dev, "scmi_dev.%d", id);
142
143 retval = device_register(&scmi_dev->dev);
144 if (!retval)
145 return scmi_dev;
146
147 put_device(&scmi_dev->dev);
148 kfree(scmi_dev);
149no_mem:
150 ida_simple_remove(&scmi_bus_id, id);
151 return NULL;
152}
153
154void scmi_device_destroy(struct scmi_device *scmi_dev)
155{
156 scmi_handle_put(scmi_dev->handle);
157 device_unregister(&scmi_dev->dev);
158 ida_simple_remove(&scmi_bus_id, scmi_dev->id);
159 kfree(scmi_dev);
160}
161
162void scmi_set_handle(struct scmi_device *scmi_dev)
163{
164 scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
165}
166
167int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn)
168{
169 int ret;
170
171 spin_lock(&protocol_lock);
172 ret = idr_alloc(&scmi_protocols, fn, protocol_id, protocol_id + 1,
173 GFP_ATOMIC);
174 if (ret != protocol_id)
175 pr_err("unable to allocate SCMI idr slot, err %d\n", ret);
176 spin_unlock(&protocol_lock);
177
178 return ret;
179}
180EXPORT_SYMBOL_GPL(scmi_protocol_register);
181
182void scmi_protocol_unregister(int protocol_id)
183{
184 spin_lock(&protocol_lock);
185 idr_remove(&scmi_protocols, protocol_id);
186 spin_unlock(&protocol_lock);
187}
188EXPORT_SYMBOL_GPL(scmi_protocol_unregister);
189
190static int __scmi_devices_unregister(struct device *dev, void *data)
191{
192 struct scmi_device *scmi_dev = to_scmi_dev(dev);
193
194 scmi_device_destroy(scmi_dev);
195 return 0;
196}
197
198static void scmi_devices_unregister(void)
199{
200 bus_for_each_dev(&scmi_bus_type, NULL, NULL, __scmi_devices_unregister);
201}
202
203static int __init scmi_bus_init(void)
204{
205 int retval;
206
207 retval = bus_register(&scmi_bus_type);
208 if (retval)
209 pr_err("scmi protocol bus register failed (%d)\n", retval);
210
211 return retval;
212}
213subsys_initcall(scmi_bus_init);
214
215static void __exit scmi_bus_exit(void)
216{
217 scmi_devices_unregister();
218 bus_unregister(&scmi_bus_type);
219 ida_destroy(&scmi_bus_id);
220}
221module_exit(scmi_bus_exit);
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 0fc9f5ae8684..95053ed5ccb9 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -96,6 +96,7 @@ int scmi_one_xfer_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
96 size_t tx_size, size_t rx_size, struct scmi_xfer **p); 96 size_t tx_size, size_t rx_size, struct scmi_xfer **p);
97int scmi_handle_put(const struct scmi_handle *handle); 97int scmi_handle_put(const struct scmi_handle *handle);
98struct scmi_handle *scmi_handle_get(struct device *dev); 98struct scmi_handle *scmi_handle_get(struct device *dev);
99void scmi_set_handle(struct scmi_device *scmi_dev);
99int scmi_version_get(const struct scmi_handle *h, u8 protocol, u32 *version); 100int scmi_version_get(const struct scmi_handle *h, u8 protocol, u32 *version);
100void scmi_setup_protocol_implemented(const struct scmi_handle *handle, 101void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
101 u8 *prot_imp); 102 u8 *prot_imp);
diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
index 08fcc1dd0276..464086b9d8c5 100644
--- a/include/linux/scmi_protocol.h
+++ b/include/linux/scmi_protocol.h
@@ -4,6 +4,7 @@
4 * 4 *
5 * Copyright (C) 2018 ARM Ltd. 5 * Copyright (C) 2018 ARM Ltd.
6 */ 6 */
7#include <linux/device.h>
7#include <linux/types.h> 8#include <linux/types.h>
8 9
9#define SCMI_MAX_STR_SIZE 16 10#define SCMI_MAX_STR_SIZE 16
@@ -51,3 +52,66 @@ enum scmi_std_protocol {
51 SCMI_PROTOCOL_CLOCK = 0x14, 52 SCMI_PROTOCOL_CLOCK = 0x14,
52 SCMI_PROTOCOL_SENSOR = 0x15, 53 SCMI_PROTOCOL_SENSOR = 0x15,
53}; 54};
55
56struct scmi_device {
57 u32 id;
58 u8 protocol_id;
59 struct device dev;
60 struct scmi_handle *handle;
61};
62
63#define to_scmi_dev(d) container_of(d, struct scmi_device, dev)
64
65struct scmi_device *
66scmi_device_create(struct device_node *np, struct device *parent, int protocol);
67void scmi_device_destroy(struct scmi_device *scmi_dev);
68
69struct scmi_device_id {
70 u8 protocol_id;
71};
72
73struct scmi_driver {
74 const char *name;
75 int (*probe)(struct scmi_device *sdev);
76 void (*remove)(struct scmi_device *sdev);
77 const struct scmi_device_id *id_table;
78
79 struct device_driver driver;
80};
81
82#define to_scmi_driver(d) container_of(d, struct scmi_driver, driver)
83
84#ifdef CONFIG_ARM_SCMI_PROTOCOL
85int scmi_driver_register(struct scmi_driver *driver,
86 struct module *owner, const char *mod_name);
87void scmi_driver_unregister(struct scmi_driver *driver);
88#else
89static inline int
90scmi_driver_register(struct scmi_driver *driver, struct module *owner,
91 const char *mod_name)
92{
93 return -EINVAL;
94}
95
96static inline void scmi_driver_unregister(struct scmi_driver *driver) {}
97#endif /* CONFIG_ARM_SCMI_PROTOCOL */
98
99#define scmi_register(driver) \
100 scmi_driver_register(driver, THIS_MODULE, KBUILD_MODNAME)
101#define scmi_unregister(driver) \
102 scmi_driver_unregister(driver)
103
104/**
105 * module_scmi_driver() - Helper macro for registering a scmi driver
106 * @__scmi_driver: scmi_driver structure
107 *
108 * Helper macro for scmi drivers to set up proper module init / exit
109 * functions. Replaces module_init() and module_exit() and keeps people from
110 * printing pointless things to the kernel log when their driver is loaded.
111 */
112#define module_scmi_driver(__scmi_driver) \
113 module_driver(__scmi_driver, scmi_register, scmi_unregister)
114
115typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *);
116int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn);
117void scmi_protocol_unregister(int protocol_id);