1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/malloc.h>
35 #include <sys/bus.h>
36 #include <sys/cpu.h>
37 #include <machine/bus.h>
38
39 #include <dev/fdt/simplebus.h>
40
41 #include <dev/ofw/openfirm.h>
42 #include <dev/ofw/ofw_bus.h>
43 #include <dev/ofw/ofw_bus_subr.h>
44
45 #include <dev/psci/smccc.h>
46
47 #include <dev/firmware/xilinx/pm_defs.h>
48
49 #include "zynqmp_firmware_if.h"
50
51 enum {
52 IOCTL_GET_RPU_OPER_MODE = 0,
53 IOCTL_SET_RPU_OPER_MODE = 1,
54 IOCTL_RPU_BOOT_ADDR_CONFIG = 2,
55 IOCTL_TCM_COMB_CONFIG = 3,
56 IOCTL_SET_TAPDELAY_BYPASS = 4,
57 IOCTL_SET_SGMII_MODE = 5,
58 IOCTL_SD_DLL_RESET = 6,
59 IOCTL_SET_SD_TAPDELAY = 7,
60 /* Ioctl for clock driver */
61 IOCTL_SET_PLL_FRAC_MODE = 8,
62 IOCTL_GET_PLL_FRAC_MODE = 9,
63 IOCTL_SET_PLL_FRAC_DATA = 10,
64 IOCTL_GET_PLL_FRAC_DATA = 11,
65 IOCTL_WRITE_GGS = 12,
66 IOCTL_READ_GGS = 13,
67 IOCTL_WRITE_PGGS = 14,
68 IOCTL_READ_PGGS = 15,
69 /* IOCTL for ULPI reset */
70 IOCTL_ULPI_RESET = 16,
71 /* Set healthy bit value */
72 IOCTL_SET_BOOT_HEALTH_STATUS = 17,
73 IOCTL_AFI = 18,
74 /* Probe counter read/write */
75 IOCTL_PROBE_COUNTER_READ = 19,
76 IOCTL_PROBE_COUNTER_WRITE = 20,
77 IOCTL_OSPI_MUX_SELECT = 21,
78 /* IOCTL for USB power request */
79 IOCTL_USB_SET_STATE = 22,
80 /* IOCTL to get last reset reason */
81 IOCTL_GET_LAST_RESET_REASON = 23,
82 /* AI engine NPI ISR clear */
83 IOCTL_AIE_ISR_CLEAR = 24,
84 /* Register SGI to ATF */
85 IOCTL_REGISTER_SGI = 25,
86 };
87
88 typedef int (*zynqmp_callfn_t)(register_t, register_t, register_t, uint32_t *payload);
89
90 struct zynqmp_firmware_softc {
91 struct simplebus_softc sc;
92 device_t dev;
93 zynqmp_callfn_t callfn;
94 };
95
96 /* SMC calling methods */
97 #define PM_SIP_SVC 0xC2000000
98
99 static int
zynqmp_call_smc(uint32_t id,uint32_t a0,uint32_t a1,uint32_t a2,uint32_t a3,uint32_t * payload,bool ignore_error)100 zynqmp_call_smc(uint32_t id, uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t *payload, bool ignore_error)
101 {
102 struct arm_smccc_res res;
103 uint64_t args[3];
104
105 args[0] = id | PM_SIP_SVC;
106 args[1] = ((uint64_t)a1 << 32) | a0;
107 args[2] = ((uint64_t)a3 << 32) | a2;
108 arm_smccc_invoke_smc(args[0], args[1], args[2], &res);
109 if (payload != NULL) {
110 payload[0] = res.a0 & 0xFFFFFFFF;
111 payload[1] = res.a0 >> 32;
112 payload[2] = res.a1 & 0xFFFFFFFF;
113 payload[3] = res.a1 >> 32;
114 if (!ignore_error && payload[0] != PM_RET_SUCCESS) {
115 printf("%s: fail %x\n", __func__, payload[0]);
116 return (EINVAL);
117 }
118 }
119 return (0);
120 }
121
122 /* Firmware methods */
123 static int
zynqmp_get_api_version(struct zynqmp_firmware_softc * sc)124 zynqmp_get_api_version(struct zynqmp_firmware_softc *sc)
125 {
126 uint32_t payload[4];
127 int rv;
128
129 rv = zynqmp_call_smc(PM_GET_API_VERSION, 0, 0, 0, 0, payload, false);
130 if (rv != 0) {
131 device_printf(sc->dev, "SMC Call fail %d\n", rv);
132 goto out;
133 }
134 device_printf(sc->dev, "API version = %d.%d\n",
135 payload[1] >> 16, payload[1] & 0xFFFF);
136 out:
137 return (rv);
138 }
139
140 static int
zynqmp_get_chipid(struct zynqmp_firmware_softc * sc)141 zynqmp_get_chipid(struct zynqmp_firmware_softc *sc)
142 {
143 uint32_t payload[4];
144 int rv;
145
146 rv = zynqmp_call_smc(PM_GET_CHIPID, 0, 0, 0, 0, payload, false);
147 if (rv != 0) {
148 device_printf(sc->dev, "SMC Call fail %d\n", rv);
149 goto out;
150 }
151 device_printf(sc->dev, "ID Code = %x Version = %x\n",
152 payload[1], payload[2]);
153 out:
154 return (rv);
155 }
156
157 static int
zynqmp_get_trustzone_version(struct zynqmp_firmware_softc * sc)158 zynqmp_get_trustzone_version(struct zynqmp_firmware_softc *sc)
159 {
160 uint32_t payload[4];
161 int rv;
162
163 rv = zynqmp_call_smc(PM_GET_TRUSTZONE_VERSION, 0, 0, 0, 0, payload, false);
164 if (rv != 0) {
165 device_printf(sc->dev, "SMC Call fail %d\n", rv);
166 goto out;
167 }
168 device_printf(sc->dev, "Trustzone Version = %x\n",
169 payload[1]);
170 out:
171 return (rv);
172 }
173
174 /* zynqmp_firmware methods */
175 static int
zynqmp_firmware_clock_enable(device_t dev,uint32_t clkid)176 zynqmp_firmware_clock_enable(device_t dev, uint32_t clkid)
177 {
178 struct zynqmp_firmware_softc *sc;
179 uint32_t payload[4];
180 int rv;
181
182 sc = device_get_softc(dev);
183 rv = zynqmp_call_smc(PM_CLOCK_ENABLE, clkid, 0, 0, 0, payload, false);
184 if (rv != 0)
185 device_printf(sc->dev, "SMC Call fail %d\n", rv);
186 return (rv);
187 }
188
189 static int
zynqmp_firmware_clock_disable(device_t dev,uint32_t clkid)190 zynqmp_firmware_clock_disable(device_t dev, uint32_t clkid)
191 {
192 struct zynqmp_firmware_softc *sc;
193 uint32_t payload[4];
194 int rv;
195
196 sc = device_get_softc(dev);
197 rv = zynqmp_call_smc(PM_CLOCK_DISABLE, clkid, 0, 0, 0, payload, false);
198 if (rv != 0)
199 device_printf(sc->dev, "SMC Call fail %d\n", rv);
200 return (rv);
201 }
202
203 static int
zynqmp_firmware_clock_getstate(device_t dev,uint32_t clkid,bool * enabled)204 zynqmp_firmware_clock_getstate(device_t dev, uint32_t clkid, bool *enabled)
205 {
206 struct zynqmp_firmware_softc *sc;
207 uint32_t payload[4];
208 int rv;
209
210 sc = device_get_softc(dev);
211 rv = zynqmp_call_smc(PM_CLOCK_GETSTATE, clkid, 0, 0, 0, payload, false);
212 if (rv != 0) {
213 device_printf(sc->dev, "SMC Call fail %d\n", rv);
214 goto out;
215 }
216 *enabled = payload[1] == 1 ? true : false;
217
218 out:
219 return (rv);
220 }
221
222 static int
zynqmp_firmware_clock_setdivider(device_t dev,uint32_t clkid,uint32_t div)223 zynqmp_firmware_clock_setdivider(device_t dev, uint32_t clkid, uint32_t div)
224 {
225 struct zynqmp_firmware_softc *sc;
226 uint32_t payload[4];
227 int rv;
228
229 sc = device_get_softc(dev);
230 rv = zynqmp_call_smc(PM_CLOCK_SETDIVIDER, clkid, div, 0, 0, payload, false);
231 if (rv != 0)
232 device_printf(sc->dev, "SMC Call fail %d\n", rv);
233 return (rv);
234 }
235
236 static int
zynqmp_firmware_clock_getdivider(device_t dev,uint32_t clkid,uint32_t * div)237 zynqmp_firmware_clock_getdivider(device_t dev, uint32_t clkid, uint32_t *div)
238 {
239 struct zynqmp_firmware_softc *sc;
240 uint32_t payload[4];
241 int rv;
242
243 sc = device_get_softc(dev);
244 rv = zynqmp_call_smc(PM_CLOCK_GETDIVIDER, clkid, 0, 0, 0, payload, false);
245 if (rv != 0) {
246 device_printf(sc->dev, "SMC Call fail %d\n", rv);
247 goto out;
248 }
249 *div = payload[1];
250
251 out:
252 return (rv);
253 }
254
255 static int
zynqmp_firmware_clock_setparent(device_t dev,uint32_t clkid,uint32_t parentid)256 zynqmp_firmware_clock_setparent(device_t dev, uint32_t clkid, uint32_t parentid)
257 {
258 struct zynqmp_firmware_softc *sc;
259 uint32_t payload[4];
260 int rv;
261
262 sc = device_get_softc(dev);
263 rv = zynqmp_call_smc(PM_CLOCK_SETPARENT, clkid, parentid, 0, 0, payload, false);
264 if (rv != 0)
265 device_printf(sc->dev, "SMC Call fail %d\n", rv);
266 return (rv);
267 }
268
269 static int
zynqmp_firmware_clock_getparent(device_t dev,uint32_t clkid,uint32_t * parentid)270 zynqmp_firmware_clock_getparent(device_t dev, uint32_t clkid, uint32_t *parentid)
271 {
272 struct zynqmp_firmware_softc *sc;
273 uint32_t payload[4];
274 int rv;
275
276 sc = device_get_softc(dev);
277 rv = zynqmp_call_smc(PM_CLOCK_GETPARENT, clkid, 0, 0, 0, payload, false);
278 if (rv != 0) {
279 device_printf(sc->dev, "SMC Call fail %d\n", rv);
280 goto out;
281 }
282 *parentid = payload[1];
283 out:
284 return (rv);
285 }
286
287 static int
zynqmp_firmware_pll_get_mode(device_t dev,uint32_t pllid,uint32_t * mode)288 zynqmp_firmware_pll_get_mode(device_t dev, uint32_t pllid, uint32_t *mode)
289 {
290 struct zynqmp_firmware_softc *sc;
291 uint32_t payload[4];
292 int rv;
293
294 sc = device_get_softc(dev);
295 rv = zynqmp_call_smc(PM_IOCTL, 0, IOCTL_GET_PLL_FRAC_MODE, pllid, 0, payload, false);
296 if (rv != 0) {
297 device_printf(sc->dev, "SMC Call fail %d\n", rv);
298 goto out;
299 }
300 *mode = payload[1];
301 out:
302 return (rv);
303 }
304
305 static int
zynqmp_firmware_pll_get_frac_data(device_t dev,uint32_t pllid,uint32_t * data)306 zynqmp_firmware_pll_get_frac_data(device_t dev, uint32_t pllid, uint32_t *data)
307 {
308 struct zynqmp_firmware_softc *sc;
309 uint32_t payload[4];
310 int rv;
311
312 sc = device_get_softc(dev);
313 rv = zynqmp_call_smc(PM_IOCTL, 0, IOCTL_GET_PLL_FRAC_DATA, pllid, 0, payload, false);
314 if (rv != 0) {
315 device_printf(sc->dev, "SMC Call fail %d\n", rv);
316 goto out;
317 }
318 *data = payload[1];
319 out:
320 return (rv);
321 }
322
323 static int
zynqmp_firmware_clock_get_fixedfactor(device_t dev,uint32_t clkid,uint32_t * mult,uint32_t * div)324 zynqmp_firmware_clock_get_fixedfactor(device_t dev, uint32_t clkid, uint32_t *mult, uint32_t *div)
325 {
326 struct zynqmp_firmware_softc *sc;
327 uint32_t payload[4];
328 int rv;
329
330 sc = device_get_softc(dev);
331 rv = zynqmp_call_smc(PM_QUERY_DATA, PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS, clkid, 0, 0, payload, true);
332 if (rv != 0) {
333 device_printf(sc->dev, "SMC Call fail %d\n", rv);
334 goto out;
335 }
336 *mult = payload[1];
337 *div = payload[2];
338 out:
339 return (rv);
340 }
341
342 static int
zynqmp_firmware_query_data(device_t dev,uint32_t qid,uint32_t arg1,uint32_t arg2,uint32_t arg3,uint32_t * data)343 zynqmp_firmware_query_data(device_t dev, uint32_t qid, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t *data)
344 {
345 struct zynqmp_firmware_softc *sc;
346 int rv;
347
348 sc = device_get_softc(dev);
349 rv = zynqmp_call_smc(PM_QUERY_DATA, qid, arg1, arg2, arg3, data, true);
350 /*
351 * PM_QID_CLOCK_GET_NAME always success and if the clock name couldn't
352 * be found the clock name will be all null byte
353 */
354 if (qid == 1)
355 rv = 0;
356 if (rv != 0)
357 device_printf(sc->dev, "SMC Call fail %d\n", rv);
358 return (rv);
359 }
360
361 static int
zynqmp_firmware_reset_assert(device_t dev,uint32_t resetid,bool enable)362 zynqmp_firmware_reset_assert(device_t dev, uint32_t resetid, bool enable)
363 {
364 struct zynqmp_firmware_softc *sc;
365 int rv;
366
367 sc = device_get_softc(dev);
368 rv = zynqmp_call_smc(PM_RESET_ASSERT, resetid, enable, 0, 0, NULL, true);
369 if (rv != 0)
370 device_printf(sc->dev, "SMC Call fail %d\n", rv);
371
372 return (rv);
373 }
374
375 static int
zynqmp_firmware_reset_get_status(device_t dev,uint32_t resetid,bool * status)376 zynqmp_firmware_reset_get_status(device_t dev, uint32_t resetid, bool *status)
377 {
378 struct zynqmp_firmware_softc *sc;
379 uint32_t payload[4];
380 int rv;
381
382 sc = device_get_softc(dev);
383 rv = zynqmp_call_smc(PM_RESET_GET_STATUS, resetid, 0, 0, 0, payload, true);
384 if (rv != 0) {
385 device_printf(sc->dev, "SMC Call fail %d\n", rv);
386 return (rv);
387 }
388 *status = payload[1];
389
390 return (rv);
391 }
392
393 /* Simplebus methods */
394 static struct simplebus_devinfo *
zynqmp_firmware_setup_dinfo(device_t dev,phandle_t node,struct simplebus_devinfo * di)395 zynqmp_firmware_setup_dinfo(device_t dev, phandle_t node,
396 struct simplebus_devinfo *di)
397 {
398 struct simplebus_softc *sc;
399 struct simplebus_devinfo *ndi;
400
401 sc = device_get_softc(dev);
402 if (di == NULL)
403 ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO);
404 else
405 ndi = di;
406 if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) {
407 if (di == NULL)
408 free(ndi, M_DEVBUF);
409 return (NULL);
410 }
411
412 /* reg resources is from the parent but interrupts is on the node itself */
413 resource_list_init(&ndi->rl);
414 ofw_bus_reg_to_rl(dev, OF_parent(node), sc->acells, sc->scells, &ndi->rl);
415 ofw_bus_intr_to_rl(dev, node, &ndi->rl, NULL);
416
417 return (ndi);
418 }
419
420 static device_t
zynqmp_firmware_add_device(device_t dev,phandle_t node,u_int order,const char * name,int unit,struct simplebus_devinfo * di)421 zynqmp_firmware_add_device(device_t dev, phandle_t node, u_int order,
422 const char *name, int unit, struct simplebus_devinfo *di)
423 {
424 struct simplebus_devinfo *ndi;
425 device_t cdev;
426
427 if ((ndi = zynqmp_firmware_setup_dinfo(dev, node, di)) == NULL)
428 return (NULL);
429 cdev = device_add_child_ordered(dev, order, name, unit);
430 if (cdev == NULL) {
431 device_printf(dev, "<%s>: device_add_child failed\n",
432 ndi->obdinfo.obd_name);
433 resource_list_free(&ndi->rl);
434 ofw_bus_gen_destroy_devinfo(&ndi->obdinfo);
435 if (di == NULL)
436 free(ndi, M_DEVBUF);
437 return (NULL);
438 }
439 device_set_ivars(cdev, ndi);
440
441 return(cdev);
442 }
443
444 static int
zynqmp_firmware_probe(device_t dev)445 zynqmp_firmware_probe(device_t dev)
446 {
447
448 if (!ofw_bus_status_okay(dev))
449 return (ENXIO);
450 if (!ofw_bus_is_compatible(dev, "xlnx,zynqmp-firmware"))
451 return (ENXIO);
452 device_set_desc(dev, "ZynqMP Firmware");
453 return (0);
454 }
455
456 static int
zynqmp_firmware_attach(device_t dev)457 zynqmp_firmware_attach(device_t dev)
458 {
459 struct zynqmp_firmware_softc *sc;
460 phandle_t node, child;
461 device_t cdev;
462
463 sc = device_get_softc(dev);
464 sc->dev = dev;
465
466 if (bootverbose) {
467 zynqmp_get_api_version(sc);
468 zynqmp_get_chipid(sc);
469 zynqmp_get_trustzone_version(sc);
470 }
471
472 /* Attach children */
473 node = ofw_bus_get_node(dev);
474 for (child = OF_child(node); child != 0; child = OF_peer(child)) {
475 cdev = zynqmp_firmware_add_device(dev, child, 0, NULL, -1, NULL);
476 if (cdev != NULL)
477 device_probe_and_attach(cdev);
478 }
479
480 bus_attach_children(dev);
481 return (0);
482 }
483
484 static device_method_t zynqmp_firmware_methods[] = {
485 /* device_if */
486 DEVMETHOD(device_probe, zynqmp_firmware_probe),
487 DEVMETHOD(device_attach, zynqmp_firmware_attach),
488
489 /* zynqmp_firmware_if */
490 DEVMETHOD(zynqmp_firmware_clock_enable, zynqmp_firmware_clock_enable),
491 DEVMETHOD(zynqmp_firmware_clock_disable, zynqmp_firmware_clock_disable),
492 DEVMETHOD(zynqmp_firmware_clock_getstate, zynqmp_firmware_clock_getstate),
493 DEVMETHOD(zynqmp_firmware_clock_setdivider, zynqmp_firmware_clock_setdivider),
494 DEVMETHOD(zynqmp_firmware_clock_getdivider, zynqmp_firmware_clock_getdivider),
495 DEVMETHOD(zynqmp_firmware_clock_setparent, zynqmp_firmware_clock_setparent),
496 DEVMETHOD(zynqmp_firmware_clock_getparent, zynqmp_firmware_clock_getparent),
497 DEVMETHOD(zynqmp_firmware_pll_get_mode, zynqmp_firmware_pll_get_mode),
498 DEVMETHOD(zynqmp_firmware_pll_get_frac_data, zynqmp_firmware_pll_get_frac_data),
499 DEVMETHOD(zynqmp_firmware_clock_get_fixedfactor, zynqmp_firmware_clock_get_fixedfactor),
500 DEVMETHOD(zynqmp_firmware_query_data, zynqmp_firmware_query_data),
501 DEVMETHOD(zynqmp_firmware_reset_assert, zynqmp_firmware_reset_assert),
502 DEVMETHOD(zynqmp_firmware_reset_get_status, zynqmp_firmware_reset_get_status),
503
504 DEVMETHOD_END
505 };
506
507 DEFINE_CLASS_1(zynqmp_firmware, zynqmp_firmware_driver, zynqmp_firmware_methods,
508 sizeof(struct zynqmp_firmware_softc), simplebus_driver);
509
510 EARLY_DRIVER_MODULE(zynqmp_firmware, simplebus, zynqmp_firmware_driver, 0, 0,
511 BUS_PASS_BUS + BUS_PASS_ORDER_LATE);
512 MODULE_VERSION(zynqmp_firmware, 1);
513