1 /*
2  * Copyright 2024 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #ifndef _ZEPHYR_DRIVERS_FIRMWARE_SCMI_MAILBOX_H_
8 #define _ZEPHYR_DRIVERS_FIRMWARE_SCMI_MAILBOX_H_
9 
10 #include <zephyr/drivers/firmware/scmi/transport.h>
11 #include <zephyr/drivers/firmware/scmi/util.h>
12 #include <zephyr/drivers/firmware/scmi/shmem.h>
13 #include <zephyr/drivers/mbox.h>
14 #include <zephyr/kernel.h>
15 
16 #define DT_DRV_COMPAT arm_scmi
17 
18 /* get a `struct device` for a protocol's shared memory area */
19 #define _SCMI_MBOX_SHMEM_BY_IDX(node_id, idx)					\
20 	COND_CODE_1(DT_PROP_HAS_IDX(node_id, shmem, idx),			\
21 		    (DEVICE_DT_GET(DT_PROP_BY_IDX(node_id, shmem, idx))),	\
22 		    (NULL))
23 
24 /* get the name of mailbox channel's private data */
25 #define _SCMI_MBOX_CHAN_NAME(proto, idx)\
26 	CONCAT(SCMI_TRANSPORT_CHAN_NAME(proto, idx), _, priv)
27 
28 /* fetch a mailbox channel's doorbell */
29 #define _SCMI_MBOX_CHAN_DBELL(node_id, name)			\
30 	COND_CODE_1(DT_PROP_HAS_NAME(node_id, mboxes, name),	\
31 		    (MBOX_DT_SPEC_GET(node_id, name)),		\
32 		    ({ }))
33 
34 /* define private data for a protocol TX channel */
35 #define _SCMI_MBOX_CHAN_DEFINE_PRIV_TX(node_id, proto)			\
36 	static struct scmi_mbox_channel _SCMI_MBOX_CHAN_NAME(proto, 0) =\
37 	{								\
38 		.shmem = _SCMI_MBOX_SHMEM_BY_IDX(node_id, 0),		\
39 		.tx = _SCMI_MBOX_CHAN_DBELL(node_id, tx),		\
40 		.tx_reply = _SCMI_MBOX_CHAN_DBELL(node_id, tx_reply),	\
41 	}
42 
43 /*
44  * Define a mailbox channel. This does two things:
45  *	1) Define the mandatory `struct scmi_channel` structure
46  *	2) Define the mailbox-specific private data for said
47  *	channel (i.e: a struct scmi_mbox_channel)
48  */
49 #define _SCMI_MBOX_CHAN_DEFINE(node_id, proto, idx)				\
50 	_SCMI_MBOX_CHAN_DEFINE_PRIV_TX(node_id, proto);				\
51 	DT_SCMI_TRANSPORT_CHAN_DEFINE(node_id, idx, proto,			\
52 				    &(_SCMI_MBOX_CHAN_NAME(proto, idx)));	\
53 
54 /*
55  * Optionally define a mailbox channel for a protocol. This is optional
56  * because a protocol might not have a dedicated channel.
57  */
58 #define _SCMI_MBOX_CHAN_DEFINE_OPTIONAL(node_id, proto, idx)		\
59 	COND_CODE_1(DT_PROP_HAS_IDX(node_id, shmem, idx),		\
60 		    (_SCMI_MBOX_CHAN_DEFINE(node_id, proto, idx)),	\
61 		    ())
62 
63 /* define a TX channel for a protocol node. This is preferred over
64  * _SCMI_MBOX_CHAN_DEFINE_OPTIONAL() since support for RX channels
65  * might be added later on. This macro is supposed to also define
66  * the RX channel
67  */
68 #define SCMI_MBOX_PROTO_CHAN_DEFINE(node_id)\
69 	_SCMI_MBOX_CHAN_DEFINE_OPTIONAL(node_id, DT_REG_ADDR(node_id), 0)
70 
71 /* define and validate base protocol TX channel */
72 #define DT_INST_SCMI_MBOX_BASE_CHAN_DEFINE(inst)			\
73 	BUILD_ASSERT(DT_INST_PROP_LEN(inst, mboxes) != 1 ||		\
74 		     (DT_INST_PROP_HAS_IDX(inst, shmem, 0) &&		\
75 		     DT_INST_PROP_HAS_NAME(inst, mboxes, tx)),		\
76 		     "bad bidirectional channel description");		\
77 									\
78 	BUILD_ASSERT(DT_INST_PROP_LEN(inst, mboxes) != 2 ||		\
79 		     (DT_INST_PROP_HAS_NAME(inst, mboxes, tx) &&	\
80 		     DT_INST_PROP_HAS_NAME(inst, mboxes, tx_reply)),	\
81 		     "bad unidirectional channel description");		\
82 									\
83 	BUILD_ASSERT(DT_INST_PROP_LEN(inst, shmem) == 1,		\
84 		     "bad SHMEM count");				\
85 									\
86 	BUILD_ASSERT(DT_INST_PROP_LEN(inst, mboxes) <= 2,		\
87 		     "bad mbox count");					\
88 									\
89 	_SCMI_MBOX_CHAN_DEFINE(DT_INST(inst, DT_DRV_COMPAT), SCMI_PROTOCOL_BASE, 0)
90 
91 /*
92  * Define the mailbox-based transport layer. What this does is:
93  *
94  *	1) Goes through all protocol nodes (children of the `scmi` node)
95  *	and creates a `struct scmi_channel` and its associated
96  *	`struct scmi_mbox_channel` if the protocol has a dedicated channel.
97  *
98  *	2) Creates aforementioned structures for the base protocol
99  *	(identified by the `scmi` node)
100  *
101  *	3) "registers" the driver via `DT_INST_SCMI_TRANSPORT_DEFINE()`.
102  */
103 #define DT_INST_SCMI_MAILBOX_DEFINE(inst, level, prio, api)			\
104 	DT_INST_FOREACH_CHILD_STATUS_OKAY(inst, SCMI_MBOX_PROTO_CHAN_DEFINE)	\
105 	DT_INST_SCMI_MBOX_BASE_CHAN_DEFINE(inst)				\
106 	DT_INST_SCMI_TRANSPORT_DEFINE(inst, NULL, NULL, NULL, level, prio, api)
107 
108 struct scmi_mbox_channel {
109 	/* SHMEM area bound to the channel */
110 	const struct device *shmem;
111 	/* TX dbell */
112 	struct mbox_dt_spec tx;
113 	/* TX reply dbell */
114 	struct mbox_dt_spec tx_reply;
115 };
116 
117 #endif /* _ZEPHYR_DRIVERS_FIRMWARE_SCMI_MAILBOX_H_ */
118