From nobody Mon Dec 19 22:30:43 2022 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4NbZD35pyMz1G0gP; Mon, 19 Dec 2022 22:30:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4NbZD35XHQz3HtK; Mon, 19 Dec 2022 22:30:43 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1671489043; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=1i7FSirGQs9dlkKrn3RK/IHxs9a6GbCMAlUQ4UwWGpU=; b=JVtOt5jGGUatJL8wJnG78mJAxMwUHjyK4+pEmx+rnmLjP3t0nbXb7AM8VqqqxiFEfG/6Ti kCPOxQ26Z2vSlIoXKvL4G/aJU3xf+C8cfjHTf2LxjFNhBBral2NJnE44Q4liQXxgAZZhKW IhNGvyOawD2sEzuVTOiwzCRlUi2a2pxZXJZJSogXqoiizj96yMP/dyvihnoJvUkOplPrjN Tcmx7BxK9OpPlP0kZ8z1wWrGKBrFDULvyo6wKN7R5ZaKLaRPa3+RWyN+v1wNFVIaRCCxZB ql8uEBdrqC4N1PeVCrmcFJ4TufQxkQ7EJ+asTfGwHrDsNKuCsdug9W2RhdJsvw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1671489043; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=1i7FSirGQs9dlkKrn3RK/IHxs9a6GbCMAlUQ4UwWGpU=; b=mkwpv4t0qoecXtrWE9Kmc//N8rm9pk8yBJt4O/OU1kKt/fJDhP5iS8Sypr08kJXrfEftkl /NHit0yCoALtt2xCPAGx4leyXqZ7HBdYYyvLR1g1XJpOllUuSP+aggCXS7jO07I1YpU9wa aY+op9hHdZD/lqj60ol2hvTZXPEfgOuJ5oaHI34I5zqhbs8R6/zIlO0AgLC7Zq3l5eLQzn ej6bGyfv5JmAJ6ZkqD7oSXProGUd/JQ+7PAPO8tv/LpYWaCGwO7lt/UnnFc/xfRtlHXS9P E9TfCIInjAUhgSQF4p2aHWLOSHZsNqAlm98sXV2gmqg1Rdr37/OujT3Q6pyLvQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1671489043; a=rsa-sha256; cv=none; b=kmNtYGrSru580aNL0EsKUotKwhQEqJPPIAvCEmccryigR8nxraifYdfFdfBjEGxcpMOgnp 5lS6ozzBiB6KDpRRyFTOPyW+frH9Y77wP2ygBdG1lFUAkBLfQiboAlZWvVTdvEzz5bXC+E 5xlxPTAe8oY+HRO+3uNwwL4ijt4FPw8K5xSo/1wa7K9md8ltAxl8yVkDTbPEzOtEzaY7BG MPNXNuru94ufM3WTTWWfU+O5Yqr0RHKMde2r/09aEHpLzZW/LoGCXjlCipoDTu1lbCRPXn SwtFi4KS/e2us6rTmzh0he9A43mHYLEEQoifHF9ViDiRdvrhKFD+9Qaa+p2s8g== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4NbZD34c13z1BjW; Mon, 19 Dec 2022 22:30:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 2BJMUhne081648; Mon, 19 Dec 2022 22:30:43 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 2BJMUhRj081647; Mon, 19 Dec 2022 22:30:43 GMT (envelope-from git) Date: Mon, 19 Dec 2022 22:30:43 GMT Message-Id: <202212192230.2BJMUhRj081647@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Ruslan Bukin Subject: git: 54b96380f577 - main - Add support for ARM System Control and Management Interface (SCMI) v3.1. List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: br X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 54b96380f5774c1754a0fcf25212fa8e01db74f6 Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by br: URL: https://cgit.FreeBSD.org/src/commit/?id=54b96380f5774c1754a0fcf25212fa8e01db74f6 commit 54b96380f5774c1754a0fcf25212fa8e01db74f6 Author: Ruslan Bukin AuthorDate: 2022-12-19 20:16:18 +0000 Commit: Ruslan Bukin CommitDate: 2022-12-19 22:28:21 +0000 Add support for ARM System Control and Management Interface (SCMI) v3.1. The SCMI specification describes a set of standard interfaces for power, performance and system management. SCMI is extensible and provides interfaces to access functions which are often implemented in firmwares in the System Control Processor (SCP). This implements Shared Memory-based transfer, which is one of the ways on how messages are exchanged between agents and the platform. This includes a driver for ARM Message Handling Unit (MHU) Doorbell, which is a mechanism that the caller can use to alert the callee of the presence of a message. The support implements clock management interface. For instance this allows us to control HDMI pixel clock on ARM Morello Board. Tested on ARM Morello Board. Obtained from: CheriBSD Differential Revision: https://reviews.freebsd.org/D37316 Reviewed by: manu Sponsored by: UKRI --- sys/arm64/conf/std.arm | 2 + sys/arm64/conf/std.dev | 5 + sys/conf/files.arm64 | 8 + sys/dev/firmware/arm/scmi.c | 273 +++++++++++++++++++++ sys/dev/firmware/arm/scmi.h | 82 +++++++ sys/dev/firmware/arm/scmi_clk.c | 434 ++++++++++++++++++++++++++++++++++ sys/dev/firmware/arm/scmi_clk.h | 116 +++++++++ sys/dev/firmware/arm/scmi_protocols.h | 63 +++++ sys/dev/firmware/arm/scmi_shmem.c | 146 ++++++++++++ sys/dev/mailbox/arm/arm_doorbell.c | 347 +++++++++++++++++++++++++++ sys/dev/mailbox/arm/arm_doorbell.h | 51 ++++ sys/dev/sram/mmio_sram.c | 164 +++++++++++++ sys/dev/sram/mmio_sram_if.m | 44 ++++ 13 files changed, 1735 insertions(+) diff --git a/sys/arm64/conf/std.arm b/sys/arm64/conf/std.arm index af1958cef073..d17c80b78fff 100644 --- a/sys/arm64/conf/std.arm +++ b/sys/arm64/conf/std.arm @@ -11,5 +11,7 @@ device pl061 # Arm PL061 GPIO controller # Serial (COM) ports device pl011 +device arm_doorbell # ARM Message Handling Unit (MHU) + options FDT device acpi diff --git a/sys/arm64/conf/std.dev b/sys/arm64/conf/std.dev index 6ef7358e5e85..affe3d3014a3 100644 --- a/sys/arm64/conf/std.dev +++ b/sys/arm64/conf/std.dev @@ -107,3 +107,8 @@ device mmcsd # mmc/sd flash cards # HID support options HID_DEBUG # enable debug msgs device hid # Generic HID support + +# Firmware +device scmi # System Control Management Interface + +device mmio_sram # Generic on-chip SRAM diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index a009a19b1359..455018bc70b3 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -210,6 +210,10 @@ dev/enetc/if_enetc.c optional enetc iflib pci fdt soc_nxp_ls dev/etherswitch/felix/felix.c optional enetc etherswitch fdt felix pci soc_nxp_ls +dev/firmware/arm/scmi.c optional fdt scmi +dev/firmware/arm/scmi_clk.c optional fdt scmi +dev/firmware/arm/scmi_shmem.c optional fdt scmi + dev/gpio/pl061.c optional pl061 gpio dev/gpio/pl061_acpi.c optional pl061 gpio acpi dev/gpio/pl061_fdt.c optional pl061 gpio fdt @@ -311,6 +315,7 @@ dev/ipmi/ipmi_acpi.c optional ipmi acpi dev/ipmi/ipmi_kcs.c optional ipmi dev/ipmi/ipmi_smic.c optional ipmi +dev/mailbox/arm/arm_doorbell.c optional fdt arm_doorbell dev/mbox/mbox_if.m optional soc_brcm_bcm2837 dev/mmc/host/dwmmc.c optional dwmmc fdt @@ -343,6 +348,9 @@ dev/sdhci/sdhci_xenon.c optional sdhci_xenon sdhci dev/sdhci/sdhci_xenon_acpi.c optional sdhci_xenon sdhci acpi dev/sdhci/sdhci_xenon_fdt.c optional sdhci_xenon sdhci fdt +dev/sram/mmio_sram.c optional fdt mmio_sram +dev/sram/mmio_sram_if.m optional fdt mmio_sram + dev/uart/uart_cpu_arm64.c optional uart dev/uart/uart_dev_mu.c optional uart uart_mu fdt dev/uart/uart_dev_pl011.c optional uart pl011 diff --git a/sys/dev/firmware/arm/scmi.c b/sys/dev/firmware/arm/scmi.c new file mode 100644 index 000000000000..deb334ecfb68 --- /dev/null +++ b/sys/dev/firmware/arm/scmi.c @@ -0,0 +1,273 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dev/mailbox/arm/arm_doorbell.h" + +#include "scmi.h" +#include "scmi_protocols.h" + +struct scmi_softc { + struct simplebus_softc simplebus_sc; + device_t dev; + device_t tx_shmem; + struct arm_doorbell *db; + struct mtx mtx; + int req_done; +}; + +static device_t +scmi_get_shmem(struct scmi_softc *sc, int index) +{ + phandle_t *shmems; + phandle_t node; + device_t dev; + size_t len; + + node = ofw_bus_get_node(sc->dev); + if (node <= 0) + return (NULL); + + len = OF_getencprop_alloc_multi(node, "shmem", sizeof(*shmems), + (void **)&shmems); + if (len <= 0) { + device_printf(sc->dev, "%s: Can't get shmem node.\n", __func__); + return (NULL); + } + + if (index >= len) { + OF_prop_free(shmems); + return (NULL); + } + + dev = OF_device_from_xref(shmems[index]); + if (dev == NULL) + device_printf(sc->dev, "%s: Can't get shmem device.\n", + __func__); + + OF_prop_free(shmems); + + return (dev); +} + +static void +scmi_callback(void *arg) +{ + struct scmi_softc *sc; + + sc = arg; + + dprintf("%s sc %p\n", __func__, sc); + + SCMI_LOCK(sc); + sc->req_done = 1; + wakeup(sc); + SCMI_UNLOCK(sc); +} + +static int +scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req) +{ + struct scmi_smt_header hdr; + int timeout; + + bzero(&hdr, sizeof(struct scmi_smt_header)); + + SCMI_ASSERT_LOCKED(sc); + + /* Read header */ + scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); + + if ((hdr.channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0) + return (1); + + /* Update header */ + hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE; + hdr.msg_header = req->protocol_id << SMT_HEADER_PROTOCOL_ID_S; + hdr.msg_header |= req->message_id << SMT_HEADER_MESSAGE_ID_S; + hdr.length = sizeof(hdr.msg_header) + req->in_size; + hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; + + /* Write header */ + scmi_shmem_write(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); + + /* Write request */ + scmi_shmem_write(sc->tx_shmem, SMT_HEADER_SIZE, req->in_buf, + req->in_size); + + sc->req_done = 0; + + /* Interrupt SCP firmware. */ + arm_doorbell_set(sc->db); + + timeout = 200; + + dprintf("%s: request\n", __func__); + + do { + if (cold) { + if (arm_doorbell_get(sc->db)) + break; + DELAY(10000); + } else { + msleep(sc, &sc->mtx, 0, "scmi", hz / 10); + if (sc->req_done) + break; + } + } while (timeout--); + + if (timeout <= 0) + return (-1); + + dprintf("%s: got reply, timeout %d\n", __func__, timeout); + + /* Read header. */ + scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); + + /* Read response */ + scmi_shmem_read(sc->tx_shmem, SMT_HEADER_SIZE, req->out_buf, + req->out_size); + + return (0); +} + +int +scmi_request(device_t dev, struct scmi_req *req) +{ + struct scmi_softc *sc; + int error; + + sc = device_get_softc(dev); + + SCMI_LOCK(sc); + error = scmi_request_locked(sc, req); + SCMI_UNLOCK(sc); + + return (error); +} + +static int +scmi_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "arm,scmi")) + return (ENXIO); + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + device_set_desc(dev, "ARM SCMI interface driver"); + + return (BUS_PROBE_DEFAULT); +} + +static int +scmi_attach(device_t dev) +{ + struct scmi_softc *sc; + phandle_t node; + int error; + + sc = device_get_softc(dev); + sc->dev = dev; + + node = ofw_bus_get_node(dev); + if (node == -1) + return (ENXIO); + + sc->tx_shmem = scmi_get_shmem(sc, 0); + if (sc->tx_shmem == NULL) { + device_printf(dev, "TX shmem dev not found.\n"); + return (ENXIO); + } + + sc->db = arm_doorbell_ofw_get(sc->dev, "tx"); + if (sc->db == NULL) { + device_printf(dev, "Doorbell device not found.\n"); + return (ENXIO); + } + + mtx_init(&sc->mtx, device_get_nameunit(dev), "SCMI", MTX_DEF); + + arm_doorbell_set_handler(sc->db, scmi_callback, sc); + + simplebus_init(dev, node); + + /* + * Allow devices to identify. + */ + bus_generic_probe(dev); + + /* + * Now walk the OFW tree and attach top-level devices. + */ + for (node = OF_child(node); node > 0; node = OF_peer(node)) + simplebus_add_device(dev, node, 0, NULL, -1, NULL); + + error = bus_generic_attach(dev); + + return (error); +} + +static int +scmi_detach(device_t dev) +{ + + return (0); +} + +static device_method_t scmi_methods[] = { + DEVMETHOD(device_probe, scmi_probe), + DEVMETHOD(device_attach, scmi_attach), + DEVMETHOD(device_detach, scmi_detach), + DEVMETHOD_END +}; + +DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc), + simplebus_driver); + +DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0); +MODULE_VERSION(scmi, 1); diff --git a/sys/dev/firmware/arm/scmi.h b/sys/dev/firmware/arm/scmi.h new file mode 100644 index 000000000000..d2ced74840f7 --- /dev/null +++ b/sys/dev/firmware/arm/scmi.h @@ -0,0 +1,82 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _ARM64_SCMI_SCMI_H_ +#define _ARM64_SCMI_SCMI_H_ + +#define SCMI_LOCK(sc) mtx_lock(&(sc)->mtx) +#define SCMI_UNLOCK(sc) mtx_unlock(&(sc)->mtx) +#define SCMI_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED) + +#define dprintf(fmt, ...) + +/* Shared Memory Transfer. */ +struct scmi_smt_header { + uint32_t reserved; + uint32_t channel_status; +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR (1 << 1) +#define SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE (1 << 0) + uint32_t reserved1[2]; + uint32_t flags; +#define SCMI_SHMEM_FLAG_INTR_ENABLED (1 << 0) + uint32_t length; + uint32_t msg_header; + uint8_t msg_payload[0]; +}; + +#define SMT_HEADER_SIZE sizeof(struct scmi_smt_header) + +#define SMT_HEADER_TOKEN_S 18 +#define SMT_HEADER_TOKEN_M (0x3fff << SMT_HEADER_TOKEN_S) +#define SMT_HEADER_PROTOCOL_ID_S 10 +#define SMT_HEADER_PROTOCOL_ID_M (0xff << SMT_HEADER_PROTOCOL_ID_S) +#define SMT_HEADER_MESSAGE_TYPE_S 8 +#define SMT_HEADER_MESSAGE_TYPE_M (0x3 << SMT_HEADER_MESSAGE_TYPE_S) +#define SMT_HEADER_MESSAGE_ID_S 0 +#define SMT_HEADER_MESSAGE_ID_M (0xff << SMT_HEADER_MESSAGE_ID_S) + +struct scmi_req { + int protocol_id; + int message_id; + const void *in_buf; + uint32_t in_size; + void *out_buf; + uint32_t out_size; +}; + +int scmi_request(device_t dev, struct scmi_req *req); +void scmi_shmem_read(device_t dev, bus_size_t offset, void *buf, + bus_size_t len); +void scmi_shmem_write(device_t dev, bus_size_t offset, const void *buf, + bus_size_t len); + +#endif /* !_ARM64_SCMI_SCMI_H_ */ diff --git a/sys/dev/firmware/arm/scmi_clk.c b/sys/dev/firmware/arm/scmi_clk.c new file mode 100644 index 000000000000..a42f0ed2c0ba --- /dev/null +++ b/sys/dev/firmware/arm/scmi_clk.c @@ -0,0 +1,434 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "scmi.h" +#include "scmi_protocols.h" +#include "scmi_clk.h" + +struct scmi_clk_softc { + device_t dev; + device_t scmi; + struct clkdom *clkdom; +}; + +struct scmi_clknode_softc { + device_t dev; + int clock_id; +}; + +static int +scmi_clk_get_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t *rate) +{ + struct scmi_clk_rate_get_out out; + struct scmi_clk_rate_get_in in; + struct scmi_req req; + int error; + + req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; + req.message_id = SCMI_CLOCK_RATE_GET; + req.in_buf = ∈ + req.in_size = sizeof(struct scmi_clk_rate_get_in); + req.out_buf = &out; + req.out_size = sizeof(struct scmi_clk_rate_get_out); + + in.clock_id = clk_id; + + error = scmi_request(sc->scmi, &req); + if (error != 0) + return (error); + + if (out.status != 0) + return (ENXIO); + + *rate = out.rate_lsb | ((uint64_t)out.rate_msb << 32); + + return (0); +} + +static int +scmi_clk_set_rate(struct scmi_clk_softc *sc, int clk_id, uint64_t rate) +{ + struct scmi_clk_rate_set_out out; + struct scmi_clk_rate_set_in in; + struct scmi_req req; + int error; + + req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; + req.message_id = SCMI_CLOCK_RATE_SET; + req.in_buf = ∈ + req.in_size = sizeof(struct scmi_clk_rate_set_in); + req.out_buf = &out; + req.out_size = sizeof(struct scmi_clk_rate_set_out); + + in.clock_id = clk_id; + in.flags = SCMI_CLK_RATE_ROUND_CLOSEST; + in.rate_lsb = (uint32_t)rate; + in.rate_msb = (uint32_t)(rate >> 32); + + error = scmi_request(sc->scmi, &req); + if (error != 0) + return (error); + + if (out.status != 0) + return (ENXIO); + + return (0); +} + +static int +scmi_clk_gate(struct scmi_clk_softc *sc, int clk_id, int enable) +{ + struct scmi_clk_state_out out; + struct scmi_clk_state_in in; + struct scmi_req req; + int error; + + req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; + req.message_id = SCMI_CLOCK_CONFIG_SET; + req.in_buf = ∈ + req.in_size = sizeof(struct scmi_clk_state_in); + req.out_buf = &out; + req.out_size = sizeof(struct scmi_clk_state_out); + + in.clock_id = clk_id; + in.attributes = enable; + + error = scmi_request(sc->scmi, &req); + if (error != 0) + return (error); + + if (out.status != 0) + return (ENXIO); + + return (0); +} + +static int +scmi_clknode_init(struct clknode *clk, device_t dev) +{ + + clknode_init_parent_idx(clk, 0); + + return (0); +} + +static int +scmi_clknode_recalc_freq(struct clknode *clk, uint64_t *freq) +{ + + return (0); +} + +static int +scmi_clknode_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct scmi_clknode_softc *clk_sc; + struct scmi_clk_softc *sc; + + clk_sc = clknode_get_softc(clk); + sc = device_get_softc(clk_sc->dev); + + dprintf("%s: %ld\n", __func__, *fout); + + scmi_clk_set_rate(sc, clk_sc->clock_id, *fout); + + *stop = 1; + + return (0); +} + +static clknode_method_t scmi_clknode_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, scmi_clknode_init), + CLKNODEMETHOD(clknode_recalc_freq, scmi_clknode_recalc_freq), + CLKNODEMETHOD(clknode_set_freq, scmi_clknode_set_freq), + CLKNODEMETHOD_END +}; + +DEFINE_CLASS_1(scmi_clknode, scmi_clknode_class, scmi_clknode_methods, + sizeof(struct scmi_clknode_softc), clknode_class); + +static int +scmi_clk_add_node(struct scmi_clk_softc *sc, int index, char *clock_name) +{ + struct scmi_clknode_softc *clk_sc; + struct clknode_init_def def; + struct clknode *clk; + + memset(&def, 0, sizeof(def)); + def.id = index; + def.name = clock_name; + def.parent_names = NULL; + def.parent_cnt = 0; + + clk = clknode_create(sc->clkdom, &scmi_clknode_class, &def); + if (clk == NULL) { + device_printf(sc->dev, "Cannot create clknode.\n"); + return (ENXIO); + } + + clk_sc = clknode_get_softc(clk); + clk_sc->dev = sc->dev; + clk_sc->clock_id = index; + + if (clknode_register(sc->clkdom, clk) == NULL) { + device_printf(sc->dev, "Could not register clock '%s'.\n", + def.name); + return (ENXIO); + } + + device_printf(sc->dev, "Clock '%s' registered.\n", def.name); + + return (0); +} + +static int +scmi_clk_get_name(struct scmi_clk_softc *sc, int index, char **result) +{ + struct scmi_clk_name_get_out out; + struct scmi_clk_name_get_in in; + struct scmi_req req; + char *clock_name; + int error; + + req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; + req.message_id = SCMI_CLOCK_NAME_GET; + req.in_buf = ∈ + req.in_size = sizeof(struct scmi_clk_name_get_in); + req.out_buf = &out; + req.out_size = sizeof(struct scmi_clk_name_get_out); + + in.clock_id = index; + + error = scmi_request(sc->scmi, &req); + if (error != 0) + return (error); + + if (out.status != 0) + return (ENXIO); + + clock_name = malloc(sizeof(out.name), M_DEVBUF, M_WAITOK); + strncpy(clock_name, out.name, sizeof(out.name)); + + *result = clock_name; + + return (0); +} + +static int +scmi_clk_attrs(struct scmi_clk_softc *sc, int index) +{ + struct scmi_clk_attrs_out out; + struct scmi_clk_attrs_in in; + struct scmi_req req; + int error; + char *clock_name; + + req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; + req.message_id = SCMI_CLOCK_ATTRIBUTES; + req.in_buf = ∈ + req.in_size = sizeof(struct scmi_clk_attrs_in); + req.out_buf = &out; + req.out_size = sizeof(struct scmi_clk_attrs_out); + + in.clock_id = index; + + error = scmi_request(sc->scmi, &req); + if (error != 0) + return (error); + + if (out.status != 0) + return (ENXIO); + + if (out.attributes & CLK_ATTRS_EXT_CLK_NAME) { + error = scmi_clk_get_name(sc, index, &clock_name); + if (error) + return (error); + } else { + clock_name = malloc(sizeof(out.clock_name), M_DEVBUF, M_WAITOK); + strncpy(clock_name, out.clock_name, sizeof(out.clock_name)); + } + + error = scmi_clk_add_node(sc, index, clock_name); + + return (error); +} + +static int +scmi_clk_discover(struct scmi_clk_softc *sc) +{ + struct scmi_clk_protocol_attrs_out out; + struct scmi_req req; + int nclocks; + int failing; + int error; + int i; + + req.protocol_id = SCMI_PROTOCOL_ID_CLOCK; + req.message_id = SCMI_PROTOCOL_ATTRIBUTES; + req.in_buf = NULL; + req.in_size = 0; + req.out_buf = &out; + req.out_size = sizeof(struct scmi_clk_protocol_attrs_out); + + error = scmi_request(sc->scmi, &req); + if (error != 0) + return (error); + + if (out.status != 0) + return (ENXIO); + + nclocks = (out.attributes & CLK_ATTRS_NCLOCKS_M) >> + CLK_ATTRS_NCLOCKS_S; + + device_printf(sc->dev, "Found %d clocks.\n", nclocks); + + failing = 0; + + for (i = 0; i < nclocks; i++) { + error = scmi_clk_attrs(sc, i); + if (error) { + device_printf(sc->dev, + "Could not process clock index %d.\n", i); + failing++; + } + } + + if (failing == nclocks) + return (ENXIO); + + return (0); +} + +static int +scmi_clk_init(struct scmi_clk_softc *sc) +{ + int error; + + /* Create clock domain */ + sc->clkdom = clkdom_create(sc->dev); + if (sc->clkdom == NULL) + return (ENXIO); + + error = scmi_clk_discover(sc); + if (error) { + device_printf(sc->dev, "Could not discover clocks.\n"); + return (ENXIO); + } + + error = clkdom_finit(sc->clkdom); + if (error) { + device_printf(sc->dev, "Failed to init clock domain.\n"); + return (ENXIO); + } + + return (0); +} + +static int +scmi_clk_probe(device_t dev) +{ + phandle_t node; + uint32_t reg; + int error; + + node = ofw_bus_get_node(dev); + + error = OF_getencprop(node, "reg", ®, sizeof(uint32_t)); + if (error < 0) + return (ENXIO); + + if (reg != SCMI_PROTOCOL_ID_CLOCK) + return (ENXIO); + + device_set_desc(dev, "SCMI Clock Management Unit"); + + return (BUS_PROBE_DEFAULT); +} + +static int +scmi_clk_attach(device_t dev) +{ + struct scmi_clk_softc *sc; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->scmi = device_get_parent(dev); + + node = ofw_bus_get_node(sc->dev); + + OF_device_register_xref(OF_xref_from_node(node), sc->dev); + + scmi_clk_init(sc); + + return (0); +} + +static int +scmi_clk_detach(device_t dev) +{ + + return (0); +} + +static device_method_t scmi_clk_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, scmi_clk_probe), + DEVMETHOD(device_attach, scmi_clk_attach), + DEVMETHOD(device_detach, scmi_clk_detach), + DEVMETHOD_END +}; + +static driver_t scmi_clk_driver = { + "scmi_clk", + scmi_clk_methods, + sizeof(struct scmi_clk_softc), +}; + +EARLY_DRIVER_MODULE(scmi_clk, scmi, scmi_clk_driver, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(scmi_clk, 1); diff --git a/sys/dev/firmware/arm/scmi_clk.h b/sys/dev/firmware/arm/scmi_clk.h new file mode 100644 index 000000000000..41e347cc8906 --- /dev/null +++ b/sys/dev/firmware/arm/scmi_clk.h @@ -0,0 +1,116 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Ruslan Bukin + * + * This work was supported by Innovate UK project 105694, "Digital Security + * by Design (DSbD) Technology Platform Prototype". + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _ARM64_SCMI_SCMI_CLK_H_ +#define _ARM64_SCMI_SCMI_CLK_H_ + +/* + * SCMI Clock Protocol + */ + +struct scmi_clk_protocol_attrs_out { + int32_t status; + uint32_t attributes; +#define CLK_ATTRS_NCLOCKS_S 0 +#define CLK_ATTRS_NCLOCKS_M (0xffff << CLK_ATTRS_NCLOCKS_S) +}; + +struct scmi_clk_attrs_in { + uint32_t clock_id; +}; + +struct scmi_clk_attrs_out { + int32_t status; + uint32_t attributes; +#define CLK_ATTRS_RATE_CHANGE_NOTIFY_SUPP (1 << 31) +#define CLK_ATTRS_RATE_REQ_CHANGE_NOTIFY_SUPP (1 << 30) +#define CLK_ATTRS_EXT_CLK_NAME (1 << 29) +#define CLK_ATTRS_ENABLED (1 << 0) + uint8_t clock_name[16]; /* only if attrs bit 29 unset */ + uint32_t clock_enable_delay; /* worst case */ +}; + +struct scmi_clk_name_get_in { + uint32_t clock_id; +}; + +struct scmi_clk_name_get_out { *** 901 LINES SKIPPED ***