Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.7 381 lines 9.3 kB view raw
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 17static const char cosm_driver_name[] = "mic"; 18 19/* COSM ID allocator */ 20static struct ida g_cosm_ida; 21/* Class of MIC devices for sysfs accessibility. */ 22static struct class *g_cosm_class; 23/* Number of MIC devices */ 24static 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 */ 30static 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 */ 62int 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 } 75retry: 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); 121unlock_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 */ 135void 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 */ 169static 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 */ 182int 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 */ 207int 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; 236err: 237 mutex_unlock(&cdev->cosm_mutex); 238 return rc; 239} 240 241static 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; 275destroy_device: 276 device_destroy(g_cosm_class, MKDEV(0, cdev->index)); 277scif_exit: 278 if (atomic_dec_and_test(&g_num_dev)) 279 cosm_scif_exit(); 280 return rc; 281} 282 283static 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 300static 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 325static const struct dev_pm_ops cosm_pm_ops = { 326 .suspend = cosm_suspend, 327 .freeze = cosm_suspend 328}; 329 330static 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 340static 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; 360ida_destroy: 361 ida_destroy(&g_cosm_ida); 362 class_destroy(g_cosm_class); 363cleanup_debugfs: 364 cosm_exit_debugfs(); 365 return ret; 366} 367 368static 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 376module_init(cosm_init); 377module_exit(cosm_exit); 378 379MODULE_AUTHOR("Intel Corporation"); 380MODULE_DESCRIPTION("Intel(R) MIC Coprocessor State Management (COSM) Driver"); 381MODULE_LICENSE("GPL v2");