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
scmi_smc_transport_init(device_t dev)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
scmi_smc_xfer_msg(device_t dev,struct scmi_msg * msg)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
scmi_smc_poll_msg(device_t dev,struct scmi_msg * msg,unsigned int tmo)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
scmi_smc_collect_reply(device_t dev,struct scmi_msg * msg)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
scmi_smc_tx_complete(device_t dev,void * chan)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
scmi_smc_probe(device_t dev)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