min/max: remove sparse warnings when they're nested
[cascardo/linux.git] / drivers / staging / greybus / fw-core.c
1 /*
2  * Greybus Firmware Core Bundle Driver.
3  *
4  * Copyright 2016 Google Inc.
5  * Copyright 2016 Linaro Ltd.
6  *
7  * Released under the GPLv2 only.
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
16 struct 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
23 static struct spilib_ops *spilib_ops;
24
25 struct 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
32 static 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
53 static 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
62 static 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         /* Parse CPorts and create connections */
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                         /* Disallow multiple Firmware Management CPorts */
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                         /* Disallow multiple Firmware Download CPorts */
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                         /* Disallow multiple SPI CPorts */
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                         /* Disallow multiple CAP CPorts */
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         /* Firmware Management connection is mandatory */
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                 /* We may still be able to work with the Interface */
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                 /* We may still be able to work with the Interface */
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                 /* We may still be able to work with the Interface */
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                 /* We may still be able to work with the Interface */
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         /* FIXME: Remove this after S2 Loader gets runtime PM support */
213         if (!(bundle->intf->quirks & GB_INTERFACE_QUIRK_NO_PM))
214                 gb_pm_runtime_put_autosuspend(bundle);
215
216         return 0;
217
218 err_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);
222 err_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
232 static 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         /* FIXME: Remove this after S2 Loader gets runtime PM support */
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
257 static const struct greybus_bundle_id gb_fw_core_id_table[] = {
258         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FW_MANAGEMENT) },
259         { }
260 };
261
262 static 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
269 static 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
292 cap_exit:
293         cap_exit();
294 fw_mgmt_exit:
295         fw_mgmt_exit();
296
297         return ret;
298 }
299 module_init(fw_core_init);
300
301 static void __exit fw_core_exit(void)
302 {
303         greybus_deregister(&gb_fw_core_driver);
304         cap_exit();
305         fw_mgmt_exit();
306 }
307 module_exit(fw_core_exit);
308
309 MODULE_ALIAS("greybus:firmware");
310 MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
311 MODULE_DESCRIPTION("Greybus Firmware Bundle Driver");
312 MODULE_LICENSE("GPL v2");