Linux-libre 4.7-rc7-gnu
[librecmc/linux-libre.git] / drivers / misc / mic / cosm / cosm_main.c
1 /*
2  * Intel MIC Platform Software Stack (MPSS)
3  *
4  * Copyright(c) 2015 Intel Corporation.
5  *
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.
9  *
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.
14  *
15  * The full GNU General Public License is included in this distribution in
16  * the file called "COPYING".
17  *
18  * Intel MIC Coprocessor State Management (COSM) Driver
19  *
20  */
21
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"
28
29 static const char cosm_driver_name[] = "mic";
30
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;
37
38 /**
39  * cosm_hw_reset - Issue a HW reset for the MIC device
40  * @cdev: pointer to cosm_device instance
41  */
42 static void cosm_hw_reset(struct cosm_device *cdev, bool force)
43 {
44         int i;
45
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);
51
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;
56                 }
57                 /*
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.
61                  */
62                 msleep(1000);
63         }
64         cosm_set_state(cdev, MIC_RESET_FAILED);
65 }
66
67 /**
68  * cosm_start - Start the MIC
69  * @cdev: pointer to cosm_device instance
70  *
71  * This function prepares an MIC for boot and initiates boot.
72  * RETURNS: An appropriate -ERRNO error value on error, or 0 for success.
73  */
74 int cosm_start(struct cosm_device *cdev)
75 {
76         const struct cred *orig_cred;
77         struct cred *override_cred;
78         int rc;
79
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;
86         }
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;
93         }
94         if (!cdev->hw_ops->ready(cdev)) {
95                 cosm_hw_reset(cdev, false);
96                 /*
97                  * The state will either be MIC_READY if the reset succeeded
98                  * or MIC_RESET_FAILED if the firmware reset failed.
99                  */
100                 goto retry;
101         }
102
103         /*
104          * Set credentials to root to allow non-root user to download initramsfs
105          * with 600 permissions
106          */
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;
113         }
114         override_cred->fsuid = GLOBAL_ROOT_UID;
115         orig_cred = override_creds(override_cred);
116
117         rc = cdev->hw_ops->start(cdev, cdev->index);
118
119         revert_creds(orig_cred);
120         put_cred(override_cred);
121         if (rc)
122                 goto unlock_ret;
123
124         /*
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.
128          */
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;
138 }
139
140 /**
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.
144  *
145  * RETURNS: None
146  */
147 void cosm_stop(struct cosm_device *cdev, bool force)
148 {
149         mutex_lock(&cdev->cosm_mutex);
150         if (cdev->state != MIC_READY || force) {
151                 /*
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.
155                  */
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;
160
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);
170         }
171         mutex_unlock(&cdev->cosm_mutex);
172         flush_work(&cdev->scif_work);
173 }
174
175 /**
176  * cosm_reset_trigger_work - Trigger MIC reset
177  * @work: The work structure
178  *
179  * This work is scheduled whenever the host wants to reset the MIC.
180  */
181 static void cosm_reset_trigger_work(struct work_struct *work)
182 {
183         struct cosm_device *cdev = container_of(work, struct cosm_device,
184                                                 reset_trigger_work);
185         cosm_stop(cdev, false);
186 }
187
188 /**
189  * cosm_reset - Schedule MIC reset
190  * @cdev: pointer to cosm_device instance
191  *
192  * RETURNS: An -EINVAL if the card is already READY or 0 for success.
193  */
194 int cosm_reset(struct cosm_device *cdev)
195 {
196         int rc = 0;
197
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);
204                 }
205         } else {
206                 dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__);
207                 rc = -EINVAL;
208         }
209         mutex_unlock(&cdev->cosm_mutex);
210         return rc;
211 }
212
213 /**
214  * cosm_shutdown - Initiate MIC shutdown.
215  * @cdev: pointer to cosm_device instance
216  *
217  * RETURNS: None
218  */
219 int cosm_shutdown(struct cosm_device *cdev)
220 {
221         struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN };
222         int rc = 0;
223
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;
230         }
231
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;
237         }
238
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;
244         }
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;
251 }
252
253 static int cosm_driver_probe(struct cosm_device *cdev)
254 {
255         int rc;
256
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;
262         }
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;
276         }
277
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;
284         }
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;
293 }
294
295 static void cosm_driver_remove(struct cosm_device *cdev)
296 {
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();
304
305         /* These sysfs entries might have allocated */
306         kfree(cdev->cmdline);
307         kfree(cdev->firmware);
308         kfree(cdev->ramdisk);
309         kfree(cdev->bootmode);
310 }
311
312 static int cosm_suspend(struct device *dev)
313 {
314         struct cosm_device *cdev = dev_to_cosm(dev);
315
316         mutex_lock(&cdev->cosm_mutex);
317         switch (cdev->state) {
318         /**
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.
323          */
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;
333         }
334         return 0;
335 }
336
337 static const struct dev_pm_ops cosm_pm_ops = {
338         .suspend = cosm_suspend,
339         .freeze = cosm_suspend
340 };
341
342 static struct cosm_driver cosm_driver = {
343         .driver = {
344                 .name =  KBUILD_MODNAME,
345                 .owner = THIS_MODULE,
346                 .pm = &cosm_pm_ops,
347         },
348         .probe = cosm_driver_probe,
349         .remove = cosm_driver_remove
350 };
351
352 static int __init cosm_init(void)
353 {
354         int ret;
355
356         cosm_init_debugfs();
357
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;
363         }
364
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;
370         }
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;
378 }
379
380 static void __exit cosm_exit(void)
381 {
382         cosm_unregister_driver(&cosm_driver);
383         ida_destroy(&g_cosm_ida);
384         class_destroy(g_cosm_class);
385         cosm_exit_debugfs();
386 }
387
388 module_init(cosm_init);
389 module_exit(cosm_exit);
390
391 MODULE_AUTHOR("Intel Corporation");
392 MODULE_DESCRIPTION("Intel(R) MIC Coprocessor State Management (COSM) Driver");
393 MODULE_LICENSE("GPL v2");