xref: /freebsd/sys/dev/firmware/arm/scmi.c (revision a87dd741)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Ruslan Bukin <br@bsdpad.com>
5  * Copyright (c) 2023 Arm Ltd
6  *
7  * This work was supported by Innovate UK project 105694, "Digital Security
8  * by Design (DSbD) Technology Platform Prototype".
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/_bitset.h>
35 #include <sys/bitset.h>
36 #include <sys/bus.h>
37 #include <sys/cpu.h>
38 #include <sys/endian.h>
39 #include <sys/kernel.h>
40 #include <sys/lock.h>
41 #include <sys/malloc.h>
42 #include <sys/module.h>
43 #include <sys/mutex.h>
44 #include <sys/queue.h>
45 #include <sys/refcount.h>
46 
47 #include <dev/clk/clk.h>
48 #include <dev/fdt/simplebus.h>
49 #include <dev/fdt/fdt_common.h>
50 #include <dev/ofw/ofw_bus_subr.h>
51 
52 #include "scmi.h"
53 #include "scmi_protocols.h"
54 
55 #define SCMI_MAX_TOKEN		1024
56 
57 #define	SCMI_HDR_TOKEN_S		18
58 #define SCMI_HDR_TOKEN_BF		(0x3fff)
59 #define	SCMI_HDR_TOKEN_M		(SCMI_HDR_TOKEN_BF << SCMI_HDR_TOKEN_S)
60 
61 #define	SCMI_HDR_PROTOCOL_ID_S		10
62 #define	SCMI_HDR_PROTOCOL_ID_BF		(0xff)
63 #define	SCMI_HDR_PROTOCOL_ID_M		\
64     (SCMI_HDR_PROTOCOL_ID_BF << SCMI_HDR_PROTOCOL_ID_S)
65 
66 #define	SCMI_HDR_MESSAGE_TYPE_S		8
67 #define	SCMI_HDR_MESSAGE_TYPE_BF	(0x3)
68 #define	SCMI_HDR_MESSAGE_TYPE_M		\
69     (SCMI_HDR_MESSAGE_TYPE_BF << SCMI_HDR_MESSAGE_TYPE_S)
70 
71 #define	SCMI_HDR_MESSAGE_ID_S		0
72 #define	SCMI_HDR_MESSAGE_ID_BF		(0xff)
73 #define	SCMI_HDR_MESSAGE_ID_M		\
74     (SCMI_HDR_MESSAGE_ID_BF << SCMI_HDR_MESSAGE_ID_S)
75 
76 #define SCMI_MSG_TYPE_CMD	0
77 #define SCMI_MSG_TYPE_DRESP	2
78 #define SCMI_MSG_TYPE_NOTIF	3
79 
80 #define SCMI_MSG_TYPE_CHECK(_h, _t)					\
81     ((((_h) & SCMI_HDR_MESSAGE_TYPE_M) >> SCMI_HDR_MESSAGE_TYPE_S) == (_t))
82 
83 #define SCMI_IS_MSG_TYPE_NOTIF(h)					\
84     SCMI_MSG_TYPE_CHECK((h), SCMI_MSG_TYPE_NOTIF)
85 #define SCMI_IS_MSG_TYPE_DRESP(h)					\
86     SCMI_MSG_TYPE_CHECK((h), SCMI_MSG_TYPE_DRESP)
87 
88 #define SCMI_MSG_TOKEN(_hdr)		\
89     (((_hdr) & SCMI_HDR_TOKEN_M) >> SCMI_HDR_TOKEN_S)
90 
91 struct scmi_req {
92 	int		cnt;
93 	bool		timed_out;
94 	bool		use_polling;
95 	bool		done;
96 	struct mtx	mtx;
97 	LIST_ENTRY(scmi_req)	next;
98 	int		protocol_id;
99 	int		message_id;
100 	int		token;
101 	uint32_t	header;
102 	struct scmi_msg msg;
103 };
104 
105 #define buf_to_msg(b)	__containerof((b), struct scmi_msg, payld)
106 #define msg_to_req(m)	__containerof((m), struct scmi_req, msg)
107 #define buf_to_req(b)	msg_to_req(buf_to_msg(b))
108 
109 LIST_HEAD(reqs_head, scmi_req);
110 
111 struct scmi_reqs_pool {
112 	struct mtx		mtx;
113 	struct reqs_head	head;
114 };
115 
116 BITSET_DEFINE(_scmi_tokens, SCMI_MAX_TOKEN);
117 LIST_HEAD(inflight_head, scmi_req);
118 #define	REQHASH(_sc, _tk)		\
119     (&((_sc)->trs->inflight_ht[(_tk) & (_sc)->trs->inflight_mask]))
120 
121 struct scmi_transport {
122 	unsigned long		next_id;
123 	struct _scmi_tokens	avail_tokens;
124 	struct inflight_head	*inflight_ht;
125 	unsigned long		inflight_mask;
126 	struct scmi_reqs_pool	*chans[SCMI_CHAN_MAX];
127 	struct mtx		mtx;
128 };
129 
130 static int		scmi_transport_init(struct scmi_softc *);
131 static void		scmi_transport_cleanup(struct scmi_softc *);
132 static struct scmi_reqs_pool *scmi_reqs_pool_allocate(const int, const int);
133 static void		scmi_reqs_pool_free(struct scmi_reqs_pool *);
134 static struct scmi_req *scmi_req_alloc(struct scmi_softc *, enum scmi_chan);
135 static void		scmi_req_free_unlocked(struct scmi_softc *,
136     enum scmi_chan, struct scmi_req *);
137 static void		scmi_req_get(struct scmi_softc *, struct scmi_req *);
138 static void		scmi_req_put(struct scmi_softc *, struct scmi_req *);
139 static int		scmi_token_pick(struct scmi_softc *);
140 static void		scmi_token_release_unlocked(struct scmi_softc *, int);
141 static int		scmi_req_track_inflight(struct scmi_softc *,
142 			    struct scmi_req *);
143 static int		scmi_req_drop_inflight(struct scmi_softc *,
144 			    struct scmi_req *);
145 static struct scmi_req *scmi_req_lookup_inflight(struct scmi_softc *, uint32_t);
146 
147 static int		scmi_wait_for_response(struct scmi_softc *,
148 			    struct scmi_req *, void **);
149 static void		scmi_process_response(struct scmi_softc *, uint32_t);
150 
151 int
scmi_attach(device_t dev)152 scmi_attach(device_t dev)
153 {
154 	struct scmi_softc *sc;
155 	phandle_t node;
156 	int error;
157 
158 	sc = device_get_softc(dev);
159 	sc->dev = dev;
160 
161 	node = ofw_bus_get_node(dev);
162 	if (node == -1)
163 		return (ENXIO);
164 
165 	simplebus_init(dev, node);
166 
167 	error = scmi_transport_init(sc);
168 	if (error != 0)
169 		return (error);
170 
171 	device_printf(dev, "Transport reply timeout initialized to %dms\n",
172 	    sc->trs_desc.reply_timo_ms);
173 
174 	/*
175 	 * Allow devices to identify.
176 	 */
177 	bus_generic_probe(dev);
178 
179 	/*
180 	 * Now walk the OFW tree and attach top-level devices.
181 	 */
182 	for (node = OF_child(node); node > 0; node = OF_peer(node))
183 		simplebus_add_device(dev, node, 0, NULL, -1, NULL);
184 
185 	error = bus_generic_attach(dev);
186 
187 	return (error);
188 }
189 
190 static int
scmi_detach(device_t dev)191 scmi_detach(device_t dev)
192 {
193 	struct scmi_softc *sc;
194 
195 	sc = device_get_softc(dev);
196 	scmi_transport_cleanup(sc);
197 
198 	return (0);
199 }
200 
201 static device_method_t scmi_methods[] = {
202 	DEVMETHOD(device_attach,	scmi_attach),
203 	DEVMETHOD(device_detach,	scmi_detach),
204 
205 	DEVMETHOD_END
206 };
207 
208 DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc),
209     simplebus_driver);
210 
211 DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0);
212 MODULE_VERSION(scmi, 1);
213 
214 static struct scmi_reqs_pool *
scmi_reqs_pool_allocate(const int max_msg,const int max_payld_sz)215 scmi_reqs_pool_allocate(const int max_msg, const int max_payld_sz)
216 {
217 	struct scmi_reqs_pool *rp;
218 	struct scmi_req *req;
219 
220 	rp = malloc(sizeof(*rp), M_DEVBUF, M_ZERO | M_WAITOK);
221 
222 	LIST_INIT(&rp->head);
223 	for (int i = 0; i < max_msg; i++) {
224 		req = malloc(sizeof(*req) + max_payld_sz,
225 		    M_DEVBUF, M_ZERO | M_WAITOK);
226 
227 		mtx_init(&req->mtx, "req", "SCMI", MTX_SPIN);
228 		LIST_INSERT_HEAD(&rp->head, req, next);
229 	}
230 
231 	mtx_init(&rp->mtx, "reqs_pool", "SCMI", MTX_SPIN);
232 
233 	return (rp);
234 }
235 
236 static void
scmi_reqs_pool_free(struct scmi_reqs_pool * rp)237 scmi_reqs_pool_free(struct scmi_reqs_pool *rp)
238 {
239 	struct scmi_req *req;
240 
241 	LIST_FOREACH(req, &rp->head, next) {
242 		mtx_destroy(&req->mtx);
243 		free(req, M_DEVBUF);
244 	}
245 
246 	mtx_destroy(&rp->mtx);
247 	free(rp, M_DEVBUF);
248 }
249 
250 static int
scmi_transport_init(struct scmi_softc * sc)251 scmi_transport_init(struct scmi_softc *sc)
252 {
253 	struct scmi_transport *trs;
254 	int ret;
255 
256 	trs = malloc(sizeof(*trs), M_DEVBUF, M_ZERO | M_WAITOK);
257 
258 	BIT_FILL(SCMI_MAX_TOKEN, &trs->avail_tokens);
259 	mtx_init(&trs->mtx, "tokens", "SCMI", MTX_SPIN);
260 
261 	trs->inflight_ht = hashinit(SCMI_MAX_MSG, M_DEVBUF,
262 	    &trs->inflight_mask);
263 
264 	trs->chans[SCMI_CHAN_A2P] =
265 	    scmi_reqs_pool_allocate(SCMI_MAX_MSG, SCMI_MAX_MSG_PAYLD_SIZE);
266 	if (trs->chans[SCMI_CHAN_A2P] == NULL) {
267 		free(trs, M_DEVBUF);
268 		return (ENOMEM);
269 	}
270 
271 	trs->chans[SCMI_CHAN_P2A] =
272 	    scmi_reqs_pool_allocate(SCMI_MAX_MSG, SCMI_MAX_MSG_PAYLD_SIZE);
273 	if (trs->chans[SCMI_CHAN_P2A] == NULL) {
274 		scmi_reqs_pool_free(trs->chans[SCMI_CHAN_A2P]);
275 		free(trs, M_DEVBUF);
276 		return (ENOMEM);
277 	}
278 
279 	sc->trs = trs;
280 	ret = SCMI_TRANSPORT_INIT(sc->dev);
281 	if (ret != 0) {
282 		scmi_reqs_pool_free(trs->chans[SCMI_CHAN_A2P]);
283 		scmi_reqs_pool_free(trs->chans[SCMI_CHAN_P2A]);
284 		free(trs, M_DEVBUF);
285 		return (ret);
286 	}
287 
288 	return (0);
289 }
290 static void
scmi_transport_cleanup(struct scmi_softc * sc)291 scmi_transport_cleanup(struct scmi_softc *sc)
292 {
293 
294 	SCMI_TRANSPORT_CLEANUP(sc->dev);
295 	mtx_destroy(&sc->trs->mtx);
296 	hashdestroy(sc->trs->inflight_ht, M_DEVBUF, sc->trs->inflight_mask);
297 	scmi_reqs_pool_free(sc->trs->chans[SCMI_CHAN_A2P]);
298 	scmi_reqs_pool_free(sc->trs->chans[SCMI_CHAN_P2A]);
299 	free(sc->trs, M_DEVBUF);
300 }
301 
302 static struct scmi_req *
scmi_req_alloc(struct scmi_softc * sc,enum scmi_chan ch_idx)303 scmi_req_alloc(struct scmi_softc *sc, enum scmi_chan ch_idx)
304 {
305 	struct scmi_reqs_pool *rp;
306 	struct scmi_req *req = NULL;
307 
308 	rp = sc->trs->chans[ch_idx];
309 	mtx_lock_spin(&rp->mtx);
310 	if (!LIST_EMPTY(&rp->head)) {
311 		req = LIST_FIRST(&rp->head);
312 		LIST_REMOVE_HEAD(&rp->head, next);
313 	}
314 	mtx_unlock_spin(&rp->mtx);
315 
316 	if (req != NULL)
317 		refcount_init(&req->cnt, 1);
318 
319 	return (req);
320 }
321 
322 static void
scmi_req_free_unlocked(struct scmi_softc * sc,enum scmi_chan ch_idx,struct scmi_req * req)323 scmi_req_free_unlocked(struct scmi_softc *sc, enum scmi_chan ch_idx,
324     struct scmi_req *req)
325 {
326 	struct scmi_reqs_pool *rp;
327 
328 	rp = sc->trs->chans[ch_idx];
329 	mtx_lock_spin(&rp->mtx);
330 	req->timed_out = false;
331 	req->done = false;
332 	refcount_init(&req->cnt, 0);
333 	LIST_INSERT_HEAD(&rp->head, req, next);
334 	mtx_unlock_spin(&rp->mtx);
335 }
336 
337 static void
scmi_req_get(struct scmi_softc * sc,struct scmi_req * req)338 scmi_req_get(struct scmi_softc *sc, struct scmi_req *req)
339 {
340 	bool ok;
341 
342 	mtx_lock_spin(&req->mtx);
343 	ok = refcount_acquire_if_not_zero(&req->cnt);
344 	mtx_unlock_spin(&req->mtx);
345 
346 	if (!ok)
347 		device_printf(sc->dev, "%s() -- BAD REFCOUNT\n", __func__);
348 
349 	return;
350 }
351 
352 static void
scmi_req_put(struct scmi_softc * sc,struct scmi_req * req)353 scmi_req_put(struct scmi_softc *sc, struct scmi_req *req)
354 {
355 	mtx_lock_spin(&req->mtx);
356 	if (!refcount_release_if_not_last(&req->cnt)) {
357 		bzero(&req->msg, sizeof(req->msg) + SCMI_MAX_MSG_PAYLD_SIZE);
358 		scmi_req_free_unlocked(sc, SCMI_CHAN_A2P, req);
359 	}
360 	mtx_unlock_spin(&req->mtx);
361 }
362 
363 static int
scmi_token_pick(struct scmi_softc * sc)364 scmi_token_pick(struct scmi_softc *sc)
365 {
366 	unsigned long next_msg_id, token;
367 
368 	mtx_lock_spin(&sc->trs->mtx);
369 	/*
370 	 * next_id is a monotonically increasing unsigned long that can be used
371 	 * for tracing purposes; next_msg_id is a 10-bit sequence number derived
372 	 * from it.
373 	 */
374 	next_msg_id = sc->trs->next_id++ & SCMI_HDR_TOKEN_BF;
375 	token = BIT_FFS_AT(SCMI_MAX_TOKEN, &sc->trs->avail_tokens, next_msg_id);
376 	/* TODO Account for wrap-arounds and holes */
377 	if (token != 0)
378 		BIT_CLR(SCMI_MAX_TOKEN, token - 1, &sc->trs->avail_tokens);
379 	mtx_unlock_spin(&sc->trs->mtx);
380 
381 	/*
382 	 * BIT_FFS_AT returns 1-indexed values, so 0 means failure to find a
383 	 * free slot: all possible SCMI messages are in-flight using all of the
384 	 * SCMI_MAX_TOKEN sequence numbers.
385 	 */
386 	if (!token)
387 		return (-EBUSY);
388 
389 	return ((int)(token - 1));
390 }
391 
392 static void
scmi_token_release_unlocked(struct scmi_softc * sc,int token)393 scmi_token_release_unlocked(struct scmi_softc *sc, int token)
394 {
395 
396 	BIT_SET(SCMI_MAX_TOKEN, token, &sc->trs->avail_tokens);
397 }
398 
399 static int
scmi_finalize_req(struct scmi_softc * sc,struct scmi_req * req)400 scmi_finalize_req(struct scmi_softc *sc, struct scmi_req *req)
401 {
402 	uint32_t header = 0;
403 
404 	req->token = scmi_token_pick(sc);
405 	if (req->token < 0)
406 		return (EBUSY);
407 
408 	header = req->message_id;
409 	header |= SCMI_MSG_TYPE_CMD << SCMI_HDR_MESSAGE_TYPE_S;
410 	header |= req->protocol_id << SCMI_HDR_PROTOCOL_ID_S;
411 	header |= req->token << SCMI_HDR_TOKEN_S;
412 
413 	req->header = htole32(header);
414 	req->msg.hdr = htole32(header);
415 
416 	return (0);
417 }
418 
419 static int
scmi_req_track_inflight(struct scmi_softc * sc,struct scmi_req * req)420 scmi_req_track_inflight(struct scmi_softc *sc, struct scmi_req *req)
421 {
422 	int error;
423 
424 	/* build hdr, pick token */
425 	error = scmi_finalize_req(sc, req);
426 	if (error != 0)
427 		return (error);
428 
429 	/* Bump refcount to get hold of this in-flight transaction */
430 	scmi_req_get(sc, req);
431 	/* Register in the inflight hashtable */
432 	mtx_lock_spin(&sc->trs->mtx);
433 	LIST_INSERT_HEAD(REQHASH(sc, req->token), req, next);
434 	mtx_unlock_spin(&sc->trs->mtx);
435 
436 	return (0);
437 }
438 
439 static int
scmi_req_drop_inflight(struct scmi_softc * sc,struct scmi_req * req)440 scmi_req_drop_inflight(struct scmi_softc *sc, struct scmi_req *req)
441 {
442 
443 	/* Remove from inflight hashtable at first ... */
444 	mtx_lock_spin(&sc->trs->mtx);
445 	LIST_REMOVE(req, next);
446 	scmi_token_release_unlocked(sc, req->token);
447 	mtx_unlock_spin(&sc->trs->mtx);
448 	/* ...and drop refcount..potentially releasing *req */
449 	scmi_req_put(sc, req);
450 
451 	return (0);
452 }
453 
454 static struct scmi_req *
scmi_req_lookup_inflight(struct scmi_softc * sc,uint32_t hdr)455 scmi_req_lookup_inflight(struct scmi_softc *sc, uint32_t hdr)
456 {
457 	struct scmi_req *req = NULL;
458 	unsigned int token;
459 
460 	token = SCMI_MSG_TOKEN(hdr);
461 	mtx_lock_spin(&sc->trs->mtx);
462 	LIST_FOREACH(req, REQHASH(sc, token), next) {
463 		if (req->token == token)
464 			break;
465 	}
466 	mtx_unlock_spin(&sc->trs->mtx);
467 
468 	return (req);
469 }
470 
471 static void
scmi_process_response(struct scmi_softc * sc,uint32_t hdr)472 scmi_process_response(struct scmi_softc *sc, uint32_t hdr)
473 {
474 	bool timed_out = false;
475 	struct scmi_req *req;
476 
477 	req = scmi_req_lookup_inflight(sc, hdr);
478 	if (req == NULL) {
479 		device_printf(sc->dev,
480 		    "Unexpected reply with header |%X| - token: 0x%X Drop.\n",
481 		    hdr, SCMI_MSG_TOKEN(hdr));
482 		return;
483 	}
484 
485 	mtx_lock_spin(&req->mtx);
486 	req->done = true;
487 	if (!req->timed_out) {
488 		/*
489 		 * Consider the case in which a polled message is picked
490 		 * by chance on the IRQ path on another CPU: setting poll_done
491 		 * will terminate the other poll loop.
492 		 */
493 		if (!req->msg.polling)
494 			wakeup(req);
495 		else
496 			atomic_store_rel_int(&req->msg.poll_done, 1);
497 	} else {
498 		timed_out = true;
499 	}
500 	mtx_unlock_spin(&req->mtx);
501 
502 	if (timed_out)
503 		device_printf(sc->dev,
504 		    "Late reply for timed-out request - token: 0x%X. Ignore.\n",
505 		    req->token);
506 
507 	/*
508 	 * In case of a late reply to a timed-out transaction this will
509 	 * finally free the pending scmi_req
510 	 */
511 	scmi_req_drop_inflight(sc, req);
512 }
513 
514 void
scmi_rx_irq_callback(device_t dev,void * chan,uint32_t hdr)515 scmi_rx_irq_callback(device_t dev, void *chan, uint32_t hdr)
516 {
517 	struct scmi_softc *sc;
518 
519 	sc = device_get_softc(dev);
520 
521 	if (SCMI_IS_MSG_TYPE_NOTIF(hdr) || SCMI_IS_MSG_TYPE_DRESP(hdr)) {
522 		device_printf(dev, "DRESP/NOTIF unsupported. Drop.\n");
523 		SCMI_CLEAR_CHANNEL(dev, chan);
524 		return;
525 	}
526 
527 	scmi_process_response(sc, hdr);
528 }
529 
530 static int
scmi_wait_for_response(struct scmi_softc * sc,struct scmi_req * req,void ** out)531 scmi_wait_for_response(struct scmi_softc *sc, struct scmi_req *req, void **out)
532 {
533 	int ret;
534 
535 	if (req->msg.polling) {
536 		bool needs_drop;
537 
538 		ret = SCMI_POLL_MSG(sc->dev, &req->msg,
539 		    sc->trs_desc.reply_timo_ms);
540 		/*
541 		 * Drop reference to successfully polled req unless it had
542 		 * already also been processed on the IRQ path.
543 		 * Addresses a possible race-condition between polling and
544 		 * interrupt reception paths.
545 		 */
546 		mtx_lock_spin(&req->mtx);
547 		needs_drop = (ret == 0) && !req->done;
548 		mtx_unlock_spin(&req->mtx);
549 		if (needs_drop)
550 			scmi_req_drop_inflight(sc, req);
551 		if (ret == 0 && req->msg.hdr != req->header) {
552 			device_printf(sc->dev,
553 			    "Malformed reply with header |%08X|. Expected: |%08X|Drop.\n",
554 			    le32toh(req->msg.hdr), le32toh(req->header));
555 		}
556 	} else {
557 		ret = tsleep(req, 0, "scmi_wait4",
558 		    (sc->trs_desc.reply_timo_ms * hz) / 1000);
559 		/* Check for lost wakeups since there is no associated lock */
560 		mtx_lock_spin(&req->mtx);
561 		if (ret != 0 && req->done)
562 			ret = 0;
563 		mtx_unlock_spin(&req->mtx);
564 	}
565 
566 	if (ret == 0) {
567 		SCMI_COLLECT_REPLY(sc->dev, &req->msg);
568 		if (req->msg.payld[0] != 0)
569 			ret = req->msg.payld[0];
570 		*out = &req->msg.payld[SCMI_MSG_HDR_SIZE];
571 	} else {
572 		mtx_lock_spin(&req->mtx);
573 		req->timed_out = true;
574 		mtx_unlock_spin(&req->mtx);
575 		device_printf(sc->dev,
576 		    "Request for token 0x%X timed-out.\n", req->token);
577 	}
578 
579 	SCMI_TX_COMPLETE(sc->dev, NULL);
580 
581 	return (ret);
582 }
583 
584 void *
scmi_buf_get(device_t dev,uint8_t protocol_id,uint8_t message_id,int tx_payld_sz,int rx_payld_sz)585 scmi_buf_get(device_t dev, uint8_t protocol_id, uint8_t message_id,
586     int tx_payld_sz, int rx_payld_sz)
587 {
588 	struct scmi_softc *sc;
589 	struct scmi_req *req;
590 
591 	sc = device_get_softc(dev);
592 
593 	if (tx_payld_sz > SCMI_MAX_MSG_PAYLD_SIZE ||
594 	    rx_payld_sz > SCMI_MAX_MSG_REPLY_SIZE) {
595 		device_printf(dev, "Unsupported payload size. Drop.\n");
596 		return (NULL);
597 	}
598 
599 	/* Pick one from free list */
600 	req = scmi_req_alloc(sc, SCMI_CHAN_A2P);
601 	if (req == NULL)
602 		return (NULL);
603 
604 	req->protocol_id = protocol_id & SCMI_HDR_PROTOCOL_ID_BF;
605 	req->message_id = message_id & SCMI_HDR_MESSAGE_ID_BF;
606 	req->msg.tx_len = sizeof(req->msg.hdr) + tx_payld_sz;
607 	req->msg.rx_len = rx_payld_sz ?
608 	    rx_payld_sz + 2 * sizeof(uint32_t) : SCMI_MAX_MSG_SIZE;
609 
610 	return (&req->msg.payld[0]);
611 }
612 
613 void
scmi_buf_put(device_t dev,void * buf)614 scmi_buf_put(device_t dev, void *buf)
615 {
616 	struct scmi_softc *sc;
617 	struct scmi_req *req;
618 
619 	sc = device_get_softc(dev);
620 
621 	req = buf_to_req(buf);
622 	scmi_req_put(sc, req);
623 }
624 
625 int
scmi_request(device_t dev,void * in,void ** out)626 scmi_request(device_t dev, void *in, void **out)
627 {
628 	struct scmi_softc *sc;
629 	struct scmi_req *req;
630 	int error;
631 
632 	sc = device_get_softc(dev);
633 
634 	req = buf_to_req(in);
635 
636 	req->msg.polling =
637 	    (cold || sc->trs_desc.no_completion_irq || req->use_polling);
638 
639 	/* Set inflight and send using transport specific method - refc-2 */
640 	error = scmi_req_track_inflight(sc, req);
641 	if (error != 0)
642 		return (error);
643 
644 	error = SCMI_XFER_MSG(sc->dev, &req->msg);
645 	if (error != 0) {
646 		scmi_req_drop_inflight(sc, req);
647 		return (error);
648 	}
649 
650 	return (scmi_wait_for_response(sc, req, out));
651 }
652