net: ks8851: Remove type_frame_head
[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 #include <log.h>
11 #include <zynqmp_firmware.h>
12 #include <asm/cache.h>
13 #include <asm/ptrace.h>
14
15 #if defined(CONFIG_ZYNQMP_IPI)
16 #include <mailbox.h>
17 #include <asm/arch/sys_proto.h>
18
19 #define PMUFW_PAYLOAD_ARG_CNT   8
20
21 struct zynqmp_power {
22         struct mbox_chan tx_chan;
23         struct mbox_chan rx_chan;
24 } zynqmp_power;
25
26 static int ipi_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
27 {
28         struct zynqmp_ipi_msg msg;
29         int ret;
30
31         if (req_len > PMUFW_PAYLOAD_ARG_CNT ||
32             res_maxlen > PMUFW_PAYLOAD_ARG_CNT)
33                 return -EINVAL;
34
35         if (!(zynqmp_power.tx_chan.dev) || !(&zynqmp_power.rx_chan.dev))
36                 return -EINVAL;
37
38         msg.buf = (u32 *)req;
39         msg.len = req_len;
40         ret = mbox_send(&zynqmp_power.tx_chan, &msg);
41         if (ret) {
42                 debug("%s: Sending message failed\n", __func__);
43                 return ret;
44         }
45
46         msg.buf = res;
47         msg.len = res_maxlen;
48         ret = mbox_recv(&zynqmp_power.rx_chan, &msg, 100);
49         if (ret)
50                 debug("%s: Receiving message failed\n", __func__);
51
52         return ret;
53 }
54
55 static int send_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
56 {
57         if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3)
58                 return ipi_req(req, req_len, res, res_maxlen);
59
60         return xilinx_pm_request(req[0], 0, 0, 0, 0, res);
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                 const u32 request[] = { PM_GET_API_VERSION };
76
77                 ret = send_req(request, ARRAY_SIZE(request), ret_payload, 2);
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         const u32 request[] = {
99                 PM_SET_CONFIGURATION,
100                 (u32)((u64)cfg_obj)
101         };
102         u32 response;
103         int err;
104
105         printf("Loading new PMUFW cfg obj (%ld bytes)\n", size);
106
107         err = send_req(request, ARRAY_SIZE(request), &response, 1);
108         if (err)
109                 panic("Cannot load PMUFW configuration object (%d)\n", err);
110         if (response != 0)
111                 panic("PMUFW returned 0x%08x status!\n", response);
112 }
113
114 static int zynqmp_power_probe(struct udevice *dev)
115 {
116         int ret;
117
118         debug("%s, (dev=%p)\n", __func__, dev);
119
120         ret = mbox_get_by_name(dev, "tx", &zynqmp_power.tx_chan);
121         if (ret) {
122                 debug("%s: Cannot find tx mailbox\n", __func__);
123                 return ret;
124         }
125
126         ret = mbox_get_by_name(dev, "rx", &zynqmp_power.rx_chan);
127         if (ret) {
128                 debug("%s: Cannot find rx mailbox\n", __func__);
129                 return ret;
130         }
131
132         ret = zynqmp_firmware_version();
133         printf("PMUFW:\tv%d.%d\n",
134                ret >> ZYNQMP_PM_VERSION_MAJOR_SHIFT,
135                ret & ZYNQMP_PM_VERSION_MINOR_MASK);
136
137         return 0;
138 };
139
140 static const struct udevice_id zynqmp_power_ids[] = {
141         { .compatible = "xlnx,zynqmp-power" },
142         { }
143 };
144
145 U_BOOT_DRIVER(zynqmp_power) = {
146         .name = "zynqmp_power",
147         .id = UCLASS_FIRMWARE,
148         .of_match = zynqmp_power_ids,
149         .probe = zynqmp_power_probe,
150 };
151 #endif
152
153 int __maybe_unused xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2,
154                                      u32 arg3, u32 *ret_payload)
155 {
156         /*
157          * Added SIP service call Function Identifier
158          * Make sure to stay in x0 register
159          */
160         struct pt_regs regs;
161
162         if (current_el() == 3) {
163                 printf("%s: Can't call SMC from EL3 context\n", __func__);
164                 return -EPERM;
165         }
166
167         regs.regs[0] = PM_SIP_SVC | api_id;
168         regs.regs[1] = ((u64)arg1 << 32) | arg0;
169         regs.regs[2] = ((u64)arg3 << 32) | arg2;
170
171         smc_call(&regs);
172
173         if (ret_payload) {
174                 ret_payload[0] = (u32)regs.regs[0];
175                 ret_payload[1] = upper_32_bits(regs.regs[0]);
176                 ret_payload[2] = (u32)regs.regs[1];
177                 ret_payload[3] = upper_32_bits(regs.regs[1]);
178                 ret_payload[4] = (u32)regs.regs[2];
179         }
180
181         return regs.regs[0];
182 }
183
184 static const struct udevice_id zynqmp_firmware_ids[] = {
185         { .compatible = "xlnx,zynqmp-firmware" },
186         { .compatible = "xlnx,versal-firmware"},
187         { }
188 };
189
190 U_BOOT_DRIVER(zynqmp_firmware) = {
191         .id = UCLASS_FIRMWARE,
192         .name = "zynqmp-firmware",
193         .of_match = zynqmp_firmware_ids,
194 };