1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * System Control and Management Interface (SCMI) Message Mailbox Transport |
4 | * driver. |
5 | * |
6 | * Copyright (C) 2019 ARM Ltd. |
7 | */ |
8 | |
9 | #include <linux/err.h> |
10 | #include <linux/device.h> |
11 | #include <linux/mailbox_client.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/slab.h> |
15 | |
16 | #include "common.h" |
17 | |
18 | /** |
19 | * struct scmi_mailbox - Structure representing a SCMI mailbox transport |
20 | * |
21 | * @cl: Mailbox Client |
22 | * @chan: Transmit/Receive mailbox uni/bi-directional channel |
23 | * @chan_receiver: Optional Receiver mailbox unidirectional channel |
24 | * @cinfo: SCMI channel info |
25 | * @shmem: Transmit/Receive shared memory area |
26 | */ |
27 | struct scmi_mailbox { |
28 | struct mbox_client cl; |
29 | struct mbox_chan *chan; |
30 | struct mbox_chan *chan_receiver; |
31 | struct scmi_chan_info *cinfo; |
32 | struct scmi_shared_mem __iomem *shmem; |
33 | }; |
34 | |
35 | #define client_to_scmi_mailbox(c) container_of(c, struct scmi_mailbox, cl) |
36 | |
37 | static void tx_prepare(struct mbox_client *cl, void *m) |
38 | { |
39 | struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); |
40 | |
41 | shmem_tx_prepare(shmem: smbox->shmem, xfer: m, cinfo: smbox->cinfo); |
42 | } |
43 | |
44 | static void rx_callback(struct mbox_client *cl, void *m) |
45 | { |
46 | struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); |
47 | |
48 | /* |
49 | * An A2P IRQ is NOT valid when received while the platform still has |
50 | * the ownership of the channel, because the platform at first releases |
51 | * the SMT channel and then sends the completion interrupt. |
52 | * |
53 | * This addresses a possible race condition in which a spurious IRQ from |
54 | * a previous timed-out reply which arrived late could be wrongly |
55 | * associated with the next pending transaction. |
56 | */ |
57 | if (cl->knows_txdone && !shmem_channel_free(shmem: smbox->shmem)) { |
58 | dev_warn(smbox->cinfo->dev, "Ignoring spurious A2P IRQ !\n" ); |
59 | return; |
60 | } |
61 | |
62 | scmi_rx_callback(cinfo: smbox->cinfo, msg_hdr: shmem_read_header(shmem: smbox->shmem), NULL); |
63 | } |
64 | |
65 | static bool mailbox_chan_available(struct device_node *of_node, int idx) |
66 | { |
67 | int num_mb; |
68 | |
69 | /* |
70 | * Just check if bidirrectional channels are involved, and check the |
71 | * index accordingly; proper full validation will be made later |
72 | * in mailbox_chan_setup(). |
73 | */ |
74 | num_mb = of_count_phandle_with_args(np: of_node, list_name: "mboxes" , cells_name: "#mbox-cells" ); |
75 | if (num_mb == 3 && idx == 1) |
76 | idx = 2; |
77 | |
78 | return !of_parse_phandle_with_args(np: of_node, list_name: "mboxes" , |
79 | cells_name: "#mbox-cells" , index: idx, NULL); |
80 | } |
81 | |
82 | /** |
83 | * mailbox_chan_validate - Validate transport configuration and map channels |
84 | * |
85 | * @cdev: Reference to the underlying transport device carrying the |
86 | * of_node descriptor to analyze. |
87 | * @a2p_rx_chan: A reference to an optional unidirectional channel to use |
88 | * for replies on the a2p channel. Set as zero if not present. |
89 | * @p2a_chan: A reference to the optional p2a channel. |
90 | * Set as zero if not present. |
91 | * |
92 | * At first, validate the transport configuration as described in terms of |
93 | * 'mboxes' and 'shmem', then determin which mailbox channel indexes are |
94 | * appropriate to be use in the current configuration. |
95 | * |
96 | * Return: 0 on Success or error |
97 | */ |
98 | static int mailbox_chan_validate(struct device *cdev, |
99 | int *a2p_rx_chan, int *p2a_chan) |
100 | { |
101 | int num_mb, num_sh, ret = 0; |
102 | struct device_node *np = cdev->of_node; |
103 | |
104 | num_mb = of_count_phandle_with_args(np, list_name: "mboxes" , cells_name: "#mbox-cells" ); |
105 | num_sh = of_count_phandle_with_args(np, list_name: "shmem" , NULL); |
106 | dev_dbg(cdev, "Found %d mboxes and %d shmems !\n" , num_mb, num_sh); |
107 | |
108 | /* Bail out if mboxes and shmem descriptors are inconsistent */ |
109 | if (num_mb <= 0 || num_sh <= 0 || num_sh > 2 || num_mb > 3 || |
110 | (num_mb == 1 && num_sh != 1) || (num_mb == 3 && num_sh != 2)) { |
111 | dev_warn(cdev, |
112 | "Invalid channel descriptor for '%s' - mbs:%d shm:%d\n" , |
113 | of_node_full_name(np), num_mb, num_sh); |
114 | return -EINVAL; |
115 | } |
116 | |
117 | /* Bail out if provided shmem descriptors do not refer distinct areas */ |
118 | if (num_sh > 1) { |
119 | struct device_node *np_tx, *np_rx; |
120 | |
121 | np_tx = of_parse_phandle(np, phandle_name: "shmem" , index: 0); |
122 | np_rx = of_parse_phandle(np, phandle_name: "shmem" , index: 1); |
123 | if (!np_tx || !np_rx || np_tx == np_rx) { |
124 | dev_warn(cdev, "Invalid shmem descriptor for '%s'\n" , |
125 | of_node_full_name(np)); |
126 | ret = -EINVAL; |
127 | } |
128 | |
129 | of_node_put(node: np_tx); |
130 | of_node_put(node: np_rx); |
131 | } |
132 | |
133 | /* Calculate channels IDs to use depending on mboxes/shmem layout */ |
134 | if (!ret) { |
135 | switch (num_mb) { |
136 | case 1: |
137 | *a2p_rx_chan = 0; |
138 | *p2a_chan = 0; |
139 | break; |
140 | case 2: |
141 | if (num_sh == 2) { |
142 | *a2p_rx_chan = 0; |
143 | *p2a_chan = 1; |
144 | } else { |
145 | *a2p_rx_chan = 1; |
146 | *p2a_chan = 0; |
147 | } |
148 | break; |
149 | case 3: |
150 | *a2p_rx_chan = 1; |
151 | *p2a_chan = 2; |
152 | break; |
153 | } |
154 | } |
155 | |
156 | return ret; |
157 | } |
158 | |
159 | static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, |
160 | bool tx) |
161 | { |
162 | const char *desc = tx ? "Tx" : "Rx" ; |
163 | struct device *cdev = cinfo->dev; |
164 | struct scmi_mailbox *smbox; |
165 | struct device_node *shmem; |
166 | int ret, a2p_rx_chan, p2a_chan, idx = tx ? 0 : 1; |
167 | struct mbox_client *cl; |
168 | resource_size_t size; |
169 | struct resource res; |
170 | |
171 | ret = mailbox_chan_validate(cdev, a2p_rx_chan: &a2p_rx_chan, p2a_chan: &p2a_chan); |
172 | if (ret) |
173 | return ret; |
174 | |
175 | if (!tx && !p2a_chan) |
176 | return -ENODEV; |
177 | |
178 | smbox = devm_kzalloc(dev, size: sizeof(*smbox), GFP_KERNEL); |
179 | if (!smbox) |
180 | return -ENOMEM; |
181 | |
182 | shmem = of_parse_phandle(np: cdev->of_node, phandle_name: "shmem" , index: idx); |
183 | if (!of_device_is_compatible(device: shmem, "arm,scmi-shmem" )) { |
184 | of_node_put(node: shmem); |
185 | return -ENXIO; |
186 | } |
187 | |
188 | ret = of_address_to_resource(dev: shmem, index: 0, r: &res); |
189 | of_node_put(node: shmem); |
190 | if (ret) { |
191 | dev_err(cdev, "failed to get SCMI %s shared memory\n" , desc); |
192 | return ret; |
193 | } |
194 | |
195 | size = resource_size(res: &res); |
196 | smbox->shmem = devm_ioremap(dev, offset: res.start, size); |
197 | if (!smbox->shmem) { |
198 | dev_err(dev, "failed to ioremap SCMI %s shared memory\n" , desc); |
199 | return -EADDRNOTAVAIL; |
200 | } |
201 | |
202 | cl = &smbox->cl; |
203 | cl->dev = cdev; |
204 | cl->tx_prepare = tx ? tx_prepare : NULL; |
205 | cl->rx_callback = rx_callback; |
206 | cl->tx_block = false; |
207 | cl->knows_txdone = tx; |
208 | |
209 | smbox->chan = mbox_request_channel(cl, index: tx ? 0 : p2a_chan); |
210 | if (IS_ERR(ptr: smbox->chan)) { |
211 | ret = PTR_ERR(ptr: smbox->chan); |
212 | if (ret != -EPROBE_DEFER) |
213 | dev_err(cdev, |
214 | "failed to request SCMI %s mailbox\n" , desc); |
215 | return ret; |
216 | } |
217 | |
218 | /* Additional unidirectional channel for TX if needed */ |
219 | if (tx && a2p_rx_chan) { |
220 | smbox->chan_receiver = mbox_request_channel(cl, index: a2p_rx_chan); |
221 | if (IS_ERR(ptr: smbox->chan_receiver)) { |
222 | ret = PTR_ERR(ptr: smbox->chan_receiver); |
223 | if (ret != -EPROBE_DEFER) |
224 | dev_err(cdev, "failed to request SCMI Tx Receiver mailbox\n" ); |
225 | return ret; |
226 | } |
227 | } |
228 | |
229 | cinfo->transport_info = smbox; |
230 | smbox->cinfo = cinfo; |
231 | |
232 | return 0; |
233 | } |
234 | |
235 | static int mailbox_chan_free(int id, void *p, void *data) |
236 | { |
237 | struct scmi_chan_info *cinfo = p; |
238 | struct scmi_mailbox *smbox = cinfo->transport_info; |
239 | |
240 | if (smbox && !IS_ERR(ptr: smbox->chan)) { |
241 | mbox_free_channel(chan: smbox->chan); |
242 | mbox_free_channel(chan: smbox->chan_receiver); |
243 | cinfo->transport_info = NULL; |
244 | smbox->chan = NULL; |
245 | smbox->chan_receiver = NULL; |
246 | smbox->cinfo = NULL; |
247 | } |
248 | |
249 | return 0; |
250 | } |
251 | |
252 | static int mailbox_send_message(struct scmi_chan_info *cinfo, |
253 | struct scmi_xfer *xfer) |
254 | { |
255 | struct scmi_mailbox *smbox = cinfo->transport_info; |
256 | int ret; |
257 | |
258 | ret = mbox_send_message(chan: smbox->chan, mssg: xfer); |
259 | |
260 | /* mbox_send_message returns non-negative value on success, so reset */ |
261 | if (ret > 0) |
262 | ret = 0; |
263 | |
264 | return ret; |
265 | } |
266 | |
267 | static void mailbox_mark_txdone(struct scmi_chan_info *cinfo, int ret, |
268 | struct scmi_xfer *__unused) |
269 | { |
270 | struct scmi_mailbox *smbox = cinfo->transport_info; |
271 | |
272 | /* |
273 | * NOTE: we might prefer not to need the mailbox ticker to manage the |
274 | * transfer queueing since the protocol layer queues things by itself. |
275 | * Unfortunately, we have to kick the mailbox framework after we have |
276 | * received our message. |
277 | */ |
278 | mbox_client_txdone(chan: smbox->chan, r: ret); |
279 | } |
280 | |
281 | static void mailbox_fetch_response(struct scmi_chan_info *cinfo, |
282 | struct scmi_xfer *xfer) |
283 | { |
284 | struct scmi_mailbox *smbox = cinfo->transport_info; |
285 | |
286 | shmem_fetch_response(shmem: smbox->shmem, xfer); |
287 | } |
288 | |
289 | static void mailbox_fetch_notification(struct scmi_chan_info *cinfo, |
290 | size_t max_len, struct scmi_xfer *xfer) |
291 | { |
292 | struct scmi_mailbox *smbox = cinfo->transport_info; |
293 | |
294 | shmem_fetch_notification(shmem: smbox->shmem, max_len, xfer); |
295 | } |
296 | |
297 | static void mailbox_clear_channel(struct scmi_chan_info *cinfo) |
298 | { |
299 | struct scmi_mailbox *smbox = cinfo->transport_info; |
300 | |
301 | shmem_clear_channel(shmem: smbox->shmem); |
302 | } |
303 | |
304 | static bool |
305 | mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) |
306 | { |
307 | struct scmi_mailbox *smbox = cinfo->transport_info; |
308 | |
309 | return shmem_poll_done(shmem: smbox->shmem, xfer); |
310 | } |
311 | |
312 | static const struct scmi_transport_ops scmi_mailbox_ops = { |
313 | .chan_available = mailbox_chan_available, |
314 | .chan_setup = mailbox_chan_setup, |
315 | .chan_free = mailbox_chan_free, |
316 | .send_message = mailbox_send_message, |
317 | .mark_txdone = mailbox_mark_txdone, |
318 | .fetch_response = mailbox_fetch_response, |
319 | .fetch_notification = mailbox_fetch_notification, |
320 | .clear_channel = mailbox_clear_channel, |
321 | .poll_done = mailbox_poll_done, |
322 | }; |
323 | |
324 | const struct scmi_desc scmi_mailbox_desc = { |
325 | .ops = &scmi_mailbox_ops, |
326 | .max_rx_timeout_ms = 30, /* We may increase this if required */ |
327 | .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ |
328 | .max_msg_size = 128, |
329 | }; |
330 | |