blob: eaf96751ceb851601eae0620d1259ec32179ed82 [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__);
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 */
224int32_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 */
284int32_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}
steven kao05ee5822018-01-02 19:07:00 -0800305
306int 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
Varun Wadekard11345a2018-05-25 14:34:53 -0700319 ret = tegra_bpmp_ipc_send_req_atomic(MRQ_CLK, &req, (uint32_t)sizeof(req),
steven kao05ee5822018-01-02 19:07:00 -0800320 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
329int 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
Varun Wadekard11345a2018-05-25 14:34:53 -0700342 ret = tegra_bpmp_ipc_send_req_atomic(MRQ_CLK, &req, (uint32_t)sizeof(req),
steven kao05ee5822018-01-02 19:07:00 -0800343 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}