Import the Linux v5.13 state of the SCMI reset protocol driver. Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx> --- drivers/firmware/arm_scmi/Makefile | 2 +- drivers/firmware/arm_scmi/common.h | 1 + drivers/firmware/arm_scmi/driver.c | 2 + drivers/firmware/arm_scmi/reset.c | 229 +++++++++++++++++++++++++++++ drivers/reset/Kconfig | 11 ++ drivers/reset/Makefile | 1 + drivers/reset/reset-scmi.c | 130 ++++++++++++++++ 7 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_scmi/reset.c create mode 100644 drivers/reset/reset-scmi.c diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 8bb25484b9b2..510c0372a096 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -3,7 +3,7 @@ scmi-bus-y = bus.o scmi-driver-y = driver.o scmi-transport-y = shmem.o scmi-transport-$(CONFIG_ARM_SMCCC) += smc.o -scmi-protocols-y = base.o +scmi-protocols-y = base.o reset.o scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \ $(scmi-transport-y) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 9e48c0e7747d..f1f6cdae5af2 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -232,6 +232,7 @@ void __exit scmi_bus_exit(void); #define DECLARE_SCMI_REGISTER(func) \ int __init scmi_##func##_register(void); DECLARE_SCMI_REGISTER(base); +DECLARE_SCMI_REGISTER(reset); #define DEFINE_SCMI_PROTOCOL_REGISTER(name, proto) \ static const struct scmi_protocol *__this_proto = &(proto); \ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index c9819e7c2aa3..d862e4de8afb 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -1259,6 +1259,8 @@ static int __init scmi_bus_driver_init(void) scmi_base_register(); + scmi_reset_register(); + return 0; } pure_initcall(scmi_bus_driver_init); diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c new file mode 100644 index 000000000000..94baab99e1e4 --- /dev/null +++ b/drivers/firmware/arm_scmi/reset.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Reset Protocol + * + * Copyright (C) 2019-2021 ARM Ltd. + */ + +#define pr_fmt(fmt) "SCMI RESET - " fmt + +#include <common.h> +#include <linux/scmi_protocol.h> + +#include "common.h" + +enum scmi_reset_protocol_cmd { + RESET_DOMAIN_ATTRIBUTES = 0x3, + RESET = 0x4, + RESET_NOTIFY = 0x5, +}; + +#define NUM_RESET_DOMAIN_MASK 0xffff + +struct scmi_msg_resp_reset_domain_attributes { + __le32 attributes; +#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31)) +#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30)) + __le32 latency; + u8 name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_msg_reset_domain_reset { + __le32 domain_id; + __le32 flags; +#define AUTONOMOUS_RESET BIT(0) +#define EXPLICIT_RESET_ASSERT BIT(1) +#define ASYNCHRONOUS_RESET BIT(2) + __le32 reset_state; +#define ARCH_COLD_RESET 0 +}; + +struct reset_dom_info { + bool async_reset; + u32 latency_us; + char name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_reset_info { + u32 version; + int num_domains; + struct reset_dom_info *dom_info; +}; + +static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph, + struct scmi_reset_info *pi) +{ + int ret; + struct scmi_xfer *t; + u32 attr; + + ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, + 0, sizeof(attr), &t); + if (ret) + return ret; + + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + attr = get_unaligned_le32(t->rx.buf); + pi->num_domains = attr & NUM_RESET_DOMAIN_MASK; + } + + ph->xops->xfer_put(ph, t); + return ret; +} + +static int +scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, + u32 domain, struct reset_dom_info *dom_info) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_resp_reset_domain_attributes *attr; + + ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES, + sizeof(domain), sizeof(*attr), &t); + if (ret) + return ret; + + put_unaligned_le32(domain, t->tx.buf); + attr = t->rx.buf; + + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + u32 attributes = le32_to_cpu(attr->attributes); + + dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes); + dom_info->latency_us = le32_to_cpu(attr->latency); + if (dom_info->latency_us == U32_MAX) + dom_info->latency_us = 0; + strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE); + } + + ph->xops->xfer_put(ph, t); + return ret; +} + +static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph) +{ + struct scmi_reset_info *pi = ph->get_priv(ph); + + return pi->num_domains; +} + +static char *scmi_reset_name_get(const struct scmi_protocol_handle *ph, + u32 domain) +{ + struct scmi_reset_info *pi = ph->get_priv(ph); + + struct reset_dom_info *dom = pi->dom_info + domain; + + return dom->name; +} + +static int scmi_reset_latency_get(const struct scmi_protocol_handle *ph, + u32 domain) +{ + struct scmi_reset_info *pi = ph->get_priv(ph); + struct reset_dom_info *dom = pi->dom_info + domain; + + return dom->latency_us; +} + +static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain, + u32 flags, u32 state) +{ + int ret; + struct scmi_xfer *t; + struct scmi_msg_reset_domain_reset *dom; + struct scmi_reset_info *pi = ph->get_priv(ph); + struct reset_dom_info *rdom = pi->dom_info + domain; + + if (rdom->async_reset) + flags |= ASYNCHRONOUS_RESET; + + ret = ph->xops->xfer_get_init(ph, RESET, sizeof(*dom), 0, &t); + if (ret) + return ret; + + dom = t->tx.buf; + dom->domain_id = cpu_to_le32(domain); + dom->flags = cpu_to_le32(flags); + dom->reset_state = cpu_to_le32(state); + + if (rdom->async_reset) + ret = ph->xops->do_xfer_with_response(ph, t); + else + ret = ph->xops->do_xfer(ph, t); + + ph->xops->xfer_put(ph, t); + return ret; +} + +static int scmi_reset_domain_reset(const struct scmi_protocol_handle *ph, + u32 domain) +{ + return scmi_domain_reset(ph, domain, AUTONOMOUS_RESET, + ARCH_COLD_RESET); +} + +static int +scmi_reset_domain_assert(const struct scmi_protocol_handle *ph, u32 domain) +{ + return scmi_domain_reset(ph, domain, EXPLICIT_RESET_ASSERT, + ARCH_COLD_RESET); +} + +static int +scmi_reset_domain_deassert(const struct scmi_protocol_handle *ph, u32 domain) +{ + return scmi_domain_reset(ph, domain, 0, ARCH_COLD_RESET); +} + +static const struct scmi_reset_proto_ops reset_proto_ops = { + .num_domains_get = scmi_reset_num_domains_get, + .name_get = scmi_reset_name_get, + .latency_get = scmi_reset_latency_get, + .reset = scmi_reset_domain_reset, + .assert = scmi_reset_domain_assert, + .deassert = scmi_reset_domain_deassert, +}; + +static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph) +{ + int domain; + u32 version; + struct scmi_reset_info *pinfo; + + ph->xops->version_get(ph, &version); + + dev_dbg(ph->dev, "Reset Version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL); + if (!pinfo) + return -ENOMEM; + + scmi_reset_attributes_get(ph, pinfo); + + pinfo->dom_info = kcalloc(pinfo->num_domains, + sizeof(*pinfo->dom_info), GFP_KERNEL); + if (!pinfo->dom_info) + return -ENOMEM; + + for (domain = 0; domain < pinfo->num_domains; domain++) { + struct reset_dom_info *dom = pinfo->dom_info + domain; + + scmi_reset_domain_attributes_get(ph, domain, dom); + } + + pinfo->version = version; + return ph->set_priv(ph, pinfo); +} + +static const struct scmi_protocol scmi_reset = { + .id = SCMI_PROTOCOL_RESET, + .instance_init = &scmi_reset_protocol_init, + .ops = &reset_proto_ops, +}; + +DEFINE_SCMI_PROTOCOL_REGISTER(reset, scmi_reset) diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 21a6e1a50d8f..913b309eac3a 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -45,4 +45,15 @@ config RESET_STARFIVE help This enables the reset controller driver for the StarFive JH7100. +config RESET_SCMI + tristate "Reset driver controlled via ARM SCMI interface" + depends on ARM_SCMI_PROTOCOL || COMPILE_TEST + default ARM_SCMI_PROTOCOL + help + This driver provides support for reset signal/domains that are + controlled by firmware that implements the SCMI interface. + + This driver uses SCMI Message Protocol to interact with the + firmware controlling all the reset signals. + endif diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 6d0cd51f86ce..b1668433d76a 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_ARCH_SOCFPGA) += reset-socfpga.o obj-$(CONFIG_RESET_IMX7) += reset-imx7.o obj-$(CONFIG_RESET_STARFIVE) += reset-starfive-vic.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 000000000000..c33bbc5c8afe --- /dev/null +++ b/drivers/reset/reset-scmi.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM System Control and Management Interface (ARM SCMI) reset driver + * + * Copyright (C) 2019-2021 ARM Ltd. + */ + +#include <common.h> +#include <of.h> +#include <driver.h> +#include <linux/reset-controller.h> +#include <linux/scmi_protocol.h> + +static const struct scmi_reset_proto_ops *reset_ops; + +/** + * struct scmi_reset_data - reset controller information structure + * @rcdev: reset controller entity + * @ph: ARM SCMI protocol handle used for communication with system controller + */ +struct scmi_reset_data { + struct reset_controller_dev rcdev; + const struct scmi_protocol_handle *ph; +}; + +#define to_scmi_reset_data(p) container_of((p), struct scmi_reset_data, rcdev) +#define to_scmi_handle(p) (to_scmi_reset_data(p)->ph) + +/** + * scmi_reset_assert() - assert device reset + * @rcdev: reset controller entity + * @id: ID of the reset to be asserted + * + * This function implements the reset driver op to assert a device's reset + * using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev); + + return reset_ops->assert(ph, id); +} + +/** + * scmi_reset_deassert() - deassert device reset + * @rcdev: reset controller entity + * @id: ID of the reset to be deasserted + * + * This function implements the reset driver op to deassert a device's reset + * using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev); + + return reset_ops->deassert(ph, id); +} + +/** + * scmi_reset_reset() - reset the device + * @rcdev: reset controller entity + * @id: ID of the reset signal to be reset(assert + deassert) + * + * This function implements the reset driver op to trigger a device's + * reset signal using the ARM SCMI protocol. + * + * Return: 0 for successful request, else a corresponding error value + */ +static int +scmi_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) +{ + const struct scmi_protocol_handle *ph = to_scmi_handle(rcdev); + + return reset_ops->reset(ph, id); +} + +static const struct reset_control_ops scmi_reset_ops = { + .assert = scmi_reset_assert, + .deassert = scmi_reset_deassert, + .reset = scmi_reset_reset, +}; + +static int scmi_reset_probe(struct scmi_device *sdev) +{ + struct scmi_reset_data *data; + struct device_d *dev = &sdev->dev; + struct device_node *np = dev->device_node; + const struct scmi_handle *handle = sdev->handle; + struct scmi_protocol_handle *ph; + + if (!handle) + return -ENODEV; + + reset_ops = handle->protocol_get(sdev, SCMI_PROTOCOL_RESET, &ph); + if (IS_ERR(reset_ops)) + return PTR_ERR(reset_ops); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->rcdev.ops = &scmi_reset_ops; + data->rcdev.of_node = np; + data->rcdev.nr_resets = reset_ops->num_domains_get(ph); + data->ph = ph; + + return reset_controller_register(&data->rcdev); +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_RESET, "reset" }, + { }, +}; + +static struct scmi_driver scmi_reset_driver = { + .name = "scmi-reset", + .probe = scmi_reset_probe, + .id_table = scmi_id_table, +}; +core_scmi_driver(scmi_reset_driver); + +MODULE_AUTHOR("Sudeep Holla <sudeep.holla@xxxxxxx>"); +MODULE_DESCRIPTION("ARM SCMI reset controller driver"); +MODULE_LICENSE("GPL v2"); -- 2.30.2 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox