remoteproc: Add coredump as part of sysfs interface
[linux-block.git] / drivers / remoteproc / remoteproc_sysfs.c
CommitLineData
1802d0be 1// SPDX-License-Identifier: GPL-2.0-only
2aefbef0
MR
2/*
3 * Remote Processor Framework
2aefbef0
MR
4 */
5
6#include <linux/remoteproc.h>
bf89a7c0 7#include <linux/slab.h>
2aefbef0
MR
8
9#include "remoteproc_internal.h"
10
11#define to_rproc(d) container_of(d, struct rproc, dev)
12
f75c6043
RB
13/*
14 * A coredump-configuration-to-string lookup table, for exposing a
15 * human readable configuration via sysfs. Always keep in sync with
16 * enum rproc_coredump_mechanism
17 */
18static const char * const rproc_coredump_str[] = {
19 [RPROC_COREDUMP_DISABLED] = "disabled",
20 [RPROC_COREDUMP_ENABLED] = "enabled",
21 [RPROC_COREDUMP_INLINE] = "inline",
22};
23
24/* Expose the current coredump configuration via debugfs */
25static ssize_t coredump_show(struct device *dev,
26 struct device_attribute *attr, char *buf)
27{
28 struct rproc *rproc = to_rproc(dev);
29
30 return sprintf(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]);
31}
32
33/*
34 * By writing to the 'coredump' sysfs entry, we control the behavior of the
35 * coredump mechanism dynamically. The default value of this entry is "default".
36 *
37 * The 'coredump' sysfs entry supports these commands:
38 *
39 * disabled: This is the default coredump mechanism. Recovery will proceed
40 * without collecting any dump.
41 *
42 * default: When the remoteproc crashes the entire coredump will be
43 * copied to a separate buffer and exposed to userspace.
44 *
45 * inline: The coredump will not be copied to a separate buffer and the
46 * recovery process will have to wait until data is read by
47 * userspace. But this avoid usage of extra memory.
48 */
49static ssize_t coredump_store(struct device *dev,
50 struct device_attribute *attr,
51 const char *buf, size_t count)
52{
53 struct rproc *rproc = to_rproc(dev);
54
55 if (rproc->state == RPROC_CRASHED) {
56 dev_err(&rproc->dev, "can't change coredump configuration\n");
57 return -EBUSY;
58 }
59
60 if (sysfs_streq(buf, "disabled")) {
61 rproc->dump_conf = RPROC_COREDUMP_DISABLED;
62 } else if (sysfs_streq(buf, "enabled")) {
63 rproc->dump_conf = RPROC_COREDUMP_ENABLED;
64 } else if (sysfs_streq(buf, "inline")) {
65 rproc->dump_conf = RPROC_COREDUMP_INLINE;
66 } else {
67 dev_err(&rproc->dev, "Invalid coredump configuration\n");
68 return -EINVAL;
69 }
70
71 return count;
72}
73static DEVICE_ATTR_RW(coredump);
74
2aefbef0
MR
75/* Expose the loaded / running firmware name via sysfs */
76static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
77 char *buf)
78{
79 struct rproc *rproc = to_rproc(dev);
4a4dca19 80 const char *firmware = rproc->firmware;
2aefbef0 81
4a4dca19
MP
82 /*
83 * If the remote processor has been started by an external
84 * entity we have no idea of what image it is running. As such
85 * simply display a generic string rather then rproc->firmware.
86 *
87 * Here we rely on the autonomous flag because a remote processor
88 * may have been attached to and currently in a running state.
89 */
90 if (rproc->autonomous)
91 firmware = "unknown";
92
93 return sprintf(buf, "%s\n", firmware);
2aefbef0
MR
94}
95
96/* Change firmware name via sysfs */
97static ssize_t firmware_store(struct device *dev,
98 struct device_attribute *attr,
99 const char *buf, size_t count)
100{
101 struct rproc *rproc = to_rproc(dev);
102 char *p;
103 int err, len = count;
104
105 err = mutex_lock_interruptible(&rproc->lock);
106 if (err) {
107 dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err);
108 return -EINVAL;
109 }
110
111 if (rproc->state != RPROC_OFFLINE) {
112 dev_err(dev, "can't change firmware while running\n");
113 err = -EBUSY;
114 goto out;
115 }
116
117 len = strcspn(buf, "\n");
faeadbb6
SA
118 if (!len) {
119 dev_err(dev, "can't provide a NULL firmware\n");
120 err = -EINVAL;
121 goto out;
122 }
2aefbef0
MR
123
124 p = kstrndup(buf, len, GFP_KERNEL);
125 if (!p) {
126 err = -ENOMEM;
127 goto out;
128 }
129
130 kfree(rproc->firmware);
131 rproc->firmware = p;
132out:
133 mutex_unlock(&rproc->lock);
134
135 return err ? err : count;
136}
137static DEVICE_ATTR_RW(firmware);
138
139/*
140 * A state-to-string lookup table, for exposing a human readable state
141 * via sysfs. Always keep in sync with enum rproc_state
142 */
143static const char * const rproc_state_string[] = {
144 [RPROC_OFFLINE] = "offline",
145 [RPROC_SUSPENDED] = "suspended",
146 [RPROC_RUNNING] = "running",
147 [RPROC_CRASHED] = "crashed",
608d7921 148 [RPROC_DELETED] = "deleted",
e2e5c55e 149 [RPROC_DETACHED] = "detached",
2aefbef0
MR
150 [RPROC_LAST] = "invalid",
151};
152
153/* Expose the state of the remote processor via sysfs */
154static ssize_t state_show(struct device *dev, struct device_attribute *attr,
155 char *buf)
156{
157 struct rproc *rproc = to_rproc(dev);
158 unsigned int state;
159
160 state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
161 return sprintf(buf, "%s\n", rproc_state_string[state]);
162}
163
164/* Change remote processor state via sysfs */
165static ssize_t state_store(struct device *dev,
166 struct device_attribute *attr,
167 const char *buf, size_t count)
168{
169 struct rproc *rproc = to_rproc(dev);
170 int ret = 0;
171
172 if (sysfs_streq(buf, "start")) {
173 if (rproc->state == RPROC_RUNNING)
174 return -EBUSY;
175
176 ret = rproc_boot(rproc);
177 if (ret)
178 dev_err(&rproc->dev, "Boot failed: %d\n", ret);
179 } else if (sysfs_streq(buf, "stop")) {
180 if (rproc->state != RPROC_RUNNING)
181 return -EINVAL;
182
183 rproc_shutdown(rproc);
184 } else {
185 dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
186 ret = -EINVAL;
187 }
188 return ret ? ret : count;
189}
190static DEVICE_ATTR_RW(state);
191
6ed756aa
SA
192/* Expose the name of the remote processor via sysfs */
193static ssize_t name_show(struct device *dev, struct device_attribute *attr,
194 char *buf)
195{
196 struct rproc *rproc = to_rproc(dev);
197
198 return sprintf(buf, "%s\n", rproc->name);
199}
200static DEVICE_ATTR_RO(name);
201
2aefbef0 202static struct attribute *rproc_attrs[] = {
f75c6043 203 &dev_attr_coredump.attr,
2aefbef0
MR
204 &dev_attr_firmware.attr,
205 &dev_attr_state.attr,
6ed756aa 206 &dev_attr_name.attr,
2aefbef0
MR
207 NULL
208};
209
210static const struct attribute_group rproc_devgroup = {
211 .attrs = rproc_attrs
212};
213
214static const struct attribute_group *rproc_devgroups[] = {
215 &rproc_devgroup,
216 NULL
217};
218
219struct class rproc_class = {
220 .name = "remoteproc",
221 .dev_groups = rproc_devgroups,
222};
223
224int __init rproc_init_sysfs(void)
225{
226 /* create remoteproc device class for sysfs */
227 int err = class_register(&rproc_class);
228
229 if (err)
230 pr_err("remoteproc: unable to register class\n");
231 return err;
232}
233
234void __exit rproc_exit_sysfs(void)
235{
236 class_unregister(&rproc_class);
237}