linux/drivers/staging/greybus/fw-core.c
<<
>>
Prefs
   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
  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        /* 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
 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        /* 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
 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