linux/drivers/staging/greybus/fw-core.c
<<
>>
Prefs
   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");
 312