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