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> 7*bf89a7c0SMichael 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 132aefbef0SMatt Redfearn /* Expose the loaded / running firmware name via sysfs */ 142aefbef0SMatt Redfearn static ssize_t firmware_show(struct device *dev, struct device_attribute *attr, 152aefbef0SMatt Redfearn char *buf) 162aefbef0SMatt Redfearn { 172aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 182aefbef0SMatt Redfearn 192aefbef0SMatt Redfearn return sprintf(buf, "%s\n", rproc->firmware); 202aefbef0SMatt Redfearn } 212aefbef0SMatt Redfearn 222aefbef0SMatt Redfearn /* Change firmware name via sysfs */ 232aefbef0SMatt Redfearn static ssize_t firmware_store(struct device *dev, 242aefbef0SMatt Redfearn struct device_attribute *attr, 252aefbef0SMatt Redfearn const char *buf, size_t count) 262aefbef0SMatt Redfearn { 272aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 282aefbef0SMatt Redfearn char *p; 292aefbef0SMatt Redfearn int err, len = count; 302aefbef0SMatt Redfearn 312aefbef0SMatt Redfearn err = mutex_lock_interruptible(&rproc->lock); 322aefbef0SMatt Redfearn if (err) { 332aefbef0SMatt Redfearn dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, err); 342aefbef0SMatt Redfearn return -EINVAL; 352aefbef0SMatt Redfearn } 362aefbef0SMatt Redfearn 372aefbef0SMatt Redfearn if (rproc->state != RPROC_OFFLINE) { 382aefbef0SMatt Redfearn dev_err(dev, "can't change firmware while running\n"); 392aefbef0SMatt Redfearn err = -EBUSY; 402aefbef0SMatt Redfearn goto out; 412aefbef0SMatt Redfearn } 422aefbef0SMatt Redfearn 432aefbef0SMatt Redfearn len = strcspn(buf, "\n"); 44faeadbb6SSuman Anna if (!len) { 45faeadbb6SSuman Anna dev_err(dev, "can't provide a NULL firmware\n"); 46faeadbb6SSuman Anna err = -EINVAL; 47faeadbb6SSuman Anna goto out; 48faeadbb6SSuman Anna } 492aefbef0SMatt Redfearn 502aefbef0SMatt Redfearn p = kstrndup(buf, len, GFP_KERNEL); 512aefbef0SMatt Redfearn if (!p) { 522aefbef0SMatt Redfearn err = -ENOMEM; 532aefbef0SMatt Redfearn goto out; 542aefbef0SMatt Redfearn } 552aefbef0SMatt Redfearn 562aefbef0SMatt Redfearn kfree(rproc->firmware); 572aefbef0SMatt Redfearn rproc->firmware = p; 582aefbef0SMatt Redfearn out: 592aefbef0SMatt Redfearn mutex_unlock(&rproc->lock); 602aefbef0SMatt Redfearn 612aefbef0SMatt Redfearn return err ? err : count; 622aefbef0SMatt Redfearn } 632aefbef0SMatt Redfearn static DEVICE_ATTR_RW(firmware); 642aefbef0SMatt Redfearn 652aefbef0SMatt Redfearn /* 662aefbef0SMatt Redfearn * A state-to-string lookup table, for exposing a human readable state 672aefbef0SMatt Redfearn * via sysfs. Always keep in sync with enum rproc_state 682aefbef0SMatt Redfearn */ 692aefbef0SMatt Redfearn static const char * const rproc_state_string[] = { 702aefbef0SMatt Redfearn [RPROC_OFFLINE] = "offline", 712aefbef0SMatt Redfearn [RPROC_SUSPENDED] = "suspended", 722aefbef0SMatt Redfearn [RPROC_RUNNING] = "running", 732aefbef0SMatt Redfearn [RPROC_CRASHED] = "crashed", 74608d7921SSarangdhar Joshi [RPROC_DELETED] = "deleted", 752aefbef0SMatt Redfearn [RPROC_LAST] = "invalid", 762aefbef0SMatt Redfearn }; 772aefbef0SMatt Redfearn 782aefbef0SMatt Redfearn /* Expose the state of the remote processor via sysfs */ 792aefbef0SMatt Redfearn static ssize_t state_show(struct device *dev, struct device_attribute *attr, 802aefbef0SMatt Redfearn char *buf) 812aefbef0SMatt Redfearn { 822aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 832aefbef0SMatt Redfearn unsigned int state; 842aefbef0SMatt Redfearn 852aefbef0SMatt Redfearn state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; 862aefbef0SMatt Redfearn return sprintf(buf, "%s\n", rproc_state_string[state]); 872aefbef0SMatt Redfearn } 882aefbef0SMatt Redfearn 892aefbef0SMatt Redfearn /* Change remote processor state via sysfs */ 902aefbef0SMatt Redfearn static ssize_t state_store(struct device *dev, 912aefbef0SMatt Redfearn struct device_attribute *attr, 922aefbef0SMatt Redfearn const char *buf, size_t count) 932aefbef0SMatt Redfearn { 942aefbef0SMatt Redfearn struct rproc *rproc = to_rproc(dev); 952aefbef0SMatt Redfearn int ret = 0; 962aefbef0SMatt Redfearn 972aefbef0SMatt Redfearn if (sysfs_streq(buf, "start")) { 982aefbef0SMatt Redfearn if (rproc->state == RPROC_RUNNING) 992aefbef0SMatt Redfearn return -EBUSY; 1002aefbef0SMatt Redfearn 1012aefbef0SMatt Redfearn ret = rproc_boot(rproc); 1022aefbef0SMatt Redfearn if (ret) 1032aefbef0SMatt Redfearn dev_err(&rproc->dev, "Boot failed: %d\n", ret); 1042aefbef0SMatt Redfearn } else if (sysfs_streq(buf, "stop")) { 1052aefbef0SMatt Redfearn if (rproc->state != RPROC_RUNNING) 1062aefbef0SMatt Redfearn return -EINVAL; 1072aefbef0SMatt Redfearn 1082aefbef0SMatt Redfearn rproc_shutdown(rproc); 1092aefbef0SMatt Redfearn } else { 1102aefbef0SMatt Redfearn dev_err(&rproc->dev, "Unrecognised option: %s\n", buf); 1112aefbef0SMatt Redfearn ret = -EINVAL; 1122aefbef0SMatt Redfearn } 1132aefbef0SMatt Redfearn return ret ? ret : count; 1142aefbef0SMatt Redfearn } 1152aefbef0SMatt Redfearn static DEVICE_ATTR_RW(state); 1162aefbef0SMatt Redfearn 1176ed756aaSSuman Anna /* Expose the name of the remote processor via sysfs */ 1186ed756aaSSuman Anna static ssize_t name_show(struct device *dev, struct device_attribute *attr, 1196ed756aaSSuman Anna char *buf) 1206ed756aaSSuman Anna { 1216ed756aaSSuman Anna struct rproc *rproc = to_rproc(dev); 1226ed756aaSSuman Anna 1236ed756aaSSuman Anna return sprintf(buf, "%s\n", rproc->name); 1246ed756aaSSuman Anna } 1256ed756aaSSuman Anna static DEVICE_ATTR_RO(name); 1266ed756aaSSuman Anna 1272aefbef0SMatt Redfearn static struct attribute *rproc_attrs[] = { 1282aefbef0SMatt Redfearn &dev_attr_firmware.attr, 1292aefbef0SMatt Redfearn &dev_attr_state.attr, 1306ed756aaSSuman Anna &dev_attr_name.attr, 1312aefbef0SMatt Redfearn NULL 1322aefbef0SMatt Redfearn }; 1332aefbef0SMatt Redfearn 1342aefbef0SMatt Redfearn static const struct attribute_group rproc_devgroup = { 1352aefbef0SMatt Redfearn .attrs = rproc_attrs 1362aefbef0SMatt Redfearn }; 1372aefbef0SMatt Redfearn 1382aefbef0SMatt Redfearn static const struct attribute_group *rproc_devgroups[] = { 1392aefbef0SMatt Redfearn &rproc_devgroup, 1402aefbef0SMatt Redfearn NULL 1412aefbef0SMatt Redfearn }; 1422aefbef0SMatt Redfearn 1432aefbef0SMatt Redfearn struct class rproc_class = { 1442aefbef0SMatt Redfearn .name = "remoteproc", 1452aefbef0SMatt Redfearn .dev_groups = rproc_devgroups, 1462aefbef0SMatt Redfearn }; 1472aefbef0SMatt Redfearn 1482aefbef0SMatt Redfearn int __init rproc_init_sysfs(void) 1492aefbef0SMatt Redfearn { 1502aefbef0SMatt Redfearn /* create remoteproc device class for sysfs */ 1512aefbef0SMatt Redfearn int err = class_register(&rproc_class); 1522aefbef0SMatt Redfearn 1532aefbef0SMatt Redfearn if (err) 1542aefbef0SMatt Redfearn pr_err("remoteproc: unable to register class\n"); 1552aefbef0SMatt Redfearn return err; 1562aefbef0SMatt Redfearn } 1572aefbef0SMatt Redfearn 1582aefbef0SMatt Redfearn void __exit rproc_exit_sysfs(void) 1592aefbef0SMatt Redfearn { 1602aefbef0SMatt Redfearn class_unregister(&rproc_class); 1612aefbef0SMatt Redfearn } 162