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