Merge branch 'work.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-microblaze.git] / drivers / remoteproc / remoteproc_sysfs.c
1 /*
2  * Remote Processor Framework
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * version 2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13
14 #include <linux/remoteproc.h>
15
16 #include "remoteproc_internal.h"
17
18 #define to_rproc(d) container_of(d, struct rproc, dev)
19
20 /* Expose the loaded / running firmware name via sysfs */
21 static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
22                           char *buf)
23 {
24         struct rproc *rproc = to_rproc(dev);
25
26         return sprintf(buf, "%s\n", rproc->firmware);
27 }
28
29 /* Change firmware name via sysfs */
30 static ssize_t firmware_store(struct device *dev,
31                               struct device_attribute *attr,
32                               const char *buf, size_t count)
33 {
34         struct rproc *rproc = to_rproc(dev);
35         char *p;
36         int err, len = count;
37
38         err = mutex_lock_interruptible(&rproc->lock);
39         if (err) {
40                 dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err);
41                 return -EINVAL;
42         }
43
44         if (rproc->state != RPROC_OFFLINE) {
45                 dev_err(dev, "can't change firmware while running\n");
46                 err = -EBUSY;
47                 goto out;
48         }
49
50         len = strcspn(buf, "\n");
51         if (!len) {
52                 dev_err(dev, "can't provide a NULL firmware\n");
53                 err = -EINVAL;
54                 goto out;
55         }
56
57         p = kstrndup(buf, len, GFP_KERNEL);
58         if (!p) {
59                 err = -ENOMEM;
60                 goto out;
61         }
62
63         kfree(rproc->firmware);
64         rproc->firmware = p;
65 out:
66         mutex_unlock(&rproc->lock);
67
68         return err ? err : count;
69 }
70 static DEVICE_ATTR_RW(firmware);
71
72 /*
73  * A state-to-string lookup table, for exposing a human readable state
74  * via sysfs. Always keep in sync with enum rproc_state
75  */
76 static const char * const rproc_state_string[] = {
77         [RPROC_OFFLINE]         = "offline",
78         [RPROC_SUSPENDED]       = "suspended",
79         [RPROC_RUNNING]         = "running",
80         [RPROC_CRASHED]         = "crashed",
81         [RPROC_DELETED]         = "deleted",
82         [RPROC_LAST]            = "invalid",
83 };
84
85 /* Expose the state of the remote processor via sysfs */
86 static ssize_t state_show(struct device *dev, struct device_attribute *attr,
87                           char *buf)
88 {
89         struct rproc *rproc = to_rproc(dev);
90         unsigned int state;
91
92         state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
93         return sprintf(buf, "%s\n", rproc_state_string[state]);
94 }
95
96 /* Change remote processor state via sysfs */
97 static ssize_t state_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         int ret = 0;
103
104         if (sysfs_streq(buf, "start")) {
105                 if (rproc->state == RPROC_RUNNING)
106                         return -EBUSY;
107
108                 ret = rproc_boot(rproc);
109                 if (ret)
110                         dev_err(&rproc->dev, "Boot failed: %d\n", ret);
111         } else if (sysfs_streq(buf, "stop")) {
112                 if (rproc->state != RPROC_RUNNING)
113                         return -EINVAL;
114
115                 rproc_shutdown(rproc);
116         } else {
117                 dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
118                 ret = -EINVAL;
119         }
120         return ret ? ret : count;
121 }
122 static DEVICE_ATTR_RW(state);
123
124 static struct attribute *rproc_attrs[] = {
125         &dev_attr_firmware.attr,
126         &dev_attr_state.attr,
127         NULL
128 };
129
130 static const struct attribute_group rproc_devgroup = {
131         .attrs = rproc_attrs
132 };
133
134 static const struct attribute_group *rproc_devgroups[] = {
135         &rproc_devgroup,
136         NULL
137 };
138
139 struct class rproc_class = {
140         .name           = "remoteproc",
141         .dev_groups     = rproc_devgroups,
142 };
143
144 int __init rproc_init_sysfs(void)
145 {
146         /* create remoteproc device class for sysfs */
147         int err = class_register(&rproc_class);
148
149         if (err)
150                 pr_err("remoteproc: unable to register class\n");
151         return err;
152 }
153
154 void __exit rproc_exit_sysfs(void)
155 {
156         class_unregister(&rproc_class);
157 }