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