Merge tag 'irqchip-fixes-5.10-1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / remoteproc / remoteproc_sysfs.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Remote Processor Framework
4  */
5
6 #include <linux/remoteproc.h>
7 #include <linux/slab.h>
8
9 #include "remoteproc_internal.h"
10
11 #define to_rproc(d) container_of(d, struct rproc, dev)
12
13 /* Expose the loaded / running firmware name via sysfs */
14 static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
15                           char *buf)
16 {
17         struct rproc *rproc = to_rproc(dev);
18         const char *firmware = rproc->firmware;
19
20         /*
21          * If the remote processor has been started by an external
22          * entity we have no idea of what image it is running.  As such
23          * simply display a generic string rather then rproc->firmware.
24          *
25          * Here we rely on the autonomous flag because a remote processor
26          * may have been attached to and currently in a running state.
27          */
28         if (rproc->autonomous)
29                 firmware = "unknown";
30
31         return sprintf(buf, "%s\n", firmware);
32 }
33
34 /* Change firmware name via sysfs */
35 static ssize_t firmware_store(struct device *dev,
36                               struct device_attribute *attr,
37                               const char *buf, size_t count)
38 {
39         struct rproc *rproc = to_rproc(dev);
40         char *p;
41         int err, len = count;
42
43         err = mutex_lock_interruptible(&rproc->lock);
44         if (err) {
45                 dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err);
46                 return -EINVAL;
47         }
48
49         if (rproc->state != RPROC_OFFLINE) {
50                 dev_err(dev, "can't change firmware while running\n");
51                 err = -EBUSY;
52                 goto out;
53         }
54
55         len = strcspn(buf, "\n");
56         if (!len) {
57                 dev_err(dev, "can't provide a NULL firmware\n");
58                 err = -EINVAL;
59                 goto out;
60         }
61
62         p = kstrndup(buf, len, GFP_KERNEL);
63         if (!p) {
64                 err = -ENOMEM;
65                 goto out;
66         }
67
68         kfree(rproc->firmware);
69         rproc->firmware = p;
70 out:
71         mutex_unlock(&rproc->lock);
72
73         return err ? err : count;
74 }
75 static DEVICE_ATTR_RW(firmware);
76
77 /*
78  * A state-to-string lookup table, for exposing a human readable state
79  * via sysfs. Always keep in sync with enum rproc_state
80  */
81 static const char * const rproc_state_string[] = {
82         [RPROC_OFFLINE]         = "offline",
83         [RPROC_SUSPENDED]       = "suspended",
84         [RPROC_RUNNING]         = "running",
85         [RPROC_CRASHED]         = "crashed",
86         [RPROC_DELETED]         = "deleted",
87         [RPROC_DETACHED]        = "detached",
88         [RPROC_LAST]            = "invalid",
89 };
90
91 /* Expose the state of the remote processor via sysfs */
92 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
93                           char *buf)
94 {
95         struct rproc *rproc = to_rproc(dev);
96         unsigned int state;
97
98         state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
99         return sprintf(buf, "%s\n", rproc_state_string[state]);
100 }
101
102 /* Change remote processor state via sysfs */
103 static ssize_t state_store(struct device *dev,
104                               struct device_attribute *attr,
105                               const char *buf, size_t count)
106 {
107         struct rproc *rproc = to_rproc(dev);
108         int ret = 0;
109
110         if (sysfs_streq(buf, "start")) {
111                 if (rproc->state == RPROC_RUNNING)
112                         return -EBUSY;
113
114                 ret = rproc_boot(rproc);
115                 if (ret)
116                         dev_err(&rproc->dev, "Boot failed: %d\n", ret);
117         } else if (sysfs_streq(buf, "stop")) {
118                 if (rproc->state != RPROC_RUNNING)
119                         return -EINVAL;
120
121                 rproc_shutdown(rproc);
122         } else {
123                 dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
124                 ret = -EINVAL;
125         }
126         return ret ? ret : count;
127 }
128 static DEVICE_ATTR_RW(state);
129
130 /* Expose the name of the remote processor via sysfs */
131 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
132                          char *buf)
133 {
134         struct rproc *rproc = to_rproc(dev);
135
136         return sprintf(buf, "%s\n", rproc->name);
137 }
138 static DEVICE_ATTR_RO(name);
139
140 static struct attribute *rproc_attrs[] = {
141         &dev_attr_firmware.attr,
142         &dev_attr_state.attr,
143         &dev_attr_name.attr,
144         NULL
145 };
146
147 static const struct attribute_group rproc_devgroup = {
148         .attrs = rproc_attrs
149 };
150
151 static const struct attribute_group *rproc_devgroups[] = {
152         &rproc_devgroup,
153         NULL
154 };
155
156 struct class rproc_class = {
157         .name           = "remoteproc",
158         .dev_groups     = rproc_devgroups,
159 };
160
161 int __init rproc_init_sysfs(void)
162 {
163         /* create remoteproc device class for sysfs */
164         int err = class_register(&rproc_class);
165
166         if (err)
167                 pr_err("remoteproc: unable to register class\n");
168         return err;
169 }
170
171 void __exit rproc_exit_sysfs(void)
172 {
173         class_unregister(&rproc_class);
174 }