xref: /openbmc/linux/drivers/remoteproc/remoteproc_sysfs.c (revision f75c6043a38b89d01dd9ccb0d9cd81c291e04b18)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22aefbef0SMatt Redfearn /*
32aefbef0SMatt Redfearn  * Remote Processor Framework
42aefbef0SMatt Redfearn  */
52aefbef0SMatt Redfearn 
62aefbef0SMatt Redfearn #include <linux/remoteproc.h>
7bf89a7c0SMichael S. Tsirkin #include <linux/slab.h>
82aefbef0SMatt Redfearn 
92aefbef0SMatt Redfearn #include "remoteproc_internal.h"
102aefbef0SMatt Redfearn 
112aefbef0SMatt Redfearn #define to_rproc(d) container_of(d, struct rproc, dev)
122aefbef0SMatt Redfearn 
13*f75c6043SRishabh Bhatnagar /*
14*f75c6043SRishabh Bhatnagar  * A coredump-configuration-to-string lookup table, for exposing a
15*f75c6043SRishabh Bhatnagar  * human readable configuration via sysfs. Always keep in sync with
16*f75c6043SRishabh Bhatnagar  * enum rproc_coredump_mechanism
17*f75c6043SRishabh Bhatnagar  */
18*f75c6043SRishabh Bhatnagar static const char * const rproc_coredump_str[] = {
19*f75c6043SRishabh Bhatnagar 	[RPROC_COREDUMP_DISABLED]	= "disabled",
20*f75c6043SRishabh Bhatnagar 	[RPROC_COREDUMP_ENABLED]	= "enabled",
21*f75c6043SRishabh Bhatnagar 	[RPROC_COREDUMP_INLINE]		= "inline",
22*f75c6043SRishabh Bhatnagar };
23*f75c6043SRishabh Bhatnagar 
24*f75c6043SRishabh Bhatnagar /* Expose the current coredump configuration via debugfs */
25*f75c6043SRishabh Bhatnagar static ssize_t coredump_show(struct device *dev,
26*f75c6043SRishabh Bhatnagar 			     struct device_attribute *attr, char *buf)
27*f75c6043SRishabh Bhatnagar {
28*f75c6043SRishabh Bhatnagar 	struct rproc *rproc = to_rproc(dev);
29*f75c6043SRishabh Bhatnagar 
30*f75c6043SRishabh Bhatnagar 	return sprintf(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]);
31*f75c6043SRishabh Bhatnagar }
32*f75c6043SRishabh Bhatnagar 
33*f75c6043SRishabh Bhatnagar /*
34*f75c6043SRishabh Bhatnagar  * By writing to the 'coredump' sysfs entry, we control the behavior of the
35*f75c6043SRishabh Bhatnagar  * coredump mechanism dynamically. The default value of this entry is "default".
36*f75c6043SRishabh Bhatnagar  *
37*f75c6043SRishabh Bhatnagar  * The 'coredump' sysfs entry supports these commands:
38*f75c6043SRishabh Bhatnagar  *
39*f75c6043SRishabh Bhatnagar  * disabled:	This is the default coredump mechanism. Recovery will proceed
40*f75c6043SRishabh Bhatnagar  *		without collecting any dump.
41*f75c6043SRishabh Bhatnagar  *
42*f75c6043SRishabh Bhatnagar  * default:	When the remoteproc crashes the entire coredump will be
43*f75c6043SRishabh Bhatnagar  *		copied to a separate buffer and exposed to userspace.
44*f75c6043SRishabh Bhatnagar  *
45*f75c6043SRishabh Bhatnagar  * inline:	The coredump will not be copied to a separate buffer and the
46*f75c6043SRishabh Bhatnagar  *		recovery process will have to wait until data is read by
47*f75c6043SRishabh Bhatnagar  *		userspace. But this avoid usage of extra memory.
48*f75c6043SRishabh Bhatnagar  */
49*f75c6043SRishabh Bhatnagar static ssize_t coredump_store(struct device *dev,
50*f75c6043SRishabh Bhatnagar 			      struct device_attribute *attr,
51*f75c6043SRishabh Bhatnagar 			      const char *buf, size_t count)
52*f75c6043SRishabh Bhatnagar {
53*f75c6043SRishabh Bhatnagar 	struct rproc *rproc = to_rproc(dev);
54*f75c6043SRishabh Bhatnagar 
55*f75c6043SRishabh Bhatnagar 	if (rproc->state == RPROC_CRASHED) {
56*f75c6043SRishabh Bhatnagar 		dev_err(&rproc->dev, "can't change coredump configuration\n");
57*f75c6043SRishabh Bhatnagar 		return -EBUSY;
58*f75c6043SRishabh Bhatnagar 	}
59*f75c6043SRishabh Bhatnagar 
60*f75c6043SRishabh Bhatnagar 	if (sysfs_streq(buf, "disabled")) {
61*f75c6043SRishabh Bhatnagar 		rproc->dump_conf = RPROC_COREDUMP_DISABLED;
62*f75c6043SRishabh Bhatnagar 	} else if (sysfs_streq(buf, "enabled")) {
63*f75c6043SRishabh Bhatnagar 		rproc->dump_conf = RPROC_COREDUMP_ENABLED;
64*f75c6043SRishabh Bhatnagar 	} else if (sysfs_streq(buf, "inline")) {
65*f75c6043SRishabh Bhatnagar 		rproc->dump_conf = RPROC_COREDUMP_INLINE;
66*f75c6043SRishabh Bhatnagar 	} else {
67*f75c6043SRishabh Bhatnagar 		dev_err(&rproc->dev, "Invalid coredump configuration\n");
68*f75c6043SRishabh Bhatnagar 		return -EINVAL;
69*f75c6043SRishabh Bhatnagar 	}
70*f75c6043SRishabh Bhatnagar 
71*f75c6043SRishabh Bhatnagar 	return count;
72*f75c6043SRishabh Bhatnagar }
73*f75c6043SRishabh Bhatnagar static DEVICE_ATTR_RW(coredump);
74*f75c6043SRishabh Bhatnagar 
752aefbef0SMatt Redfearn /* Expose the loaded / running firmware name via sysfs */
762aefbef0SMatt Redfearn static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
772aefbef0SMatt Redfearn 			  char *buf)
782aefbef0SMatt Redfearn {
792aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
804a4dca19SMathieu Poirier 	const char *firmware = rproc->firmware;
812aefbef0SMatt Redfearn 
824a4dca19SMathieu Poirier 	/*
834a4dca19SMathieu Poirier 	 * If the remote processor has been started by an external
844a4dca19SMathieu Poirier 	 * entity we have no idea of what image it is running.  As such
854a4dca19SMathieu Poirier 	 * simply display a generic string rather then rproc->firmware.
864a4dca19SMathieu Poirier 	 *
874a4dca19SMathieu Poirier 	 * Here we rely on the autonomous flag because a remote processor
884a4dca19SMathieu Poirier 	 * may have been attached to and currently in a running state.
894a4dca19SMathieu Poirier 	 */
904a4dca19SMathieu Poirier 	if (rproc->autonomous)
914a4dca19SMathieu Poirier 		firmware = "unknown";
924a4dca19SMathieu Poirier 
934a4dca19SMathieu Poirier 	return sprintf(buf, "%s\n", firmware);
942aefbef0SMatt Redfearn }
952aefbef0SMatt Redfearn 
962aefbef0SMatt Redfearn /* Change firmware name via sysfs */
972aefbef0SMatt Redfearn static ssize_t firmware_store(struct device *dev,
982aefbef0SMatt Redfearn 			      struct device_attribute *attr,
992aefbef0SMatt Redfearn 			      const char *buf, size_t count)
1002aefbef0SMatt Redfearn {
1012aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
1022aefbef0SMatt Redfearn 	char *p;
1032aefbef0SMatt Redfearn 	int err, len = count;
1042aefbef0SMatt Redfearn 
1052aefbef0SMatt Redfearn 	err = mutex_lock_interruptible(&rproc->lock);
1062aefbef0SMatt Redfearn 	if (err) {
1072aefbef0SMatt Redfearn 		dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err);
1082aefbef0SMatt Redfearn 		return -EINVAL;
1092aefbef0SMatt Redfearn 	}
1102aefbef0SMatt Redfearn 
1112aefbef0SMatt Redfearn 	if (rproc->state != RPROC_OFFLINE) {
1122aefbef0SMatt Redfearn 		dev_err(dev, "can't change firmware while running\n");
1132aefbef0SMatt Redfearn 		err = -EBUSY;
1142aefbef0SMatt Redfearn 		goto out;
1152aefbef0SMatt Redfearn 	}
1162aefbef0SMatt Redfearn 
1172aefbef0SMatt Redfearn 	len = strcspn(buf, "\n");
118faeadbb6SSuman Anna 	if (!len) {
119faeadbb6SSuman Anna 		dev_err(dev, "can't provide a NULL firmware\n");
120faeadbb6SSuman Anna 		err = -EINVAL;
121faeadbb6SSuman Anna 		goto out;
122faeadbb6SSuman Anna 	}
1232aefbef0SMatt Redfearn 
1242aefbef0SMatt Redfearn 	p = kstrndup(buf, len, GFP_KERNEL);
1252aefbef0SMatt Redfearn 	if (!p) {
1262aefbef0SMatt Redfearn 		err = -ENOMEM;
1272aefbef0SMatt Redfearn 		goto out;
1282aefbef0SMatt Redfearn 	}
1292aefbef0SMatt Redfearn 
1302aefbef0SMatt Redfearn 	kfree(rproc->firmware);
1312aefbef0SMatt Redfearn 	rproc->firmware = p;
1322aefbef0SMatt Redfearn out:
1332aefbef0SMatt Redfearn 	mutex_unlock(&rproc->lock);
1342aefbef0SMatt Redfearn 
1352aefbef0SMatt Redfearn 	return err ? err : count;
1362aefbef0SMatt Redfearn }
1372aefbef0SMatt Redfearn static DEVICE_ATTR_RW(firmware);
1382aefbef0SMatt Redfearn 
1392aefbef0SMatt Redfearn /*
1402aefbef0SMatt Redfearn  * A state-to-string lookup table, for exposing a human readable state
1412aefbef0SMatt Redfearn  * via sysfs. Always keep in sync with enum rproc_state
1422aefbef0SMatt Redfearn  */
1432aefbef0SMatt Redfearn static const char * const rproc_state_string[] = {
1442aefbef0SMatt Redfearn 	[RPROC_OFFLINE]		= "offline",
1452aefbef0SMatt Redfearn 	[RPROC_SUSPENDED]	= "suspended",
1462aefbef0SMatt Redfearn 	[RPROC_RUNNING]		= "running",
1472aefbef0SMatt Redfearn 	[RPROC_CRASHED]		= "crashed",
148608d7921SSarangdhar Joshi 	[RPROC_DELETED]		= "deleted",
149e2e5c55eSMathieu Poirier 	[RPROC_DETACHED]	= "detached",
1502aefbef0SMatt Redfearn 	[RPROC_LAST]		= "invalid",
1512aefbef0SMatt Redfearn };
1522aefbef0SMatt Redfearn 
1532aefbef0SMatt Redfearn /* Expose the state of the remote processor via sysfs */
1542aefbef0SMatt Redfearn static ssize_t state_show(struct device *dev, struct device_attribute *attr,
1552aefbef0SMatt Redfearn 			  char *buf)
1562aefbef0SMatt Redfearn {
1572aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
1582aefbef0SMatt Redfearn 	unsigned int state;
1592aefbef0SMatt Redfearn 
1602aefbef0SMatt Redfearn 	state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
1612aefbef0SMatt Redfearn 	return sprintf(buf, "%s\n", rproc_state_string[state]);
1622aefbef0SMatt Redfearn }
1632aefbef0SMatt Redfearn 
1642aefbef0SMatt Redfearn /* Change remote processor state via sysfs */
1652aefbef0SMatt Redfearn static ssize_t state_store(struct device *dev,
1662aefbef0SMatt Redfearn 			      struct device_attribute *attr,
1672aefbef0SMatt Redfearn 			      const char *buf, size_t count)
1682aefbef0SMatt Redfearn {
1692aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
1702aefbef0SMatt Redfearn 	int ret = 0;
1712aefbef0SMatt Redfearn 
1722aefbef0SMatt Redfearn 	if (sysfs_streq(buf, "start")) {
1732aefbef0SMatt Redfearn 		if (rproc->state == RPROC_RUNNING)
1742aefbef0SMatt Redfearn 			return -EBUSY;
1752aefbef0SMatt Redfearn 
1762aefbef0SMatt Redfearn 		ret = rproc_boot(rproc);
1772aefbef0SMatt Redfearn 		if (ret)
1782aefbef0SMatt Redfearn 			dev_err(&rproc->dev, "Boot failed: %d\n", ret);
1792aefbef0SMatt Redfearn 	} else if (sysfs_streq(buf, "stop")) {
1802aefbef0SMatt Redfearn 		if (rproc->state != RPROC_RUNNING)
1812aefbef0SMatt Redfearn 			return -EINVAL;
1822aefbef0SMatt Redfearn 
1832aefbef0SMatt Redfearn 		rproc_shutdown(rproc);
1842aefbef0SMatt Redfearn 	} else {
1852aefbef0SMatt Redfearn 		dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
1862aefbef0SMatt Redfearn 		ret = -EINVAL;
1872aefbef0SMatt Redfearn 	}
1882aefbef0SMatt Redfearn 	return ret ? ret : count;
1892aefbef0SMatt Redfearn }
1902aefbef0SMatt Redfearn static DEVICE_ATTR_RW(state);
1912aefbef0SMatt Redfearn 
1926ed756aaSSuman Anna /* Expose the name of the remote processor via sysfs */
1936ed756aaSSuman Anna static ssize_t name_show(struct device *dev, struct device_attribute *attr,
1946ed756aaSSuman Anna 			 char *buf)
1956ed756aaSSuman Anna {
1966ed756aaSSuman Anna 	struct rproc *rproc = to_rproc(dev);
1976ed756aaSSuman Anna 
1986ed756aaSSuman Anna 	return sprintf(buf, "%s\n", rproc->name);
1996ed756aaSSuman Anna }
2006ed756aaSSuman Anna static DEVICE_ATTR_RO(name);
2016ed756aaSSuman Anna 
2022aefbef0SMatt Redfearn static struct attribute *rproc_attrs[] = {
203*f75c6043SRishabh Bhatnagar 	&dev_attr_coredump.attr,
2042aefbef0SMatt Redfearn 	&dev_attr_firmware.attr,
2052aefbef0SMatt Redfearn 	&dev_attr_state.attr,
2066ed756aaSSuman Anna 	&dev_attr_name.attr,
2072aefbef0SMatt Redfearn 	NULL
2082aefbef0SMatt Redfearn };
2092aefbef0SMatt Redfearn 
2102aefbef0SMatt Redfearn static const struct attribute_group rproc_devgroup = {
2112aefbef0SMatt Redfearn 	.attrs = rproc_attrs
2122aefbef0SMatt Redfearn };
2132aefbef0SMatt Redfearn 
2142aefbef0SMatt Redfearn static const struct attribute_group *rproc_devgroups[] = {
2152aefbef0SMatt Redfearn 	&rproc_devgroup,
2162aefbef0SMatt Redfearn 	NULL
2172aefbef0SMatt Redfearn };
2182aefbef0SMatt Redfearn 
2192aefbef0SMatt Redfearn struct class rproc_class = {
2202aefbef0SMatt Redfearn 	.name		= "remoteproc",
2212aefbef0SMatt Redfearn 	.dev_groups	= rproc_devgroups,
2222aefbef0SMatt Redfearn };
2232aefbef0SMatt Redfearn 
2242aefbef0SMatt Redfearn int __init rproc_init_sysfs(void)
2252aefbef0SMatt Redfearn {
2262aefbef0SMatt Redfearn 	/* create remoteproc device class for sysfs */
2272aefbef0SMatt Redfearn 	int err = class_register(&rproc_class);
2282aefbef0SMatt Redfearn 
2292aefbef0SMatt Redfearn 	if (err)
2302aefbef0SMatt Redfearn 		pr_err("remoteproc: unable to register class\n");
2312aefbef0SMatt Redfearn 	return err;
2322aefbef0SMatt Redfearn }
2332aefbef0SMatt Redfearn 
2342aefbef0SMatt Redfearn void __exit rproc_exit_sysfs(void)
2352aefbef0SMatt Redfearn {
2362aefbef0SMatt Redfearn 	class_unregister(&rproc_class);
2372aefbef0SMatt Redfearn }
238