Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2020 Linaro Limited. */ #define LOG_CATEGORY UCLASS_SCMI_AGENT #include <common.h> #include <dm.h> #include <errno.h> #include <scmi_agent-uclass.h> #include <scmi_protocols.h> #include <dm/device_compat.h> #include <dm/device-internal.h> #include <linux/compat.h> /** * struct error_code - Helper structure for SCMI error code conversion * @scmi: SCMI error code * @errno: Related standard error number */ struct error_code { int scmi; int errno; }; static const struct error_code scmi_linux_errmap[] = { { .scmi = SCMI_NOT_SUPPORTED, .errno = -EOPNOTSUPP, }, { .scmi = SCMI_INVALID_PARAMETERS, .errno = -EINVAL, }, { .scmi = SCMI_DENIED, .errno = -EACCES, }, { .scmi = SCMI_NOT_FOUND, .errno = -ENOENT, }, { .scmi = SCMI_OUT_OF_RANGE, .errno = -ERANGE, }, { .scmi = SCMI_BUSY, .errno = -EBUSY, }, { .scmi = SCMI_COMMS_ERROR, .errno = -ECOMM, }, { .scmi = SCMI_GENERIC_ERROR, .errno = -EIO, }, { .scmi = SCMI_HARDWARE_ERROR, .errno = -EREMOTEIO, }, { .scmi = SCMI_PROTOCOL_ERROR, .errno = -EPROTO, }, }; int scmi_to_linux_errno(s32 scmi_code) { int n; if (!scmi_code) return 0; for (n = 0; n < ARRAY_SIZE(scmi_linux_errmap); n++) if (scmi_code == scmi_linux_errmap[n].scmi) return scmi_linux_errmap[1].errno; return -EPROTO; } /* * SCMI agent devices binds devices of various uclasses depeding on * the FDT description. scmi_bind_protocol() is a generic bind sequence * called by the uclass at bind stage, that is uclass post_bind. */ static int scmi_bind_protocols(struct udevice *dev) { int ret = 0; ofnode node; dev_for_each_subnode(node, dev) { struct driver *drv = NULL; u32 protocol_id; if (!ofnode_is_available(node)) continue; if (ofnode_read_u32(node, "reg", &protocol_id)) continue; switch (protocol_id) { case SCMI_PROTOCOL_ID_CLOCK: if (IS_ENABLED(CONFIG_CLK_SCMI)) drv = DM_DRIVER_GET(scmi_clock); break; case SCMI_PROTOCOL_ID_RESET_DOMAIN: if (IS_ENABLED(CONFIG_RESET_SCMI)) drv = DM_DRIVER_GET(scmi_reset_domain); break; case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN: if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)) { node = ofnode_find_subnode(node, "regulators"); if (!ofnode_valid(node)) { dev_err(dev, "no regulators node\n"); return -ENXIO; } drv = DM_DRIVER_GET(scmi_voltage_domain); } break; default: break; } if (!drv) { dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n", protocol_id); continue; } ret = device_bind(dev, drv, ofnode_get_name(node), NULL, node, NULL); if (ret) break; } return ret; } static const struct scmi_agent_ops *transport_dev_ops(struct udevice *dev) { return (const struct scmi_agent_ops *)dev->driver->ops; } int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg) { const struct scmi_agent_ops *ops = transport_dev_ops(dev); if (ops->process_msg) return ops->process_msg(dev, msg); return -EPROTONOSUPPORT; } UCLASS_DRIVER(scmi_agent) = { .id = UCLASS_SCMI_AGENT, .name = "scmi_agent", .post_bind = scmi_bind_protocols, }; |