apple_bce/vhci: add T2 virtual USB host controller
Implements a VHCI driver on top of the BCE transport: - Virtual USB bus registration via usb_controller - Port discovery and device enumeration - Control, interrupt, and bulk endpoint support - Firmware event handling with taskqueue - Suspend/resume via BCE mailbox Provides keyboard, trackpad, and Touch Bar access on T2 Macs. Tested-on: MacBookPro16,2 (A2251), Mac mini 8,1 (A1993) Reviewed by: adrian Differential Revision: https://reviews.freebsd.org/D57089
This commit is contained in:
committed by
Adrian Chadd
parent
6fd2ad9aa3
commit
9f90536c74
@@ -115,6 +115,7 @@ 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/apple_bce/apple_bce_vhci.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
|
||||
|
||||
@@ -32,10 +32,13 @@
|
||||
#include "apple_bce.h"
|
||||
#include "apple_bce_mailbox.h"
|
||||
#include "apple_bce_queue.h"
|
||||
#include "apple_bce_vhci.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 int apple_bce_suspend(device_t dev);
|
||||
static int apple_bce_resume(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,
|
||||
@@ -516,6 +519,12 @@ apple_bce_attach(device_t dev)
|
||||
goto fail;
|
||||
|
||||
device_printf(dev, "Apple T2 BCE initialized\n");
|
||||
|
||||
/* Create VHCI child for virtual USB */
|
||||
error = bce_vhci_attach(sc);
|
||||
if (error != 0)
|
||||
goto fail;
|
||||
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
@@ -531,6 +540,9 @@ apple_bce_detach(device_t dev)
|
||||
{
|
||||
struct apple_bce_softc *sc = device_get_softc(dev);
|
||||
|
||||
/* 0. Detach VHCI child first (before destroying parent resources) */
|
||||
bce_vhci_detach(sc);
|
||||
|
||||
/* 1. Stop timestamp */
|
||||
if (sc->sc_bar4 != NULL && mtx_initialized(&sc->sc_timestamp_lock))
|
||||
apple_bce_timestamp_stop(sc);
|
||||
@@ -616,10 +628,85 @@ apple_bce_detach(device_t dev)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
apple_bce_suspend(device_t dev)
|
||||
{
|
||||
struct apple_bce_softc *sc = device_get_softc(dev);
|
||||
int error, restore_error;
|
||||
|
||||
apple_bce_timestamp_stop(sc);
|
||||
|
||||
error = bce_vhci_detach(sc);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "failed to detach VHCI for suspend: %d\n",
|
||||
error);
|
||||
apple_bce_timestamp_start(sc, 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
error = bce_mailbox_send(&sc->sc_mbox,
|
||||
BCE_MB_MSG(BCE_MB_SLEEP_NO_STATE, 0), NULL,
|
||||
BCE_MBOX_TIMEOUT_MS);
|
||||
if (error != 0) {
|
||||
device_printf(dev,
|
||||
"failed to send SLEEP_NO_STATE mailbox command: %d\n",
|
||||
error);
|
||||
restore_error = bce_vhci_attach(sc);
|
||||
if (restore_error != 0) {
|
||||
device_printf(dev,
|
||||
"failed to reattach VHCI after suspend error: %d\n",
|
||||
restore_error);
|
||||
}
|
||||
apple_bce_timestamp_start(sc, 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
apple_bce_resume(device_t dev)
|
||||
{
|
||||
struct apple_bce_softc *sc = device_get_softc(dev);
|
||||
uint64_t reply;
|
||||
int error;
|
||||
|
||||
error = bce_mailbox_send(&sc->sc_mbox,
|
||||
BCE_MB_MSG(BCE_MB_RESTORE_NO_STATE, 0), &reply,
|
||||
BCE_MBOX_TIMEOUT_MS);
|
||||
if (error != 0) {
|
||||
device_printf(dev,
|
||||
"failed to send RESTORE_NO_STATE mailbox command: %d\n",
|
||||
error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
if (BCE_MB_TYPE(reply) != BCE_MB_RESTORE_NO_STATE) {
|
||||
device_printf(dev,
|
||||
"unexpected RESTORE_NO_STATE reply: type=%u val=0x%llx\n",
|
||||
BCE_MB_TYPE(reply),
|
||||
(unsigned long long)BCE_MB_VALUE(reply));
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
error = bce_vhci_attach(sc);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "failed to reattach VHCI after resume: %d\n",
|
||||
error);
|
||||
apple_bce_timestamp_start(sc, 0);
|
||||
return (error);
|
||||
}
|
||||
|
||||
apple_bce_timestamp_start(sc, 0);
|
||||
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(device_suspend, apple_bce_suspend),
|
||||
DEVMETHOD(device_resume, apple_bce_resume),
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#define BCE_PCI_DEVICE_T2 0x1801
|
||||
|
||||
#define BCE_MAX_QUEUE_COUNT 0x100
|
||||
#define BCE_MAX_CQ_COUNT 16 /* Max completion queues tracked */
|
||||
#define BCE_MAX_CQ_COUNT 64 /* 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
|
||||
@@ -286,6 +286,7 @@ struct apple_bce_softc {
|
||||
struct mtx sc_queues_lock;
|
||||
struct bce_queue_cq *sc_cq_list[BCE_MAX_CQ_COUNT];
|
||||
struct bce_queue_sq *sc_int_sq_list[BCE_MAX_QUEUE_COUNT];
|
||||
device_t sc_vhci_dev;
|
||||
};
|
||||
|
||||
/* Inline helpers */
|
||||
|
||||
@@ -57,6 +57,11 @@ bce_mailbox_send(struct bce_mailbox *mb, uint64_t msg, uint64_t *recv,
|
||||
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 */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,251 @@
|
||||
/*-
|
||||
* Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Apple T2 BCE Virtual USB Host Controller Interface (VHCI).
|
||||
* Translates USB operations into BCE firmware messages over DMA queues.
|
||||
*/
|
||||
|
||||
#ifndef _APPLE_BCE_VHCI_H_
|
||||
#define _APPLE_BCE_VHCI_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/sx.h>
|
||||
#include <sys/sema.h>
|
||||
#include <sys/taskqueue.h>
|
||||
|
||||
#include "apple_bce.h"
|
||||
|
||||
/* Forward declaration -- full USB headers included only in .c file */
|
||||
struct usb_bus;
|
||||
struct usb_device;
|
||||
struct usb_xfer;
|
||||
|
||||
/*
|
||||
* VHCI limits.
|
||||
*/
|
||||
#define BCE_VHCI_MAX_PORTS 16
|
||||
#define BCE_VHCI_MAX_DEVICES 32
|
||||
#define BCE_VHCI_MAX_ENDPOINTS 32
|
||||
|
||||
/* Queue element counts */
|
||||
#define BCE_VHCI_MSG_QUEUE_EL 32
|
||||
#define BCE_VHCI_EVT_QUEUE_EL 256
|
||||
#define BCE_VHCI_EVT_PENDING 32
|
||||
#define BCE_VHCI_TQ_EL 32 /* Transfer queue elements */
|
||||
#define BCE_VHCI_XFER_BUFSZ 4096 /* DMA buffer per transfer queue */
|
||||
|
||||
/*
|
||||
* VHCI message format (16 bytes, matches firmware protocol).
|
||||
*/
|
||||
struct bce_vhci_message {
|
||||
uint16_t cmd;
|
||||
uint16_t status;
|
||||
uint32_t param1;
|
||||
uint64_t param2;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* VHCI command IDs.
|
||||
*/
|
||||
enum bce_vhci_cmd_id {
|
||||
/* Controller commands */
|
||||
BCE_VHCI_CMD_CONTROLLER_ENABLE = 0x0001,
|
||||
BCE_VHCI_CMD_CONTROLLER_DISABLE = 0x0002,
|
||||
BCE_VHCI_CMD_CONTROLLER_START = 0x0003,
|
||||
BCE_VHCI_CMD_CONTROLLER_PAUSE = 0x0004,
|
||||
|
||||
/* Port commands */
|
||||
BCE_VHCI_CMD_PORT_POWER_ON = 0x0010,
|
||||
BCE_VHCI_CMD_PORT_POWER_OFF = 0x0011,
|
||||
BCE_VHCI_CMD_PORT_RESUME = 0x0012,
|
||||
BCE_VHCI_CMD_PORT_SUSPEND = 0x0013,
|
||||
BCE_VHCI_CMD_PORT_RESET = 0x0014,
|
||||
BCE_VHCI_CMD_PORT_DISABLE = 0x0015,
|
||||
BCE_VHCI_CMD_PORT_STATUS = 0x0016,
|
||||
BCE_VHCI_CMD_PORT_STATUS_CHANGE = 0x0018,
|
||||
|
||||
/* Device commands */
|
||||
BCE_VHCI_CMD_DEVICE_CREATE = 0x0030,
|
||||
BCE_VHCI_CMD_DEVICE_DESTROY = 0x0031,
|
||||
|
||||
/* Endpoint commands */
|
||||
BCE_VHCI_CMD_ENDPOINT_CREATE = 0x0040,
|
||||
BCE_VHCI_CMD_ENDPOINT_DESTROY = 0x0041,
|
||||
BCE_VHCI_CMD_ENDPOINT_SET_STATE = 0x0042,
|
||||
BCE_VHCI_CMD_ENDPOINT_REQ_STATE = 0x0043,
|
||||
BCE_VHCI_CMD_ENDPOINT_RESET = 0x0044,
|
||||
|
||||
/* Transfer commands */
|
||||
BCE_VHCI_CMD_TRANSFER_REQUEST = 0x1000,
|
||||
BCE_VHCI_CMD_CTRL_TRANSFER_STATUS = 0x1005,
|
||||
|
||||
/* Reply flag -- firmware replies have cmd | 0x8000 */
|
||||
BCE_VHCI_CMD_REPLY_FLAG = 0x8000,
|
||||
|
||||
/* Cancel flag -- timeout sends cmd | 0x4000 */
|
||||
BCE_VHCI_CMD_CANCEL_FLAG = 0x4000,
|
||||
};
|
||||
|
||||
/*
|
||||
* VHCI message status codes.
|
||||
*/
|
||||
enum bce_vhci_msg_status {
|
||||
BCE_VHCI_SUCCESS = 1,
|
||||
BCE_VHCI_ERROR = 2,
|
||||
BCE_VHCI_PIPE_STALL = 3,
|
||||
BCE_VHCI_ABORT = 4,
|
||||
BCE_VHCI_BAD_ARGUMENT = 5,
|
||||
BCE_VHCI_OVERRUN = 6,
|
||||
BCE_VHCI_INTERNAL_ERROR = 7,
|
||||
BCE_VHCI_NO_POWER = 8,
|
||||
BCE_VHCI_UNSUPPORTED = 9,
|
||||
};
|
||||
|
||||
/*
|
||||
* Endpoint states.
|
||||
*/
|
||||
enum bce_vhci_endpoint_state {
|
||||
BCE_VHCI_ENDP_ACTIVE = 0,
|
||||
BCE_VHCI_ENDP_PAUSED = 1,
|
||||
BCE_VHCI_ENDP_STALLED = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* Control transfer state machine.
|
||||
*/
|
||||
enum bce_vhci_ctrl_state {
|
||||
BCE_VHCI_CTRL_IDLE = 0,
|
||||
BCE_VHCI_CTRL_SETUP = 1, /* Awaiting setup XFER_REQ */
|
||||
BCE_VHCI_CTRL_DATA = 2, /* Awaiting data XFER_REQ */
|
||||
BCE_VHCI_CTRL_STATUS = 3, /* Awaiting CTRL_XFER_STATUS */
|
||||
};
|
||||
|
||||
/*
|
||||
* Pause sources (bitmask).
|
||||
*/
|
||||
#define BCE_VHCI_PAUSE_INTERNAL 0x01
|
||||
#define BCE_VHCI_PAUSE_FIRMWARE 0x02
|
||||
#define BCE_VHCI_PAUSE_SUSPEND 0x04
|
||||
#define BCE_VHCI_PAUSE_SHUTDOWN 0x08
|
||||
|
||||
/*
|
||||
* Port status bit mapping (firmware -> USB).
|
||||
* Firmware uses its own bit encoding; we translate in roothub_exec.
|
||||
*/
|
||||
#define BCE_VHCI_PORT_CONNECTED 0x0004
|
||||
#define BCE_VHCI_PORT_ENABLED 0x0010
|
||||
#define BCE_VHCI_PORT_SUSPENDED 0x0060
|
||||
#define BCE_VHCI_PORT_OVERCURRENT 0x0002
|
||||
#define BCE_VHCI_PORT_RESET 0x0008
|
||||
#define BCE_VHCI_PORT_C_CONNECTION 0x40000
|
||||
|
||||
/*
|
||||
* VHCI message queue (host -> device).
|
||||
*/
|
||||
struct bce_vhci_msg_queue {
|
||||
struct bce_queue_cq *cq;
|
||||
struct bce_queue_sq *sq;
|
||||
bus_dma_tag_t dma_tag;
|
||||
bus_dmamap_t dma_map;
|
||||
bus_addr_t dma_addr;
|
||||
struct bce_vhci_message *data;
|
||||
uint32_t el_count;
|
||||
};
|
||||
|
||||
/*
|
||||
* VHCI event queue (device -> host).
|
||||
* Single contiguous DMA buffer for all receive slots.
|
||||
*/
|
||||
struct bce_vhci_evt_queue {
|
||||
struct bce_queue_sq *sq;
|
||||
bus_dma_tag_t dma_tag;
|
||||
bus_dmamap_t dma_map;
|
||||
bus_addr_t dma_addr;
|
||||
struct bce_vhci_message *data;
|
||||
uint32_t el_count;
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
/*
|
||||
* VHCI command queue (synchronous command execution).
|
||||
*/
|
||||
struct bce_vhci_cmd_queue {
|
||||
struct bce_vhci_msg_queue *msg;
|
||||
struct sx exec_lock; /* Serialize callers */
|
||||
struct mtx lock;
|
||||
struct sema completion;
|
||||
struct bce_vhci_message response;
|
||||
volatile int pending;
|
||||
uint16_t expected_cmd; /* Filter late replies */
|
||||
};
|
||||
|
||||
/*
|
||||
* VHCI transfer queue (per endpoint).
|
||||
*
|
||||
* Each endpoint gets a CQ + IN SQ + OUT SQ triplet registered with
|
||||
* firmware as named DMA queues. The DMA buffer is used to shuttle
|
||||
* USB payloads between the USB stack's frame buffers and firmware.
|
||||
*/
|
||||
struct bce_vhci_transfer_queue {
|
||||
struct bce_vhci_softc *vhci;
|
||||
uint8_t dev_addr; /* firmware device id */
|
||||
uint8_t endp_addr; /* USB endpoint address */
|
||||
struct mtx lock; /* Protects SQ submission */
|
||||
struct bce_queue_cq *cq;
|
||||
struct bce_queue_sq *sq_in; /* Device -> host */
|
||||
struct bce_queue_sq *sq_out; /* Host -> device */
|
||||
struct usb_xfer *active_xfer;
|
||||
uint32_t paused_by;
|
||||
int active; /* Queues created with FW */
|
||||
int stalled;
|
||||
int dma_inflight; /* DMA pending */
|
||||
int create_pending; /* Deferred ep create */
|
||||
struct usb_endpoint_descriptor *create_edesc;
|
||||
struct usb_xfer *create_xfer; /* Deferred xfer */
|
||||
|
||||
/* DMA buffer for data transfer */
|
||||
bus_dma_tag_t dma_tag;
|
||||
bus_dmamap_t dma_map;
|
||||
bus_addr_t dma_addr;
|
||||
void *dma_buf;
|
||||
|
||||
/* Control transfer state machine */
|
||||
enum bce_vhci_ctrl_state ctrl_state;
|
||||
uint8_t ctrl_dir; /* UE_DIR_IN or UE_DIR_OUT */
|
||||
uint32_t ctrl_data_len; /* Expected data phase length */
|
||||
uint32_t ctrl_actual; /* Actual bytes transferred */
|
||||
int ctrl_data_done; /* IN DMA completion seen */
|
||||
int ctrl_status_pending; /* Deferred STATUS msg */
|
||||
struct bce_vhci_message ctrl_status_msg; /* Saved STATUS for defer */
|
||||
|
||||
/* Queued transfer waiting for active_xfer to finish */
|
||||
struct usb_xfer *pending_xfer;
|
||||
|
||||
/* Deferred firmware event (TRANSFER_REQUEST arrives before xfer) */
|
||||
int evt_pending;
|
||||
struct bce_vhci_message evt_saved;
|
||||
};
|
||||
|
||||
/*
|
||||
* VHCI per-device state.
|
||||
*/
|
||||
struct bce_vhci_device {
|
||||
int allocated; /* Device created with FW */
|
||||
uint8_t fw_dev_id; /* Firmware device ID */
|
||||
uint8_t port; /* Port number */
|
||||
struct bce_vhci_transfer_queue tq[BCE_VHCI_MAX_ENDPOINTS];
|
||||
};
|
||||
|
||||
/* VHCI softc is defined in apple_bce_vhci.c (depends on USB headers) */
|
||||
struct bce_vhci_softc;
|
||||
|
||||
/* VHCI driver interface */
|
||||
int bce_vhci_attach(struct apple_bce_softc *sc);
|
||||
int bce_vhci_detach(struct apple_bce_softc *sc);
|
||||
|
||||
#endif /* _APPLE_BCE_VHCI_H_ */
|
||||
@@ -135,6 +135,7 @@ DRIVER_MODULE(usbus, octusb, usb_driver, 0, 0);
|
||||
|
||||
/* Dual Mode Drivers */
|
||||
DRIVER_MODULE(usbus, dwcotg, usb_driver, 0, 0);
|
||||
DRIVER_MODULE(usbus, bce_vhci, usb_driver, 0, 0);
|
||||
|
||||
/*------------------------------------------------------------------------*
|
||||
* usb_probe
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
KMOD= apple_bce
|
||||
SRCS= apple_bce.c apple_bce_mailbox.c apple_bce_queue.c
|
||||
SRCS+= apple_bce_vhci.c
|
||||
SRCS+= bus_if.h device_if.h pci_if.h
|
||||
SRCS+= opt_usb.h opt_bus.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
||||
Reference in New Issue
Block a user