154b96380SRuslan Bukin /*- 254b96380SRuslan Bukin * SPDX-License-Identifier: BSD-2-Clause 354b96380SRuslan Bukin * 454b96380SRuslan Bukin * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com> 5d220b1cfSCristian Marussi * Copyright (c) 2023 Arm Ltd 654b96380SRuslan Bukin * 754b96380SRuslan Bukin * This work was supported by Innovate UK project 105694, "Digital Security 854b96380SRuslan Bukin * by Design (DSbD) Technology Platform Prototype". 954b96380SRuslan Bukin * 1054b96380SRuslan Bukin * Redistribution and use in source and binary forms, with or without 1154b96380SRuslan Bukin * modification, are permitted provided that the following conditions 1254b96380SRuslan Bukin * are met: 1354b96380SRuslan Bukin * 1. Redistributions of source code must retain the above copyright 1454b96380SRuslan Bukin * notice, this list of conditions and the following disclaimer. 1554b96380SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 1654b96380SRuslan Bukin * notice, this list of conditions and the following disclaimer in the 1754b96380SRuslan Bukin * documentation and/or other materials provided with the distribution. 1854b96380SRuslan Bukin * 1954b96380SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2054b96380SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2154b96380SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2254b96380SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2354b96380SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2454b96380SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2554b96380SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2654b96380SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2754b96380SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2854b96380SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2954b96380SRuslan Bukin * SUCH DAMAGE. 3054b96380SRuslan Bukin */ 3154b96380SRuslan Bukin 3254b96380SRuslan Bukin #include <sys/param.h> 3354b96380SRuslan Bukin #include <sys/systm.h> 343595f18fSCristian Marussi #include <sys/_bitset.h> 353595f18fSCristian Marussi #include <sys/bitset.h> 3654b96380SRuslan Bukin #include <sys/bus.h> 3754b96380SRuslan Bukin #include <sys/cpu.h> 383595f18fSCristian Marussi #include <sys/endian.h> 3954b96380SRuslan Bukin #include <sys/kernel.h> 4054b96380SRuslan Bukin #include <sys/lock.h> 413595f18fSCristian Marussi #include <sys/malloc.h> 4254b96380SRuslan Bukin #include <sys/module.h> 4354b96380SRuslan Bukin #include <sys/mutex.h> 443595f18fSCristian Marussi #include <sys/queue.h> 4535f93203SCristian Marussi #include <sys/refcount.h> 46984f9fb8SCristian Marussi #include <sys/sdt.h> 47*e6d0edd2SCristian Marussi #include <sys/sysctl.h> 48b802926bSCristian Marussi #include <sys/taskqueue.h> 4954b96380SRuslan Bukin 50be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h> 5154b96380SRuslan Bukin #include <dev/fdt/simplebus.h> 5254b96380SRuslan Bukin #include <dev/fdt/fdt_common.h> 5354b96380SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h> 5454b96380SRuslan Bukin 5554b96380SRuslan Bukin #include "scmi.h" 5654b96380SRuslan Bukin #include "scmi_protocols.h" 5754b96380SRuslan Bukin 58984f9fb8SCristian Marussi SDT_PROVIDER_DEFINE(scmi); 59984f9fb8SCristian Marussi SDT_PROBE_DEFINE3(scmi, func, scmi_req_alloc, req_alloc, 60984f9fb8SCristian Marussi "int", "int", "int"); 61984f9fb8SCristian Marussi SDT_PROBE_DEFINE3(scmi, func, scmi_req_free_unlocked, req_alloc, 62984f9fb8SCristian Marussi "int", "int", "int"); 63984f9fb8SCristian Marussi SDT_PROBE_DEFINE3(scmi, func, scmi_req_get, req_alloc, 64984f9fb8SCristian Marussi "int", "int", "int"); 65984f9fb8SCristian Marussi SDT_PROBE_DEFINE3(scmi, func, scmi_req_put, req_alloc, 66984f9fb8SCristian Marussi "int", "int", "int"); 67984f9fb8SCristian Marussi SDT_PROBE_DEFINE5(scmi, func, scmi_request_tx, xfer_track, 68984f9fb8SCristian Marussi "int", "int", "int", "int", "int"); 69984f9fb8SCristian Marussi SDT_PROBE_DEFINE5(scmi, entry, scmi_wait_for_response, xfer_track, 70984f9fb8SCristian Marussi "int", "int", "int", "int", "int"); 71984f9fb8SCristian Marussi SDT_PROBE_DEFINE5(scmi, exit, scmi_wait_for_response, xfer_track, 72984f9fb8SCristian Marussi "int", "int", "int", "int", "int"); 73984f9fb8SCristian Marussi SDT_PROBE_DEFINE2(scmi, func, scmi_rx_irq_callback, hdr_dump, 74984f9fb8SCristian Marussi "int", "int"); 75984f9fb8SCristian Marussi SDT_PROBE_DEFINE5(scmi, func, scmi_process_response, xfer_track, 76984f9fb8SCristian Marussi "int", "int", "int", "int", "int"); 77984f9fb8SCristian Marussi 783595f18fSCristian Marussi #define SCMI_MAX_TOKEN 1024 793595f18fSCristian Marussi 80d220b1cfSCristian Marussi #define SCMI_HDR_TOKEN_S 18 8100b77e63SCristian Marussi #define SCMI_HDR_TOKEN_BF (0x3ff) 82d220b1cfSCristian Marussi #define SCMI_HDR_TOKEN_M (SCMI_HDR_TOKEN_BF << SCMI_HDR_TOKEN_S) 8354b96380SRuslan Bukin 84d220b1cfSCristian Marussi #define SCMI_HDR_PROTOCOL_ID_S 10 85d220b1cfSCristian Marussi #define SCMI_HDR_PROTOCOL_ID_BF (0xff) 86d220b1cfSCristian Marussi #define SCMI_HDR_PROTOCOL_ID_M \ 87d220b1cfSCristian Marussi (SCMI_HDR_PROTOCOL_ID_BF << SCMI_HDR_PROTOCOL_ID_S) 8854b96380SRuslan Bukin 89d220b1cfSCristian Marussi #define SCMI_HDR_MESSAGE_TYPE_S 8 90d220b1cfSCristian Marussi #define SCMI_HDR_MESSAGE_TYPE_BF (0x3) 91d220b1cfSCristian Marussi #define SCMI_HDR_MESSAGE_TYPE_M \ 92d220b1cfSCristian Marussi (SCMI_HDR_MESSAGE_TYPE_BF << SCMI_HDR_MESSAGE_TYPE_S) 9354b96380SRuslan Bukin 94d220b1cfSCristian Marussi #define SCMI_HDR_MESSAGE_ID_S 0 95d220b1cfSCristian Marussi #define SCMI_HDR_MESSAGE_ID_BF (0xff) 96d220b1cfSCristian Marussi #define SCMI_HDR_MESSAGE_ID_M \ 97d220b1cfSCristian Marussi (SCMI_HDR_MESSAGE_ID_BF << SCMI_HDR_MESSAGE_ID_S) 9854b96380SRuslan Bukin 99d220b1cfSCristian Marussi #define SCMI_MSG_TYPE_CMD 0 100d220b1cfSCristian Marussi #define SCMI_MSG_TYPE_DRESP 2 101d220b1cfSCristian Marussi #define SCMI_MSG_TYPE_NOTIF 3 10254b96380SRuslan Bukin 1033595f18fSCristian Marussi #define SCMI_MSG_TYPE_CHECK(_h, _t) \ 1043595f18fSCristian Marussi ((((_h) & SCMI_HDR_MESSAGE_TYPE_M) >> SCMI_HDR_MESSAGE_TYPE_S) == (_t)) 10554b96380SRuslan Bukin 1063595f18fSCristian Marussi #define SCMI_IS_MSG_TYPE_NOTIF(h) \ 1073595f18fSCristian Marussi SCMI_MSG_TYPE_CHECK((h), SCMI_MSG_TYPE_NOTIF) 1083595f18fSCristian Marussi #define SCMI_IS_MSG_TYPE_DRESP(h) \ 1093595f18fSCristian Marussi SCMI_MSG_TYPE_CHECK((h), SCMI_MSG_TYPE_DRESP) 11054b96380SRuslan Bukin 1113595f18fSCristian Marussi #define SCMI_MSG_TOKEN(_hdr) \ 1123595f18fSCristian Marussi (((_hdr) & SCMI_HDR_TOKEN_M) >> SCMI_HDR_TOKEN_S) 113984f9fb8SCristian Marussi #define SCMI_MSG_PROTOCOL_ID(_hdr) \ 114984f9fb8SCristian Marussi (((_hdr) & SCMI_HDR_PROTOCOL_ID_M) >> SCMI_HDR_PROTOCOL_ID_S) 115984f9fb8SCristian Marussi #define SCMI_MSG_MESSAGE_ID(_hdr) \ 116984f9fb8SCristian Marussi (((_hdr) & SCMI_HDR_MESSAGE_ID_M) >> SCMI_HDR_MESSAGE_ID_S) 117984f9fb8SCristian Marussi #define SCMI_MSG_TYPE(_hdr) \ 118984f9fb8SCristian Marussi (((_hdr) & SCMI_HDR_TYPE_ID_M) >> SCMI_HDR_TYPE_ID_S) 11954b96380SRuslan Bukin 12035f93203SCristian Marussi struct scmi_req { 12135f93203SCristian Marussi int cnt; 12235f93203SCristian Marussi bool timed_out; 12335f93203SCristian Marussi bool use_polling; 12435f93203SCristian Marussi bool done; 125c508841dSCristian Marussi bool is_raw; 126b802926bSCristian Marussi device_t dev; 127b802926bSCristian Marussi struct task tsk; 12835f93203SCristian Marussi struct mtx mtx; 12935f93203SCristian Marussi LIST_ENTRY(scmi_req) next; 13035f93203SCristian Marussi int protocol_id; 13135f93203SCristian Marussi int message_id; 13235f93203SCristian Marussi int token; 13335f93203SCristian Marussi uint32_t header; 13435f93203SCristian Marussi struct scmi_msg msg; 13535f93203SCristian Marussi }; 13635f93203SCristian Marussi 137b802926bSCristian Marussi #define tsk_to_req(t) __containerof((t), struct scmi_req, tsk) 13835f93203SCristian Marussi #define buf_to_msg(b) __containerof((b), struct scmi_msg, payld) 13935f93203SCristian Marussi #define msg_to_req(m) __containerof((m), struct scmi_req, msg) 14035f93203SCristian Marussi #define buf_to_req(b) msg_to_req(buf_to_msg(b)) 14135f93203SCristian Marussi 14235f93203SCristian Marussi LIST_HEAD(reqs_head, scmi_req); 14335f93203SCristian Marussi 14435f93203SCristian Marussi struct scmi_reqs_pool { 14535f93203SCristian Marussi struct mtx mtx; 14635f93203SCristian Marussi struct reqs_head head; 14735f93203SCristian Marussi }; 14835f93203SCristian Marussi 1493595f18fSCristian Marussi BITSET_DEFINE(_scmi_tokens, SCMI_MAX_TOKEN); 1503595f18fSCristian Marussi LIST_HEAD(inflight_head, scmi_req); 1513595f18fSCristian Marussi #define REQHASH(_sc, _tk) \ 1523595f18fSCristian Marussi (&((_sc)->trs->inflight_ht[(_tk) & (_sc)->trs->inflight_mask])) 15354b96380SRuslan Bukin 1543595f18fSCristian Marussi struct scmi_transport { 1553595f18fSCristian Marussi unsigned long next_id; 1563595f18fSCristian Marussi struct _scmi_tokens avail_tokens; 1573595f18fSCristian Marussi struct inflight_head *inflight_ht; 1583595f18fSCristian Marussi unsigned long inflight_mask; 15935f93203SCristian Marussi struct scmi_reqs_pool *chans[SCMI_CHAN_MAX]; 1603595f18fSCristian Marussi struct mtx mtx; 1613595f18fSCristian Marussi }; 162a0ba2a97SCristian Marussi 163103ea03eSCristian Marussi static void scmi_transport_configure(struct scmi_transport_desc *, phandle_t); 164103ea03eSCristian Marussi static int scmi_transport_init(struct scmi_softc *, phandle_t); 1653595f18fSCristian Marussi static void scmi_transport_cleanup(struct scmi_softc *); 166b802926bSCristian Marussi static void scmi_req_async_waiter(void *, int); 167b802926bSCristian Marussi static struct scmi_reqs_pool *scmi_reqs_pool_allocate(device_t, const int, 168b802926bSCristian Marussi const int); 16935f93203SCristian Marussi static void scmi_reqs_pool_free(struct scmi_reqs_pool *); 17035f93203SCristian Marussi static struct scmi_req *scmi_req_alloc(struct scmi_softc *, enum scmi_chan); 171e887179dSCristian Marussi static struct scmi_req *scmi_req_initialized_alloc(device_t, int, int); 17235f93203SCristian Marussi static void scmi_req_free_unlocked(struct scmi_softc *, 17335f93203SCristian Marussi enum scmi_chan, struct scmi_req *); 17435f93203SCristian Marussi static void scmi_req_get(struct scmi_softc *, struct scmi_req *); 17535f93203SCristian Marussi static void scmi_req_put(struct scmi_softc *, struct scmi_req *); 1763595f18fSCristian Marussi static int scmi_token_pick(struct scmi_softc *); 177c508841dSCristian Marussi static int scmi_token_reserve(struct scmi_softc *, uint16_t); 1783595f18fSCristian Marussi static void scmi_token_release_unlocked(struct scmi_softc *, int); 1793595f18fSCristian Marussi static int scmi_req_track_inflight(struct scmi_softc *, 1803595f18fSCristian Marussi struct scmi_req *); 1813595f18fSCristian Marussi static int scmi_req_drop_inflight(struct scmi_softc *, 1823595f18fSCristian Marussi struct scmi_req *); 1833595f18fSCristian Marussi static struct scmi_req *scmi_req_lookup_inflight(struct scmi_softc *, uint32_t); 18454b96380SRuslan Bukin 1853595f18fSCristian Marussi static int scmi_wait_for_response(struct scmi_softc *, 18635f93203SCristian Marussi struct scmi_req *, void **); 1879342829dSCristian Marussi static void scmi_process_response(struct scmi_softc *, uint32_t, 1889342829dSCristian Marussi unsigned int); 18954b96380SRuslan Bukin 190d46f01fdSAndrew Turner int 19154b96380SRuslan Bukin scmi_attach(device_t dev) 19254b96380SRuslan Bukin { 193*e6d0edd2SCristian Marussi struct sysctl_oid *sysctl_trans; 19454b96380SRuslan Bukin struct scmi_softc *sc; 19554b96380SRuslan Bukin phandle_t node; 19654b96380SRuslan Bukin int error; 19754b96380SRuslan Bukin 19854b96380SRuslan Bukin sc = device_get_softc(dev); 19954b96380SRuslan Bukin sc->dev = dev; 20054b96380SRuslan Bukin 20154b96380SRuslan Bukin node = ofw_bus_get_node(dev); 20254b96380SRuslan Bukin if (node == -1) 20354b96380SRuslan Bukin return (ENXIO); 20454b96380SRuslan Bukin 20554b96380SRuslan Bukin simplebus_init(dev, node); 20654b96380SRuslan Bukin 207103ea03eSCristian Marussi error = scmi_transport_init(sc, node); 208403ca28cSCristian Marussi if (error != 0) 209403ca28cSCristian Marussi return (error); 210403ca28cSCristian Marussi 211103ea03eSCristian Marussi device_printf(dev, "Transport - max_msg:%d max_payld_sz:%lu reply_timo_ms:%d\n", 212103ea03eSCristian Marussi SCMI_MAX_MSG(sc), SCMI_MAX_MSG_PAYLD_SIZE(sc), SCMI_MAX_MSG_TIMEOUT_MS(sc)); 2133595f18fSCristian Marussi 214*e6d0edd2SCristian Marussi sc->sysctl_root = SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(_hw), 215*e6d0edd2SCristian Marussi OID_AUTO, "scmi", CTLFLAG_RD, 0, "SCMI root"); 216*e6d0edd2SCristian Marussi sysctl_trans = SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(sc->sysctl_root), 217*e6d0edd2SCristian Marussi OID_AUTO, "transport", CTLFLAG_RD, 0, "SCMI Transport properties"); 218*e6d0edd2SCristian Marussi SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(sysctl_trans), OID_AUTO, "max_msg", 219*e6d0edd2SCristian Marussi CTLFLAG_RD, &sc->trs_desc.max_msg, 0, "SCMI Max number of inflight messages"); 220*e6d0edd2SCristian Marussi SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(sysctl_trans), OID_AUTO, "max_msg_size", 221*e6d0edd2SCristian Marussi CTLFLAG_RD, &sc->trs_desc.max_payld_sz, 0, "SCMI Max message payload size"); 222*e6d0edd2SCristian Marussi SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(sysctl_trans), OID_AUTO, "max_rx_timeout_ms", 223*e6d0edd2SCristian Marussi CTLFLAG_RD, &sc->trs_desc.reply_timo_ms, 0, "SCMI Max message RX timeout ms"); 224*e6d0edd2SCristian Marussi 22554b96380SRuslan Bukin /* 22654b96380SRuslan Bukin * Allow devices to identify. 22754b96380SRuslan Bukin */ 228723da5d9SJohn Baldwin bus_identify_children(dev); 22954b96380SRuslan Bukin 23054b96380SRuslan Bukin /* 23154b96380SRuslan Bukin * Now walk the OFW tree and attach top-level devices. 23254b96380SRuslan Bukin */ 23354b96380SRuslan Bukin for (node = OF_child(node); node > 0; node = OF_peer(node)) 23454b96380SRuslan Bukin simplebus_add_device(dev, node, 0, NULL, -1, NULL); 23554b96380SRuslan Bukin 23618250ec6SJohn Baldwin bus_attach_children(dev); 23754b96380SRuslan Bukin 23818250ec6SJohn Baldwin return (0); 23954b96380SRuslan Bukin } 24054b96380SRuslan Bukin 24154b96380SRuslan Bukin static int 24254b96380SRuslan Bukin scmi_detach(device_t dev) 24354b96380SRuslan Bukin { 2443595f18fSCristian Marussi struct scmi_softc *sc; 24554b96380SRuslan Bukin 2463595f18fSCristian Marussi sc = device_get_softc(dev); 2473595f18fSCristian Marussi scmi_transport_cleanup(sc); 2483595f18fSCristian Marussi 24954b96380SRuslan Bukin return (0); 25054b96380SRuslan Bukin } 25154b96380SRuslan Bukin 25254b96380SRuslan Bukin static device_method_t scmi_methods[] = { 25354b96380SRuslan Bukin DEVMETHOD(device_attach, scmi_attach), 25454b96380SRuslan Bukin DEVMETHOD(device_detach, scmi_detach), 255d46f01fdSAndrew Turner 25654b96380SRuslan Bukin DEVMETHOD_END 25754b96380SRuslan Bukin }; 25854b96380SRuslan Bukin 25954b96380SRuslan Bukin DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc), 26054b96380SRuslan Bukin simplebus_driver); 26154b96380SRuslan Bukin 26254b96380SRuslan Bukin DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0); 26354b96380SRuslan Bukin MODULE_VERSION(scmi, 1); 2643595f18fSCristian Marussi 26535f93203SCristian Marussi static struct scmi_reqs_pool * 266b802926bSCristian Marussi scmi_reqs_pool_allocate(device_t dev, const int max_msg, const int max_payld_sz) 26735f93203SCristian Marussi { 26835f93203SCristian Marussi struct scmi_reqs_pool *rp; 26935f93203SCristian Marussi struct scmi_req *req; 27035f93203SCristian Marussi 27135f93203SCristian Marussi rp = malloc(sizeof(*rp), M_DEVBUF, M_ZERO | M_WAITOK); 27235f93203SCristian Marussi 27335f93203SCristian Marussi LIST_INIT(&rp->head); 27435f93203SCristian Marussi for (int i = 0; i < max_msg; i++) { 27535f93203SCristian Marussi req = malloc(sizeof(*req) + max_payld_sz, 27635f93203SCristian Marussi M_DEVBUF, M_ZERO | M_WAITOK); 27735f93203SCristian Marussi 278b802926bSCristian Marussi req->dev = dev; 279b802926bSCristian Marussi req->tsk.ta_context = &req->tsk; 280b802926bSCristian Marussi req->tsk.ta_func = scmi_req_async_waiter; 281b802926bSCristian Marussi 28235f93203SCristian Marussi mtx_init(&req->mtx, "req", "SCMI", MTX_SPIN); 28335f93203SCristian Marussi LIST_INSERT_HEAD(&rp->head, req, next); 28435f93203SCristian Marussi } 28535f93203SCristian Marussi 28635f93203SCristian Marussi mtx_init(&rp->mtx, "reqs_pool", "SCMI", MTX_SPIN); 28735f93203SCristian Marussi 28835f93203SCristian Marussi return (rp); 28935f93203SCristian Marussi } 29035f93203SCristian Marussi 29135f93203SCristian Marussi static void 29235f93203SCristian Marussi scmi_reqs_pool_free(struct scmi_reqs_pool *rp) 29335f93203SCristian Marussi { 29435f93203SCristian Marussi struct scmi_req *req; 29535f93203SCristian Marussi 29635f93203SCristian Marussi LIST_FOREACH(req, &rp->head, next) { 29735f93203SCristian Marussi mtx_destroy(&req->mtx); 29835f93203SCristian Marussi free(req, M_DEVBUF); 29935f93203SCristian Marussi } 30035f93203SCristian Marussi 30135f93203SCristian Marussi mtx_destroy(&rp->mtx); 30235f93203SCristian Marussi free(rp, M_DEVBUF); 30335f93203SCristian Marussi } 30435f93203SCristian Marussi 305103ea03eSCristian Marussi static void 306103ea03eSCristian Marussi scmi_transport_configure(struct scmi_transport_desc *td, phandle_t node) 3073595f18fSCristian Marussi { 308103ea03eSCristian Marussi if (OF_getencprop(node, "arm,max-msg", &td->max_msg, sizeof(td->max_msg)) == -1) 309103ea03eSCristian Marussi td->max_msg = SCMI_DEF_MAX_MSG; 310103ea03eSCristian Marussi 311103ea03eSCristian Marussi if (OF_getencprop(node, "arm,max-msg-size", &td->max_payld_sz, 312103ea03eSCristian Marussi sizeof(td->max_payld_sz)) == -1) 313103ea03eSCristian Marussi td->max_payld_sz = SCMI_DEF_MAX_MSG_PAYLD_SIZE; 314103ea03eSCristian Marussi } 315103ea03eSCristian Marussi 316103ea03eSCristian Marussi static int 317103ea03eSCristian Marussi scmi_transport_init(struct scmi_softc *sc, phandle_t node) 318103ea03eSCristian Marussi { 319103ea03eSCristian Marussi struct scmi_transport_desc *td = &sc->trs_desc; 3203595f18fSCristian Marussi struct scmi_transport *trs; 3213595f18fSCristian Marussi int ret; 3223595f18fSCristian Marussi 3233595f18fSCristian Marussi trs = malloc(sizeof(*trs), M_DEVBUF, M_ZERO | M_WAITOK); 3243595f18fSCristian Marussi 325103ea03eSCristian Marussi scmi_transport_configure(td, node); 326103ea03eSCristian Marussi 3273595f18fSCristian Marussi BIT_FILL(SCMI_MAX_TOKEN, &trs->avail_tokens); 3283595f18fSCristian Marussi mtx_init(&trs->mtx, "tokens", "SCMI", MTX_SPIN); 3293595f18fSCristian Marussi 330103ea03eSCristian Marussi trs->inflight_ht = hashinit(td->max_msg, M_DEVBUF, &trs->inflight_mask); 3313595f18fSCristian Marussi 33235f93203SCristian Marussi trs->chans[SCMI_CHAN_A2P] = 333b802926bSCristian Marussi scmi_reqs_pool_allocate(sc->dev, td->max_msg, td->max_payld_sz); 33435f93203SCristian Marussi if (trs->chans[SCMI_CHAN_A2P] == NULL) { 33535f93203SCristian Marussi free(trs, M_DEVBUF); 33635f93203SCristian Marussi return (ENOMEM); 33735f93203SCristian Marussi } 33835f93203SCristian Marussi 33935f93203SCristian Marussi trs->chans[SCMI_CHAN_P2A] = 340b802926bSCristian Marussi scmi_reqs_pool_allocate(sc->dev, td->max_msg, td->max_payld_sz); 34135f93203SCristian Marussi if (trs->chans[SCMI_CHAN_P2A] == NULL) { 34235f93203SCristian Marussi scmi_reqs_pool_free(trs->chans[SCMI_CHAN_A2P]); 34335f93203SCristian Marussi free(trs, M_DEVBUF); 34435f93203SCristian Marussi return (ENOMEM); 34535f93203SCristian Marussi } 34635f93203SCristian Marussi 3473595f18fSCristian Marussi sc->trs = trs; 3483595f18fSCristian Marussi ret = SCMI_TRANSPORT_INIT(sc->dev); 3493595f18fSCristian Marussi if (ret != 0) { 35035f93203SCristian Marussi scmi_reqs_pool_free(trs->chans[SCMI_CHAN_A2P]); 35135f93203SCristian Marussi scmi_reqs_pool_free(trs->chans[SCMI_CHAN_P2A]); 3523595f18fSCristian Marussi free(trs, M_DEVBUF); 3533595f18fSCristian Marussi return (ret); 3543595f18fSCristian Marussi } 3553595f18fSCristian Marussi 356103ea03eSCristian Marussi /* Use default transport timeout if not overridden by OF */ 357103ea03eSCristian Marussi OF_getencprop(node, "arm,max-rx-timeout-ms", &td->reply_timo_ms, 358103ea03eSCristian Marussi sizeof(td->reply_timo_ms)); 359103ea03eSCristian Marussi 3603595f18fSCristian Marussi return (0); 3613595f18fSCristian Marussi } 362103ea03eSCristian Marussi 3633595f18fSCristian Marussi static void 3643595f18fSCristian Marussi scmi_transport_cleanup(struct scmi_softc *sc) 3653595f18fSCristian Marussi { 3663595f18fSCristian Marussi 3673595f18fSCristian Marussi SCMI_TRANSPORT_CLEANUP(sc->dev); 3683595f18fSCristian Marussi mtx_destroy(&sc->trs->mtx); 3693595f18fSCristian Marussi hashdestroy(sc->trs->inflight_ht, M_DEVBUF, sc->trs->inflight_mask); 37035f93203SCristian Marussi scmi_reqs_pool_free(sc->trs->chans[SCMI_CHAN_A2P]); 37135f93203SCristian Marussi scmi_reqs_pool_free(sc->trs->chans[SCMI_CHAN_P2A]); 3723595f18fSCristian Marussi free(sc->trs, M_DEVBUF); 3733595f18fSCristian Marussi } 3743595f18fSCristian Marussi 37535f93203SCristian Marussi static struct scmi_req * 376e887179dSCristian Marussi scmi_req_initialized_alloc(device_t dev, int tx_payld_sz, int rx_payld_sz) 377e887179dSCristian Marussi { 378e887179dSCristian Marussi struct scmi_softc *sc; 379e887179dSCristian Marussi struct scmi_req *req; 380e887179dSCristian Marussi 381e887179dSCristian Marussi sc = device_get_softc(dev); 382e887179dSCristian Marussi 383e887179dSCristian Marussi if (tx_payld_sz > SCMI_MAX_MSG_PAYLD_SIZE(sc) || 384e887179dSCristian Marussi rx_payld_sz > SCMI_MAX_MSG_REPLY_SIZE(sc)) { 385e887179dSCristian Marussi device_printf(dev, "Unsupported payload size. Drop.\n"); 386e887179dSCristian Marussi return (NULL); 387e887179dSCristian Marussi } 388e887179dSCristian Marussi 389e887179dSCristian Marussi /* Pick one from free list */ 390e887179dSCristian Marussi req = scmi_req_alloc(sc, SCMI_CHAN_A2P); 391e887179dSCristian Marussi if (req == NULL) 392e887179dSCristian Marussi return (NULL); 393e887179dSCristian Marussi 394e887179dSCristian Marussi req->msg.tx_len = sizeof(req->msg.hdr) + tx_payld_sz; 395e887179dSCristian Marussi req->msg.rx_len = rx_payld_sz ? 396e887179dSCristian Marussi rx_payld_sz + 2 * sizeof(uint32_t) : SCMI_MAX_MSG_SIZE(sc); 397e887179dSCristian Marussi 398e887179dSCristian Marussi return (req); 399e887179dSCristian Marussi } 400e887179dSCristian Marussi 401e887179dSCristian Marussi static struct scmi_req * 40235f93203SCristian Marussi scmi_req_alloc(struct scmi_softc *sc, enum scmi_chan ch_idx) 40335f93203SCristian Marussi { 40435f93203SCristian Marussi struct scmi_reqs_pool *rp; 40535f93203SCristian Marussi struct scmi_req *req = NULL; 40635f93203SCristian Marussi 40735f93203SCristian Marussi rp = sc->trs->chans[ch_idx]; 40835f93203SCristian Marussi mtx_lock_spin(&rp->mtx); 40935f93203SCristian Marussi if (!LIST_EMPTY(&rp->head)) { 41035f93203SCristian Marussi req = LIST_FIRST(&rp->head); 41135f93203SCristian Marussi LIST_REMOVE_HEAD(&rp->head, next); 41235f93203SCristian Marussi } 41335f93203SCristian Marussi mtx_unlock_spin(&rp->mtx); 41435f93203SCristian Marussi 415984f9fb8SCristian Marussi if (req != NULL) { 41635f93203SCristian Marussi refcount_init(&req->cnt, 1); 417984f9fb8SCristian Marussi SDT_PROBE3(scmi, func, scmi_req_alloc, req_alloc, 418984f9fb8SCristian Marussi req, refcount_load(&req->cnt), -1); 419984f9fb8SCristian Marussi } 42035f93203SCristian Marussi 42135f93203SCristian Marussi return (req); 42235f93203SCristian Marussi } 42335f93203SCristian Marussi 42435f93203SCristian Marussi static void 42535f93203SCristian Marussi scmi_req_free_unlocked(struct scmi_softc *sc, enum scmi_chan ch_idx, 42635f93203SCristian Marussi struct scmi_req *req) 42735f93203SCristian Marussi { 42835f93203SCristian Marussi struct scmi_reqs_pool *rp; 42935f93203SCristian Marussi 43035f93203SCristian Marussi rp = sc->trs->chans[ch_idx]; 43135f93203SCristian Marussi mtx_lock_spin(&rp->mtx); 43235f93203SCristian Marussi req->timed_out = false; 43335f93203SCristian Marussi req->done = false; 434c508841dSCristian Marussi req->is_raw = false; 43535f93203SCristian Marussi refcount_init(&req->cnt, 0); 43635f93203SCristian Marussi LIST_INSERT_HEAD(&rp->head, req, next); 43735f93203SCristian Marussi mtx_unlock_spin(&rp->mtx); 438984f9fb8SCristian Marussi 439984f9fb8SCristian Marussi SDT_PROBE3(scmi, func, scmi_req_free_unlocked, req_alloc, 440984f9fb8SCristian Marussi req, refcount_load(&req->cnt), -1); 44135f93203SCristian Marussi } 44235f93203SCristian Marussi 44335f93203SCristian Marussi static void 44435f93203SCristian Marussi scmi_req_get(struct scmi_softc *sc, struct scmi_req *req) 44535f93203SCristian Marussi { 44635f93203SCristian Marussi bool ok; 44735f93203SCristian Marussi 44835f93203SCristian Marussi mtx_lock_spin(&req->mtx); 44935f93203SCristian Marussi ok = refcount_acquire_if_not_zero(&req->cnt); 45035f93203SCristian Marussi mtx_unlock_spin(&req->mtx); 45135f93203SCristian Marussi 45235f93203SCristian Marussi if (!ok) 45335f93203SCristian Marussi device_printf(sc->dev, "%s() -- BAD REFCOUNT\n", __func__); 45435f93203SCristian Marussi 455984f9fb8SCristian Marussi SDT_PROBE3(scmi, func, scmi_req_get, req_alloc, 456984f9fb8SCristian Marussi req, refcount_load(&req->cnt), SCMI_MSG_TOKEN(req->msg.hdr)); 457984f9fb8SCristian Marussi 45835f93203SCristian Marussi return; 45935f93203SCristian Marussi } 46035f93203SCristian Marussi 46135f93203SCristian Marussi static void 46235f93203SCristian Marussi scmi_req_put(struct scmi_softc *sc, struct scmi_req *req) 46335f93203SCristian Marussi { 46435f93203SCristian Marussi mtx_lock_spin(&req->mtx); 46535f93203SCristian Marussi if (!refcount_release_if_not_last(&req->cnt)) { 466e887179dSCristian Marussi req->protocol_id = 0; 467e887179dSCristian Marussi req->message_id = 0; 468e887179dSCristian Marussi req->token = 0; 469e887179dSCristian Marussi req->header = 0; 470103ea03eSCristian Marussi bzero(&req->msg, sizeof(req->msg) + SCMI_MAX_MSG_PAYLD_SIZE(sc)); 47135f93203SCristian Marussi scmi_req_free_unlocked(sc, SCMI_CHAN_A2P, req); 472984f9fb8SCristian Marussi } else { 473984f9fb8SCristian Marussi SDT_PROBE3(scmi, func, scmi_req_put, req_alloc, 474984f9fb8SCristian Marussi req, refcount_load(&req->cnt), SCMI_MSG_TOKEN(req->msg.hdr)); 47535f93203SCristian Marussi } 47635f93203SCristian Marussi mtx_unlock_spin(&req->mtx); 47735f93203SCristian Marussi } 47835f93203SCristian Marussi 4793595f18fSCristian Marussi static int 4803595f18fSCristian Marussi scmi_token_pick(struct scmi_softc *sc) 4813595f18fSCristian Marussi { 4823595f18fSCristian Marussi unsigned long next_msg_id, token; 4833595f18fSCristian Marussi 4843595f18fSCristian Marussi mtx_lock_spin(&sc->trs->mtx); 4853595f18fSCristian Marussi /* 4863595f18fSCristian Marussi * next_id is a monotonically increasing unsigned long that can be used 4873595f18fSCristian Marussi * for tracing purposes; next_msg_id is a 10-bit sequence number derived 4883595f18fSCristian Marussi * from it. 4893595f18fSCristian Marussi */ 4903595f18fSCristian Marussi next_msg_id = sc->trs->next_id++ & SCMI_HDR_TOKEN_BF; 4913595f18fSCristian Marussi token = BIT_FFS_AT(SCMI_MAX_TOKEN, &sc->trs->avail_tokens, next_msg_id); 4923595f18fSCristian Marussi if (token != 0) 4933595f18fSCristian Marussi BIT_CLR(SCMI_MAX_TOKEN, token - 1, &sc->trs->avail_tokens); 4943595f18fSCristian Marussi mtx_unlock_spin(&sc->trs->mtx); 4953595f18fSCristian Marussi 4963595f18fSCristian Marussi /* 4973595f18fSCristian Marussi * BIT_FFS_AT returns 1-indexed values, so 0 means failure to find a 4983595f18fSCristian Marussi * free slot: all possible SCMI messages are in-flight using all of the 4993595f18fSCristian Marussi * SCMI_MAX_TOKEN sequence numbers. 5003595f18fSCristian Marussi */ 5013595f18fSCristian Marussi if (!token) 5023595f18fSCristian Marussi return (-EBUSY); 5033595f18fSCristian Marussi 5043595f18fSCristian Marussi return ((int)(token - 1)); 5053595f18fSCristian Marussi } 5063595f18fSCristian Marussi 507c508841dSCristian Marussi static int 508c508841dSCristian Marussi scmi_token_reserve(struct scmi_softc *sc, uint16_t candidate) 509c508841dSCristian Marussi { 510c508841dSCristian Marussi int token = -EBUSY, retries = 3; 511c508841dSCristian Marussi 512c508841dSCristian Marussi do { 513c508841dSCristian Marussi mtx_lock_spin(&sc->trs->mtx); 514c508841dSCristian Marussi if (BIT_ISSET(SCMI_MAX_TOKEN, candidate, &sc->trs->avail_tokens)) { 515c508841dSCristian Marussi BIT_CLR(SCMI_MAX_TOKEN, candidate, &sc->trs->avail_tokens); 516c508841dSCristian Marussi token = candidate; 517c508841dSCristian Marussi sc->trs->next_id++; 518c508841dSCristian Marussi } 519c508841dSCristian Marussi mtx_unlock_spin(&sc->trs->mtx); 520c508841dSCristian Marussi if (token == candidate || retries-- == 0) 521c508841dSCristian Marussi break; 522c508841dSCristian Marussi 523c508841dSCristian Marussi pause("scmi_tk_reserve", hz); 524c508841dSCristian Marussi } while (1); 525c508841dSCristian Marussi 526c508841dSCristian Marussi return (token); 527c508841dSCristian Marussi } 528c508841dSCristian Marussi 5293595f18fSCristian Marussi static void 5303595f18fSCristian Marussi scmi_token_release_unlocked(struct scmi_softc *sc, int token) 5313595f18fSCristian Marussi { 5323595f18fSCristian Marussi 5333595f18fSCristian Marussi BIT_SET(SCMI_MAX_TOKEN, token, &sc->trs->avail_tokens); 5343595f18fSCristian Marussi } 5353595f18fSCristian Marussi 5363595f18fSCristian Marussi static int 5373595f18fSCristian Marussi scmi_finalize_req(struct scmi_softc *sc, struct scmi_req *req) 5383595f18fSCristian Marussi { 539c508841dSCristian Marussi if (!req->is_raw) 5403595f18fSCristian Marussi req->token = scmi_token_pick(sc); 541c508841dSCristian Marussi else 542c508841dSCristian Marussi req->token = scmi_token_reserve(sc, SCMI_MSG_TOKEN(req->msg.hdr)); 543c508841dSCristian Marussi 5443595f18fSCristian Marussi if (req->token < 0) 5453595f18fSCristian Marussi return (EBUSY); 5463595f18fSCristian Marussi 547c508841dSCristian Marussi if (!req->is_raw) { 548c508841dSCristian Marussi req->msg.hdr = req->message_id; 549c508841dSCristian Marussi req->msg.hdr |= SCMI_MSG_TYPE_CMD << SCMI_HDR_MESSAGE_TYPE_S; 550c508841dSCristian Marussi req->msg.hdr |= req->protocol_id << SCMI_HDR_PROTOCOL_ID_S; 551c508841dSCristian Marussi req->msg.hdr |= req->token << SCMI_HDR_TOKEN_S; 552c508841dSCristian Marussi } 5533595f18fSCristian Marussi 554c508841dSCristian Marussi /* Save requested header */ 555c508841dSCristian Marussi req->header = req->msg.hdr; 5563595f18fSCristian Marussi 5573595f18fSCristian Marussi return (0); 5583595f18fSCristian Marussi } 5593595f18fSCristian Marussi 5603595f18fSCristian Marussi static int 5613595f18fSCristian Marussi scmi_req_track_inflight(struct scmi_softc *sc, struct scmi_req *req) 5623595f18fSCristian Marussi { 5633595f18fSCristian Marussi int error; 5643595f18fSCristian Marussi 5653595f18fSCristian Marussi /* build hdr, pick token */ 5663595f18fSCristian Marussi error = scmi_finalize_req(sc, req); 5673595f18fSCristian Marussi if (error != 0) 5683595f18fSCristian Marussi return (error); 5693595f18fSCristian Marussi 57035f93203SCristian Marussi /* Bump refcount to get hold of this in-flight transaction */ 57135f93203SCristian Marussi scmi_req_get(sc, req); 57235f93203SCristian Marussi /* Register in the inflight hashtable */ 5733595f18fSCristian Marussi mtx_lock_spin(&sc->trs->mtx); 5743595f18fSCristian Marussi LIST_INSERT_HEAD(REQHASH(sc, req->token), req, next); 5753595f18fSCristian Marussi mtx_unlock_spin(&sc->trs->mtx); 5763595f18fSCristian Marussi 5773595f18fSCristian Marussi return (0); 5783595f18fSCristian Marussi } 5793595f18fSCristian Marussi 5803595f18fSCristian Marussi static int 5813595f18fSCristian Marussi scmi_req_drop_inflight(struct scmi_softc *sc, struct scmi_req *req) 5823595f18fSCristian Marussi { 5833595f18fSCristian Marussi 58435f93203SCristian Marussi /* Remove from inflight hashtable at first ... */ 5853595f18fSCristian Marussi mtx_lock_spin(&sc->trs->mtx); 5863595f18fSCristian Marussi LIST_REMOVE(req, next); 5873595f18fSCristian Marussi scmi_token_release_unlocked(sc, req->token); 5883595f18fSCristian Marussi mtx_unlock_spin(&sc->trs->mtx); 58935f93203SCristian Marussi /* ...and drop refcount..potentially releasing *req */ 59035f93203SCristian Marussi scmi_req_put(sc, req); 5913595f18fSCristian Marussi 5923595f18fSCristian Marussi return (0); 5933595f18fSCristian Marussi } 5943595f18fSCristian Marussi 5953595f18fSCristian Marussi static struct scmi_req * 5963595f18fSCristian Marussi scmi_req_lookup_inflight(struct scmi_softc *sc, uint32_t hdr) 5973595f18fSCristian Marussi { 5983595f18fSCristian Marussi struct scmi_req *req = NULL; 5993595f18fSCristian Marussi unsigned int token; 6003595f18fSCristian Marussi 6013595f18fSCristian Marussi token = SCMI_MSG_TOKEN(hdr); 6023595f18fSCristian Marussi mtx_lock_spin(&sc->trs->mtx); 6033595f18fSCristian Marussi LIST_FOREACH(req, REQHASH(sc, token), next) { 6043595f18fSCristian Marussi if (req->token == token) 6053595f18fSCristian Marussi break; 6063595f18fSCristian Marussi } 6073595f18fSCristian Marussi mtx_unlock_spin(&sc->trs->mtx); 6083595f18fSCristian Marussi 6093595f18fSCristian Marussi return (req); 6103595f18fSCristian Marussi } 6113595f18fSCristian Marussi 6123595f18fSCristian Marussi static void 6139342829dSCristian Marussi scmi_process_response(struct scmi_softc *sc, uint32_t hdr, uint32_t rx_len) 6143595f18fSCristian Marussi { 61535f93203SCristian Marussi bool timed_out = false; 6163595f18fSCristian Marussi struct scmi_req *req; 6173595f18fSCristian Marussi 6183595f18fSCristian Marussi req = scmi_req_lookup_inflight(sc, hdr); 6193595f18fSCristian Marussi if (req == NULL) { 6203595f18fSCristian Marussi device_printf(sc->dev, 6213595f18fSCristian Marussi "Unexpected reply with header |%X| - token: 0x%X Drop.\n", 6223595f18fSCristian Marussi hdr, SCMI_MSG_TOKEN(hdr)); 6233595f18fSCristian Marussi return; 6243595f18fSCristian Marussi } 6253595f18fSCristian Marussi 626984f9fb8SCristian Marussi SDT_PROBE5(scmi, func, scmi_process_response, xfer_track, req, 627984f9fb8SCristian Marussi SCMI_MSG_PROTOCOL_ID(req->msg.hdr), SCMI_MSG_MESSAGE_ID(req->msg.hdr), 628984f9fb8SCristian Marussi SCMI_MSG_TOKEN(req->msg.hdr), req->timed_out); 629984f9fb8SCristian Marussi 63035f93203SCristian Marussi mtx_lock_spin(&req->mtx); 6313595f18fSCristian Marussi req->done = true; 6329342829dSCristian Marussi req->msg.rx_len = rx_len; 633a87dd741SCristian Marussi if (!req->timed_out) { 634a87dd741SCristian Marussi /* 635a87dd741SCristian Marussi * Consider the case in which a polled message is picked 636a87dd741SCristian Marussi * by chance on the IRQ path on another CPU: setting poll_done 637a87dd741SCristian Marussi * will terminate the other poll loop. 638a87dd741SCristian Marussi */ 639a87dd741SCristian Marussi if (!req->msg.polling) 6403595f18fSCristian Marussi wakeup(req); 64135f93203SCristian Marussi else 642a87dd741SCristian Marussi atomic_store_rel_int(&req->msg.poll_done, 1); 643a87dd741SCristian Marussi } else { 64435f93203SCristian Marussi timed_out = true; 645a87dd741SCristian Marussi } 64635f93203SCristian Marussi mtx_unlock_spin(&req->mtx); 64735f93203SCristian Marussi 64835f93203SCristian Marussi if (timed_out) 64935f93203SCristian Marussi device_printf(sc->dev, 65035f93203SCristian Marussi "Late reply for timed-out request - token: 0x%X. Ignore.\n", 65135f93203SCristian Marussi req->token); 65235f93203SCristian Marussi 65335f93203SCristian Marussi /* 65435f93203SCristian Marussi * In case of a late reply to a timed-out transaction this will 65535f93203SCristian Marussi * finally free the pending scmi_req 65635f93203SCristian Marussi */ 65735f93203SCristian Marussi scmi_req_drop_inflight(sc, req); 6583595f18fSCristian Marussi } 6593595f18fSCristian Marussi 6603595f18fSCristian Marussi void 6619342829dSCristian Marussi scmi_rx_irq_callback(device_t dev, void *chan, uint32_t hdr, uint32_t rx_len) 6623595f18fSCristian Marussi { 6633595f18fSCristian Marussi struct scmi_softc *sc; 6643595f18fSCristian Marussi 6653595f18fSCristian Marussi sc = device_get_softc(dev); 6663595f18fSCristian Marussi 667984f9fb8SCristian Marussi SDT_PROBE2(scmi, func, scmi_rx_irq_callback, hdr_dump, hdr, rx_len); 668984f9fb8SCristian Marussi 6693595f18fSCristian Marussi if (SCMI_IS_MSG_TYPE_NOTIF(hdr) || SCMI_IS_MSG_TYPE_DRESP(hdr)) { 6703595f18fSCristian Marussi device_printf(dev, "DRESP/NOTIF unsupported. Drop.\n"); 6713595f18fSCristian Marussi SCMI_CLEAR_CHANNEL(dev, chan); 6723595f18fSCristian Marussi return; 6733595f18fSCristian Marussi } 6743595f18fSCristian Marussi 6759342829dSCristian Marussi scmi_process_response(sc, hdr, rx_len); 6763595f18fSCristian Marussi } 6773595f18fSCristian Marussi 6783595f18fSCristian Marussi static int 67935f93203SCristian Marussi scmi_wait_for_response(struct scmi_softc *sc, struct scmi_req *req, void **out) 6803595f18fSCristian Marussi { 681103ea03eSCristian Marussi unsigned int reply_timo_ms = SCMI_MAX_MSG_TIMEOUT_MS(sc); 6823595f18fSCristian Marussi int ret; 6833595f18fSCristian Marussi 684984f9fb8SCristian Marussi SDT_PROBE5(scmi, entry, scmi_wait_for_response, xfer_track, req, 685984f9fb8SCristian Marussi SCMI_MSG_PROTOCOL_ID(req->msg.hdr), SCMI_MSG_MESSAGE_ID(req->msg.hdr), 686984f9fb8SCristian Marussi SCMI_MSG_TOKEN(req->msg.hdr), reply_timo_ms); 687984f9fb8SCristian Marussi 68835f93203SCristian Marussi if (req->msg.polling) { 68935f93203SCristian Marussi bool needs_drop; 69035f93203SCristian Marussi 691103ea03eSCristian Marussi ret = SCMI_POLL_MSG(sc->dev, &req->msg, reply_timo_ms); 69235f93203SCristian Marussi /* 69335f93203SCristian Marussi * Drop reference to successfully polled req unless it had 69435f93203SCristian Marussi * already also been processed on the IRQ path. 69535f93203SCristian Marussi * Addresses a possible race-condition between polling and 69635f93203SCristian Marussi * interrupt reception paths. 69735f93203SCristian Marussi */ 69835f93203SCristian Marussi mtx_lock_spin(&req->mtx); 69935f93203SCristian Marussi needs_drop = (ret == 0) && !req->done; 700c56e586fSCristian Marussi req->timed_out = ret != 0; 70135f93203SCristian Marussi mtx_unlock_spin(&req->mtx); 70235f93203SCristian Marussi if (needs_drop) 70335f93203SCristian Marussi scmi_req_drop_inflight(sc, req); 70435f93203SCristian Marussi if (ret == 0 && req->msg.hdr != req->header) { 70535f93203SCristian Marussi device_printf(sc->dev, 70635f93203SCristian Marussi "Malformed reply with header |%08X|. Expected: |%08X|Drop.\n", 70735f93203SCristian Marussi le32toh(req->msg.hdr), le32toh(req->header)); 70835f93203SCristian Marussi } 7093595f18fSCristian Marussi } else { 710103ea03eSCristian Marussi ret = tsleep(req, 0, "scmi_wait4", (reply_timo_ms * hz) / 1000); 7113595f18fSCristian Marussi /* Check for lost wakeups since there is no associated lock */ 71235f93203SCristian Marussi mtx_lock_spin(&req->mtx); 7133595f18fSCristian Marussi if (ret != 0 && req->done) 7143595f18fSCristian Marussi ret = 0; 715c56e586fSCristian Marussi req->timed_out = ret != 0; 71635f93203SCristian Marussi mtx_unlock_spin(&req->mtx); 7173595f18fSCristian Marussi } 7183595f18fSCristian Marussi 71935f93203SCristian Marussi if (ret == 0) { 72035f93203SCristian Marussi SCMI_COLLECT_REPLY(sc->dev, &req->msg); 72135f93203SCristian Marussi if (req->msg.payld[0] != 0) 72235f93203SCristian Marussi ret = req->msg.payld[0]; 723b802926bSCristian Marussi if (out != NULL) 72435f93203SCristian Marussi *out = &req->msg.payld[SCMI_MSG_HDR_SIZE]; 72535f93203SCristian Marussi } else { 7263595f18fSCristian Marussi device_printf(sc->dev, 7273595f18fSCristian Marussi "Request for token 0x%X timed-out.\n", req->token); 72835f93203SCristian Marussi } 7293595f18fSCristian Marussi 7303595f18fSCristian Marussi SCMI_TX_COMPLETE(sc->dev, NULL); 7313595f18fSCristian Marussi 732984f9fb8SCristian Marussi SDT_PROBE5(scmi, exit, scmi_wait_for_response, xfer_track, req, 733984f9fb8SCristian Marussi SCMI_MSG_PROTOCOL_ID(req->msg.hdr), SCMI_MSG_MESSAGE_ID(req->msg.hdr), 734984f9fb8SCristian Marussi SCMI_MSG_TOKEN(req->msg.hdr), req->timed_out); 735984f9fb8SCristian Marussi 7363595f18fSCristian Marussi return (ret); 7373595f18fSCristian Marussi } 7383595f18fSCristian Marussi 73935f93203SCristian Marussi void * 74035f93203SCristian Marussi scmi_buf_get(device_t dev, uint8_t protocol_id, uint8_t message_id, 74135f93203SCristian Marussi int tx_payld_sz, int rx_payld_sz) 7423595f18fSCristian Marussi { 74335f93203SCristian Marussi struct scmi_req *req; 74435f93203SCristian Marussi 745e887179dSCristian Marussi /* Pick a pre-built req */ 746e887179dSCristian Marussi req = scmi_req_initialized_alloc(dev, tx_payld_sz, rx_payld_sz); 74735f93203SCristian Marussi if (req == NULL) 74835f93203SCristian Marussi return (NULL); 74935f93203SCristian Marussi 75035f93203SCristian Marussi req->protocol_id = protocol_id & SCMI_HDR_PROTOCOL_ID_BF; 75135f93203SCristian Marussi req->message_id = message_id & SCMI_HDR_MESSAGE_ID_BF; 75235f93203SCristian Marussi 75335f93203SCristian Marussi return (&req->msg.payld[0]); 75435f93203SCristian Marussi } 75535f93203SCristian Marussi 75635f93203SCristian Marussi void 75735f93203SCristian Marussi scmi_buf_put(device_t dev, void *buf) 75835f93203SCristian Marussi { 75935f93203SCristian Marussi struct scmi_softc *sc; 76035f93203SCristian Marussi struct scmi_req *req; 76135f93203SCristian Marussi 76235f93203SCristian Marussi sc = device_get_softc(dev); 76335f93203SCristian Marussi 76435f93203SCristian Marussi req = buf_to_req(buf); 76535f93203SCristian Marussi scmi_req_put(sc, req); 76635f93203SCristian Marussi } 76735f93203SCristian Marussi 768e887179dSCristian Marussi struct scmi_msg * 769e887179dSCristian Marussi scmi_msg_get(device_t dev, int tx_payld_sz, int rx_payld_sz) 770e887179dSCristian Marussi { 771e887179dSCristian Marussi struct scmi_req *req; 772e887179dSCristian Marussi 773e887179dSCristian Marussi /* Pick a pre-built req */ 774e887179dSCristian Marussi req = scmi_req_initialized_alloc(dev, tx_payld_sz, rx_payld_sz); 775e887179dSCristian Marussi if (req == NULL) 776e887179dSCristian Marussi return (NULL); 777e887179dSCristian Marussi 778c508841dSCristian Marussi req->is_raw = true; 779c508841dSCristian Marussi 780e887179dSCristian Marussi return (&req->msg); 781e887179dSCristian Marussi } 782e887179dSCristian Marussi 783b802926bSCristian Marussi static void 784b802926bSCristian Marussi scmi_req_async_waiter(void *context, int pending) 785b802926bSCristian Marussi { 786b802926bSCristian Marussi struct task *ta = context; 787b802926bSCristian Marussi struct scmi_softc *sc; 788b802926bSCristian Marussi struct scmi_req *req; 789b802926bSCristian Marussi 790b802926bSCristian Marussi req = tsk_to_req(ta); 791b802926bSCristian Marussi sc = device_get_softc(req->dev); 792b802926bSCristian Marussi scmi_wait_for_response(sc, req, NULL); 793b802926bSCristian Marussi 794b802926bSCristian Marussi scmi_msg_put(req->dev, &req->msg); 795b802926bSCristian Marussi } 796b802926bSCristian Marussi 797e887179dSCristian Marussi void 798e887179dSCristian Marussi scmi_msg_put(device_t dev, struct scmi_msg *msg) 799e887179dSCristian Marussi { 800e887179dSCristian Marussi struct scmi_softc *sc; 801e887179dSCristian Marussi struct scmi_req *req; 802e887179dSCristian Marussi 803e887179dSCristian Marussi sc = device_get_softc(dev); 804e887179dSCristian Marussi 805e887179dSCristian Marussi req = msg_to_req(msg); 806e887179dSCristian Marussi 807e887179dSCristian Marussi scmi_req_put(sc, req); 808e887179dSCristian Marussi } 809e887179dSCristian Marussi 81035f93203SCristian Marussi int 811341d8fd9SCristian Marussi scmi_request_tx(device_t dev, void *in) 81235f93203SCristian Marussi { 81335f93203SCristian Marussi struct scmi_softc *sc; 81435f93203SCristian Marussi struct scmi_req *req; 8153595f18fSCristian Marussi int error; 8163595f18fSCristian Marussi 8173595f18fSCristian Marussi sc = device_get_softc(dev); 8183595f18fSCristian Marussi 81935f93203SCristian Marussi req = buf_to_req(in); 82035f93203SCristian Marussi 82135f93203SCristian Marussi req->msg.polling = 82235f93203SCristian Marussi (cold || sc->trs_desc.no_completion_irq || req->use_polling); 8233595f18fSCristian Marussi 8243595f18fSCristian Marussi /* Set inflight and send using transport specific method - refc-2 */ 8253595f18fSCristian Marussi error = scmi_req_track_inflight(sc, req); 826341d8fd9SCristian Marussi if (error != 0) { 827341d8fd9SCristian Marussi device_printf(dev, "Failed to build req with HDR |%0X|\n", 828341d8fd9SCristian Marussi req->msg.hdr); 8293595f18fSCristian Marussi return (error); 830341d8fd9SCristian Marussi } 8313595f18fSCristian Marussi 83235f93203SCristian Marussi error = SCMI_XFER_MSG(sc->dev, &req->msg); 83335f93203SCristian Marussi if (error != 0) { 8343595f18fSCristian Marussi scmi_req_drop_inflight(sc, req); 8353595f18fSCristian Marussi return (error); 8363595f18fSCristian Marussi } 83735f93203SCristian Marussi 838984f9fb8SCristian Marussi SDT_PROBE5(scmi, func, scmi_request_tx, xfer_track, req, 839984f9fb8SCristian Marussi SCMI_MSG_PROTOCOL_ID(req->msg.hdr), SCMI_MSG_MESSAGE_ID(req->msg.hdr), 840984f9fb8SCristian Marussi SCMI_MSG_TOKEN(req->msg.hdr), req->msg.polling); 841984f9fb8SCristian Marussi 842341d8fd9SCristian Marussi return (0); 843341d8fd9SCristian Marussi } 844341d8fd9SCristian Marussi 845341d8fd9SCristian Marussi int 846341d8fd9SCristian Marussi scmi_request(device_t dev, void *in, void **out) 847341d8fd9SCristian Marussi { 848341d8fd9SCristian Marussi struct scmi_softc *sc; 849341d8fd9SCristian Marussi struct scmi_req *req; 850341d8fd9SCristian Marussi int error; 851341d8fd9SCristian Marussi 852341d8fd9SCristian Marussi error = scmi_request_tx(dev, in); 853341d8fd9SCristian Marussi if (error != 0) 854341d8fd9SCristian Marussi return (error); 855341d8fd9SCristian Marussi 856341d8fd9SCristian Marussi sc = device_get_softc(dev); 857341d8fd9SCristian Marussi req = buf_to_req(in); 858341d8fd9SCristian Marussi 85935f93203SCristian Marussi return (scmi_wait_for_response(sc, req, out)); 86035f93203SCristian Marussi } 861b802926bSCristian Marussi 862b802926bSCristian Marussi int 863b802926bSCristian Marussi scmi_msg_async_enqueue(struct scmi_msg *msg) 864b802926bSCristian Marussi { 865b802926bSCristian Marussi struct scmi_req *req; 866b802926bSCristian Marussi 867b802926bSCristian Marussi req = msg_to_req(msg); 868b802926bSCristian Marussi 869b802926bSCristian Marussi return taskqueue_enqueue_flags(taskqueue_thread, &req->tsk, 870b802926bSCristian Marussi TASKQUEUE_FAIL_IF_PENDING | TASKQUEUE_FAIL_IF_CANCELING); 871b802926bSCristian Marussi } 872