aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAshutosh Dixit <ashutosh.dixit@intel.com>2015-09-29 21:12:27 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-10-04 07:46:06 -0400
commitf8487a26b285feea2cb210efec42c49dce9ae708 (patch)
treee592e68a7d1f744bb9f847db59c2aeb5150f5824
parenta3283d831a71d53234fcc279e0d9ed262cef94d5 (diff)
misc: mic: Coprocessor State Management (COSM) driver
The COSM driver allows boot, shutdown and reset of Intel MIC devices via sysfs. This functionality was previously present in the Intel MIC host driver but has now been taken out into a separate driver so that it can be shared between multiple generations of Intel MIC products. The sysfs kernel ABI used by the COSM driver is the same as that defined originally for the MIC host driver in Documentation/ABI/testing/sysfs-class-mic.txt. The COSM driver also contains support for dumping the MIC card log_buf and doing a "force reset" for the card via debugfs. The OSPM support present in the MIC host driver has now largely been moved to user space and only a small required OSPM functionality is now present in the driver. Reviewed-by: Nikhil Rao <nikhil.rao@intel.com> Reviewed-by: Sudeep Dutt <sudeep.dutt@intel.com> Signed-off-by: Dasaratharaman Chandramouli <dasaratharaman.chandramouli@intel.com> Signed-off-by: Ashutosh Dixit <ashutosh.dixit@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/misc/mic/cosm/Makefile10
-rw-r--r--drivers/misc/mic/cosm/cosm_debugfs.c155
-rw-r--r--drivers/misc/mic/cosm/cosm_main.c388
-rw-r--r--drivers/misc/mic/cosm/cosm_main.h70
-rw-r--r--drivers/misc/mic/cosm/cosm_sysfs.c461
5 files changed, 1084 insertions, 0 deletions
diff --git a/drivers/misc/mic/cosm/Makefile b/drivers/misc/mic/cosm/Makefile
new file mode 100644
index 000000000000..b85d4d49df46
--- /dev/null
+++ b/drivers/misc/mic/cosm/Makefile
@@ -0,0 +1,10 @@
1#
2# Makefile - Intel MIC Coprocessor State Management (COSM) Driver
3# Copyright(c) 2015, Intel Corporation.
4#
5obj-$(CONFIG_MIC_COSM) += mic_cosm.o
6
7mic_cosm-objs := cosm_main.o
8mic_cosm-objs += cosm_debugfs.o
9mic_cosm-objs += cosm_sysfs.o
10mic_cosm-objs += cosm_scif_server.o
diff --git a/drivers/misc/mic/cosm/cosm_debugfs.c b/drivers/misc/mic/cosm/cosm_debugfs.c
new file mode 100644
index 000000000000..30d953dff4fa
--- /dev/null
+++ b/drivers/misc/mic/cosm/cosm_debugfs.c
@@ -0,0 +1,155 @@
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/debugfs.h>
23#include <linux/slab.h>
24#include "cosm_main.h"
25
26/* Debugfs parent dir */
27static struct dentry *cosm_dbg;
28
29/**
30 * cosm_log_buf_show - Display MIC kernel log buffer
31 *
32 * log_buf addr/len is read from System.map by user space
33 * and populated in sysfs entries.
34 */
35static int cosm_log_buf_show(struct seq_file *s, void *unused)
36{
37 void __iomem *log_buf_va;
38 int __iomem *log_buf_len_va;
39 struct cosm_device *cdev = s->private;
40 void *kva;
41 int size;
42 u64 aper_offset;
43
44 if (!cdev || !cdev->log_buf_addr || !cdev->log_buf_len)
45 goto done;
46
47 mutex_lock(&cdev->cosm_mutex);
48 switch (cdev->state) {
49 case MIC_BOOTING:
50 case MIC_ONLINE:
51 case MIC_SHUTTING_DOWN:
52 break;
53 default:
54 goto unlock;
55 }
56
57 /*
58 * Card kernel will never be relocated and any kernel text/data mapping
59 * can be translated to phys address by subtracting __START_KERNEL_map.
60 */
61 aper_offset = (u64)cdev->log_buf_len - __START_KERNEL_map;
62 log_buf_len_va = cdev->hw_ops->aper(cdev)->va + aper_offset;
63 aper_offset = (u64)cdev->log_buf_addr - __START_KERNEL_map;
64 log_buf_va = cdev->hw_ops->aper(cdev)->va + aper_offset;
65
66 size = ioread32(log_buf_len_va);
67 kva = kmalloc(size, GFP_KERNEL);
68 if (!kva)
69 goto unlock;
70
71 memcpy_fromio(kva, log_buf_va, size);
72 seq_write(s, kva, size);
73 kfree(kva);
74unlock:
75 mutex_unlock(&cdev->cosm_mutex);
76done:
77 return 0;
78}
79
80static int cosm_log_buf_open(struct inode *inode, struct file *file)
81{
82 return single_open(file, cosm_log_buf_show, inode->i_private);
83}
84
85static const struct file_operations log_buf_ops = {
86 .owner = THIS_MODULE,
87 .open = cosm_log_buf_open,
88 .read = seq_read,
89 .llseek = seq_lseek,
90 .release = single_release
91};
92
93/**
94 * cosm_force_reset_show - Force MIC reset
95 *
96 * Invokes the force_reset COSM bus op instead of the standard reset
97 * op in case a force reset of the MIC device is required
98 */
99static int cosm_force_reset_show(struct seq_file *s, void *pos)
100{
101 struct cosm_device *cdev = s->private;
102
103 cosm_stop(cdev, true);
104 return 0;
105}
106
107static int cosm_force_reset_debug_open(struct inode *inode, struct file *file)
108{
109 return single_open(file, cosm_force_reset_show, inode->i_private);
110}
111
112static const struct file_operations force_reset_ops = {
113 .owner = THIS_MODULE,
114 .open = cosm_force_reset_debug_open,
115 .read = seq_read,
116 .llseek = seq_lseek,
117 .release = single_release
118};
119
120void cosm_create_debug_dir(struct cosm_device *cdev)
121{
122 char name[16];
123
124 if (!cosm_dbg)
125 return;
126
127 scnprintf(name, sizeof(name), "mic%d", cdev->index);
128 cdev->dbg_dir = debugfs_create_dir(name, cosm_dbg);
129 if (!cdev->dbg_dir)
130 return;
131
132 debugfs_create_file("log_buf", 0444, cdev->dbg_dir, cdev, &log_buf_ops);
133 debugfs_create_file("force_reset", 0444, cdev->dbg_dir, cdev,
134 &force_reset_ops);
135}
136
137void cosm_delete_debug_dir(struct cosm_device *cdev)
138{
139 if (!cdev->dbg_dir)
140 return;
141
142 debugfs_remove_recursive(cdev->dbg_dir);
143}
144
145void cosm_init_debugfs(void)
146{
147 cosm_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
148 if (!cosm_dbg)
149 pr_err("can't create debugfs dir\n");
150}
151
152void cosm_exit_debugfs(void)
153{
154 debugfs_remove(cosm_dbg);
155}
diff --git a/drivers/misc/mic/cosm/cosm_main.c b/drivers/misc/mic/cosm/cosm_main.c
new file mode 100644
index 000000000000..4b4b356c797d
--- /dev/null
+++ b/drivers/misc/mic/cosm/cosm_main.c
@@ -0,0 +1,388 @@
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
29static const char cosm_driver_name[] = "mic";
30
31/* COSM ID allocator */
32static struct ida g_cosm_ida;
33/* Class of MIC devices for sysfs accessibility. */
34static struct class *g_cosm_class;
35/* Number of MIC devices */
36static 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 */
42static 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 */
74int 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 }
87retry:
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);
133unlock_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 */
147void 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 bool call_hw_ops = cdev->state != MIC_RESET_FAILED &&
157 cdev->state != MIC_READY;
158
159 if (cdev->state != MIC_RESETTING)
160 cosm_set_state(cdev, MIC_RESETTING);
161 cdev->heartbeat_watchdog_enable = false;
162 if (call_hw_ops)
163 cdev->hw_ops->stop(cdev, force);
164 cosm_hw_reset(cdev, force);
165 cosm_set_shutdown_status(cdev, MIC_NOP);
166 if (call_hw_ops && cdev->hw_ops->post_reset)
167 cdev->hw_ops->post_reset(cdev, cdev->state);
168 }
169 mutex_unlock(&cdev->cosm_mutex);
170 flush_work(&cdev->scif_work);
171}
172
173/**
174 * cosm_reset_trigger_work - Trigger MIC reset
175 * @work: The work structure
176 *
177 * This work is scheduled whenever the host wants to reset the MIC.
178 */
179static void cosm_reset_trigger_work(struct work_struct *work)
180{
181 struct cosm_device *cdev = container_of(work, struct cosm_device,
182 reset_trigger_work);
183 cosm_stop(cdev, false);
184}
185
186/**
187 * cosm_reset - Schedule MIC reset
188 * @cdev: pointer to cosm_device instance
189 *
190 * RETURNS: An -EINVAL if the card is already READY or 0 for success.
191 */
192int cosm_reset(struct cosm_device *cdev)
193{
194 int rc = 0;
195
196 mutex_lock(&cdev->cosm_mutex);
197 if (cdev->state != MIC_READY) {
198 cosm_set_state(cdev, MIC_RESETTING);
199 schedule_work(&cdev->reset_trigger_work);
200 } else {
201 dev_err(&cdev->dev, "%s %d MIC is READY\n", __func__, __LINE__);
202 rc = -EINVAL;
203 }
204 mutex_unlock(&cdev->cosm_mutex);
205 return rc;
206}
207
208/**
209 * cosm_shutdown - Initiate MIC shutdown.
210 * @cdev: pointer to cosm_device instance
211 *
212 * RETURNS: None
213 */
214int cosm_shutdown(struct cosm_device *cdev)
215{
216 struct cosm_msg msg = { .id = COSM_MSG_SHUTDOWN };
217 int rc = 0;
218
219 mutex_lock(&cdev->cosm_mutex);
220 if (cdev->state != MIC_ONLINE) {
221 rc = -EINVAL;
222 dev_err(&cdev->dev, "%s %d skipping shutdown in state: %s\n",
223 __func__, __LINE__, cosm_state_string[cdev->state]);
224 goto err;
225 }
226
227 if (!cdev->epd) {
228 rc = -ENOTCONN;
229 dev_err(&cdev->dev, "%s %d scif endpoint not connected rc %d\n",
230 __func__, __LINE__, rc);
231 goto err;
232 }
233
234 rc = scif_send(cdev->epd, &msg, sizeof(msg), SCIF_SEND_BLOCK);
235 if (rc < 0) {
236 dev_err(&cdev->dev, "%s %d scif_send failed rc %d\n",
237 __func__, __LINE__, rc);
238 goto err;
239 }
240 cdev->heartbeat_watchdog_enable = false;
241 cosm_set_state(cdev, MIC_SHUTTING_DOWN);
242 rc = 0;
243err:
244 mutex_unlock(&cdev->cosm_mutex);
245 return rc;
246}
247
248static int cosm_driver_probe(struct cosm_device *cdev)
249{
250 int rc;
251
252 /* Initialize SCIF server at first probe */
253 if (atomic_add_return(1, &g_num_dev) == 1) {
254 rc = cosm_scif_init();
255 if (rc)
256 goto scif_exit;
257 }
258 mutex_init(&cdev->cosm_mutex);
259 INIT_WORK(&cdev->reset_trigger_work, cosm_reset_trigger_work);
260 INIT_WORK(&cdev->scif_work, cosm_scif_work);
261 cdev->sysfs_heartbeat_enable = true;
262 cosm_sysfs_init(cdev);
263 cdev->sdev = device_create_with_groups(g_cosm_class, cdev->dev.parent,
264 MKDEV(0, cdev->index), cdev, cdev->attr_group,
265 "mic%d", cdev->index);
266 if (IS_ERR(cdev->sdev)) {
267 rc = PTR_ERR(cdev->sdev);
268 dev_err(&cdev->dev, "device_create_with_groups failed rc %d\n",
269 rc);
270 goto scif_exit;
271 }
272
273 cdev->state_sysfs = sysfs_get_dirent(cdev->sdev->kobj.sd,
274 "state");
275 if (!cdev->state_sysfs) {
276 rc = -ENODEV;
277 dev_err(&cdev->dev, "sysfs_get_dirent failed rc %d\n", rc);
278 goto destroy_device;
279 }
280 cosm_create_debug_dir(cdev);
281 return 0;
282destroy_device:
283 device_destroy(g_cosm_class, MKDEV(0, cdev->index));
284scif_exit:
285 if (atomic_dec_and_test(&g_num_dev))
286 cosm_scif_exit();
287 return rc;
288}
289
290static void cosm_driver_remove(struct cosm_device *cdev)
291{
292 cosm_delete_debug_dir(cdev);
293 sysfs_put(cdev->state_sysfs);
294 device_destroy(g_cosm_class, MKDEV(0, cdev->index));
295 flush_work(&cdev->reset_trigger_work);
296 cosm_stop(cdev, false);
297 if (atomic_dec_and_test(&g_num_dev))
298 cosm_scif_exit();
299
300 /* These sysfs entries might have allocated */
301 kfree(cdev->cmdline);
302 kfree(cdev->firmware);
303 kfree(cdev->ramdisk);
304 kfree(cdev->bootmode);
305}
306
307static int cosm_suspend(struct device *dev)
308{
309 struct cosm_device *cdev = dev_to_cosm(dev);
310
311 mutex_lock(&cdev->cosm_mutex);
312 switch (cdev->state) {
313 /**
314 * Suspend/freeze hooks in userspace have already shutdown the card.
315 * Card should be 'ready' in most cases. It is however possible that
316 * some userspace application initiated a boot. In those cases, we
317 * simply reset the card.
318 */
319 case MIC_ONLINE:
320 case MIC_BOOTING:
321 case MIC_SHUTTING_DOWN:
322 mutex_unlock(&cdev->cosm_mutex);
323 cosm_stop(cdev, false);
324 break;
325 default:
326 mutex_unlock(&cdev->cosm_mutex);
327 break;
328 }
329 return 0;
330}
331
332static const struct dev_pm_ops cosm_pm_ops = {
333 .suspend = cosm_suspend,
334 .freeze = cosm_suspend
335};
336
337static struct cosm_driver cosm_driver = {
338 .driver = {
339 .name = KBUILD_MODNAME,
340 .owner = THIS_MODULE,
341 .pm = &cosm_pm_ops,
342 },
343 .probe = cosm_driver_probe,
344 .remove = cosm_driver_remove
345};
346
347static int __init cosm_init(void)
348{
349 int ret;
350
351 cosm_init_debugfs();
352
353 g_cosm_class = class_create(THIS_MODULE, cosm_driver_name);
354 if (IS_ERR(g_cosm_class)) {
355 ret = PTR_ERR(g_cosm_class);
356 pr_err("class_create failed ret %d\n", ret);
357 goto cleanup_debugfs;
358 }
359
360 ida_init(&g_cosm_ida);
361 ret = cosm_register_driver(&cosm_driver);
362 if (ret) {
363 pr_err("cosm_register_driver failed ret %d\n", ret);
364 goto ida_destroy;
365 }
366 return 0;
367ida_destroy:
368 ida_destroy(&g_cosm_ida);
369 class_destroy(g_cosm_class);
370cleanup_debugfs:
371 cosm_exit_debugfs();
372 return ret;
373}
374
375static void __exit cosm_exit(void)
376{
377 cosm_unregister_driver(&cosm_driver);
378 ida_destroy(&g_cosm_ida);
379 class_destroy(g_cosm_class);
380 cosm_exit_debugfs();
381}
382
383module_init(cosm_init);
384module_exit(cosm_exit);
385
386MODULE_AUTHOR("Intel Corporation");
387MODULE_DESCRIPTION("Intel(R) MIC Coprocessor State Management (COSM) Driver");
388MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/mic/cosm/cosm_main.h b/drivers/misc/mic/cosm/cosm_main.h
new file mode 100644
index 000000000000..f01156fca881
--- /dev/null
+++ b/drivers/misc/mic/cosm/cosm_main.h
@@ -0,0 +1,70 @@
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#ifndef _COSM_COSM_H_
22#define _COSM_COSM_H_
23
24#include <linux/scif.h>
25#include "../bus/cosm_bus.h"
26
27#define COSM_HEARTBEAT_SEND_SEC 30
28#define SCIF_COSM_LISTEN_PORT 201
29
30/**
31 * enum COSM msg id's
32 * @COSM_MSG_SHUTDOWN: host->card trigger shutdown
33 * @COSM_MSG_SYNC_TIME: host->card send host time to card to sync time
34 * @COSM_MSG_HEARTBEAT: card->host heartbeat
35 * @COSM_MSG_SHUTDOWN_STATUS: card->host with shutdown status as payload
36 */
37enum cosm_msg_id {
38 COSM_MSG_SHUTDOWN,
39 COSM_MSG_SYNC_TIME,
40 COSM_MSG_HEARTBEAT,
41 COSM_MSG_SHUTDOWN_STATUS,
42};
43
44struct cosm_msg {
45 u64 id;
46 union {
47 u64 shutdown_status;
48 struct timespec64 timespec;
49 };
50};
51
52extern const char * const cosm_state_string[];
53extern const char * const cosm_shutdown_status_string[];
54
55void cosm_sysfs_init(struct cosm_device *cdev);
56int cosm_start(struct cosm_device *cdev);
57void cosm_stop(struct cosm_device *cdev, bool force);
58int cosm_reset(struct cosm_device *cdev);
59int cosm_shutdown(struct cosm_device *cdev);
60void cosm_set_state(struct cosm_device *cdev, u8 state);
61void cosm_set_shutdown_status(struct cosm_device *cdev, u8 status);
62void cosm_init_debugfs(void);
63void cosm_exit_debugfs(void);
64void cosm_create_debug_dir(struct cosm_device *cdev);
65void cosm_delete_debug_dir(struct cosm_device *cdev);
66int cosm_scif_init(void);
67void cosm_scif_exit(void);
68void cosm_scif_work(struct work_struct *work);
69
70#endif
diff --git a/drivers/misc/mic/cosm/cosm_sysfs.c b/drivers/misc/mic/cosm/cosm_sysfs.c
new file mode 100644
index 000000000000..29d6863b6e59
--- /dev/null
+++ b/drivers/misc/mic/cosm/cosm_sysfs.c
@@ -0,0 +1,461 @@
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#include <linux/slab.h>
22#include "cosm_main.h"
23
24/*
25 * A state-to-string lookup table, for exposing a human readable state
26 * via sysfs. Always keep in sync with enum cosm_states
27 */
28const char * const cosm_state_string[] = {
29 [MIC_READY] = "ready",
30 [MIC_BOOTING] = "booting",
31 [MIC_ONLINE] = "online",
32 [MIC_SHUTTING_DOWN] = "shutting_down",
33 [MIC_RESETTING] = "resetting",
34 [MIC_RESET_FAILED] = "reset_failed",
35};
36
37/*
38 * A shutdown-status-to-string lookup table, for exposing a human
39 * readable state via sysfs. Always keep in sync with enum cosm_shutdown_status
40 */
41const char * const cosm_shutdown_status_string[] = {
42 [MIC_NOP] = "nop",
43 [MIC_CRASHED] = "crashed",
44 [MIC_HALTED] = "halted",
45 [MIC_POWER_OFF] = "poweroff",
46 [MIC_RESTART] = "restart",
47};
48
49void cosm_set_shutdown_status(struct cosm_device *cdev, u8 shutdown_status)
50{
51 dev_dbg(&cdev->dev, "Shutdown Status %s -> %s\n",
52 cosm_shutdown_status_string[cdev->shutdown_status],
53 cosm_shutdown_status_string[shutdown_status]);
54 cdev->shutdown_status = shutdown_status;
55}
56
57void cosm_set_state(struct cosm_device *cdev, u8 state)
58{
59 dev_dbg(&cdev->dev, "State %s -> %s\n",
60 cosm_state_string[cdev->state],
61 cosm_state_string[state]);
62 cdev->state = state;
63 sysfs_notify_dirent(cdev->state_sysfs);
64}
65
66static ssize_t
67family_show(struct device *dev, struct device_attribute *attr, char *buf)
68{
69 struct cosm_device *cdev = dev_get_drvdata(dev);
70
71 if (!cdev)
72 return -EINVAL;
73
74 return cdev->hw_ops->family(cdev, buf);
75}
76static DEVICE_ATTR_RO(family);
77
78static ssize_t
79stepping_show(struct device *dev, struct device_attribute *attr, char *buf)
80{
81 struct cosm_device *cdev = dev_get_drvdata(dev);
82
83 if (!cdev)
84 return -EINVAL;
85
86 return cdev->hw_ops->stepping(cdev, buf);
87}
88static DEVICE_ATTR_RO(stepping);
89
90static ssize_t
91state_show(struct device *dev, struct device_attribute *attr, char *buf)
92{
93 struct cosm_device *cdev = dev_get_drvdata(dev);
94
95 if (!cdev || cdev->state >= MIC_LAST)
96 return -EINVAL;
97
98 return scnprintf(buf, PAGE_SIZE, "%s\n",
99 cosm_state_string[cdev->state]);
100}
101
102static ssize_t
103state_store(struct device *dev, struct device_attribute *attr,
104 const char *buf, size_t count)
105{
106 struct cosm_device *cdev = dev_get_drvdata(dev);
107 int rc;
108
109 if (!cdev)
110 return -EINVAL;
111
112 if (sysfs_streq(buf, "boot")) {
113 rc = cosm_start(cdev);
114 goto done;
115 }
116 if (sysfs_streq(buf, "reset")) {
117 rc = cosm_reset(cdev);
118 goto done;
119 }
120
121 if (sysfs_streq(buf, "shutdown")) {
122 rc = cosm_shutdown(cdev);
123 goto done;
124 }
125 rc = -EINVAL;
126done:
127 if (rc)
128 count = rc;
129 return count;
130}
131static DEVICE_ATTR_RW(state);
132
133static ssize_t shutdown_status_show(struct device *dev,
134 struct device_attribute *attr, char *buf)
135{
136 struct cosm_device *cdev = dev_get_drvdata(dev);
137
138 if (!cdev || cdev->shutdown_status >= MIC_STATUS_LAST)
139 return -EINVAL;
140
141 return scnprintf(buf, PAGE_SIZE, "%s\n",
142 cosm_shutdown_status_string[cdev->shutdown_status]);
143}
144static DEVICE_ATTR_RO(shutdown_status);
145
146static ssize_t
147heartbeat_enable_show(struct device *dev,
148 struct device_attribute *attr, char *buf)
149{
150 struct cosm_device *cdev = dev_get_drvdata(dev);
151
152 if (!cdev)
153 return -EINVAL;
154
155 return scnprintf(buf, PAGE_SIZE, "%d\n", cdev->sysfs_heartbeat_enable);
156}
157
158static ssize_t
159heartbeat_enable_store(struct device *dev,
160 struct device_attribute *attr,
161 const char *buf, size_t count)
162{
163 struct cosm_device *cdev = dev_get_drvdata(dev);
164 int enable;
165 int ret;
166
167 if (!cdev)
168 return -EINVAL;
169
170 mutex_lock(&cdev->cosm_mutex);
171 ret = kstrtoint(buf, 10, &enable);
172 if (ret)
173 goto unlock;
174
175 cdev->sysfs_heartbeat_enable = enable;
176 /* if state is not online, cdev->heartbeat_watchdog_enable is 0 */
177 if (cdev->state == MIC_ONLINE)
178 cdev->heartbeat_watchdog_enable = enable;
179 ret = count;
180unlock:
181 mutex_unlock(&cdev->cosm_mutex);
182 return ret;
183}
184static DEVICE_ATTR_RW(heartbeat_enable);
185
186static ssize_t
187cmdline_show(struct device *dev, struct device_attribute *attr, char *buf)
188{
189 struct cosm_device *cdev = dev_get_drvdata(dev);
190 char *cmdline;
191
192 if (!cdev)
193 return -EINVAL;
194
195 cmdline = cdev->cmdline;
196
197 if (cmdline)
198 return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline);
199 return 0;
200}
201
202static ssize_t
203cmdline_store(struct device *dev, struct device_attribute *attr,
204 const char *buf, size_t count)
205{
206 struct cosm_device *cdev = dev_get_drvdata(dev);
207
208 if (!cdev)
209 return -EINVAL;
210
211 mutex_lock(&cdev->cosm_mutex);
212 kfree(cdev->cmdline);
213
214 cdev->cmdline = kmalloc(count + 1, GFP_KERNEL);
215 if (!cdev->cmdline) {
216 count = -ENOMEM;
217 goto unlock;
218 }
219
220 strncpy(cdev->cmdline, buf, count);
221
222 if (cdev->cmdline[count - 1] == '\n')
223 cdev->cmdline[count - 1] = '\0';
224 else
225 cdev->cmdline[count] = '\0';
226unlock:
227 mutex_unlock(&cdev->cosm_mutex);
228 return count;
229}
230static DEVICE_ATTR_RW(cmdline);
231
232static ssize_t
233firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
234{
235 struct cosm_device *cdev = dev_get_drvdata(dev);
236 char *firmware;
237
238 if (!cdev)
239 return -EINVAL;
240
241 firmware = cdev->firmware;
242
243 if (firmware)
244 return scnprintf(buf, PAGE_SIZE, "%s\n", firmware);
245 return 0;
246}
247
248static ssize_t
249firmware_store(struct device *dev, struct device_attribute *attr,
250 const char *buf, size_t count)
251{
252 struct cosm_device *cdev = dev_get_drvdata(dev);
253
254 if (!cdev)
255 return -EINVAL;
256
257 mutex_lock(&cdev->cosm_mutex);
258 kfree(cdev->firmware);
259
260 cdev->firmware = kmalloc(count + 1, GFP_KERNEL);
261 if (!cdev->firmware) {
262 count = -ENOMEM;
263 goto unlock;
264 }
265 strncpy(cdev->firmware, buf, count);
266
267 if (cdev->firmware[count - 1] == '\n')
268 cdev->firmware[count - 1] = '\0';
269 else
270 cdev->firmware[count] = '\0';
271unlock:
272 mutex_unlock(&cdev->cosm_mutex);
273 return count;
274}
275static DEVICE_ATTR_RW(firmware);
276
277static ssize_t
278ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf)
279{
280 struct cosm_device *cdev = dev_get_drvdata(dev);
281 char *ramdisk;
282
283 if (!cdev)
284 return -EINVAL;
285
286 ramdisk = cdev->ramdisk;
287
288 if (ramdisk)
289 return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk);
290 return 0;
291}
292
293static ssize_t
294ramdisk_store(struct device *dev, struct device_attribute *attr,
295 const char *buf, size_t count)
296{
297 struct cosm_device *cdev = dev_get_drvdata(dev);
298
299 if (!cdev)
300 return -EINVAL;
301
302 mutex_lock(&cdev->cosm_mutex);
303 kfree(cdev->ramdisk);
304
305 cdev->ramdisk = kmalloc(count + 1, GFP_KERNEL);
306 if (!cdev->ramdisk) {
307 count = -ENOMEM;
308 goto unlock;
309 }
310
311 strncpy(cdev->ramdisk, buf, count);
312
313 if (cdev->ramdisk[count - 1] == '\n')
314 cdev->ramdisk[count - 1] = '\0';
315 else
316 cdev->ramdisk[count] = '\0';
317unlock:
318 mutex_unlock(&cdev->cosm_mutex);
319 return count;
320}
321static DEVICE_ATTR_RW(ramdisk);
322
323static ssize_t
324bootmode_show(struct device *dev, struct device_attribute *attr, char *buf)
325{
326 struct cosm_device *cdev = dev_get_drvdata(dev);
327 char *bootmode;
328
329 if (!cdev)
330 return -EINVAL;
331
332 bootmode = cdev->bootmode;
333
334 if (bootmode)
335 return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode);
336 return 0;
337}
338
339static ssize_t
340bootmode_store(struct device *dev, struct device_attribute *attr,
341 const char *buf, size_t count)
342{
343 struct cosm_device *cdev = dev_get_drvdata(dev);
344
345 if (!cdev)
346 return -EINVAL;
347
348 if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "flash"))
349 return -EINVAL;
350
351 mutex_lock(&cdev->cosm_mutex);
352 kfree(cdev->bootmode);
353
354 cdev->bootmode = kmalloc(count + 1, GFP_KERNEL);
355 if (!cdev->bootmode) {
356 count = -ENOMEM;
357 goto unlock;
358 }
359
360 strncpy(cdev->bootmode, buf, count);
361
362 if (cdev->bootmode[count - 1] == '\n')
363 cdev->bootmode[count - 1] = '\0';
364 else
365 cdev->bootmode[count] = '\0';
366unlock:
367 mutex_unlock(&cdev->cosm_mutex);
368 return count;
369}
370static DEVICE_ATTR_RW(bootmode);
371
372static ssize_t
373log_buf_addr_show(struct device *dev, struct device_attribute *attr,
374 char *buf)
375{
376 struct cosm_device *cdev = dev_get_drvdata(dev);
377
378 if (!cdev)
379 return -EINVAL;
380
381 return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_addr);
382}
383
384static ssize_t
385log_buf_addr_store(struct device *dev, struct device_attribute *attr,
386 const char *buf, size_t count)
387{
388 struct cosm_device *cdev = dev_get_drvdata(dev);
389 int ret;
390 unsigned long addr;
391
392 if (!cdev)
393 return -EINVAL;
394
395 ret = kstrtoul(buf, 16, &addr);
396 if (ret)
397 goto exit;
398
399 cdev->log_buf_addr = (void *)addr;
400 ret = count;
401exit:
402 return ret;
403}
404static DEVICE_ATTR_RW(log_buf_addr);
405
406static ssize_t
407log_buf_len_show(struct device *dev, struct device_attribute *attr,
408 char *buf)
409{
410 struct cosm_device *cdev = dev_get_drvdata(dev);
411
412 if (!cdev)
413 return -EINVAL;
414
415 return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_len);
416}
417
418static ssize_t
419log_buf_len_store(struct device *dev, struct device_attribute *attr,
420 const char *buf, size_t count)
421{
422 struct cosm_device *cdev = dev_get_drvdata(dev);
423 int ret;
424 unsigned long addr;
425
426 if (!cdev)
427 return -EINVAL;
428
429 ret = kstrtoul(buf, 16, &addr);
430 if (ret)
431 goto exit;
432
433 cdev->log_buf_len = (int *)addr;
434 ret = count;
435exit:
436 return ret;
437}
438static DEVICE_ATTR_RW(log_buf_len);
439
440static struct attribute *cosm_default_attrs[] = {
441 &dev_attr_family.attr,
442 &dev_attr_stepping.attr,
443 &dev_attr_state.attr,
444 &dev_attr_shutdown_status.attr,
445 &dev_attr_heartbeat_enable.attr,
446 &dev_attr_cmdline.attr,
447 &dev_attr_firmware.attr,
448 &dev_attr_ramdisk.attr,
449 &dev_attr_bootmode.attr,
450 &dev_attr_log_buf_addr.attr,
451 &dev_attr_log_buf_len.attr,
452
453 NULL
454};
455
456ATTRIBUTE_GROUPS(cosm_default);
457
458void cosm_sysfs_init(struct cosm_device *cdev)
459{
460 cdev->attr_group = cosm_default_groups;
461}