root/sys/dev/apple_bce/apple_bce_mailbox.c
/*
 * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Apple BCE mailbox -- 64-bit request/reply over BAR4 registers.
 */

#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/sema.h>
#include <machine/bus.h>
#include <machine/atomic.h>

#include "apple_bce.h"
#include "apple_bce_mailbox.h"

int
bce_mailbox_init(struct bce_mailbox *mb, struct resource *reg)
{
        mb->reg = reg;
        mb->status = 0;
        sema_init(&mb->mb_cmpl, 0, "bce_mb");
        mb->mb_result = 0;
        mb->initialized = 1;
        return (0);
}

void
bce_mailbox_destroy(struct bce_mailbox *mb)
{
        if (mb->initialized) {
                sema_destroy(&mb->mb_cmpl);
                mb->initialized = 0;
        }
}

/*
 * Send a mailbox message and wait for reply.
 * Only one message in flight at a time (enforced by cmpxchg).
 */
int
bce_mailbox_send(struct bce_mailbox *mb, uint64_t msg, uint64_t *recv,
    unsigned int timeout_ms)
{
        int old;

        /* Acquire: idle (0) -> pending (1) */
        old = atomic_cmpset_int(&mb->status, 0, 1);
        if (old == 0)
                return (EBUSY);

        /* Write 64-bit message as 4x u32 to MBOX_OUT */
        bus_write_4(mb->reg, BCE_REG_MBOX_OUT, (uint32_t)msg);
        bus_write_4(mb->reg, BCE_REG_MBOX_OUT + 4, (uint32_t)(msg >> 32));
        bus_write_4(mb->reg, BCE_REG_MBOX_OUT + 8, 0);
        bus_write_4(mb->reg, BCE_REG_MBOX_OUT + 12, 0);

        if (recv == NULL) {
                atomic_store_int(&mb->status, 0);
                return (0);
        }

        /* Wait for interrupt-driven reply */
        if (sema_timedwait(&mb->mb_cmpl, hz * timeout_ms / 1000) != 0) {
                /* Timeout -- reset to idle */
                atomic_store_int(&mb->status, 0);
                return (ETIMEDOUT);
        }

        if (atomic_load_int(&mb->status) != 2) {
                atomic_store_int(&mb->status, 0);
                return (ETIMEDOUT);
        }

        if (recv != NULL)
                *recv = mb->mb_result;

        atomic_store_int(&mb->status, 0);
        return (0);
}

/*
 * Called from ISR to retrieve mailbox reply.
 */
int
bce_mailbox_handle_interrupt(struct bce_mailbox *mb)
{
        uint32_t res, lo, hi;
        int count;

        res = bus_read_4(mb->reg, BCE_REG_MBOX_REPLY_CTR);
        count = (res >> 20) & 0xf;

        if (count == 0)
                return (ENOMSG);

        while (count-- > 0) {
                lo = bus_read_4(mb->reg, BCE_REG_MBOX_REPLY);
                hi = bus_read_4(mb->reg, BCE_REG_MBOX_REPLY + 4);
                bus_read_4(mb->reg, BCE_REG_MBOX_REPLY + 8);
                bus_read_4(mb->reg, BCE_REG_MBOX_REPLY + 12);
                mb->mb_result = ((uint64_t)hi << 32) | lo;
        }

        if (atomic_load_int(&mb->status) == 1) {
                atomic_store_int(&mb->status, 2);
                sema_post(&mb->mb_cmpl);
        }

        return (0);
}