Merge branch 'x86/debug' into core/urgent
[linux-2.6-microblaze.git] / drivers / misc / mic / cosm / cosm_main.c
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");