firmware: zynqmp: get fw version with mailbox driver
[oweals/u-boot.git] / drivers / firmware / firmware-zynqmp.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Xilinx Zynq MPSoC Firmware driver
4  *
5  * Copyright (C) 2018-2019 Xilinx, Inc.
6  */
7
8 #include <common.h>
9 #include <dm.h>
10
11 #if defined(CONFIG_ZYNQMP_IPI)
12 #include <mailbox.h>
13 #include <asm/arch/sys_proto.h>
14
15 #define PMUFW_PAYLOAD_ARG_CNT   8
16
17 struct zynqmp_power {
18         struct mbox_chan tx_chan;
19         struct mbox_chan rx_chan;
20 } zynqmp_power;
21
22 static int ipi_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
23 {
24         struct zynqmp_ipi_msg msg;
25         int ret;
26
27         if (req_len > PMUFW_PAYLOAD_ARG_CNT ||
28             res_maxlen > PMUFW_PAYLOAD_ARG_CNT)
29                 return -EINVAL;
30
31         if (!(zynqmp_power.tx_chan.dev) || !(&zynqmp_power.rx_chan.dev))
32                 return -EINVAL;
33
34         msg.buf = (u32 *)req;
35         msg.len = req_len;
36         ret = mbox_send(&zynqmp_power.tx_chan, &msg);
37         if (ret) {
38                 debug("%s: Sending message failed\n", __func__);
39                 return ret;
40         }
41
42         msg.buf = res;
43         msg.len = res_maxlen;
44         ret = mbox_recv(&zynqmp_power.rx_chan, &msg, 100);
45         if (ret)
46                 debug("%s: Receiving message failed\n", __func__);
47
48         return ret;
49 }
50
51 unsigned int zynqmp_firmware_version(void)
52 {
53         int ret;
54         u32 ret_payload[PAYLOAD_ARG_CNT];
55         static u32 pm_api_version = ZYNQMP_PM_VERSION_INVALID;
56
57         /*
58          * Get PMU version only once and later
59          * just return stored values instead of
60          * asking PMUFW again.
61          **/
62         if (pm_api_version == ZYNQMP_PM_VERSION_INVALID) {
63                 if (IS_ENABLED(CONFIG_SPL_BUILD)) {
64                         const u32 request[] = { PM_GET_API_VERSION };
65
66                         ret = ipi_req(request, ARRAY_SIZE(request),
67                                       ret_payload, 2);
68                 } else {
69                         ret = invoke_smc(ZYNQMP_SIP_SVC_GET_API_VERSION, 0, 0,
70                                          0, 0, ret_payload);
71                 };
72
73                 if (ret)
74                         panic("PMUFW is not found - Please load it!\n");
75
76                 pm_api_version = ret_payload[1];
77                 if (pm_api_version < ZYNQMP_PM_VERSION)
78                         panic("PMUFW version error. Expected: v%d.%d\n",
79                               ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR);
80         }
81
82         return pm_api_version;
83 };
84
85 static int zynqmp_power_probe(struct udevice *dev)
86 {
87         int ret = 0;
88
89         debug("%s, (dev=%p)\n", __func__, dev);
90
91         ret = mbox_get_by_name(dev, "tx", &zynqmp_power.tx_chan);
92         if (ret) {
93                 debug("%s, cannot tx mailbox\n", __func__);
94                 return ret;
95         }
96
97         ret = mbox_get_by_name(dev, "rx", &zynqmp_power.rx_chan);
98         if (ret) {
99                 debug("%s, cannot rx mailbox\n", __func__);
100                 return ret;
101         }
102
103         ret = zynqmp_firmware_version();
104         printf("PMUFW:\tv%d.%d\n",
105                ret >> ZYNQMP_PM_VERSION_MAJOR_SHIFT,
106                ret & ZYNQMP_PM_VERSION_MINOR_MASK);
107
108         return 0;
109 };
110
111 static const struct udevice_id zynqmp_power_ids[] = {
112         { .compatible = "xlnx,zynqmp-power" },
113         { }
114 };
115
116 U_BOOT_DRIVER(zynqmp_power) = {
117         .name = "zynqmp_power",
118         .id = UCLASS_FIRMWARE,
119         .of_match = zynqmp_power_ids,
120         .probe = zynqmp_power_probe,
121 };
122 #endif
123
124 static const struct udevice_id zynqmp_firmware_ids[] = {
125         { .compatible = "xlnx,zynqmp-firmware" },
126         { .compatible = "xlnx,versal-firmware"},
127         { }
128 };
129
130 U_BOOT_DRIVER(zynqmp_firmware) = {
131         .id = UCLASS_FIRMWARE,
132         .name = "zynqmp-firmware",
133         .probe = dm_scan_fdt_dev,
134         .of_match = zynqmp_firmware_ids,
135 };