1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Greybus Firmware Core Bundle Driver.
4 *
5 * Copyright 2016 Google Inc.
6 * Copyright 2016 Linaro Ltd.
7 */
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9
10 #include <linux/firmware.h>
11 #include <linux/greybus.h>
12 #include "firmware.h"
13 #include "spilib.h"
14
15 struct gb_fw_core {
16 struct gb_connection *download_connection;
17 struct gb_connection *mgmt_connection;
18 struct gb_connection *spi_connection;
19 struct gb_connection *cap_connection;
20 };
21
22 static struct spilib_ops *spilib_ops;
23
to_fw_mgmt_connection(struct device * dev)24 struct gb_connection *to_fw_mgmt_connection(struct device *dev)
25 {
26 struct gb_fw_core *fw_core = dev_get_drvdata(dev);
27
28 return fw_core->mgmt_connection;
29 }
30
gb_fw_spi_connection_init(struct gb_connection * connection)31 static int gb_fw_spi_connection_init(struct gb_connection *connection)
32 {
33 int ret;
34
35 if (!connection)
36 return 0;
37
38 ret = gb_connection_enable(connection);
39 if (ret)
40 return ret;
41
42 ret = gb_spilib_master_init(connection, &connection->bundle->dev,
43 spilib_ops);
44 if (ret) {
45 gb_connection_disable(connection);
46 return ret;
47 }
48
49 return 0;
50 }
51
gb_fw_spi_connection_exit(struct gb_connection * connection)52 static void gb_fw_spi_connection_exit(struct gb_connection *connection)
53 {
54 if (!connection)
55 return;
56
57 gb_spilib_master_exit(connection);
58 gb_connection_disable(connection);
59 }
60
gb_fw_core_probe(struct gb_bundle * bundle,const struct greybus_bundle_id * id)61 static int gb_fw_core_probe(struct gb_bundle *bundle,
62 const struct greybus_bundle_id *id)
63 {
64 struct greybus_descriptor_cport *cport_desc;
65 struct gb_connection *connection;
66 struct gb_fw_core *fw_core;
67 int ret, i;
68 u16 cport_id;
69 u8 protocol_id;
70
71 fw_core = kzalloc(sizeof(*fw_core), GFP_KERNEL);
72 if (!fw_core)
73 return -ENOMEM;
74
75 /* Parse CPorts and create connections */
76 for (i = 0; i < bundle->num_cports; i++) {
77 cport_desc = &bundle->cport_desc[i];
78 cport_id = le16_to_cpu(cport_desc->id);
79 protocol_id = cport_desc->protocol_id;
80
81 switch (protocol_id) {
82 case GREYBUS_PROTOCOL_FW_MANAGEMENT:
83 /* Disallow multiple Firmware Management CPorts */
84 if (fw_core->mgmt_connection) {
85 dev_err(&bundle->dev,
86 "multiple management CPorts found\n");
87 ret = -EINVAL;
88 goto err_destroy_connections;
89 }
90
91 connection = gb_connection_create(bundle, cport_id,
92 gb_fw_mgmt_request_handler);
93 if (IS_ERR(connection)) {
94 ret = PTR_ERR(connection);
95 dev_err(&bundle->dev,
96 "failed to create management connection (%d)\n",
97 ret);
98 goto err_destroy_connections;
99 }
100
101 fw_core->mgmt_connection = connection;
102 break;
103 case GREYBUS_PROTOCOL_FW_DOWNLOAD:
104 /* Disallow multiple Firmware Download CPorts */
105 if (fw_core->download_connection) {
106 dev_err(&bundle->dev,
107 "multiple download CPorts found\n");
108 ret = -EINVAL;
109 goto err_destroy_connections;
110 }
111
112 connection = gb_connection_create(bundle, cport_id,
113 gb_fw_download_request_handler);
114 if (IS_ERR(connection)) {
115 dev_err(&bundle->dev, "failed to create download connection (%ld)\n",
116 PTR_ERR(connection));
117 } else {
118 fw_core->download_connection = connection;
119 }
120
121 break;
122 case GREYBUS_PROTOCOL_SPI:
123 /* Disallow multiple SPI CPorts */
124 if (fw_core->spi_connection) {
125 dev_err(&bundle->dev,
126 "multiple SPI CPorts found\n");
127 ret = -EINVAL;
128 goto err_destroy_connections;
129 }
130
131 connection = gb_connection_create(bundle, cport_id,
132 NULL);
133 if (IS_ERR(connection)) {
134 dev_err(&bundle->dev, "failed to create SPI connection (%ld)\n",
135 PTR_ERR(connection));
136 } else {
137 fw_core->spi_connection = connection;
138 }
139
140 break;
141 case GREYBUS_PROTOCOL_AUTHENTICATION:
142 /* Disallow multiple CAP CPorts */
143 if (fw_core->cap_connection) {
144 dev_err(&bundle->dev, "multiple Authentication CPorts found\n");
145 ret = -EINVAL;
146 goto err_destroy_connections;
147 }
148
149 connection = gb_connection_create(bundle, cport_id,
150 NULL);
151 if (IS_ERR(connection)) {
152 dev_err(&bundle->dev, "failed to create Authentication connection (%ld)\n",
153 PTR_ERR(connection));
154 } else {
155 fw_core->cap_connection = connection;
156 }
157
158 break;
159 default:
160 dev_err(&bundle->dev, "invalid protocol id (0x%02x)\n",
161 protocol_id);
162 ret = -EINVAL;
163 goto err_destroy_connections;
164 }
165 }
166
167 /* Firmware Management connection is mandatory */
168 if (!fw_core->mgmt_connection) {
169 dev_err(&bundle->dev, "missing management connection\n");
170 ret = -ENODEV;
171 goto err_destroy_connections;
172 }
173
174 ret = gb_fw_download_connection_init(fw_core->download_connection);
175 if (ret) {
176 /* We may still be able to work with the Interface */
177 dev_err(&bundle->dev, "failed to initialize firmware download connection, disable it (%d)\n",
178 ret);
179 gb_connection_destroy(fw_core->download_connection);
180 fw_core->download_connection = NULL;
181 }
182
183 ret = gb_fw_spi_connection_init(fw_core->spi_connection);
184 if (ret) {
185 /* We may still be able to work with the Interface */
186 dev_err(&bundle->dev, "failed to initialize SPI connection, disable it (%d)\n",
187 ret);
188 gb_connection_destroy(fw_core->spi_connection);
189 fw_core->spi_connection = NULL;
190 }
191
192 ret = gb_cap_connection_init(fw_core->cap_connection);
193 if (ret) {
194 /* We may still be able to work with the Interface */
195 dev_err(&bundle->dev, "failed to initialize CAP connection, disable it (%d)\n",
196 ret);
197 gb_connection_destroy(fw_core->cap_connection);
198 fw_core->cap_connection = NULL;
199 }
200
201 ret = gb_fw_mgmt_connection_init(fw_core->mgmt_connection);
202 if (ret) {
203 /* We may still be able to work with the Interface */
204 dev_err(&bundle->dev, "failed to initialize firmware management connection, disable it (%d)\n",
205 ret);
206 goto err_exit_connections;
207 }
208
209 greybus_set_drvdata(bundle, fw_core);
210
211 /* FIXME: Remove this after S2 Loader gets runtime PM support */
212 if (!(bundle->intf->quirks & GB_INTERFACE_QUIRK_NO_PM))
213 gb_pm_runtime_put_autosuspend(bundle);
214
215 return 0;
216
217 err_exit_connections:
218 gb_cap_connection_exit(fw_core->cap_connection);
219 gb_fw_spi_connection_exit(fw_core->spi_connection);
220 gb_fw_download_connection_exit(fw_core->download_connection);
221 err_destroy_connections:
222 gb_connection_destroy(fw_core->mgmt_connection);
223 gb_connection_destroy(fw_core->cap_connection);
224 gb_connection_destroy(fw_core->spi_connection);
225 gb_connection_destroy(fw_core->download_connection);
226 kfree(fw_core);
227
228 return ret;
229 }
230
gb_fw_core_disconnect(struct gb_bundle * bundle)231 static void gb_fw_core_disconnect(struct gb_bundle *bundle)
232 {
233 struct gb_fw_core *fw_core = greybus_get_drvdata(bundle);
234 int ret;
235
236 /* FIXME: Remove this after S2 Loader gets runtime PM support */
237 if (!(bundle->intf->quirks & GB_INTERFACE_QUIRK_NO_PM)) {
238 ret = gb_pm_runtime_get_sync(bundle);
239 if (ret)
240 gb_pm_runtime_get_noresume(bundle);
241 }
242
243 gb_fw_mgmt_connection_exit(fw_core->mgmt_connection);
244 gb_cap_connection_exit(fw_core->cap_connection);
245 gb_fw_spi_connection_exit(fw_core->spi_connection);
246 gb_fw_download_connection_exit(fw_core->download_connection);
247
248 gb_connection_destroy(fw_core->mgmt_connection);
249 gb_connection_destroy(fw_core->cap_connection);
250 gb_connection_destroy(fw_core->spi_connection);
251 gb_connection_destroy(fw_core->download_connection);
252
253 kfree(fw_core);
254 }
255
256 static const struct greybus_bundle_id gb_fw_core_id_table[] = {
257 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FW_MANAGEMENT) },
258 { }
259 };
260
261 static struct greybus_driver gb_fw_core_driver = {
262 .name = "gb-firmware",
263 .probe = gb_fw_core_probe,
264 .disconnect = gb_fw_core_disconnect,
265 .id_table = gb_fw_core_id_table,
266 };
267
fw_core_init(void)268 static int fw_core_init(void)
269 {
270 int ret;
271
272 ret = fw_mgmt_init();
273 if (ret) {
274 pr_err("Failed to initialize fw-mgmt core (%d)\n", ret);
275 return ret;
276 }
277
278 ret = cap_init();
279 if (ret) {
280 pr_err("Failed to initialize component authentication core (%d)\n",
281 ret);
282 goto fw_mgmt_exit;
283 }
284
285 ret = greybus_register(&gb_fw_core_driver);
286 if (ret)
287 goto cap_exit;
288
289 return 0;
290
291 cap_exit:
292 cap_exit();
293 fw_mgmt_exit:
294 fw_mgmt_exit();
295
296 return ret;
297 }
298 module_init(fw_core_init);
299
fw_core_exit(void)300 static void __exit fw_core_exit(void)
301 {
302 greybus_deregister(&gb_fw_core_driver);
303 cap_exit();
304 fw_mgmt_exit();
305 }
306 module_exit(fw_core_exit);
307
308 MODULE_ALIAS("greybus:firmware");
309 MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
310 MODULE_DESCRIPTION("Greybus Firmware Bundle Driver");
311 MODULE_LICENSE("GPL v2");
312