Add support for a loadable module implementing a custom vendor protocol just as an example.
Signed-off-by: Cristian Marussi <cristian.maru...@arm.com> --- drivers/firmware/Kconfig | 10 +- drivers/firmware/arm_scmi/Makefile | 3 + drivers/firmware/arm_scmi/scmi_custom.c | 170 ++++++++++++++++++++++++ include/linux/scmi_protocol.h | 21 +++ 4 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/arm_scmi/scmi_custom.c diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index afdbebba628a..75e2668a6490 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -6,7 +6,7 @@ menu "Firmware Drivers" -config ARM_SCMI_PROTOCOL +menuconfig ARM_SCMI_PROTOCOL tristate "ARM System Control and Management Interface (SCMI) Message Protocol" depends on ARM || ARM64 || COMPILE_TEST depends on MAILBOX @@ -27,6 +27,14 @@ config ARM_SCMI_PROTOCOL This protocol library provides interface for all the client drivers making use of the features offered by the SCMI. +if ARM_SCMI_PROTOCOL + config ARM_SCMI_PROTOCOL_CUSTOM + tristate "SCMI Custom Protocol Implementation" + default y + help + ARM SCMI System Custom Protocol implementation +endif + config ARM_SCMI_POWER_DOMAIN tristate "SCMI power domain driver" depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF) diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index bc0d54f8e861..0a03b7432497 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -7,5 +7,8 @@ scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o system.o scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \ $(scmi-transport-y) + +obj-$(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM) += scmi_custom.o + obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o diff --git a/drivers/firmware/arm_scmi/scmi_custom.c b/drivers/firmware/arm_scmi/scmi_custom.c new file mode 100644 index 000000000000..5dfdbd175043 --- /dev/null +++ b/drivers/firmware/arm_scmi/scmi_custom.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Dummy Custom Protocol + * + * Copyright (C) 2018-2020 ARM Ltd. + */ + +#define pr_fmt(fmt) "SCMI Notifications CUSTOM - " fmt + +#include <linux/scmi_protocol.h> + +#include "common.h" +#include "notify.h" + +static const struct scmi_xfer_ops *ops; + +struct scmi_dummy_scream_notify_payld { + __le32 agent_id; + __le32 domain_id; + __le32 scream; +}; + +struct dummy_dom_info { + bool state_set_sync; + bool state_set_async; + bool state_set_notify; + char name[SCMI_MAX_STR_SIZE]; +}; + +struct scmi_dummy_info { + u32 version; + int num_domains; + u64 stats_addr; + u32 stats_size; + struct dummy_dom_info *dom_info; +}; + +static int scmi_dummy_num_domains_get(const struct scmi_handle *handle) +{ + struct scmi_dummy_info *di = + ops->get_proto_priv(handle, SCMI_PROTOCOL_CUSTOM_DUMMY); + + return di->num_domains; +} + +static const struct scmi_dummy_ops dummy_ops = { + .num_domains_get = scmi_dummy_num_domains_get, +}; + +static int scmi_dummy_request_notify(const struct scmi_handle *handle, + u32 domain, bool enable) +{ + return domain % 2; +} + +static int scmi_dummy_set_notify_enabled(const struct scmi_handle *handle, + u8 evt_id, u32 src_id, bool enable) +{ + int ret; + + ret = scmi_dummy_request_notify(handle, src_id, enable); + if (ret) + pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n", + evt_id, src_id, ret); + else + pr_warn("NOTIF %sABLED - evt[%X] dom[%d]\n", + enable ? "EN" : "DIS", evt_id, src_id); + + return ret; +} + +static void *scmi_dummy_fill_custom_report(const struct scmi_handle *handle, + u8 evt_id, ktime_t timestamp, + const void *payld, size_t payld_sz, + void *report, u32 *src_id) +{ + const struct scmi_dummy_scream_notify_payld *p = payld; + struct scmi_dummy_scream_report *r = report; + + if (evt_id != SCMI_EVENT_DUMMY_SCREAM || sizeof(*p) != payld_sz) + return NULL; + + r->timestamp = timestamp; + r->agent_id = le32_to_cpu(p->agent_id); + r->domain_id = le32_to_cpu(p->domain_id); + r->scream = le32_to_cpu(p->scream); + *src_id = r->domain_id; + + return r; +} + +static int scmi_dummy_get_num_sources(const struct scmi_handle *handle) +{ + struct scmi_dummy_info *dinfo = + ops->get_proto_priv(handle, SCMI_PROTOCOL_CUSTOM_DUMMY); + + if (!dinfo) + return -EINVAL; + + return dinfo->num_domains; +} + +static const struct scmi_event dummy_events[] = { + { + .id = SCMI_EVENT_DUMMY_SCREAM, + .max_payld_sz = sizeof(struct scmi_dummy_scream_notify_payld), + .max_report_sz = + sizeof(struct scmi_dummy_scream_report), + }, +}; + +static const struct scmi_event_ops dummy_event_ops = { + .get_num_sources = scmi_dummy_get_num_sources, + .set_notify_enabled = scmi_dummy_set_notify_enabled, + .fill_custom_report = scmi_dummy_fill_custom_report, +}; + +static const struct scmi_protocol_events dummy_protocol_events = { + .queue_sz = SCMI_PROTO_QUEUE_SZ, + .ops = &dummy_event_ops, + .evts = dummy_events, + .num_events = ARRAY_SIZE(dummy_events), +}; + +static int scmi_dummy_protocol_init(const struct scmi_handle *handle, + const struct scmi_xfer_ops *xops) +{ + u32 version = 0x20000001; + struct scmi_dummy_info *dinfo; + + ops = xops; + //ops->version_get(handle, SCMI_PROTOCOL_POWER, &version); + + dev_dbg(handle->dev, "Dummy Custom Version %d.%d\n", + PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); + + dinfo = devm_kzalloc(handle->dev, sizeof(*dinfo), GFP_KERNEL); + if (!dinfo) + return -ENOMEM; + + dinfo->num_domains = 5; + + dinfo->dom_info = devm_kcalloc(handle->dev, dinfo->num_domains, + sizeof(*dinfo->dom_info), GFP_KERNEL); + if (!dinfo->dom_info) + return -ENOMEM; + + dinfo->version = version; + return ops->set_proto_priv(handle, SCMI_PROTOCOL_CUSTOM_DUMMY, dinfo); +} + +static int scmi_dummy_protocol_deinit(const struct scmi_handle *handle, + const struct scmi_xfer_ops *xops) +{ + return 0; +} + +static struct scmi_protocol scmi_custom = { + .id = SCMI_PROTOCOL_CUSTOM_DUMMY, + .init = &scmi_dummy_protocol_init, + .deinit = &scmi_dummy_protocol_deinit, + .ops = &dummy_ops, + .events = &dummy_protocol_events, +}; + +module_scmi_protocol(scmi_custom); + +MODULE_AUTHOR("DummyMaster"); +MODULE_DESCRIPTION("ARM SCMI Custom Dummy Protocol"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index da675d6f90c0..9a89ca68b27d 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -209,6 +209,12 @@ struct scmi_reset_ops { int (*deassert)(const struct scmi_handle *handle, u32 domain); }; +#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM) +struct scmi_dummy_ops { + int (*num_domains_get)(const struct scmi_handle *handle); +}; +#endif + /** * struct scmi_notify_ops - represents notifications' operations provided by * SCMI core @@ -278,6 +284,9 @@ enum scmi_std_protocol { SCMI_PROTOCOL_CLOCK = 0x14, SCMI_PROTOCOL_SENSOR = 0x15, SCMI_PROTOCOL_RESET = 0x16, +#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM) + SCMI_PROTOCOL_CUSTOM_DUMMY = 0x99, +#endif }; enum scmi_system_events { @@ -380,6 +389,9 @@ enum scmi_notification_events { SCMI_EVENT_RESET_ISSUED = 0x0, SCMI_EVENT_BASE_ERROR_EVENT = 0x0, SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER = 0x0, +#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM) + SCMI_EVENT_DUMMY_SCREAM = 0x0, +#endif }; struct scmi_power_state_changed_report { @@ -433,4 +445,13 @@ struct scmi_base_error_report { unsigned long long reports[]; }; +#if IS_ENABLED(CONFIG_ARM_SCMI_PROTOCOL_CUSTOM) +struct scmi_dummy_scream_report { + ktime_t timestamp; + unsigned int agent_id; + unsigned int domain_id; + unsigned int scream; +}; +#endif + #endif /* _LINUX_SCMI_PROTOCOL_H */ -- 2.17.1