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