firmware: zynqmp: Move loading message to debug
[platform/kernel/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 <cpu_func.h>
10 #include <dm.h>
11 #include <log.h>
12 #include <zynqmp_firmware.h>
13 #include <asm/cache.h>
14 #include <asm/ptrace.h>
15
16 #if defined(CONFIG_ZYNQMP_IPI)
17 #include <mailbox.h>
18 #include <asm/arch/sys_proto.h>
19
20 #define PMUFW_PAYLOAD_ARG_CNT   8
21
22 #define XST_PM_NO_ACCESS        2002L
23
24 struct zynqmp_power {
25         struct mbox_chan tx_chan;
26         struct mbox_chan rx_chan;
27 } zynqmp_power;
28
29 static int ipi_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
30 {
31         struct zynqmp_ipi_msg msg;
32         int ret;
33         u32 buffer[PAYLOAD_ARG_CNT];
34
35         if (!res)
36                 res = buffer;
37
38         if (req_len > PMUFW_PAYLOAD_ARG_CNT ||
39             res_maxlen > PMUFW_PAYLOAD_ARG_CNT)
40                 return -EINVAL;
41
42         if (!(zynqmp_power.tx_chan.dev) || !(&zynqmp_power.rx_chan.dev))
43                 return -EINVAL;
44
45         debug("%s, Sending IPI message with ID: 0x%0x\n", __func__, req[0]);
46         msg.buf = (u32 *)req;
47         msg.len = req_len;
48         ret = mbox_send(&zynqmp_power.tx_chan, &msg);
49         if (ret) {
50                 debug("%s: Sending message failed\n", __func__);
51                 return ret;
52         }
53
54         msg.buf = res;
55         msg.len = res_maxlen;
56         ret = mbox_recv(&zynqmp_power.rx_chan, &msg, 100);
57         if (ret)
58                 debug("%s: Receiving message failed\n", __func__);
59
60         return ret;
61 }
62
63 unsigned int zynqmp_firmware_version(void)
64 {
65         int ret;
66         u32 ret_payload[PAYLOAD_ARG_CNT];
67         static u32 pm_api_version = ZYNQMP_PM_VERSION_INVALID;
68
69         /*
70          * Get PMU version only once and later
71          * just return stored values instead of
72          * asking PMUFW again.
73          **/
74         if (pm_api_version == ZYNQMP_PM_VERSION_INVALID) {
75
76                 ret = xilinx_pm_request(PM_GET_API_VERSION, 0, 0, 0, 0,
77                                         ret_payload);
78                 if (ret)
79                         panic("PMUFW is not found - Please load it!\n");
80
81                 pm_api_version = ret_payload[1];
82                 if (pm_api_version < ZYNQMP_PM_VERSION)
83                         panic("PMUFW version error. Expected: v%d.%d\n",
84                               ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR);
85         }
86
87         return pm_api_version;
88 };
89
90 /**
91  * Send a configuration object to the PMU firmware.
92  *
93  * @cfg_obj: Pointer to the configuration object
94  * @size:    Size of @cfg_obj in bytes
95  */
96 void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size)
97 {
98         int err;
99         u32 ret_payload[PAYLOAD_ARG_CNT];
100
101         if (IS_ENABLED(CONFIG_SPL_BUILD))
102                 printf("Loading new PMUFW cfg obj (%ld bytes)\n", size);
103
104         flush_dcache_range((ulong)cfg_obj, (ulong)(cfg_obj + size));
105
106         err = xilinx_pm_request(PM_SET_CONFIGURATION, (u32)(u64)cfg_obj, 0, 0,
107                                 0, ret_payload);
108         if (err == XST_PM_NO_ACCESS) {
109                 printf("PMUFW no permission to change config object\n");
110                 return;
111         }
112
113         if (err)
114                 printf("Cannot load PMUFW configuration object (%d)\n", err);
115
116         if (ret_payload[0])
117                 printf("PMUFW returned 0x%08x status!\n", ret_payload[0]);
118
119         if ((err || ret_payload[0]) && IS_ENABLED(CONFIG_SPL_BUILD))
120                 panic("PMUFW config object loading failed in EL3\n");
121 }
122
123 static int zynqmp_power_probe(struct udevice *dev)
124 {
125         int ret;
126
127         debug("%s, (dev=%p)\n", __func__, dev);
128
129         ret = mbox_get_by_name(dev, "tx", &zynqmp_power.tx_chan);
130         if (ret) {
131                 debug("%s: Cannot find tx mailbox\n", __func__);
132                 return ret;
133         }
134
135         ret = mbox_get_by_name(dev, "rx", &zynqmp_power.rx_chan);
136         if (ret) {
137                 debug("%s: Cannot find rx mailbox\n", __func__);
138                 return ret;
139         }
140
141         ret = zynqmp_firmware_version();
142         printf("PMUFW:\tv%d.%d\n",
143                ret >> ZYNQMP_PM_VERSION_MAJOR_SHIFT,
144                ret & ZYNQMP_PM_VERSION_MINOR_MASK);
145
146         return 0;
147 };
148
149 static const struct udevice_id zynqmp_power_ids[] = {
150         { .compatible = "xlnx,zynqmp-power" },
151         { }
152 };
153
154 U_BOOT_DRIVER(zynqmp_power) = {
155         .name = "zynqmp_power",
156         .id = UCLASS_FIRMWARE,
157         .of_match = zynqmp_power_ids,
158         .probe = zynqmp_power_probe,
159 };
160 #endif
161
162 int __maybe_unused xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2,
163                                      u32 arg3, u32 *ret_payload)
164 {
165         debug("%s at EL%d, API ID: 0x%0x\n", __func__, current_el(), api_id);
166
167         if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3) {
168 #if defined(CONFIG_ZYNQMP_IPI)
169                 /*
170                  * Use fixed payload and arg size as the EL2 call. The firmware
171                  * is capable to handle PMUFW_PAYLOAD_ARG_CNT bytes but the
172                  * firmware API is limited by the SMC call size
173                  */
174                 u32 regs[] = {api_id, arg0, arg1, arg2, arg3};
175                 int ret;
176
177                 if (api_id == PM_FPGA_LOAD) {
178                         /* Swap addr_hi/low because of incompatibility */
179                         u32 temp = regs[1];
180
181                         regs[1] = regs[2];
182                         regs[2] = temp;
183                 }
184
185                 ret = ipi_req(regs, PAYLOAD_ARG_CNT, ret_payload,
186                               PAYLOAD_ARG_CNT);
187                 if (ret)
188                         return ret;
189 #else
190                 return -EPERM;
191 #endif
192         } else {
193                 /*
194                  * Added SIP service call Function Identifier
195                  * Make sure to stay in x0 register
196                  */
197                 struct pt_regs regs;
198
199                 regs.regs[0] = PM_SIP_SVC | api_id;
200                 regs.regs[1] = ((u64)arg1 << 32) | arg0;
201                 regs.regs[2] = ((u64)arg3 << 32) | arg2;
202
203                 smc_call(&regs);
204
205                 if (ret_payload) {
206                         ret_payload[0] = (u32)regs.regs[0];
207                         ret_payload[1] = upper_32_bits(regs.regs[0]);
208                         ret_payload[2] = (u32)regs.regs[1];
209                         ret_payload[3] = upper_32_bits(regs.regs[1]);
210                         ret_payload[4] = (u32)regs.regs[2];
211                 }
212
213         }
214         return (ret_payload) ? ret_payload[0] : 0;
215 }
216
217 static const struct udevice_id zynqmp_firmware_ids[] = {
218         { .compatible = "xlnx,zynqmp-firmware" },
219         { .compatible = "xlnx,versal-firmware"},
220         { }
221 };
222
223 U_BOOT_DRIVER(zynqmp_firmware) = {
224         .id = UCLASS_FIRMWARE,
225         .name = "zynqmp_firmware",
226         .of_match = zynqmp_firmware_ids,
227 };