This source file includes following definitions.
- cosm_hw_reset
- cosm_start
- cosm_stop
- cosm_reset_trigger_work
- cosm_reset
- cosm_shutdown
- cosm_driver_probe
- cosm_driver_remove
- cosm_suspend
- cosm_init
- cosm_exit
1
2
3
4
5
6
7
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
20 static struct ida g_cosm_ida;
21
22 static struct class *g_cosm_class;
23
24 static atomic_t g_num_dev;
25
26
27
28
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
47
48
49
50 msleep(1000);
51 }
52 cosm_set_state(cdev, MIC_RESET_FAILED);
53 }
54
55
56
57
58
59
60
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
86
87
88 goto retry;
89 }
90
91
92
93
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
114
115
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
130
131
132
133
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
141
142
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
165
166
167
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
178
179
180
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
203
204
205
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
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
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
308
309
310
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");