From 54b96380f5774c1754a0fcf25212fa8e01db74f6 Mon Sep 17 00:00:00 2001 From: Ruslan Bukin Date: Mon, 19 Dec 2022 20:16:18 +0000 Subject: [PATCH] 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(+) create mode 100644 sys/dev/firmware/arm/scmi.c create mode 100644 sys/dev/firmware/arm/scmi.h create mode 100644 sys/dev/firmware/arm/scmi_clk.c create mode 100644 sys/dev/firmware/arm/scmi_clk.h create mode 100644 sys/dev/firmware/arm/scmi_protocols.h create mode 100644 sys/dev/firmware/arm/scmi_shmem.c create mode 100644 sys/dev/mailbox/arm/arm_doorbell.c create mode 100644 sys/dev/mailbox/arm/arm_doorbell.h create mode 100644 sys/dev/sram/mmio_sram.c create mode 100644 sys/dev/sram/mmio_sram_if.m 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 { + int32_t status; + uint32_t flags; + uint8_t name[64]; +}; + +enum scmi_clock_message_id { + SCMI_CLOCK_ATTRIBUTES = 0x3, + SCMI_CLOCK_RATE_SET = 0x5, + SCMI_CLOCK_RATE_GET = 0x6, + SCMI_CLOCK_CONFIG_SET = 0x7, + SCMI_CLOCK_NAME_GET = 0x8, +}; + +#define SCMI_CLK_RATE_ASYNC_NOTIFY (1 << 0) +#define SCMI_CLK_RATE_ASYNC_NORESP (1 << 0 | 1 << 1) +#define SCMI_CLK_RATE_ROUND_DOWN 0 +#define SCMI_CLK_RATE_ROUND_UP (1 << 2) +#define SCMI_CLK_RATE_ROUND_CLOSEST (1 << 3) + +struct scmi_clk_state_in { + uint32_t clock_id; + uint32_t attributes; +}; + +struct scmi_clk_state_out { + int32_t status; +}; + +struct scmi_clk_rate_get_in { + uint32_t clock_id; +}; + +struct scmi_clk_rate_get_out { + int32_t status; + uint32_t rate_lsb; + uint32_t rate_msb; +}; + +struct scmi_clk_rate_set_in { + uint32_t flags; + uint32_t clock_id; + uint32_t rate_lsb; + uint32_t rate_msb; +}; + +struct scmi_clk_rate_set_out { + int32_t status; +}; + +#endif /* !_ARM64_SCMI_SCMI_CLK_H_ */ diff --git a/sys/dev/firmware/arm/scmi_protocols.h b/sys/dev/firmware/arm/scmi_protocols.h new file mode 100644 index 000000000000..aa2eceb15660 --- /dev/null +++ b/sys/dev/firmware/arm/scmi_protocols.h @@ -0,0 +1,63 @@ +/*- + * 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_PROTOCOLS_H_ +#define _ARM64_SCMI_SCMI_PROTOCOLS_H_ + +enum scmi_std_protocol { + SCMI_PROTOCOL_ID_BASE = 0x10, + SCMI_PROTOCOL_ID_POWER_DOMAIN = 0x11, + SCMI_PROTOCOL_ID_SYSTEM = 0x12, + SCMI_PROTOCOL_ID_PERF = 0x13, + SCMI_PROTOCOL_ID_CLOCK = 0x14, + SCMI_PROTOCOL_ID_SENSOR = 0x15, + SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16, + SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17, +}; + +enum scmi_status_code { + SCMI_SUCCESS = 0, + SCMI_NOT_SUPPORTED = -1, + SCMI_INVALID_PARAMETERS = -2, + SCMI_DENIED = -3, + SCMI_NOT_FOUND = -4, + SCMI_OUT_OF_RANGE = -5, + SCMI_BUSY = -6, + SCMI_COMMS_ERROR = -7, + SCMI_GENERIC_ERROR = -8, + SCMI_HARDWARE_ERROR = -9, + SCMI_PROTOCOL_ERROR = -10, +}; + +#define SCMI_PROTOCOL_ATTRIBUTES 0x1 + +#endif /* !_ARM64_SCMI_SCMI_PROTOCOLS_H_ */ diff --git a/sys/dev/firmware/arm/scmi_shmem.c b/sys/dev/firmware/arm/scmi_shmem.c new file mode 100644 index 000000000000..892dc5aee20e --- /dev/null +++ b/sys/dev/firmware/arm/scmi_shmem.c @@ -0,0 +1,146 @@ +/*- + * 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 "mmio_sram_if.h" + +#include "scmi.h" + +struct shmem_softc { + device_t dev; + device_t parent; + int reg; +}; + +static int +shmem_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "arm,scmi-shmem")) + return (ENXIO); + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + device_set_desc(dev, "ARM SCMI Shared Memory driver"); + + return (BUS_PROBE_DEFAULT); +} + +static int +shmem_attach(device_t dev) +{ + struct shmem_softc *sc; + phandle_t node; + int reg; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->parent = device_get_parent(dev); + + node = ofw_bus_get_node(dev); + if (node == -1) + return (ENXIO); + + OF_getencprop(node, "reg", ®, sizeof(reg)); + + dprintf("%s: reg %x\n", __func__, reg); + + sc->reg = reg; + + OF_device_register_xref(OF_xref_from_node(node), dev); + + return (0); +} + +static int +shmem_detach(device_t dev) +{ + + return (0); +} + +void +scmi_shmem_read(device_t dev, bus_size_t offset, void *buf, bus_size_t len) +{ + struct shmem_softc *sc; + uint8_t *addr; + int i; + + sc = device_get_softc(dev); + + addr = (uint8_t *)buf; + + for (i = 0; i < len; i++) + addr[i] = MMIO_SRAM_READ_1(sc->parent, sc->reg + offset + i); +} + +void +scmi_shmem_write(device_t dev, bus_size_t offset, const void *buf, + bus_size_t len) +{ + struct shmem_softc *sc; + const uint8_t *addr; + int i; + + sc = device_get_softc(dev); + + addr = (const uint8_t *)buf; + + for (i = 0; i < len; i++) + MMIO_SRAM_WRITE_1(sc->parent, sc->reg + offset + i, addr[i]); +} + +static device_method_t shmem_methods[] = { + DEVMETHOD(device_probe, shmem_probe), + DEVMETHOD(device_attach, shmem_attach), + DEVMETHOD(device_detach, shmem_detach), + DEVMETHOD_END +}; + +DEFINE_CLASS_1(shmem, shmem_driver, shmem_methods, sizeof(struct shmem_softc), + simplebus_driver); + +EARLY_DRIVER_MODULE(shmem, mmio_sram, shmem_driver, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(scmi, 1); diff --git a/sys/dev/mailbox/arm/arm_doorbell.c b/sys/dev/mailbox/arm/arm_doorbell.c new file mode 100644 index 000000000000..dcf88c711140 --- /dev/null +++ b/sys/dev/mailbox/arm/arm_doorbell.c @@ -0,0 +1,347 @@ +/*- + * 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 "arm_doorbell.h" + +#define MHU_CHAN_RX_LP 0x000 /* Low priority channel */ +#define MHU_CHAN_RX_HP 0x020 /* High priority channel */ +#define MHU_CHAN_RX_SEC 0x200 /* Secure channel */ +#define MHU_INTR_STAT 0x00 +#define MHU_INTR_SET 0x08 +#define MHU_INTR_CLEAR 0x10 + +#define MHU_TX_REG_OFFSET 0x100 + +#define DOORBELL_N_CHANNELS 3 +#define DOORBELL_N_DOORBELLS (DOORBELL_N_CHANNELS * 32) + +struct arm_doorbell dbells[DOORBELL_N_DOORBELLS]; + +static struct resource_spec arm_doorbell_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { SYS_RES_IRQ, 1, RF_ACTIVE }, + { -1, 0 } +}; + +struct arm_doorbell_softc { + struct resource *res[3]; + void *lp_intr_cookie; + void *hp_intr_cookie; + device_t dev; +}; + +static void +arm_doorbell_lp_intr(void *arg) +{ + struct arm_doorbell_softc *sc; + struct arm_doorbell *db; + uint32_t reg; + int i; + + sc = arg; + + reg = bus_read_4(sc->res[0], MHU_CHAN_RX_LP + MHU_INTR_STAT); + for (i = 0; i < 32; i++) { + if (reg & (1 << i)) { + db = &dbells[i]; + bus_write_4(sc->res[0], MHU_CHAN_RX_LP + MHU_INTR_CLEAR, + (1 << i)); + if (db->func != NULL) + db->func(db->arg); + } + } +} + +static void +arm_doorbell_hp_intr(void *arg) +{ + struct arm_doorbell_softc *sc; + struct arm_doorbell *db; + uint32_t reg; + int i; + + sc = arg; + + reg = bus_read_4(sc->res[0], MHU_CHAN_RX_HP + MHU_INTR_STAT); + for (i = 0; i < 32; i++) { + if (reg & (1 << i)) { + db = &dbells[i]; + bus_write_4(sc->res[0], MHU_CHAN_RX_HP + MHU_INTR_CLEAR, + (1 << i)); + if (db->func != NULL) + db->func(db->arg); + } + } +} + +static int +arm_doorbell_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "arm,mhu-doorbell")) + return (ENXIO); + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + device_set_desc(dev, "ARM MHU Doorbell"); + + return (BUS_PROBE_DEFAULT); +} + +static int +arm_doorbell_attach(device_t dev) +{ + struct arm_doorbell_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); + + if (bus_alloc_resources(dev, arm_doorbell_spec, sc->res) != 0) { + device_printf(dev, "Can't allocate resources for device.\n"); + return (ENXIO); + } + + /* Setup interrupt handlers. */ + error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, + NULL, arm_doorbell_lp_intr, sc, &sc->lp_intr_cookie); + if (error != 0) { + device_printf(dev, "Can't setup LP interrupt handler.\n"); + bus_release_resources(dev, arm_doorbell_spec, sc->res); + return (ENXIO); + } + + error = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE, + NULL, arm_doorbell_hp_intr, sc, &sc->hp_intr_cookie); + if (error != 0) { + device_printf(dev, "Can't setup HP interrupt handler.\n"); + bus_release_resources(dev, arm_doorbell_spec, sc->res); + return (ENXIO); + } + + OF_device_register_xref(OF_xref_from_node(node), dev); + + return (0); +} + +static int +arm_doorbell_detach(device_t dev) +{ + + return (EBUSY); +} + +struct arm_doorbell * +arm_doorbell_ofw_get(device_t dev, const char *name) +{ + phandle_t node, parent; + struct arm_doorbell *db; + device_t db_dev; + pcell_t *cells; + int nmboxes; + int ncells; + int idx; + int db_id; + int error; + int chan; + + node = ofw_bus_get_node(dev); + + error = ofw_bus_parse_xref_list_get_length(node, "mboxes", + "#mbox-cells", &nmboxes); + if (error) { + device_printf(dev, "%s can't get mboxes list.\n", __func__); + return (NULL); + } + + if (nmboxes == 0) { + device_printf(dev, "%s mbox list is empty.\n", __func__); + return (NULL); + } + + error = ofw_bus_find_string_index(node, "mbox-names", name, &idx); + if (error != 0) { + device_printf(dev, "%s can't find string index.\n", + __func__); + return (NULL); + } + + error = ofw_bus_parse_xref_list_alloc(node, "mboxes", "#mbox-cells", + idx, &parent, &ncells, &cells); + if (error != 0) { + device_printf(dev, "%s can't get mbox device xref\n", + __func__); + return (NULL); + } + + if (ncells != 2) { + device_printf(dev, "Unexpected data size.\n"); + OF_prop_free(cells); + return (NULL); + } + + db_dev = OF_device_from_xref(parent); + if (db_dev == NULL) { + device_printf(dev, "%s: Can't get arm_doorbell device\n", + __func__); + OF_prop_free(cells); + return (NULL); + } + + chan = cells[0]; + if (chan >= DOORBELL_N_CHANNELS) { + device_printf(dev, "Unexpected channel number.\n"); + OF_prop_free(cells); + return (NULL); + } + + db_id = cells[1]; + if (db_id >= 32) { + device_printf(dev, "Unexpected channel bit.\n"); + OF_prop_free(cells); + return (NULL); + } + + db = &dbells[chan * db_id]; + db->dev = dev; + db->db_dev = db_dev; + db->chan = chan; + db->db = db_id; + + OF_prop_free(cells); + + return (db); +} + +void +arm_doorbell_set(struct arm_doorbell *db) +{ + struct arm_doorbell_softc *sc; + uint32_t offset; + + sc = device_get_softc(db->db_dev); + + switch (db->chan) { + case 0: + offset = MHU_CHAN_RX_LP; + break; + case 1: + offset = MHU_CHAN_RX_HP; + break; + case 2: + offset = MHU_CHAN_RX_SEC; + break; + default: + panic("not reached"); + }; + + offset |= MHU_TX_REG_OFFSET; + + bus_write_4(sc->res[0], offset + MHU_INTR_SET, (1 << db->db)); +} + +int +arm_doorbell_get(struct arm_doorbell *db) +{ + struct arm_doorbell_softc *sc; + uint32_t offset; + uint32_t reg; + + sc = device_get_softc(db->db_dev); + + switch (db->chan) { + case 0: + offset = MHU_CHAN_RX_LP; + break; + case 1: + offset = MHU_CHAN_RX_HP; + break; + case 2: + offset = MHU_CHAN_RX_SEC; + break; + default: + panic("not reached"); + }; + + reg = bus_read_4(sc->res[0], offset + MHU_INTR_STAT); + if (reg & (1 << db->db)) { + bus_write_4(sc->res[0], offset + MHU_INTR_CLEAR, + (1 << db->db)); + return (1); + } + + return (0); +} + +void +arm_doorbell_set_handler(struct arm_doorbell *db, void (*func)(void *), + void *arg) +{ + + db->func = func; + db->arg = arg; +} + +static device_method_t arm_doorbell_methods[] = { + DEVMETHOD(device_probe, arm_doorbell_probe), + DEVMETHOD(device_attach, arm_doorbell_attach), + DEVMETHOD(device_detach, arm_doorbell_detach), + DEVMETHOD_END +}; + +DEFINE_CLASS_1(arm_doorbell, arm_doorbell_driver, arm_doorbell_methods, + sizeof(struct arm_doorbell_softc), simplebus_driver); + +EARLY_DRIVER_MODULE(arm_doorbell, simplebus, arm_doorbell_driver, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(arm_doorbell, 1); diff --git a/sys/dev/mailbox/arm/arm_doorbell.h b/sys/dev/mailbox/arm/arm_doorbell.h new file mode 100644 index 000000000000..7f109704ccd6 --- /dev/null +++ b/sys/dev/mailbox/arm/arm_doorbell.h @@ -0,0 +1,51 @@ +/*- + * 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_ARM_DOORBELL_H_ +#define _ARM64_SCMI_ARM_DOORBELL_H_ + +struct arm_doorbell { + device_t dev; + device_t db_dev; + int chan; + int db; + void (*func)(void *); + void *arg; +}; + +void arm_doorbell_set(struct arm_doorbell *db); +int arm_doorbell_get(struct arm_doorbell *db); +struct arm_doorbell * arm_doorbell_ofw_get(device_t dev, const char *name); +void arm_doorbell_set_handler(struct arm_doorbell *db, void (*func)(void *), + void *arg); + +#endif /* !_ARM64_SCMI_ARM_DOORBELL_H_ */ diff --git a/sys/dev/sram/mmio_sram.c b/sys/dev/sram/mmio_sram.c new file mode 100644 index 000000000000..0425723450a0 --- /dev/null +++ b/sys/dev/sram/mmio_sram.c @@ -0,0 +1,164 @@ +/*- + * 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 "mmio_sram_if.h" + +#define dprintf(fmt, ...) + +static struct resource_spec mmio_sram_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +struct mmio_sram_softc { + struct simplebus_softc simplebus_sc; + struct resource *res[1]; + device_t dev; +}; + +static int +mmio_sram_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "mmio-sram")) + return (ENXIO); + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + device_set_desc(dev, "MMIO SRAM"); + + return (BUS_PROBE_DEFAULT); +} + +static int +mmio_sram_attach(device_t dev) +{ + struct mmio_sram_softc *sc; + phandle_t node; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, mmio_sram_spec, sc->res) != 0) { + device_printf(dev, "Can't allocate resources for device.\n"); + return (ENXIO); + } + + node = ofw_bus_get_node(dev); + if (node == -1) + return (ENXIO); + + 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); + + return (bus_generic_attach(dev)); +} + +static int +mmio_sram_detach(device_t dev) +{ + struct mmio_sram_softc *sc; + + sc = device_get_softc(dev); + + bus_release_resources(dev, mmio_sram_spec, sc->res); + + return (0); +} + +static uint8_t +mmio_sram_read_1(device_t dev, bus_size_t offset) +{ + struct mmio_sram_softc *sc; + + sc = device_get_softc(dev); + + dprintf("%s: reading from %lx\n", __func__, offset); + + return (bus_read_1(sc->res[0], offset)); +} + +static void +mmio_sram_write_1(device_t dev, bus_size_t offset, uint8_t val) +{ + struct mmio_sram_softc *sc; + + sc = device_get_softc(dev); + + dprintf("%s: writing to %lx val %x\n", __func__, offset, val); + + bus_write_1(sc->res[0], offset, val); +} + +static device_method_t mmio_sram_methods[] = { + /* Device Interface */ + DEVMETHOD(device_probe, mmio_sram_probe), + DEVMETHOD(device_attach, mmio_sram_attach), + DEVMETHOD(device_detach, mmio_sram_detach), + + /* MMIO interface */ + DEVMETHOD(mmio_sram_read_1, mmio_sram_read_1), + DEVMETHOD(mmio_sram_write_1, mmio_sram_write_1), + DEVMETHOD_END +}; + +DEFINE_CLASS_1(mmio_sram, mmio_sram_driver, mmio_sram_methods, + sizeof(struct mmio_sram_softc), simplebus_driver); + +EARLY_DRIVER_MODULE(mmio_sram, simplebus, mmio_sram_driver, 0, 0, + BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE); +MODULE_VERSION(mmio_sram, 1); diff --git a/sys/dev/sram/mmio_sram_if.m b/sys/dev/sram/mmio_sram_if.m new file mode 100644 index 000000000000..02994175c069 --- /dev/null +++ b/sys/dev/sram/mmio_sram_if.m @@ -0,0 +1,44 @@ +#- +# 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$ +# + +#include + +INTERFACE mmio_sram; + +METHOD uint8_t read_1 { + device_t dev; + bus_size_t offset; +}; + +METHOD void write_1 { + device_t dev; + bus_size_t offset; + uint8_t value; +};