2 * Copyright (c) 2017-2018, NVIDIA CORPORATION. All rights reserved.
4 * SPDX-License-Identifier: BSD-3-Clause
10 #include <drivers/delay_timer.h>
13 #include <lib/utils_def.h>
16 #include <tegra_def.h>
22 * Holds IVC channel data
24 struct ccplex_bpmp_channel_data
{
25 /* Buffer for incoming data */
26 struct frame_data
*ib
;
28 /* Buffer for outgoing data */
29 struct frame_data
*ob
;
32 static struct ccplex_bpmp_channel_data s_channel
;
33 static struct ivc ivc_ccplex_bpmp_channel
;
36 * Helper functions to access the HSP doorbell registers
38 static inline uint32_t hsp_db_read(uint32_t reg
)
40 return mmio_read_32((uint32_t)(TEGRA_HSP_DBELL_BASE
+ reg
));
43 static inline void hsp_db_write(uint32_t reg
, uint32_t val
)
45 mmio_write_32((uint32_t)(TEGRA_HSP_DBELL_BASE
+ reg
), val
);
48 /*******************************************************************************
49 * IVC wrappers for CCPLEX <-> BPMP communication.
50 ******************************************************************************/
52 static void tegra_bpmp_ring_bpmp_doorbell(void);
55 * Get the next frame where data can be written.
57 static struct frame_data
*tegra_bpmp_get_next_out_frame(void)
59 struct frame_data
*frame
;
60 const struct ivc
*ch
= &ivc_ccplex_bpmp_channel
;
62 frame
= (struct frame_data
*)tegra_ivc_write_get_next_frame(ch
);
64 ERROR("%s: Error in getting next frame, exiting\n", __func__
);
72 static void tegra_bpmp_signal_slave(void)
74 (void)tegra_ivc_write_advance(&ivc_ccplex_bpmp_channel
);
75 tegra_bpmp_ring_bpmp_doorbell();
78 static int32_t tegra_bpmp_free_master(void)
80 return tegra_ivc_read_advance(&ivc_ccplex_bpmp_channel
);
83 static bool tegra_bpmp_slave_acked(void)
85 struct frame_data
*frame
;
88 frame
= (struct frame_data
*)tegra_ivc_read_get_next_frame(&ivc_ccplex_bpmp_channel
);
98 static struct frame_data
*tegra_bpmp_get_cur_in_frame(void)
104 * Enables BPMP to ring CCPlex doorbell
106 static void tegra_bpmp_enable_ccplex_doorbell(void)
110 reg
= hsp_db_read(HSP_DBELL_1_ENABLE
);
111 reg
|= HSP_MASTER_BPMP_BIT
;
112 hsp_db_write(HSP_DBELL_1_ENABLE
, reg
);
116 * CCPlex rings the BPMP doorbell
118 static void tegra_bpmp_ring_bpmp_doorbell(void)
121 * Any writes to this register has the same effect,
122 * uses master ID of the write transaction and set
123 * corresponding flag.
125 hsp_db_write(HSP_DBELL_3_TRIGGER
, HSP_MASTER_CCPLEX_BIT
);
129 * Returns true if CCPLex can ring BPMP doorbell, otherwise false.
130 * This also signals that BPMP is up and ready.
132 static bool tegra_bpmp_can_ccplex_ring_doorbell(void)
136 /* check if ccplex can communicate with bpmp */
137 reg
= hsp_db_read(HSP_DBELL_3_ENABLE
);
139 return ((reg
& HSP_MASTER_CCPLEX_BIT
) != 0U);
142 static int32_t tegra_bpmp_wait_for_slave_ack(void)
144 uint32_t timeout
= TIMEOUT_RESPONSE_FROM_BPMP_US
;
146 while (!tegra_bpmp_slave_acked() && (timeout
!= 0U)) {
151 return ((timeout
== 0U) ? -ETIMEDOUT
: 0);
155 * Notification from the ivc layer
157 static void tegra_bpmp_ivc_notify(const struct ivc
*ivc
)
161 tegra_bpmp_ring_bpmp_doorbell();
165 * Atomic send/receive API, which means it waits until slave acks
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
)
170 struct frame_data
*frame
= tegra_bpmp_get_next_out_frame();
171 const struct frame_data
*f_in
= NULL
;
175 if ((p_out
== NULL
) || (size_out
> IVC_DATA_SZ_BYTES
) ||
177 ERROR("%s: invalid parameters, exiting\n", __func__
);
183 /* prepare the command frame */
185 frame
->flags
= FLAG_DO_ACK
;
186 p_fdata
= frame
->data
;
187 (void)memcpy(p_fdata
, p_out
, (size_t)size_out
);
189 /* signal the slave */
190 tegra_bpmp_signal_slave();
192 /* wait for slave to ack */
193 ret
= tegra_bpmp_wait_for_slave_ack();
195 ERROR("failed waiting for the slave to ack\n");
198 /* retrieve the response frame */
199 if ((size_in
<= IVC_DATA_SZ_BYTES
) && (p_in
!= NULL
) &&
202 f_in
= tegra_bpmp_get_cur_in_frame();
204 ERROR("Failed to get next input frame!\n");
206 (void)memcpy(p_in
, p_fdata
, (size_t)size_in
);
211 ret
= tegra_bpmp_free_master();
213 ERROR("Failed to free master\n");
222 * Initializes the BPMP<--->CCPlex communication path.
224 int32_t tegra_bpmp_ipc_init(void)
227 uint32_t frame_size
, timeout
;
230 /* allow bpmp to ring CCPLEX's doorbell */
231 tegra_bpmp_enable_ccplex_doorbell();
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 */
241 ERROR("%s: BPMP firmware is not ready\n", __func__
);
245 INFO("%s: BPMP handshake completed\n", __func__
);
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__
);
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
);
260 ERROR("%s: IVC init failed (%d)\n", __func__
, error
);
265 tegra_ivc_channel_reset(&ivc_ccplex_bpmp_channel
);
267 /* wait for notification from BPMP */
268 while (tegra_ivc_channel_notified(&ivc_ccplex_bpmp_channel
) != 0) {
270 * Interrupt BPMP with doorbell each time after
271 * tegra_ivc_channel_notified() returns non zero
274 tegra_bpmp_ring_bpmp_doorbell();
277 INFO("%s: All communication channels initialized\n", __func__
);
283 /* Handler to reset a hardware module */
284 int32_t tegra_bpmp_ipc_reset_module(uint32_t rst_id
)
287 struct mrq_reset_request req
= {
288 .cmd
= (uint32_t)CMD_RESET_MODULE
,
292 /* only GPCDMA/XUSB_PADCTL resets are supported */
293 assert((rst_id
== TEGRA_RESET_ID_XUSB_PADCTL
) ||
294 (rst_id
== TEGRA_RESET_ID_GPCDMA
));
296 ret
= tegra_bpmp_ipc_send_req_atomic(MRQ_RESET
, &req
,
297 (uint32_t)sizeof(req
), NULL
, 0);
299 ERROR("%s: failed for module %d with error %d\n", __func__
,
306 int tegra_bpmp_ipc_enable_clock(uint32_t clk_id
)
309 struct mrq_clk_request req
;
311 /* only SE clocks are supported */
312 if (clk_id
!= TEGRA_CLK_SE
) {
316 /* prepare the MRQ_CLK command */
317 req
.cmd_and_id
= make_mrq_clk_cmd(CMD_CLK_ENABLE
, clk_id
);
319 ret
= tegra_bpmp_ipc_send_req_atomic(MRQ_CLK
, &req
, sizeof(req
),
322 ERROR("%s: failed for module %d with error %d\n", __func__
,
329 int tegra_bpmp_ipc_disable_clock(uint32_t clk_id
)
332 struct mrq_clk_request req
;
334 /* only SE clocks are supported */
335 if (clk_id
!= TEGRA_CLK_SE
) {
339 /* prepare the MRQ_CLK command */
340 req
.cmd_and_id
= make_mrq_clk_cmd(CMD_CLK_DISABLE
, clk_id
);
342 ret
= tegra_bpmp_ipc_send_req_atomic(MRQ_CLK
, &req
, sizeof(req
),
345 ERROR("%s: failed for module %d with error %d\n", __func__
,