From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id A94D5CCD195 for ; Fri, 17 Oct 2025 13:24:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:To:In-Reply-To:References: Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Cc:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=adfp7LNQr0dg2nRY6gLH983eNTc7IlGRST+yKcVkIFk=; b=XmHuu4V8zX2H3oWxhziIqlutdy fBZBkhTeNgD8HuEw5NJULrloIf92QrTkmDhdfxwk/QHlv6rVqYYOaQXmJ1StKidsh8TWRaj+1ybC4 3sMaMv/aVL34ny3ioyDOi1CuHRABVPtET3FbtZM7oo7j4QbtzZFm65BKzRPbujTRx/ZBfPhvRQEBI qKk4/BGcCQtnNGZW2s8JuERDZNtYNWTzdSk2tOIS4RcVsEimJ8BaLyoCtp3OtlSRkU/9hSMfvSI7n r/n2GYLuRbcHHb0DtG0HmIc+Lk81S0MII7m+2I8W/hvfmANgxWN/ZT10rLmMWSq20lBaZd5lHl8OZ dCfSrO7Q==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux)) id 1v9kRh-00000007ugg-1G3j; Fri, 17 Oct 2025 13:24:37 +0000 Received: from desiato.infradead.org ([2001:8b0:10b:1:d65d:64ff:fe57:4e05]) by bombadil.infradead.org with esmtps (Exim 4.98.2 #2 (Red Hat Linux)) id 1v9kRR-00000007uRL-17nH for linux-arm-kernel@bombadil.infradead.org; Fri, 17 Oct 2025 13:24:21 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=desiato.20200630; h=To:In-Reply-To:References:Message-Id: Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date:From:Sender: Reply-To:Cc:Content-ID:Content-Description; bh=adfp7LNQr0dg2nRY6gLH983eNTc7IlGRST+yKcVkIFk=; b=rIyJDR3gwWpcVx4TbgFPCoCHGB zsklS6+XFYgZF1OQRC4IP8n95Xmme/iw5ZTZb6kM1jJ1Kvk9AtuR/DY9I3Jbgd1XB3nMQM8hxetdq cRsC42W3tLtCt1Ee8O+hE1dVsrMYBotFlA5DI21m4fiHyg0atIGcOpwWqR3i0rWDJR9um9A1ynMRA ZBQnQ5V1r/NT+2IIXNk/EDD8f+PoxGWCQ1VhH6chnkc3107HmLD0/QFSotl0mPFGSoAhKnxQT9Bh7 VXl207U0OcW6fnq0fEg34Hjagb8I+sKwFpbAjKyoXhp6zJb+CAdUUPZvGmQ5AjpBc7f4JDFc7S44x rwNvXybQ==; Received: from foss.arm.com ([217.140.110.172]) by desiato.infradead.org with esmtp (Exim 4.98.2 #2 (Red Hat Linux)) id 1v9kRO-00000007c2C-0foX for linux-arm-kernel@lists.infradead.org; Fri, 17 Oct 2025 13:24:20 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 627DA1596; Fri, 17 Oct 2025 06:24:09 -0700 (PDT) Received: from e133711.arm.com (e133711.arm.com [10.1.196.55]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 8F3CC3F66E; Fri, 17 Oct 2025 06:24:16 -0700 (PDT) From: Sudeep Holla Date: Fri, 17 Oct 2025 14:23:50 +0100 Subject: [PATCH 7/8] firmware: arm_scmi: transport: Add ACPI PCC transport MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20251017-acpi_scmi_pcc-v1-7-0adbab7709d9@arm.com> References: <20251017-acpi_scmi_pcc-v1-0-0adbab7709d9@arm.com> In-Reply-To: <20251017-acpi_scmi_pcc-v1-0-0adbab7709d9@arm.com> To: Cristian Marussi , arm-scmi@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Sudeep Holla X-Mailer: b4 0.14.3 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20251017_142418_658025_528CF78D X-CRM114-Status: GOOD ( 27.26 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org Introduce a new SCMI transport that uses ACPI PCCT (PCC) subspaces via the Linux PCC mailbox layer. The driver parses ACPI _DSD data to map protocols to PCC subspace UIDs, supports shared TX/RX channels, and optionally sets up a P2A channel for notifications. Key points: - new CONFIG_ARM_SCMI_TRANSPORT_PCC option - integration with SCMI core via scmi_desc and transport ops - response/notification fetch from PCC shared memory header/payload - ACPI device matching and registration via the ACPI transport macro This enables SCMI to be exercised over PCC on ACPI platforms. Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/common.h | 11 + drivers/firmware/arm_scmi/transports/Kconfig | 12 + drivers/firmware/arm_scmi/transports/Makefile | 2 + drivers/firmware/arm_scmi/transports/pcc.c | 390 ++++++++++++++++++++++++++ 4 files changed, 415 insertions(+) diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 11831da27d31..9afab6fb78be 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -440,6 +440,17 @@ struct scmi_transport_core_operations { const struct scmi_message_operations *msg; }; +struct scmi_dsd_info { + u32 protocol_id; + const char *const property_name; +}; + +static struct scmi_dsd_info scmi_dsd_info_list[] = { + { SCMI_PROTOCOL_BASE, "arm-arml0001-transport-pcc"}, + { SCMI_PROTOCOL_POWERCAP, "arm-arml0001-protocol-pcap"}, + { SCMI_PROTOCOL_TELEMETRY, "arm-arml0001-protocol-telemetry"}, +}; + /** * struct scmi_transport - A structure representing a configured transport * diff --git a/drivers/firmware/arm_scmi/transports/Kconfig b/drivers/firmware/arm_scmi/transports/Kconfig index 57eccf316e26..4936078f279f 100644 --- a/drivers/firmware/arm_scmi/transports/Kconfig +++ b/drivers/firmware/arm_scmi/transports/Kconfig @@ -77,6 +77,18 @@ config ARM_SCMI_TRANSPORT_OPTEE This driver can also be built as a module. If so, the module will be called scmi_transport_optee. +config ARM_SCMI_TRANSPORT_PCC + tristate "SCMI transport based on ACPI PCC" + depends on PCC + select ARM_SCMI_HAVE_TRANSPORT + help + Enable ACPI PCC mailbox based transport for SCMI. + + If you want the ARM SCMI PROTOCOL stack to include support for a + transport based on mailboxes, answer Y. + This driver can also be built as a module. If so, the module + will be called scmi_transport_pcc. + config ARM_SCMI_TRANSPORT_VIRTIO tristate "SCMI transport based on VirtIO" depends on VIRTIO diff --git a/drivers/firmware/arm_scmi/transports/Makefile b/drivers/firmware/arm_scmi/transports/Makefile index 3ba3d3bee151..e7c4b8de6251 100644 --- a/drivers/firmware/arm_scmi/transports/Makefile +++ b/drivers/firmware/arm_scmi/transports/Makefile @@ -7,6 +7,8 @@ scmi_transport_mailbox-objs := mailbox.o obj-$(CONFIG_ARM_SCMI_TRANSPORT_MAILBOX) += scmi_transport_mailbox.o scmi_transport_optee-objs := optee.o obj-$(CONFIG_ARM_SCMI_TRANSPORT_OPTEE) += scmi_transport_optee.o +scmi_transport_pcc-objs := pcc.o +obj-$(CONFIG_ARM_SCMI_TRANSPORT_PCC) += scmi_transport_pcc.o scmi_transport_virtio-objs := virtio.o obj-$(CONFIG_ARM_SCMI_TRANSPORT_VIRTIO) += scmi_transport_virtio.o diff --git a/drivers/firmware/arm_scmi/transports/pcc.c b/drivers/firmware/arm_scmi/transports/pcc.c new file mode 100644 index 000000000000..39ef83e2dfd4 --- /dev/null +++ b/drivers/firmware/arm_scmi/transports/pcc.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * System Control and Management Interface (SCMI) Message ACPI PCC + * Transport Driver + * + * This transport uses ACPI PCC (PCCT Type 3/4) subspaces via the Linux + * PCC mailbox controller to exchange SCMI messages over the standard + * SCMI Shared Memory Transport (SMT) layout. + * + * PCC subspace selection is conveyed via ACPI SCMI namespace device. + * + * Copyright (C) 2025 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common.h" + +#define SCMI_SHMEM_FLAG_INTR_ENABLED BIT(0) +#define SCMI_TRANSPORT_PACKAGE_MAX_VERSION (1) +#define SCMI_PROTOCOL_PACKAGE_MAX_VERSION (1) +#define SCMI_TRANSPORT_SHARED_CHANNEL BIT(0) +#define SCMI_TRANSPORT_P2A_CHANNEL BIT(1) + +/* + * SCMI specification requires all parameters, message headers, return + * arguments or any protocol data to be expressed in little endian + * format only. + */ +struct pcc_shared_mem { + struct acpi_pcct_ext_pcc_shared_memory header; + u8 msg_payload[]; +}; + +/** + * struct scmi_pcc - Structure representing a SCMI mailbox transport + * + * @cl: Mailbox Client + * @pchan: Transmit/Receive PCC/mailbox channel + * @cinfo: SCMI channel info + * @shmem: Transmit/Receive shared memory area + */ +struct scmi_pcc { + struct mbox_client cl; + struct pcc_mbox_chan *pchan; + struct scmi_chan_info *cinfo; +}; + +struct pcc_transport { + u32 pcc_ss_id; + u32 protocol_id; + u32 flags; + struct hlist_node hnode; +}; + +/* Not all MAX_PCC_SUBSPACES will be used for SCMI, keeping it at 16 for now */ +static DECLARE_HASHTABLE(pcc_id_hash, ilog2(MAX_PCC_SUBSPACES / 8)); + +#define client_to_scmi_pcc(c) container_of(c, struct scmi_pcc, cl) + +static struct scmi_transport_core_operations *core; + +static int acpi_scmi_dsd_parse_transport_package(const union acpi_object *obj) +{ + unsigned int revision = obj->package.elements[0].integer.value; + unsigned int pkg_cnt = obj->package.elements[1].integer.value; + int idx; + + if (revision > SCMI_TRANSPORT_PACKAGE_MAX_VERSION) + return -EINVAL; + + for (idx = 0; idx < pkg_cnt; idx++) { + union acpi_object *pack = &obj->package.elements[idx + 2]; + struct pcc_transport *p, *tmp; + u32 uid; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + if (pack->type != ACPI_TYPE_PACKAGE || pack->package.count != 3) + pr_info("Invalid transport properties pkg %d\n", idx); + + uid = pack->package.elements[1].integer.value; + p->pcc_ss_id = pack->package.elements[0].integer.value; + p->flags = pack->package.elements[2].integer.value; + + hash_for_each_possible(pcc_id_hash, tmp, hnode, uid) + if (tmp) { + pr_info("Duplicate UID %d\n", uid); + return -EEXIST; + } + + if (p->flags & SCMI_TRANSPORT_SHARED_CHANNEL) + p->protocol_id = SCMI_PROTOCOL_BASE; + + hash_add(pcc_id_hash, &p->hnode, uid); + } + + return 0; +} + +static int acpi_scmi_dsd_parse_protocol_subpackage(const union acpi_object *obj, + int prot_id) +{ + u32 uid; + int idx, ret = 0; + struct pcc_transport *p; + unsigned int pkg_cnt = obj->package.count; + + if (pkg_cnt > 2) { + pr_warn("Only 2 channels: one Tx and one Rx needed\n"); + return -EINVAL; + } + + for (idx = 0; idx < pkg_cnt; idx++) { + union acpi_object *pack = &obj->package.elements[idx]; + + /* Flags(pack->package.elements[1]) must be always 0 for now */ + uid = pack->package.elements[0].integer.value; + hash_for_each_possible(pcc_id_hash, p, hnode, uid) { + if (p->flags & SCMI_TRANSPORT_SHARED_CHANNEL) { + pr_info("Invalid! %d channel is shared\n", + p->pcc_ss_id); + ret = -EINVAL; + break; + } + p->protocol_id = prot_id; + } + } + + return ret; +} + +static int +acpi_scmi_dsd_parse_protocol_package(const union acpi_object *obj, int prot_id) +{ + unsigned int revision = obj->package.elements[0].integer.value; + union acpi_object *pack = &obj->package.elements[1]; + + if (revision > SCMI_PROTOCOL_PACKAGE_MAX_VERSION) + return -EINVAL; + + if (pack->type != ACPI_TYPE_PACKAGE) { + pr_info("Invalid protocol transport package\n"); + return -EINVAL; + } + + /* Empty protocol specific transport package allowed */ + if (pack->package.count != 0) + acpi_scmi_dsd_parse_protocol_subpackage(pack, prot_id); + + pack = &obj->package.elements[2]; + if (pack->type != ACPI_TYPE_PACKAGE) + pr_info("Invalid protocol transport association package\n"); + + if (pack->package.count != 0) + pr_info("Non-empty association package not supported\n"); + + return 0; +} + +static int acpi_scmi_namespace_device_parse(struct fwnode_handle *fwnode) +{ + struct acpi_device *adev = to_acpi_device_node(fwnode); + const union acpi_object *obj; + int ret, idx; + + hash_init(pcc_id_hash); + + for (idx = 0; idx < ARRAY_SIZE(scmi_dsd_info_list); idx++) { + int prot_id = scmi_dsd_info_list[idx].protocol_id; + const char *propname = scmi_dsd_info_list[idx].property_name; + + ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, &obj); + if (ret) + continue; + + if (obj->type != ACPI_TYPE_PACKAGE) { + dev_warn(&adev->dev, "PACKAGE property expected!\n"); + break; + } + + if (prot_id == SCMI_PROTOCOL_BASE) + acpi_scmi_dsd_parse_transport_package(obj); + else + acpi_scmi_dsd_parse_protocol_package(obj, prot_id); + } + + return 0; +} + +static int pcc_get_ss_id(u32 prot_id, bool tx) +{ + struct pcc_transport *p; + int idx; + + hash_for_each(pcc_id_hash, idx, p, hnode) + if (p->protocol_id == prot_id) { + if ((!tx && (p->flags & SCMI_TRANSPORT_P2A_CHANNEL)) || + (tx && !(p->flags & SCMI_TRANSPORT_P2A_CHANNEL))) + return p->pcc_ss_id; + } + + return -ENOENT; +} + +static bool +pcc_chan_available(struct fwnode_handle *fwnode, int prot_id, int idx) +{ + /* + * Just parse the ACPI SCMI namespace device once for idx = 0, + * defer full validation until pcc_chan_setup() for simplicity. + */ + if (!idx) + acpi_scmi_namespace_device_parse(fwnode); + + if (pcc_get_ss_id(prot_id, idx ? false : true) < 0) + return false; + + return true; +} + +static void tx_prepare(struct mbox_client *cl, void *m) +{ + struct scmi_pcc *smbox = client_to_scmi_pcc(cl); + struct pcc_shared_mem __iomem *shmem = smbox->pchan->shmem; + struct scmi_xfer *xfer = m; + + /* PCC take cares not to call tx_prepare until last transmit is done */ + /* Mark channel busy + clear error */ + iowrite32(SCMI_SHMEM_FLAG_INTR_ENABLED, &shmem->header.flags); + iowrite32(sizeof(shmem->header.command) + xfer->tx.len, + &shmem->header.length); + iowrite32(pack_scmi_header(&xfer->hdr), &shmem->header.command); + if (xfer->tx.buf) + memcpy_toio(shmem->msg_payload, xfer->tx.buf, xfer->tx.len); +} + +static void rx_callback(struct mbox_client *cl, void *m) +{ + struct scmi_pcc *smbox = client_to_scmi_pcc(cl); + struct pcc_shared_mem __iomem *shmem = smbox->pchan->shmem; + + core->rx_callback(smbox->cinfo, ioread32(&shmem->header.command), NULL); +} + +static int pcc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, + bool tx) +{ + const char *desc = tx ? "Tx" : "Rx"; + struct device *cdev = cinfo->dev; + struct scmi_pcc *smbox; + int ret, ss_id; + struct mbox_client *cl; + + smbox = devm_kzalloc(dev, sizeof(*smbox), GFP_KERNEL); + if (!smbox) + return -ENOMEM; + + cl = &smbox->cl; + cl->dev = cdev; + cl->tx_prepare = tx ? tx_prepare : NULL; + cl->rx_callback = rx_callback; + cl->tx_block = false; + + ss_id = pcc_get_ss_id(cinfo->id, tx); + if (ss_id < 0) + return ss_id; + + smbox->pchan = pcc_mbox_request_channel(cl, ss_id); + if (IS_ERR(smbox->pchan)) { + ret = PTR_ERR(smbox->pchan); + if (ret != -EPROBE_DEFER) + dev_err(cdev, + "failed to request SCMI %s mailbox\n", desc); + return ret; + } + + cinfo->transport_info = smbox; + smbox->cinfo = cinfo; + + return 0; +} + +static int pcc_chan_free(int id, void *p, void *data) +{ + struct scmi_chan_info *cinfo = p; + struct scmi_pcc *smbox = cinfo->transport_info; + + if (smbox && !IS_ERR(smbox->pchan)) { + pcc_mbox_free_channel(smbox->pchan); + cinfo->transport_info = NULL; + smbox->pchan = NULL; + smbox->cinfo = NULL; + } + + return 0; +} + +static int +pcc_send_message(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) +{ + struct scmi_pcc *smbox = cinfo->transport_info; + int ret; + + /* + * The mailbox layer has its own queue. However the mailbox queue + * confuses the per message SCMI timeouts since the clock starts when + * the message is submitted into the mailbox queue. So when multiple + * messages are queued up the clock starts on all messages instead of + * only the one inflight. + */ + ret = mbox_send_message(smbox->pchan->mchan, xfer); + /* mbox_send_message returns non-negative value on success */ + if (ret < 0) + return ret; + + return 0; +} + +static void pcc_fetch_response(struct scmi_chan_info *cinfo, + struct scmi_xfer *xfer) +{ + struct scmi_pcc *smbox = cinfo->transport_info; + struct pcc_shared_mem __iomem *shmem = smbox->pchan->shmem; + size_t len = ioread32(&shmem->header.length); + + xfer->hdr.status = ioread32(shmem->msg_payload); + /* Skip the length of header and status in shmem area i.e 8 bytes */ + xfer->rx.len = min_t(size_t, xfer->rx.len, len > 8 ? len - 8 : 0); + + /* Take a copy to the rx buffer.. */ + memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len); +} + +static void pcc_fetch_notification(struct scmi_chan_info *cinfo, size_t max_len, + struct scmi_xfer *xfer) +{ + struct scmi_pcc *smbox = cinfo->transport_info; + struct pcc_shared_mem __iomem *shmem = smbox->pchan->shmem; + size_t len = ioread32(&shmem->header.length); + + /* Skip only the length of header in shmem area i.e 4 bytes */ + xfer->rx.len = min_t(size_t, max_len, len > 4 ? len - 4 : 0); + + /* Take a copy to the rx buffer.. */ + memcpy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len); +} + +static const struct scmi_transport_ops scmi_pcc_ops = { + .chan_available = pcc_chan_available, + .chan_setup = pcc_chan_setup, + .chan_free = pcc_chan_free, + .send_message = pcc_send_message, + .fetch_response = pcc_fetch_response, + .fetch_notification = pcc_fetch_notification, +}; + +static struct scmi_desc scmi_pcc_desc = { + .ops = &scmi_pcc_ops, + .max_rx_timeout_ms = 30, /* We may increase this if required */ + .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ + .max_msg_size = SCMI_SHMEM_MAX_PAYLOAD_SIZE, +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id scmi_acpi_ids[] = { + {"ARML0001", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, scmi_acpi_ids); +#endif + +DEFINE_SCMI_ACPI_TRANSPORT_DRIVER(scmi_pcc, scmi_pcc_driver, + scmi_pcc_desc, scmi_acpi_ids, core); +module_platform_driver(scmi_pcc_driver); + +MODULE_AUTHOR("Sudeep Holla "); +MODULE_DESCRIPTION("SCMI ACPI PCC Transport driver"); +MODULE_LICENSE("GPL"); -- 2.34.1