blob: 118c608ecd6c621a62143fa61bb6d239a13984aa [file] [log] [blame]
Aziz IDOMARd3fbce72023-12-19 19:43:52 +00001/*
2 * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <stdbool.h>
9#include <stdint.h>
10
11#include "mhu_v3_x.h"
12
13#include "mhu_v3_x_private.h"
14
15/*
16 * Get the device base from the device struct. Return an error if the dev is
17 * invalid.
18 */
19static enum mhu_v3_x_error_t get_dev_base(const struct mhu_v3_x_dev_t *dev,
20 union _mhu_v3_x_frame_t **base)
21{
22 if (dev == NULL) {
23 return MHU_V_3_X_ERR_INVALID_PARAM;
24 }
25
26 /* Ensure driver has been initialized */
27 if (dev->is_initialized == false) {
28 return MHU_V_3_X_ERR_NOT_INIT;
29 }
30
31 *base = (union _mhu_v3_x_frame_t *)dev->base;
32
33 return MHU_V_3_X_ERR_NONE;
34}
35
36enum mhu_v3_x_error_t mhu_v3_x_driver_init(struct mhu_v3_x_dev_t *dev)
37{
38 uint32_t aidr = 0;
39 uint8_t mhu_major_rev;
40 union _mhu_v3_x_frame_t *p_mhu;
41
42 if (dev == NULL) {
43 return MHU_V_3_X_ERR_INVALID_PARAM;
44 }
45
46 /* Return if already initialized */
47 if (dev->is_initialized == true) {
48 return MHU_V_3_X_ERR_NONE;
49 }
50
51 p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
52
53 /* Read revision from MHU hardware */
54 if (dev->frame == MHU_V3_X_PBX_FRAME) {
55 aidr = p_mhu->pbx_frame.pbx_ctrl_page.pbx_aidr;
56 } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
57 aidr = p_mhu->mbx_frame.mbx_ctrl_page.mbx_aidr;
58 } else {
59 /* Only PBX and MBX frames are supported. */
60 return MHU_V_3_X_ERR_UNSUPPORTED;
61 }
62
63 /* Read the MHU Architecture Major Revision */
64 mhu_major_rev =
65 ((aidr & MHU_ARCH_MAJOR_REV_MASK) >> MHU_ARCH_MAJOR_REV_OFF);
66
67 /* Return error if the MHU major revision is not 3 */
68 if (mhu_major_rev != MHU_MAJOR_REV_V3) {
69 /* Unsupported MHU version */
70 return MHU_V_3_X_ERR_UNSUPPORTED_VERSION;
71 }
72
73 /* Read the MHU Architecture Minor Revision */
74 dev->subversion =
75 ((aidr & MHU_ARCH_MINOR_REV_MASK) >> MHU_ARCH_MINOR_REV_MASK);
76
77 /* Return error if the MHU minor revision is not 0 */
78 if (dev->subversion != MHU_MINOR_REV_3_0) {
79 /* Unsupported subversion */
80 return MHU_V_3_X_ERR_UNSUPPORTED_VERSION;
81 }
82
83 /* Initialize the Postbox/Mailbox to remain in operational state */
84 if (dev->frame == MHU_V3_X_PBX_FRAME) {
85 p_mhu->pbx_frame.pbx_ctrl_page.pbx_ctrl |= MHU_V3_OP_REQ;
86 } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
87 p_mhu->mbx_frame.mbx_ctrl_page.mbx_ctrl |= MHU_V3_OP_REQ;
88 } else {
89 /* Only PBX and MBX frames are supported. */
90 return MHU_V_3_X_ERR_UNSUPPORTED;
91 }
92
93 dev->is_initialized = true;
94
95 return MHU_V_3_X_ERR_NONE;
96}
97
98enum mhu_v3_x_error_t mhu_v3_x_get_num_channel_implemented(
99 const struct mhu_v3_x_dev_t *dev,
100 enum mhu_v3_x_channel_type_t ch_type, uint8_t *num_ch)
101{
102 enum mhu_v3_x_error_t status;
103 union _mhu_v3_x_frame_t *p_mhu;
104
105 if (num_ch == NULL) {
106 return MHU_V_3_X_ERR_INVALID_PARAM;
107 }
108
109 /* Get dev->base if it is valid or return an error if dev is not */
110 status = get_dev_base(dev, &p_mhu);
111 if (status != MHU_V_3_X_ERR_NONE) {
112 return status;
113 }
114
115 /* Only doorbell channel is supported */
116 if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) {
117 return MHU_V_3_X_ERR_UNSUPPORTED;
118 }
119
120 /* Read the number of channels implemented in the MHU */
121 if (dev->frame == MHU_V3_X_PBX_FRAME) {
122 *num_ch = (p_mhu->pbx_frame.pbx_ctrl_page.pbx_dbch_cfg0 + 1);
123 } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
124 *num_ch = (p_mhu->mbx_frame.mbx_ctrl_page.mbx_dbch_cfg0 + 1);
125 } else {
126 /* Only PBX and MBX frames are supported. */
127 return MHU_V_3_X_ERR_UNSUPPORTED;
128 }
129
130 return MHU_V_3_X_ERR_NONE;
131}
132
133enum mhu_v3_x_error_t mhu_v3_x_doorbell_clear(const struct mhu_v3_x_dev_t *dev,
134 const uint32_t channel, uint32_t flags)
135{
136 union _mhu_v3_x_frame_t *p_mhu;
137 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
138 enum mhu_v3_x_error_t status;
139
140 /* Get dev->base if it is valid or return an error if dev is not */
141 status = get_dev_base(dev, &p_mhu);
142 if (status != MHU_V_3_X_ERR_NONE) {
143 return status;
144 }
145
146 /* Only MBX can clear the Doorbell channel */
147 if (dev->frame != MHU_V3_X_MBX_FRAME) {
148 return MHU_V_3_X_ERR_INVALID_PARAM;
149 }
150
151 p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
152 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
153 &(p_mhu->mbx_frame.mdbcw_page);
154
155 /* Clear the bits in the doorbell channel */
156 mdbcw_reg[channel].mdbcw_clr |= flags;
157
158 return MHU_V_3_X_ERR_NONE;
159}
160
161enum mhu_v3_x_error_t mhu_v3_x_doorbell_write(const struct mhu_v3_x_dev_t *dev,
162 const uint32_t channel, uint32_t flags)
163{
164 union _mhu_v3_x_frame_t *p_mhu;
165 struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg;
166 enum mhu_v3_x_error_t status;
167
168 /* Get dev->base if it is valid or return an error if dev is not */
169 status = get_dev_base(dev, &p_mhu);
170 if (status != MHU_V_3_X_ERR_NONE) {
171 return status;
172 }
173
174 /* Only PBX can set the Doorbell channel value */
175 if (dev->frame != MHU_V3_X_PBX_FRAME) {
176 return MHU_V_3_X_ERR_INVALID_PARAM;
177 }
178
179 p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
180
181 pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)
182 &(p_mhu->pbx_frame.pdbcw_page);
183
184 /* Write the value to the doorbell channel */
185 pdbcw_reg[channel].pdbcw_set |= flags;
186
187 return MHU_V_3_X_ERR_NONE;
188}
189
190enum mhu_v3_x_error_t mhu_v3_x_doorbell_read(const struct mhu_v3_x_dev_t *dev,
191 const uint32_t channel, uint32_t *flags)
192{
193 union _mhu_v3_x_frame_t *p_mhu;
194 enum mhu_v3_x_error_t status;
195 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
196 struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg;
197
198 if (flags == NULL) {
199 return MHU_V_3_X_ERR_INVALID_PARAM;
200 }
201
202 /* Get dev->base if it is valid or return an error if dev is not */
203 status = get_dev_base(dev, &p_mhu);
204 if (status != MHU_V_3_X_ERR_NONE) {
205 return status;
206 }
207
208 p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
209
210 if (dev->frame == MHU_V3_X_PBX_FRAME) {
211 pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)
212 &(p_mhu->pbx_frame.pdbcw_page);
213
214 /* Read the value from Postbox Doorbell status register */
215 *flags = pdbcw_reg[channel].pdbcw_st;
216 } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
217 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
218 &(p_mhu->mbx_frame.mdbcw_page);
219
220 /* Read the value from Mailbox Doorbell status register */
221 *flags = mdbcw_reg[channel].mdbcw_st;
222 } else {
223 /* Only PBX and MBX frames are supported. */
224 return MHU_V_3_X_ERR_UNSUPPORTED;
225 }
226
227 return MHU_V_3_X_ERR_NONE;
228}
229
230enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_set(
231 const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
232 uint32_t flags)
233{
234 union _mhu_v3_x_frame_t *p_mhu;
235 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
236 enum mhu_v3_x_error_t status;
237
238 /* Get dev->base if it is valid or return an error if dev is not */
239 status = get_dev_base(dev, &p_mhu);
240 if (status != MHU_V_3_X_ERR_NONE) {
241 return status;
242 }
243
244 /* Doorbell channel mask is not applicable for PBX */
245 if (dev->frame != MHU_V3_X_MBX_FRAME) {
246 return MHU_V_3_X_ERR_INVALID_PARAM;
247 }
248
249 p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
250
251 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
252 &(p_mhu->mbx_frame.mdbcw_page);
253
254 /* Set the Doorbell channel mask */
255 mdbcw_reg[channel].mdbcw_msk_set |= flags;
256
257 return MHU_V_3_X_ERR_NONE;
258}
259
260enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_clear(
261 const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
262 uint32_t flags)
263{
264 union _mhu_v3_x_frame_t *p_mhu;
265 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
266 enum mhu_v3_x_error_t status;
267
268 /* Get dev->base if it is valid or return an error if dev is not */
269 status = get_dev_base(dev, &p_mhu);
270 if (status != MHU_V_3_X_ERR_NONE) {
271 return status;
272 }
273
274 /* Doorbell channel mask is not applicable for PBX */
275 if (dev->frame != MHU_V3_X_MBX_FRAME) {
276 return MHU_V_3_X_ERR_INVALID_PARAM;
277 }
278
279 p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
280
281 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
282 &(p_mhu->mbx_frame.mdbcw_page);
283
284 /* Clear the Doorbell channel mask */
285 mdbcw_reg[channel].mdbcw_msk_clr = flags;
286
287 return MHU_V_3_X_ERR_NONE;
288}
289
290enum mhu_v3_x_error_t mhu_v3_x_doorbell_mask_get(
291 const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
292 uint32_t *flags)
293{
294 union _mhu_v3_x_frame_t *p_mhu;
295 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
296 enum mhu_v3_x_error_t status;
297
298 if (flags == NULL) {
299 return MHU_V_3_X_ERR_INVALID_PARAM;
300 }
301
302 /* Get dev->base if it is valid or return an error if dev is not */
303 status = get_dev_base(dev, &p_mhu);
304 if (status != MHU_V_3_X_ERR_NONE) {
305 return status;
306 }
307
308 /* Doorbell channel mask is not applicable for PBX */
309 if (dev->frame != MHU_V3_X_MBX_FRAME) {
310 return MHU_V_3_X_ERR_INVALID_PARAM;
311 }
312
313 p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
314
315 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
316 &(p_mhu->mbx_frame.mdbcw_page);
317
318 /* Save the Doorbell channel mask status */
319 *flags = mdbcw_reg[channel].mdbcw_msk_st;
320
321 return MHU_V_3_X_ERR_NONE;
322}
323
324enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_enable(
325 const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
326 enum mhu_v3_x_channel_type_t ch_type)
327{
328 enum mhu_v3_x_error_t status;
329
330 union _mhu_v3_x_frame_t *p_mhu;
331 struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg;
332 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
333
334 /* Get dev->base if it is valid or return an error if dev is not */
335 status = get_dev_base(dev, &p_mhu);
336 if (status != MHU_V_3_X_ERR_NONE) {
337 return status;
338 }
339
340 /* Only doorbell channel is supported */
341 if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) {
342 return MHU_V_3_X_ERR_UNSUPPORTED;
343 }
344
345 p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
346
347 if (dev->frame == MHU_V3_X_PBX_FRAME) {
348 pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)
349 &(p_mhu->pbx_frame.pdbcw_page);
350
351 /*
352 * Enable this doorbell channel to generate interrupts for
353 * transfer acknowledge events.
354 */
355 pdbcw_reg[channel].pdbcw_int_en = MHU_V3_X_PDBCW_INT_X_TFR_ACK;
356
357 /*
358 * Enable this doorbell channel to contribute to the PBX
359 * combined interrupt.
360 */
361 pdbcw_reg[channel].pdbcw_ctrl = MHU_V3_X_PDBCW_CTRL_PBX_COMB_EN;
362 } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
363 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
364 &(p_mhu->mbx_frame.mdbcw_page);
365
366 /*
367 * Enable this doorbell channel to contribute to the MBX
368 * combined interrupt.
369 */
370 mdbcw_reg[channel].mdbcw_ctrl = MHU_V3_X_MDBCW_CTRL_MBX_COMB_EN;
371 } else {
372 /* Only PBX and MBX frames are supported. */
373 return MHU_V_3_X_ERR_UNSUPPORTED;
374 }
375
376 return MHU_V_3_X_ERR_NONE;
377}
378
379enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_disable(
380 const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
381 enum mhu_v3_x_channel_type_t ch_type)
382{
383 enum mhu_v3_x_error_t status;
384
385 union _mhu_v3_x_frame_t *p_mhu;
386 struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg;
387 struct _mhu_v3_x_mdbcw_reg_t *mdbcw_reg;
388
389 /* Get dev->base if it is valid or return an error if dev is not */
390 status = get_dev_base(dev, &p_mhu);
391 if (status != MHU_V_3_X_ERR_NONE) {
392 return status;
393 }
394
395 /* Only doorbell channel is supported */
396 if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) {
397 return MHU_V_3_X_ERR_UNSUPPORTED;
398 }
399
400 p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
401
402 if (dev->frame == MHU_V3_X_PBX_FRAME) {
403 pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)
404 &(p_mhu->pbx_frame.pdbcw_page);
405
406 /* Clear channel transfer acknowledge event interrupt */
407 pdbcw_reg[channel].pdbcw_int_clr = MHU_V3_X_PDBCW_INT_X_TFR_ACK;
408
409 /* Disable channel transfer acknowledge event interrupt */
410 pdbcw_reg[channel].pdbcw_int_en &=
411 ~(MHU_V3_X_PDBCW_INT_X_TFR_ACK);
412
413 /*
414 * Disable this doorbell channel from contributing to the PBX
415 * combined interrupt.
416 */
417 pdbcw_reg[channel].pdbcw_ctrl &=
418 ~(MHU_V3_X_PDBCW_CTRL_PBX_COMB_EN);
419 } else if (dev->frame == MHU_V3_X_MBX_FRAME) {
420 mdbcw_reg = (struct _mhu_v3_x_mdbcw_reg_t *)
421 &(p_mhu->mbx_frame.mdbcw_page);
422
423 /*
424 * Disable this doorbell channel from contributing to the MBX
425 * combined interrupt.
426 */
427 mdbcw_reg[channel].mdbcw_ctrl &=
428 ~(MHU_V3_X_MDBCW_CTRL_MBX_COMB_EN);
429 } else {
430 /* Only PBX and MBX frames are supported. */
431 return MHU_V_3_X_ERR_UNSUPPORTED;
432 }
433
434 return MHU_V_3_X_ERR_NONE;
435}
436
437enum mhu_v3_x_error_t mhu_v3_x_channel_interrupt_clear(
438 const struct mhu_v3_x_dev_t *dev, const uint32_t channel,
439 enum mhu_v3_x_channel_type_t ch_type)
440{
441 enum mhu_v3_x_error_t status;
442 union _mhu_v3_x_frame_t *p_mhu;
443 struct _mhu_v3_x_pdbcw_reg_t *pdbcw_reg;
444
445 /* Get dev->base if it is valid or return an error if dev is not */
446 status = get_dev_base(dev, &p_mhu);
447 if (status != MHU_V_3_X_ERR_NONE) {
448 return status;
449 }
450
451 /* Only doorbell channel is supported */
452 if (ch_type != MHU_V3_X_CHANNEL_TYPE_DBCH) {
453 return MHU_V_3_X_ERR_UNSUPPORTED;
454 }
455
456 /*
457 * Only postbox doorbell channel transfer acknowledge interrupt can be
458 * cleared manually.
459 *
460 * To clear MBX interrupt the unmasked status must be cleared using
461 * mhu_v3_x_doorbell_clear.
462 */
463 if (dev->frame != MHU_V3_X_PBX_FRAME) {
464 return MHU_V_3_X_ERR_INVALID_PARAM;
465 }
466
467 p_mhu = (union _mhu_v3_x_frame_t *)dev->base;
468 pdbcw_reg = (struct _mhu_v3_x_pdbcw_reg_t *)&(
469 p_mhu->pbx_frame.pdbcw_page);
470
471 /* Clear channel transfer acknowledge event interrupt */
472 pdbcw_reg[channel].pdbcw_int_clr |= 0x1;
473
474 return MHU_V_3_X_ERR_NONE;
475}