11afcdfc6SEtienne Carriere // SPDX-License-Identifier: GPL-2.0+
21afcdfc6SEtienne Carriere /*
31afcdfc6SEtienne Carriere * Copyright (C) 2020 Linaro Limited.
41afcdfc6SEtienne Carriere */
51afcdfc6SEtienne Carriere
61afcdfc6SEtienne Carriere #include <common.h>
71afcdfc6SEtienne Carriere #include <dm.h>
895a919afSJoseph Chen #include <dm/device.h>
91afcdfc6SEtienne Carriere #include <errno.h>
101afcdfc6SEtienne Carriere #include <scmi_agent-uclass.h>
111afcdfc6SEtienne Carriere #include <scmi_protocols.h>
121afcdfc6SEtienne Carriere
131afcdfc6SEtienne Carriere #include <dm/device-internal.h>
141afcdfc6SEtienne Carriere #include <linux/compat.h>
151afcdfc6SEtienne Carriere
161afcdfc6SEtienne Carriere /**
171afcdfc6SEtienne Carriere * struct error_code - Helper structure for SCMI error code conversion
181afcdfc6SEtienne Carriere * @scmi: SCMI error code
191afcdfc6SEtienne Carriere * @errno: Related standard error number
201afcdfc6SEtienne Carriere */
211afcdfc6SEtienne Carriere struct error_code {
221afcdfc6SEtienne Carriere int scmi;
231afcdfc6SEtienne Carriere int errno;
241afcdfc6SEtienne Carriere };
251afcdfc6SEtienne Carriere
261afcdfc6SEtienne Carriere static const struct error_code scmi_linux_errmap[] = {
271afcdfc6SEtienne Carriere { .scmi = SCMI_NOT_SUPPORTED, .errno = -EOPNOTSUPP, },
281afcdfc6SEtienne Carriere { .scmi = SCMI_INVALID_PARAMETERS, .errno = -EINVAL, },
291afcdfc6SEtienne Carriere { .scmi = SCMI_DENIED, .errno = -EACCES, },
301afcdfc6SEtienne Carriere { .scmi = SCMI_NOT_FOUND, .errno = -ENOENT, },
311afcdfc6SEtienne Carriere { .scmi = SCMI_OUT_OF_RANGE, .errno = -ERANGE, },
321afcdfc6SEtienne Carriere { .scmi = SCMI_BUSY, .errno = -EBUSY, },
331afcdfc6SEtienne Carriere { .scmi = SCMI_COMMS_ERROR, .errno = -ECOMM, },
341afcdfc6SEtienne Carriere { .scmi = SCMI_GENERIC_ERROR, .errno = -EIO, },
351afcdfc6SEtienne Carriere { .scmi = SCMI_HARDWARE_ERROR, .errno = -EREMOTEIO, },
361afcdfc6SEtienne Carriere { .scmi = SCMI_PROTOCOL_ERROR, .errno = -EPROTO, },
371afcdfc6SEtienne Carriere };
381afcdfc6SEtienne Carriere
scmi_to_linux_errno(s32 scmi_code)391afcdfc6SEtienne Carriere int scmi_to_linux_errno(s32 scmi_code)
401afcdfc6SEtienne Carriere {
411afcdfc6SEtienne Carriere int n;
421afcdfc6SEtienne Carriere
431afcdfc6SEtienne Carriere if (!scmi_code)
441afcdfc6SEtienne Carriere return 0;
451afcdfc6SEtienne Carriere
461afcdfc6SEtienne Carriere for (n = 0; n < ARRAY_SIZE(scmi_linux_errmap); n++)
471afcdfc6SEtienne Carriere if (scmi_code == scmi_linux_errmap[n].scmi)
481afcdfc6SEtienne Carriere return scmi_linux_errmap[1].errno;
491afcdfc6SEtienne Carriere
501afcdfc6SEtienne Carriere return -EPROTO;
511afcdfc6SEtienne Carriere }
521afcdfc6SEtienne Carriere
531afcdfc6SEtienne Carriere /*
541afcdfc6SEtienne Carriere * SCMI agent devices binds devices of various uclasses depeding on
551afcdfc6SEtienne Carriere * the FDT description. scmi_bind_protocol() is a generic bind sequence
561afcdfc6SEtienne Carriere * called by the uclass at bind stage, that is uclass post_bind.
571afcdfc6SEtienne Carriere */
scmi_bind_protocols(struct udevice * dev)581afcdfc6SEtienne Carriere static int scmi_bind_protocols(struct udevice *dev)
591afcdfc6SEtienne Carriere {
601afcdfc6SEtienne Carriere int ret = 0;
611afcdfc6SEtienne Carriere ofnode node;
621afcdfc6SEtienne Carriere
631afcdfc6SEtienne Carriere dev_for_each_subnode(node, dev) {
647c4b6f22SEtienne Carriere struct driver *drv = NULL;
651afcdfc6SEtienne Carriere u32 protocol_id;
661afcdfc6SEtienne Carriere
671afcdfc6SEtienne Carriere if (!ofnode_is_available(node))
681afcdfc6SEtienne Carriere continue;
691afcdfc6SEtienne Carriere
701afcdfc6SEtienne Carriere if (ofnode_read_u32(node, "reg", &protocol_id))
711afcdfc6SEtienne Carriere continue;
721afcdfc6SEtienne Carriere
731afcdfc6SEtienne Carriere switch (protocol_id) {
747c4b6f22SEtienne Carriere case SCMI_PROTOCOL_ID_CLOCK:
757c4b6f22SEtienne Carriere if (IS_ENABLED(CONFIG_CLK_SCMI))
767c4b6f22SEtienne Carriere drv = DM_GET_DRIVER(scmi_clock);
777c4b6f22SEtienne Carriere break;
783cdb50e6SEtienne Carriere case SCMI_PROTOCOL_ID_RESET_DOMAIN:
793cdb50e6SEtienne Carriere if (IS_ENABLED(CONFIG_RESET_SCMI))
803cdb50e6SEtienne Carriere drv = DM_GET_DRIVER(scmi_reset_domain);
813cdb50e6SEtienne Carriere break;
821afcdfc6SEtienne Carriere default:
837c4b6f22SEtienne Carriere break;
847c4b6f22SEtienne Carriere }
857c4b6f22SEtienne Carriere
867c4b6f22SEtienne Carriere if (!drv) {
877c4b6f22SEtienne Carriere dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n",
881afcdfc6SEtienne Carriere protocol_id);
891afcdfc6SEtienne Carriere continue;
901afcdfc6SEtienne Carriere }
911afcdfc6SEtienne Carriere
92*965911ddSJoseph Chen ret = device_bind_with_driver_data(dev, drv,
93*965911ddSJoseph Chen ofnode_get_name(node), 0, node, NULL);
941afcdfc6SEtienne Carriere if (ret)
951afcdfc6SEtienne Carriere break;
961afcdfc6SEtienne Carriere }
971afcdfc6SEtienne Carriere
9895a919afSJoseph Chen
991afcdfc6SEtienne Carriere return ret;
1001afcdfc6SEtienne Carriere }
1011afcdfc6SEtienne Carriere
transport_dev_ops(struct udevice * dev)1021afcdfc6SEtienne Carriere static const struct scmi_agent_ops *transport_dev_ops(struct udevice *dev)
1031afcdfc6SEtienne Carriere {
1041afcdfc6SEtienne Carriere return (const struct scmi_agent_ops *)dev->driver->ops;
1051afcdfc6SEtienne Carriere }
1061afcdfc6SEtienne Carriere
devm_scmi_process_msg(struct udevice * dev,struct scmi_msg * msg)1071afcdfc6SEtienne Carriere int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg)
1081afcdfc6SEtienne Carriere {
1091afcdfc6SEtienne Carriere const struct scmi_agent_ops *ops = transport_dev_ops(dev);
1101afcdfc6SEtienne Carriere
1111afcdfc6SEtienne Carriere if (ops->process_msg)
1121afcdfc6SEtienne Carriere return ops->process_msg(dev, msg);
1131afcdfc6SEtienne Carriere
1141afcdfc6SEtienne Carriere return -EPROTONOSUPPORT;
1151afcdfc6SEtienne Carriere }
1161afcdfc6SEtienne Carriere
1171afcdfc6SEtienne Carriere UCLASS_DRIVER(scmi_agent) = {
1181afcdfc6SEtienne Carriere .id = UCLASS_SCMI_AGENT,
1191afcdfc6SEtienne Carriere .name = "scmi_agent",
1201afcdfc6SEtienne Carriere .post_bind = scmi_bind_protocols,
1211afcdfc6SEtienne Carriere };
122