xref: /openbmc/linux/drivers/remoteproc/remoteproc_sysfs.c (revision 145e1da374bcba14c9ca069646f68b76c422612a)
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 
13526b9e0cSRishabh Bhatnagar static ssize_t recovery_show(struct device *dev,
14526b9e0cSRishabh Bhatnagar 			     struct device_attribute *attr, char *buf)
15526b9e0cSRishabh Bhatnagar {
16526b9e0cSRishabh Bhatnagar 	struct rproc *rproc = to_rproc(dev);
17526b9e0cSRishabh Bhatnagar 
18*145e1da3SRaghavendra Rao Ananta 	return sysfs_emit(buf, "%s", rproc->recovery_disabled ? "disabled\n" : "enabled\n");
19526b9e0cSRishabh Bhatnagar }
20526b9e0cSRishabh Bhatnagar 
21526b9e0cSRishabh Bhatnagar /*
22526b9e0cSRishabh Bhatnagar  * By writing to the 'recovery' sysfs entry, we control the behavior of the
23526b9e0cSRishabh Bhatnagar  * recovery mechanism dynamically. The default value of this entry is "enabled".
24526b9e0cSRishabh Bhatnagar  *
25526b9e0cSRishabh Bhatnagar  * The 'recovery' sysfs entry supports these commands:
26526b9e0cSRishabh Bhatnagar  *
27526b9e0cSRishabh Bhatnagar  * enabled:	When enabled, the remote processor will be automatically
28526b9e0cSRishabh Bhatnagar  *		recovered whenever it crashes. Moreover, if the remote
29526b9e0cSRishabh Bhatnagar  *		processor crashes while recovery is disabled, it will
30526b9e0cSRishabh Bhatnagar  *		be automatically recovered too as soon as recovery is enabled.
31526b9e0cSRishabh Bhatnagar  *
32526b9e0cSRishabh Bhatnagar  * disabled:	When disabled, a remote processor will remain in a crashed
33526b9e0cSRishabh Bhatnagar  *		state if it crashes. This is useful for debugging purposes;
34526b9e0cSRishabh Bhatnagar  *		without it, debugging a crash is substantially harder.
35526b9e0cSRishabh Bhatnagar  *
36526b9e0cSRishabh Bhatnagar  * recover:	This function will trigger an immediate recovery if the
37526b9e0cSRishabh Bhatnagar  *		remote processor is in a crashed state, without changing
38526b9e0cSRishabh Bhatnagar  *		or checking the recovery state (enabled/disabled).
39526b9e0cSRishabh Bhatnagar  *		This is useful during debugging sessions, when one expects
40526b9e0cSRishabh Bhatnagar  *		additional crashes to happen after enabling recovery. In this
41526b9e0cSRishabh Bhatnagar  *		case, enabling recovery will make it hard to debug subsequent
42526b9e0cSRishabh Bhatnagar  *		crashes, so it's recommended to keep recovery disabled, and
43526b9e0cSRishabh Bhatnagar  *		instead use the "recover" command as needed.
44526b9e0cSRishabh Bhatnagar  */
45526b9e0cSRishabh Bhatnagar static ssize_t recovery_store(struct device *dev,
46526b9e0cSRishabh Bhatnagar 			      struct device_attribute *attr,
47526b9e0cSRishabh Bhatnagar 			      const char *buf, size_t count)
48526b9e0cSRishabh Bhatnagar {
49526b9e0cSRishabh Bhatnagar 	struct rproc *rproc = to_rproc(dev);
50526b9e0cSRishabh Bhatnagar 
51526b9e0cSRishabh Bhatnagar 	if (sysfs_streq(buf, "enabled")) {
52526b9e0cSRishabh Bhatnagar 		/* change the flag and begin the recovery process if needed */
53526b9e0cSRishabh Bhatnagar 		rproc->recovery_disabled = false;
54526b9e0cSRishabh Bhatnagar 		rproc_trigger_recovery(rproc);
55526b9e0cSRishabh Bhatnagar 	} else if (sysfs_streq(buf, "disabled")) {
56526b9e0cSRishabh Bhatnagar 		rproc->recovery_disabled = true;
57526b9e0cSRishabh Bhatnagar 	} else if (sysfs_streq(buf, "recover")) {
58526b9e0cSRishabh Bhatnagar 		/* begin the recovery process without changing the flag */
59526b9e0cSRishabh Bhatnagar 		rproc_trigger_recovery(rproc);
60526b9e0cSRishabh Bhatnagar 	} else {
61526b9e0cSRishabh Bhatnagar 		return -EINVAL;
62526b9e0cSRishabh Bhatnagar 	}
63526b9e0cSRishabh Bhatnagar 
64526b9e0cSRishabh Bhatnagar 	return count;
65526b9e0cSRishabh Bhatnagar }
66526b9e0cSRishabh Bhatnagar static DEVICE_ATTR_RW(recovery);
67526b9e0cSRishabh Bhatnagar 
68f75c6043SRishabh Bhatnagar /*
69f75c6043SRishabh Bhatnagar  * A coredump-configuration-to-string lookup table, for exposing a
70f75c6043SRishabh Bhatnagar  * human readable configuration via sysfs. Always keep in sync with
71f75c6043SRishabh Bhatnagar  * enum rproc_coredump_mechanism
72f75c6043SRishabh Bhatnagar  */
73f75c6043SRishabh Bhatnagar static const char * const rproc_coredump_str[] = {
74f75c6043SRishabh Bhatnagar 	[RPROC_COREDUMP_DISABLED]	= "disabled",
75f75c6043SRishabh Bhatnagar 	[RPROC_COREDUMP_ENABLED]	= "enabled",
76f75c6043SRishabh Bhatnagar 	[RPROC_COREDUMP_INLINE]		= "inline",
77f75c6043SRishabh Bhatnagar };
78f75c6043SRishabh Bhatnagar 
79f75c6043SRishabh Bhatnagar /* Expose the current coredump configuration via debugfs */
80f75c6043SRishabh Bhatnagar static ssize_t coredump_show(struct device *dev,
81f75c6043SRishabh Bhatnagar 			     struct device_attribute *attr, char *buf)
82f75c6043SRishabh Bhatnagar {
83f75c6043SRishabh Bhatnagar 	struct rproc *rproc = to_rproc(dev);
84f75c6043SRishabh Bhatnagar 
85*145e1da3SRaghavendra Rao Ananta 	return sysfs_emit(buf, "%s\n", rproc_coredump_str[rproc->dump_conf]);
86f75c6043SRishabh Bhatnagar }
87f75c6043SRishabh Bhatnagar 
88f75c6043SRishabh Bhatnagar /*
89f75c6043SRishabh Bhatnagar  * By writing to the 'coredump' sysfs entry, we control the behavior of the
90f75c6043SRishabh Bhatnagar  * coredump mechanism dynamically. The default value of this entry is "default".
91f75c6043SRishabh Bhatnagar  *
92f75c6043SRishabh Bhatnagar  * The 'coredump' sysfs entry supports these commands:
93f75c6043SRishabh Bhatnagar  *
94f75c6043SRishabh Bhatnagar  * disabled:	This is the default coredump mechanism. Recovery will proceed
95f75c6043SRishabh Bhatnagar  *		without collecting any dump.
96f75c6043SRishabh Bhatnagar  *
97f75c6043SRishabh Bhatnagar  * default:	When the remoteproc crashes the entire coredump will be
98f75c6043SRishabh Bhatnagar  *		copied to a separate buffer and exposed to userspace.
99f75c6043SRishabh Bhatnagar  *
100f75c6043SRishabh Bhatnagar  * inline:	The coredump will not be copied to a separate buffer and the
101f75c6043SRishabh Bhatnagar  *		recovery process will have to wait until data is read by
102f75c6043SRishabh Bhatnagar  *		userspace. But this avoid usage of extra memory.
103f75c6043SRishabh Bhatnagar  */
104f75c6043SRishabh Bhatnagar static ssize_t coredump_store(struct device *dev,
105f75c6043SRishabh Bhatnagar 			      struct device_attribute *attr,
106f75c6043SRishabh Bhatnagar 			      const char *buf, size_t count)
107f75c6043SRishabh Bhatnagar {
108f75c6043SRishabh Bhatnagar 	struct rproc *rproc = to_rproc(dev);
109f75c6043SRishabh Bhatnagar 
110f75c6043SRishabh Bhatnagar 	if (rproc->state == RPROC_CRASHED) {
111f75c6043SRishabh Bhatnagar 		dev_err(&rproc->dev, "can't change coredump configuration\n");
112f75c6043SRishabh Bhatnagar 		return -EBUSY;
113f75c6043SRishabh Bhatnagar 	}
114f75c6043SRishabh Bhatnagar 
115f75c6043SRishabh Bhatnagar 	if (sysfs_streq(buf, "disabled")) {
116f75c6043SRishabh Bhatnagar 		rproc->dump_conf = RPROC_COREDUMP_DISABLED;
117f75c6043SRishabh Bhatnagar 	} else if (sysfs_streq(buf, "enabled")) {
118f75c6043SRishabh Bhatnagar 		rproc->dump_conf = RPROC_COREDUMP_ENABLED;
119f75c6043SRishabh Bhatnagar 	} else if (sysfs_streq(buf, "inline")) {
120f75c6043SRishabh Bhatnagar 		rproc->dump_conf = RPROC_COREDUMP_INLINE;
121f75c6043SRishabh Bhatnagar 	} else {
122f75c6043SRishabh Bhatnagar 		dev_err(&rproc->dev, "Invalid coredump configuration\n");
123f75c6043SRishabh Bhatnagar 		return -EINVAL;
124f75c6043SRishabh Bhatnagar 	}
125f75c6043SRishabh Bhatnagar 
126f75c6043SRishabh Bhatnagar 	return count;
127f75c6043SRishabh Bhatnagar }
128f75c6043SRishabh Bhatnagar static DEVICE_ATTR_RW(coredump);
129f75c6043SRishabh Bhatnagar 
1302aefbef0SMatt Redfearn /* Expose the loaded / running firmware name via sysfs */
1312aefbef0SMatt Redfearn static ssize_t firmware_show(struct device *dev, struct device_attribute *attr,
1322aefbef0SMatt Redfearn 			  char *buf)
1332aefbef0SMatt Redfearn {
1342aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
1354a4dca19SMathieu Poirier 	const char *firmware = rproc->firmware;
1362aefbef0SMatt Redfearn 
1374a4dca19SMathieu Poirier 	/*
1384a4dca19SMathieu Poirier 	 * If the remote processor has been started by an external
1394a4dca19SMathieu Poirier 	 * entity we have no idea of what image it is running.  As such
1404a4dca19SMathieu Poirier 	 * simply display a generic string rather then rproc->firmware.
1414a4dca19SMathieu Poirier 	 *
1424a4dca19SMathieu Poirier 	 * Here we rely on the autonomous flag because a remote processor
1434a4dca19SMathieu Poirier 	 * may have been attached to and currently in a running state.
1444a4dca19SMathieu Poirier 	 */
1454a4dca19SMathieu Poirier 	if (rproc->autonomous)
1464a4dca19SMathieu Poirier 		firmware = "unknown";
1474a4dca19SMathieu Poirier 
1484a4dca19SMathieu Poirier 	return sprintf(buf, "%s\n", firmware);
1492aefbef0SMatt Redfearn }
1502aefbef0SMatt Redfearn 
1512aefbef0SMatt Redfearn /* Change firmware name via sysfs */
1522aefbef0SMatt Redfearn static ssize_t firmware_store(struct device *dev,
1532aefbef0SMatt Redfearn 			      struct device_attribute *attr,
1542aefbef0SMatt Redfearn 			      const char *buf, size_t count)
1552aefbef0SMatt Redfearn {
1562aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
1574c1ad562SSuman Anna 	int err;
1582aefbef0SMatt Redfearn 
1594c1ad562SSuman Anna 	err = rproc_set_firmware(rproc, buf);
1602aefbef0SMatt Redfearn 
1612aefbef0SMatt Redfearn 	return err ? err : count;
1622aefbef0SMatt Redfearn }
1632aefbef0SMatt Redfearn static DEVICE_ATTR_RW(firmware);
1642aefbef0SMatt Redfearn 
1652aefbef0SMatt Redfearn /*
1662aefbef0SMatt Redfearn  * A state-to-string lookup table, for exposing a human readable state
1672aefbef0SMatt Redfearn  * via sysfs. Always keep in sync with enum rproc_state
1682aefbef0SMatt Redfearn  */
1692aefbef0SMatt Redfearn static const char * const rproc_state_string[] = {
1702aefbef0SMatt Redfearn 	[RPROC_OFFLINE]		= "offline",
1712aefbef0SMatt Redfearn 	[RPROC_SUSPENDED]	= "suspended",
1722aefbef0SMatt Redfearn 	[RPROC_RUNNING]		= "running",
1732aefbef0SMatt Redfearn 	[RPROC_CRASHED]		= "crashed",
174608d7921SSarangdhar Joshi 	[RPROC_DELETED]		= "deleted",
175e2e5c55eSMathieu Poirier 	[RPROC_DETACHED]	= "detached",
1762aefbef0SMatt Redfearn 	[RPROC_LAST]		= "invalid",
1772aefbef0SMatt Redfearn };
1782aefbef0SMatt Redfearn 
1792aefbef0SMatt Redfearn /* Expose the state of the remote processor via sysfs */
1802aefbef0SMatt Redfearn static ssize_t state_show(struct device *dev, struct device_attribute *attr,
1812aefbef0SMatt Redfearn 			  char *buf)
1822aefbef0SMatt Redfearn {
1832aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
1842aefbef0SMatt Redfearn 	unsigned int state;
1852aefbef0SMatt Redfearn 
1862aefbef0SMatt Redfearn 	state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
1872aefbef0SMatt Redfearn 	return sprintf(buf, "%s\n", rproc_state_string[state]);
1882aefbef0SMatt Redfearn }
1892aefbef0SMatt Redfearn 
1902aefbef0SMatt Redfearn /* Change remote processor state via sysfs */
1912aefbef0SMatt Redfearn static ssize_t state_store(struct device *dev,
1922aefbef0SMatt Redfearn 			      struct device_attribute *attr,
1932aefbef0SMatt Redfearn 			      const char *buf, size_t count)
1942aefbef0SMatt Redfearn {
1952aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
1962aefbef0SMatt Redfearn 	int ret = 0;
1972aefbef0SMatt Redfearn 
1982aefbef0SMatt Redfearn 	if (sysfs_streq(buf, "start")) {
1992aefbef0SMatt Redfearn 		if (rproc->state == RPROC_RUNNING)
2002aefbef0SMatt Redfearn 			return -EBUSY;
2012aefbef0SMatt Redfearn 
2022aefbef0SMatt Redfearn 		ret = rproc_boot(rproc);
2032aefbef0SMatt Redfearn 		if (ret)
2042aefbef0SMatt Redfearn 			dev_err(&rproc->dev, "Boot failed: %d\n", ret);
2052aefbef0SMatt Redfearn 	} else if (sysfs_streq(buf, "stop")) {
2062aefbef0SMatt Redfearn 		if (rproc->state != RPROC_RUNNING)
2072aefbef0SMatt Redfearn 			return -EINVAL;
2082aefbef0SMatt Redfearn 
2092aefbef0SMatt Redfearn 		rproc_shutdown(rproc);
2102aefbef0SMatt Redfearn 	} else {
2112aefbef0SMatt Redfearn 		dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
2122aefbef0SMatt Redfearn 		ret = -EINVAL;
2132aefbef0SMatt Redfearn 	}
2142aefbef0SMatt Redfearn 	return ret ? ret : count;
2152aefbef0SMatt Redfearn }
2162aefbef0SMatt Redfearn static DEVICE_ATTR_RW(state);
2172aefbef0SMatt Redfearn 
2186ed756aaSSuman Anna /* Expose the name of the remote processor via sysfs */
2196ed756aaSSuman Anna static ssize_t name_show(struct device *dev, struct device_attribute *attr,
2206ed756aaSSuman Anna 			 char *buf)
2216ed756aaSSuman Anna {
2226ed756aaSSuman Anna 	struct rproc *rproc = to_rproc(dev);
2236ed756aaSSuman Anna 
2246ed756aaSSuman Anna 	return sprintf(buf, "%s\n", rproc->name);
2256ed756aaSSuman Anna }
2266ed756aaSSuman Anna static DEVICE_ATTR_RO(name);
2276ed756aaSSuman Anna 
2282aefbef0SMatt Redfearn static struct attribute *rproc_attrs[] = {
229f75c6043SRishabh Bhatnagar 	&dev_attr_coredump.attr,
230526b9e0cSRishabh Bhatnagar 	&dev_attr_recovery.attr,
2312aefbef0SMatt Redfearn 	&dev_attr_firmware.attr,
2322aefbef0SMatt Redfearn 	&dev_attr_state.attr,
2336ed756aaSSuman Anna 	&dev_attr_name.attr,
2342aefbef0SMatt Redfearn 	NULL
2352aefbef0SMatt Redfearn };
2362aefbef0SMatt Redfearn 
2372aefbef0SMatt Redfearn static const struct attribute_group rproc_devgroup = {
2382aefbef0SMatt Redfearn 	.attrs = rproc_attrs
2392aefbef0SMatt Redfearn };
2402aefbef0SMatt Redfearn 
2412aefbef0SMatt Redfearn static const struct attribute_group *rproc_devgroups[] = {
2422aefbef0SMatt Redfearn 	&rproc_devgroup,
2432aefbef0SMatt Redfearn 	NULL
2442aefbef0SMatt Redfearn };
2452aefbef0SMatt Redfearn 
2462aefbef0SMatt Redfearn struct class rproc_class = {
2472aefbef0SMatt Redfearn 	.name		= "remoteproc",
2482aefbef0SMatt Redfearn 	.dev_groups	= rproc_devgroups,
2492aefbef0SMatt Redfearn };
2502aefbef0SMatt Redfearn 
2512aefbef0SMatt Redfearn int __init rproc_init_sysfs(void)
2522aefbef0SMatt Redfearn {
2532aefbef0SMatt Redfearn 	/* create remoteproc device class for sysfs */
2542aefbef0SMatt Redfearn 	int err = class_register(&rproc_class);
2552aefbef0SMatt Redfearn 
2562aefbef0SMatt Redfearn 	if (err)
2572aefbef0SMatt Redfearn 		pr_err("remoteproc: unable to register class\n");
2582aefbef0SMatt Redfearn 	return err;
2592aefbef0SMatt Redfearn }
2602aefbef0SMatt Redfearn 
2612aefbef0SMatt Redfearn void __exit rproc_exit_sysfs(void)
2622aefbef0SMatt Redfearn {
2632aefbef0SMatt Redfearn 	class_unregister(&rproc_class);
2642aefbef0SMatt Redfearn }
265