File Index
Files and directories
Sign in
FILE

fw-core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/greybus/fw-core.c C
  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
 15struct 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
 22static struct spilib_ops *spilib_ops;
 23
 24struct 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
 31static 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
 52static 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
 61static 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
217err_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);
221err_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
231static 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
256static const struct greybus_bundle_id gb_fw_core_id_table[] = {
257	{ GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FW_MANAGEMENT) },
258	{ }
259};
260
261static 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
268static 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
291cap_exit:
292	cap_exit();
293fw_mgmt_exit:
294	fw_mgmt_exit();
295
296	return ret;
297}
298module_init(fw_core_init);
299
300static void __exit fw_core_exit(void)
301{
302	greybus_deregister(&gb_fw_core_driver);
303	cap_exit();
304	fw_mgmt_exit();
305}
306module_exit(fw_core_exit);
307
308MODULE_ALIAS("greybus:firmware");
309MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
310MODULE_DESCRIPTION("Greybus Firmware Bundle Driver");
311MODULE_LICENSE("GPL v2");
~/ Read-only file preview.