Makefile: remove extra include paths in INCLUDES
[project/bcm63xx/atf.git] / plat / nvidia / tegra / common / drivers / bpmp_ipc / intf.c
1 /*
2 * Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <assert.h>
8 #include <bpmp_ipc.h>
9 #include <debug.h>
10 #include <drivers/delay_timer.h>
11 #include <errno.h>
12 #include <lib/mmio.h>
13 #include <lib/utils_def.h>
14 #include <stdbool.h>
15 #include <string.h>
16 #include <tegra_def.h>
17
18 #include "intf.h"
19 #include "ivc.h"
20
21 /**
22 * Holds IVC channel data
23 */
24 struct ccplex_bpmp_channel_data {
25 /* Buffer for incoming data */
26 struct frame_data *ib;
27
28 /* Buffer for outgoing data */
29 struct frame_data *ob;
30 };
31
32 static struct ccplex_bpmp_channel_data s_channel;
33 static struct ivc ivc_ccplex_bpmp_channel;
34
35 /*
36 * Helper functions to access the HSP doorbell registers
37 */
38 static inline uint32_t hsp_db_read(uint32_t reg)
39 {
40 return mmio_read_32((uint32_t)(TEGRA_HSP_DBELL_BASE + reg));
41 }
42
43 static inline void hsp_db_write(uint32_t reg, uint32_t val)
44 {
45 mmio_write_32((uint32_t)(TEGRA_HSP_DBELL_BASE + reg), val);
46 }
47
48 /*******************************************************************************
49 * IVC wrappers for CCPLEX <-> BPMP communication.
50 ******************************************************************************/
51
52 static void tegra_bpmp_ring_bpmp_doorbell(void);
53
54 /*
55 * Get the next frame where data can be written.
56 */
57 static struct frame_data *tegra_bpmp_get_next_out_frame(void)
58 {
59 struct frame_data *frame;
60 const struct ivc *ch = &ivc_ccplex_bpmp_channel;
61
62 frame = (struct frame_data *)tegra_ivc_write_get_next_frame(ch);
63 if (frame == NULL) {
64 ERROR("%s: Error in getting next frame, exiting\n", __func__);
65 } else {
66 s_channel.ob = frame;
67 }
68
69 return frame;
70 }
71
72 static void tegra_bpmp_signal_slave(void)
73 {
74 (void)tegra_ivc_write_advance(&ivc_ccplex_bpmp_channel);
75 tegra_bpmp_ring_bpmp_doorbell();
76 }
77
78 static int32_t tegra_bpmp_free_master(void)
79 {
80 return tegra_ivc_read_advance(&ivc_ccplex_bpmp_channel);
81 }
82
83 static bool tegra_bpmp_slave_acked(void)
84 {
85 struct frame_data *frame;
86 bool ret = true;
87
88 frame = (struct frame_data *)tegra_ivc_read_get_next_frame(&ivc_ccplex_bpmp_channel);
89 if (frame == NULL) {
90 ret = false;
91 } else {
92 s_channel.ib = frame;
93 }
94
95 return ret;
96 }
97
98 static struct frame_data *tegra_bpmp_get_cur_in_frame(void)
99 {
100 return s_channel.ib;
101 }
102
103 /*
104 * Enables BPMP to ring CCPlex doorbell
105 */
106 static void tegra_bpmp_enable_ccplex_doorbell(void)
107 {
108 uint32_t reg;
109
110 reg = hsp_db_read(HSP_DBELL_1_ENABLE);
111 reg |= HSP_MASTER_BPMP_BIT;
112 hsp_db_write(HSP_DBELL_1_ENABLE, reg);
113 }
114
115 /*
116 * CCPlex rings the BPMP doorbell
117 */
118 static void tegra_bpmp_ring_bpmp_doorbell(void)
119 {
120 /*
121 * Any writes to this register has the same effect,
122 * uses master ID of the write transaction and set
123 * corresponding flag.
124 */
125 hsp_db_write(HSP_DBELL_3_TRIGGER, HSP_MASTER_CCPLEX_BIT);
126 }
127
128 /*
129 * Returns true if CCPLex can ring BPMP doorbell, otherwise false.
130 * This also signals that BPMP is up and ready.
131 */
132 static bool tegra_bpmp_can_ccplex_ring_doorbell(void)
133 {
134 uint32_t reg;
135
136 /* check if ccplex can communicate with bpmp */
137 reg = hsp_db_read(HSP_DBELL_3_ENABLE);
138
139 return ((reg & HSP_MASTER_CCPLEX_BIT) != 0U);
140 }
141
142 static int32_t tegra_bpmp_wait_for_slave_ack(void)
143 {
144 uint32_t timeout = TIMEOUT_RESPONSE_FROM_BPMP_US;
145
146 while (!tegra_bpmp_slave_acked() && (timeout != 0U)) {
147 udelay(1);
148 timeout--;
149 };
150
151 return ((timeout == 0U) ? -ETIMEDOUT : 0);
152 }
153
154 /*
155 * Notification from the ivc layer
156 */
157 static void tegra_bpmp_ivc_notify(const struct ivc *ivc)
158 {
159 (void)(ivc);
160
161 tegra_bpmp_ring_bpmp_doorbell();
162 }
163
164 /*
165 * Atomic send/receive API, which means it waits until slave acks
166 */
167 static int32_t tegra_bpmp_ipc_send_req_atomic(uint32_t mrq, void *p_out,
168 uint32_t size_out, void *p_in, uint32_t size_in)
169 {
170 struct frame_data *frame = tegra_bpmp_get_next_out_frame();
171 const struct frame_data *f_in = NULL;
172 int32_t ret = 0;
173 void *p_fdata;
174
175 if ((p_out == NULL) || (size_out > IVC_DATA_SZ_BYTES) ||
176 (frame == NULL)) {
177 ERROR("%s: invalid parameters, exiting\n", __func__);
178 ret = -EINVAL;
179 }
180
181 if (ret == 0) {
182
183 /* prepare the command frame */
184 frame->mrq = mrq;
185 frame->flags = FLAG_DO_ACK;
186 p_fdata = frame->data;
187 (void)memcpy(p_fdata, p_out, (size_t)size_out);
188
189 /* signal the slave */
190 tegra_bpmp_signal_slave();
191
192 /* wait for slave to ack */
193 ret = tegra_bpmp_wait_for_slave_ack();
194 if (ret != 0) {
195 ERROR("failed waiting for the slave to ack\n");
196 }
197
198 /* retrieve the response frame */
199 if ((size_in <= IVC_DATA_SZ_BYTES) && (p_in != NULL) &&
200 (ret == 0)) {
201
202 f_in = tegra_bpmp_get_cur_in_frame();
203 if (f_in != NULL) {
204 ERROR("Failed to get next input frame!\n");
205 } else {
206 (void)memcpy(p_in, p_fdata, (size_t)size_in);
207 }
208 }
209
210 if (ret == 0) {
211 ret = tegra_bpmp_free_master();
212 if (ret != 0) {
213 ERROR("Failed to free master\n");
214 }
215 }
216 }
217
218 return ret;
219 }
220
221 /*
222 * Initializes the BPMP<--->CCPlex communication path.
223 */
224 int32_t tegra_bpmp_ipc_init(void)
225 {
226 size_t msg_size;
227 uint32_t frame_size, timeout;
228 int32_t error = 0;
229
230 /* allow bpmp to ring CCPLEX's doorbell */
231 tegra_bpmp_enable_ccplex_doorbell();
232
233 /* wait for BPMP to actually ring the doorbell */
234 timeout = TIMEOUT_RESPONSE_FROM_BPMP_US;
235 while ((timeout != 0U) && !tegra_bpmp_can_ccplex_ring_doorbell()) {
236 udelay(1); /* bpmp turn-around time */
237 timeout--;
238 }
239
240 if (timeout == 0U) {
241 ERROR("%s: BPMP firmware is not ready\n", __func__);
242 return -ENOTSUP;
243 }
244
245 INFO("%s: BPMP handshake completed\n", __func__);
246
247 msg_size = tegra_ivc_align(IVC_CMD_SZ_BYTES);
248 frame_size = (uint32_t)tegra_ivc_total_queue_size(msg_size);
249 if (frame_size > TEGRA_BPMP_IPC_CH_MAP_SIZE) {
250 ERROR("%s: carveout size is not sufficient\n", __func__);
251 return -EINVAL;
252 }
253
254 error = tegra_ivc_init(&ivc_ccplex_bpmp_channel,
255 (uint32_t)TEGRA_BPMP_IPC_RX_PHYS_BASE,
256 (uint32_t)TEGRA_BPMP_IPC_TX_PHYS_BASE,
257 1U, frame_size, tegra_bpmp_ivc_notify);
258 if (error != 0) {
259
260 ERROR("%s: IVC init failed (%d)\n", __func__, error);
261
262 } else {
263
264 /* reset channel */
265 tegra_ivc_channel_reset(&ivc_ccplex_bpmp_channel);
266
267 /* wait for notification from BPMP */
268 while (tegra_ivc_channel_notified(&ivc_ccplex_bpmp_channel) != 0) {
269 /*
270 * Interrupt BPMP with doorbell each time after
271 * tegra_ivc_channel_notified() returns non zero
272 * value.
273 */
274 tegra_bpmp_ring_bpmp_doorbell();
275 }
276
277 INFO("%s: All communication channels initialized\n", __func__);
278 }
279
280 return error;
281 }
282
283 /* Handler to reset a hardware module */
284 int32_t tegra_bpmp_ipc_reset_module(uint32_t rst_id)
285 {
286 int32_t ret;
287 struct mrq_reset_request req = {
288 .cmd = (uint32_t)CMD_RESET_MODULE,
289 .reset_id = rst_id
290 };
291
292 /* only GPCDMA/XUSB_PADCTL resets are supported */
293 assert((rst_id == TEGRA_RESET_ID_XUSB_PADCTL) ||
294 (rst_id == TEGRA_RESET_ID_GPCDMA));
295
296 ret = tegra_bpmp_ipc_send_req_atomic(MRQ_RESET, &req,
297 (uint32_t)sizeof(req), NULL, 0);
298 if (ret != 0) {
299 ERROR("%s: failed for module %d with error %d\n", __func__,
300 rst_id, ret);
301 }
302
303 return ret;
304 }
305
306 int tegra_bpmp_ipc_enable_clock(uint32_t clk_id)
307 {
308 int ret;
309 struct mrq_clk_request req;
310
311 /* only SE clocks are supported */
312 if (clk_id != TEGRA_CLK_SE) {
313 return -ENOTSUP;
314 }
315
316 /* prepare the MRQ_CLK command */
317 req.cmd_and_id = make_mrq_clk_cmd(CMD_CLK_ENABLE, clk_id);
318
319 ret = tegra_bpmp_ipc_send_req_atomic(MRQ_CLK, &req, sizeof(req),
320 NULL, 0);
321 if (ret != 0) {
322 ERROR("%s: failed for module %d with error %d\n", __func__,
323 clk_id, ret);
324 }
325
326 return ret;
327 }
328
329 int tegra_bpmp_ipc_disable_clock(uint32_t clk_id)
330 {
331 int ret;
332 struct mrq_clk_request req;
333
334 /* only SE clocks are supported */
335 if (clk_id != TEGRA_CLK_SE) {
336 return -ENOTSUP;
337 }
338
339 /* prepare the MRQ_CLK command */
340 req.cmd_and_id = make_mrq_clk_cmd(CMD_CLK_DISABLE, clk_id);
341
342 ret = tegra_bpmp_ipc_send_req_atomic(MRQ_CLK, &req, sizeof(req),
343 NULL, 0);
344 if (ret != 0) {
345 ERROR("%s: failed for module %d with error %d\n", __func__,
346 clk_id, ret);
347 }
348
349 return ret;
350 }