root/drivers/misc/mic/cosm/cosm_main.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cosm_hw_reset
  2. cosm_start
  3. cosm_stop
  4. cosm_reset_trigger_work
  5. cosm_reset
  6. cosm_shutdown
  7. cosm_driver_probe
  8. cosm_driver_remove
  9. cosm_suspend
  10. cosm_init
  11. cosm_exit

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Intel MIC Platform Software Stack (MPSS)
   4  *
   5  * Copyright(c) 2015 Intel Corporation.
   6  *
   7  * Intel MIC Coprocessor State Management (COSM) Driver
   8  */
   9 
  10 #include <linux/module.h>
  11 #include <linux/delay.h>
  12 #include <linux/idr.h>
  13 #include <linux/slab.h>
  14 #include <linux/cred.h>
  15 #include "cosm_main.h"
  16 
  17 static const char cosm_driver_name[] = "mic";
  18 
  19 /* COSM ID allocator */
  20 static struct ida g_cosm_ida;
  21 /* Class of MIC devices for sysfs accessibility. */
  22 static struct class *g_cosm_class;
  23 /* Number of MIC devices */
  24 static atomic_t g_num_dev;
  25 
  26 /**
  27  * cosm_hw_reset - Issue a HW reset for the MIC device
  28  * @cdev: pointer to cosm_device instance
  29  */
  30 static void cosm_hw_reset(struct cosm_device *cdev, bool force)
  31 {
  32         int i;
  33 
  34 #define MIC_RESET_TO (45)
  35         if (force && cdev->hw_ops->force_reset)
  36                 cdev->hw_ops->force_reset(cdev);
  37         else
  38                 cdev->hw_ops->reset(cdev);
  39 
  40         for (i = 0; i < MIC_RESET_TO; i++) {
  41                 if (cdev->hw_ops->ready(cdev)) {
  42                         cosm_set_state(cdev, MIC_READY);
  43                         return;
  44                 }
  45                 /*
  46                  * Resets typically take 10s of seconds to complete.
  47                  * Since an MMIO read is required to check if the
  48                  * firmware is ready or not, a 1 second delay works nicely.
  49                  */
  50                 msleep(1000);
  51         }
  52         cosm_set_state(cdev, MIC_RESET_FAILED);
  53 }
  54 
  55 /**
  56  * cosm_start - Start the MIC
  57  * @cdev: pointer to cosm_device instance
  58  *
  59  * This function prepares an MIC for boot and initiates boot.
  60  * RETURNS: An appropriate -ERRNO error value on error, or 0 for success.
  61  */
  62 int cosm_start(struct cosm_device *cdev)
  63 {
  64         const struct cred *orig_cred;
  65         struct cred *override_cred;
  66         int rc;
  67 
  68         mutex_lock(&cdev->cosm_mutex);
  69         if (!cdev->bootmode) {
  70                 dev_err(&cdev->dev, "%s %d bootmode not set\n",
  71                         __func__, __LINE__);
  72                 rc = -EINVAL;
  73                 goto unlock_ret;
  74         }
  75 retry:
  76         if (cdev->state != MIC_READY) {
  77                 dev_err(&cdev->dev, "%s %d MIC state not READY\n",
  78                         __func__, __LINE__);
  79                 rc = -EINVAL;
  80                 goto unlock_ret;
  81         }
  82         if (!cdev->hw_ops->ready(cdev)) {
  83                 cosm_hw_reset(cdev, false);
  84                 /*
  85                  * The state will either be MIC_READY if the reset succeeded
  86                  * or MIC_RESET_FAILED if the firmware reset failed.
  87                  */
  88                 goto retry;
  89         }
  90 
  91         /*
  92          * Set credentials to root to allow non-root user to download initramsfs
  93          * with 600 permissions
  94          */
  95         override_cred = prepare_creds();
  96         if (!override_cred) {
  97                 dev_err(&cdev->dev, "%s %d prepare_creds failed\n",
  98                         __func__, __LINE__);
  99                 rc = -ENOMEM;
 100                 goto unlock_ret;
 101         }
 102         override_cred->fsuid = GLOBAL_ROOT_UID;
 103         orig_cred = override_creds(override_cred);
 104 
 105         rc = cdev->hw_ops->start(cdev, cdev->index);
 106 
 107         revert_creds(orig_cred);
 108         put_cred(override_cred);
 109         if (rc)
 110                 goto unlock_ret;
 111 
 112         /*
 113          * If linux is being booted, card is treated 'online' only
 114          * when the scif interface in the card is up. If anything else
 115          * is booted, we set card to 'online' immediately.
 116          */
 117         if (!strcmp(cdev->bootmode, "linux"))
 118                 cosm_set_state(cdev, MIC_BOOTING);
 119         else
 120                 cosm_set_state(cdev, MIC_ONLINE);
 121 unlock_ret:
 122         mutex_unlock(&cdev->cosm_mutex);
 123         if (rc)
 124                 dev_err(&cdev->dev, "cosm_start failed rc %d\n", rc);
 125         return rc;
 126 }
 127 
 128 /**
 129  * cosm_stop - Prepare the MIC for reset and trigger reset
 130  * @cdev: pointer to cosm_device instance
 131  * @force: force a MIC to reset even if it is already reset and ready.
 132  *
 133  * RETURNS: None
 134  */
 135 void cosm_stop(struct cosm_device *cdev, bool force)
 136 {
 137         mutex_lock(&cdev->cosm_mutex);
 138         if (cdev->state != MIC_READY || force) {
 139                 /*
 140                  * Don't call hw_ops if they have been called previously.
 141                  * stop(..) calls device_unregister and will crash the system if
 142                  * called multiple times.
 143                  */
 144                 u8 state = cdev->state == MIC_RESETTING ?
 145                                         cdev->prev_state : cdev->state;
 146                 bool call_hw_ops = state != MIC_RESET_FAILED &&
 147                                         state != MIC_READY;
 148 
 149                 if (cdev->state != MIC_RESETTING)
 150                         cosm_set_state(cdev, MIC_RESETTING);
 151                 cdev->heartbeat_watchdog_enable = false;
 152                 if (call_hw_ops)
 153                         cdev->hw_ops->stop(cdev, force);
 154                 cosm_hw_reset(cdev, force);
 155                 cosm_set_shutdown_status(cdev, MIC_NOP);
 156                 if (call_hw_ops && cdev->hw_ops->post_reset)
 157                         cdev->hw_ops->post_reset(cdev, cdev->state);
 158         }
 159         mutex_unlock(&cdev->cosm_mutex);
 160         flush_work(&cdev->scif_work);
 161 }
 162 
 163 /**
 164  * cosm_reset_trigger_work - Trigger MIC reset
 165  * @work: The work structure
 166  *
 167  * This work is scheduled whenever the host wants to reset the MIC.
 168  */
 169 static void cosm_reset_trigger_work(struct work_struct *work)
 170 {
 171         struct cosm_device *cdev = container_of(work, struct cosm_device,
 172                                                 reset_trigger_work);
 173         cosm_stop(cdev, false);
 174 }
 175 
 176 /**
 177  * cosm_reset - Schedule MIC reset
 178  * @cdev: pointer to cosm_device instance
 179  *
 180  * RETURNS: An -EINVAL if the card is already READY or 0 for success.
 181  */
 182 int cosm_reset(struct cosm_device *cdev)
 183 {
 184         int rc = 0;
 185 
 186         mutex_lock(&cdev->cosm_mutex);
 187         if (cdev->state != MIC_READY) {
 188                 if (cdev->state != MIC_RESETTING) {
 189                         cdev->prev_state = cdev->state;
 190                         cosm_set_state(cdev, MIC_RESETTING);
 191                         schedule_work(&cdev->reset_trigger_work);
 192                 }
 193         } else {
 194                 dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__);
 195                 rc = -EINVAL;
 196         }
 197         mutex_unlock(&cdev->cosm_mutex);
 198         return rc;
 199 }
 200 
 201 /**
 202  * cosm_shutdown - Initiate MIC shutdown.
 203  * @cdev: pointer to cosm_device instance
 204  *
 205  * RETURNS: None
 206  */
 207 int cosm_shutdown(struct cosm_device *cdev)
 208 {
 209         struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN };
 210         int rc = 0;
 211 
 212         mutex_lock(&cdev->cosm_mutex);
 213         if (cdev->state != MIC_ONLINE) {
 214                 rc = -EINVAL;
 215                 dev_err(&cdev->dev, "%s %d skipping shutdown in state: %s\n",
 216                         __func__, __LINE__, cosm_state_string[cdev->state]);
 217                 goto err;
 218         }
 219 
 220         if (!cdev->epd) {
 221                 rc = -ENOTCONN;
 222                 dev_err(&cdev->dev, "%s %d scif endpoint not connected rc %d\n",
 223                         __func__, __LINE__, rc);
 224                 goto err;
 225         }
 226 
 227         rc = scif_send(cdev->epd, &msg, sizeof(msg), SCIF_SEND_BLOCK);
 228         if (rc < 0) {
 229                 dev_err(&cdev->dev, "%s %d scif_send failed rc %d\n",
 230                         __func__, __LINE__, rc);
 231                 goto err;
 232         }
 233         cdev->heartbeat_watchdog_enable = false;
 234         cosm_set_state(cdev, MIC_SHUTTING_DOWN);
 235         rc = 0;
 236 err:
 237         mutex_unlock(&cdev->cosm_mutex);
 238         return rc;
 239 }
 240 
 241 static int cosm_driver_probe(struct cosm_device *cdev)
 242 {
 243         int rc;
 244 
 245         /* Initialize SCIF server at first probe */
 246         if (atomic_add_return(1, &g_num_dev) == 1) {
 247                 rc = cosm_scif_init();
 248                 if (rc)
 249                         goto scif_exit;
 250         }
 251         mutex_init(&cdev->cosm_mutex);
 252         INIT_WORK(&cdev->reset_trigger_work, cosm_reset_trigger_work);
 253         INIT_WORK(&cdev->scif_work, cosm_scif_work);
 254         cdev->sysfs_heartbeat_enable = true;
 255         cosm_sysfs_init(cdev);
 256         cdev->sdev = device_create_with_groups(g_cosm_class, cdev->dev.parent,
 257                                MKDEV(0, cdev->index), cdev, cdev->attr_group,
 258                                "mic%d", cdev->index);
 259         if (IS_ERR(cdev->sdev)) {
 260                 rc = PTR_ERR(cdev->sdev);
 261                 dev_err(&cdev->dev, "device_create_with_groups failed rc %d\n",
 262                         rc);
 263                 goto scif_exit;
 264         }
 265 
 266         cdev->state_sysfs = sysfs_get_dirent(cdev->sdev->kobj.sd,
 267                 "state");
 268         if (!cdev->state_sysfs) {
 269                 rc = -ENODEV;
 270                 dev_err(&cdev->dev, "sysfs_get_dirent failed rc %d\n", rc);
 271                 goto destroy_device;
 272         }
 273         cosm_create_debug_dir(cdev);
 274         return 0;
 275 destroy_device:
 276         device_destroy(g_cosm_class, MKDEV(0, cdev->index));
 277 scif_exit:
 278         if (atomic_dec_and_test(&g_num_dev))
 279                 cosm_scif_exit();
 280         return rc;
 281 }
 282 
 283 static void cosm_driver_remove(struct cosm_device *cdev)
 284 {
 285         cosm_delete_debug_dir(cdev);
 286         sysfs_put(cdev->state_sysfs);
 287         device_destroy(g_cosm_class, MKDEV(0, cdev->index));
 288         flush_work(&cdev->reset_trigger_work);
 289         cosm_stop(cdev, false);
 290         if (atomic_dec_and_test(&g_num_dev))
 291                 cosm_scif_exit();
 292 
 293         /* These sysfs entries might have allocated */
 294         kfree(cdev->cmdline);
 295         kfree(cdev->firmware);
 296         kfree(cdev->ramdisk);
 297         kfree(cdev->bootmode);
 298 }
 299 
 300 static int cosm_suspend(struct device *dev)
 301 {
 302         struct cosm_device *cdev = dev_to_cosm(dev);
 303 
 304         mutex_lock(&cdev->cosm_mutex);
 305         switch (cdev->state) {
 306         /**
 307          * Suspend/freeze hooks in userspace have already shutdown the card.
 308          * Card should be 'ready' in most cases. It is however possible that
 309          * some userspace application initiated a boot. In those cases, we
 310          * simply reset the card.
 311          */
 312         case MIC_ONLINE:
 313         case MIC_BOOTING:
 314         case MIC_SHUTTING_DOWN:
 315                 mutex_unlock(&cdev->cosm_mutex);
 316                 cosm_stop(cdev, false);
 317                 break;
 318         default:
 319                 mutex_unlock(&cdev->cosm_mutex);
 320                 break;
 321         }
 322         return 0;
 323 }
 324 
 325 static const struct dev_pm_ops cosm_pm_ops = {
 326         .suspend = cosm_suspend,
 327         .freeze = cosm_suspend
 328 };
 329 
 330 static struct cosm_driver cosm_driver = {
 331         .driver = {
 332                 .name =  KBUILD_MODNAME,
 333                 .owner = THIS_MODULE,
 334                 .pm = &cosm_pm_ops,
 335         },
 336         .probe = cosm_driver_probe,
 337         .remove = cosm_driver_remove
 338 };
 339 
 340 static int __init cosm_init(void)
 341 {
 342         int ret;
 343 
 344         cosm_init_debugfs();
 345 
 346         g_cosm_class = class_create(THIS_MODULE, cosm_driver_name);
 347         if (IS_ERR(g_cosm_class)) {
 348                 ret = PTR_ERR(g_cosm_class);
 349                 pr_err("class_create failed ret %d\n", ret);
 350                 goto cleanup_debugfs;
 351         }
 352 
 353         ida_init(&g_cosm_ida);
 354         ret = cosm_register_driver(&cosm_driver);
 355         if (ret) {
 356                 pr_err("cosm_register_driver failed ret %d\n", ret);
 357                 goto ida_destroy;
 358         }
 359         return 0;
 360 ida_destroy:
 361         ida_destroy(&g_cosm_ida);
 362         class_destroy(g_cosm_class);
 363 cleanup_debugfs:
 364         cosm_exit_debugfs();
 365         return ret;
 366 }
 367 
 368 static void __exit cosm_exit(void)
 369 {
 370         cosm_unregister_driver(&cosm_driver);
 371         ida_destroy(&g_cosm_ida);
 372         class_destroy(g_cosm_class);
 373         cosm_exit_debugfs();
 374 }
 375 
 376 module_init(cosm_init);
 377 module_exit(cosm_exit);
 378 
 379 MODULE_AUTHOR("Intel Corporation");
 380 MODULE_DESCRIPTION("Intel(R) MIC Coprocessor State Management (COSM) Driver");
 381 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */