41089712b0884b2a2b931c8535c583fc44931add
[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
14 #if defined(CONFIG_ZYNQMP_IPI)
15 #include <mailbox.h>
16 #include <asm/arch/sys_proto.h>
17
18 #define PMUFW_PAYLOAD_ARG_CNT   8
19
20 struct zynqmp_power {
21         struct mbox_chan tx_chan;
22         struct mbox_chan rx_chan;
23 } zynqmp_power;
24
25 static int ipi_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
26 {
27         struct zynqmp_ipi_msg msg;
28         int ret;
29
30         if (req_len > PMUFW_PAYLOAD_ARG_CNT ||
31             res_maxlen > PMUFW_PAYLOAD_ARG_CNT)
32                 return -EINVAL;
33
34         if (!(zynqmp_power.tx_chan.dev) || !(&zynqmp_power.rx_chan.dev))
35                 return -EINVAL;
36
37         msg.buf = (u32 *)req;
38         msg.len = req_len;
39         ret = mbox_send(&zynqmp_power.tx_chan, &msg);
40         if (ret) {
41                 debug("%s: Sending message failed\n", __func__);
42                 return ret;
43         }
44
45         msg.buf = res;
46         msg.len = res_maxlen;
47         ret = mbox_recv(&zynqmp_power.rx_chan, &msg, 100);
48         if (ret)
49                 debug("%s: Receiving message failed\n", __func__);
50
51         return ret;
52 }
53
54 static int send_req(const u32 *req, size_t req_len, u32 *res, size_t res_maxlen)
55 {
56         if (IS_ENABLED(CONFIG_SPL_BUILD) || current_el() == 3)
57                 return ipi_req(req, req_len, res, res_maxlen);
58
59         return xilinx_pm_request(req[0], 0, 0, 0, 0, res);
60 }
61
62 unsigned int zynqmp_firmware_version(void)
63 {
64         int ret;
65         u32 ret_payload[PAYLOAD_ARG_CNT];
66         static u32 pm_api_version = ZYNQMP_PM_VERSION_INVALID;
67
68         /*
69          * Get PMU version only once and later
70          * just return stored values instead of
71          * asking PMUFW again.
72          **/
73         if (pm_api_version == ZYNQMP_PM_VERSION_INVALID) {
74                 const u32 request[] = { PM_GET_API_VERSION };
75
76                 ret = send_req(request, ARRAY_SIZE(request), ret_payload, 2);
77                 if (ret)
78                         panic("PMUFW is not found - Please load it!\n");
79
80                 pm_api_version = ret_payload[1];
81                 if (pm_api_version < ZYNQMP_PM_VERSION)
82                         panic("PMUFW version error. Expected: v%d.%d\n",
83                               ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR);
84         }
85
86         return pm_api_version;
87 };
88
89 /**
90  * Send a configuration object to the PMU firmware.
91  *
92  * @cfg_obj: Pointer to the configuration object
93  * @size:    Size of @cfg_obj in bytes
94  */
95 void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size)
96 {
97         const u32 request[] = {
98                 PM_SET_CONFIGURATION,
99                 (u32)((u64)cfg_obj)
100         };
101         u32 response;
102         int err;
103
104         printf("Loading new PMUFW cfg obj (%ld bytes)\n", size);
105
106         err = send_req(request, ARRAY_SIZE(request), &response, 1);
107         if (err)
108                 panic("Cannot load PMUFW configuration object (%d)\n", err);
109         if (response != 0)
110                 panic("PMUFW returned 0x%08x status!\n", response);
111 }
112
113 static int zynqmp_power_probe(struct udevice *dev)
114 {
115         int ret;
116
117         debug("%s, (dev=%p)\n", __func__, dev);
118
119         ret = mbox_get_by_name(dev, "tx", &zynqmp_power.tx_chan);
120         if (ret) {
121                 debug("%s: Cannot find tx mailbox\n", __func__);
122                 return ret;
123         }
124
125         ret = mbox_get_by_name(dev, "rx", &zynqmp_power.rx_chan);
126         if (ret) {
127                 debug("%s: Cannot find rx mailbox\n", __func__);
128                 return ret;
129         }
130
131         ret = zynqmp_firmware_version();
132         printf("PMUFW:\tv%d.%d\n",
133                ret >> ZYNQMP_PM_VERSION_MAJOR_SHIFT,
134                ret & ZYNQMP_PM_VERSION_MINOR_MASK);
135
136         return 0;
137 };
138
139 static const struct udevice_id zynqmp_power_ids[] = {
140         { .compatible = "xlnx,zynqmp-power" },
141         { }
142 };
143
144 U_BOOT_DRIVER(zynqmp_power) = {
145         .name = "zynqmp_power",
146         .id = UCLASS_FIRMWARE,
147         .of_match = zynqmp_power_ids,
148         .probe = zynqmp_power_probe,
149 };
150 #endif
151
152 int __maybe_unused xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2,
153                                      u32 arg3, u32 *ret_payload)
154 {
155         /*
156          * Added SIP service call Function Identifier
157          * Make sure to stay in x0 register
158          */
159         struct pt_regs regs;
160
161         if (current_el() == 3) {
162                 printf("%s: Can't call SMC from EL3 context\n", __func__);
163                 return -EPERM;
164         }
165
166         regs.regs[0] = PM_SIP_SVC | api_id;
167         regs.regs[1] = ((u64)arg1 << 32) | arg0;
168         regs.regs[2] = ((u64)arg3 << 32) | arg2;
169
170         smc_call(&regs);
171
172         if (ret_payload) {
173                 ret_payload[0] = (u32)regs.regs[0];
174                 ret_payload[1] = upper_32_bits(regs.regs[0]);
175                 ret_payload[2] = (u32)regs.regs[1];
176                 ret_payload[3] = upper_32_bits(regs.regs[1]);
177                 ret_payload[4] = (u32)regs.regs[2];
178         }
179
180         return regs.regs[0];
181 }
182
183 static const struct udevice_id zynqmp_firmware_ids[] = {
184         { .compatible = "xlnx,zynqmp-firmware" },
185         { .compatible = "xlnx,versal-firmware"},
186         { }
187 };
188
189 U_BOOT_DRIVER(zynqmp_firmware) = {
190         .id = UCLASS_FIRMWARE,
191         .name = "zynqmp-firmware",
192         .of_match = zynqmp_firmware_ids,
193 };