root/usr/src/uts/common/sys/usb/hcd/xhci/xhci.h
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright (c) 2018, Joyent, Inc.
 * Copyright (c) 2019 by Western Digital Corporation
 * Copyright 2024 Oxide Computer Company
 */

#ifndef _SYS_USB_XHCI_XHCI_H
#define _SYS_USB_XHCI_XHCI_H

/*
 * Extensible Host Controller Interface (xHCI) USB Driver
 */

#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/taskq_impl.h>
#include <sys/sysmacros.h>
#include <sys/usb/hcd/xhci/xhcireg.h>

#include <sys/usb/usba.h>
#include <sys/usb/usba/hcdi.h>
#include <sys/usb/hubd/hub.h>
#include <sys/usb/usba/hubdi.h>
#include <sys/usb/hubd/hubdvar.h>


#ifdef __cplusplus
extern "C" {
#endif

/*
 * The base segment for DMA attributes was determined to be 4k based on xHCI 1.1
 * / table 54: Data Structure Max Size, Boundary, and Alignment Requirement
 * Summary.  This indicates that the required alignment for most things is
 * PAGESIZE, which in our current implementation is required to be 4K. We
 * provide the ring segment value below for the things which need 64K alignment
 *
 * Similarly, in the same table, the maximum required alignment is 64 bytes,
 * hence we use that for everything.
 *
 * Next is the scatter/gather lengths. For most of the data structures, we only
 * want to have a single SGL entry, e.g. just a simple flat mapping. For many of
 * our transfers, we use the same logic to simplify the implementation of the
 * driver. However, for bulk transfers, which are the largest by far, we want to
 * be able to leverage SGLs to give us more DMA flexibility.
 *
 * We can transfer up to 64K in one transfer request block (TRB) which
 * corresponds to a single SGL entry. Each ring we create is a single page in
 * size and will support at most 256 TRBs. To try and give the operating system
 * flexibility when allocating DMA transfers, we've opted to allow up to 63
 * SGLs. Because there isn't a good way to support DMA windows with the xHCI
 * controller design, if this number is too small then DMA allocations and
 * binding might fail. If the DMA binding fails, the transfer will fail.
 *
 * The reason that we use 63 SGLs and not the expected 64 is that we always need
 * to allocate an additional TRB for the event data. This leaves us with a
 * nicely divisible number of entries.
 *
 * The final piece of this is the maximum sized transfer that the driver
 * advertises to the broader framework. This is currently sized at 512 KiB. For
 * reference the ehci driver sized this value at 640 KiB. It's important to
 * understand that this isn't reflected in the DMA attribute limitation, because
 * it's not an attribute of the hardware. Experimentally, this has proven to be
 * sufficient for most of the drivers that we support today. When considering
 * increasing this number, please note the impact that might have on the
 * required number of DMA SGL entries required to satisfy the allocation.
 *
 * The value of 512 KiB was originally based on the number of SGLs we supported
 * multiplied by the maximum transfer size. The original number of
 * XHCI_TRANSFER_DMA_SGL was 8. The 512 KiB value was based upon taking the
 * number of SGLs and assuming that each TRB used its maximum transfer size of
 * 64 KiB.
 */
#define XHCI_TRB_MAX_TRANSFER   65536   /* 64 KiB */
#define XHCI_DMA_ALIGN          64
#define XHCI_DEF_DMA_SGL        1
#define XHCI_TRANSFER_DMA_SGL   63
#define XHCI_MAX_TRANSFER       524288  /* 512 KiB */

/*
 * Properties and values for rerouting ehci ports to xhci.
 */
#define XHCI_PROP_REROUTE_DISABLE       0
#define XHCI_PROP_REROUTE_DEFAULT       1

/*
 * This number is a bit made up. Truthfully, the API here isn't the most useful
 * for what we need to define as it should really be based on the endpoint that
 * we're interested in rather than the device as a whole.
 *
 * We're basically being asked how many TRBs we're willing to schedule in one
 * go. There's no great way to come up with this number, so we basically are
 * making up something such that we use up a good portion of a ring, but not too
 * much of it.
 */
#define XHCI_ISOC_MAX_TRB       64

#ifdef  DEBUG
#define XHCI_DMA_SYNC(dma, flag)        VERIFY0(ddi_dma_sync( \
                                            (dma).xdb_dma_handle, 0, 0, \
                                            (flag)))
#else
#define XHCI_DMA_SYNC(dma, flag)        ((void) ddi_dma_sync( \
                                            (dma).xdb_dma_handle, 0, 0, \
                                            (flag)))
#endif

/*
 * TRBs need to indicate the number of remaining USB packets in the overall
 * transfer. This is a 5-bit value, which means that the maximum value we can
 * store in that TRD field is 31.
 */
#define XHCI_MAX_TDSIZE         31

/*
 * This defines a time in 2-ms ticks that is required to wait for the controller
 * to be ready to go. Section 5.4.8 of the XHCI specification in the description
 * of the PORTSC register indicates that the upper bound is 20 ms. Therefore the
 * number of ticks is 10.
 */
#define XHCI_POWER_GOOD 10

/*
 * Definitions to determine the default number of interrupts. Note that we only
 * bother with a single interrupt at this time, though we've arranged the driver
 * to make it possible to request more if, for some unlikely reason, it becomes
 * necessary.
 */
#define XHCI_NINTR      1

/*
 * Default interrupt modulation value. This enables us to have 4000 interrupts /
 * second. This is supposed to be the default value of the controller. See xHCI
 * 1.1 / 4.17.2 for more information.
 */
#define XHCI_IMOD_DEFAULT       0x000003F8U

/*
 * Definitions that surround the default values used in various contexts. These
 * come from various parts of the xHCI specification. In general, see xHCI 1.1 /
 * 4.8.2. Note that the MPS_MASK is used for ISOCH and INTR endpoints which have
 * different sizes.
 *
 * The burst member is a bit more complicated. By default for USB 2 devices, it
 * only matters for ISOCH and INTR endpoints and so we use the macros below to
 * pull it out of the endpoint description's max packet field. For USB 3, it
 * matters for non-control endpoints. However, it comes out of a companion
 * description.
 *
 * By default the mult member is zero for all cases except for super speed
 * ISOCH endpoints, where it comes from the companion descriptor.
 */
#define XHCI_CONTEXT_DEF_CERR           3
#define XHCI_CONTEXT_ISOCH_CERR         0
#define XHCI_CONTEXT_MPS_MASK           0x07ff
#define XHCI_CONTEXT_BURST_MASK         0x1800
#define XHCI_CONTEXT_BURST_SHIFT        11
#define XHCI_CONTEXT_DEF_MULT           0
#define XHCI_CONTEXT_DEF_MAX_ESIT       0
#define XHCI_CONTEXT_DEF_CTRL_ATL       8

/*
 * This number represents the number of transfers that we'll set up for a given
 * interrupt transfer. Note that the idea here is that we'll want to allocate a
 * certain number of transfers to basically ensure that we'll always be able to
 * have a transfer available, even if the system is a bit caught up in trying to
 * process it and for some reason we can't fire the interrupt. As such, we
 * basically want to have enough available that at the fastest interval (125 us)
 * that we have enough. So in this case we choose 8, with the assumption that we
 * should be able to process at least one in a given millisecond. Note that this
 * is not based in fact and is really just as much a guess and a hope.
 *
 * While we could then use less resources for other interrupt transfers that are
 * slower, starting with uniform resource usage will make things a bit easier.
 */
#define XHCI_INTR_IN_NTRANSFERS 8

/*
 * This number represents the number of xhci_transfer_t structures that we'll
 * set up for a given isochronous transfer polling request. A given isochronous
 * transfer may actually have multiple units of time associated with it. As
 * such, we basically want to treat this like a case of classic double
 * buffering. We have one ready to go while the other is being filled up. This
 * will compensate for additional latency in the system. This is smaller than
 * the Interrupt IN transfer case above as many callers may ask for multiple
 * intervals in a single request.
 */
#define XHCI_ISOC_IN_NTRANSFERS 2

#define XHCI_PERIODIC_IN_NTRANSFERS                                     \
        MAX(XHCI_ISOC_IN_NTRANSFERS, XHCI_INTR_IN_NTRANSFERS)

/*
 * Mask for a route string which is a 20-bit value.
 */
#define XHCI_ROUTE_MASK(x)      ((x) & 0xfffff)

/*
 * This is the default tick that we use for timeouts while endpoints have
 * outstanding, active, non-periodic transfers. We choose one second as the USBA
 * specifies timeouts in units of seconds. Note that this is in microseconds, so
 * it can be fed into drv_usectohz().
 */
#define XHCI_TICK_TIMEOUT_US    (MICROSEC)

/*
 * Set of bits that we need one of to indicate that this port has something
 * interesting on it.
 */
#define XHCI_HUB_INTR_CHANGE_MASK       (XHCI_PS_CSC | XHCI_PS_PEC | \
    XHCI_PS_WRC | XHCI_PS_OCC | XHCI_PS_PRC | XHCI_PS_PLC | XHCI_PS_CEC)

/*
 * These represent known issues with various xHCI controllers.
 *
 *      XHCI_QUIRK_NO_MSI       MSI support on this controller is known to be
 *                              broken.
 *
 *      XHCI_QUIRK_32_ONLY      Only use 32-bit DMA addreses with this
 *                              controller.
 *
 *      XHCI_QUIRK_INTC_EHCI    This is an Intel platform which supports
 *                              rerouting ports between EHCI and xHCI
 *                              controllers on the platform.
 */
typedef enum xhci_quirk {
        XHCI_QUIRK_NO_MSI       = 0x01,
        XHCI_QUIRK_32_ONLY      = 0x02,
        XHCI_QUIRK_INTC_EHCI    = 0x04
} xhci_quirk_t;

/*
 * xHCI capability parameter flags. These are documented in xHCI 1.1 / 5.3.6.
 */
typedef enum xhci_cap_flags {
        XCAP_AC64       = 0x001,
        XCAP_BNC        = 0x002,
        XCAP_CSZ        = 0x004,
        XCAP_PPC        = 0x008,
        XCAP_PIND       = 0x010,
        XCAP_LHRC       = 0x020,
        XCAP_LTC        = 0x040,
        XCAP_NSS        = 0x080,
        XCAP_PAE        = 0x100,
        XCAP_SPC        = 0x200,
        XCAP_SEC        = 0x400,
        XCAP_CFC        = 0x800
} xchi_cap_flags_t;

/*
 * Second set of capabilities, these are documented in xHCI 1.1 / 5.3.9.
 */
typedef enum xhci_cap2_flags {
        XCAP2_U3C       = 0x01,
        XCAP2_CMC       = 0x02,
        XCAP2_FMC       = 0x04,
        XCAP2_CTC       = 0x08,
        XCAP2_LEC       = 0x10,
        XCAP2_CIC       = 0x20
} xhci_cap2_flags_t;

/*
 * These represent and store the various capability registers that we'll need to
 * use. In addition, we stash a few other versioning related bits here. Note
 * that we cache more information than we might need so that we have it for
 * debugging purposes.
 */
typedef struct xhci_capability {
        uint8_t                 xcap_usb_vers;
        uint16_t                xcap_hci_vers;
        uint32_t                xcap_pagesize;
        uint8_t                 xcap_max_slots;
        uint16_t                xcap_max_intrs;
        uint8_t                 xcap_max_ports;
        boolean_t               xcap_ist_micro;
        uint8_t                 xcap_ist;
        uint16_t                xcap_max_esrt;
        boolean_t               xcap_scratch_restore;
        uint16_t                xcap_max_scratch;
        uint8_t                 xcap_u1_lat;
        uint16_t                xcap_u2_lat;
        xchi_cap_flags_t        xcap_flags;
        uint8_t                 xcap_max_psa;
        uint16_t                xcap_xecp_off;
        xhci_cap2_flags_t       xcap_flags2;
        int                     xcap_intr_types;
} xhci_capability_t;

/*
 * This represents a single logical DMA allocation. For the vast majority of
 * non-transfer cases, it only represents a single DMA buffer and not a
 * scatter-gather list.
 */
typedef struct xhci_dma_buffer {
        caddr_t                 xdb_va;         /* Buffer VA */
        size_t                  xdb_len;        /* Buffer logical len */
        ddi_acc_handle_t        xdb_acc_handle; /* Access handle */
        ddi_dma_handle_t        xdb_dma_handle; /* DMA handle */
        int                     xdb_ncookies;   /* Number of actual cookies */
        ddi_dma_cookie_t        xdb_cookies[XHCI_TRANSFER_DMA_SGL];
} xhci_dma_buffer_t;

/*
 * This is a single transfer descriptor. It's packed to match the hardware
 * layout.
 */
#pragma pack(1)
typedef struct xhci_trb {
        uint64_t        trb_addr;
        uint32_t        trb_status;
        uint32_t        trb_flags;
} xhci_trb_t;
#pragma pack()

/*
 * This represents a single transfer that we want to allocate and perform.
 */
typedef struct xhci_transfer {
        list_node_t             xt_link;
        hrtime_t                xt_sched_time;
        xhci_dma_buffer_t       xt_buffer;
        uint_t                  xt_ntrbs;
        uint_t                  xt_short;
        uint_t                  xt_timeout;
        usb_cr_t                xt_cr;
        boolean_t               xt_data_tohost;
        xhci_trb_t              *xt_trbs;
        uint64_t                *xt_trbs_pa;
        usb_isoc_pkt_descr_t    *xt_isoc;
        usb_opaque_t            xt_usba_req;
} xhci_transfer_t;

/*
 * A transfer on a periodic endpoint does not generally have an associated USB
 * request, except in the case of a oneshot Interrupt IN transfer.
 */
#define XHCI_IS_ONESHOT_XFER(xt)        ((xt)->xt_usba_req != NULL)

/*
 * This represents a ring in xHCI, upon which event, transfer, and command TRBs
 * are scheduled.
 */
typedef struct xhci_ring {
        xhci_dma_buffer_t       xr_dma;
        uint_t                  xr_ntrb;
        xhci_trb_t              *xr_trb;
        uint_t                  xr_head;
        uint_t                  xr_tail;
        uint8_t                 xr_cycle;
} xhci_ring_t;

/*
 * This structure is used to represent the xHCI Device Context Base Address
 * Array. It's defined in section 6.1 of the specification and is required for
 * the controller to start.
 *
 * The maximum number of slots supported is always 256, therefore we size this
 * structure at its maximum.
 */
#define XHCI_MAX_SLOTS  256
#define XHCI_DCBAA_SCRATCHPAD_INDEX     0

typedef struct xhci_dcbaa {
        uint64_t                *xdc_base_addrs;
        xhci_dma_buffer_t       xdc_dma;
} xhci_dcbaa_t;

typedef struct xhci_scratchpad {
        uint64_t                *xsp_addrs;
        xhci_dma_buffer_t       xsp_addr_dma;
        xhci_dma_buffer_t       *xsp_scratch_dma;
} xhci_scratchpad_t;

/*
 * Contexts. These structures are inserted into the DCBAA above and are used for
 * describing the state of the system. Note, that while many of these are
 * 32-bytes in size, the xHCI specification defines that they'll be extended to
 * 64-bytes with all the extra bytes as zeros if the CSZ flag is set in the
 * HCCPARAMS1 register, e.g. we have the flag XCAP_CSZ set.
 *
 * The device context covers the slot context and 31 endpoints.
 */
#define XHCI_DEVICE_CONTEXT_32  1024
#define XHCI_DEVICE_CONTEXT_64  2048
#define XHCI_NUM_ENDPOINTS      31
#define XHCI_DEFAULT_ENDPOINT   0

#pragma pack(1)
typedef struct xhci_slot_context {
        uint32_t        xsc_info;
        uint32_t        xsc_info2;
        uint32_t        xsc_tt;
        uint32_t        xsc_state;
        uint32_t        xsc_reserved[4];
} xhci_slot_context_t;

typedef struct xhci_endpoint_context {
        uint32_t        xec_info;
        uint32_t        xec_info2;
        uint64_t        xec_dequeue;
        uint32_t        xec_txinfo;
        uint32_t        xec_reserved[3];
} xhci_endpoint_context_t;

typedef struct xhci_input_context {
        uint32_t        xic_drop_flags;
        uint32_t        xic_add_flags;
        uint32_t        xic_reserved[6];
} xhci_input_context_t;
#pragma pack()

/*
 * Definitions and structures for maintaining the event ring.
 */
#define XHCI_EVENT_NSEGS        1

#pragma pack(1)
typedef struct xhci_event_segment {
        uint64_t        xes_addr;
        uint16_t        xes_size;
        uint16_t        xes_rsvd0;
        uint32_t        xes_rsvd1;
} xhci_event_segment_t;
#pragma pack()

typedef struct xhci_event_ring {
        xhci_event_segment_t    *xev_segs;
        xhci_dma_buffer_t       xev_dma;
        xhci_ring_t             xev_ring;
} xhci_event_ring_t;

typedef enum xhci_command_ring_state {
        XHCI_COMMAND_RING_IDLE          = 0x00,
        XHCI_COMMAND_RING_RUNNING       = 0x01,
        XHCI_COMMAND_RING_ABORTING      = 0x02,
        XHCI_COMMAND_RING_ABORT_DONE    = 0x03
} xhci_command_ring_state_t;

typedef struct xhci_command_ring {
        xhci_ring_t                     xcr_ring;
        kmutex_t                        xcr_lock;
        kcondvar_t                      xcr_cv;
        list_t                          xcr_commands;
        timeout_id_t                    xcr_timeout;
        xhci_command_ring_state_t       xcr_state;
} xhci_command_ring_t;

/*
 * Individual command states.
 *
 * XHCI_COMMAND_S_INIT          The command has yet to be inserted into the
 *                              command ring.
 *
 * XHCI_COMMAND_S_QUEUED        The command is queued in the command ring.
 *
 * XHCI_COMMAND_S_RECEIVED      A command completion for this was received.
 *
 * XHCI_COMMAND_S_DONE          The command has been executed. Note that it may
 *                              have been aborted.
 *
 * XHCI_COMMAND_S_RESET         The ring is being reset due to a fatal error and
 *                              this command has been removed from the ring.
 *                              This means it has been aborted, but it was not
 *                              the cause of the abort.
 *
 * Note, when adding states, anything after XHCI_COMMAND_S_DONE implies that
 * upon reaching this state, it is no longer in the ring.
 */
typedef enum xhci_command_state {
        XHCI_COMMAND_S_INIT     = 0x00,
        XHCI_COMMAND_S_QUEUED   = 0x01,
        XHCI_COMMAND_S_RECEIVED = 0x02,
        XHCI_COMMAND_S_DONE     = 0x03,
        XHCI_COMMAND_S_RESET    = 0x04
} xhci_command_state_t;

/*
 * The TRB contents here are always kept in host byte order and are transformed
 * to little endian when actually scheduled on the ring.
 */
typedef struct xhci_command {
        list_node_t             xco_link;
        kcondvar_t              xco_cv;
        xhci_trb_t              xco_req;
        xhci_trb_t              xco_res;
        xhci_command_state_t    xco_state;
} xhci_command_t;

typedef enum xhci_endpoint_state {
        XHCI_ENDPOINT_PERIODIC          = 0x01,
        XHCI_ENDPOINT_HALTED            = 0x02,
        XHCI_ENDPOINT_QUIESCE           = 0x04,
        XHCI_ENDPOINT_TIMED_OUT         = 0x08,
        /*
         * This enpdoint is being torn down and should make sure it de-schedules
         * itself.
         */
        XHCI_ENDPOINT_TEARDOWN          = 0x10,
        /*
         * This endpoint is currently used in polled I/O mode by the
         * kernel debugger.
         */
        XHCI_ENDPOINT_POLLED            = 0x20,
        /*
         * This endpoint is open and in use by a pipe.
         */
        XHCI_ENDPOINT_OPEN              = 0x40,
} xhci_endpoint_state_t;

/*
 * This is a composite of states that we need to watch for. We don't
 * want to allow ourselves to set one of these flags while one of them
 * is currently active.
 */
#define XHCI_ENDPOINT_SERIALIZE         (XHCI_ENDPOINT_QUIESCE |        \
                                        XHCI_ENDPOINT_TIMED_OUT)

/*
 * This is a composite of states that we need to make sure that if set, we do
 * not schedule activity on the ring.
 */
#define XHCI_ENDPOINT_DONT_SCHEDULE     (XHCI_ENDPOINT_HALTED |         \
                                        XHCI_ENDPOINT_QUIESCE |         \
                                        XHCI_ENDPOINT_TIMED_OUT)

/*
 * Forwards required for the endpoint
 */
struct xhci_device;
struct xhci;

typedef struct xhci_endpoint_params {
        boolean_t               xepp_configured;
        uint_t                  xepp_eptype;
        uint_t                  xepp_burst;
        uint_t                  xepp_ival;
        uint_t                  xepp_max_esit;
        uint_t                  xepp_avgtrb;
        uint_t                  xepp_mps;
        uint_t                  xepp_mult;
        uint_t                  xepp_cerr;
} xhci_endpoint_params_t;

typedef struct xhci_endpoint {
        struct xhci             *xep_xhci;
        struct xhci_device      *xep_xd;
        uint_t                  xep_num;
        uint_t                  xep_type;
        xhci_endpoint_state_t   xep_state;
        kcondvar_t              xep_state_cv;
        timeout_id_t            xep_timeout;
        list_t                  xep_transfers;
        usba_pipe_handle_data_t *xep_pipe;
        xhci_ring_t             xep_ring;
        xhci_endpoint_params_t  xep_params;
} xhci_endpoint_t;

typedef struct xhci_device {
        list_node_t             xd_link;
        usb_port_t              xd_port;
        uint8_t                 xd_slot;
        boolean_t               xd_addressed;
        usba_device_t           *xd_usbdev;
        xhci_dma_buffer_t       xd_ictx;
        kmutex_t                xd_imtx;        /* Protects input contexts */
        xhci_input_context_t    *xd_input;
        xhci_slot_context_t     *xd_slotin;
        xhci_endpoint_context_t *xd_endin[XHCI_NUM_ENDPOINTS];
        xhci_dma_buffer_t       xd_octx;
        xhci_slot_context_t     *xd_slotout;
        xhci_endpoint_context_t *xd_endout[XHCI_NUM_ENDPOINTS];
        xhci_endpoint_t         *xd_endpoints[XHCI_NUM_ENDPOINTS];
} xhci_device_t;

typedef enum xhci_periodic_state {
        XHCI_PERIODIC_POLL_IDLE = 0x0,
        XHCI_PERIODIC_POLL_ACTIVE,
        XHCI_PERIODIC_POLL_NOMEM,
        XHCI_PERIODIC_POLL_STOPPING
} xhci_periodic_state_t;

typedef struct xhci_periodic_pipe {
        xhci_periodic_state_t   xpp_poll_state;
        usb_opaque_t            xpp_usb_req;
        size_t                  xpp_tsize;
        uint_t                  xpp_ntransfers;
        xhci_transfer_t         *xpp_transfers[XHCI_PERIODIC_IN_NTRANSFERS];
} xhci_periodic_pipe_t;

typedef struct xhci_pipe {
        list_node_t             xp_link;
        hrtime_t                xp_opentime;
        usba_pipe_handle_data_t *xp_pipe;
        xhci_endpoint_t         *xp_ep;
        xhci_periodic_pipe_t    xp_periodic;
} xhci_pipe_t;

typedef struct xhci_usba {
        usba_hcdi_ops_t         *xa_ops;
        ddi_dma_attr_t          xa_dma_attr;
        usb_dev_descr_t         xa_dev_descr;
        usb_ss_hub_descr_t      xa_hub_descr;
        usba_pipe_handle_data_t *xa_intr_cb_ph;
        usb_intr_req_t          *xa_intr_cb_req;
        list_t                  xa_devices;
        list_t                  xa_pipes;
} xhci_usba_t;

typedef enum xhci_attach_seq {
        XHCI_ATTACH_FM          = 0x1 << 0,
        XHCI_ATTACH_PCI_CONFIG  = 0x1 << 1,
        XHCI_ATTACH_REGS_MAP    = 0x1 << 2,
        XHCI_ATTACH_INTR_ALLOC  = 0x1 << 3,
        XHCI_ATTACH_INTR_ADD    = 0x1 << 4,
        XHCI_ATTACH_SYNCH       = 0x1 << 5,
        XHCI_ATTACH_INTR_ENABLE = 0x1 << 6,
        XHCI_ATTACH_STARTED     = 0x1 << 7,
        XHCI_ATTACH_USBA        = 0x1 << 8,
        XHCI_ATTACH_ROOT_HUB    = 0x1 << 9
} xhci_attach_seq_t;

typedef enum xhci_state_flags {
        XHCI_S_ERROR            = 0x1 << 0
} xhci_state_flags_t;

typedef struct xhci {
        dev_info_t              *xhci_dip;
        xhci_attach_seq_t       xhci_seq;
        int                     xhci_fm_caps;
        ddi_acc_handle_t        xhci_cfg_handle;
        uint16_t                xhci_vendor_id;
        uint16_t                xhci_device_id;
        caddr_t                 xhci_regs_base;
        ddi_acc_handle_t        xhci_regs_handle;
        uint_t                  xhci_regs_capoff;
        uint_t                  xhci_regs_operoff;
        uint_t                  xhci_regs_runoff;
        uint_t                  xhci_regs_dooroff;
        xhci_capability_t       xhci_caps;
        xhci_quirk_t            xhci_quirks;
        ddi_intr_handle_t       xhci_intr_hdl;
        int                     xhci_intr_num;
        int                     xhci_intr_type;
        uint_t                  xhci_intr_pri;
        int                     xhci_intr_caps;
        xhci_dcbaa_t            xhci_dcbaa;
        xhci_scratchpad_t       xhci_scratchpad;
        xhci_command_ring_t     xhci_command;
        xhci_event_ring_t       xhci_event;
        taskq_ent_t             xhci_tqe;
        kmutex_t                xhci_lock;
        kcondvar_t              xhci_statecv;
        xhci_state_flags_t      xhci_state;
        xhci_usba_t             xhci_usba;
} xhci_t;

/*
 * The xHCI memory mapped registers come in four different categories. The
 * offset to them is variable. These represent the given register set that we're
 * after.
 */
typedef enum xhci_reg_type {
        XHCI_R_CAP,
        XHCI_R_OPER,
        XHCI_R_RUN,
        XHCI_R_DOOR
} xhci_reg_type_t;

/*
 * Polled I/O data structure
 */
typedef struct xhci_polled {
        /*
         * Pointer to the xhcip structure for the device that is to  be
         * used as input in polled mode.
         */
        xhci_t                  *xhci_polled_xhci;

        /*
         * Pipe handle for the pipe that is to be used as input device
         * in POLLED mode.
         */
        usba_pipe_handle_data_t *xhci_polled_input_pipe_handle;

        /* Endpoint for the above */
        xhci_endpoint_t         *xhci_polled_endpoint;

        /*
         * The buffer that the USB HDI scan codes are copied into.
         * A USB keyboard will report up to 8 bytes consisting of the
         * modifier status, a reserved byte and up to 6 key presses.
         * This buffer is sized to be large enough for one such report.
         */
        uchar_t                 xhci_polled_buf[8];

        /*
         * Track how many times xhci_polled_input_enter() and
         * xhci_polled_input_exit() have been called so that the host
         * controller isn't switched back to OS mode prematurely.
         */
        uint_t                  xhci_polled_entry;

        /*
         * Remember persistent errors that will prevent us from reading
         * further input to avoid repeatedly polling to no avail
         */
        int                     xhci_polled_persistent_error;
} xhci_polled_t;

/*
 * Helper functions
 */
extern xhci_t *xhci_hcdi_get_xhcip_from_dev(usba_device_t *);
extern xhci_device_t *xhci_device_lookup_by_slot(xhci_t *, int);

/*
 * Quirks related functions
 */
extern void xhci_quirks_populate(xhci_t *);
extern void xhci_reroute_intel(xhci_t *);

/*
 * Interrupt related functions
 */
extern uint_t xhci_intr(caddr_t, caddr_t);
extern boolean_t xhci_ddi_intr_disable(xhci_t *);
extern boolean_t xhci_ddi_intr_enable(xhci_t *);
extern int xhci_intr_conf(xhci_t *);

/*
 * DMA related functions
 */
extern int xhci_check_dma_handle(xhci_t *, xhci_dma_buffer_t *);
extern void xhci_dma_acc_attr(xhci_t *, ddi_device_acc_attr_t *);
extern void xhci_dma_dma_attr(xhci_t *, ddi_dma_attr_t *);
extern void xhci_dma_scratchpad_attr(xhci_t *, ddi_dma_attr_t *);
extern void xhci_dma_transfer_attr(xhci_t *, ddi_dma_attr_t *, uint_t);
extern void xhci_dma_free(xhci_dma_buffer_t *);
extern boolean_t xhci_dma_alloc(xhci_t *, xhci_dma_buffer_t *, ddi_dma_attr_t *,
    ddi_device_acc_attr_t *, boolean_t, size_t, boolean_t);
extern uint64_t xhci_dma_pa(xhci_dma_buffer_t *);

/*
 * DMA Transfer Ring functions
 */
extern xhci_transfer_t *xhci_transfer_alloc(xhci_t *, xhci_endpoint_t *, size_t,
    uint_t, int);
extern void xhci_transfer_free(xhci_t *, xhci_transfer_t *);
extern void xhci_transfer_copy(xhci_transfer_t *, void *, size_t, boolean_t);
extern int xhci_transfer_sync(xhci_t *, xhci_transfer_t *, uint_t);
extern void xhci_transfer_trb_fill_data(xhci_endpoint_t *, xhci_transfer_t *,
    int, boolean_t);
extern void xhci_transfer_calculate_isoc(xhci_device_t *, xhci_endpoint_t *,
    uint_t, uint_t *, uint_t *);

/*
 * Context (DCBAA, Scratchpad, Slot) functions
 */
extern int xhci_context_init(xhci_t *);
extern void xhci_context_fini(xhci_t *);
extern boolean_t xhci_context_slot_output_init(xhci_t *, xhci_device_t *);
extern void xhci_context_slot_output_fini(xhci_t *, xhci_device_t *);

/*
 * Command Ring Functions
 */
extern int xhci_command_ring_init(xhci_t *);
extern void xhci_command_ring_fini(xhci_t *);
extern boolean_t xhci_command_event_callback(xhci_t *, xhci_trb_t *trb);

extern void xhci_command_init(xhci_command_t *);
extern void xhci_command_fini(xhci_command_t *);

extern int xhci_command_enable_slot(xhci_t *, uint8_t *);
extern int xhci_command_disable_slot(xhci_t *, uint8_t);
extern int xhci_command_set_address(xhci_t *, xhci_device_t *, boolean_t);
extern int xhci_command_configure_endpoint(xhci_t *, xhci_device_t *);
extern int xhci_command_evaluate_context(xhci_t *, xhci_device_t *);
extern int xhci_command_reset_endpoint(xhci_t *, xhci_device_t *,
    xhci_endpoint_t *);
extern int xhci_command_set_tr_dequeue(xhci_t *, xhci_device_t *,
    xhci_endpoint_t *);
extern int xhci_command_stop_endpoint(xhci_t *, xhci_device_t *,
    xhci_endpoint_t *);

/*
 * Event Ring Functions
 */
extern int xhci_event_init(xhci_t *);
extern void xhci_event_fini(xhci_t *);
extern boolean_t xhci_event_process_trb(xhci_t *, xhci_trb_t *);
extern boolean_t xhci_event_process(xhci_t *);

/*
 * General Ring functions
 */
extern void xhci_ring_free(xhci_ring_t *);
extern int xhci_ring_reset(xhci_t *, xhci_ring_t *);
extern int xhci_ring_alloc(xhci_t *, xhci_ring_t *);

/*
 * Event Ring (Consumer) oriented functions.
 */
extern xhci_trb_t *xhci_ring_event_advance(xhci_ring_t *);


/*
 * Command and Transfer Ring (Producer) oriented functions.
 */
extern boolean_t xhci_ring_trb_tail_valid(xhci_ring_t *, uint64_t);
extern int xhci_ring_trb_valid_range(xhci_ring_t *, uint64_t, uint_t);

extern boolean_t xhci_ring_trb_space(xhci_ring_t *, uint_t);
extern void xhci_ring_trb_fill(xhci_ring_t *, uint_t, xhci_trb_t *, uint64_t *,
    boolean_t);
extern void xhci_ring_trb_produce(xhci_ring_t *, uint_t);
extern boolean_t xhci_ring_trb_consumed(xhci_ring_t *, uint64_t);
extern void xhci_ring_trb_put(xhci_ring_t *, xhci_trb_t *);
extern void xhci_ring_skip(xhci_ring_t *);
extern void xhci_ring_skip_transfer(xhci_ring_t *, xhci_transfer_t *);

/*
 * MMIO related functions. Note callers are responsible for checking with FM
 * after accessing registers.
 */
extern int xhci_check_regs_acc(xhci_t *);

extern uint8_t xhci_get8(xhci_t *, xhci_reg_type_t, uintptr_t);
extern uint16_t xhci_get16(xhci_t *, xhci_reg_type_t, uintptr_t);
extern uint32_t xhci_get32(xhci_t *, xhci_reg_type_t, uintptr_t);
extern uint64_t xhci_get64(xhci_t *, xhci_reg_type_t, uintptr_t);

extern void xhci_put8(xhci_t *, xhci_reg_type_t, uintptr_t, uint8_t);
extern void xhci_put16(xhci_t *, xhci_reg_type_t, uintptr_t, uint16_t);
extern void xhci_put32(xhci_t *, xhci_reg_type_t, uintptr_t, uint32_t);
extern void xhci_put64(xhci_t *, xhci_reg_type_t, uintptr_t, uint64_t);

/*
 * Runtime FM related functions
 */
extern void xhci_fm_runtime_reset(xhci_t *);

/*
 * Endpoint related functions
 */
extern int xhci_endpoint_init(xhci_t *, xhci_device_t *,
    usba_pipe_handle_data_t *);
extern int xhci_endpoint_reopen(xhci_t *, xhci_device_t *,
    xhci_endpoint_t *, usba_pipe_handle_data_t *);
extern void xhci_endpoint_close(xhci_t *, xhci_endpoint_t *);
extern int xhci_endpoint_unconfigure(xhci_t *, xhci_device_t *,
    xhci_endpoint_t *);
extern void xhci_endpoint_fini(xhci_device_t *, int);
extern int xhci_endpoint_update_default(xhci_t *, xhci_device_t *,
    xhci_endpoint_t *);
extern void xhci_endpoint_timeout_cancel(xhci_t *, xhci_endpoint_t *);

extern int xhci_endpoint_setup_default_context(xhci_t *, xhci_device_t *,
    xhci_endpoint_t *);

extern uint_t xhci_endpoint_pipe_to_epid(usba_pipe_handle_data_t *);
extern boolean_t xhci_endpoint_is_periodic_in(xhci_endpoint_t *);

extern void xhci_endpoint_serialize(xhci_t *, xhci_endpoint_t *);
extern int xhci_endpoint_quiesce(xhci_t *, xhci_device_t *, xhci_endpoint_t *);
extern int xhci_endpoint_schedule(xhci_t *, xhci_device_t *, xhci_endpoint_t *,
    xhci_transfer_t *, boolean_t);
extern int xhci_endpoint_ring(xhci_t *, xhci_device_t *, xhci_endpoint_t *);
extern boolean_t xhci_endpoint_transfer_callback(xhci_t *, xhci_trb_t *);

extern xhci_transfer_t *xhci_endpoint_determine_transfer(xhci_t *,
    xhci_endpoint_t *, xhci_trb_t *, uint_t *);

/*
 * USB Framework related functions
 */
extern int xhci_hcd_init(xhci_t *);
extern void xhci_hcd_fini(xhci_t *);

/*
 * Root hub related functions
 */
extern int xhci_root_hub_init(xhci_t *);
extern int xhci_root_hub_fini(xhci_t *);
extern int xhci_root_hub_ctrl_req(xhci_t *, usba_pipe_handle_data_t *,
    usb_ctrl_req_t *);
extern void xhci_root_hub_psc_callback(xhci_t *);
extern int xhci_root_hub_intr_root_enable(xhci_t *, usba_pipe_handle_data_t *,
    usb_intr_req_t *);
extern void xhci_root_hub_intr_root_disable(xhci_t *);

/*
 * Polled I/O functions
 */
extern int xhci_hcdi_console_input_init(usba_pipe_handle_data_t *, uchar_t **,
    usb_console_info_impl_t *);
extern int xhci_hcdi_console_input_fini(usb_console_info_impl_t *);
extern int xhci_hcdi_console_input_enter(usb_console_info_impl_t *);
extern int xhci_hcdi_console_read(usb_console_info_impl_t *, uint_t *);
extern int xhci_hcdi_console_input_exit(usb_console_info_impl_t *);
extern int xhci_hcdi_console_output_init(usba_pipe_handle_data_t *,
    usb_console_info_impl_t *);
extern int xhci_hcdi_console_output_fini(usb_console_info_impl_t *);
extern int xhci_hcdi_console_output_enter(usb_console_info_impl_t *);
extern int xhci_hcdi_console_write(usb_console_info_impl_t *, uchar_t *,
    uint_t, uint_t *);
extern int xhci_hcdi_console_output_exit(usb_console_info_impl_t *);

/*
 * Logging functions
 */
extern void xhci_log(xhci_t *xhcip, const char *fmt, ...) __KPRINTFLIKE(2);
extern void xhci_error(xhci_t *xhcip, const char *fmt, ...) __KPRINTFLIKE(2);

/*
 * Misc. data
 */
extern void *xhci_soft_state;

#ifdef __cplusplus
}
#endif

#endif /* _SYS_USB_XHCI_XHCI_H */