1 /*
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  */
get_dev_base(const struct mhu_v3_x_dev_t * dev,union _mhu_v3_x_frame_t ** base)19 static 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 
mhu_v3_x_driver_init(struct mhu_v3_x_dev_t * dev)36 enum 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 
mhu_v3_x_get_num_channel_implemented(const struct mhu_v3_x_dev_t * dev,enum mhu_v3_x_channel_type_t ch_type,uint8_t * num_ch)98 enum 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 
mhu_v3_x_doorbell_clear(const struct mhu_v3_x_dev_t * dev,const uint32_t channel,uint32_t flags)133 enum 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 
mhu_v3_x_doorbell_write(const struct mhu_v3_x_dev_t * dev,const uint32_t channel,uint32_t flags)161 enum 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 
mhu_v3_x_doorbell_read(const struct mhu_v3_x_dev_t * dev,const uint32_t channel,uint32_t * flags)190 enum 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 
mhu_v3_x_doorbell_mask_set(const struct mhu_v3_x_dev_t * dev,const uint32_t channel,uint32_t flags)230 enum 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 
mhu_v3_x_doorbell_mask_clear(const struct mhu_v3_x_dev_t * dev,const uint32_t channel,uint32_t flags)260 enum 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 
mhu_v3_x_doorbell_mask_get(const struct mhu_v3_x_dev_t * dev,const uint32_t channel,uint32_t * flags)290 enum 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 
mhu_v3_x_channel_interrupt_enable(const struct mhu_v3_x_dev_t * dev,const uint32_t channel,enum mhu_v3_x_channel_type_t ch_type)324 enum 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 
mhu_v3_x_channel_interrupt_disable(const struct mhu_v3_x_dev_t * dev,const uint32_t channel,enum mhu_v3_x_channel_type_t ch_type)379 enum 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 
mhu_v3_x_channel_interrupt_clear(const struct mhu_v3_x_dev_t * dev,const uint32_t channel,enum mhu_v3_x_channel_type_t ch_type)437 enum 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 }
476