Date: Sun, 14 Jun 2026 20:57:23 +0000 From: Adrian Chadd <adrian@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: Abdelkader Boudih <freebsd@seuros.com> Subject: git: 6fd2ad9aa39d - main - apple_bce: add Apple T2 Buffer Copy Engine driver Message-ID: <6a2f15b3.391a7.374d1475@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by adrian: URL: https://cgit.FreeBSD.org/src/commit/?id=6fd2ad9aa39db916bf2da9607653fb133f8fa078 commit 6fd2ad9aa39db916bf2da9607653fb133f8fa078 Author: Abdelkader Boudih <freebsd@seuros.com> AuthorDate: 2026-06-14 20:54:03 +0000 Commit: Adrian Chadd <adrian@FreeBSD.org> CommitDate: 2026-06-14 20:56:50 +0000 apple_bce: add Apple T2 Buffer Copy Engine driver DMA ring transport between the host and the T2 coprocessor. Provides mailbox handshake, queue setup, and firmware keepalive for higher-level T2 services (VHCI, audio, etc.). Tested-on: MacBookPro16,2 (A2251), Mac mini 8,1 (A1993) Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D57088 --- sys/conf/files.amd64 | 3 + sys/dev/apple_bce/apple_bce.c | 642 ++++++++++++++++++++++++++++++++++ sys/dev/apple_bce/apple_bce.h | 304 ++++++++++++++++ sys/dev/apple_bce/apple_bce_mailbox.c | 108 ++++++ sys/dev/apple_bce/apple_bce_mailbox.h | 20 ++ sys/dev/apple_bce/apple_bce_queue.c | 492 ++++++++++++++++++++++++++ sys/dev/apple_bce/apple_bce_queue.h | 52 +++ sys/modules/Makefile | 2 + sys/modules/apple_bce/Makefile | 7 + 9 files changed, 1630 insertions(+) diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index f12547727a97..4d37266d1973 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -112,6 +112,9 @@ crypto/openssl/amd64/sha512-x86_64.S optional ossl crypto/openssl/amd64/ossl_aes_gcm_avx512.c optional ossl crypto/openssl/ossl_aes_gcm.c optional ossl dev/amdgpio/amdgpio.c optional amdgpio +dev/apple_bce/apple_bce.c optional apple_bce pci +dev/apple_bce/apple_bce_mailbox.c optional apple_bce pci +dev/apple_bce/apple_bce_queue.c optional apple_bce pci dev/asmc/asmc.c optional asmc isa dev/asmc/asmcmmio.c optional asmc isa dev/axgbe/if_axgbe_pci.c optional axp diff --git a/sys/dev/apple_bce/apple_bce.c b/sys/dev/apple_bce/apple_bce.c new file mode 100644 index 000000000000..8700eddbc0fe --- /dev/null +++ b/sys/dev/apple_bce/apple_bce.c @@ -0,0 +1,642 @@ +/* + * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com> + * + * SPDX-License-Identifier: BSD-2-Clause + * + * Apple T2 Buffer Copy Engine (BCE) PCI driver. + * Provides the transport layer for T2 coprocessor communication: + * mailbox handshake, DMA queue setup, and firmware keepalive. + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/sema.h> +#include <sys/sysctl.h> +#include <sys/systm.h> +#include <sys/callout.h> +#include <sys/rman.h> +#include <sys/time.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/atomic.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#include "apple_bce.h" +#include "apple_bce_mailbox.h" +#include "apple_bce_queue.h" + +static int apple_bce_probe(device_t dev); +static int apple_bce_attach(device_t dev); +static int apple_bce_detach(device_t dev); +static void apple_bce_timestamp_cb(void *arg); +static void apple_bce_timestamp_init(struct apple_bce_softc *sc); +static void apple_bce_timestamp_start(struct apple_bce_softc *sc, + int is_initial); +static void apple_bce_timestamp_stop(struct apple_bce_softc *sc); + +/* + * Timestamp protocol -- T2 firmware expects periodic boottime updates. + * + * Register layout at BAR4 + 0xC000: + * regb + 0 (0xC000): high 32 bits / control + * regb + 8 (0xC008): low 32 bits / data + * + * Control opcodes (written to regb+8 with 0xFFFFFFFF to regb+0): + * -4 (0xFFFFFFFC): initial start + * -3 (0xFFFFFFFD): restart + * -2 (0xFFFFFFFE): stop + */ +static void +apple_bce_timestamp_init(struct apple_bce_softc *sc) +{ + /* Read control register and barrier to sync with firmware */ + bus_read_4(sc->sc_bar4, BCE_REG_TIMESTAMP); + mb(); +} + +static void +apple_bce_timestamp_start(struct apple_bce_softc *sc, int is_initial) +{ + /* Send start opcode: -4 for initial, -3 for restart */ + bus_write_4(sc->sc_bar4, BCE_REG_TIMESTAMP + 8, + is_initial ? 0xFFFFFFFC : 0xFFFFFFFD); + bus_write_4(sc->sc_bar4, BCE_REG_TIMESTAMP, 0xFFFFFFFF); + + mtx_lock_spin(&sc->sc_timestamp_lock); + sc->sc_timestamp_stopped = 0; + mtx_unlock_spin(&sc->sc_timestamp_lock); + + callout_reset(&sc->sc_timestamp_co, hz * BCE_TIMESTAMP_MS / 1000, + apple_bce_timestamp_cb, sc); +} + +static void +apple_bce_timestamp_stop(struct apple_bce_softc *sc) +{ + mtx_lock_spin(&sc->sc_timestamp_lock); + sc->sc_timestamp_stopped = 1; + mtx_unlock_spin(&sc->sc_timestamp_lock); + + callout_drain(&sc->sc_timestamp_co); + + /* Send stop opcode */ + bus_write_4(sc->sc_bar4, BCE_REG_TIMESTAMP + 8, 0xFFFFFFFE); + bus_write_4(sc->sc_bar4, BCE_REG_TIMESTAMP, 0xFFFFFFFF); +} + +static void +apple_bce_timestamp_cb(void *arg) +{ + struct apple_bce_softc *sc = arg; + struct bintime bt; + uint64_t ns; + + /* Read to sync, then barrier */ + bus_read_4(sc->sc_bar4, BCE_REG_TIMESTAMP + 8); + mb(); + + /* Get boot time in nanoseconds */ + binuptime(&bt); + ns = (uint64_t)bt.sec * 1000000000ULL + + (((uint64_t)(bt.frac >> 32) * 1000000000ULL) >> 32); + + /* Write: low 32 bits to regb+8, high 32 bits to regb+0 */ + bus_write_4(sc->sc_bar4, BCE_REG_TIMESTAMP + 8, (uint32_t)ns); + bus_write_4(sc->sc_bar4, BCE_REG_TIMESTAMP, (uint32_t)(ns >> 32)); + + mtx_lock_spin(&sc->sc_timestamp_lock); + if (!sc->sc_timestamp_stopped) + callout_schedule(&sc->sc_timestamp_co, + hz * BCE_TIMESTAMP_MS / 1000); + mtx_unlock_spin(&sc->sc_timestamp_lock); +} + +/* + * IRQ handlers. + */ +static void +apple_bce_mbox_intr(void *arg) +{ + struct apple_bce_softc *sc = arg; + + bce_mailbox_handle_interrupt(&sc->sc_mbox); +} + +static void +apple_bce_dma_intr(void *arg) +{ + struct apple_bce_softc *sc = arg; + int i; + + /* + * Only process registered CQs. sc_cq_list[] contains only CQ + * pointers -- separate from sc_queues[] which mixes CQ and SQ. + */ + mtx_lock(&sc->sc_queues_lock); + for (i = 0; i < BCE_MAX_CQ_COUNT; i++) { + if (sc->sc_cq_list[i] != NULL) + bce_handle_cq_completions(sc, sc->sc_cq_list[i]); + } + mtx_unlock(&sc->sc_queues_lock); +} + +/* + * Enable bus master on PCI function 0 (NVMe). + * T2 requires function 0 to be bus master for DMA on function 1. + */ +static int +apple_bce_enable_pci0_busmaster(device_t dev) +{ + device_t pci0; + device_t bus; + + bus = device_get_parent(dev); + if (bus == NULL) + return (ENXIO); + + /* + * Find function 0 on the same bus/slot. + * Our device is function 1; function 0 is NVMe. + */ + pci0 = pci_find_dbsf(pci_get_domain(dev), pci_get_bus(dev), + pci_get_slot(dev), 0); + if (pci0 == NULL) { + device_printf(dev, "cannot find PCI function 0\n"); + return (ENXIO); + } + + pci_enable_busmaster(pci0); + device_printf(dev, "enabled bus master on function 0 (%s)\n", + device_get_nameunit(pci0)); + return (0); +} + +/* + * Firmware handshake via mailbox. + */ +static int +apple_bce_fw_handshake(struct apple_bce_softc *sc) +{ + uint64_t reply; + int error; + + error = bce_mailbox_send(&sc->sc_mbox, + BCE_MB_MSG(BCE_MB_SET_FW_PROTOCOL_VER, BCE_FW_PROTOCOL_VER), + &reply, BCE_MBOX_TIMEOUT_MS); + if (error != 0) { + device_printf(sc->sc_dev, "firmware handshake timeout\n"); + return (error); + } + + if (BCE_MB_TYPE(reply) != BCE_MB_SET_FW_PROTOCOL_VER || + BCE_MB_VALUE(reply) != BCE_FW_PROTOCOL_VER) { + device_printf(sc->sc_dev, + "firmware version mismatch: got type=%u val=0x%llx\n", + BCE_MB_TYPE(reply), + (unsigned long long)BCE_MB_VALUE(reply)); + return (ENODEV); + } + + device_printf(sc->sc_dev, "firmware handshake OK (protocol 0x%x)\n", + BCE_FW_PROTOCOL_VER); + return (0); +} + +/* + * DMA callback for command queue registration. + */ +static void +bce_reg_dma_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct { bus_addr_t addr; int error; } *cb = arg; + + cb->error = error; + if (error == 0) + cb->addr = segs[0].ds_addr; +} + +/* + * Register a command queue (CQ or SQ) with firmware via mailbox. + * The memcfg struct is DMA-mapped and its physical address sent + * in the mailbox message -- firmware reads the config from DMA. + */ +static int +apple_bce_register_cmd_queue(struct apple_bce_softc *sc, + struct bce_queue_memcfg *cfg, int is_sq) +{ + bus_dma_tag_t tag; + bus_dmamap_t map; + bus_addr_t paddr; + struct bce_queue_memcfg *dma_cfg; + struct { + bus_addr_t addr; + int error; + } cb; + uint64_t reply; + int error, cmd_type; + + /* Allocate DMA-coherent buffer for memcfg (8-byte aligned) */ + error = bus_dma_tag_create(sc->sc_dma_tag, + 8, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, + NULL, NULL, sizeof(*cfg), 1, sizeof(*cfg), + BUS_DMA_WAITOK, NULL, NULL, &tag); + if (error != 0) + return (error); + + error = bus_dmamem_alloc(tag, (void **)&dma_cfg, + BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &map); + if (error != 0) { + bus_dma_tag_destroy(tag); + return (error); + } + + /* Copy config and load DMA address */ + memcpy(dma_cfg, cfg, sizeof(*cfg)); + + cb.error = 0; + error = bus_dmamap_load(tag, map, dma_cfg, sizeof(*cfg), + bce_reg_dma_cb, &cb, BUS_DMA_WAITOK); + if (error != 0 || cb.error != 0) { + bus_dmamem_free(tag, dma_cfg, map); + bus_dma_tag_destroy(tag); + return (error != 0 ? error : cb.error); + } + paddr = cb.addr; + + /* Sync DMA buffer before device access */ + bus_dmamap_sync(tag, map, BUS_DMASYNC_PREWRITE); + + /* Send DMA address of memcfg to firmware */ + cmd_type = is_sq ? BCE_MB_REGISTER_CMD_SQ : BCE_MB_REGISTER_CMD_CQ; + error = bce_mailbox_send(&sc->sc_mbox, + BCE_MB_MSG(cmd_type, paddr), &reply, BCE_MBOX_TIMEOUT_MS); + + bus_dmamap_sync(tag, map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(tag, map); + bus_dmamem_free(tag, dma_cfg, map); + bus_dma_tag_destroy(tag); + + if (error != 0) + return (error); + + if (BCE_MB_TYPE(reply) != BCE_MB_REGISTER_QUEUE_REPLY) { + device_printf(sc->sc_dev, + "unexpected queue registration reply: type=%u\n", + BCE_MB_TYPE(reply)); + return (EINVAL); + } + + return (0); +} + +/* + * Setup command queues (CQ qid=0, SQ qid=1). + */ +static int +apple_bce_setup_cmd_queues(struct apple_bce_softc *sc) +{ + struct bce_queue_memcfg cfg; + struct bce_queue_sq *sq; + int error; + + /* Allocate command CQ (qid 0, 32 entries) */ + sc->sc_cmd_cq = bce_alloc_cq(sc, 0, 32); + if (sc->sc_cmd_cq == NULL) { + device_printf(sc->sc_dev, "failed to allocate command CQ\n"); + return (ENOMEM); + } + + /* Register CQ with firmware via DMA-mapped memcfg */ + bce_get_cq_memcfg(sc->sc_cmd_cq, &cfg); + error = apple_bce_register_cmd_queue(sc, &cfg, 0); + if (error != 0) { + device_printf(sc->sc_dev, "failed to register command CQ\n"); + goto fail_cq; + } + + /* Store CQ in queue registries */ + mtx_lock(&sc->sc_queues_lock); + sc->sc_queues[0] = sc->sc_cmd_cq; + sc->sc_cq_list[0] = sc->sc_cmd_cq; + mtx_unlock(&sc->sc_queues_lock); + + /* Allocate command SQ (qid 1, 64-byte elements, 32 entries) */ + sq = bce_alloc_sq(sc, 1, BCE_CMD_SIZE, 32, NULL, NULL); + if (sq == NULL) { + device_printf(sc->sc_dev, "failed to allocate command SQ\n"); + goto fail_cq; + } + + /* Wrap SQ in command queue (sets completion callback internally) */ + sc->sc_cmd_cmdq = bce_alloc_cmdq(sc, sq); + if (sc->sc_cmd_cmdq == NULL) { + bce_free_sq(sc, sq); + goto fail_cq; + } + + /* Register SQ with firmware via DMA-mapped memcfg */ + bce_get_sq_memcfg(sq, sc->sc_cmd_cq, &cfg); + error = apple_bce_register_cmd_queue(sc, &cfg, 1); + if (error != 0) { + device_printf(sc->sc_dev, "failed to register command SQ\n"); + goto fail_sq; + } + + /* Store SQ in queue registry */ + mtx_lock(&sc->sc_queues_lock); + sc->sc_queues[1] = sq; + sc->sc_int_sq_list[0] = sq; + mtx_unlock(&sc->sc_queues_lock); + + device_printf(sc->sc_dev, "command queues created (CQ=0, SQ=1)\n"); + return (0); + +fail_sq: + bce_free_cmdq(sc->sc_cmd_cmdq); + sc->sc_cmd_cmdq = NULL; + bce_free_sq(sc, sq); +fail_cq: + /* Clear CQ from registries before freeing */ + mtx_lock(&sc->sc_queues_lock); + sc->sc_queues[0] = NULL; + sc->sc_cq_list[0] = NULL; + mtx_unlock(&sc->sc_queues_lock); + bce_free_cq(sc, sc->sc_cmd_cq); + sc->sc_cmd_cq = NULL; + return (error != 0 ? error : ENOMEM); +} + +/* + * PCI probe. + */ +static int +apple_bce_probe(device_t dev) +{ + if (pci_get_vendor(dev) != BCE_PCI_VENDOR_APPLE || + pci_get_device(dev) != BCE_PCI_DEVICE_T2) + return (ENXIO); + + /* Only attach to function 1 (BCE), not function 0 (NVMe) */ + if (pci_get_function(dev) != 1) + return (ENXIO); + + device_set_desc(dev, "Apple T2 Buffer Copy Engine"); + return (BUS_PROBE_DEFAULT); +} + +/* + * PCI attach. + */ +static int +apple_bce_attach(device_t dev) +{ + struct apple_bce_softc *sc = device_get_softc(dev); + int error; + + sc->sc_dev = dev; + pci_enable_busmaster(dev); + mtx_init(&sc->sc_queues_lock, "bce_queues", NULL, MTX_DEF); + mtx_init(&sc->sc_timestamp_lock, "bce_ts", NULL, MTX_SPIN); + callout_init(&sc->sc_timestamp_co, 1); + + /* Map BAR2 (DMA) and BAR4 (mailbox) */ + sc->sc_bar2_rid = PCIR_BAR(2); + sc->sc_bar2 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_bar2_rid, RF_ACTIVE); + if (sc->sc_bar2 == NULL) { + device_printf(dev, "cannot map BAR2 (DMA)\n"); + error = ENXIO; + goto fail; + } + + sc->sc_bar4_rid = PCIR_BAR(4); + sc->sc_bar4 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_bar4_rid, RF_ACTIVE); + if (sc->sc_bar4 == NULL) { + device_printf(dev, "cannot map BAR4 (mailbox)\n"); + error = ENXIO; + goto fail; + } + + /* Allocate MSI vectors (need at least 5: mbox=0, dma=4) */ + sc->sc_msi_count = 8; /* Must be power of 2 for FreeBSD */ + error = pci_alloc_msi(dev, &sc->sc_msi_count); + if (error != 0 || sc->sc_msi_count < 8) { + device_printf(dev, "cannot allocate MSI vectors: " + "error=%d got=%d\n", error, sc->sc_msi_count); + if (error == 0 && sc->sc_msi_count > 0) + pci_release_msi(dev); + sc->sc_msi_count = 0; + error = ENXIO; + goto fail; + } + + /* Initialize mailbox before installing handlers */ + bce_mailbox_init(&sc->sc_mbox, sc->sc_bar4); + + /* Setup IRQ: vector 0 = mailbox, vector 4 = DMA */ + sc->sc_irq_rid_mbox = 1; /* MSI vectors start at rid 1 */ + sc->sc_irq_mbox = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irq_rid_mbox, RF_ACTIVE); + if (sc->sc_irq_mbox == NULL) { + device_printf(dev, "cannot allocate mailbox IRQ\n"); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->sc_irq_mbox, + INTR_TYPE_MISC | INTR_MPSAFE, + NULL, apple_bce_mbox_intr, sc, &sc->sc_irq_mbox_cookie); + if (error != 0) { + device_printf(dev, "cannot setup mailbox IRQ\n"); + goto fail; + } + + sc->sc_irq_rid_dma = 5; /* Vector 4 = rid 5 */ + sc->sc_irq_dma = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irq_rid_dma, RF_ACTIVE); + if (sc->sc_irq_dma == NULL) { + device_printf(dev, "cannot allocate DMA IRQ\n"); + error = ENXIO; + goto fail; + } + + error = bus_setup_intr(dev, sc->sc_irq_dma, + INTR_TYPE_MISC | INTR_MPSAFE, + NULL, apple_bce_dma_intr, sc, &sc->sc_irq_dma_cookie); + if (error != 0) { + device_printf(dev, "cannot setup DMA IRQ\n"); + goto fail; + } + + /* Create parent DMA tag with 37-bit addressing limit */ + error = bus_dma_tag_create(bus_get_dma_tag(dev), + 1, 0, /* alignment, boundary */ + (1ULL << 37) - 1, /* lowaddr: 37-bit limit */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter */ + BUS_SPACE_MAXSIZE, /* maxsize */ + BUS_SPACE_UNRESTRICTED, /* nsegments */ + BUS_SPACE_MAXSIZE, /* maxsegsize */ + 0, /* flags */ + NULL, NULL, /* lockfunc */ + &sc->sc_dma_tag); + if (error != 0) { + device_printf(dev, "cannot create DMA tag\n"); + goto fail; + } + + /* + * Enable bus master on function 0 (NVMe) -- T2 quirk. + * Must be done before firmware handshake; T2 rejects DMA + * on function 1 unless function 0 is bus master. + */ + apple_bce_enable_pci0_busmaster(dev); + + /* Initialize and start timestamp keepalive */ + apple_bce_timestamp_init(sc); + apple_bce_timestamp_start(sc, 1); + + /* Firmware handshake */ + error = apple_bce_fw_handshake(sc); + if (error != 0) + goto fail; + + /* Setup command queues */ + error = apple_bce_setup_cmd_queues(sc); + if (error != 0) + goto fail; + + device_printf(dev, "Apple T2 BCE initialized\n"); + return (0); + +fail: + apple_bce_detach(dev); + return (error); +} + +/* + * PCI detach. + */ +static int +apple_bce_detach(device_t dev) +{ + struct apple_bce_softc *sc = device_get_softc(dev); + + /* 1. Stop timestamp */ + if (sc->sc_bar4 != NULL && mtx_initialized(&sc->sc_timestamp_lock)) + apple_bce_timestamp_stop(sc); + else + callout_drain(&sc->sc_timestamp_co); + + /* 2. Tear down IRQs first -- no more interrupts after this */ + if (sc->sc_irq_dma_cookie != NULL) { + bus_teardown_intr(dev, sc->sc_irq_dma, sc->sc_irq_dma_cookie); + sc->sc_irq_dma_cookie = NULL; + } + if (sc->sc_irq_mbox_cookie != NULL) { + bus_teardown_intr(dev, sc->sc_irq_mbox, + sc->sc_irq_mbox_cookie); + sc->sc_irq_mbox_cookie = NULL; + } + + /* 3. Free command queues (safe now -- no IRQs can fire) */ + if (sc->sc_cmd_cmdq != NULL) { + struct bce_queue_sq *cmd_sq; + + cmd_sq = sc->sc_cmd_cmdq->sq; + bce_free_cmdq(sc->sc_cmd_cmdq); + sc->sc_cmd_cmdq = NULL; + bce_free_sq(sc, cmd_sq); + } + if (sc->sc_cmd_cq != NULL) { + bce_free_cq(sc, sc->sc_cmd_cq); + sc->sc_cmd_cq = NULL; + } + + /* 4. Clear queue registries */ + if (mtx_initialized(&sc->sc_queues_lock)) { + mtx_lock(&sc->sc_queues_lock); + memset(sc->sc_queues, 0, sizeof(sc->sc_queues)); + memset(sc->sc_cq_list, 0, sizeof(sc->sc_cq_list)); + memset(sc->sc_int_sq_list, 0, sizeof(sc->sc_int_sq_list)); + mtx_unlock(&sc->sc_queues_lock); + } + + /* 5. Destroy mailbox */ + bce_mailbox_destroy(&sc->sc_mbox); + + /* 6. Release DMA tag */ + if (sc->sc_dma_tag != NULL) { + bus_dma_tag_destroy(sc->sc_dma_tag); + sc->sc_dma_tag = NULL; + } + + /* 7. Release IRQ resources */ + if (sc->sc_irq_dma != NULL) { + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid_dma, + sc->sc_irq_dma); + sc->sc_irq_dma = NULL; + } + if (sc->sc_irq_mbox != NULL) { + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid_mbox, + sc->sc_irq_mbox); + sc->sc_irq_mbox = NULL; + } + if (sc->sc_msi_count > 0) { + pci_release_msi(dev); + sc->sc_msi_count = 0; + } + + /* 8. Release BARs */ + if (sc->sc_bar4 != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_bar4_rid, + sc->sc_bar4); + sc->sc_bar4 = NULL; + } + if (sc->sc_bar2 != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_bar2_rid, + sc->sc_bar2); + sc->sc_bar2 = NULL; + } + + if (mtx_initialized(&sc->sc_timestamp_lock)) + mtx_destroy(&sc->sc_timestamp_lock); + if (mtx_initialized(&sc->sc_queues_lock)) + mtx_destroy(&sc->sc_queues_lock); + + return (0); +} + +static device_method_t apple_bce_methods[] = { + DEVMETHOD(device_probe, apple_bce_probe), + DEVMETHOD(device_attach, apple_bce_attach), + DEVMETHOD(device_detach, apple_bce_detach), + DEVMETHOD_END +}; + +static driver_t apple_bce_driver = { + "apple_bce", + apple_bce_methods, + sizeof(struct apple_bce_softc) +}; + +DRIVER_MODULE(apple_bce, pci, apple_bce_driver, NULL, NULL); +MODULE_DEPEND(apple_bce, pci, 1, 1, 1); +static const struct { + uint16_t vendor; + uint16_t device; +} apple_bce_pnp[] = { + { BCE_PCI_VENDOR_APPLE, BCE_PCI_DEVICE_T2 }, +}; + +MODULE_PNP_INFO("U16:vendor;U16:device", pci, apple_bce, apple_bce_pnp, + nitems(apple_bce_pnp)); diff --git a/sys/dev/apple_bce/apple_bce.h b/sys/dev/apple_bce/apple_bce.h new file mode 100644 index 000000000000..7c2198c7232b --- /dev/null +++ b/sys/dev/apple_bce/apple_bce.h @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com> + * + * SPDX-License-Identifier: BSD-2-Clause + * + * Apple T2 Buffer Copy Engine (BCE) driver. + * PCI transport layer for T2 coprocessor communication. + */ + +#ifndef _APPLE_BCE_H_ +#define _APPLE_BCE_H_ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/sema.h> +#include <sys/callout.h> +#include <sys/rman.h> +#include <machine/bus.h> +#include <machine/resource.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +#define BCE_PCI_VENDOR_APPLE 0x106b +#define BCE_PCI_DEVICE_T2 0x1801 + +#define BCE_MAX_QUEUE_COUNT 0x100 +#define BCE_MAX_CQ_COUNT 16 /* Max completion queues tracked */ +#define BCE_QUEUE_USER_MIN 2 +#define BCE_QUEUE_USER_MAX (BCE_MAX_QUEUE_COUNT - 1) +#define BCE_CMD_SIZE 0x40 + +#define BCE_FW_PROTOCOL_VER 0x20001 +#define BCE_MBOX_TIMEOUT_MS 1000 +#define BCE_TIMESTAMP_MS 150 /* Firmware keepalive interval */ + +/* + * Mailbox register offsets (BAR4). + */ +#define BCE_REG_MBOX_OUT 0x820 /* 4x u32 to-device */ +#define BCE_REG_MBOX_REPLY 0x810 /* 4x u32 from-device */ +#define BCE_REG_MBOX_REPLY_CTR 0x108 /* Reply counter (bits 23:20 = count) */ +#define BCE_REG_TIMESTAMP 0xC000 /* Timestamp sync */ + +/* + * DMA register offsets (BAR2). + */ +#define BCE_REG_DOORBELL_BASE 0x44000 + +/* + * Mailbox message format: 6-bit type (63:58) + 58-bit value. + */ +#define BCE_MB_MSG(type, val) (((uint64_t)(type) << 58) | \ + ((val) & 0x3FFFFFFFFFFFFFFULL)) +#define BCE_MB_TYPE(v) ((uint32_t)((v) >> 58)) +#define BCE_MB_VALUE(v) ((v) & 0x3FFFFFFFFFFFFFFULL) + +/* + * Mailbox message types. + */ +enum bce_mb_type { + BCE_MB_REGISTER_CMD_SQ = 0x07, + BCE_MB_REGISTER_CMD_CQ = 0x08, + BCE_MB_REGISTER_QUEUE_REPLY = 0x0B, + BCE_MB_SET_FW_PROTOCOL_VER = 0x0C, + BCE_MB_SLEEP_NO_STATE = 0x14, + BCE_MB_RESTORE_NO_STATE = 0x15, + BCE_MB_SAVE_AND_SLEEP = 0x17, + BCE_MB_RESTORE_AND_WAKE = 0x18, + BCE_MB_SAVE_FAILURE = 0x19, + BCE_MB_SAVE_RESTORE_COMPLETE = 0x1A, +}; + +/* + * Queue types. + */ +enum bce_queue_type { + BCE_QUEUE_CQ = 0, + BCE_QUEUE_SQ = 1, +}; + +/* + * Completion queue entry. + */ +struct bce_qe_completion { + uint64_t result; + uint64_t data_size; + uint16_t qid; /* Source SQ */ + uint16_t completion_index; + uint16_t status; + uint16_t flags; +#define BCE_CQ_FLAG_PENDING 0x8000 +} __packed; + +/* + * Completion status codes. + */ +enum bce_completion_status { + BCE_COMP_SUCCESS = 0, + BCE_COMP_ERROR = 1, + BCE_COMP_ABORTED = 2, + BCE_COMP_NO_SPACE = 3, + BCE_COMP_OVERRUN = 4, +}; + +/* + * Submission queue entry. + */ +struct bce_qe_submission { + uint64_t length; + uint64_t addr; + uint64_t segl_addr; /* Scatter-gather list */ + uint64_t segl_length; +} __packed; + +/* + * Queue memory configuration (for mailbox registration). + */ +struct bce_queue_memcfg { + uint16_t qid; + uint16_t el_count; + uint16_t vector_or_cq; + uint16_t _pad; + uint64_t addr; + uint64_t length; +} __packed; + +/* + * Completion data cached per SQ slot. + */ +struct bce_sq_completion_data { + uint32_t status; + uint64_t data_size; + uint64_t result; +}; + +/* + * Completion callback. + */ +struct bce_queue_sq; +typedef void (*bce_sq_completion_fn)(struct bce_queue_sq *sq); + +/* + * Completion queue. + */ +struct bce_queue_cq { + int qid; + uint32_t el_count; + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_addr_t dma_addr; + struct bce_qe_completion *data; + uint32_t index; /* Consumer index */ +}; + +/* + * Submission queue. + */ +struct bce_queue_sq { + int qid; + uint32_t el_size; + uint32_t el_count; + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_addr_t dma_addr; + void *data; + void *userdata; + + /* Submission tracking */ + volatile int available_commands; + uint32_t head; + uint32_t tail; + + /* Completion tracking */ + uint32_t completion_cidx; + uint32_t completion_tail; + struct bce_sq_completion_data *completion_data; + int has_pending; + bce_sq_completion_fn completion; +}; + +/* + * Command queue (wraps SQ for synchronous ops). + */ +struct bce_queue_cmdq_result { + struct sema cmpl; + uint32_t status; + uint64_t result; +}; + +struct bce_queue_cmdq { + struct bce_queue_sq *sq; + struct mtx lck; + struct bce_queue_cmdq_result **tres; /* Pending results per slot */ +}; + +/* + * Command queue command IDs. + */ +enum bce_cmdq_cmd { + BCE_CMD_REGISTER_QUEUE = 0x20, + BCE_CMD_UNREGISTER_QUEUE = 0x30, + BCE_CMD_FLUSH_QUEUE = 0x40, + BCE_CMD_SET_QUEUE_PROPERTY = 0x50, +}; + +/* + * Register queue command payload (fits in BCE_CMD_SIZE). + */ +struct bce_cmdq_reg_cmd { + uint16_t cmd; + uint16_t flags; +#define BCE_CMDQ_FLAG_NAMED 0x02 +#define BCE_CMDQ_FLAG_OUT 0x01 + uint16_t qid; + uint16_t _pad; + uint16_t el_count; + uint16_t vector_or_cq; + uint16_t _pad2; + uint16_t name_len; + char name[0x20]; + uint64_t addr; + uint64_t length; +} __packed; + +/* + * Simple queue command (unregister/flush). + */ +struct bce_cmdq_simple_cmd { + uint16_t cmd; + uint16_t flags; + uint16_t qid; +} __packed; + +/* + * Mailbox state. + */ +struct bce_mailbox { + struct resource *reg; /* BAR4 resource */ + volatile int status; /* 0=idle, 1=pending, 2=ready */ + int initialized; + struct sema mb_cmpl; + uint64_t mb_result; +}; + +/* + * Main device softc. + */ +struct apple_bce_softc { + device_t sc_dev; + + /* PCI resources */ + int sc_bar2_rid; + struct resource *sc_bar2; /* DMA registers */ + int sc_bar4_rid; + struct resource *sc_bar4; /* Mailbox registers */ + + /* MSI */ + int sc_msi_count; + int sc_irq_rid_mbox; + struct resource *sc_irq_mbox; + void *sc_irq_mbox_cookie; + int sc_irq_rid_dma; + struct resource *sc_irq_dma; + void *sc_irq_dma_cookie; + + /* DMA */ + bus_dma_tag_t sc_dma_tag; + + /* Mailbox */ + struct bce_mailbox sc_mbox; + + /* Timestamp keepalive */ + struct callout sc_timestamp_co; + struct mtx sc_timestamp_lock; + int sc_timestamp_stopped; + + /* Command path */ + struct bce_queue_cq *sc_cmd_cq; + struct bce_queue_cmdq *sc_cmd_cmdq; + + /* Queue registry */ + void *sc_queues[BCE_MAX_QUEUE_COUNT]; + struct mtx sc_queues_lock; *** 747 LINES SKIPPED ***home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a2f15b3.391a7.374d1475>
