xref: /freebsd-src/sys/dev/firmware/arm/scmi.c (revision e6d0edd2fe8c3c15faa7c9c66703efa007a5fbe1)
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