From 45db55111393ee74ed9943b682de8e5cbbb636ac Mon Sep 17 00:00:00 2001 From: Andriy Tryshnivskyy Date: Mon, 4 Oct 2021 20:31:34 +0300 Subject: virtualization/linux-yocto: Backport virtio SCMI driver. This patch series is a "RFC v2" implementation of a driver for virtio SCMI device [1]. [1]: https://github.com/oasis-tcs/virtio-spec/blob/master/virtio-scmi.tex Bug-AGL: SPEC-3865 Upstream-Status: Submitted [https://lore.kernel.org/linux-arm-kernel/20201105212116.411422-1-peter.hilber@opensynergy.com/] Signed-off-by: Andriy Tryshnivskyy Change-Id: I653cb44769232ae5434bd54169910fd0518f1db8 --- ...m_scmi-smccc-mailbox-Make-shmem-based-tra.patch | 119 ++++ ...m_scmi-Document-that-max_msg-is-a-per-cha.patch | 34 + ...e-arm_scmi-Add-op-to-override-max-message.patch | 165 +++++ ...e-arm_scmi-Add-per-message-transport-data.patch | 42 ++ ...m_scmi-Add-xfer_init_buffers-transport-op.patch | 79 +++ ...m_scmi-Add-optional-link_supplier-transpo.patch | 54 ++ ...m_scmi-Add-per-device-transport-private-i.patch | 92 +++ ...ware-arm_scmi-Add-is_scmi_protocol_device.patch | 46 ++ ...indings-arm-Add-virtio-transport-for-SCMI.patch | 89 +++ ...10-firmware-arm_scmi-Add-virtio-transport.patch | 756 +++++++++++++++++++++ .../recipes-kernel/linux/linux-yocto_%.bbappend | 13 + 11 files changed, 1489 insertions(+) create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0001-firmware-arm_scmi-smccc-mailbox-Make-shmem-based-tra.patch create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0002-firmware-arm_scmi-Document-that-max_msg-is-a-per-cha.patch create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0003-firmware-arm_scmi-Add-op-to-override-max-message.patch create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0004-firmware-arm_scmi-Add-per-message-transport-data.patch create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0005-firmware-arm_scmi-Add-xfer_init_buffers-transport-op.patch create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0006-firmware-arm_scmi-Add-optional-link_supplier-transpo.patch create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0007-firmware-arm_scmi-Add-per-device-transport-private-i.patch create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0008-firmware-arm_scmi-Add-is_scmi_protocol_device.patch create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0009-dt-bindings-arm-Add-virtio-transport-for-SCMI.patch create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0010-firmware-arm_scmi-Add-virtio-transport.patch create mode 100644 meta-egvirt/recipes-kernel/linux/linux-yocto_%.bbappend diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0001-firmware-arm_scmi-smccc-mailbox-Make-shmem-based-tra.patch b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0001-firmware-arm_scmi-smccc-mailbox-Make-shmem-based-tra.patch new file mode 100644 index 00000000..3d9d0fdb --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0001-firmware-arm_scmi-smccc-mailbox-Make-shmem-based-tra.patch @@ -0,0 +1,119 @@ +From 36269e8f5fa1f33a793c472935affe3039c052bd Mon Sep 17 00:00:00 2001 +From: Igor Skalkin +Date: Thu, 5 Nov 2020 22:21:07 +0100 +Subject: [PATCH] firmware: arm_scmi, smccc, mailbox: Make shmem based + transports optional + +Upon adding the virtio transport in this patch series, SCMI will also +work without shared memory based transports. Also, the mailbox transport +may not be needed if the smc transport is used. + +- Compile shmem.c only if a shmem based transport is available. + +- Remove hard dependency of SCMI on mailbox. + +Co-developed-by: Peter Hilber +Signed-off-by: Peter Hilber +Signed-off-by: Igor Skalkin +Signed-off-by: Vasyl Vavrychuk +--- + drivers/firmware/Kconfig | 9 ++++++++- + drivers/firmware/arm_scmi/Makefile | 2 +- + drivers/firmware/arm_scmi/common.h | 2 ++ + drivers/firmware/arm_scmi/driver.c | 2 ++ + drivers/firmware/smccc/Kconfig | 1 + + drivers/mailbox/Kconfig | 1 + + 6 files changed, 15 insertions(+), 2 deletions(-) + +diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig +index 3315e3c21586..30a85d38d0c0 100644 +--- a/drivers/firmware/Kconfig ++++ b/drivers/firmware/Kconfig +@@ -9,7 +9,7 @@ menu "Firmware Drivers" + config ARM_SCMI_PROTOCOL + tristate "ARM System Control and Management Interface (SCMI) Message Protocol" + depends on ARM || ARM64 || COMPILE_TEST +- depends on MAILBOX ++ depends on ARM_SCMI_HAVE_SHMEM + help + ARM System Control and Management Interface (SCMI) protocol is a + set of operating system-independent software interfaces that are +@@ -27,6 +27,13 @@ config ARM_SCMI_PROTOCOL + This protocol library provides interface for all the client drivers + making use of the features offered by the SCMI. + ++config ARM_SCMI_HAVE_SHMEM ++ bool ++ default n ++ help ++ This declares whether a shared memory based transport for SCMI is ++ available. ++ + 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..3cc7fa40a464 100644 +--- a/drivers/firmware/arm_scmi/Makefile ++++ b/drivers/firmware/arm_scmi/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + scmi-bus-y = bus.o + scmi-driver-y = driver.o notify.o +-scmi-transport-y = shmem.o ++scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o + scmi-transport-$(CONFIG_MAILBOX) += mailbox.o + 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 +diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h +index 65063fa948d4..aed192238177 100644 +--- a/drivers/firmware/arm_scmi/common.h ++++ b/drivers/firmware/arm_scmi/common.h +@@ -242,7 +242,9 @@ struct scmi_desc { + int max_msg_size; + }; + ++#ifdef CONFIG_MAILBOX + extern const struct scmi_desc scmi_mailbox_desc; ++#endif + #ifdef CONFIG_HAVE_ARM_SMCCC + extern const struct scmi_desc scmi_smc_desc; + #endif +diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c +index 6b2ce3f28f7b..7efbf66f117b 100644 +--- a/drivers/firmware/arm_scmi/driver.c ++++ b/drivers/firmware/arm_scmi/driver.c +@@ -918,7 +918,9 @@ ATTRIBUTE_GROUPS(versions); + + /* Each compatible listed below must have descriptor associated with it */ + static const struct of_device_id scmi_of_match[] = { ++#ifdef CONFIG_MAILBOX + { .compatible = "arm,scmi", .data = &scmi_mailbox_desc }, ++#endif + #ifdef CONFIG_HAVE_ARM_SMCCC_DISCOVERY + { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc}, + #endif +diff --git a/drivers/firmware/smccc/Kconfig b/drivers/firmware/smccc/Kconfig +index 15e7466179a6..69c4d6cabf62 100644 +--- a/drivers/firmware/smccc/Kconfig ++++ b/drivers/firmware/smccc/Kconfig +@@ -9,6 +9,7 @@ config HAVE_ARM_SMCCC_DISCOVERY + bool + depends on ARM_PSCI_FW + default y ++ select ARM_SCMI_HAVE_SHMEM + help + SMCCC v1.0 lacked discoverability and hence PSCI v1.0 was updated + to add SMCCC discovery mechanism though the PSCI firmware +diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig +index 05b1009e2820..5ffe1ab0c869 100644 +--- a/drivers/mailbox/Kconfig ++++ b/drivers/mailbox/Kconfig +@@ -1,6 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0-only + menuconfig MAILBOX + bool "Mailbox Hardware Support" ++ select ARM_SCMI_HAVE_SHMEM + help + Mailbox is a framework to control hardware communication between + on-chip processors through queued messages and interrupt driven diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0002-firmware-arm_scmi-Document-that-max_msg-is-a-per-cha.patch b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0002-firmware-arm_scmi-Document-that-max_msg-is-a-per-cha.patch new file mode 100644 index 00000000..7a21fedf --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0002-firmware-arm_scmi-Document-that-max_msg-is-a-per-cha.patch @@ -0,0 +1,34 @@ +From 30de0a5e1fec42589469e5ec8951d15d364df63a Mon Sep 17 00:00:00 2001 +From: Igor Skalkin +Date: Thu, 5 Nov 2020 22:21:08 +0100 +Subject: [PATCH] firmware: arm_scmi: Document that max_msg is a per channel + type limit + +struct scmi_desc.max_msg specifies a limit for the pending messages. +This limit is a per SCMI channel type (tx, rx) limit. State that +explicitly in the inline documentation. The following patch will add an +op to override the limit per channel type. + +Co-developed-by: Peter Hilber +Signed-off-by: Peter Hilber +Signed-off-by: Igor Skalkin +Signed-off-by: Vasyl Vavrychuk +--- + drivers/firmware/arm_scmi/common.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h +index aed192238177..38e6aabbe3dd 100644 +--- a/drivers/firmware/arm_scmi/common.h ++++ b/drivers/firmware/arm_scmi/common.h +@@ -231,8 +231,8 @@ struct scmi_transport_ops { + * + * @ops: Pointer to the transport specific ops structure + * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) +- * @max_msg: Maximum number of messages that can be pending +- * simultaneously in the system ++ * @max_msg: Maximum number of messages for a channel type (tx or rx) that can ++ * be pending simultaneously in the system + * @max_msg_size: Maximum size of data per message that can be handled. + */ + struct scmi_desc { diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0003-firmware-arm_scmi-Add-op-to-override-max-message.patch b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0003-firmware-arm_scmi-Add-op-to-override-max-message.patch new file mode 100644 index 00000000..f58d6d88 --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0003-firmware-arm_scmi-Add-op-to-override-max-message.patch @@ -0,0 +1,165 @@ +From 9ffe778acc541cec68c954f84c6fcfef8a35bec2 Mon Sep 17 00:00:00 2001 +From: Igor Skalkin +Date: Thu, 5 Nov 2020 22:21:09 +0100 +Subject: [PATCH] firmware: arm_scmi: Add op to override max message # + +The number of messages that the upcoming scmi-virtio transport can +support depends on the virtio device (SCMI platform) and can differ for +each channel. (The scmi-virtio transport does only have one tx and at +most 1 rx channel.) + +Add an optional transport op so that scmi-virtio can report the actual +max message # for each channel type. Respect these new limits. + +Co-developed-by: Peter Hilber +Signed-off-by: Peter Hilber +Signed-off-by: Igor Skalkin +Signed-off-by: Vasyl Vavrychuk +--- + drivers/firmware/arm_scmi/common.h | 8 ++++- + drivers/firmware/arm_scmi/driver.c | 49 ++++++++++++++++++++++-------- + 2 files changed, 43 insertions(+), 14 deletions(-) + +diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h +index 38e6aabbe3dd..9a8359ecd220 100644 +--- a/drivers/firmware/arm_scmi/common.h ++++ b/drivers/firmware/arm_scmi/common.h +@@ -203,6 +203,9 @@ struct scmi_chan_info { + * @chan_available: Callback to check if channel is available or not + * @chan_setup: Callback to allocate and setup a channel + * @chan_free: Callback to free a channel ++ * @get_max_msg: Optional callback to provide max_msg dynamically ++ * @max_msg: Maximum number of messages for the channel type (tx or rx) ++ * that can be pending simultaneously in the system + * @send_message: Callback to send a message + * @mark_txdone: Callback to mark tx as done + * @fetch_response: Callback to fetch response +@@ -215,6 +218,8 @@ struct scmi_transport_ops { + int (*chan_setup)(struct scmi_chan_info *cinfo, struct device *dev, + bool tx); + int (*chan_free)(int id, void *p, void *data); ++ int (*get_max_msg)(bool tx, struct scmi_chan_info *base_cinfo, ++ int *max_msg); + int (*send_message)(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer); + void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret); +@@ -232,7 +237,8 @@ struct scmi_transport_ops { + * @ops: Pointer to the transport specific ops structure + * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) + * @max_msg: Maximum number of messages for a channel type (tx or rx) that can +- * be pending simultaneously in the system ++ * be pending simultaneously in the system. May be overridden by the ++ * get_max_msg op. + * @max_msg_size: Maximum size of data per message that can be handled. + */ + struct scmi_desc { +diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c +index 7efbf66f117b..5baa23789a49 100644 +--- a/drivers/firmware/arm_scmi/driver.c ++++ b/drivers/firmware/arm_scmi/driver.c +@@ -61,11 +61,13 @@ static atomic_t transfer_last_id; + * Index of this bitmap table is also used for message + * sequence identifier. + * @xfer_lock: Protection for message allocation ++ * @max_msg: Maximum number of messages that can be pending + */ + struct scmi_xfers_info { + struct scmi_xfer *xfer_block; + unsigned long *xfer_alloc_table; + spinlock_t xfer_lock; ++ int max_msg; + }; + + /** +@@ -157,13 +159,11 @@ static struct scmi_xfer *scmi_xfer_get(const struct scmi_handle *handle, + u16 xfer_id; + struct scmi_xfer *xfer; + unsigned long flags, bit_pos; +- struct scmi_info *info = handle_to_scmi_info(handle); + + /* Keep the locked section as small as possible */ + spin_lock_irqsave(&minfo->xfer_lock, flags); +- bit_pos = find_first_zero_bit(minfo->xfer_alloc_table, +- info->desc->max_msg); +- if (bit_pos == info->desc->max_msg) { ++ bit_pos = find_first_zero_bit(minfo->xfer_alloc_table, minfo->max_msg); ++ if (bit_pos == minfo->max_msg) { + spin_unlock_irqrestore(&minfo->xfer_lock, flags); + return ERR_PTR(-ENOMEM); + } +@@ -602,32 +602,44 @@ int scmi_handle_put(const struct scmi_handle *handle) + } + + static int __scmi_xfer_info_init(struct scmi_info *sinfo, +- struct scmi_xfers_info *info) ++ struct scmi_xfers_info *info, ++ bool tx, ++ struct scmi_chan_info *base_cinfo) + { + int i; + struct scmi_xfer *xfer; + struct device *dev = sinfo->dev; + const struct scmi_desc *desc = sinfo->desc; + ++ info->max_msg = desc->max_msg; ++ ++ if (desc->ops->get_max_msg) { ++ int ret = ++ desc->ops->get_max_msg(tx, base_cinfo, &info->max_msg); ++ ++ if (ret) ++ return ret; ++ } ++ + /* Pre-allocated messages, no more than what hdr.seq can support */ +- if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) { ++ if (WARN_ON(info->max_msg >= MSG_TOKEN_MAX)) { + dev_err(dev, "Maximum message of %d exceeds supported %ld\n", +- desc->max_msg, MSG_TOKEN_MAX); ++ info->max_msg, MSG_TOKEN_MAX); + return -EINVAL; + } + +- info->xfer_block = devm_kcalloc(dev, desc->max_msg, ++ info->xfer_block = devm_kcalloc(dev, info->max_msg, + sizeof(*info->xfer_block), GFP_KERNEL); + if (!info->xfer_block) + return -ENOMEM; + +- info->xfer_alloc_table = devm_kcalloc(dev, BITS_TO_LONGS(desc->max_msg), ++ info->xfer_alloc_table = devm_kcalloc(dev, BITS_TO_LONGS(info->max_msg), + sizeof(long), GFP_KERNEL); + if (!info->xfer_alloc_table) + return -ENOMEM; + + /* Pre-initialize the buffer pointer to pre-allocated buffers */ +- for (i = 0, xfer = info->xfer_block; i < desc->max_msg; i++, xfer++) { ++ for (i = 0, xfer = info->xfer_block; i < info->max_msg; i++, xfer++) { + xfer->rx.buf = devm_kcalloc(dev, sizeof(u8), desc->max_msg_size, + GFP_KERNEL); + if (!xfer->rx.buf) +@@ -644,10 +656,21 @@ static int __scmi_xfer_info_init(struct scmi_info *sinfo, + + static int scmi_xfer_info_init(struct scmi_info *sinfo) + { +- int ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo); ++ int ret; ++ struct scmi_chan_info *base_tx_cinfo; ++ struct scmi_chan_info *base_rx_cinfo; ++ ++ base_tx_cinfo = idr_find(&sinfo->tx_idr, SCMI_PROTOCOL_BASE); ++ if (unlikely(!base_tx_cinfo)) ++ return -EINVAL; ++ ++ ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo, true, ++ base_tx_cinfo); + +- if (!ret && idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE)) +- ret = __scmi_xfer_info_init(sinfo, &sinfo->rx_minfo); ++ base_rx_cinfo = idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE); ++ if (!ret && base_rx_cinfo) ++ ret = __scmi_xfer_info_init(sinfo, &sinfo->rx_minfo, false, ++ base_rx_cinfo); + + return ret; + } diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0004-firmware-arm_scmi-Add-per-message-transport-data.patch b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0004-firmware-arm_scmi-Add-per-message-transport-data.patch new file mode 100644 index 00000000..644b015d --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0004-firmware-arm_scmi-Add-per-message-transport-data.patch @@ -0,0 +1,42 @@ +From 0771d1d96e6439c00497a521f8d2c9803f7e07e2 Mon Sep 17 00:00:00 2001 +From: Igor Skalkin +Date: Thu, 5 Nov 2020 22:21:10 +0100 +Subject: [PATCH] firmware: arm_scmi: Add per message transport data + +The virtio transport in this patch series can be simplified by using the +scmi_xfer tx/rx buffers for data exchange with the virtio device, and +for saving the message state. But the virtio transport requires +prepending a transport-specific header. Also, for data exchange using +virtqueues, the tx and rx buffers should not overlap. + +The first step to solve the aforementioned issues is to add a +transport-specific data pointer to scmi_xfer. + +Co-developed-by: Peter Hilber +Signed-off-by: Peter Hilber +Signed-off-by: Igor Skalkin +Signed-off-by: Vasyl Vavrychuk +--- + drivers/firmware/arm_scmi/common.h | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h +index 9a8359ecd220..c998ec29018e 100644 +--- a/drivers/firmware/arm_scmi/common.h ++++ b/drivers/firmware/arm_scmi/common.h +@@ -131,6 +131,7 @@ struct scmi_msg { + * buffer for the rx path as we use for the tx path. + * @done: command message transmit completion event + * @async_done: pointer to delayed response message received event completion ++ * @extra_data: Transport-specific private data pointer + */ + struct scmi_xfer { + int transfer_id; +@@ -139,6 +140,7 @@ struct scmi_xfer { + struct scmi_msg rx; + struct completion done; + struct completion *async_done; ++ void *extra_data; + }; + + void scmi_xfer_put(const struct scmi_handle *h, struct scmi_xfer *xfer); diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0005-firmware-arm_scmi-Add-xfer_init_buffers-transport-op.patch b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0005-firmware-arm_scmi-Add-xfer_init_buffers-transport-op.patch new file mode 100644 index 00000000..e62e5c50 --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0005-firmware-arm_scmi-Add-xfer_init_buffers-transport-op.patch @@ -0,0 +1,79 @@ +From fddb9bcd706ed3bb262f4f37707616dd06c7a4cc Mon Sep 17 00:00:00 2001 +From: Igor Skalkin +Date: Thu, 5 Nov 2020 22:21:11 +0100 +Subject: [PATCH] firmware: arm_scmi: Add xfer_init_buffers transport op + +The virtio transport in this patch series can be simplified by using the +scmi_xfer tx/rx buffers for data exchange with the virtio device, and +for saving the message state. But the virtio transport requires +prepending a transport-specific header. Also, for data exchange using +virtqueues, the tx and rx buffers should not overlap. + +After the previous patch, this is the second and final step to enable +the virtio transport to use the scmi_xfer buffers for data exchange. + +Add an optional op through which the transport can allocate the tx/rx +buffers along with room for the prepended transport-specific headers. + +Co-developed-by: Peter Hilber +Signed-off-by: Peter Hilber +Signed-off-by: Igor Skalkin +Signed-off-by: Vasyl Vavrychuk +--- + drivers/firmware/arm_scmi/common.h | 3 +++ + drivers/firmware/arm_scmi/driver.c | 21 +++++++++++++++------ + 2 files changed, 18 insertions(+), 6 deletions(-) + +diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h +index c998ec29018e..ae5db602e45d 100644 +--- a/drivers/firmware/arm_scmi/common.h ++++ b/drivers/firmware/arm_scmi/common.h +@@ -208,6 +208,7 @@ struct scmi_chan_info { + * @get_max_msg: Optional callback to provide max_msg dynamically + * @max_msg: Maximum number of messages for the channel type (tx or rx) + * that can be pending simultaneously in the system ++ * @xfer_init_buffers: Callback to initialize buffers for scmi_xfer + * @send_message: Callback to send a message + * @mark_txdone: Callback to mark tx as done + * @fetch_response: Callback to fetch response +@@ -222,6 +223,8 @@ struct scmi_transport_ops { + int (*chan_free)(int id, void *p, void *data); + int (*get_max_msg)(bool tx, struct scmi_chan_info *base_cinfo, + int *max_msg); ++ int (*xfer_init_buffers)(struct scmi_chan_info *cinfo, ++ struct scmi_xfer *xfer, int max_msg_size); + int (*send_message)(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer); + void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret); +diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c +index 5baa23789a49..27dd43dcff5b 100644 +--- a/drivers/firmware/arm_scmi/driver.c ++++ b/drivers/firmware/arm_scmi/driver.c +@@ -640,12 +640,21 @@ static int __scmi_xfer_info_init(struct scmi_info *sinfo, + + /* Pre-initialize the buffer pointer to pre-allocated buffers */ + for (i = 0, xfer = info->xfer_block; i < info->max_msg; i++, xfer++) { +- xfer->rx.buf = devm_kcalloc(dev, sizeof(u8), desc->max_msg_size, +- GFP_KERNEL); +- if (!xfer->rx.buf) +- return -ENOMEM; +- +- xfer->tx.buf = xfer->rx.buf; ++ if (desc->ops->xfer_init_buffers) { ++ int ret = desc->ops->xfer_init_buffers( ++ base_cinfo, xfer, desc->max_msg_size); ++ ++ if (ret) ++ return ret; ++ } else { ++ xfer->rx.buf = devm_kcalloc(dev, sizeof(u8), ++ desc->max_msg_size, ++ GFP_KERNEL); ++ if (!xfer->rx.buf) ++ return -ENOMEM; ++ ++ xfer->tx.buf = xfer->rx.buf; ++ } + init_completion(&xfer->done); + } + diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0006-firmware-arm_scmi-Add-optional-link_supplier-transpo.patch b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0006-firmware-arm_scmi-Add-optional-link_supplier-transpo.patch new file mode 100644 index 00000000..0f5180fa --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0006-firmware-arm_scmi-Add-optional-link_supplier-transpo.patch @@ -0,0 +1,54 @@ +From f0d7ff1f10ab846bf32bae3b7d32bf95653954fa Mon Sep 17 00:00:00 2001 +From: Peter Hilber +Date: Thu, 5 Nov 2020 22:21:12 +0100 +Subject: [PATCH] firmware: arm_scmi: Add optional link_supplier() transport op + +For the scmi-virtio transport, it might not be possible to refer to the +proper virtio device at device tree build time. Therefore, add an op +which will allow scmi-virtio to dynamically link to the proper virtio +device during probe. + +Signed-off-by: Peter Hilber +Signed-off-by: Vasyl Vavrychuk +--- + drivers/firmware/arm_scmi/common.h | 2 ++ + drivers/firmware/arm_scmi/driver.c | 6 ++++++ + 2 files changed, 8 insertions(+) + +diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h +index ae5db602e45d..2f55ac71555a 100644 +--- a/drivers/firmware/arm_scmi/common.h ++++ b/drivers/firmware/arm_scmi/common.h +@@ -202,6 +202,7 @@ struct scmi_chan_info { + /** + * struct scmi_transport_ops - Structure representing a SCMI transport ops + * ++ * @link_supplier: Optional callback to add link to a supplier device + * @chan_available: Callback to check if channel is available or not + * @chan_setup: Callback to allocate and setup a channel + * @chan_free: Callback to free a channel +@@ -217,6 +218,7 @@ struct scmi_chan_info { + * @poll_done: Callback to poll transfer status + */ + struct scmi_transport_ops { ++ int (*link_supplier)(struct device *dev); + bool (*chan_available)(struct device *dev, int idx); + int (*chan_setup)(struct scmi_chan_info *cinfo, struct device *dev, + bool tx); +diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c +index 27dd43dcff5b..0b70f9ef9477 100644 +--- a/drivers/firmware/arm_scmi/driver.c ++++ b/drivers/firmware/arm_scmi/driver.c +@@ -826,6 +826,12 @@ static int scmi_probe(struct platform_device *pdev) + handle->dev = info->dev; + handle->version = &info->version; + ++ if (desc->ops->link_supplier) { ++ ret = desc->ops->link_supplier(dev); ++ if (ret) ++ return ret; ++ } ++ + ret = scmi_txrx_setup(info, dev, SCMI_PROTOCOL_BASE); + if (ret) + return ret; diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0007-firmware-arm_scmi-Add-per-device-transport-private-i.patch b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0007-firmware-arm_scmi-Add-per-device-transport-private-i.patch new file mode 100644 index 00000000..1c82530f --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0007-firmware-arm_scmi-Add-per-device-transport-private-i.patch @@ -0,0 +1,92 @@ +From 9d5366d4765fb60cafa31c2b46cd80d284c847bb Mon Sep 17 00:00:00 2001 +From: Peter Hilber +Date: Thu, 5 Nov 2020 22:21:13 +0100 +Subject: [PATCH] firmware: arm_scmi: Add per-device transport private info + +The scmi-virtio transport will link a supplier device to the arm-scmi +device in the link_supplier() op. The transport should then save a +pointer to the linked device. + +To enable this, add a transport private info to the scmi_info. (The +scmi_info is already reachable through the arm-scmi device driver_data.) + +Signed-off-by: Peter Hilber +Signed-off-by: Vasyl Vavrychuk +--- + drivers/firmware/arm_scmi/common.h | 2 ++ + drivers/firmware/arm_scmi/driver.c | 35 ++++++++++++++++++++++++++++++ + 2 files changed, 37 insertions(+) + +diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h +index 2f55ac71555a..ec9fd7fce3c7 100644 +--- a/drivers/firmware/arm_scmi/common.h ++++ b/drivers/firmware/arm_scmi/common.h +@@ -262,6 +262,8 @@ extern const struct scmi_desc scmi_mailbox_desc; + extern const struct scmi_desc scmi_smc_desc; + #endif + ++int scmi_set_transport_info(struct device *dev, void *transport_info); ++void *scmi_get_transport_info(struct device *dev); + void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr); + void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id); + +diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c +index 0b70f9ef9477..93ce17bb4079 100644 +--- a/drivers/firmware/arm_scmi/driver.c ++++ b/drivers/firmware/arm_scmi/driver.c +@@ -84,6 +84,7 @@ struct scmi_xfers_info { + * @rx_idr: IDR object to map protocol id to Rx channel info pointer + * @protocols_imp: List of protocols implemented, currently maximum of + * MAX_PROTOCOLS_IMP elements allocated by the base protocol ++ * @transport_info: Transport private info + * @node: List head + * @users: Number of users of this instance + */ +@@ -97,6 +98,7 @@ struct scmi_info { + struct idr tx_idr; + struct idr rx_idr; + u8 *protocols_imp; ++ void *transport_info; + struct list_head node; + int users; + }; +@@ -315,6 +317,39 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) + } + } + ++/** ++ * scmi_set_transport_info() - Set transport private info ++ * ++ * @dev: SCMI instance device ++ * @transport_info: transport private info ++ * ++ * Return: 0 on success, otherwise error. ++ */ ++int scmi_set_transport_info(struct device *dev, void *transport_info) ++{ ++ struct scmi_info *info = dev_get_drvdata(dev); ++ ++ if (!info) ++ return -EBADR; ++ ++ info->transport_info = transport_info; ++ return 0; ++} ++ ++/** ++ * scmi_get_transport_info() - Get transport private info ++ * ++ * @dev: SCMI instance device ++ * ++ * Return: transport private info on success, otherwise NULL. ++ */ ++void *scmi_get_transport_info(struct device *dev) ++{ ++ struct scmi_info *info = dev_get_drvdata(dev); ++ ++ return info ? info->transport_info : NULL; ++} ++ + /** + * scmi_xfer_put() - Release a transmit message + * diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0008-firmware-arm_scmi-Add-is_scmi_protocol_device.patch b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0008-firmware-arm_scmi-Add-is_scmi_protocol_device.patch new file mode 100644 index 00000000..92aad65e --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0008-firmware-arm_scmi-Add-is_scmi_protocol_device.patch @@ -0,0 +1,46 @@ +From c4b299105e74f7863b2aa63bd838e7762761629c Mon Sep 17 00:00:00 2001 +From: Peter Hilber +Date: Thu, 5 Nov 2020 22:21:14 +0100 +Subject: [PATCH] firmware: arm_scmi: Add is_scmi_protocol_device() + +The scmi-virtio transport driver will need to distinguish SCMI protocol +devices from the SCMI instance device in the chan_setup() and +chan_free() ops. Add this internal helper to be able to distinguish the +two. + +Signed-off-by: Peter Hilber +Signed-off-by: Vasyl Vavrychuk +--- + drivers/firmware/arm_scmi/bus.c | 5 +++++ + drivers/firmware/arm_scmi/common.h | 2 ++ + 2 files changed, 7 insertions(+) + +diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c +index 1377ec76a45d..4f19faafb2c5 100644 +--- a/drivers/firmware/arm_scmi/bus.c ++++ b/drivers/firmware/arm_scmi/bus.c +@@ -108,6 +108,11 @@ static struct bus_type scmi_bus_type = { + .remove = scmi_dev_remove, + }; + ++bool is_scmi_protocol_device(struct device *dev) ++{ ++ return dev->bus == &scmi_bus_type; ++} ++ + int scmi_driver_register(struct scmi_driver *driver, struct module *owner, + const char *mod_name) + { +diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h +index ec9fd7fce3c7..13c9ac176b23 100644 +--- a/drivers/firmware/arm_scmi/common.h ++++ b/drivers/firmware/arm_scmi/common.h +@@ -158,6 +158,8 @@ int scmi_version_get(const struct scmi_handle *h, u8 protocol, u32 *version); + void scmi_setup_protocol_implemented(const struct scmi_handle *handle, + u8 *prot_imp); + ++bool is_scmi_protocol_device(struct device *dev); ++ + int scmi_base_protocol_init(struct scmi_handle *h); + + int __init scmi_bus_init(void); diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0009-dt-bindings-arm-Add-virtio-transport-for-SCMI.patch b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0009-dt-bindings-arm-Add-virtio-transport-for-SCMI.patch new file mode 100644 index 00000000..beb2d99f --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0009-dt-bindings-arm-Add-virtio-transport-for-SCMI.patch @@ -0,0 +1,89 @@ +From 592064dd36739ad5f3d885b9880ee1bc2d66e2df Mon Sep 17 00:00:00 2001 +From: Igor Skalkin +Date: Thu, 5 Nov 2020 22:21:15 +0100 +Subject: [PATCH] dt-bindings: arm: Add virtio transport for SCMI + +Document the properties for arm,scmi-virtio compatible nodes. The +backing virtio SCMI device is described in patch [1]. + +[1] https://lists.oasis-open.org/archives/virtio-comment/202005/msg00096.html + +Co-developed-by: Peter Hilber +Signed-off-by: Peter Hilber +Signed-off-by: Igor Skalkin +Signed-off-by: Vasyl Vavrychuk +--- + .../devicetree/bindings/arm/arm,scmi.txt | 35 +++++++++++++++++-- + 1 file changed, 33 insertions(+), 2 deletions(-) + +diff --git a/Documentation/devicetree/bindings/arm/arm,scmi.txt b/Documentation/devicetree/bindings/arm/arm,scmi.txt +index 55deb68230eb..6ded49d82773 100644 +--- a/Documentation/devicetree/bindings/arm/arm,scmi.txt ++++ b/Documentation/devicetree/bindings/arm/arm,scmi.txt +@@ -13,6 +13,9 @@ the device tree. + Required properties: + + The scmi node with the following properties shall be under the /firmware/ node. ++Some properties are specific to a transport type. ++ ++shmem-based transports (mailbox, smc/hvc): + + - compatible : shall be "arm,scmi" or "arm,scmi-smc" for smc/hvc transports + - mboxes: List of phandle and mailbox channel specifiers. It should contain +@@ -21,6 +24,15 @@ The scmi node with the following properties shall be under the /firmware/ node. + supported. + - shmem : List of phandle pointing to the shared memory(SHM) area as per + generic mailbox client binding. ++ ++Virtio transport: ++ ++- compatible : shall be "arm,scmi-virtio". ++ ++The virtio transport only supports a single device. ++ ++Additional required properties: ++ + - #address-cells : should be '1' if the device has sub-nodes, maps to + protocol identifier for a given sub-node. + - #size-cells : should be '0' as 'reg' property doesn't have any size +@@ -42,7 +54,8 @@ Each protocol supported shall have a sub-node with corresponding compatible + as described in the following sections. If the platform supports dedicated + communication channel for a particular protocol, the 3 properties namely: + mboxes, mbox-names and shmem shall be present in the sub-node corresponding +-to that protocol. ++to that protocol. The virtio transport does not support dedicated communication ++channels. + + Clock/Performance bindings for the clocks/OPPs based on SCMI Message Protocol + ------------------------------------------------------------ +@@ -106,7 +119,8 @@ Required sub-node properties: + [4] Documentation/devicetree/bindings/sram/sram.yaml + [5] Documentation/devicetree/bindings/reset/reset.txt + +-Example: ++Example (mailbox transport): ++---------------------------- + + sram@50000000 { + compatible = "mmio-sram"; +@@ -195,3 +209,20 @@ thermal-zones { + ... + }; + }; ++ ++Example (virtio transport): ++--------------------------- ++ ++virtio_mmio@4b001000 { ++ compatible = "virtio,mmio"; ++ ... ++}; ++ ++firmware { ++ ... ++ scmi { ++ compatible = "arm,scmi-virtio"; ++ ... ++ ++The rest is similar to the mailbox transport example, when omitting the ++mailbox/shmem-specific properties. diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0010-firmware-arm_scmi-Add-virtio-transport.patch b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0010-firmware-arm_scmi-Add-virtio-transport.patch new file mode 100644 index 00000000..35ea04f8 --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto/virtio-scmi/0010-firmware-arm_scmi-Add-virtio-transport.patch @@ -0,0 +1,756 @@ +From 7b9f3fe0766d6d36ca7465e54754df51ca175079 Mon Sep 17 00:00:00 2001 +From: Igor Skalkin +Date: Thu, 5 Nov 2020 22:21:16 +0100 +Subject: [PATCH] firmware: arm_scmi: Add virtio transport + +This transport enables accessing an SCMI platform as a virtio device. + +Implement an SCMI virtio driver according to the virtio SCMI device spec +patch v5 [1]. Virtio device id 32 has been reserved for the SCMI device +[2]. + +The virtio transport has one tx channel (virtio cmdq, A2P channel) and +at most one rx channel (virtio eventq, P2A channel). + +The following feature bit defined in [1] is not implemented: +VIRTIO_SCMI_F_SHARED_MEMORY. + +After the preparatory patches, implement the virtio transport as +paraphrased: + +Only support a single arm-scmi device (which is consistent with the SCMI +spec). Call scmi-virtio init from arm-scmi module init. During the +arm-scmi probing, link to the first probed scmi-virtio device. Defer +arm-scmi probing if no scmi-virtio device is bound yet. + +Use the scmi_xfer tx/rx buffers for data exchange with the virtio device +in order to avoid redundant maintenance of additional buffers. Allocate +the buffers in the SCMI transport, and prepend room for a small header +used by the virtio transport to the tx/rx buffers. + +For simplicity, restrict the number of messages which can be pending +simultaneously according to the virtqueue capacity. (The virtqueue sizes +are negotiated with the virtio device.) + +As soon as rx channel message buffers are allocated or have been read +out by the arm-scmi driver, feed them to the virtio device. + +Since some virtio devices may not have the short response time exhibited +by SCMI platforms using other transports, set a generous response +timeout. + +Limitations: + +Do not adjust the other SCMI timeouts for delayed response and polling +for now, since these timeouts are only relevant in special cases which +are not yet deemed relevant for this transport. + +To do (as discussed in the cover letter): + +- Avoid re-use of buffers still being used by the virtio device on + timeouts. + +- Avoid race conditions on receiving messages during/after channel free + on driver probe failure or remove. + +[1] https://lists.oasis-open.org/archives/virtio-comment/202005/msg00096.html +[2] https://www.oasis-open.org/committees/ballot.php?id=3496 + +Co-developed-by: Peter Hilber +Signed-off-by: Peter Hilber +Signed-off-by: Igor Skalkin +Signed-off-by: Vasyl Vavrychuk +--- + MAINTAINERS | 1 + + drivers/firmware/Kconfig | 12 +- + drivers/firmware/arm_scmi/Makefile | 1 + + drivers/firmware/arm_scmi/common.h | 14 + + drivers/firmware/arm_scmi/driver.c | 11 + + drivers/firmware/arm_scmi/virtio.c | 493 +++++++++++++++++++++++++++++ + include/uapi/linux/virtio_ids.h | 1 + + include/uapi/linux/virtio_scmi.h | 41 +++ + 8 files changed, 573 insertions(+), 1 deletion(-) + create mode 100644 drivers/firmware/arm_scmi/virtio.c + create mode 100644 include/uapi/linux/virtio_scmi.h + +diff --git a/MAINTAINERS b/MAINTAINERS +index 49772b741967..d223a5c3f465 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -16973,6 +16973,7 @@ F: drivers/firmware/arm_scpi.c + F: drivers/reset/reset-scmi.c + F: include/linux/sc[mp]i_protocol.h + F: include/trace/events/scmi.h ++F: include/uapi/linux/virtio_scmi.h + + SYSTEM RESET/SHUTDOWN DRIVERS + M: Sebastian Reichel +diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig +index 30a85d38d0c0..82e97904bcae 100644 +--- a/drivers/firmware/Kconfig ++++ b/drivers/firmware/Kconfig +@@ -9,7 +9,7 @@ menu "Firmware Drivers" + config ARM_SCMI_PROTOCOL + tristate "ARM System Control and Management Interface (SCMI) Message Protocol" + depends on ARM || ARM64 || COMPILE_TEST +- depends on ARM_SCMI_HAVE_SHMEM ++ depends on ARM_SCMI_HAVE_SHMEM || VIRTIO_SCMI + help + ARM System Control and Management Interface (SCMI) protocol is a + set of operating system-independent software interfaces that are +@@ -34,6 +34,16 @@ config ARM_SCMI_HAVE_SHMEM + This declares whether a shared memory based transport for SCMI is + available. + ++config VIRTIO_SCMI ++ bool "Virtio transport for SCMI" ++ default n ++ depends on VIRTIO ++ help ++ This enables the virtio based transport for SCMI. ++ ++ If you want to use the ARM SCMI protocol between the virtio guest and ++ a host providing a virtio SCMI device, answer Y. ++ + 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 3cc7fa40a464..25caea5e1969 100644 +--- a/drivers/firmware/arm_scmi/Makefile ++++ b/drivers/firmware/arm_scmi/Makefile +@@ -4,6 +4,7 @@ scmi-driver-y = driver.o notify.o + scmi-transport-$(CONFIG_ARM_SCMI_HAVE_SHMEM) = shmem.o + scmi-transport-$(CONFIG_MAILBOX) += mailbox.o + scmi-transport-$(CONFIG_HAVE_ARM_SMCCC_DISCOVERY) += smc.o ++scmi-transport-$(CONFIG_VIRTIO_SCMI) += virtio.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) +diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h +index 13c9ac176b23..b46dfe84e78b 100644 +--- a/drivers/firmware/arm_scmi/common.h ++++ b/drivers/firmware/arm_scmi/common.h +@@ -165,6 +165,17 @@ int scmi_base_protocol_init(struct scmi_handle *h); + int __init scmi_bus_init(void); + void __exit scmi_bus_exit(void); + ++#ifdef CONFIG_VIRTIO_SCMI ++int __init virtio_scmi_init(void); ++void __exit virtio_scmi_exit(void); ++#else ++static inline int __init virtio_scmi_init(void) ++{ ++ return 0; ++} ++#define virtio_scmi_exit() do { } while (0) ++#endif ++ + #define DECLARE_SCMI_REGISTER_UNREGISTER(func) \ + int __init scmi_##func##_register(void); \ + void __exit scmi_##func##_unregister(void) +@@ -263,6 +274,9 @@ extern const struct scmi_desc scmi_mailbox_desc; + #ifdef CONFIG_HAVE_ARM_SMCCC + extern const struct scmi_desc scmi_smc_desc; + #endif ++#ifdef CONFIG_VIRTIO_SCMI ++extern const struct scmi_desc scmi_virtio_desc; ++#endif + + int scmi_set_transport_info(struct device *dev, void *transport_info); + void *scmi_get_transport_info(struct device *dev); +diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c +index 93ce17bb4079..540a55285349 100644 +--- a/drivers/firmware/arm_scmi/driver.c ++++ b/drivers/firmware/arm_scmi/driver.c +@@ -996,6 +996,9 @@ static const struct of_device_id scmi_of_match[] = { + #endif + #ifdef CONFIG_HAVE_ARM_SMCCC_DISCOVERY + { .compatible = "arm,scmi-smc", .data = &scmi_smc_desc}, ++#endif ++#ifdef CONFIG_VIRTIO_SCMI ++ { .compatible = "arm,scmi-virtio", .data = &scmi_virtio_desc}, + #endif + { /* Sentinel */ }, + }; +@@ -1014,8 +1017,14 @@ static struct platform_driver scmi_driver = { + + static int __init scmi_driver_init(void) + { ++ int ret; ++ + scmi_bus_init(); + ++ ret = virtio_scmi_init(); ++ if (ret) ++ return ret; ++ + scmi_clock_register(); + scmi_perf_register(); + scmi_power_register(); +@@ -1038,6 +1047,8 @@ static void __exit scmi_driver_exit(void) + scmi_sensors_unregister(); + scmi_system_unregister(); + ++ virtio_scmi_exit(); ++ + platform_driver_unregister(&scmi_driver); + } + module_exit(scmi_driver_exit); +diff --git a/drivers/firmware/arm_scmi/virtio.c b/drivers/firmware/arm_scmi/virtio.c +new file mode 100644 +index 000000000000..f70aa72f34f1 +--- /dev/null ++++ b/drivers/firmware/arm_scmi/virtio.c +@@ -0,0 +1,493 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Virtio Transport driver for Arm System Control and Management Interface ++ * (SCMI). ++ * ++ * Copyright (C) 2020 OpenSynergy. ++ */ ++ ++/** ++ * DOC: Theory of Operation ++ * ++ * The scmi-virtio transport implements a driver for the virtio SCMI device ++ * proposed in virtio spec patch v5[1]. ++ * ++ * There is one tx channel (virtio cmdq, A2P channel) and at most one rx ++ * channel (virtio eventq, P2A channel). Each channel is implemented through a ++ * virtqueue. Access to each virtqueue is protected by a spinlock. ++ * ++ * This SCMI transport uses the scmi_xfer tx/rx buffers for data exchange with ++ * the virtio device to avoid maintenance of additional buffers. ++ * ++ * [1] https://lists.oasis-open.org/archives/virtio-comment/202005/msg00096.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "common.h" ++ ++#define VIRTIO_SCMI_MAX_MSG_SIZE 128 /* Value may be increased. */ ++#define DESCR_PER_TX_MSG 2 ++ ++struct scmi_vio_channel { ++ spinlock_t lock; ++ struct virtqueue *vqueue; ++ struct scmi_chan_info *cinfo; ++ u8 is_rx; ++}; ++ ++union virtio_scmi_input { ++ __virtio32 hdr; ++ struct virtio_scmi_response response; ++ struct virtio_scmi_notification notification; ++}; ++ ++struct scmi_vio_msg { ++ struct virtio_scmi_request *request; ++ union virtio_scmi_input *input; ++ u8 completed; ++}; ++ ++static int scmi_vio_populate_vq_rx(struct scmi_vio_channel *vioch, ++ struct scmi_xfer *xfer) ++{ ++ struct scatterlist sg_in; ++ struct scmi_vio_msg *msg = xfer->extra_data; ++ int rc; ++ ++ msg->completed = false; ++ ++ sg_init_one(&sg_in, msg->input, ++ sizeof(*msg->input) + VIRTIO_SCMI_MAX_MSG_SIZE); ++ ++ rc = virtqueue_add_inbuf(vioch->vqueue, &sg_in, 1, xfer, GFP_ATOMIC); ++ if (rc) ++ dev_err(vioch->cinfo->dev, "%s() rc=%d\n", __func__, rc); ++ else ++ virtqueue_kick(vioch->vqueue); ++ ++ return rc; ++} ++ ++static void scmi_vio_complete_cb(struct virtqueue *vqueue) ++{ ++ struct scmi_vio_channel *vioch = vqueue->priv; ++ unsigned long iflags; ++ unsigned int length; ++ ++ spin_lock_irqsave(&vioch->lock, iflags); ++ ++ do { ++ struct scmi_xfer *xfer; ++ ++ virtqueue_disable_cb(vqueue); ++ ++ while ((xfer = virtqueue_get_buf(vqueue, &length))) { ++ struct scmi_vio_msg *msg = xfer->extra_data; ++ u32 msg_hdr = ++ virtio32_to_cpu(vqueue->vdev, msg->input->hdr); ++ u8 msg_type = MSG_XTRACT_TYPE(msg_hdr); ++ ++ if (!vioch->is_rx) { /* tx queue response */ ++ msg->completed = true; ++ xfer->rx.len = ++ length - sizeof(msg->input->response); ++ if (!xfer->hdr.poll_completion) ++ scmi_rx_callback(vioch->cinfo, msg_hdr); ++ continue; ++ } ++ ++ /* rx queue - notification or delayed response */ ++ switch (msg_type) { ++ case MSG_TYPE_NOTIFICATION: ++ xfer->rx.len = length - ++ sizeof(msg->input->notification); ++ xfer->rx.buf = msg->input->notification.data; ++ break; ++ case MSG_TYPE_DELAYED_RESP: ++ xfer->rx.len = ++ length - sizeof(msg->input->response); ++ xfer->rx.buf = msg->input->response.data; ++ break; ++ default: ++ dev_warn_once(vioch->cinfo->dev, ++ "rx: unknown message_type %d\n", ++ msg_type); ++ scmi_vio_populate_vq_rx(vioch, xfer); ++ continue; ++ } ++ ++ scmi_rx_callback(vioch->cinfo, msg_hdr); ++ scmi_vio_populate_vq_rx(vioch, xfer); ++ } ++ ++ if (unlikely(virtqueue_is_broken(vqueue))) ++ break; ++ } while (!virtqueue_enable_cb(vqueue)); ++ ++ spin_unlock_irqrestore(&vioch->lock, iflags); ++} ++ ++static const char *const scmi_vio_vqueue_names[] = { "tx", "rx" }; ++ ++static vq_callback_t *scmi_vio_complete_callbacks[] = { ++ scmi_vio_complete_cb, ++ scmi_vio_complete_cb ++}; ++ ++static int scmi_vio_match_any_dev(struct device *dev, const void *data) ++{ ++ (void)dev; ++ (void)data; ++ ++ return 1; ++} ++ ++static struct virtio_driver virtio_scmi_driver; /* Forward declaration */ ++ ++static int virtio_link_supplier(struct device *dev) ++{ ++ struct device *vdev = driver_find_device( ++ &virtio_scmi_driver.driver, NULL, NULL, scmi_vio_match_any_dev); ++ ++ if (!vdev) { ++ dev_notice_once( ++ dev, ++ "Deferring probe after not finding a bound scmi-virtio device\n"); ++ return -EPROBE_DEFER; ++ } ++ ++ /* ++ * Add plain device link for completeness. It might have no effect ++ * beyond sysfs. ++ */ ++ if (!device_link_add(dev, vdev, DL_FLAG_AUTOREMOVE_CONSUMER)) { ++ put_device(vdev); ++ dev_err(dev, "Adding link to supplier virtio device failed\n"); ++ return -ECANCELED; ++ } ++ ++ put_device(vdev); ++ return scmi_set_transport_info(dev, dev_to_virtio(vdev)); ++} ++ ++static bool virtio_chan_available(struct device *dev, int idx) ++{ ++ struct virtio_device *vdev; ++ struct scmi_vio_channel **vioch; ++ ++ /* scmi-virtio doesn't support per-protocol channels */ ++ if (is_scmi_protocol_device(dev)) ++ return false; ++ ++ vdev = scmi_get_transport_info(dev); ++ if (!vdev) ++ return false; ++ ++ vioch = vdev->priv; ++ if (!vioch) ++ return false; ++ ++ return vioch[idx] && vioch[idx]->vqueue; ++} ++ ++static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, ++ bool tx) ++{ ++ struct virtio_device *vdev; ++ struct scmi_vio_channel **vioch; ++ int vioch_index = tx ? VIRTIO_SCMI_VQ_TX : VIRTIO_SCMI_VQ_RX; ++ ++ /* scmi-virtio doesn't support per-protocol channels */ ++ if (is_scmi_protocol_device(dev)) ++ return -1; ++ ++ vdev = scmi_get_transport_info(dev); ++ if (!vdev) ++ return -1; ++ ++ vioch = vdev->priv; ++ if (!vioch) { ++ dev_err(dev, "Data from scmi-virtio probe not found\n"); ++ return -1; ++ } ++ cinfo->transport_info = vioch[vioch_index]; ++ vioch[vioch_index]->cinfo = cinfo; ++ ++ return 0; ++} ++ ++static int virtio_chan_free(int id, void *p, void *data) ++{ ++ struct scmi_chan_info *cinfo = p; ++ struct scmi_vio_channel *vioch = cinfo->transport_info; ++ ++ if (vioch) { ++ cinfo->transport_info = NULL; ++ kfree(vioch); ++ } ++ ++ scmi_free_channel(cinfo, data, id); ++ return 0; ++} ++ ++static int virtio_get_max_msg(bool tx, struct scmi_chan_info *base_cinfo, ++ int *max_msg) ++{ ++ struct scmi_vio_channel *vioch = base_cinfo->transport_info; ++ ++ *max_msg = virtqueue_get_vring_size(vioch->vqueue); ++ ++ /* Tx messages need multiple descriptors. */ ++ if (tx) ++ *max_msg /= DESCR_PER_TX_MSG; ++ ++ if (*max_msg > MSG_TOKEN_MAX) { ++ dev_notice( ++ base_cinfo->dev, ++ "Only %ld messages can be pending simultaneously, while the virtqueue could hold %d\n", ++ MSG_TOKEN_MAX, *max_msg); ++ *max_msg = MSG_TOKEN_MAX; ++ } ++ ++ return 0; ++} ++ ++static int virtio_xfer_init_buffers(struct scmi_chan_info *cinfo, ++ struct scmi_xfer *xfer, int max_msg_size) ++{ ++ struct scmi_vio_channel *vioch = cinfo->transport_info; ++ struct scmi_vio_msg *msg; ++ ++ msg = devm_kzalloc(cinfo->dev, sizeof(*msg), GFP_KERNEL); ++ if (!msg) ++ return -ENOMEM; ++ ++ xfer->extra_data = msg; ++ ++ if (vioch->is_rx) { ++ int rc; ++ unsigned long iflags; ++ ++ msg->input = devm_kzalloc(cinfo->dev, ++ sizeof(*msg->input) + max_msg_size, ++ GFP_KERNEL); ++ if (!msg->input) ++ return -ENOMEM; ++ ++ /* ++ * xfer->rx.buf will be set to notification or delayed response ++ * specific values in the receive callback, according to the ++ * type of the received message. ++ */ ++ ++ spin_lock_irqsave(&vioch->lock, iflags); ++ rc = scmi_vio_populate_vq_rx(vioch, xfer); ++ spin_unlock_irqrestore(&vioch->lock, iflags); ++ if (rc) ++ return rc; ++ } else { ++ msg->request = ++ devm_kzalloc(cinfo->dev, ++ sizeof(*msg->request) + max_msg_size, ++ GFP_KERNEL); ++ if (!msg->request) ++ return -ENOMEM; ++ ++ xfer->tx.buf = msg->request->data; ++ ++ msg->input = devm_kzalloc( ++ cinfo->dev, sizeof(msg->input->response) + max_msg_size, ++ GFP_KERNEL); ++ if (!msg->input) ++ return -ENOMEM; ++ ++ xfer->rx.buf = msg->input->response.data; ++ } ++ ++ return 0; ++} ++ ++static int scmi_vio_send(struct scmi_vio_channel *vioch, struct scmi_xfer *xfer) ++{ ++ struct scatterlist sg_out; ++ struct scatterlist sg_in; ++ struct scatterlist *sgs[DESCR_PER_TX_MSG] = { &sg_out, &sg_in }; ++ struct scmi_vio_msg *msg = xfer->extra_data; ++ unsigned long iflags; ++ int rc; ++ ++ msg->completed = false; ++ ++ sg_init_one(&sg_out, msg->request, ++ sizeof(*msg->request) + xfer->tx.len); ++ sg_init_one(&sg_in, &msg->input->response, ++ sizeof(msg->input->response) + xfer->rx.len); ++ ++ spin_lock_irqsave(&vioch->lock, iflags); ++ rc = virtqueue_add_sgs(vioch->vqueue, sgs, 1, 1, xfer, GFP_ATOMIC); ++ if (rc) ++ dev_err(vioch->cinfo->dev, "%s() rc=%d\n", __func__, rc); ++ else ++ virtqueue_kick(vioch->vqueue); ++ spin_unlock_irqrestore(&vioch->lock, iflags); ++ ++ return rc; ++} ++ ++static int virtio_send_message(struct scmi_chan_info *cinfo, ++ struct scmi_xfer *xfer) ++{ ++ uint32_t hdr; ++ struct scmi_vio_channel *vioch = cinfo->transport_info; ++ struct virtio_device *vdev = vioch->vqueue->vdev; ++ struct scmi_vio_msg *msg = xfer->extra_data; ++ ++ hdr = pack_scmi_header(&xfer->hdr); ++ ++ msg->request->hdr = cpu_to_virtio32(vdev, hdr); ++ ++ return scmi_vio_send(vioch, xfer); ++} ++ ++static void virtio_fetch_response(struct scmi_chan_info *cinfo, ++ struct scmi_xfer *xfer) ++{ ++ struct scmi_vio_channel *vioch = cinfo->transport_info; ++ struct scmi_vio_msg *msg = xfer->extra_data; ++ ++ xfer->hdr.status = virtio32_to_cpu(vioch->vqueue->vdev, ++ msg->input->response.status); ++} ++ ++static void dummy_fetch_notification(struct scmi_chan_info *cinfo, ++ size_t max_len, struct scmi_xfer *xfer) ++{ ++ (void)cinfo; ++ (void)max_len; ++ (void)xfer; ++} ++ ++static void dummy_clear_channel(struct scmi_chan_info *cinfo) ++{ ++ (void)cinfo; ++} ++ ++static bool virtio_poll_done(struct scmi_chan_info *cinfo, ++ struct scmi_xfer *xfer) ++{ ++ struct scmi_vio_channel *vioch = cinfo->transport_info; ++ struct scmi_vio_msg *msg = xfer->extra_data; ++ unsigned long iflags; ++ bool completed; ++ ++ spin_lock_irqsave(&vioch->lock, iflags); ++ completed = msg->completed; ++ spin_unlock_irqrestore(&vioch->lock, iflags); ++ ++ return completed; ++} ++ ++static const struct scmi_transport_ops scmi_virtio_ops = { ++ .link_supplier = virtio_link_supplier, ++ .chan_available = virtio_chan_available, ++ .chan_setup = virtio_chan_setup, ++ .chan_free = virtio_chan_free, ++ .get_max_msg = virtio_get_max_msg, ++ .send_message = virtio_send_message, ++ .fetch_response = virtio_fetch_response, ++ .fetch_notification = dummy_fetch_notification, ++ .clear_channel = dummy_clear_channel, ++ .poll_done = virtio_poll_done, ++ .xfer_init_buffers = virtio_xfer_init_buffers, ++}; ++ ++const struct scmi_desc scmi_virtio_desc = { ++ .ops = &scmi_virtio_ops, ++ .max_rx_timeout_ms = 60000, /* for non-realtime virtio devices */ ++ .max_msg = 0, /* overridden by virtio_get_max_msg() */ ++ .max_msg_size = VIRTIO_SCMI_MAX_MSG_SIZE, ++}; ++ ++static int scmi_vio_probe(struct virtio_device *vdev) ++{ ++ struct device *dev = &vdev->dev; ++ struct scmi_vio_channel **vioch; ++ bool have_vq_rx; ++ int vq_cnt; ++ int i; ++ struct virtqueue *vqs[VIRTIO_SCMI_VQ_MAX_CNT]; ++ ++ vioch = devm_kcalloc(dev, VIRTIO_SCMI_VQ_MAX_CNT, sizeof(*vioch), ++ GFP_KERNEL); ++ if (!vioch) ++ return -ENOMEM; ++ ++ have_vq_rx = virtio_has_feature(vdev, VIRTIO_SCMI_F_P2A_CHANNELS); ++ vq_cnt = have_vq_rx ? VIRTIO_SCMI_VQ_MAX_CNT : 1; ++ ++ for (i = 0; i < vq_cnt; i++) { ++ vioch[i] = devm_kzalloc(dev, sizeof(**vioch), GFP_KERNEL); ++ if (!vioch[i]) ++ return -ENOMEM; ++ } ++ ++ if (have_vq_rx) ++ vioch[VIRTIO_SCMI_VQ_RX]->is_rx = true; ++ ++ if (virtio_find_vqs(vdev, vq_cnt, vqs, scmi_vio_complete_callbacks, ++ scmi_vio_vqueue_names, NULL)) { ++ dev_err(dev, "Failed to get %d virtqueue(s)\n", vq_cnt); ++ return -1; ++ } ++ dev_info(dev, "Found %d virtqueue(s)\n", vq_cnt); ++ ++ for (i = 0; i < vq_cnt; i++) { ++ spin_lock_init(&vioch[i]->lock); ++ vioch[i]->vqueue = vqs[i]; ++ vioch[i]->vqueue->priv = vioch[i]; ++ } ++ ++ vdev->priv = vioch; ++ ++ virtio_device_ready(vdev); ++ ++ return 0; ++} ++ ++static unsigned int features[] = { ++ VIRTIO_SCMI_F_P2A_CHANNELS, ++}; ++ ++static const struct virtio_device_id id_table[] = { ++ { VIRTIO_ID_SCMI, VIRTIO_DEV_ANY_ID }, ++ { 0 } ++}; ++ ++static struct virtio_driver virtio_scmi_driver = { ++ .driver.name = "scmi-virtio", ++ .driver.owner = THIS_MODULE, ++ .feature_table = features, ++ .feature_table_size = ARRAY_SIZE(features), ++ .id_table = id_table, ++ .probe = scmi_vio_probe, ++}; ++ ++int __init virtio_scmi_init(void) ++{ ++ return register_virtio_driver(&virtio_scmi_driver); ++} ++ ++void __exit virtio_scmi_exit(void) ++{ ++ unregister_virtio_driver(&virtio_scmi_driver); ++} +diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h +index bc740d6d2259..287daba2abdb 100644 +--- a/include/uapi/linux/virtio_ids.h ++++ b/include/uapi/linux/virtio_ids.h +@@ -49,5 +49,6 @@ + #define VIRTIO_ID_FS 26 /* virtio filesystem */ + #define VIRTIO_ID_PMEM 27 /* virtio pmem */ + #define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */ ++#define VIRTIO_ID_SCMI 32 /* virtio SCMI */ + + #endif /* _LINUX_VIRTIO_IDS_H */ +diff --git a/include/uapi/linux/virtio_scmi.h b/include/uapi/linux/virtio_scmi.h +new file mode 100644 +index 000000000000..9f21b3dbbfe2 +--- /dev/null ++++ b/include/uapi/linux/virtio_scmi.h +@@ -0,0 +1,41 @@ ++/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ ++/* ++ * Copyright (C) 2020 OpenSynergy GmbH ++ */ ++ ++#ifndef _UAPI_LINUX_VIRTIO_SCMI_H ++#define _UAPI_LINUX_VIRTIO_SCMI_H ++ ++#include ++ ++/* Feature bits */ ++ ++/* Device implements some SCMI notifications, or delayed responses. */ ++#define VIRTIO_SCMI_F_P2A_CHANNELS 0 ++ ++/* Device implements any SCMI statistics shared memory region */ ++#define VIRTIO_SCMI_F_SHARED_MEMORY 1 ++ ++/* Virtqueues */ ++ ++#define VIRTIO_SCMI_VQ_TX 0 /* cmdq */ ++#define VIRTIO_SCMI_VQ_RX 1 /* eventq */ ++#define VIRTIO_SCMI_VQ_MAX_CNT 2 ++ ++struct virtio_scmi_request { ++ __virtio32 hdr; ++ __u8 data[]; ++}; ++ ++struct virtio_scmi_response { ++ __virtio32 hdr; ++ __virtio32 status; ++ __u8 data[]; ++}; ++ ++struct virtio_scmi_notification { ++ __virtio32 hdr; ++ __u8 data[]; ++}; ++ ++#endif /* _UAPI_LINUX_VIRTIO_SCMI_H */ diff --git a/meta-egvirt/recipes-kernel/linux/linux-yocto_%.bbappend b/meta-egvirt/recipes-kernel/linux/linux-yocto_%.bbappend new file mode 100644 index 00000000..a115ce35 --- /dev/null +++ b/meta-egvirt/recipes-kernel/linux/linux-yocto_%.bbappend @@ -0,0 +1,13 @@ +# virtio SCMI +SRC_URI += " \ + file://virtio-scmi/0001-firmware-arm_scmi-smccc-mailbox-Make-shmem-based-tra.patch \ + file://virtio-scmi/0002-firmware-arm_scmi-Document-that-max_msg-is-a-per-cha.patch \ + file://virtio-scmi/0003-firmware-arm_scmi-Add-op-to-override-max-message.patch \ + file://virtio-scmi/0004-firmware-arm_scmi-Add-per-message-transport-data.patch \ + file://virtio-scmi/0005-firmware-arm_scmi-Add-xfer_init_buffers-transport-op.patch \ + file://virtio-scmi/0006-firmware-arm_scmi-Add-optional-link_supplier-transpo.patch \ + file://virtio-scmi/0007-firmware-arm_scmi-Add-per-device-transport-private-i.patch \ + file://virtio-scmi/0008-firmware-arm_scmi-Add-is_scmi_protocol_device.patch \ + file://virtio-scmi/0009-dt-bindings-arm-Add-virtio-transport-for-SCMI.patch \ + file://virtio-scmi/0010-firmware-arm_scmi-Add-virtio-transport.patch \ +" -- cgit 1.2.3-korg