1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022 Ruslan Bukin <[email protected]>
5 *
6 * This work was supported by Innovate UK project 105694, "Digital Security
7 * by Design (DSbD) Technology Platform Prototype".
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/cpu.h>
36 #include <sys/kernel.h>
37 #include <sys/lock.h>
38 #include <sys/module.h>
39 #include <sys/mutex.h>
40
41 #include <dev/extres/clk/clk.h>
42 #include <dev/fdt/simplebus.h>
43 #include <dev/fdt/fdt_common.h>
44 #include <dev/ofw/ofw_bus_subr.h>
45
46 #include "dev/mailbox/arm/arm_doorbell.h"
47
48 #include "scmi.h"
49 #include "scmi_protocols.h"
50
51 struct scmi_softc {
52 struct simplebus_softc simplebus_sc;
53 device_t dev;
54 device_t tx_shmem;
55 struct arm_doorbell *db;
56 struct mtx mtx;
57 int req_done;
58 };
59
60 static device_t
scmi_get_shmem(struct scmi_softc * sc,int index)61 scmi_get_shmem(struct scmi_softc *sc, int index)
62 {
63 phandle_t *shmems;
64 phandle_t node;
65 device_t dev;
66 size_t len;
67
68 node = ofw_bus_get_node(sc->dev);
69 if (node <= 0)
70 return (NULL);
71
72 len = OF_getencprop_alloc_multi(node, "shmem", sizeof(*shmems),
73 (void **)&shmems);
74 if (len <= 0) {
75 device_printf(sc->dev, "%s: Can't get shmem node.\n", __func__);
76 return (NULL);
77 }
78
79 if (index >= len) {
80 OF_prop_free(shmems);
81 return (NULL);
82 }
83
84 dev = OF_device_from_xref(shmems[index]);
85 if (dev == NULL)
86 device_printf(sc->dev, "%s: Can't get shmem device.\n",
87 __func__);
88
89 OF_prop_free(shmems);
90
91 return (dev);
92 }
93
94 static void
scmi_callback(void * arg)95 scmi_callback(void *arg)
96 {
97 struct scmi_softc *sc;
98
99 sc = arg;
100
101 dprintf("%s sc %p\n", __func__, sc);
102
103 SCMI_LOCK(sc);
104 sc->req_done = 1;
105 wakeup(sc);
106 SCMI_UNLOCK(sc);
107 }
108
109 static int
scmi_request_locked(struct scmi_softc * sc,struct scmi_req * req)110 scmi_request_locked(struct scmi_softc *sc, struct scmi_req *req)
111 {
112 struct scmi_smt_header hdr;
113 int timeout;
114
115 bzero(&hdr, sizeof(struct scmi_smt_header));
116
117 SCMI_ASSERT_LOCKED(sc);
118
119 /* Read header */
120 scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE);
121
122 if ((hdr.channel_status & SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) == 0)
123 return (1);
124
125 /* Update header */
126 hdr.channel_status &= ~SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE;
127 hdr.msg_header = req->protocol_id << SMT_HEADER_PROTOCOL_ID_S;
128 hdr.msg_header |= req->message_id << SMT_HEADER_MESSAGE_ID_S;
129 hdr.length = sizeof(hdr.msg_header) + req->in_size;
130 hdr.flags |= SCMI_SHMEM_FLAG_INTR_ENABLED;
131
132 /* Write header */
133 scmi_shmem_write(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE);
134
135 /* Write request */
136 scmi_shmem_write(sc->tx_shmem, SMT_HEADER_SIZE, req->in_buf,
137 req->in_size);
138
139 sc->req_done = 0;
140
141 /* Interrupt SCP firmware. */
142 arm_doorbell_set(sc->db);
143
144 timeout = 200;
145
146 dprintf("%s: request\n", __func__);
147
148 do {
149 if (cold) {
150 if (arm_doorbell_get(sc->db))
151 break;
152 DELAY(10000);
153 } else {
154 msleep(sc, &sc->mtx, 0, "scmi", hz / 10);
155 if (sc->req_done)
156 break;
157 }
158 } while (timeout--);
159
160 if (timeout <= 0)
161 return (-1);
162
163 dprintf("%s: got reply, timeout %d\n", __func__, timeout);
164
165 /* Read header. */
166 scmi_shmem_read(sc->tx_shmem, 0, &hdr, SMT_HEADER_SIZE);
167
168 /* Read response */
169 scmi_shmem_read(sc->tx_shmem, SMT_HEADER_SIZE, req->out_buf,
170 req->out_size);
171
172 return (0);
173 }
174
175 int
scmi_request(device_t dev,struct scmi_req * req)176 scmi_request(device_t dev, struct scmi_req *req)
177 {
178 struct scmi_softc *sc;
179 int error;
180
181 sc = device_get_softc(dev);
182
183 SCMI_LOCK(sc);
184 error = scmi_request_locked(sc, req);
185 SCMI_UNLOCK(sc);
186
187 return (error);
188 }
189
190 static int
scmi_probe(device_t dev)191 scmi_probe(device_t dev)
192 {
193
194 if (!ofw_bus_is_compatible(dev, "arm,scmi"))
195 return (ENXIO);
196
197 if (!ofw_bus_status_okay(dev))
198 return (ENXIO);
199
200 device_set_desc(dev, "ARM SCMI interface driver");
201
202 return (BUS_PROBE_DEFAULT);
203 }
204
205 static int
scmi_attach(device_t dev)206 scmi_attach(device_t dev)
207 {
208 struct scmi_softc *sc;
209 phandle_t node;
210 int error;
211
212 sc = device_get_softc(dev);
213 sc->dev = dev;
214
215 node = ofw_bus_get_node(dev);
216 if (node == -1)
217 return (ENXIO);
218
219 sc->tx_shmem = scmi_get_shmem(sc, 0);
220 if (sc->tx_shmem == NULL) {
221 device_printf(dev, "TX shmem dev not found.\n");
222 return (ENXIO);
223 }
224
225 sc->db = arm_doorbell_ofw_get(sc->dev, "tx");
226 if (sc->db == NULL) {
227 device_printf(dev, "Doorbell device not found.\n");
228 return (ENXIO);
229 }
230
231 mtx_init(&sc->mtx, device_get_nameunit(dev), "SCMI", MTX_DEF);
232
233 arm_doorbell_set_handler(sc->db, scmi_callback, sc);
234
235 simplebus_init(dev, node);
236
237 /*
238 * Allow devices to identify.
239 */
240 bus_generic_probe(dev);
241
242 /*
243 * Now walk the OFW tree and attach top-level devices.
244 */
245 for (node = OF_child(node); node > 0; node = OF_peer(node))
246 simplebus_add_device(dev, node, 0, NULL, -1, NULL);
247
248 error = bus_generic_attach(dev);
249
250 return (error);
251 }
252
253 static int
scmi_detach(device_t dev)254 scmi_detach(device_t dev)
255 {
256
257 return (0);
258 }
259
260 static device_method_t scmi_methods[] = {
261 DEVMETHOD(device_probe, scmi_probe),
262 DEVMETHOD(device_attach, scmi_attach),
263 DEVMETHOD(device_detach, scmi_detach),
264 DEVMETHOD_END
265 };
266
267 DEFINE_CLASS_1(scmi, scmi_driver, scmi_methods, sizeof(struct scmi_softc),
268 simplebus_driver);
269
270 DRIVER_MODULE(scmi, simplebus, scmi_driver, 0, 0);
271 MODULE_VERSION(scmi, 1);
272