diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 2523e59723..4769ec0866 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -366,6 +366,22 @@ sandbox_firmware: sandbox-firmware { compatible = "sandbox,firmware"; }; + + sandbox-scmi-agent@0 { + compatible = "sandbox,scmi-agent"; + #address-cells = <1>; + #size-cells = <0>; + }; + + sandbox-scmi-agent@1 { + compatible = "sandbox,scmi-agent"; + #address-cells = <1>; + #size-cells = <0>; + + protocol@10 { + reg = <0x10>; + }; + }; }; pinctrl-gpio { diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h new file mode 100644 index 0000000000..a811fe19c3 --- /dev/null +++ b/arch/sandbox/include/asm/scmi_test.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020, Linaro Limited + */ + +#ifndef __SANDBOX_SCMI_TEST_H +#define __SANDBOX_SCMI_TEST_H + +struct udevice; +struct sandbox_scmi_agent; +struct sandbox_scmi_service; + +/** + * struct sandbox_scmi_agent - Simulated SCMI service seen by SCMI agent + * @idx: Identifier for the SCMI agent, its index + */ +struct sandbox_scmi_agent { + uint idx; +}; + +/** + * struct sandbox_scmi_service - Reference to simutaed SCMI agents/services + * @agent: Pointer to SCMI sandbox agent pointers array + * @agent_count: Number of emulated agents exposed in array @agent. + */ +struct sandbox_scmi_service { + struct sandbox_scmi_agent **agent; + size_t agent_count; +}; + +#ifdef CONFIG_SCMI_FIRMWARE +/** + * sandbox_scmi_service_context - Get the simulated SCMI services context + * @return: Reference to backend simulated resources state + */ +struct sandbox_scmi_service *sandbox_scmi_service_ctx(void); +#else +static inline struct sandbox_scmi_service *sandbox_scmi_service_ctx(void) +{ + return NULL; +} +#endif /* CONFIG_SCMI_FIRMWARE */ +#endif /* __SANDBOX_SCMI_TEST_H */ diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 6e9f029cc9..2c130c01f0 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -132,6 +132,8 @@ CONFIG_BOARD_SANDBOX=y CONFIG_DMA=y CONFIG_DMA_CHANNELS=y CONFIG_SANDBOX_DMA=y +CONFIG_FIRMWARE=y +CONFIG_SCMI_FIRMWARE=y CONFIG_GPIO_HOG=y CONFIG_DM_GPIO_LOOKUP_LABEL=y CONFIG_PM8916_GPIO=y diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index b70a206355..ef958b3a7a 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -36,3 +36,5 @@ config ZYNQMP_FIRMWARE various platform management services. Say yes to enable ZynqMP firmware interface driver. If in doubt, say N. + +source "drivers/firmware/scmi/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index a0c250a473..7ce83d72bd 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_$(SPL_)ARM_PSCI_FW) += psci.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o obj-$(CONFIG_SANDBOX) += firmware-sandbox.o obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware-zynqmp.o +obj-$(CONFIG_SCMI_FIRMWARE) += scmi/ diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig new file mode 100644 index 0000000000..57e2ebbe42 --- /dev/null +++ b/drivers/firmware/scmi/Kconfig @@ -0,0 +1,17 @@ +config SCMI_FIRMWARE + bool "Enable SCMI support" + select FIRMWARE + select OF_TRANSLATE + depends on SANDBOX + help + System Control and Management Interface (SCMI) is a communication + protocol that defines standard interfaces for power, performance + and system management. The SCMI specification is available at + https://developer.arm.com/architectures/system-architectures/software-standards/scmi + + An SCMI agent communicates with a related SCMI server firmware + located in another sub-system, as a companion micro controller + or a companion host in the CPU system. + + Communications between agent (client) and the SCMI server are + based on message exchange. diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile new file mode 100644 index 0000000000..336ea1f2a3 --- /dev/null +++ b/drivers/firmware/scmi/Makefile @@ -0,0 +1,2 @@ +obj-y += scmi_agent-uclass.o +obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c new file mode 100644 index 0000000000..3179438aab --- /dev/null +++ b/drivers/firmware/scmi/sandbox-scmi_agent.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The sandbox SCMI agent driver simulates to some extend a SCMI message + * processing. It simulates few of the SCMI services for some of the + * SCMI protocols embedded in U-Boot. Currently none. + * + * This driver simulates 2 SCMI agents for test purpose. + * + * This Driver exports sandbox_scmi_service_ct() for the test sequence to + * get the state of the simulated services (clock state, rate, ...) and + * check back-end device state reflects the request send through the + * various uclass devices, currently nothing. + */ + +#define SANDBOX_SCMI_AGENT_COUNT 2 + +/* The list saves to simulted end devices references for test purpose */ +struct sandbox_scmi_agent *sandbox_scmi_agent_list[SANDBOX_SCMI_AGENT_COUNT]; + +static struct sandbox_scmi_service sandbox_scmi_service_state = { + .agent = sandbox_scmi_agent_list, + .agent_count = SANDBOX_SCMI_AGENT_COUNT, +}; + +struct sandbox_scmi_service *sandbox_scmi_service_ctx(void) +{ + return &sandbox_scmi_service_state; +} + +static void debug_print_agent_state(struct udevice *dev, char *str) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + + dev_dbg(dev, "Dump sandbox_scmi_agent %u: %s\n", agent->idx, str); +}; + +static int sandbox_scmi_test_process_msg(struct udevice *dev, + struct scmi_msg *msg) +{ + switch (msg->protocol_id) { + case SCMI_PROTOCOL_ID_BASE: + case SCMI_PROTOCOL_ID_POWER_DOMAIN: + case SCMI_PROTOCOL_ID_SYSTEM: + case SCMI_PROTOCOL_ID_PERF: + case SCMI_PROTOCOL_ID_CLOCK: + case SCMI_PROTOCOL_ID_SENSOR: + case SCMI_PROTOCOL_ID_RESET_DOMAIN: + *(u32 *)msg->out_msg = SCMI_NOT_SUPPORTED; + return 0; + default: + break; + } + + dev_err(dev, "%s(%s): Unhandled protocol_id %#x/message_id %#x\n", + __func__, dev->name, msg->protocol_id, msg->message_id); + + if (msg->out_msg_sz < sizeof(u32)) + return -EINVAL; + + /* Intentionnaly report unhandled IDs through the SCMI return code */ + *(u32 *)msg->out_msg = SCMI_PROTOCOL_ERROR; + return 0; +} + +static int sandbox_scmi_test_remove(struct udevice *dev) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + + debug_print_agent_state(dev, "removed"); + + /* We only need to dereference the agent in the context */ + sandbox_scmi_service_ctx()->agent[agent->idx] = NULL; + + return 0; +} + +static int sandbox_scmi_test_probe(struct udevice *dev) +{ + static const char basename[] = "sandbox-scmi-agent@"; + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + const size_t basename_size = sizeof(basename) - 1; + + if (strncmp(basename, dev->name, basename_size)) + return -ENOENT; + + switch (dev->name[basename_size]) { + case '0': + *agent = (struct sandbox_scmi_agent){ + .idx = 0, + }; + break; + case '1': + *agent = (struct sandbox_scmi_agent){ + .idx = 1, + }; + break; + default: + dev_err(dev, "%s(): Unexpected agent ID %s\n", + __func__, dev->name + basename_size); + return -ENOENT; + } + + debug_print_agent_state(dev, "probed"); + + /* Save reference for tests purpose */ + sandbox_scmi_service_ctx()->agent[agent->idx] = agent; + + return 0; +}; + +static const struct udevice_id sandbox_scmi_test_ids[] = { + { .compatible = "sandbox,scmi-agent" }, + { } +}; + +struct scmi_agent_ops sandbox_scmi_test_ops = { + .process_msg = sandbox_scmi_test_process_msg, +}; + +U_BOOT_DRIVER(sandbox_scmi_agent) = { + .name = "sandbox-scmi_agent", + .id = UCLASS_SCMI_AGENT, + .of_match = sandbox_scmi_test_ids, + .priv_auto_alloc_size = sizeof(struct sandbox_scmi_agent), + .probe = sandbox_scmi_test_probe, + .remove = sandbox_scmi_test_remove, + .ops = &sandbox_scmi_test_ops, +}; diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c new file mode 100644 index 0000000000..67a6f907c9 --- /dev/null +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Linaro Limited. + */ + +#include +#include +#include +#include +#include + +#include +#include + +/** + * 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; + struct driver *drv; + + dev_for_each_subnode(node, dev) { + u32 protocol_id; + + if (!ofnode_is_available(node)) + continue; + + if (ofnode_read_u32(node, "reg", &protocol_id)) + continue; + + switch (protocol_id) { + default: + dev_info(dev, "Ignore unsupported SCMI protocol %#x\n", + protocol_id); + continue; + } + + ret = device_bind_ofnode(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, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 4ec5fa6670..88f10c4622 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -94,6 +94,7 @@ enum uclass_id { UCLASS_RESET, /* Reset controller device */ UCLASS_RNG, /* Random Number Generator */ UCLASS_RTC, /* Real time clock device */ + UCLASS_SCMI_AGENT, /* Interface with an SCMI server */ UCLASS_SCSI, /* SCSI device */ UCLASS_SERIAL, /* Serial UART */ UCLASS_SIMPLE_BUS, /* Bus with child devices */ diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h new file mode 100644 index 0000000000..a501d1b482 --- /dev/null +++ b/include/scmi_agent-uclass.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2019-2020 Linaro Limited. + */ +#ifndef _SCMI_AGENT_UCLASS_H +#define _SCMI_AGENT_UCLASS_H + +struct udevice; +struct scmi_msg; + +/** + * struct scmi_transport_ops - The functions that a SCMI transport layer must implement. + */ +struct scmi_agent_ops { + /* + * process_msg - Request transport to get the SCMI message processed + * + * @agent: Agent using the transport + * @msg: SCMI message to be transmitted + */ + int (*process_msg)(struct udevice *dev, struct scmi_msg *msg); +}; + +#endif /* _SCMI_TRANSPORT_UCLASS_H */ diff --git a/include/scmi_agent.h b/include/scmi_agent.h new file mode 100644 index 0000000000..f1be9ff209 --- /dev/null +++ b/include/scmi_agent.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (C) 2019-2020, Linaro Limited + * + * An SCMI agent device represent on communication path from a + * device driver to the remote SCMI server which driver sends + * messages to and receives response messages from. + */ +#ifndef SCMI_AGENT_H +#define SCMI_AGENT_H + +#include + +struct udevice; + +/* + * struct scmi_msg - Context of a SCMI message sent and the response received + * + * @protocol_id: SCMI protocol ID + * @message_id: SCMI message ID for a defined protocol ID + * @in_msg: Pointer to the message payload sent by the driver + * @in_msg_sz: Byte size of the message payload sent + * @out_msg: Pointer to buffer to store response message payload + * @out_msg_sz: Byte size of the response buffer and response payload + */ +struct scmi_msg { + unsigned int protocol_id; + unsigned int message_id; + u8 *in_msg; + size_t in_msg_sz; + u8 *out_msg; + size_t out_msg_sz; +}; + +/* Helper macro to match a message on input/output array references */ +#define SCMI_MSG_IN(_protocol, _message, _in_array, _out_array) \ + (struct scmi_msg){ \ + .protocol_id = (_protocol), \ + .message_id = (_message), \ + .in_msg = (uint8_t *)&(_in_array), \ + .in_msg_sz = sizeof(_in_array), \ + .out_msg = (uint8_t *)&(_out_array), \ + .out_msg_sz = sizeof(_out_array), \ + } + +/** + * scmi_send_and_process_msg() - send and process a SCMI message + * + * Send a message to a SCMI server through a target SCMI agent device. + * Caller sets scmi_msg::out_msg_sz to the output message buffer size. + * On return, scmi_msg::out_msg_sz stores the response payload size. + * + * @dev: SCMI agent device + * @msg: Message structure reference + * @return 0 on success and a negative errno on failure + */ +int devm_scmi_process_msg(struct udevice *dev, struct scmi_msg *msg); + +/** + * scmi_to_linux_errno() - Convert an SCMI error code into a Linux errno code + * + * @scmi_errno: SCMI error code value + * @return 0 for successful status and a negative errno otherwise + */ +int scmi_to_linux_errno(s32 scmi_errno); + +#endif /* SCMI_H */ diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h new file mode 100644 index 0000000000..86a2d109c8 --- /dev/null +++ b/include/scmi_protocols.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (C) 2019-2020, Linaro Limited + */ +#ifndef _SCMI_PROTOCOLS_H +#define _SCMI_PROTOCOLS_H + +#include + +/* + * Subset the SCMI protocols definition + * based on SCMI specification v2.0 (DEN0056B) + * https://developer.arm.com/docs/den0056/b + */ + +enum scmi_std_protocol { + SCMI_PROTOCOL_ID_BASE = 0x10, + SCMI_PROTOCOL_ID_POWER_DOMAIN = 0x11, + SCMI_PROTOCOL_ID_SYSTEM = 0x12, + SCMI_PROTOCOL_ID_PERF = 0x13, + SCMI_PROTOCOL_ID_CLOCK = 0x14, + SCMI_PROTOCOL_ID_SENSOR = 0x15, + SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16, +}; + +enum scmi_status_code { + SCMI_SUCCESS = 0, + SCMI_NOT_SUPPORTED = -1, + SCMI_INVALID_PARAMETERS = -2, + SCMI_DENIED = -3, + SCMI_NOT_FOUND = -4, + SCMI_OUT_OF_RANGE = -5, + SCMI_BUSY = -6, + SCMI_COMMS_ERROR = -7, + SCMI_GENERIC_ERROR = -8, + SCMI_HARDWARE_ERROR = -9, + SCMI_PROTOCOL_ERROR = -10, +}; + +#endif /* _SCMI_PROTOCOLS_H */ diff --git a/test/dm/Makefile b/test/dm/Makefile index 864c8d0b4c..70ba1b6695 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -80,4 +80,5 @@ obj-$(CONFIG_DM_RNG) += rng.o obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o +obj-$(CONFIG_SCMI_FIRMWARE) += scmi.o endif diff --git a/test/dm/scmi.c b/test/dm/scmi.c new file mode 100644 index 0000000000..d8c1e71f12 --- /dev/null +++ b/test/dm/scmi.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020, Linaro Limited + * + * Tests scmi_agent uclass and the SCMI drivers implemented in other + * uclass devices probe when a SCMI server exposes resources. + * + * Note in test.dts the protocol@10 node in agent 1. Protocol 0x10 is not + * implemented in U-Boot SCMI components but the implementation is exepected + * to not complain on unknown protocol IDs, as long as it is not used. Note + * in test.dts tests that SCMI drivers probing does not fail for such an + * unknown SCMI protocol ID. + */ + +#include +#include +#include +#include +#include +#include + +/* + * Test SCMI states when loading and releasing resources + * related to SCMI drivers. + */ +static int dm_test_scmi_sandbox_agent(struct unit_test_state *uts) +{ + struct sandbox_scmi_service *scmi_ctx = sandbox_scmi_service_ctx(); + + ut_assertnonnull(scmi_ctx); + ut_asserteq(2, scmi_ctx->agent_count); + ut_assertnull(scmi_ctx->agent[0]); + ut_assertnull(scmi_ctx->agent[1]); + + return 0; +} + +DM_TEST(dm_test_scmi_sandbox_agent, UT_TESTF_SCAN_FDT);