xref: /freebsd/sys/dev/apple_bce/apple_bce_mailbox.c (revision a259b98fa211ed87bfee58c575de4e2de94ee0fa)
1 /*
2  * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  *
6  * Apple BCE mailbox -- 64-bit request/reply over BAR4 registers.
7  */
8 
9 #include <sys/param.h>
10 #include <sys/systm.h>
11 #include <sys/bus.h>
12 #include <sys/kernel.h>
13 #include <sys/sema.h>
14 #include <machine/bus.h>
15 #include <machine/atomic.h>
16 
17 #include "apple_bce.h"
18 #include "apple_bce_mailbox.h"
19 
20 int
21 bce_mailbox_init(struct bce_mailbox *mb, struct resource *reg)
22 {
23 	mb->reg = reg;
24 	mb->status = 0;
25 	sema_init(&mb->mb_cmpl, 0, "bce_mb");
26 	mb->mb_result = 0;
27 	mb->initialized = 1;
28 	return (0);
29 }
30 
31 void
32 bce_mailbox_destroy(struct bce_mailbox *mb)
33 {
34 	if (mb->initialized) {
35 		sema_destroy(&mb->mb_cmpl);
36 		mb->initialized = 0;
37 	}
38 }
39 
40 /*
41  * Send a mailbox message and wait for reply.
42  * Only one message in flight at a time (enforced by cmpxchg).
43  */
44 int
45 bce_mailbox_send(struct bce_mailbox *mb, uint64_t msg, uint64_t *recv,
46     unsigned int timeout_ms)
47 {
48 	int old;
49 
50 	/* Acquire: idle (0) -> pending (1) */
51 	old = atomic_cmpset_int(&mb->status, 0, 1);
52 	if (old == 0)
53 		return (EBUSY);
54 
55 	/* Write 64-bit message as 4x u32 to MBOX_OUT */
56 	bus_write_4(mb->reg, BCE_REG_MBOX_OUT, (uint32_t)msg);
57 	bus_write_4(mb->reg, BCE_REG_MBOX_OUT + 4, (uint32_t)(msg >> 32));
58 	bus_write_4(mb->reg, BCE_REG_MBOX_OUT + 8, 0);
59 	bus_write_4(mb->reg, BCE_REG_MBOX_OUT + 12, 0);
60 
61 	if (recv == NULL) {
62 		atomic_store_int(&mb->status, 0);
63 		return (0);
64 	}
65 
66 	if (cold) {
67 		/*
68 		 * During early boot, timer-backed sleeps are not available.
69 		 * Poll the hardware directly, but account for an installed
70 		 * interrupt handler consuming the reply first.
71 		 */
72 		unsigned int waited = 0;
73 
74 		while (waited < timeout_ms * 1000) {
75 			if (atomic_load_int(&mb->status) == 2)
76 				break;
77 			(void)bce_mailbox_handle_interrupt(mb);
78 			if (atomic_load_int(&mb->status) == 2)
79 				break;
80 			DELAY(100);
81 			waited += 100;
82 		}
83 		if (atomic_load_int(&mb->status) != 2) {
84 			atomic_store_int(&mb->status, 0);
85 			return (ETIMEDOUT);
86 		}
87 	} else {
88 		/* Wait for interrupt-driven reply */
89 		if (sema_timedwait(&mb->mb_cmpl,
90 		    hz * timeout_ms / 1000) != 0) {
91 			atomic_store_int(&mb->status, 0);
92 			return (ETIMEDOUT);
93 		}
94 		if (atomic_load_int(&mb->status) != 2) {
95 			atomic_store_int(&mb->status, 0);
96 			return (ETIMEDOUT);
97 		}
98 	}
99 
100 	*recv = mb->mb_result;
101 
102 	atomic_store_int(&mb->status, 0);
103 	return (0);
104 }
105 
106 /*
107  * Called from ISR to retrieve mailbox reply.
108  */
109 int
110 bce_mailbox_handle_interrupt(struct bce_mailbox *mb)
111 {
112 	uint32_t res, lo, hi;
113 	int count;
114 
115 	res = bus_read_4(mb->reg, BCE_REG_MBOX_REPLY_CTR);
116 	count = (res >> 20) & 0xf;
117 
118 	if (count == 0)
119 		return (ENOMSG);
120 
121 	while (count-- > 0) {
122 		lo = bus_read_4(mb->reg, BCE_REG_MBOX_REPLY);
123 		hi = bus_read_4(mb->reg, BCE_REG_MBOX_REPLY + 4);
124 		bus_read_4(mb->reg, BCE_REG_MBOX_REPLY + 8);
125 		bus_read_4(mb->reg, BCE_REG_MBOX_REPLY + 12);
126 		mb->mb_result = ((uint64_t)hi << 32) | lo;
127 	}
128 
129 	if (atomic_load_int(&mb->status) == 1) {
130 		atomic_store_int(&mb->status, 2);
131 		/* A cold sender polls status and must not leave a token. */
132 		if (!cold)
133 			sema_post(&mb->mb_cmpl);
134 	}
135 
136 	return (0);
137 }
138