1 /*
2 * Copyright (c) 2017-2020, 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 <common/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 */
hsp_db_read(uint32_t reg)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
hsp_db_write(uint32_t reg,uint32_t val)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 */
tegra_bpmp_get_next_out_frame(void)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
tegra_bpmp_signal_slave(void)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
tegra_bpmp_free_master(void)78 static int32_t tegra_bpmp_free_master(void)
79 {
80 return tegra_ivc_read_advance(&ivc_ccplex_bpmp_channel);
81 }
82
tegra_bpmp_slave_acked(void)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
tegra_bpmp_get_cur_in_frame(void)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 */
tegra_bpmp_enable_ccplex_doorbell(void)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 */
tegra_bpmp_ring_bpmp_doorbell(void)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 */
tegra_bpmp_can_ccplex_ring_doorbell(void)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
tegra_bpmp_wait_for_slave_ack(void)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 */
tegra_bpmp_ivc_notify(const struct ivc * ivc)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 */
tegra_bpmp_ipc_send_req_atomic(uint32_t mrq,void * p_out,uint32_t size_out,void * p_in,uint32_t size_in)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 return -EINVAL;
179 }
180
181 /* prepare the command frame */
182 frame->mrq = mrq;
183 frame->flags = FLAG_DO_ACK;
184 p_fdata = frame->data;
185 (void)memcpy(p_fdata, p_out, (size_t)size_out);
186
187 /* signal the slave */
188 tegra_bpmp_signal_slave();
189
190 /* wait for slave to ack */
191 ret = tegra_bpmp_wait_for_slave_ack();
192 if (ret < 0) {
193 ERROR("%s: wait for slave failed (%d)\n", __func__, ret);
194 return ret;
195 }
196
197 /* retrieve the response frame */
198 if ((size_in <= IVC_DATA_SZ_BYTES) && (p_in != NULL)) {
199
200 f_in = tegra_bpmp_get_cur_in_frame();
201 if (f_in != NULL) {
202 ERROR("Failed to get next input frame!\n");
203 } else {
204 (void)memcpy(p_in, p_fdata, (size_t)size_in);
205 }
206 }
207
208 ret = tegra_bpmp_free_master();
209 if (ret < 0) {
210 ERROR("%s: free master failed (%d)\n", __func__, ret);
211 }
212
213 return ret;
214 }
215
216 /*
217 * Initializes the BPMP<--->CCPlex communication path.
218 */
tegra_bpmp_ipc_init(void)219 int32_t tegra_bpmp_ipc_init(void)
220 {
221 size_t msg_size;
222 uint32_t frame_size, timeout;
223 int32_t error = 0;
224
225 /* allow bpmp to ring CCPLEX's doorbell */
226 tegra_bpmp_enable_ccplex_doorbell();
227
228 /* wait for BPMP to actually ring the doorbell */
229 timeout = TIMEOUT_RESPONSE_FROM_BPMP_US;
230 while ((timeout != 0U) && !tegra_bpmp_can_ccplex_ring_doorbell()) {
231 udelay(1); /* bpmp turn-around time */
232 timeout--;
233 }
234
235 if (timeout == 0U) {
236 ERROR("%s: BPMP firmware is not ready\n", __func__);
237 return -ENOTSUP;
238 }
239
240 INFO("%s: BPMP handshake completed\n", __func__);
241
242 msg_size = tegra_ivc_align(IVC_CMD_SZ_BYTES);
243 frame_size = (uint32_t)tegra_ivc_total_queue_size(msg_size);
244 if (frame_size > TEGRA_BPMP_IPC_CH_MAP_SIZE) {
245 ERROR("%s: carveout size is not sufficient\n", __func__);
246 return -EINVAL;
247 }
248
249 error = tegra_ivc_init(&ivc_ccplex_bpmp_channel,
250 (uint32_t)TEGRA_BPMP_IPC_RX_PHYS_BASE,
251 (uint32_t)TEGRA_BPMP_IPC_TX_PHYS_BASE,
252 1U, frame_size, tegra_bpmp_ivc_notify);
253 if (error != 0) {
254
255 ERROR("%s: IVC init failed (%d)\n", __func__, error);
256
257 } else {
258
259 /* reset channel */
260 tegra_ivc_channel_reset(&ivc_ccplex_bpmp_channel);
261
262 /* wait for notification from BPMP */
263 while (tegra_ivc_channel_notified(&ivc_ccplex_bpmp_channel) != 0) {
264 /*
265 * Interrupt BPMP with doorbell each time after
266 * tegra_ivc_channel_notified() returns non zero
267 * value.
268 */
269 tegra_bpmp_ring_bpmp_doorbell();
270 }
271
272 INFO("%s: All communication channels initialized\n", __func__);
273 }
274
275 return error;
276 }
277
278 /* Handler to reset a hardware module */
tegra_bpmp_ipc_reset_module(uint32_t rst_id)279 int32_t tegra_bpmp_ipc_reset_module(uint32_t rst_id)
280 {
281 int32_t ret;
282 struct mrq_reset_request req = {
283 .cmd = (uint32_t)CMD_RESET_MODULE,
284 .reset_id = rst_id
285 };
286
287 /* only GPCDMA/XUSB_PADCTL resets are supported */
288 assert((rst_id == TEGRA_RESET_ID_XUSB_PADCTL) ||
289 (rst_id == TEGRA_RESET_ID_GPCDMA));
290
291 ret = tegra_bpmp_ipc_send_req_atomic(MRQ_RESET, &req,
292 (uint32_t)sizeof(req), NULL, 0);
293 if (ret != 0) {
294 ERROR("%s: failed for module %d with error %d\n", __func__,
295 rst_id, ret);
296 }
297
298 return ret;
299 }
300
tegra_bpmp_ipc_enable_clock(uint32_t clk_id)301 int tegra_bpmp_ipc_enable_clock(uint32_t clk_id)
302 {
303 int ret;
304 struct mrq_clk_request req;
305
306 /* only SE clocks are supported */
307 if (clk_id != TEGRA_CLK_SE) {
308 return -ENOTSUP;
309 }
310
311 /* prepare the MRQ_CLK command */
312 req.cmd_and_id = make_mrq_clk_cmd(CMD_CLK_ENABLE, clk_id);
313
314 ret = tegra_bpmp_ipc_send_req_atomic(MRQ_CLK, &req, (uint32_t)sizeof(req),
315 NULL, 0);
316 if (ret != 0) {
317 ERROR("%s: failed for module %d with error %d\n", __func__,
318 clk_id, ret);
319 }
320
321 return ret;
322 }
323
tegra_bpmp_ipc_disable_clock(uint32_t clk_id)324 int tegra_bpmp_ipc_disable_clock(uint32_t clk_id)
325 {
326 int ret;
327 struct mrq_clk_request req;
328
329 /* only SE clocks are supported */
330 if (clk_id != TEGRA_CLK_SE) {
331 return -ENOTSUP;
332 }
333
334 /* prepare the MRQ_CLK command */
335 req.cmd_and_id = make_mrq_clk_cmd(CMD_CLK_DISABLE, clk_id);
336
337 ret = tegra_bpmp_ipc_send_req_atomic(MRQ_CLK, &req, (uint32_t)sizeof(req),
338 NULL, 0);
339 if (ret != 0) {
340 ERROR("%s: failed for module %d with error %d\n", __func__,
341 clk_id, ret);
342 }
343
344 return ret;
345 }
346