From 358599efd827b0ee48af864537cc86facc9167c0 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:00 +0200 Subject: firmware: add SCMI agent uclass This change introduces SCMI agent uclass to interact with a firmware using the SCMI protocols [1]. SCMI agent uclass currently supports a single method to request processing of the SCMI message by an identified server. A SCMI message is made of a byte payload associated to a protocol ID and a message ID, all defined by the SCMI specification [1]. On return from process_msg() method, the caller gets the service response. SCMI agent uclass defines a post bind generic sequence for all devices. The sequence binds all the SCMI protocols listed in the FDT for that SCMI agent device. Currently none, but later change will introduce protocols. This change implements a simple sandbox device for the SCMI agent uclass. The sandbox nicely answers SCMI_NOT_SUPPORTED to SCMI messages. To prepare for further test support, the sandbox exposes a architecture function for test application to read the sandbox emulated devices state. Currently supports 2 SCMI agents, identified by an ID in the FDT device name. The simplistic DM test does nothing yet. SCMI agent uclass is designed for platforms that embed a SCMI server in a firmware hosted somewhere, for example in a companion co-processor or in the secure world of the executing processor. SCMI protocols allow an SCMI agent to discover and access external resources as clock, reset controllers and more. SCMI agent and server communicate following the SCMI specification [1]. This SCMI agent implementation complies with the DT bindings defined in the Linux kernel source tree regarding SCMI agent description since v5.8. Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi Signed-off-by: Etienne Carriere Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- drivers/firmware/Kconfig | 2 + drivers/firmware/Makefile | 1 + drivers/firmware/scmi/Kconfig | 17 ++++ drivers/firmware/scmi/Makefile | 2 + drivers/firmware/scmi/sandbox-scmi_agent.c | 142 +++++++++++++++++++++++++++++ drivers/firmware/scmi/scmi_agent-uclass.c | 107 ++++++++++++++++++++++ 6 files changed, 271 insertions(+) create mode 100644 drivers/firmware/scmi/Kconfig create mode 100644 drivers/firmware/scmi/Makefile create mode 100644 drivers/firmware/scmi/sandbox-scmi_agent.c create mode 100644 drivers/firmware/scmi/scmi_agent-uclass.c (limited to 'drivers/firmware') diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index b70a2063551..ef958b3a7a4 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 a0c250a473e..7ce83d72bd3 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 00000000000..57e2ebbe428 --- /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 00000000000..336ea1f2a32 --- /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 00000000000..3179438aabf --- /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 00000000000..67a6f907c99 --- /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, +}; -- cgit v1.3.1 From 240720e9052fa7459e4d8a46d508db1ae41ed238 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:01 +0200 Subject: firmware: scmi: mailbox/smt agent device This change implements a mailbox transport using SMT format for SCMI exchanges. This implementation follows the Linux kernel and SCP-firmware [1] as references implementation for SCMI message processing using SMT format for communication channel meta-data. Use of mailboxes in SCMI FDT bindings are defined in the Linux kernel DT bindings since v4.17. Links: [1] https://github.com/ARM-software/SCP-firmware Signed-off-by: Etienne Carriere Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- drivers/firmware/scmi/Kconfig | 6 +- drivers/firmware/scmi/Makefile | 2 + drivers/firmware/scmi/mailbox_agent.c | 102 +++++++++++++++++++++++++ drivers/firmware/scmi/smt.c | 139 ++++++++++++++++++++++++++++++++++ drivers/firmware/scmi/smt.h | 86 +++++++++++++++++++++ 5 files changed, 333 insertions(+), 2 deletions(-) create mode 100644 drivers/firmware/scmi/mailbox_agent.c create mode 100644 drivers/firmware/scmi/smt.c create mode 100644 drivers/firmware/scmi/smt.h (limited to 'drivers/firmware') diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index 57e2ebbe428..c501bf4943f 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -2,7 +2,7 @@ config SCMI_FIRMWARE bool "Enable SCMI support" select FIRMWARE select OF_TRANSLATE - depends on SANDBOX + depends on SANDBOX || DM_MAILBOX help System Control and Management Interface (SCMI) is a communication protocol that defines standard interfaces for power, performance @@ -14,4 +14,6 @@ config SCMI_FIRMWARE or a companion host in the CPU system. Communications between agent (client) and the SCMI server are - based on message exchange. + based on message exchange. Messages can be exchange over tranport + channels as a mailbox device with some piece of identified shared + memory. diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile index 336ea1f2a32..d22f53efe7f 100644 --- a/drivers/firmware/scmi/Makefile +++ b/drivers/firmware/scmi/Makefile @@ -1,2 +1,4 @@ obj-y += scmi_agent-uclass.o +obj-y += smt.o +obj-$(CONFIG_DM_MAILBOX) += mailbox_agent.o obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o diff --git a/drivers/firmware/scmi/mailbox_agent.c b/drivers/firmware/scmi/mailbox_agent.c new file mode 100644 index 00000000000..7d9fb3622ed --- /dev/null +++ b/drivers/firmware/scmi/mailbox_agent.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Linaro Limited. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smt.h" + +#define TIMEOUT_US_10MS 10000 + +/** + * struct scmi_mbox_channel - Description of an SCMI mailbox transport + * @smt: Shared memory buffer + * @mbox: Mailbox channel description + * @timeout_us: Timeout in microseconds for the mailbox transfer + */ +struct scmi_mbox_channel { + struct scmi_smt smt; + struct mbox_chan mbox; + ulong timeout_us; +}; + +static int scmi_mbox_process_msg(struct udevice *dev, struct scmi_msg *msg) +{ + struct scmi_mbox_channel *chan = dev_get_priv(dev); + int ret; + + ret = scmi_write_msg_to_smt(dev, &chan->smt, msg); + if (ret) + return ret; + + /* Give shm addr to mbox in case it is meaningful */ + ret = mbox_send(&chan->mbox, chan->smt.buf); + if (ret) { + dev_err(dev, "Message send failed: %d\n", ret); + goto out; + } + + /* Receive the response */ + ret = mbox_recv(&chan->mbox, chan->smt.buf, chan->timeout_us); + if (ret) { + dev_err(dev, "Response failed: %d, abort\n", ret); + goto out; + } + + ret = scmi_read_resp_from_smt(dev, &chan->smt, msg); + +out: + scmi_clear_smt_channel(&chan->smt); + + return ret; +} + +int scmi_mbox_probe(struct udevice *dev) +{ + struct scmi_mbox_channel *chan = dev_get_priv(dev); + int ret; + + chan->timeout_us = TIMEOUT_US_10MS; + + ret = mbox_get_by_index(dev, 0, &chan->mbox); + if (ret) { + dev_err(dev, "Failed to find mailbox: %d\n", ret); + goto out; + } + + ret = scmi_dt_get_smt_buffer(dev, &chan->smt); + if (ret) + dev_err(dev, "Failed to get shm resources: %d\n", ret); + +out: + if (ret) + devm_kfree(dev, chan); + + return ret; +} + +static const struct udevice_id scmi_mbox_ids[] = { + { .compatible = "arm,scmi" }, + { } +}; + +static const struct scmi_agent_ops scmi_mbox_ops = { + .process_msg = scmi_mbox_process_msg, +}; + +U_BOOT_DRIVER(scmi_mbox) = { + .name = "scmi-over-mailbox", + .id = UCLASS_SCMI_AGENT, + .of_match = scmi_mbox_ids, + .priv_auto_alloc_size = sizeof(struct scmi_mbox_channel), + .probe = scmi_mbox_probe, + .ops = &scmi_mbox_ops, +}; diff --git a/drivers/firmware/scmi/smt.c b/drivers/firmware/scmi/smt.c new file mode 100644 index 00000000000..ce8fe499390 --- /dev/null +++ b/drivers/firmware/scmi/smt.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (C) 2019-2020 Linaro Limited. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smt.h" + +/** + * Get shared memory configuration defined by the referred DT phandle + * Return with a errno compliant value. + */ +int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt) +{ + int ret; + struct ofnode_phandle_args args; + struct resource resource; + fdt32_t faddr; + phys_addr_t paddr; + + ret = dev_read_phandle_with_args(dev, "shmem", NULL, 0, 0, &args); + if (ret) + return ret; + + ret = ofnode_read_resource(args.node, 0, &resource); + if (ret) + return ret; + + faddr = cpu_to_fdt32(resource.start); + paddr = ofnode_translate_address(args.node, &faddr); + + smt->size = resource_size(&resource); + if (smt->size < sizeof(struct scmi_smt_header)) { + dev_err(dev, "Shared memory buffer too small\n"); + return -EINVAL; + } + + smt->buf = devm_ioremap(dev, paddr, smt->size); + if (!smt->buf) + return -ENOMEM; + +#ifdef CONFIG_ARM + if (dcache_status()) + mmu_set_region_dcache_behaviour((uintptr_t)smt->buf, + smt->size, DCACHE_OFF); +#endif + + return 0; +} + +/** + * Write SCMI message @msg into a SMT shared buffer @smt. + * Return 0 on success and with a negative errno in case of error. + */ +int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt, + struct scmi_msg *msg) +{ + struct scmi_smt_header *hdr = (void *)smt->buf; + + if ((!msg->in_msg && msg->in_msg_sz) || + (!msg->out_msg && msg->out_msg_sz)) + return -EINVAL; + + if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) { + dev_dbg(dev, "Channel busy\n"); + return -EBUSY; + } + + if (smt->size < (sizeof(*hdr) + msg->in_msg_sz) || + smt->size < (sizeof(*hdr) + msg->out_msg_sz)) { + dev_dbg(dev, "Buffer too small\n"); + return -ETOOSMALL; + } + + /* Load message in shared memory */ + hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; + hdr->length = msg->in_msg_sz + sizeof(hdr->msg_header); + hdr->msg_header = SMT_HEADER_TOKEN(0) | + SMT_HEADER_MESSAGE_TYPE(0) | + SMT_HEADER_PROTOCOL_ID(msg->protocol_id) | + SMT_HEADER_MESSAGE_ID(msg->message_id); + + memcpy_toio(hdr->msg_payload, msg->in_msg, msg->in_msg_sz); + + return 0; +} + +/** + * Read SCMI message from a SMT shared buffer @smt and copy it into @msg. + * Return 0 on success and with a negative errno in case of error. + */ +int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt, + struct scmi_msg *msg) +{ + struct scmi_smt_header *hdr = (void *)smt->buf; + + if (!(hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) { + dev_err(dev, "Channel unexpectedly busy\n"); + return -EBUSY; + } + + if (hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR) { + dev_err(dev, "Channel error reported, reset channel\n"); + return -ECOMM; + } + + if (hdr->length > msg->out_msg_sz + sizeof(hdr->msg_header)) { + dev_err(dev, "Buffer to small\n"); + return -ETOOSMALL; + } + + /* Get the data */ + msg->out_msg_sz = hdr->length - sizeof(hdr->msg_header); + memcpy_fromio(msg->out_msg, hdr->msg_payload, msg->out_msg_sz); + + return 0; +} + +/** + * Clear SMT flags in shared buffer to allow further message exchange + */ +void scmi_clear_smt_channel(struct scmi_smt *smt) +{ + struct scmi_smt_header *hdr = (void *)smt->buf; + + hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR; +} diff --git a/drivers/firmware/scmi/smt.h b/drivers/firmware/scmi/smt.h new file mode 100644 index 00000000000..a8c0987bd30 --- /dev/null +++ b/drivers/firmware/scmi/smt.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved. + * Copyright (C) 2019-2020 Linaro Limited. + */ +#ifndef SCMI_SMT_H +#define SCMI_SMT_H + +#include + +/** + * struct scmi_smt_header - Description of the shared memory message buffer + * + * SMT stands for Shared Memory based Transport. + * SMT uses 28 byte header prior message payload to handle the state of + * the communication channel realized by the shared memory area and + * to define SCMI protocol information the payload relates to. + */ +struct scmi_smt_header { + __le32 reserved; + __le32 channel_status; +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR BIT(1) +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE BIT(0) + __le32 reserved1[2]; + __le32 flags; +#define SCMI_SHMEM_FLAG_INTR_ENABLED BIT(0) + __le32 length; + __le32 msg_header; + u8 msg_payload[0]; +}; + +#define SMT_HEADER_TOKEN(token) (((token) << 18) & GENMASK(31, 18)) +#define SMT_HEADER_PROTOCOL_ID(proto) (((proto) << 10) & GENMASK(17, 10)) +#define SMT_HEADER_MESSAGE_TYPE(type) (((type) << 18) & GENMASK(9, 8)) +#define SMT_HEADER_MESSAGE_ID(id) ((id) & GENMASK(7, 0)) + +/** + * struct scmi_smt - Description of a SMT memory buffer + * @buf: Shared memory base address + * @size: Shared memory byte size + */ +struct scmi_smt { + u8 *buf; + size_t size; +}; + +static inline bool scmi_smt_channel_is_free(struct scmi_smt *smt) +{ + struct scmi_smt_header *hdr = (void *)smt->buf; + + return hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; +} + +static inline bool scmi_smt_channel_reports_error(struct scmi_smt *smt) +{ + struct scmi_smt_header *hdr = (void *)smt->buf; + + return hdr->channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR; +} + +static inline void scmi_smt_get_channel(struct scmi_smt *smt) +{ + struct scmi_smt_header *hdr = (void *)smt->buf; + + hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; +} + +static inline void scmi_smt_put_channel(struct scmi_smt *smt) +{ + struct scmi_smt_header *hdr = (void *)smt->buf; + + hdr->channel_status |= SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; + hdr->channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR; +} + +int scmi_dt_get_smt_buffer(struct udevice *dev, struct scmi_smt *smt); + +int scmi_write_msg_to_smt(struct udevice *dev, struct scmi_smt *smt, + struct scmi_msg *msg); + +int scmi_read_resp_from_smt(struct udevice *dev, struct scmi_smt *smt, + struct scmi_msg *msg); + +void scmi_clear_smt_channel(struct scmi_smt *smt); + +#endif /* SCMI_SMT_H */ -- cgit v1.3.1 From 1e35913a2677b1d0511769425a53cd9ae113c238 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:02 +0200 Subject: firmware: scmi: support Arm SMCCC transport This change implements a SMCCC transport for SCMI exchanges. This implementation follows the Linux kernel as references implementation for SCMI message processing, using the SMT format for communication channel meta-data. Use of SMCCC transport in SCMI FDT bindings are defined in the Linux kernel DT bindings since v5.8. SMCCC with SMT is implemented in OP-TEE from tag 3.9.0 [2]. Links: [2] https://github.com/OP-TEE/optee_os/commit/a58c4d706d23 Signed-off-by: Etienne Carriere Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- drivers/firmware/scmi/Kconfig | 6 +-- drivers/firmware/scmi/Makefile | 1 + drivers/firmware/scmi/smccc_agent.c | 89 +++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 drivers/firmware/scmi/smccc_agent.c (limited to 'drivers/firmware') diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index c501bf4943f..c3a109beac7 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -2,7 +2,7 @@ config SCMI_FIRMWARE bool "Enable SCMI support" select FIRMWARE select OF_TRANSLATE - depends on SANDBOX || DM_MAILBOX + depends on SANDBOX || DM_MAILBOX || ARM_SMCCC help System Control and Management Interface (SCMI) is a communication protocol that defines standard interfaces for power, performance @@ -15,5 +15,5 @@ config SCMI_FIRMWARE Communications between agent (client) and the SCMI server are based on message exchange. Messages can be exchange over tranport - channels as a mailbox device with some piece of identified shared - memory. + channels as a mailbox device or an Arm SMCCC service with some + piece of identified shared memory. diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile index d22f53efe7f..2f782bbd55d 100644 --- a/drivers/firmware/scmi/Makefile +++ b/drivers/firmware/scmi/Makefile @@ -1,4 +1,5 @@ obj-y += scmi_agent-uclass.o obj-y += smt.o +obj-$(CONFIG_ARM_SMCCC) += smccc_agent.o obj-$(CONFIG_DM_MAILBOX) += mailbox_agent.o obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o diff --git a/drivers/firmware/scmi/smccc_agent.c b/drivers/firmware/scmi/smccc_agent.c new file mode 100644 index 00000000000..85dbf9195e0 --- /dev/null +++ b/drivers/firmware/scmi/smccc_agent.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Linaro Limited. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smt.h" + +#define SMCCC_RET_NOT_SUPPORTED ((unsigned long)-1) + +/** + * struct scmi_smccc_channel - Description of an SCMI SMCCC transport + * @func_id: SMCCC function ID used by the SCMI transport + * @smt: Shared memory buffer + */ +struct scmi_smccc_channel { + ulong func_id; + struct scmi_smt smt; +}; + +static int scmi_smccc_process_msg(struct udevice *dev, struct scmi_msg *msg) +{ + struct scmi_smccc_channel *chan = dev_get_priv(dev); + struct arm_smccc_res res; + int ret; + + ret = scmi_write_msg_to_smt(dev, &chan->smt, msg); + if (ret) + return ret; + + arm_smccc_smc(chan->func_id, 0, 0, 0, 0, 0, 0, 0, &res); + if (res.a0 == SMCCC_RET_NOT_SUPPORTED) + ret = -ENXIO; + else + ret = scmi_read_resp_from_smt(dev, &chan->smt, msg); + + scmi_clear_smt_channel(&chan->smt); + + return ret; +} + +static int scmi_smccc_probe(struct udevice *dev) +{ + struct scmi_smccc_channel *chan = dev_get_priv(dev); + u32 func_id; + int ret; + + if (dev_read_u32(dev, "arm,smc-id", &func_id)) { + dev_err(dev, "Missing property func-id\n"); + return -EINVAL; + } + + chan->func_id = func_id; + + ret = scmi_dt_get_smt_buffer(dev, &chan->smt); + if (ret) { + dev_err(dev, "Failed to get smt resources: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct udevice_id scmi_smccc_ids[] = { + { .compatible = "arm,scmi-smc" }, + { } +}; + +static const struct scmi_agent_ops scmi_smccc_ops = { + .process_msg = scmi_smccc_process_msg, +}; + +U_BOOT_DRIVER(scmi_smccc) = { + .name = "scmi-over-smccc", + .id = UCLASS_SCMI_AGENT, + .of_match = scmi_smccc_ids, + .priv_auto_alloc_size = sizeof(struct scmi_smccc_channel), + .probe = scmi_smccc_probe, + .ops = &scmi_smccc_ops, +}; -- cgit v1.3.1 From 60388844836f5639e6c9a4331335ff22298128da Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:04 +0200 Subject: clk: add clock driver for SCMI agents This change introduces a clock driver for SCMI agent devices. When SCMI agent and SCMI clock drivers are enabled, SCMI agent binds a clock device for each SCMI clock protocol devices enabled in the FDT. SCMI clock driver is embedded upon CONFIG_CLK_SCMI=y. If enabled, CONFIG_SCMI_AGENT is also enabled. SCMI Clock protocol is defined in the SCMI specification [1]. Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi Signed-off-by: Etienne Carriere Cc: Lukasz Majewski Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- drivers/clk/Kconfig | 8 +++ drivers/clk/Makefile | 1 + drivers/clk/clk_scmi.c | 99 +++++++++++++++++++++++++++++++ drivers/firmware/scmi/scmi_agent-uclass.c | 14 ++++- include/scmi_protocols.h | 78 ++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 drivers/clk/clk_scmi.c (limited to 'drivers/firmware') diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 6003e140b58..4dfbad7986b 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -159,6 +159,14 @@ config CLK_CDCE9XX Enable the clock synthesizer driver for CDCE913/925/937/949 series of chips. +config CLK_SCMI + bool "Enable SCMI clock driver" + depends on SCMI_FIRMWARE + help + Enable this option if you want to support clock devices exposed + by a SCMI agent based on SCMI clock protocol communication + with a SCMI server. + source "drivers/clk/analogbits/Kconfig" source "drivers/clk/at91/Kconfig" source "drivers/clk/exynos/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index cda4b4b605f..d1e295ac7c1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o obj-$(CONFIG_CLK_OWL) += owl/ obj-$(CONFIG_CLK_RENESAS) += renesas/ +obj-$(CONFIG_CLK_SCMI) += clk_scmi.o obj-$(CONFIG_CLK_SIFIVE) += sifive/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c new file mode 100644 index 00000000000..93a4819501c --- /dev/null +++ b/drivers/clk/clk_scmi.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2020 Linaro Limited + */ +#include +#include +#include +#include +#include +#include + +static int scmi_clk_gate(struct clk *clk, int enable) +{ + struct scmi_clk_state_in in = { + .clock_id = clk->id, + .attributes = enable, + }; + struct scmi_clk_state_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_CONFIG_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(clk->dev->parent, &msg); + if (ret) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_clk_enable(struct clk *clk) +{ + return scmi_clk_gate(clk, 1); +} + +static int scmi_clk_disable(struct clk *clk) +{ + return scmi_clk_gate(clk, 0); +} + +static ulong scmi_clk_get_rate(struct clk *clk) +{ + struct scmi_clk_rate_get_in in = { + .clock_id = clk->id, + }; + struct scmi_clk_rate_get_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_RATE_GET, + in, out); + int ret; + + ret = devm_scmi_process_msg(clk->dev->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb); +} + +static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) +{ + struct scmi_clk_rate_set_in in = { + .clock_id = clk->id, + .flags = SCMI_CLK_RATE_ROUND_CLOSEST, + .rate_lsb = (u32)rate, + .rate_msb = (u32)((u64)rate >> 32), + }; + struct scmi_clk_rate_set_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, + SCMI_CLOCK_RATE_SET, + in, out); + int ret; + + ret = devm_scmi_process_msg(clk->dev->parent, &msg); + if (ret < 0) + return ret; + + ret = scmi_to_linux_errno(out.status); + if (ret < 0) + return ret; + + return scmi_clk_get_rate(clk); +} + +static const struct clk_ops scmi_clk_ops = { + .enable = scmi_clk_enable, + .disable = scmi_clk_disable, + .get_rate = scmi_clk_get_rate, + .set_rate = scmi_clk_set_rate, +}; + +U_BOOT_DRIVER(scmi_clock) = { + .name = "scmi_clk", + .id = UCLASS_CLK, + .ops = &scmi_clk_ops, +}; diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 67a6f907c99..1f36f23b6d8 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -58,9 +58,9 @@ static int scmi_bind_protocols(struct udevice *dev) { int ret = 0; ofnode node; - struct driver *drv; dev_for_each_subnode(node, dev) { + struct driver *drv = NULL; u32 protocol_id; if (!ofnode_is_available(node)) @@ -70,9 +70,17 @@ static int scmi_bind_protocols(struct udevice *dev) continue; switch (protocol_id) { + case SCMI_PROTOCOL_ID_CLOCK: + if (IS_ENABLED(CONFIG_CLK_SCMI)) + drv = DM_GET_DRIVER(scmi_clock); + break; default: - dev_info(dev, "Ignore unsupported SCMI protocol %#x\n", - protocol_id); + break; + } + + if (!drv) { + dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n", + protocol_id); continue; } diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 86a2d109c8c..4778bcfc474 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -7,6 +7,7 @@ #define _SCMI_PROTOCOLS_H #include +#include /* * Subset the SCMI protocols definition @@ -38,4 +39,81 @@ enum scmi_status_code { SCMI_PROTOCOL_ERROR = -10, }; +/* + * SCMI Clock Protocol + */ + +enum scmi_clock_message_id { + SCMI_CLOCK_RATE_SET = 0x5, + SCMI_CLOCK_RATE_GET = 0x6, + SCMI_CLOCK_CONFIG_SET = 0x7, +}; + +#define SCMI_CLK_RATE_ASYNC_NOTIFY BIT(0) +#define SCMI_CLK_RATE_ASYNC_NORESP (BIT(0) | BIT(1)) +#define SCMI_CLK_RATE_ROUND_DOWN 0 +#define SCMI_CLK_RATE_ROUND_UP BIT(2) +#define SCMI_CLK_RATE_ROUND_CLOSEST BIT(3) + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_CONFIG_SET command + * @clock_id: SCMI clock ID + * @attributes: Attributes of the targets clock state + */ +struct scmi_clk_state_in { + u32 clock_id; + u32 attributes; +}; + +/** + * struct scmi_clk_state_out - Response payload for CLOCK_CONFIG_SET command + * @status: SCMI command status + */ +struct scmi_clk_state_out { + s32 status; +}; + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_RATE_GET command + * @clock_id: SCMI clock ID + * @attributes: Attributes of the targets clock state + */ +struct scmi_clk_rate_get_in { + u32 clock_id; +}; + +/** + * struct scmi_clk_rate_get_out - Response payload for CLOCK_RATE_GET command + * @status: SCMI command status + * @rate_lsb: 32bit LSB of the clock rate in Hertz + * @rate_msb: 32bit MSB of the clock rate in Hertz + */ +struct scmi_clk_rate_get_out { + s32 status; + u32 rate_lsb; + u32 rate_msb; +}; + +/** + * struct scmi_clk_state_in - Message payload for CLOCK_RATE_SET command + * @clock_id: SCMI clock ID + * @flags: Flags for the clock rate set request + * @rate_lsb: 32bit LSB of the clock rate in Hertz + * @rate_msb: 32bit MSB of the clock rate in Hertz + */ +struct scmi_clk_rate_set_in { + u32 clock_id; + u32 flags; + u32 rate_lsb; + u32 rate_msb; +}; + +/** + * struct scmi_clk_rate_set_out - Response payload for CLOCK_RATE_SET command + * @status: SCMI command status + */ +struct scmi_clk_rate_set_out { + s32 status; +}; + #endif /* _SCMI_PROTOCOLS_H */ -- cgit v1.3.1 From 87d4f277d4101c995e198ed3313da48690df5bb7 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:05 +0200 Subject: firmware: scmi: sandbox test for SCMI clocks Add tests for SCMI clocks. A test device driver sandbox-scmi_devices.c is used to get clock resources, allowing further clock manipulation. Change sandbox-smci_agent to emulate 3 clocks exposed through 2 agents. Add DM test scmi_clocks to test these 3 clocks. Update DM test sandbox_scmi_agent with load/remove test sequences factorized by {load|remove}_sandbox_scmi_test_devices() helper functions. Signed-off-by: Etienne Carriere Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- arch/sandbox/dts/test.dts | 15 +++ arch/sandbox/include/asm/scmi_test.h | 39 +++++++ configs/sandbox_defconfig | 1 + drivers/firmware/scmi/Makefile | 2 +- drivers/firmware/scmi/sandbox-scmi_agent.c | 169 ++++++++++++++++++++++++++- drivers/firmware/scmi/sandbox-scmi_devices.c | 75 ++++++++++++ test/dm/scmi.c | 141 +++++++++++++++++++++- 7 files changed, 431 insertions(+), 11 deletions(-) create mode 100644 drivers/firmware/scmi/sandbox-scmi_devices.c (limited to 'drivers/firmware') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 4769ec08669..5ed364ff03c 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -371,6 +371,11 @@ compatible = "sandbox,scmi-agent"; #address-cells = <1>; #size-cells = <0>; + + clk_scmi0: protocol@14 { + reg = <0x14>; + #clock-cells = <1>; + }; }; sandbox-scmi-agent@1 { @@ -378,6 +383,11 @@ #address-cells = <1>; #size-cells = <0>; + clk_scmi1: protocol@14 { + reg = <0x14>; + #clock-cells = <1>; + }; + protocol@10 { reg = <0x10>; }; @@ -1069,6 +1079,11 @@ compatible = "sandbox,virtio2"; }; + sandbox_scmi { + compatible = "sandbox,scmi-devices"; + clocks = <&clk_scmi0 7>, <&clk_scmi0 3>, <&clk_scmi1 1>; + }; + pinctrl { compatible = "sandbox,pinctrl"; diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h index a811fe19c35..63093fdb4d4 100644 --- a/arch/sandbox/include/asm/scmi_test.h +++ b/arch/sandbox/include/asm/scmi_test.h @@ -10,12 +10,28 @@ struct udevice; struct sandbox_scmi_agent; struct sandbox_scmi_service; +/** + * struct sandbox_scmi_clk - Simulated clock exposed by SCMI + * @id: Identifier of the clock used in the SCMI protocol + * @enabled: Clock state: true if enabled, false if disabled + * @rate: Clock rate in Hertz + */ +struct sandbox_scmi_clk { + uint id; + bool enabled; + ulong rate; +}; + /** * struct sandbox_scmi_agent - Simulated SCMI service seen by SCMI agent * @idx: Identifier for the SCMI agent, its index + * @clk: Simulated clocks + * @clk_count: Simulated clocks array size */ struct sandbox_scmi_agent { uint idx; + struct sandbox_scmi_clk *clk; + size_t clk_count; }; /** @@ -28,16 +44,39 @@ struct sandbox_scmi_service { size_t agent_count; }; +/** + * struct sandbox_scmi_devices - Reference to devices probed through SCMI + * @clk: Array the clock devices + * @clk_count: Number of clock devices probed + */ +struct sandbox_scmi_devices { + struct clk *clk; + size_t clk_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); + +/** + * sandbox_scmi_devices_get_ref - Get references to devices accessed through SCMI + * @dev: Reference to the test device used get test resources + * @return: Reference to the devices probed by the SCMI test + */ +struct sandbox_scmi_devices *sandbox_scmi_devices_ctx(struct udevice *dev); #else static inline struct sandbox_scmi_service *sandbox_scmi_service_ctx(void) { return NULL; } + +static inline +struct sandbox_scmi_devices *sandbox_scmi_devices_ctx(struct udevice *dev) +{ + return NULL; +} #endif /* CONFIG_SCMI_FIRMWARE */ #endif /* __SANDBOX_SCMI_TEST_H */ diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 2c130c01f07..7d71c805dc1 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -122,6 +122,7 @@ CONFIG_BUTTON=y CONFIG_BUTTON_GPIO=y CONFIG_CLK=y CONFIG_CLK_COMPOSITE_CCF=y +CONFIG_CLK_SCMI=y CONFIG_SANDBOX_CLK_CCF=y CONFIG_CPU=y CONFIG_DM_DEMO=y diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile index 2f782bbd55d..e1e02240664 100644 --- a/drivers/firmware/scmi/Makefile +++ b/drivers/firmware/scmi/Makefile @@ -2,4 +2,4 @@ obj-y += scmi_agent-uclass.o obj-y += smt.o obj-$(CONFIG_ARM_SMCCC) += smccc_agent.o obj-$(CONFIG_DM_MAILBOX) += mailbox_agent.o -obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o +obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o sandbox-scmi_devices.o diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c index 3179438aabf..ff590988a68 100644 --- a/drivers/firmware/scmi/sandbox-scmi_agent.c +++ b/drivers/firmware/scmi/sandbox-scmi_agent.c @@ -16,18 +16,34 @@ /* * 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. + * SCMI protocols embedded in U-Boot. Currently: + * - SCMI clock protocol: emulate 2 agents each exposing few clocks * - * This driver simulates 2 SCMI agents for test purpose. + * Agent #0 simulates 2 clocks. + * See IDs in scmi0_clk[] and "sandbox-scmi-agent@0" in test.dts. + * + * Agent #1 simulates 1 clock. + * See IDs in scmi1_clk[] and "sandbox-scmi-agent@1" in test.dts. + * + * All clocks are default disabled. * * 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. + * various uclass devices, currently only clock controllers. */ #define SANDBOX_SCMI_AGENT_COUNT 2 +static struct sandbox_scmi_clk scmi0_clk[] = { + { .id = 7, .rate = 1000 }, + { .id = 3, .rate = 333 }, +}; + +static struct sandbox_scmi_clk scmi1_clk[] = { + { .id = 1, .rate = 44 }, +}; + /* The list saves to simulted end devices references for test purpose */ struct sandbox_scmi_agent *sandbox_scmi_agent_list[SANDBOX_SCMI_AGENT_COUNT]; @@ -46,17 +62,158 @@ 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); + dev_dbg(dev, " scmi%u_clk (%zu): %d/%ld, %d/%ld, %d/%ld, ...\n", + agent->idx, + agent->clk_count, + agent->clk_count ? agent->clk[0].enabled : -1, + agent->clk_count ? agent->clk[0].rate : -1, + agent->clk_count > 1 ? agent->clk[1].enabled : -1, + agent->clk_count > 1 ? agent->clk[1].rate : -1, + agent->clk_count > 2 ? agent->clk[2].enabled : -1, + agent->clk_count > 2 ? agent->clk[2].rate : -1); }; +static struct sandbox_scmi_clk *get_scmi_clk_state(uint agent_id, uint clock_id) +{ + struct sandbox_scmi_clk *target = NULL; + size_t target_count = 0; + size_t n; + + switch (agent_id) { + case 0: + target = scmi0_clk; + target_count = ARRAY_SIZE(scmi0_clk); + break; + case 1: + target = scmi1_clk; + target_count = ARRAY_SIZE(scmi1_clk); + break; + default: + return NULL; + } + + for (n = 0; n < target_count; n++) + if (target[n].id == clock_id) + return target + n; + + return NULL; +} + +/* + * Sandbox SCMI agent ops + */ + +static int sandbox_scmi_clock_rate_set(struct udevice *dev, + struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_clk_rate_set_in *in = NULL; + struct scmi_clk_rate_set_out *out = NULL; + struct sandbox_scmi_clk *clk_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_clk_rate_set_in *)msg->in_msg; + out = (struct scmi_clk_rate_set_out *)msg->out_msg; + + clk_state = get_scmi_clk_state(agent->idx, in->clock_id); + if (!clk_state) { + dev_err(dev, "Unexpected clock ID %u\n", in->clock_id); + + out->status = SCMI_NOT_FOUND; + } else { + u64 rate = ((u64)in->rate_msb << 32) + in->rate_lsb; + + clk_state->rate = (ulong)rate; + + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_clock_rate_get(struct udevice *dev, + struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_clk_rate_get_in *in = NULL; + struct scmi_clk_rate_get_out *out = NULL; + struct sandbox_scmi_clk *clk_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_clk_rate_get_in *)msg->in_msg; + out = (struct scmi_clk_rate_get_out *)msg->out_msg; + + clk_state = get_scmi_clk_state(agent->idx, in->clock_id); + if (!clk_state) { + dev_err(dev, "Unexpected clock ID %u\n", in->clock_id); + + out->status = SCMI_NOT_FOUND; + } else { + out->rate_msb = (u32)((u64)clk_state->rate >> 32); + out->rate_lsb = (u32)clk_state->rate; + + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_clock_gate(struct udevice *dev, struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_clk_state_in *in = NULL; + struct scmi_clk_state_out *out = NULL; + struct sandbox_scmi_clk *clk_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_clk_state_in *)msg->in_msg; + out = (struct scmi_clk_state_out *)msg->out_msg; + + clk_state = get_scmi_clk_state(agent->idx, in->clock_id); + if (!clk_state) { + dev_err(dev, "Unexpected clock ID %u\n", in->clock_id); + + out->status = SCMI_NOT_FOUND; + } else if (in->attributes > 1) { + out->status = SCMI_PROTOCOL_ERROR; + } else { + clk_state->enabled = in->attributes; + + out->status = SCMI_SUCCESS; + } + + return 0; +} + static int sandbox_scmi_test_process_msg(struct udevice *dev, struct scmi_msg *msg) { switch (msg->protocol_id) { + case SCMI_PROTOCOL_ID_CLOCK: + switch (msg->message_id) { + case SCMI_CLOCK_RATE_SET: + return sandbox_scmi_clock_rate_set(dev, msg); + case SCMI_CLOCK_RATE_GET: + return sandbox_scmi_clock_rate_get(dev, msg); + case SCMI_CLOCK_CONFIG_SET: + return sandbox_scmi_clock_gate(dev, msg); + default: + break; + } + break; 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; @@ -101,11 +258,15 @@ static int sandbox_scmi_test_probe(struct udevice *dev) case '0': *agent = (struct sandbox_scmi_agent){ .idx = 0, + .clk = scmi0_clk, + .clk_count = ARRAY_SIZE(scmi0_clk), }; break; case '1': *agent = (struct sandbox_scmi_agent){ .idx = 1, + .clk = scmi1_clk, + .clk_count = ARRAY_SIZE(scmi1_clk), }; break; default: diff --git a/drivers/firmware/scmi/sandbox-scmi_devices.c b/drivers/firmware/scmi/sandbox-scmi_devices.c new file mode 100644 index 00000000000..b3e411c5ac7 --- /dev/null +++ b/drivers/firmware/scmi/sandbox-scmi_devices.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Simulate to some extent a SCMI exchange. + * This drivers gets SCMI resources and offers API function to the + * SCMI test sequence manipulate the resources, currently clocks. + */ + +#define SCMI_TEST_DEVICES_CLK_COUNT 3 + +/* + * struct sandbox_scmi_device_priv - Storage for device handles used by test + * @clk: Array of clock instances used by tests + * @devices: Resources exposed by sandbox_scmi_devices_ctx() + */ +struct sandbox_scmi_device_priv { + struct clk clk[SCMI_TEST_DEVICES_CLK_COUNT]; + struct sandbox_scmi_devices devices; +}; + +struct sandbox_scmi_devices *sandbox_scmi_devices_ctx(struct udevice *dev) +{ + struct sandbox_scmi_device_priv *priv = dev_get_priv(dev); + + if (priv) + return &priv->devices; + + return NULL; +} + +static int sandbox_scmi_devices_probe(struct udevice *dev) +{ + struct sandbox_scmi_device_priv *priv = dev_get_priv(dev); + int ret; + size_t n; + + priv->devices = (struct sandbox_scmi_devices){ + .clk = priv->clk, + .clk_count = SCMI_TEST_DEVICES_CLK_COUNT, + }; + + for (n = 0; n < SCMI_TEST_DEVICES_CLK_COUNT; n++) { + ret = clk_get_by_index(dev, n, priv->devices.clk + n); + if (ret) { + dev_err(dev, "%s: Failed on clk %zu\n", __func__, n); + return ret; + } + } + + return 0; +} + +static const struct udevice_id sandbox_scmi_devices_ids[] = { + { .compatible = "sandbox,scmi-devices" }, + { } +}; + +U_BOOT_DRIVER(sandbox_scmi_devices) = { + .name = "sandbox-scmi_devices", + .id = UCLASS_MISC, + .of_match = sandbox_scmi_devices_ids, + .priv_auto_alloc_size = sizeof(struct sandbox_scmi_device_priv), + .probe = sandbox_scmi_devices_probe, +}; diff --git a/test/dm/scmi.c b/test/dm/scmi.c index d8c1e71f127..aa46f31a475 100644 --- a/test/dm/scmi.c +++ b/test/dm/scmi.c @@ -13,26 +13,155 @@ */ #include +#include #include #include #include #include +#include #include +static int ut_assert_scmi_state_preprobe(struct unit_test_state *uts) +{ + struct sandbox_scmi_service *scmi_ctx = sandbox_scmi_service_ctx(); + + ut_assertnonnull(scmi_ctx); + if (scmi_ctx->agent_count) + ut_asserteq(2, scmi_ctx->agent_count); + + return 0; +} + +static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts, + struct udevice *dev) +{ + struct sandbox_scmi_devices *scmi_devices; + struct sandbox_scmi_service *scmi_ctx; + + /* Device references to check context against test sequence */ + scmi_devices = sandbox_scmi_devices_ctx(dev); + + ut_assertnonnull(scmi_devices); + if (IS_ENABLED(CONFIG_CLK_SCMI)) + ut_asserteq(3, scmi_devices->clk_count); + + /* State of the simulated SCMI server exposed */ + scmi_ctx = sandbox_scmi_service_ctx(); + + ut_asserteq(2, scmi_ctx->agent_count); + + ut_assertnonnull(scmi_ctx->agent[0]); + ut_asserteq(2, scmi_ctx->agent[0]->clk_count); + ut_assertnonnull(scmi_ctx->agent[0]->clk); + + ut_assertnonnull(scmi_ctx->agent[1]); + ut_assertnonnull(scmi_ctx->agent[1]->clk); + ut_asserteq(1, scmi_ctx->agent[1]->clk_count); + + return 0; +} + +static int load_sandbox_scmi_test_devices(struct unit_test_state *uts, + struct udevice **dev) +{ + int ret; + + ret = ut_assert_scmi_state_preprobe(uts); + if (ret) + return ret; + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "sandbox_scmi", + dev)); + ut_assertnonnull(*dev); + + return ut_assert_scmi_state_postprobe(uts, *dev); +} + +static int release_sandbox_scmi_test_devices(struct unit_test_state *uts, + struct udevice *dev) +{ + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + + /* Not sure test devices are fully removed, agent may not be visible */ + return 0; +} + /* * 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(); + struct udevice *dev = NULL; + int ret; - ut_assertnonnull(scmi_ctx); - ut_asserteq(2, scmi_ctx->agent_count); - ut_assertnull(scmi_ctx->agent[0]); - ut_assertnull(scmi_ctx->agent[1]); + ret = load_sandbox_scmi_test_devices(uts, &dev); + if (!ret) + ret = release_sandbox_scmi_test_devices(uts, dev); - return 0; + return ret; } DM_TEST(dm_test_scmi_sandbox_agent, UT_TESTF_SCAN_FDT); + +static int dm_test_scmi_clocks(struct unit_test_state *uts) +{ + struct sandbox_scmi_devices *scmi_devices; + struct sandbox_scmi_service *scmi_ctx; + struct udevice *dev = NULL; + int ret_dev; + int ret; + + if (!IS_ENABLED(CONFIG_CLK_SCMI)) + return 0; + + ret = load_sandbox_scmi_test_devices(uts, &dev); + if (ret) + return ret; + + scmi_devices = sandbox_scmi_devices_ctx(dev); + scmi_ctx = sandbox_scmi_service_ctx(); + + /* Test SCMI clocks rate manipulation */ + ut_asserteq(1000, clk_get_rate(&scmi_devices->clk[0])); + ut_asserteq(333, clk_get_rate(&scmi_devices->clk[1])); + ut_asserteq(44, clk_get_rate(&scmi_devices->clk[2])); + + ret_dev = clk_set_rate(&scmi_devices->clk[1], 1088); + ut_assert(!ret_dev || ret_dev == 1088); + + ut_asserteq(1000, scmi_ctx->agent[0]->clk[0].rate); + ut_asserteq(1088, scmi_ctx->agent[0]->clk[1].rate); + ut_asserteq(44, scmi_ctx->agent[1]->clk[0].rate); + + ut_asserteq(1000, clk_get_rate(&scmi_devices->clk[0])); + ut_asserteq(1088, clk_get_rate(&scmi_devices->clk[1])); + ut_asserteq(44, clk_get_rate(&scmi_devices->clk[2])); + + /* restore original rate for further tests */ + ret_dev = clk_set_rate(&scmi_devices->clk[1], 333); + ut_assert(!ret_dev || ret_dev == 333); + + /* Test SCMI clocks gating manipulation */ + ut_assert(!scmi_ctx->agent[0]->clk[0].enabled); + ut_assert(!scmi_ctx->agent[0]->clk[1].enabled); + ut_assert(!scmi_ctx->agent[1]->clk[0].enabled); + + ut_asserteq(0, clk_enable(&scmi_devices->clk[1])); + ut_asserteq(0, clk_enable(&scmi_devices->clk[2])); + + ut_assert(!scmi_ctx->agent[0]->clk[0].enabled); + ut_assert(scmi_ctx->agent[0]->clk[1].enabled); + ut_assert(scmi_ctx->agent[1]->clk[0].enabled); + + ut_assertok(clk_disable(&scmi_devices->clk[1])); + ut_assertok(clk_disable(&scmi_devices->clk[2])); + + ut_assert(!scmi_ctx->agent[0]->clk[0].enabled); + ut_assert(!scmi_ctx->agent[0]->clk[1].enabled); + ut_assert(!scmi_ctx->agent[1]->clk[0].enabled); + + return release_sandbox_scmi_test_devices(uts, dev); +} + +DM_TEST(dm_test_scmi_clocks, UT_TESTF_SCAN_FDT); -- cgit v1.3.1 From 34d76fefb2667d0ca138ff4fcf8bc8443032449f Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:06 +0200 Subject: reset: add reset controller driver for SCMI agents This change introduces a reset controller driver for SCMI agent devices. When SCMI agent and SCMI reset domain drivers are enabled, SCMI agent binds a reset controller device for each SCMI reset domain protocol devices enabled in the FDT. SCMI reset driver is embedded upon CONFIG_RESET_SCMI=y. If enabled, CONFIG_SCMI_AGENT is also enabled. SCMI Reset Domain protocol is defined in the SCMI specification [1]. Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi Signed-off-by: Etienne Carriere Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- drivers/firmware/scmi/scmi_agent-uclass.c | 4 ++ drivers/reset/Kconfig | 8 +++ drivers/reset/Makefile | 1 + drivers/reset/reset-scmi.c | 81 +++++++++++++++++++++++++++++++ include/scmi_protocols.h | 60 +++++++++++++++++++++++ 5 files changed, 154 insertions(+) create mode 100644 drivers/reset/reset-scmi.c (limited to 'drivers/firmware') diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 1f36f23b6d8..77160b19995 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -74,6 +74,10 @@ static int scmi_bind_protocols(struct udevice *dev) if (IS_ENABLED(CONFIG_CLK_SCMI)) drv = DM_GET_DRIVER(scmi_clock); break; + case SCMI_PROTOCOL_ID_RESET_DOMAIN: + if (IS_ENABLED(CONFIG_RESET_SCMI)) + drv = DM_GET_DRIVER(scmi_reset_domain); + break; default: break; } diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 3fdfe4a6cb0..b60e11f98b2 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -181,4 +181,12 @@ config RESET_RASPBERRYPI relevant. This driver provides a reset controller capable of interfacing with RPi4's co-processor and model these firmware initialization routines as reset lines. + +config RESET_SCMI + bool "Enable SCMI reset domain driver" + select SCMI_FIRMWARE + help + Enable this option if you want to support reset controller + devices exposed by a SCMI agent based on SCMI reset domain + protocol communication with a SCMI server. endmenu diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 5176da58853..10a7973f823 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -27,3 +27,4 @@ obj-$(CONFIG_RESET_IPQ419) += reset-ipq4019.o obj-$(CONFIG_RESET_SIFIVE) += reset-sifive.o obj-$(CONFIG_RESET_SYSCON) += reset-syscon.o obj-$(CONFIG_RESET_RASPBERRYPI) += reset-raspberrypi.o +obj-$(CONFIG_RESET_SCMI) += reset-scmi.o diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c new file mode 100644 index 00000000000..1bff8075ee3 --- /dev/null +++ b/drivers/reset/reset-scmi.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019-2020 Linaro Limited + */ +#include +#include +#include +#include +#include +#include +#include + +static int scmi_reset_set_level(struct reset_ctl *rst, bool assert_not_deassert) +{ + struct scmi_rd_reset_in in = { + .domain_id = rst->id, + .flags = assert_not_deassert ? SCMI_RD_RESET_FLAG_ASSERT : 0, + .reset_state = 0, + }; + struct scmi_rd_reset_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_RESET_DOMAIN, + SCMI_RESET_DOMAIN_RESET, + in, out); + int ret; + + ret = devm_scmi_process_msg(rst->dev->parent, &msg); + if (ret) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_reset_assert(struct reset_ctl *rst) +{ + return scmi_reset_set_level(rst, true); +} + +static int scmi_reset_deassert(struct reset_ctl *rst) +{ + return scmi_reset_set_level(rst, false); +} + +static int scmi_reset_request(struct reset_ctl *rst) +{ + struct scmi_rd_attr_in in = { + .domain_id = rst->id, + }; + struct scmi_rd_attr_out out; + struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_RESET_DOMAIN, + SCMI_RESET_DOMAIN_ATTRIBUTES, + in, out); + int ret; + + /* + * We don't really care about the attribute, just check + * the reset domain exists. + */ + ret = devm_scmi_process_msg(rst->dev->parent, &msg); + if (ret) + return ret; + + return scmi_to_linux_errno(out.status); +} + +static int scmi_reset_rfree(struct reset_ctl *rst) +{ + return 0; +} + +static const struct reset_ops scmi_reset_domain_ops = { + .request = scmi_reset_request, + .rfree = scmi_reset_rfree, + .rst_assert = scmi_reset_assert, + .rst_deassert = scmi_reset_deassert, +}; + +U_BOOT_DRIVER(scmi_reset_domain) = { + .name = "scmi_reset_domain", + .id = UCLASS_RESET, + .ops = &scmi_reset_domain_ops, +}; diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 4778bcfc474..ccab97c96c9 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -116,4 +116,64 @@ struct scmi_clk_rate_set_out { s32 status; }; +/* + * SCMI Reset Domain Protocol + */ + +enum scmi_reset_domain_message_id { + SCMI_RESET_DOMAIN_ATTRIBUTES = 0x3, + SCMI_RESET_DOMAIN_RESET = 0x4, +}; + +#define SCMI_RD_NAME_LEN 16 + +#define SCMI_RD_ATTRIBUTES_FLAG_ASYNC BIT(31) +#define SCMI_RD_ATTRIBUTES_FLAG_NOTIF BIT(30) + +#define SCMI_RD_RESET_FLAG_ASYNC BIT(2) +#define SCMI_RD_RESET_FLAG_ASSERT BIT(1) +#define SCMI_RD_RESET_FLAG_CYCLE BIT(0) + +/** + * struct scmi_rd_attr_in - Payload for RESET_DOMAIN_ATTRIBUTES message + * @domain_id: SCMI reset domain ID + */ +struct scmi_rd_attr_in { + u32 domain_id; +}; + +/** + * struct scmi_rd_attr_out - Payload for RESET_DOMAIN_ATTRIBUTES response + * @status: SCMI command status + * @attributes: Retrieved attributes of the reset domain + * @latency: Reset cycle max lantency + * @name: Reset domain name + */ +struct scmi_rd_attr_out { + s32 status; + u32 attributes; + u32 latency; + char name[SCMI_RD_NAME_LEN]; +}; + +/** + * struct scmi_rd_reset_in - Message payload for RESET command + * @domain_id: SCMI reset domain ID + * @flags: Flags for the reset request + * @reset_state: Reset target state + */ +struct scmi_rd_reset_in { + u32 domain_id; + u32 flags; + u32 reset_state; +}; + +/** + * struct scmi_rd_reset_out - Response payload for RESET command + * @status: SCMI command status + */ +struct scmi_rd_reset_out { + s32 status; +}; + #endif /* _SCMI_PROTOCOLS_H */ -- cgit v1.3.1 From c0dd177a9986b97dab42f07b3db0ed1d2fe6e540 Mon Sep 17 00:00:00 2001 From: Etienne Carriere Date: Wed, 9 Sep 2020 18:44:07 +0200 Subject: firmware: smci: sandbox test for SCMI reset controllers Add tests for SCMI reset controllers. A test device driver sandbox-scmi_devices.c is used to get reset resources, allowing further resets manipulation. Change sandbox-smci_agent to emulate 1 reset controller exposed through an agent. Add DM test scmi_resets to test this reset controller. Signed-off-by: Etienne Carriere Cc: Simon Glass Cc: Peng Fan Cc: Sudeep Holla Reviewed-by: Simon Glass --- arch/sandbox/dts/test.dts | 6 ++ arch/sandbox/include/asm/scmi_test.h | 17 ++++ configs/sandbox_defconfig | 1 + drivers/firmware/scmi/sandbox-scmi_agent.c | 117 +++++++++++++++++++++++++-- drivers/firmware/scmi/sandbox-scmi_devices.c | 40 ++++++++- test/dm/scmi.c | 36 +++++++++ 6 files changed, 211 insertions(+), 6 deletions(-) (limited to 'drivers/firmware') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 5ed364ff03c..e6da47d5928 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -376,6 +376,11 @@ reg = <0x14>; #clock-cells = <1>; }; + + reset_scmi0: protocol@16 { + reg = <0x16>; + #reset-cells = <1>; + }; }; sandbox-scmi-agent@1 { @@ -1082,6 +1087,7 @@ sandbox_scmi { compatible = "sandbox,scmi-devices"; clocks = <&clk_scmi0 7>, <&clk_scmi0 3>, <&clk_scmi1 1>; + resets = <&reset_scmi0 3>; }; pinctrl { diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h index 63093fdb4d4..3e8b0068fd4 100644 --- a/arch/sandbox/include/asm/scmi_test.h +++ b/arch/sandbox/include/asm/scmi_test.h @@ -22,16 +22,29 @@ struct sandbox_scmi_clk { ulong rate; }; +/** + * struct sandbox_scmi_reset - Simulated reset controller exposed by SCMI + * @asserted: Reset control state: true if asserted, false if desasserted + */ +struct sandbox_scmi_reset { + uint id; + bool asserted; +}; + /** * struct sandbox_scmi_agent - Simulated SCMI service seen by SCMI agent * @idx: Identifier for the SCMI agent, its index * @clk: Simulated clocks * @clk_count: Simulated clocks array size + * @clk: Simulated reset domains + * @clk_count: Simulated reset domains array size */ struct sandbox_scmi_agent { uint idx; struct sandbox_scmi_clk *clk; size_t clk_count; + struct sandbox_scmi_reset *reset; + size_t reset_count; }; /** @@ -48,10 +61,14 @@ struct sandbox_scmi_service { * struct sandbox_scmi_devices - Reference to devices probed through SCMI * @clk: Array the clock devices * @clk_count: Number of clock devices probed + * @reset: Array the reset controller devices + * @reset_count: Number of reset controller devices probed */ struct sandbox_scmi_devices { struct clk *clk; size_t clk_count; + struct reset_ctl *reset; + size_t reset_count; }; #ifdef CONFIG_SCMI_FIRMWARE diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 7d71c805dc1..a2ebb3c971d 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -220,6 +220,7 @@ CONFIG_REMOTEPROC_SANDBOX=y CONFIG_DM_RESET=y CONFIG_SANDBOX_RESET=y CONFIG_RESET_SYSCON=y +CONFIG_RESET_SCMI=y CONFIG_DM_RNG=y CONFIG_DM_RTC=y CONFIG_RTC_RV8803=y diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c index ff590988a68..5b6a4232af4 100644 --- a/drivers/firmware/scmi/sandbox-scmi_agent.c +++ b/drivers/firmware/scmi/sandbox-scmi_agent.c @@ -18,19 +18,20 @@ * processing. It simulates few of the SCMI services for some of the * SCMI protocols embedded in U-Boot. Currently: * - SCMI clock protocol: emulate 2 agents each exposing few clocks + * - SCMI reset protocol: emulate 1 agents each exposing a reset * - * Agent #0 simulates 2 clocks. - * See IDs in scmi0_clk[] and "sandbox-scmi-agent@0" in test.dts. + * Agent #0 simulates 2 clocks and 1 reset domain. + * See IDs in scmi0_clk[]/scmi0_reset[] and "sandbox-scmi-agent@0" in test.dts. * * Agent #1 simulates 1 clock. * See IDs in scmi1_clk[] and "sandbox-scmi-agent@1" in test.dts. * - * All clocks are default disabled. + * All clocks are default disabled and reset levels down. * * 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 only clock controllers. + * various uclass devices, as clocks and reset controllers. */ #define SANDBOX_SCMI_AGENT_COUNT 2 @@ -40,6 +41,10 @@ static struct sandbox_scmi_clk scmi0_clk[] = { { .id = 3, .rate = 333 }, }; +static struct sandbox_scmi_reset scmi0_reset[] = { + { .id = 3 }, +}; + static struct sandbox_scmi_clk scmi1_clk[] = { { .id = 1, .rate = 44 }, }; @@ -71,6 +76,11 @@ static void debug_print_agent_state(struct udevice *dev, char *str) agent->clk_count > 1 ? agent->clk[1].rate : -1, agent->clk_count > 2 ? agent->clk[2].enabled : -1, agent->clk_count > 2 ? agent->clk[2].rate : -1); + dev_dbg(dev, " scmi%u_reset (%zu): %d, %d, ...\n", + agent->idx, + agent->reset_count, + agent->reset_count ? agent->reset[0].asserted : -1, + agent->reset_count > 1 ? agent->reset[1].asserted : -1); }; static struct sandbox_scmi_clk *get_scmi_clk_state(uint agent_id, uint clock_id) @@ -99,6 +109,20 @@ static struct sandbox_scmi_clk *get_scmi_clk_state(uint agent_id, uint clock_id) return NULL; } +static struct sandbox_scmi_reset *get_scmi_reset_state(uint agent_id, + uint reset_id) +{ + size_t n; + + if (agent_id == 0) { + for (n = 0; n < ARRAY_SIZE(scmi0_reset); n++) + if (scmi0_reset[n].id == reset_id) + return scmi0_reset + n; + } + + return NULL; +} + /* * Sandbox SCMI agent ops */ @@ -194,6 +218,78 @@ static int sandbox_scmi_clock_gate(struct udevice *dev, struct scmi_msg *msg) return 0; } +static int sandbox_scmi_rd_attribs(struct udevice *dev, struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_rd_attr_in *in = NULL; + struct scmi_rd_attr_out *out = NULL; + struct sandbox_scmi_reset *reset_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_rd_attr_in *)msg->in_msg; + out = (struct scmi_rd_attr_out *)msg->out_msg; + + reset_state = get_scmi_reset_state(agent->idx, in->domain_id); + if (!reset_state) { + dev_err(dev, "Unexpected reset domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else { + memset(out, 0, sizeof(*out)); + snprintf(out->name, sizeof(out->name), "rd%u", in->domain_id); + + out->status = SCMI_SUCCESS; + } + + return 0; +} + +static int sandbox_scmi_rd_reset(struct udevice *dev, struct scmi_msg *msg) +{ + struct sandbox_scmi_agent *agent = dev_get_priv(dev); + struct scmi_rd_reset_in *in = NULL; + struct scmi_rd_reset_out *out = NULL; + struct sandbox_scmi_reset *reset_state = NULL; + + if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) || + !msg->out_msg || msg->out_msg_sz < sizeof(*out)) + return -EINVAL; + + in = (struct scmi_rd_reset_in *)msg->in_msg; + out = (struct scmi_rd_reset_out *)msg->out_msg; + + reset_state = get_scmi_reset_state(agent->idx, in->domain_id); + if (!reset_state) { + dev_err(dev, "Unexpected reset domain ID %u\n", in->domain_id); + + out->status = SCMI_NOT_FOUND; + } else if (in->reset_state > 1) { + dev_err(dev, "Invalid reset domain input attribute value\n"); + + out->status = SCMI_INVALID_PARAMETERS; + } else { + if (in->flags & SCMI_RD_RESET_FLAG_CYCLE) { + if (in->flags & SCMI_RD_RESET_FLAG_ASYNC) { + out->status = SCMI_NOT_SUPPORTED; + } else { + /* Ends deasserted whatever current state */ + reset_state->asserted = false; + out->status = SCMI_SUCCESS; + } + } else { + reset_state->asserted = in->flags & + SCMI_RD_RESET_FLAG_ASSERT; + + out->status = SCMI_SUCCESS; + } + } + + return 0; +} + static int sandbox_scmi_test_process_msg(struct udevice *dev, struct scmi_msg *msg) { @@ -210,12 +306,21 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev, break; } break; + case SCMI_PROTOCOL_ID_RESET_DOMAIN: + switch (msg->message_id) { + case SCMI_RESET_DOMAIN_ATTRIBUTES: + return sandbox_scmi_rd_attribs(dev, msg); + case SCMI_RESET_DOMAIN_RESET: + return sandbox_scmi_rd_reset(dev, msg); + default: + break; + } + break; 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_SENSOR: - case SCMI_PROTOCOL_ID_RESET_DOMAIN: *(u32 *)msg->out_msg = SCMI_NOT_SUPPORTED; return 0; default: @@ -260,6 +365,8 @@ static int sandbox_scmi_test_probe(struct udevice *dev) .idx = 0, .clk = scmi0_clk, .clk_count = ARRAY_SIZE(scmi0_clk), + .reset = scmi0_reset, + .reset_count = ARRAY_SIZE(scmi0_reset), }; break; case '1': diff --git a/drivers/firmware/scmi/sandbox-scmi_devices.c b/drivers/firmware/scmi/sandbox-scmi_devices.c index b3e411c5ac7..c69967bf693 100644 --- a/drivers/firmware/scmi/sandbox-scmi_devices.c +++ b/drivers/firmware/scmi/sandbox-scmi_devices.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -14,18 +15,22 @@ /* * Simulate to some extent a SCMI exchange. * This drivers gets SCMI resources and offers API function to the - * SCMI test sequence manipulate the resources, currently clocks. + * SCMI test sequence manipulate the resources, currently clock + * and reset controllers. */ #define SCMI_TEST_DEVICES_CLK_COUNT 3 +#define SCMI_TEST_DEVICES_RD_COUNT 1 /* * struct sandbox_scmi_device_priv - Storage for device handles used by test * @clk: Array of clock instances used by tests + * @reset_clt: Array of the reset controller instances used by tests * @devices: Resources exposed by sandbox_scmi_devices_ctx() */ struct sandbox_scmi_device_priv { struct clk clk[SCMI_TEST_DEVICES_CLK_COUNT]; + struct reset_ctl reset_ctl[SCMI_TEST_DEVICES_RD_COUNT]; struct sandbox_scmi_devices devices; }; @@ -39,6 +44,22 @@ struct sandbox_scmi_devices *sandbox_scmi_devices_ctx(struct udevice *dev) return NULL; } +static int sandbox_scmi_devices_remove(struct udevice *dev) +{ + struct sandbox_scmi_devices *devices = sandbox_scmi_devices_ctx(dev); + int ret = 0; + size_t n; + + for (n = 0; n < SCMI_TEST_DEVICES_RD_COUNT; n++) { + int ret2 = reset_free(devices->reset + n); + + if (ret2 && !ret) + ret = ret2; + } + + return ret; +} + static int sandbox_scmi_devices_probe(struct udevice *dev) { struct sandbox_scmi_device_priv *priv = dev_get_priv(dev); @@ -48,6 +69,8 @@ static int sandbox_scmi_devices_probe(struct udevice *dev) priv->devices = (struct sandbox_scmi_devices){ .clk = priv->clk, .clk_count = SCMI_TEST_DEVICES_CLK_COUNT, + .reset = priv->reset_ctl, + .reset_count = SCMI_TEST_DEVICES_RD_COUNT, }; for (n = 0; n < SCMI_TEST_DEVICES_CLK_COUNT; n++) { @@ -58,7 +81,21 @@ static int sandbox_scmi_devices_probe(struct udevice *dev) } } + for (n = 0; n < SCMI_TEST_DEVICES_RD_COUNT; n++) { + ret = reset_get_by_index(dev, n, priv->devices.reset + n); + if (ret) { + dev_err(dev, "%s: Failed on reset %zu\n", __func__, n); + goto err_reset; + } + } + return 0; + +err_reset: + for (; n > 0; n--) + reset_free(priv->devices.reset + n - 1); + + return ret; } static const struct udevice_id sandbox_scmi_devices_ids[] = { @@ -71,5 +108,6 @@ U_BOOT_DRIVER(sandbox_scmi_devices) = { .id = UCLASS_MISC, .of_match = sandbox_scmi_devices_ids, .priv_auto_alloc_size = sizeof(struct sandbox_scmi_device_priv), + .remove = sandbox_scmi_devices_remove, .probe = sandbox_scmi_devices_probe, }; diff --git a/test/dm/scmi.c b/test/dm/scmi.c index aa46f31a475..be60b44b3b0 100644 --- a/test/dm/scmi.c +++ b/test/dm/scmi.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,8 @@ static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts, ut_assertnonnull(scmi_devices); if (IS_ENABLED(CONFIG_CLK_SCMI)) ut_asserteq(3, scmi_devices->clk_count); + if (IS_ENABLED(CONFIG_RESET_SCMI)) + ut_asserteq(1, scmi_devices->reset_count); /* State of the simulated SCMI server exposed */ scmi_ctx = sandbox_scmi_service_ctx(); @@ -53,6 +56,8 @@ static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts, ut_assertnonnull(scmi_ctx->agent[0]); ut_asserteq(2, scmi_ctx->agent[0]->clk_count); ut_assertnonnull(scmi_ctx->agent[0]->clk); + ut_asserteq(1, scmi_ctx->agent[0]->reset_count); + ut_assertnonnull(scmi_ctx->agent[0]->reset); ut_assertnonnull(scmi_ctx->agent[1]); ut_assertnonnull(scmi_ctx->agent[1]->clk); @@ -165,3 +170,34 @@ static int dm_test_scmi_clocks(struct unit_test_state *uts) } DM_TEST(dm_test_scmi_clocks, UT_TESTF_SCAN_FDT); + +static int dm_test_scmi_resets(struct unit_test_state *uts) +{ + struct sandbox_scmi_devices *scmi_devices; + struct sandbox_scmi_service *scmi_ctx; + struct udevice *dev = NULL; + int ret; + + if (!IS_ENABLED(CONFIG_RESET_SCMI)) + return 0; + + ret = load_sandbox_scmi_test_devices(uts, &dev); + if (ret) + return ret; + + scmi_devices = sandbox_scmi_devices_ctx(dev); + scmi_ctx = sandbox_scmi_service_ctx(); + + /* Test SCMI resect controller manipulation */ + ut_assert(!scmi_ctx->agent[0]->reset[0].asserted) + + ut_assertok(reset_assert(&scmi_devices->reset[0])); + ut_assert(scmi_ctx->agent[0]->reset[0].asserted) + + ut_assertok(reset_deassert(&scmi_devices->reset[0])); + ut_assert(!scmi_ctx->agent[0]->reset[0].asserted); + + return release_sandbox_scmi_test_devices(uts, dev); +} + +DM_TEST(dm_test_scmi_resets, UT_TESTF_SCAN_FDT); -- cgit v1.3.1