Commit | Line | Data |
---|---|---|
5c8a47a5 VK |
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 channel | |
23 | * @cinfo: SCMI channel info | |
24 | * @shmem: Transmit/Receive shared memory area | |
25 | */ | |
26 | struct scmi_mailbox { | |
27 | struct mbox_client cl; | |
28 | struct mbox_chan *chan; | |
29 | struct scmi_chan_info *cinfo; | |
30 | struct scmi_shared_mem __iomem *shmem; | |
31 | }; | |
32 | ||
33 | #define client_to_scmi_mailbox(c) container_of(c, struct scmi_mailbox, cl) | |
34 | ||
35 | static void tx_prepare(struct mbox_client *cl, void *m) | |
36 | { | |
37 | struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); | |
38 | ||
39 | shmem_tx_prepare(smbox->shmem, m); | |
40 | } | |
41 | ||
42 | static void rx_callback(struct mbox_client *cl, void *m) | |
43 | { | |
44 | struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl); | |
45 | ||
13fba878 | 46 | scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem), NULL); |
5c8a47a5 VK |
47 | } |
48 | ||
49 | static bool mailbox_chan_available(struct device *dev, int idx) | |
50 | { | |
51 | return !of_parse_phandle_with_args(dev->of_node, "mboxes", | |
52 | "#mbox-cells", idx, NULL); | |
53 | } | |
54 | ||
55 | static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, | |
56 | bool tx) | |
57 | { | |
58 | const char *desc = tx ? "Tx" : "Rx"; | |
59 | struct device *cdev = cinfo->dev; | |
60 | struct scmi_mailbox *smbox; | |
61 | struct device_node *shmem; | |
62 | int ret, idx = tx ? 0 : 1; | |
63 | struct mbox_client *cl; | |
64 | resource_size_t size; | |
65 | struct resource res; | |
66 | ||
67 | smbox = devm_kzalloc(dev, sizeof(*smbox), GFP_KERNEL); | |
68 | if (!smbox) | |
69 | return -ENOMEM; | |
70 | ||
71 | shmem = of_parse_phandle(cdev->of_node, "shmem", idx); | |
507cd4d2 SH |
72 | if (!of_device_is_compatible(shmem, "arm,scmi-shmem")) |
73 | return -ENXIO; | |
74 | ||
5c8a47a5 VK |
75 | ret = of_address_to_resource(shmem, 0, &res); |
76 | of_node_put(shmem); | |
77 | if (ret) { | |
78 | dev_err(cdev, "failed to get SCMI %s shared memory\n", desc); | |
79 | return ret; | |
80 | } | |
81 | ||
82 | size = resource_size(&res); | |
83 | smbox->shmem = devm_ioremap(dev, res.start, size); | |
84 | if (!smbox->shmem) { | |
85 | dev_err(dev, "failed to ioremap SCMI %s shared memory\n", desc); | |
86 | return -EADDRNOTAVAIL; | |
87 | } | |
88 | ||
89 | cl = &smbox->cl; | |
90 | cl->dev = cdev; | |
91 | cl->tx_prepare = tx ? tx_prepare : NULL; | |
92 | cl->rx_callback = rx_callback; | |
93 | cl->tx_block = false; | |
94 | cl->knows_txdone = tx; | |
95 | ||
96 | smbox->chan = mbox_request_channel(cl, tx ? 0 : 1); | |
97 | if (IS_ERR(smbox->chan)) { | |
98 | ret = PTR_ERR(smbox->chan); | |
99 | if (ret != -EPROBE_DEFER) | |
100 | dev_err(cdev, "failed to request SCMI %s mailbox\n", | |
101 | tx ? "Tx" : "Rx"); | |
102 | return ret; | |
103 | } | |
104 | ||
105 | cinfo->transport_info = smbox; | |
106 | smbox->cinfo = cinfo; | |
107 | ||
108 | return 0; | |
109 | } | |
110 | ||
111 | static int mailbox_chan_free(int id, void *p, void *data) | |
112 | { | |
113 | struct scmi_chan_info *cinfo = p; | |
114 | struct scmi_mailbox *smbox = cinfo->transport_info; | |
115 | ||
6ed6c558 | 116 | if (smbox && !IS_ERR(smbox->chan)) { |
5c8a47a5 VK |
117 | mbox_free_channel(smbox->chan); |
118 | cinfo->transport_info = NULL; | |
119 | smbox->chan = NULL; | |
120 | smbox->cinfo = NULL; | |
121 | } | |
122 | ||
123 | scmi_free_channel(cinfo, data, id); | |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | static int mailbox_send_message(struct scmi_chan_info *cinfo, | |
129 | struct scmi_xfer *xfer) | |
130 | { | |
131 | struct scmi_mailbox *smbox = cinfo->transport_info; | |
132 | int ret; | |
133 | ||
134 | ret = mbox_send_message(smbox->chan, xfer); | |
135 | ||
136 | /* mbox_send_message returns non-negative value on success, so reset */ | |
137 | if (ret > 0) | |
138 | ret = 0; | |
139 | ||
140 | return ret; | |
141 | } | |
142 | ||
94d0cd1d CM |
143 | static void mailbox_mark_txdone(struct scmi_chan_info *cinfo, int ret, |
144 | struct scmi_xfer *__unused) | |
5c8a47a5 VK |
145 | { |
146 | struct scmi_mailbox *smbox = cinfo->transport_info; | |
147 | ||
148 | /* | |
149 | * NOTE: we might prefer not to need the mailbox ticker to manage the | |
150 | * transfer queueing since the protocol layer queues things by itself. | |
151 | * Unfortunately, we have to kick the mailbox framework after we have | |
152 | * received our message. | |
153 | */ | |
154 | mbox_client_txdone(smbox->chan, ret); | |
155 | } | |
156 | ||
157 | static void mailbox_fetch_response(struct scmi_chan_info *cinfo, | |
158 | struct scmi_xfer *xfer) | |
159 | { | |
160 | struct scmi_mailbox *smbox = cinfo->transport_info; | |
161 | ||
162 | shmem_fetch_response(smbox->shmem, xfer); | |
163 | } | |
164 | ||
d5141f37 CM |
165 | static void mailbox_fetch_notification(struct scmi_chan_info *cinfo, |
166 | size_t max_len, struct scmi_xfer *xfer) | |
167 | { | |
168 | struct scmi_mailbox *smbox = cinfo->transport_info; | |
169 | ||
170 | shmem_fetch_notification(smbox->shmem, max_len, xfer); | |
171 | } | |
172 | ||
87dff4e6 | 173 | static void mailbox_clear_channel(struct scmi_chan_info *cinfo) |
d5141f37 CM |
174 | { |
175 | struct scmi_mailbox *smbox = cinfo->transport_info; | |
176 | ||
87dff4e6 | 177 | shmem_clear_channel(smbox->shmem); |
d5141f37 CM |
178 | } |
179 | ||
5c8a47a5 VK |
180 | static bool |
181 | mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) | |
182 | { | |
183 | struct scmi_mailbox *smbox = cinfo->transport_info; | |
184 | ||
185 | return shmem_poll_done(smbox->shmem, xfer); | |
186 | } | |
187 | ||
3de7b830 | 188 | static const struct scmi_transport_ops scmi_mailbox_ops = { |
5c8a47a5 VK |
189 | .chan_available = mailbox_chan_available, |
190 | .chan_setup = mailbox_chan_setup, | |
191 | .chan_free = mailbox_chan_free, | |
192 | .send_message = mailbox_send_message, | |
193 | .mark_txdone = mailbox_mark_txdone, | |
194 | .fetch_response = mailbox_fetch_response, | |
d5141f37 | 195 | .fetch_notification = mailbox_fetch_notification, |
87dff4e6 | 196 | .clear_channel = mailbox_clear_channel, |
5c8a47a5 VK |
197 | .poll_done = mailbox_poll_done, |
198 | }; | |
199 | ||
200 | const struct scmi_desc scmi_mailbox_desc = { | |
201 | .ops = &scmi_mailbox_ops, | |
202 | .max_rx_timeout_ms = 30, /* We may increase this if required */ | |
203 | .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ | |
204 | .max_msg_size = 128, | |
205 | }; |