Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 19 Dec 2022 22:30:43 GMT
From:      Ruslan Bukin <br@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 54b96380f577 - main - Add support for ARM System Control and Management Interface (SCMI) v3.1.
Message-ID:  <202212192230.2BJMUhRj081647@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by br:

URL: https://cgit.FreeBSD.org/src/commit/?id=54b96380f5774c1754a0fcf25212fa8e01db74f6

commit 54b96380f5774c1754a0fcf25212fa8e01db74f6
Author:     Ruslan Bukin <br@FreeBSD.org>
AuthorDate: 2022-12-19 20:16:18 +0000
Commit:     Ruslan Bukin <br@FreeBSD.org>
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 <br@bsdpad.com>
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#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 <br@bsdpad.com>
+ *
+ * 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 <br@bsdpad.com>
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/fdt/simplebus.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#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 = &in;
+	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 = &in;
+	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 = &in;
+	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 = &in;
+	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 = &in;
+	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", &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 <br@bsdpad.com>
+ *
+ * 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 ***



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202212192230.2BJMUhRj081647>