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 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 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 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 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 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 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 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