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