blob: acd0b287dba57a8fa84d0647c7810b119498d2c3 [file] [log] [blame]
Ibai Erkiaga660b0c72019-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
8#include <common.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -06009#include <log.h>
Ibai Erkiaga660b0c72019-09-27 11:36:56 +010010#include <asm/io.h>
Tanmay Shahcfb41b02023-12-04 13:56:17 -080011#include <asm/system.h>
Ibai Erkiaga660b0c72019-09-27 11:36:56 +010012#include <dm.h>
13#include <mailbox-uclass.h>
Simon Glass336d4612020-02-03 07:36:16 -070014#include <dm/device_compat.h>
Tanmay Shahcfb41b02023-12-04 13:56:17 -080015#include <dm/of_access.h>
16#include <linux/arm-smccc.h>
Ibai Erkiaga660b0c72019-09-27 11:36:56 +010017#include <linux/ioport.h>
18#include <linux/io.h>
19#include <wait_bit.h>
Ashok Reddy Somacce33512022-07-22 02:46:57 -060020#include <zynqmp_firmware.h>
Ibai Erkiaga660b0c72019-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 Shahcfb41b02023-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 Erkiaga660b0c72019-09-27 11:36:56 +010064struct ipi_int_regs {
65 u32 trig; /* 0x0 */
66 u32 obs; /* 0x4 */
Ibai Erkiagade4f7482020-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 Erkiaga660b0c72019-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 Shahcfb41b02023-12-04 13:56:17 -080082 u32 remote_id;
83 u32 local_id;
Ibai Erkiaga660b0c72019-09-27 11:36:56 +010084};
85
Tanmay Shahcfb41b02023-12-04 13:56:17 -080086static int zynqmp_ipi_fw_call(struct zynqmp_ipi *ipi_mbox,
87 unsigned long a0, unsigned long a3)
88{
89 struct arm_smccc_res res = {0};
90 unsigned long a1, a2;
91
92 a1 = ipi_mbox->local_id;
93 a2 = ipi_mbox->remote_id;
94 arm_smccc_smc(a0, a1, a2, a3, 0, 0, 0, 0, &res);
95
96 return (int)res.a0;
97}
98
Ibai Erkiaga660b0c72019-09-27 11:36:56 +010099static int zynqmp_ipi_send(struct mbox_chan *chan, const void *data)
100{
101 const struct zynqmp_ipi_msg *msg = (struct zynqmp_ipi_msg *)data;
102 struct zynqmp_ipi *zynqmp = dev_get_priv(chan->dev);
103 u32 ret;
104 u32 *mbx = (u32 *)zynqmp->local_req_regs;
105
106 for (size_t i = 0; i < msg->len; i++)
107 writel(msg->buf[i], &mbx[i]);
108
Tanmay Shahcfb41b02023-12-04 13:56:17 -0800109 /* Use SMC calls for Exception Level less than 3 where TF-A is available */
110 if (!IS_ENABLED(CONFIG_SPL_BUILD) && current_el() < 3) {
111 ret = zynqmp_ipi_fw_call(zynqmp, SMC_IPI_MAILBOX_NOTIFY, 0);
112
113 debug("%s, send %ld bytes\n", __func__, msg->len);
114
115 return ret;
116 }
117
Ibai Erkiaga660b0c72019-09-27 11:36:56 +0100118 /* Write trigger interrupt */
119 writel(IPI_BIT_MASK_PMU0, &ipi_int_apu->trig);
120
121 /* Wait until observation bit is cleared */
122 ret = wait_for_bit_le32(&ipi_int_apu->obs, IPI_BIT_MASK_PMU0, false,
Michal Simekf08d0c52020-10-05 15:23:00 +0200123 1000, false);
Ibai Erkiaga660b0c72019-09-27 11:36:56 +0100124
125 debug("%s, send %ld bytes\n", __func__, msg->len);
126 return ret;
127};
128
129static int zynqmp_ipi_recv(struct mbox_chan *chan, void *data)
130{
131 struct zynqmp_ipi_msg *msg = (struct zynqmp_ipi_msg *)data;
132 struct zynqmp_ipi *zynqmp = dev_get_priv(chan->dev);
133 u32 *mbx = (u32 *)zynqmp->local_res_regs;
Tanmay Shahcfb41b02023-12-04 13:56:17 -0800134 int ret = 0;
Ibai Erkiaga660b0c72019-09-27 11:36:56 +0100135
Ibai Erkiagade4f7482020-08-04 23:17:32 +0100136 /*
137 * PMU Firmware does not trigger IPI interrupt for API call responses so
Tanmay Shahcfb41b02023-12-04 13:56:17 -0800138 * there is no need to check ISR flags for EL3.
Ibai Erkiagade4f7482020-08-04 23:17:32 +0100139 */
Ibai Erkiaga660b0c72019-09-27 11:36:56 +0100140 for (size_t i = 0; i < msg->len; i++)
141 msg->buf[i] = readl(&mbx[i]);
142
Tanmay Shahcfb41b02023-12-04 13:56:17 -0800143 /* Ack to remote if EL is not 3 */
144 if (!IS_ENABLED(CONFIG_SPL_BUILD) && current_el() < 3) {
145 ret = zynqmp_ipi_fw_call(zynqmp, SMC_IPI_MAILBOX_ACK,
146 IPI_SMC_ACK_EIRQ_MASK);
147 }
148
Ibai Erkiaga660b0c72019-09-27 11:36:56 +0100149 debug("%s, recv %ld bytes\n", __func__, msg->len);
Tanmay Shahcfb41b02023-12-04 13:56:17 -0800150 return ret;
Ibai Erkiaga660b0c72019-09-27 11:36:56 +0100151};
152
153static int zynqmp_ipi_probe(struct udevice *dev)
154{
155 struct zynqmp_ipi *zynqmp = dev_get_priv(dev);
156 struct resource res;
157 ofnode node;
Tanmay Shahcfb41b02023-12-04 13:56:17 -0800158 int ret;
Ibai Erkiaga660b0c72019-09-27 11:36:56 +0100159
160 debug("%s(dev=%p)\n", __func__, dev);
161
162 /* Get subnode where the regs are defined */
163 /* Note IPI mailbox node needs to be the first one in DT */
164 node = ofnode_first_subnode(dev_ofnode(dev));
165
Tanmay Shahcfb41b02023-12-04 13:56:17 -0800166 ret = dev_read_u32(dev, "xlnx,ipi-id", &zynqmp->local_id);
167 if (ret) {
168 dev_err(dev, "can't get local ipi id\n");
169 return ret;
170 }
171
172 ret = ofnode_read_u32(node, "xlnx,ipi-id", &zynqmp->remote_id);
173 if (ret) {
174 dev_err(dev, "can't get remote ipi id\n");
175 return ret;
176 }
177
Ibai Erkiaga660b0c72019-09-27 11:36:56 +0100178 if (ofnode_read_resource_byname(node, "local_request_region", &res)) {
179 dev_err(dev, "No reg property for local_request_region\n");
180 return -EINVAL;
181 };
182 zynqmp->local_req_regs = devm_ioremap(dev, res.start,
183 (res.start - res.end));
184
185 if (ofnode_read_resource_byname(node, "local_response_region", &res)) {
186 dev_err(dev, "No reg property for local_response_region\n");
187 return -EINVAL;
188 };
189 zynqmp->local_res_regs = devm_ioremap(dev, res.start,
190 (res.start - res.end));
191
192 if (ofnode_read_resource_byname(node, "remote_request_region", &res)) {
193 dev_err(dev, "No reg property for remote_request_region\n");
194 return -EINVAL;
195 };
196 zynqmp->remote_req_regs = devm_ioremap(dev, res.start,
197 (res.start - res.end));
198
199 if (ofnode_read_resource_byname(node, "remote_response_region", &res)) {
200 dev_err(dev, "No reg property for remote_response_region\n");
201 return -EINVAL;
202 };
203 zynqmp->remote_res_regs = devm_ioremap(dev, res.start,
204 (res.start - res.end));
205
206 return 0;
207};
208
209static const struct udevice_id zynqmp_ipi_ids[] = {
210 { .compatible = "xlnx,zynqmp-ipi-mailbox" },
211 { }
212};
213
214struct mbox_ops zynqmp_ipi_mbox_ops = {
215 .send = zynqmp_ipi_send,
216 .recv = zynqmp_ipi_recv,
217};
218
219U_BOOT_DRIVER(zynqmp_ipi) = {
Michal Simek6c0e59f2020-01-07 08:50:34 +0100220 .name = "zynqmp_ipi",
Ibai Erkiaga660b0c72019-09-27 11:36:56 +0100221 .id = UCLASS_MAILBOX,
222 .of_match = zynqmp_ipi_ids,
223 .probe = zynqmp_ipi_probe,
Simon Glass41575d82020-12-03 16:55:17 -0700224 .priv_auto = sizeof(struct zynqmp_ipi),
Ibai Erkiaga660b0c72019-09-27 11:36:56 +0100225 .ops = &zynqmp_ipi_mbox_ops,
226};