EDAC: i7core, sb_edac: Don't return NOTIFY_BAD from mce_decoder callback
[linux/fpc-iii.git] / drivers / misc / mic / cosm / cosm_main.c
blob7005cb1e01d21080539f4d4ece33cf0beaa4278e
1 /*
2 * Intel MIC Platform Software Stack (MPSS)
4 * Copyright(c) 2015 Intel Corporation.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License, version 2, as
8 * published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * The full GNU General Public License is included in this distribution in
16 * the file called "COPYING".
18 * Intel MIC Coprocessor State Management (COSM) Driver
22 #include <linux/module.h>
23 #include <linux/delay.h>
24 #include <linux/idr.h>
25 #include <linux/slab.h>
26 #include <linux/cred.h>
27 #include "cosm_main.h"
29 static const char cosm_driver_name[] = "mic";
31 /* COSM ID allocator */
32 static struct ida g_cosm_ida;
33 /* Class of MIC devices for sysfs accessibility. */
34 static struct class *g_cosm_class;
35 /* Number of MIC devices */
36 static atomic_t g_num_dev;
38 /**
39 * cosm_hw_reset - Issue a HW reset for the MIC device
40 * @cdev: pointer to cosm_device instance
42 static void cosm_hw_reset(struct cosm_device *cdev, bool force)
44 int i;
46 #define MIC_RESET_TO (45)
47 if (force && cdev->hw_ops->force_reset)
48 cdev->hw_ops->force_reset(cdev);
49 else
50 cdev->hw_ops->reset(cdev);
52 for (i = 0; i < MIC_RESET_TO; i++) {
53 if (cdev->hw_ops->ready(cdev)) {
54 cosm_set_state(cdev, MIC_READY);
55 return;
58 * Resets typically take 10s of seconds to complete.
59 * Since an MMIO read is required to check if the
60 * firmware is ready or not, a 1 second delay works nicely.
62 msleep(1000);
64 cosm_set_state(cdev, MIC_RESET_FAILED);
67 /**
68 * cosm_start - Start the MIC
69 * @cdev: pointer to cosm_device instance
71 * This function prepares an MIC for boot and initiates boot.
72 * RETURNS: An appropriate -ERRNO error value on error, or 0 for success.
74 int cosm_start(struct cosm_device *cdev)
76 const struct cred *orig_cred;
77 struct cred *override_cred;
78 int rc;
80 mutex_lock(&cdev->cosm_mutex);
81 if (!cdev->bootmode) {
82 dev_err(&cdev->dev, "%s %d bootmode not set\n",
83 __func__, __LINE__);
84 rc = -EINVAL;
85 goto unlock_ret;
87 retry:
88 if (cdev->state != MIC_READY) {
89 dev_err(&cdev->dev, "%s %d MIC state not READY\n",
90 __func__, __LINE__);
91 rc = -EINVAL;
92 goto unlock_ret;
94 if (!cdev->hw_ops->ready(cdev)) {
95 cosm_hw_reset(cdev, false);
97 * The state will either be MIC_READY if the reset succeeded
98 * or MIC_RESET_FAILED if the firmware reset failed.
100 goto retry;
104 * Set credentials to root to allow non-root user to download initramsfs
105 * with 600 permissions
107 override_cred = prepare_creds();
108 if (!override_cred) {
109 dev_err(&cdev->dev, "%s %d prepare_creds failed\n",
110 __func__, __LINE__);
111 rc = -ENOMEM;
112 goto unlock_ret;
114 override_cred->fsuid = GLOBAL_ROOT_UID;
115 orig_cred = override_creds(override_cred);
117 rc = cdev->hw_ops->start(cdev, cdev->index);
119 revert_creds(orig_cred);
120 put_cred(override_cred);
121 if (rc)
122 goto unlock_ret;
125 * If linux is being booted, card is treated 'online' only
126 * when the scif interface in the card is up. If anything else
127 * is booted, we set card to 'online' immediately.
129 if (!strcmp(cdev->bootmode, "linux"))
130 cosm_set_state(cdev, MIC_BOOTING);
131 else
132 cosm_set_state(cdev, MIC_ONLINE);
133 unlock_ret:
134 mutex_unlock(&cdev->cosm_mutex);
135 if (rc)
136 dev_err(&cdev->dev, "cosm_start failed rc %d\n", rc);
137 return rc;
141 * cosm_stop - Prepare the MIC for reset and trigger reset
142 * @cdev: pointer to cosm_device instance
143 * @force: force a MIC to reset even if it is already reset and ready.
145 * RETURNS: None
147 void cosm_stop(struct cosm_device *cdev, bool force)
149 mutex_lock(&cdev->cosm_mutex);
150 if (cdev->state != MIC_READY || force) {
152 * Don't call hw_ops if they have been called previously.
153 * stop(..) calls device_unregister and will crash the system if
154 * called multiple times.
156 u8 state = cdev->state == MIC_RESETTING ?
157 cdev->prev_state : cdev->state;
158 bool call_hw_ops = state != MIC_RESET_FAILED &&
159 state != MIC_READY;
161 if (cdev->state != MIC_RESETTING)
162 cosm_set_state(cdev, MIC_RESETTING);
163 cdev->heartbeat_watchdog_enable = false;
164 if (call_hw_ops)
165 cdev->hw_ops->stop(cdev, force);
166 cosm_hw_reset(cdev, force);
167 cosm_set_shutdown_status(cdev, MIC_NOP);
168 if (call_hw_ops && cdev->hw_ops->post_reset)
169 cdev->hw_ops->post_reset(cdev, cdev->state);
171 mutex_unlock(&cdev->cosm_mutex);
172 flush_work(&cdev->scif_work);
176 * cosm_reset_trigger_work - Trigger MIC reset
177 * @work: The work structure
179 * This work is scheduled whenever the host wants to reset the MIC.
181 static void cosm_reset_trigger_work(struct work_struct *work)
183 struct cosm_device *cdev = container_of(work, struct cosm_device,
184 reset_trigger_work);
185 cosm_stop(cdev, false);
189 * cosm_reset - Schedule MIC reset
190 * @cdev: pointer to cosm_device instance
192 * RETURNS: An -EINVAL if the card is already READY or 0 for success.
194 int cosm_reset(struct cosm_device *cdev)
196 int rc = 0;
198 mutex_lock(&cdev->cosm_mutex);
199 if (cdev->state != MIC_READY) {
200 if (cdev->state != MIC_RESETTING) {
201 cdev->prev_state = cdev->state;
202 cosm_set_state(cdev, MIC_RESETTING);
203 schedule_work(&cdev->reset_trigger_work);
205 } else {
206 dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__);
207 rc = -EINVAL;
209 mutex_unlock(&cdev->cosm_mutex);
210 return rc;
214 * cosm_shutdown - Initiate MIC shutdown.
215 * @cdev: pointer to cosm_device instance
217 * RETURNS: None
219 int cosm_shutdown(struct cosm_device *cdev)
221 struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN };
222 int rc = 0;
224 mutex_lock(&cdev->cosm_mutex);
225 if (cdev->state != MIC_ONLINE) {
226 rc = -EINVAL;
227 dev_err(&cdev->dev, "%s %d skipping shutdown in state: %s\n",
228 __func__, __LINE__, cosm_state_string[cdev->state]);
229 goto err;
232 if (!cdev->epd) {
233 rc = -ENOTCONN;
234 dev_err(&cdev->dev, "%s %d scif endpoint not connected rc %d\n",
235 __func__, __LINE__, rc);
236 goto err;
239 rc = scif_send(cdev->epd, &msg, sizeof(msg), SCIF_SEND_BLOCK);
240 if (rc < 0) {
241 dev_err(&cdev->dev, "%s %d scif_send failed rc %d\n",
242 __func__, __LINE__, rc);
243 goto err;
245 cdev->heartbeat_watchdog_enable = false;
246 cosm_set_state(cdev, MIC_SHUTTING_DOWN);
247 rc = 0;
248 err:
249 mutex_unlock(&cdev->cosm_mutex);
250 return rc;
253 static int cosm_driver_probe(struct cosm_device *cdev)
255 int rc;
257 /* Initialize SCIF server at first probe */
258 if (atomic_add_return(1, &g_num_dev) == 1) {
259 rc = cosm_scif_init();
260 if (rc)
261 goto scif_exit;
263 mutex_init(&cdev->cosm_mutex);
264 INIT_WORK(&cdev->reset_trigger_work, cosm_reset_trigger_work);
265 INIT_WORK(&cdev->scif_work, cosm_scif_work);
266 cdev->sysfs_heartbeat_enable = true;
267 cosm_sysfs_init(cdev);
268 cdev->sdev = device_create_with_groups(g_cosm_class, cdev->dev.parent,
269 MKDEV(0, cdev->index), cdev, cdev->attr_group,
270 "mic%d", cdev->index);
271 if (IS_ERR(cdev->sdev)) {
272 rc = PTR_ERR(cdev->sdev);
273 dev_err(&cdev->dev, "device_create_with_groups failed rc %d\n",
274 rc);
275 goto scif_exit;
278 cdev->state_sysfs = sysfs_get_dirent(cdev->sdev->kobj.sd,
279 "state");
280 if (!cdev->state_sysfs) {
281 rc = -ENODEV;
282 dev_err(&cdev->dev, "sysfs_get_dirent failed rc %d\n", rc);
283 goto destroy_device;
285 cosm_create_debug_dir(cdev);
286 return 0;
287 destroy_device:
288 device_destroy(g_cosm_class, MKDEV(0, cdev->index));
289 scif_exit:
290 if (atomic_dec_and_test(&g_num_dev))
291 cosm_scif_exit();
292 return rc;
295 static void cosm_driver_remove(struct cosm_device *cdev)
297 cosm_delete_debug_dir(cdev);
298 sysfs_put(cdev->state_sysfs);
299 device_destroy(g_cosm_class, MKDEV(0, cdev->index));
300 flush_work(&cdev->reset_trigger_work);
301 cosm_stop(cdev, false);
302 if (atomic_dec_and_test(&g_num_dev))
303 cosm_scif_exit();
305 /* These sysfs entries might have allocated */
306 kfree(cdev->cmdline);
307 kfree(cdev->firmware);
308 kfree(cdev->ramdisk);
309 kfree(cdev->bootmode);
312 static int cosm_suspend(struct device *dev)
314 struct cosm_device *cdev = dev_to_cosm(dev);
316 mutex_lock(&cdev->cosm_mutex);
317 switch (cdev->state) {
319 * Suspend/freeze hooks in userspace have already shutdown the card.
320 * Card should be 'ready' in most cases. It is however possible that
321 * some userspace application initiated a boot. In those cases, we
322 * simply reset the card.
324 case MIC_ONLINE:
325 case MIC_BOOTING:
326 case MIC_SHUTTING_DOWN:
327 mutex_unlock(&cdev->cosm_mutex);
328 cosm_stop(cdev, false);
329 break;
330 default:
331 mutex_unlock(&cdev->cosm_mutex);
332 break;
334 return 0;
337 static const struct dev_pm_ops cosm_pm_ops = {
338 .suspend = cosm_suspend,
339 .freeze = cosm_suspend
342 static struct cosm_driver cosm_driver = {
343 .driver = {
344 .name = KBUILD_MODNAME,
345 .owner = THIS_MODULE,
346 .pm = &cosm_pm_ops,
348 .probe = cosm_driver_probe,
349 .remove = cosm_driver_remove
352 static int __init cosm_init(void)
354 int ret;
356 cosm_init_debugfs();
358 g_cosm_class = class_create(THIS_MODULE, cosm_driver_name);
359 if (IS_ERR(g_cosm_class)) {
360 ret = PTR_ERR(g_cosm_class);
361 pr_err("class_create failed ret %d\n", ret);
362 goto cleanup_debugfs;
365 ida_init(&g_cosm_ida);
366 ret = cosm_register_driver(&cosm_driver);
367 if (ret) {
368 pr_err("cosm_register_driver failed ret %d\n", ret);
369 goto ida_destroy;
371 return 0;
372 ida_destroy:
373 ida_destroy(&g_cosm_ida);
374 class_destroy(g_cosm_class);
375 cleanup_debugfs:
376 cosm_exit_debugfs();
377 return ret;
380 static void __exit cosm_exit(void)
382 cosm_unregister_driver(&cosm_driver);
383 ida_destroy(&g_cosm_ida);
384 class_destroy(g_cosm_class);
385 cosm_exit_debugfs();
388 module_init(cosm_init);
389 module_exit(cosm_exit);
391 MODULE_AUTHOR("Intel Corporation");
392 MODULE_DESCRIPTION("Intel(R) MIC Coprocessor State Management (COSM) Driver");
393 MODULE_LICENSE("GPL v2");