blob: 2e90d25474b50ce7d39c91b32bd811f47041f37c [file] [log] [blame]
Varun Wadekarb3741032017-09-25 13:27:45 -07001/*
Varun Wadekar60701552020-01-10 16:52:23 -08002 * Copyright (c) 2017-2020, NVIDIA CORPORATION. All rights reserved.
Varun Wadekarb3741032017-09-25 13:27:45 -07003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <bpmp_ipc.h>
Varun Wadekar60701552020-01-10 16:52:23 -08009#include <common/debug.h>
Ambroise Vincentffbf32a2019-03-28 09:01:18 +000010#include <drivers/delay_timer.h>
Varun Wadekarb3741032017-09-25 13:27:45 -070011#include <errno.h>
Ambroise Vincentffbf32a2019-03-28 09:01:18 +000012#include <lib/mmio.h>
13#include <lib/utils_def.h>
Varun Wadekarb3741032017-09-25 13:27:45 -070014#include <stdbool.h>
15#include <string.h>
16#include <tegra_def.h>
Varun Wadekarb3741032017-09-25 13:27:45 -070017
18#include "intf.h"
19#include "ivc.h"
20
21/**
22 * Holds IVC channel data
23 */
24struct 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
32static struct ccplex_bpmp_channel_data s_channel;
33static struct ivc ivc_ccplex_bpmp_channel;
34
35/*
36 * Helper functions to access the HSP doorbell registers
37 */
38static inline uint32_t hsp_db_read(uint32_t reg)
39{
40 return mmio_read_32((uint32_t)(TEGRA_HSP_DBELL_BASE + reg));
41}
42
43static 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
52static void tegra_bpmp_ring_bpmp_doorbell(void);
53
54/*
55 * Get the next frame where data can be written.
56 */
57static 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
72static 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
78static int32_t tegra_bpmp_free_master(void)
79{
80 return tegra_ivc_read_advance(&ivc_ccplex_bpmp_channel);
81}
82
83static 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
98static 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 */
106static 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 */
118static 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 */
132static 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
142static 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 */
157static 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 */
167static 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__);
Varun Wadekar9f04ab32018-06-20 16:12:50 -0700178 return -EINVAL;
Varun Wadekarb3741032017-09-25 13:27:45 -0700179 }
180
Varun Wadekar9f04ab32018-06-20 16:12:50 -0700181 /* 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);
Varun Wadekarb3741032017-09-25 13:27:45 -0700186
Varun Wadekar9f04ab32018-06-20 16:12:50 -0700187 /* signal the slave */
188 tegra_bpmp_signal_slave();
Varun Wadekarb3741032017-09-25 13:27:45 -0700189
Varun Wadekar9f04ab32018-06-20 16:12:50 -0700190 /* 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 }
Varun Wadekarb3741032017-09-25 13:27:45 -0700196
Varun Wadekar9f04ab32018-06-20 16:12:50 -0700197 /* retrieve the response frame */
198 if ((size_in <= IVC_DATA_SZ_BYTES) && (p_in != NULL)) {
Varun Wadekarb3741032017-09-25 13:27:45 -0700199
Varun Wadekar9f04ab32018-06-20 16:12:50 -0700200 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);
Varun Wadekarb3741032017-09-25 13:27:45 -0700205 }
Varun Wadekar9f04ab32018-06-20 16:12:50 -0700206 }
Varun Wadekarb3741032017-09-25 13:27:45 -0700207
Varun Wadekar9f04ab32018-06-20 16:12:50 -0700208 ret = tegra_bpmp_free_master();
209 if (ret < 0) {
210 ERROR("%s: free master failed (%d)\n", __func__, ret);
Varun Wadekarb3741032017-09-25 13:27:45 -0700211 }
212
213 return ret;
214}
215
216/*
217 * Initializes the BPMP<--->CCPlex communication path.
218 */
219int32_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 */
279int32_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}
steven kao05ee5822018-01-02 19:07:00 -0800300
301int 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
Varun Wadekard11345a2018-05-25 14:34:53 -0700314 ret = tegra_bpmp_ipc_send_req_atomic(MRQ_CLK, &req, (uint32_t)sizeof(req),
steven kao05ee5822018-01-02 19:07:00 -0800315 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
324int 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
Varun Wadekard11345a2018-05-25 14:34:53 -0700337 ret = tegra_bpmp_ipc_send_req_atomic(MRQ_CLK, &req, (uint32_t)sizeof(req),
steven kao05ee5822018-01-02 19:07:00 -0800338 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}