blob: 4df69734ed952c707992671577a375c6ddd3f75b [file] [log] [blame]
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Xilinx Zynq MPSoC Mailbox driver
4 *
5 * Copyright (C) 2018-2019 Xilinx, Inc.
6 */
7
Simon Glass0f2af882020-05-10 11:40:05 -06008#include <log.h>
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +01009#include <asm/io.h>
Tanmay Shahb7399062023-12-04 13:56:17 -080010#include <asm/system.h>
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +010011#include <dm.h>
12#include <mailbox-uclass.h>
Simon Glass9bc15642020-02-03 07:36:16 -070013#include <dm/device_compat.h>
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -080014#include <dm/lists.h>
Tanmay Shahb7399062023-12-04 13:56:17 -080015#include <dm/of_access.h>
16#include <linux/arm-smccc.h>
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +010017#include <linux/ioport.h>
18#include <linux/io.h>
19#include <wait_bit.h>
Ashok Reddy Soma73e3f992022-07-22 02:46:57 -060020#include <zynqmp_firmware.h>
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +010021
22/* IPI bitmasks, register base */
23/* TODO: move reg base to DT */
24#define IPI_BIT_MASK_PMU0 0x10000
25#define IPI_INT_REG_BASE_APU 0xFF300000
26
Tanmay Shahb7399062023-12-04 13:56:17 -080027/* IPI agent ID any */
28#define IPI_ID_ANY 0xFFUL
29
30/* indicate if ZynqMP IPI mailbox driver uses SMC calls or HVC calls */
31#define USE_SMC 0
32
33/* Default IPI SMC function IDs */
34#define SMC_IPI_MAILBOX_OPEN 0x82001000U
35#define SMC_IPI_MAILBOX_RELEASE 0x82001001U
36#define SMC_IPI_MAILBOX_STATUS_ENQUIRY 0x82001002U
37#define SMC_IPI_MAILBOX_NOTIFY 0x82001003U
38#define SMC_IPI_MAILBOX_ACK 0x82001004U
39#define SMC_IPI_MAILBOX_ENABLE_IRQ 0x82001005U
40#define SMC_IPI_MAILBOX_DISABLE_IRQ 0x82001006U
41
42/* IPI SMC Macros */
43
44/*
45 * Flag to indicate if notification interrupt
46 * to be disabled.
47 */
48#define IPI_SMC_ENQUIRY_DIRQ_MASK BIT(0)
49
50/*
51 * Flag to indicate if notification interrupt
52 * to be enabled.
53 */
54#define IPI_SMC_ACK_EIRQ_MASK BIT(0)
55
56/* IPI mailbox status */
57#define IPI_MB_STATUS_IDLE 0
58#define IPI_MB_STATUS_SEND_PENDING 1
59#define IPI_MB_STATUS_RECV_PENDING 2
60
61#define IPI_MB_CHNL_TX 0 /* IPI mailbox TX channel */
62#define IPI_MB_CHNL_RX 1 /* IPI mailbox RX channel */
63
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +010064struct ipi_int_regs {
65 u32 trig; /* 0x0 */
66 u32 obs; /* 0x4 */
Ibai Erkiagaab07ce52020-08-04 23:17:32 +010067 u32 dummy0;
68 u32 dummy1;
69 u32 isr; /* 0x10 */
70 u32 imr; /* 0x14 */
71 u32 ier; /* 0x18 */
72 u32 idr; /* 0x1C */
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +010073};
74
75#define ipi_int_apu ((struct ipi_int_regs *)IPI_INT_REG_BASE_APU)
76
77struct zynqmp_ipi {
78 void __iomem *local_req_regs;
79 void __iomem *local_res_regs;
80 void __iomem *remote_req_regs;
81 void __iomem *remote_res_regs;
Tanmay Shahb7399062023-12-04 13:56:17 -080082 u32 remote_id;
83 u32 local_id;
Tanmay Shah26954072023-12-04 13:56:18 -080084 bool el3_supported;
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +010085};
86
Tanmay Shahb7399062023-12-04 13:56:17 -080087static int zynqmp_ipi_fw_call(struct zynqmp_ipi *ipi_mbox,
88 unsigned long a0, unsigned long a3)
89{
90 struct arm_smccc_res res = {0};
91 unsigned long a1, a2;
92
93 a1 = ipi_mbox->local_id;
94 a2 = ipi_mbox->remote_id;
95 arm_smccc_smc(a0, a1, a2, a3, 0, 0, 0, 0, &res);
96
97 return (int)res.a0;
98}
99
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100100static int zynqmp_ipi_send(struct mbox_chan *chan, const void *data)
101{
102 const struct zynqmp_ipi_msg *msg = (struct zynqmp_ipi_msg *)data;
103 struct zynqmp_ipi *zynqmp = dev_get_priv(chan->dev);
104 u32 ret;
105 u32 *mbx = (u32 *)zynqmp->local_req_regs;
106
107 for (size_t i = 0; i < msg->len; i++)
108 writel(msg->buf[i], &mbx[i]);
109
Tanmay Shahb7399062023-12-04 13:56:17 -0800110 /* Use SMC calls for Exception Level less than 3 where TF-A is available */
111 if (!IS_ENABLED(CONFIG_SPL_BUILD) && current_el() < 3) {
112 ret = zynqmp_ipi_fw_call(zynqmp, SMC_IPI_MAILBOX_NOTIFY, 0);
113
114 debug("%s, send %ld bytes\n", __func__, msg->len);
115
116 return ret;
117 }
118
Tanmay Shah26954072023-12-04 13:56:18 -0800119 /* Return if EL3 is not supported */
120 if (!zynqmp->el3_supported) {
121 dev_err(chan->dev, "mailbox in EL3 only supported for zynqmp");
122 return -EOPNOTSUPP;
123 }
124
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100125 /* Write trigger interrupt */
126 writel(IPI_BIT_MASK_PMU0, &ipi_int_apu->trig);
127
128 /* Wait until observation bit is cleared */
129 ret = wait_for_bit_le32(&ipi_int_apu->obs, IPI_BIT_MASK_PMU0, false,
Michal Simekac1c7a32020-10-05 15:23:00 +0200130 1000, false);
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100131
132 debug("%s, send %ld bytes\n", __func__, msg->len);
133 return ret;
134};
135
136static int zynqmp_ipi_recv(struct mbox_chan *chan, void *data)
137{
138 struct zynqmp_ipi_msg *msg = (struct zynqmp_ipi_msg *)data;
139 struct zynqmp_ipi *zynqmp = dev_get_priv(chan->dev);
140 u32 *mbx = (u32 *)zynqmp->local_res_regs;
Tanmay Shahb7399062023-12-04 13:56:17 -0800141 int ret = 0;
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100142
Ibai Erkiagaab07ce52020-08-04 23:17:32 +0100143 /*
144 * PMU Firmware does not trigger IPI interrupt for API call responses so
Tanmay Shahb7399062023-12-04 13:56:17 -0800145 * there is no need to check ISR flags for EL3.
Ibai Erkiagaab07ce52020-08-04 23:17:32 +0100146 */
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100147 for (size_t i = 0; i < msg->len; i++)
148 msg->buf[i] = readl(&mbx[i]);
149
Tanmay Shahb7399062023-12-04 13:56:17 -0800150 /* Ack to remote if EL is not 3 */
151 if (!IS_ENABLED(CONFIG_SPL_BUILD) && current_el() < 3) {
152 ret = zynqmp_ipi_fw_call(zynqmp, SMC_IPI_MAILBOX_ACK,
153 IPI_SMC_ACK_EIRQ_MASK);
154 }
155
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100156 debug("%s, recv %ld bytes\n", __func__, msg->len);
Tanmay Shahb7399062023-12-04 13:56:17 -0800157 return ret;
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100158};
159
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800160static int zynqmp_ipi_dest_probe(struct udevice *dev)
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100161{
162 struct zynqmp_ipi *zynqmp = dev_get_priv(dev);
163 struct resource res;
164 ofnode node;
Tanmay Shahb7399062023-12-04 13:56:17 -0800165 int ret;
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100166
167 debug("%s(dev=%p)\n", __func__, dev);
168
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800169 node = dev_ofnode(dev);
170
Tanmay Shah26954072023-12-04 13:56:18 -0800171 if (IS_ENABLED(CONFIG_SPL_BUILD) || of_machine_is_compatible("xlnx,zynqmp"))
172 zynqmp->el3_supported = true;
173
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800174 ret = dev_read_u32(dev->parent, "xlnx,ipi-id", &zynqmp->local_id);
Tanmay Shahb7399062023-12-04 13:56:17 -0800175 if (ret) {
176 dev_err(dev, "can't get local ipi id\n");
177 return ret;
178 }
179
180 ret = ofnode_read_u32(node, "xlnx,ipi-id", &zynqmp->remote_id);
181 if (ret) {
182 dev_err(dev, "can't get remote ipi id\n");
183 return ret;
184 }
185
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100186 if (ofnode_read_resource_byname(node, "local_request_region", &res)) {
187 dev_err(dev, "No reg property for local_request_region\n");
188 return -EINVAL;
189 };
190 zynqmp->local_req_regs = devm_ioremap(dev, res.start,
191 (res.start - res.end));
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800192 if (!zynqmp->local_req_regs)
193 return -EINVAL;
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100194
195 if (ofnode_read_resource_byname(node, "local_response_region", &res)) {
196 dev_err(dev, "No reg property for local_response_region\n");
197 return -EINVAL;
198 };
199 zynqmp->local_res_regs = devm_ioremap(dev, res.start,
200 (res.start - res.end));
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800201 if (!zynqmp->local_res_regs)
202 return -EINVAL;
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100203
204 if (ofnode_read_resource_byname(node, "remote_request_region", &res)) {
205 dev_err(dev, "No reg property for remote_request_region\n");
206 return -EINVAL;
207 };
208 zynqmp->remote_req_regs = devm_ioremap(dev, res.start,
209 (res.start - res.end));
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800210 if (!zynqmp->remote_req_regs)
211 return -EINVAL;
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100212
213 if (ofnode_read_resource_byname(node, "remote_response_region", &res)) {
214 dev_err(dev, "No reg property for remote_response_region\n");
215 return -EINVAL;
216 };
217 zynqmp->remote_res_regs = devm_ioremap(dev, res.start,
218 (res.start - res.end));
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800219 if (!zynqmp->remote_res_regs)
220 return -EINVAL;
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100221
222 return 0;
223};
224
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800225static int zynqmp_ipi_probe(struct udevice *dev)
226{
227 struct udevice *cdev;
228 ofnode cnode;
229 int ret;
230
231 debug("%s(dev=%p)\n", __func__, dev);
232
233 dev_for_each_subnode(cnode, dev) {
234 ret = device_bind_driver_to_node(dev, "zynqmp_ipi_dest",
235 ofnode_get_name(cnode),
236 cnode, &cdev);
237 if (ret)
238 return ret;
239 }
240
241 return 0;
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100242};
243
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800244struct mbox_ops zynqmp_ipi_dest_mbox_ops = {
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100245 .send = zynqmp_ipi_send,
246 .recv = zynqmp_ipi_recv,
247};
248
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800249static const struct udevice_id zynqmp_ipi_dest_ids[] = {
250 { .compatible = "xlnx,zynqmp-ipi-dest-mailbox" },
251 { }
252};
253
254U_BOOT_DRIVER(zynqmp_ipi_dest) = {
255 .name = "zynqmp_ipi_dest",
256 .id = UCLASS_MAILBOX,
257 .of_match = zynqmp_ipi_dest_ids,
258 .probe = zynqmp_ipi_dest_probe,
259 .priv_auto = sizeof(struct zynqmp_ipi),
260 .ops = &zynqmp_ipi_dest_mbox_ops,
261};
262
263static const struct udevice_id zynqmp_ipi_ids[] = {
264 { .compatible = "xlnx,zynqmp-ipi-mailbox" },
265 { }
266};
267
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100268U_BOOT_DRIVER(zynqmp_ipi) = {
Michal Simek33093082020-01-07 08:50:34 +0100269 .name = "zynqmp_ipi",
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800270 .id = UCLASS_NOP,
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100271 .of_match = zynqmp_ipi_ids,
272 .probe = zynqmp_ipi_probe,
Tanmay Shaha7c0d3f2023-12-04 13:56:19 -0800273 .flags = DM_FLAG_PROBE_AFTER_BIND,
Ibai Erkiaga4b1264d2019-09-27 11:36:56 +0100274};