xref: /openbmc/linux/drivers/remoteproc/remoteproc_sysfs.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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 
recovery_show(struct device * dev,struct device_attribute * attr,char * buf)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 
18145e1da3SRaghavendra 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  */
recovery_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)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 */
coredump_show(struct device * dev,struct device_attribute * attr,char * buf)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 
85145e1da3SRaghavendra 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  */
coredump_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)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 */
firmware_show(struct device * dev,struct device_attribute * attr,char * buf)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 	 */
14276f4c875SMathieu Poirier 	if (rproc->state == RPROC_ATTACHED)
1434a4dca19SMathieu Poirier 		firmware = "unknown";
1444a4dca19SMathieu Poirier 
1454a4dca19SMathieu Poirier 	return sprintf(buf, "%s\n", firmware);
1462aefbef0SMatt Redfearn }
1472aefbef0SMatt Redfearn 
1482aefbef0SMatt Redfearn /* Change firmware name via sysfs */
firmware_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1492aefbef0SMatt Redfearn static ssize_t firmware_store(struct device *dev,
1502aefbef0SMatt Redfearn 			      struct device_attribute *attr,
1512aefbef0SMatt Redfearn 			      const char *buf, size_t count)
1522aefbef0SMatt Redfearn {
1532aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
1544c1ad562SSuman Anna 	int err;
1552aefbef0SMatt Redfearn 
1564c1ad562SSuman Anna 	err = rproc_set_firmware(rproc, buf);
1572aefbef0SMatt Redfearn 
1582aefbef0SMatt Redfearn 	return err ? err : count;
1592aefbef0SMatt Redfearn }
1602aefbef0SMatt Redfearn static DEVICE_ATTR_RW(firmware);
1612aefbef0SMatt Redfearn 
1622aefbef0SMatt Redfearn /*
1632aefbef0SMatt Redfearn  * A state-to-string lookup table, for exposing a human readable state
1642aefbef0SMatt Redfearn  * via sysfs. Always keep in sync with enum rproc_state
1652aefbef0SMatt Redfearn  */
1662aefbef0SMatt Redfearn static const char * const rproc_state_string[] = {
1672aefbef0SMatt Redfearn 	[RPROC_OFFLINE]		= "offline",
1682aefbef0SMatt Redfearn 	[RPROC_SUSPENDED]	= "suspended",
1692aefbef0SMatt Redfearn 	[RPROC_RUNNING]		= "running",
1702aefbef0SMatt Redfearn 	[RPROC_CRASHED]		= "crashed",
171608d7921SSarangdhar Joshi 	[RPROC_DELETED]		= "deleted",
1724196d189SMathieu Poirier 	[RPROC_ATTACHED]	= "attached",
173e2e5c55eSMathieu Poirier 	[RPROC_DETACHED]	= "detached",
1742aefbef0SMatt Redfearn 	[RPROC_LAST]		= "invalid",
1752aefbef0SMatt Redfearn };
1762aefbef0SMatt Redfearn 
1772aefbef0SMatt Redfearn /* Expose the state of the remote processor via sysfs */
state_show(struct device * dev,struct device_attribute * attr,char * buf)1782aefbef0SMatt Redfearn static ssize_t state_show(struct device *dev, struct device_attribute *attr,
1792aefbef0SMatt Redfearn 			  char *buf)
1802aefbef0SMatt Redfearn {
1812aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
1822aefbef0SMatt Redfearn 	unsigned int state;
1832aefbef0SMatt Redfearn 
1842aefbef0SMatt Redfearn 	state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
1852aefbef0SMatt Redfearn 	return sprintf(buf, "%s\n", rproc_state_string[state]);
1862aefbef0SMatt Redfearn }
1872aefbef0SMatt Redfearn 
1882aefbef0SMatt Redfearn /* Change remote processor state via sysfs */
state_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1892aefbef0SMatt Redfearn static ssize_t state_store(struct device *dev,
1902aefbef0SMatt Redfearn 			      struct device_attribute *attr,
1912aefbef0SMatt Redfearn 			      const char *buf, size_t count)
1922aefbef0SMatt Redfearn {
1932aefbef0SMatt Redfearn 	struct rproc *rproc = to_rproc(dev);
1942aefbef0SMatt Redfearn 	int ret = 0;
1952aefbef0SMatt Redfearn 
1962aefbef0SMatt Redfearn 	if (sysfs_streq(buf, "start")) {
1972aefbef0SMatt Redfearn 		ret = rproc_boot(rproc);
1982aefbef0SMatt Redfearn 		if (ret)
1992aefbef0SMatt Redfearn 			dev_err(&rproc->dev, "Boot failed: %d\n", ret);
2002aefbef0SMatt Redfearn 	} else if (sysfs_streq(buf, "stop")) {
201*c13b780cSSuman Anna 		ret = rproc_shutdown(rproc);
2025daaeb5fSMathieu Poirier 	} else if (sysfs_streq(buf, "detach")) {
2035daaeb5fSMathieu Poirier 		ret = rproc_detach(rproc);
2042aefbef0SMatt Redfearn 	} else {
2052aefbef0SMatt Redfearn 		dev_err(&rproc->dev, "Unrecognised option: %s\n", buf);
2062aefbef0SMatt Redfearn 		ret = -EINVAL;
2072aefbef0SMatt Redfearn 	}
2082aefbef0SMatt Redfearn 	return ret ? ret : count;
2092aefbef0SMatt Redfearn }
2102aefbef0SMatt Redfearn static DEVICE_ATTR_RW(state);
2112aefbef0SMatt Redfearn 
2126ed756aaSSuman Anna /* Expose the name of the remote processor via sysfs */
name_show(struct device * dev,struct device_attribute * attr,char * buf)2136ed756aaSSuman Anna static ssize_t name_show(struct device *dev, struct device_attribute *attr,
2146ed756aaSSuman Anna 			 char *buf)
2156ed756aaSSuman Anna {
2166ed756aaSSuman Anna 	struct rproc *rproc = to_rproc(dev);
2176ed756aaSSuman Anna 
2186ed756aaSSuman Anna 	return sprintf(buf, "%s\n", rproc->name);
2196ed756aaSSuman Anna }
2206ed756aaSSuman Anna static DEVICE_ATTR_RO(name);
2216ed756aaSSuman Anna 
rproc_is_visible(struct kobject * kobj,struct attribute * attr,int n)22226c9da51SPuranjay Mohan static umode_t rproc_is_visible(struct kobject *kobj, struct attribute *attr,
22326c9da51SPuranjay Mohan 				int n)
22426c9da51SPuranjay Mohan {
22526c9da51SPuranjay Mohan 	struct device *dev = kobj_to_dev(kobj);
22626c9da51SPuranjay Mohan 	struct rproc *rproc = to_rproc(dev);
22726c9da51SPuranjay Mohan 	umode_t mode = attr->mode;
22826c9da51SPuranjay Mohan 
22926c9da51SPuranjay Mohan 	if (rproc->sysfs_read_only && (attr == &dev_attr_recovery.attr ||
23026c9da51SPuranjay Mohan 				       attr == &dev_attr_firmware.attr ||
23126c9da51SPuranjay Mohan 				       attr == &dev_attr_state.attr ||
23226c9da51SPuranjay Mohan 				       attr == &dev_attr_coredump.attr))
23326c9da51SPuranjay Mohan 		mode = 0444;
23426c9da51SPuranjay Mohan 
23526c9da51SPuranjay Mohan 	return mode;
23626c9da51SPuranjay Mohan }
23726c9da51SPuranjay Mohan 
2382aefbef0SMatt Redfearn static struct attribute *rproc_attrs[] = {
239f75c6043SRishabh Bhatnagar 	&dev_attr_coredump.attr,
240526b9e0cSRishabh Bhatnagar 	&dev_attr_recovery.attr,
2412aefbef0SMatt Redfearn 	&dev_attr_firmware.attr,
2422aefbef0SMatt Redfearn 	&dev_attr_state.attr,
2436ed756aaSSuman Anna 	&dev_attr_name.attr,
2442aefbef0SMatt Redfearn 	NULL
2452aefbef0SMatt Redfearn };
2462aefbef0SMatt Redfearn 
2472aefbef0SMatt Redfearn static const struct attribute_group rproc_devgroup = {
24826c9da51SPuranjay Mohan 	.attrs = rproc_attrs,
24926c9da51SPuranjay Mohan 	.is_visible = rproc_is_visible,
2502aefbef0SMatt Redfearn };
2512aefbef0SMatt Redfearn 
2522aefbef0SMatt Redfearn static const struct attribute_group *rproc_devgroups[] = {
2532aefbef0SMatt Redfearn 	&rproc_devgroup,
2542aefbef0SMatt Redfearn 	NULL
2552aefbef0SMatt Redfearn };
2562aefbef0SMatt Redfearn 
2572aefbef0SMatt Redfearn struct class rproc_class = {
2582aefbef0SMatt Redfearn 	.name		= "remoteproc",
2592aefbef0SMatt Redfearn 	.dev_groups	= rproc_devgroups,
2602aefbef0SMatt Redfearn };
2612aefbef0SMatt Redfearn 
rproc_init_sysfs(void)2622aefbef0SMatt Redfearn int __init rproc_init_sysfs(void)
2632aefbef0SMatt Redfearn {
2642aefbef0SMatt Redfearn 	/* create remoteproc device class for sysfs */
2652aefbef0SMatt Redfearn 	int err = class_register(&rproc_class);
2662aefbef0SMatt Redfearn 
2672aefbef0SMatt Redfearn 	if (err)
2682aefbef0SMatt Redfearn 		pr_err("remoteproc: unable to register class\n");
2692aefbef0SMatt Redfearn 	return err;
2702aefbef0SMatt Redfearn }
2712aefbef0SMatt Redfearn 
rproc_exit_sysfs(void)2722aefbef0SMatt Redfearn void __exit rproc_exit_sysfs(void)
2732aefbef0SMatt Redfearn {
2742aefbef0SMatt Redfearn 	class_unregister(&rproc_class);
2752aefbef0SMatt Redfearn }
276