15ea251c4SAndrew Turner /*- 25ea251c4SAndrew Turner * SPDX-License-Identifier: BSD-2-Clause 35ea251c4SAndrew Turner * 45ea251c4SAndrew Turner * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com> 55ea251c4SAndrew Turner * Copyright (c) 2023 Arm Ltd 65ea251c4SAndrew Turner * 75ea251c4SAndrew Turner * This work was supported by Innovate UK project 105694, "Digital Security 85ea251c4SAndrew Turner * by Design (DSbD) Technology Platform Prototype". 95ea251c4SAndrew Turner * 105ea251c4SAndrew Turner * Redistribution and use in source and binary forms, with or without 115ea251c4SAndrew Turner * modification, are permitted provided that the following conditions 125ea251c4SAndrew Turner * are met: 135ea251c4SAndrew Turner * 1. Redistributions of source code must retain the above copyright 145ea251c4SAndrew Turner * notice, this list of conditions and the following disclaimer. 155ea251c4SAndrew Turner * 2. Redistributions in binary form must reproduce the above copyright 165ea251c4SAndrew Turner * notice, this list of conditions and the following disclaimer in the 175ea251c4SAndrew Turner * documentation and/or other materials provided with the distribution. 185ea251c4SAndrew Turner * 195ea251c4SAndrew Turner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 205ea251c4SAndrew Turner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 215ea251c4SAndrew Turner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 225ea251c4SAndrew Turner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 235ea251c4SAndrew Turner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 245ea251c4SAndrew Turner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 255ea251c4SAndrew Turner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 265ea251c4SAndrew Turner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 275ea251c4SAndrew Turner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 285ea251c4SAndrew Turner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 295ea251c4SAndrew Turner * SUCH DAMAGE. 305ea251c4SAndrew Turner */ 315ea251c4SAndrew Turner 325ea251c4SAndrew Turner #include <sys/cdefs.h> 335ea251c4SAndrew Turner 345ea251c4SAndrew Turner #include <sys/param.h> 355ea251c4SAndrew Turner #include <sys/systm.h> 365ea251c4SAndrew Turner #include <sys/bus.h> 375ea251c4SAndrew Turner #include <sys/cpu.h> 385ea251c4SAndrew Turner #include <sys/kernel.h> 395ea251c4SAndrew Turner #include <sys/module.h> 405ea251c4SAndrew Turner 415ea251c4SAndrew Turner #include <dev/fdt/simplebus.h> 425ea251c4SAndrew Turner #include <dev/fdt/fdt_common.h> 435ea251c4SAndrew Turner #include <dev/ofw/ofw_bus_subr.h> 445ea251c4SAndrew Turner 455ea251c4SAndrew Turner #include <dev/psci/smccc.h> 465ea251c4SAndrew Turner 475ea251c4SAndrew Turner #include "scmi.h" 485ea251c4SAndrew Turner #include "scmi_protocols.h" 49403ca28cSCristian Marussi #include "scmi_shmem.h" 505ea251c4SAndrew Turner 515ea251c4SAndrew Turner struct scmi_smc_softc { 525ea251c4SAndrew Turner struct scmi_softc base; 535ea251c4SAndrew Turner uint32_t smc_id; 54403ca28cSCristian Marussi device_t a2p_dev; 555ea251c4SAndrew Turner }; 565ea251c4SAndrew Turner 57403ca28cSCristian Marussi static int scmi_smc_transport_init(device_t); 5835f93203SCristian Marussi static int scmi_smc_xfer_msg(device_t, struct scmi_msg *); 5935f93203SCristian Marussi static int scmi_smc_poll_msg(device_t, struct scmi_msg *, unsigned int); 6035f93203SCristian Marussi static int scmi_smc_collect_reply(device_t, struct scmi_msg *); 61403ca28cSCristian Marussi static void scmi_smc_tx_complete(device_t, void *); 62403ca28cSCristian Marussi 63403ca28cSCristian Marussi static int scmi_smc_probe(device_t); 64403ca28cSCristian Marussi 655ea251c4SAndrew Turner static int 66403ca28cSCristian Marussi scmi_smc_transport_init(device_t dev) 675ea251c4SAndrew Turner { 685ea251c4SAndrew Turner struct scmi_smc_softc *sc; 69403ca28cSCristian Marussi phandle_t node; 70403ca28cSCristian Marussi ssize_t len; 71403ca28cSCristian Marussi 72403ca28cSCristian Marussi sc = device_get_softc(dev); 73403ca28cSCristian Marussi 74403ca28cSCristian Marussi node = ofw_bus_get_node(dev); 75403ca28cSCristian Marussi len = OF_getencprop(node, "arm,smc-id", &sc->smc_id, 76403ca28cSCristian Marussi sizeof(sc->smc_id)); 77403ca28cSCristian Marussi if (len <= 0) { 78403ca28cSCristian Marussi device_printf(dev, "No SMC ID found\n"); 79403ca28cSCristian Marussi return (EINVAL); 80403ca28cSCristian Marussi } 81403ca28cSCristian Marussi 82403ca28cSCristian Marussi device_printf(dev, "smc id %x\n", sc->smc_id); 83403ca28cSCristian Marussi 84403ca28cSCristian Marussi sc->a2p_dev = scmi_shmem_get(dev, node, SCMI_CHAN_A2P); 85403ca28cSCristian Marussi if (sc->a2p_dev == NULL) { 86403ca28cSCristian Marussi device_printf(dev, "A2P shmem dev not found.\n"); 87403ca28cSCristian Marussi return (ENXIO); 88403ca28cSCristian Marussi } 89403ca28cSCristian Marussi 903595f18fSCristian Marussi sc->base.trs_desc.no_completion_irq = true; 913595f18fSCristian Marussi sc->base.trs_desc.reply_timo_ms = 30; 923595f18fSCristian Marussi 93403ca28cSCristian Marussi return (0); 94403ca28cSCristian Marussi } 95403ca28cSCristian Marussi 96403ca28cSCristian Marussi static int 9735f93203SCristian Marussi scmi_smc_xfer_msg(device_t dev, struct scmi_msg *msg) 98403ca28cSCristian Marussi { 99403ca28cSCristian Marussi struct scmi_smc_softc *sc; 100403ca28cSCristian Marussi int ret; 1015ea251c4SAndrew Turner 1025ea251c4SAndrew Turner sc = device_get_softc(dev); 1035ea251c4SAndrew Turner 10435f93203SCristian Marussi ret = scmi_shmem_prepare_msg(sc->a2p_dev, (uint8_t *)&msg->hdr, 10535f93203SCristian Marussi msg->tx_len, msg->polling); 106403ca28cSCristian Marussi if (ret != 0) 107403ca28cSCristian Marussi return (ret); 108403ca28cSCristian Marussi 109b9cd72b0SAndrew Turner arm_smccc_invoke_smc(sc->smc_id, NULL); 1105ea251c4SAndrew Turner 1115ea251c4SAndrew Turner return (0); 1125ea251c4SAndrew Turner } 1135ea251c4SAndrew Turner 1145ea251c4SAndrew Turner static int 11535f93203SCristian Marussi scmi_smc_poll_msg(device_t dev, struct scmi_msg *msg, unsigned int tmo) 1163595f18fSCristian Marussi { 1173595f18fSCristian Marussi struct scmi_smc_softc *sc; 1183595f18fSCristian Marussi 1193595f18fSCristian Marussi sc = device_get_softc(dev); 1203595f18fSCristian Marussi 1213595f18fSCristian Marussi /* 1223595f18fSCristian Marussi * Nothing to poll since commands are completed as soon as smc 1233595f18fSCristian Marussi * returns ... but did we get back what we were poling for ? 1243595f18fSCristian Marussi */ 125*9342829dSCristian Marussi scmi_shmem_read_msg_header(sc->a2p_dev, &msg->hdr, &msg->rx_len); 1263595f18fSCristian Marussi 1273595f18fSCristian Marussi return (0); 1283595f18fSCristian Marussi } 1293595f18fSCristian Marussi 1303595f18fSCristian Marussi static int 13135f93203SCristian Marussi scmi_smc_collect_reply(device_t dev, struct scmi_msg *msg) 132403ca28cSCristian Marussi { 133403ca28cSCristian Marussi struct scmi_smc_softc *sc; 13435f93203SCristian Marussi int ret; 135403ca28cSCristian Marussi 136403ca28cSCristian Marussi sc = device_get_softc(dev); 137403ca28cSCristian Marussi 13835f93203SCristian Marussi ret = scmi_shmem_read_msg_payload(sc->a2p_dev, 139*9342829dSCristian Marussi msg->payld, msg->rx_len - SCMI_MSG_HDR_SIZE, msg->rx_len); 14035f93203SCristian Marussi 14135f93203SCristian Marussi return (ret); 142403ca28cSCristian Marussi } 143403ca28cSCristian Marussi 144403ca28cSCristian Marussi static void 145403ca28cSCristian Marussi scmi_smc_tx_complete(device_t dev, void *chan) 146403ca28cSCristian Marussi { 147403ca28cSCristian Marussi struct scmi_smc_softc *sc; 148403ca28cSCristian Marussi 149403ca28cSCristian Marussi sc = device_get_softc(dev); 150403ca28cSCristian Marussi scmi_shmem_tx_complete(sc->a2p_dev); 151403ca28cSCristian Marussi } 152403ca28cSCristian Marussi 153403ca28cSCristian Marussi static int 1545ea251c4SAndrew Turner scmi_smc_probe(device_t dev) 1555ea251c4SAndrew Turner { 1565ea251c4SAndrew Turner 1575ea251c4SAndrew Turner if (!ofw_bus_is_compatible(dev, "arm,scmi-smc")) 1585ea251c4SAndrew Turner return (ENXIO); 1595ea251c4SAndrew Turner 1605ea251c4SAndrew Turner if (!ofw_bus_status_okay(dev)) 1615ea251c4SAndrew Turner return (ENXIO); 1625ea251c4SAndrew Turner 1633595f18fSCristian Marussi device_set_desc(dev, "ARM SCMI SMC Transport driver"); 1645ea251c4SAndrew Turner 1655ea251c4SAndrew Turner return (BUS_PROBE_DEFAULT); 1665ea251c4SAndrew Turner } 1675ea251c4SAndrew Turner 1685ea251c4SAndrew Turner static device_method_t scmi_smc_methods[] = { 1695ea251c4SAndrew Turner DEVMETHOD(device_probe, scmi_smc_probe), 1705ea251c4SAndrew Turner 1715ea251c4SAndrew Turner /* SCMI interface */ 172403ca28cSCristian Marussi DEVMETHOD(scmi_transport_init, scmi_smc_transport_init), 1735ea251c4SAndrew Turner DEVMETHOD(scmi_xfer_msg, scmi_smc_xfer_msg), 1743595f18fSCristian Marussi DEVMETHOD(scmi_poll_msg, scmi_smc_poll_msg), 175403ca28cSCristian Marussi DEVMETHOD(scmi_collect_reply, scmi_smc_collect_reply), 176403ca28cSCristian Marussi DEVMETHOD(scmi_tx_complete, scmi_smc_tx_complete), 1775ea251c4SAndrew Turner 1785ea251c4SAndrew Turner DEVMETHOD_END 1795ea251c4SAndrew Turner }; 1805ea251c4SAndrew Turner 1815ea251c4SAndrew Turner DEFINE_CLASS_1(scmi_smc, scmi_smc_driver, scmi_smc_methods, 1825ea251c4SAndrew Turner sizeof(struct scmi_smc_softc), scmi_driver); 1835ea251c4SAndrew Turner 1845ea251c4SAndrew Turner /* Needs to be after the mmio_sram driver */ 1855ea251c4SAndrew Turner EARLY_DRIVER_MODULE(scmi_smc, simplebus, scmi_smc_driver, 0, 0, 1865ea251c4SAndrew Turner BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_LATE); 1875ea251c4SAndrew Turner MODULE_VERSION(scmi_smc, 1); 188