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