diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -272,6 +272,8 @@ dev/firmware/arm/scmi.c optional fdt scmi dev/firmware/arm/scmi_clk.c optional fdt scmi +dev/firmware/arm/scmi_if.m optional fdt scmi +dev/firmware/arm/scmi_mailbox.c optional fdt scmi dev/firmware/arm/scmi_shmem.c optional fdt scmi dev/gpio/pl061.c optional pl061 gpio diff --git a/sys/dev/firmware/arm/scmi.h b/sys/dev/firmware/arm/scmi.h --- a/sys/dev/firmware/arm/scmi.h +++ b/sys/dev/firmware/arm/scmi.h @@ -31,12 +31,21 @@ #ifndef _ARM64_SCMI_SCMI_H_ #define _ARM64_SCMI_SCMI_H_ +#include "scmi_if.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, ...) +struct scmi_softc { + struct simplebus_softc simplebus_sc; + device_t dev; + device_t tx_shmem; + struct mtx mtx; +}; + /* Shared Memory Transfer. */ struct scmi_smt_header { uint32_t reserved; @@ -71,7 +80,11 @@ uint32_t out_size; }; +DECLARE_CLASS(scmi_driver); + +int scmi_attach(device_t dev); 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, diff --git a/sys/dev/firmware/arm/scmi.c b/sys/dev/firmware/arm/scmi.c --- a/sys/dev/firmware/arm/scmi.c +++ b/sys/dev/firmware/arm/scmi.c @@ -47,15 +47,6 @@ #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) { @@ -90,26 +81,11 @@ 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; + int ret; bzero(&hdr, sizeof(struct scmi_smt_header)); @@ -125,6 +101,7 @@ 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; + /* TODO: Allocate a token */ hdr.length = sizeof(hdr.msg_header) + req->in_size; hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED; @@ -135,31 +112,9 @@ 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); + ret = SCMI_XFER_MSG(sc->dev); + if (ret != 0) + return (ret); /* Read header. */ scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE); @@ -186,22 +141,7 @@ 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 +int scmi_attach(device_t dev) { struct scmi_softc *sc; @@ -221,16 +161,8 @@ 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); /* @@ -257,9 +189,9 @@ } static device_method_t scmi_methods[] = { - DEVMETHOD(device_probe, scmi_probe), DEVMETHOD(device_attach, scmi_attach), DEVMETHOD(device_detach, scmi_detach), + DEVMETHOD_END }; diff --git a/sys/dev/firmware/arm/scmi_if.m b/sys/dev/firmware/arm/scmi_if.m new file mode 100644 --- /dev/null +++ b/sys/dev/firmware/arm/scmi_if.m @@ -0,0 +1,32 @@ +#- +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) 2023 Arm Ltd +# +# 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 unmodified, 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 ``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 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. +# + +INTERFACE scmi; + +METHOD int xfer_msg { + device_t dev; +}; diff --git a/sys/dev/firmware/arm/scmi_mailbox.c b/sys/dev/firmware/arm/scmi_mailbox.c new file mode 100644 --- /dev/null +++ b/sys/dev/firmware/arm/scmi_mailbox.c @@ -0,0 +1,178 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Ruslan Bukin + * Copyright (c) 2023 Arm Ltd + * + * 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 + +#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_mailbox_softc { + struct scmi_softc base; + struct arm_doorbell *db; + int req_done; +}; + +static void +scmi_mailbox_callback(void *arg) +{ + struct scmi_mailbox_softc *sc; + + sc = arg; + + dprintf("%s sc %p\n", __func__, sc); + + SCMI_LOCK(&sc->base); + sc->req_done = 1; + wakeup(sc); + SCMI_UNLOCK(&sc->base); +} + +static int +scmi_mailbox_xfer_msg(device_t dev) +{ + struct scmi_mailbox_softc *sc; + int timeout; + + sc = device_get_softc(dev); + SCMI_ASSERT_LOCKED(&sc->base); + + 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->base.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); + + return (0); +} + +static int +scmi_mailbox_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_mailbox_attach(device_t dev) +{ + struct scmi_mailbox_softc *sc; + int ret; + + sc = device_get_softc(dev); + + /* TODO: Support other mailbox devices */ + sc->db = arm_doorbell_ofw_get(dev, "tx"); + if (sc->db == NULL) { + device_printf(dev, "Doorbell device not found.\n"); + return (ENXIO); + } + + arm_doorbell_set_handler(sc->db, scmi_mailbox_callback, sc); + + ret = scmi_attach(dev); + if (ret != 0) + arm_doorbell_set_handler(sc->db, NULL, NULL); + + return (ret); +} + +static int +scmi_mailbox_detach(device_t dev) +{ + struct scmi_mailbox_softc *sc; + + sc = device_get_softc(dev); + + arm_doorbell_set_handler(sc->db, NULL, NULL); + + return (0); +} + +static device_method_t scmi_mailbox_methods[] = { + DEVMETHOD(device_probe, scmi_mailbox_probe), + DEVMETHOD(device_attach, scmi_mailbox_attach), + DEVMETHOD(device_detach, scmi_mailbox_detach), + + /* SCMI interface */ + DEVMETHOD(scmi_xfer_msg, scmi_mailbox_xfer_msg), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(scmi_mailbox, scmi_mailbox_driver, scmi_mailbox_methods, + sizeof(struct scmi_mailbox_softc), scmi_driver); + +DRIVER_MODULE(scmi_mailbox, simplebus, scmi_mailbox_driver, 0, 0); +MODULE_VERSION(scmi_mailbox, 1);