1300829Sgrehan/*-
2300829Sgrehan * Copyright (c) 2014 Leon Dang <ldang@nahannisys.com>
3300829Sgrehan * All rights reserved.
4300829Sgrehan *
5300829Sgrehan * Redistribution and use in source and binary forms, with or without
6300829Sgrehan * modification, are permitted provided that the following conditions
7300829Sgrehan * are met:
8300829Sgrehan * 1. Redistributions of source code must retain the above copyright
9300829Sgrehan *    notice, this list of conditions and the following disclaimer.
10300829Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
11300829Sgrehan *    notice, this list of conditions and the following disclaimer in the
12300829Sgrehan *    documentation and/or other materials provided with the distribution.
13300829Sgrehan *
14300829Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15300829Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16300829Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17300829Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18300829Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19300829Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20300829Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21300829Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22300829Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23300829Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24300829Sgrehan * SUCH DAMAGE.
25300829Sgrehan */
26300829Sgrehan/*
27300829Sgrehan   XHCI options:
28300829Sgrehan    -s <n>,xhci,{devices}
29300829Sgrehan
30300829Sgrehan   devices:
31300829Sgrehan     ums             USB tablet mouse
32300829Sgrehan */
33300829Sgrehan#include <sys/cdefs.h>
34300829Sgrehan__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/pci_xhci.c 302408 2016-07-08 00:04:57Z gjb $");
35300829Sgrehan
36300829Sgrehan#include <sys/param.h>
37300829Sgrehan#include <sys/uio.h>
38300829Sgrehan#include <sys/types.h>
39300829Sgrehan#include <sys/queue.h>
40300829Sgrehan
41300829Sgrehan#include <stdio.h>
42300829Sgrehan#include <stdlib.h>
43300829Sgrehan#include <stdint.h>
44300829Sgrehan#include <string.h>
45300829Sgrehan#include <errno.h>
46300829Sgrehan#include <pthread.h>
47300829Sgrehan#include <unistd.h>
48300829Sgrehan
49300829Sgrehan#include <dev/usb/usbdi.h>
50300829Sgrehan#include <dev/usb/usb.h>
51300829Sgrehan#include <dev/usb/usb_freebsd.h>
52300829Sgrehan#include <xhcireg.h>
53300829Sgrehan
54300829Sgrehan#include "bhyverun.h"
55300829Sgrehan#include "pci_emul.h"
56300829Sgrehan#include "pci_xhci.h"
57300829Sgrehan#include "usb_emul.h"
58300829Sgrehan
59300829Sgrehan
60300829Sgrehanstatic int xhci_debug = 0;
61300829Sgrehan#define	DPRINTF(params) if (xhci_debug) printf params
62300829Sgrehan#define	WPRINTF(params) printf params
63300829Sgrehan
64300829Sgrehan
65300829Sgrehan#define	XHCI_NAME		"xhci"
66300829Sgrehan#define	XHCI_MAX_DEVS		8	/* 4 USB3 + 4 USB2 devs */
67300829Sgrehan
68300829Sgrehan#define	XHCI_MAX_SLOTS		64	/* min allowed by Windows drivers */
69300829Sgrehan
70300829Sgrehan/*
71300829Sgrehan * XHCI data structures can be up to 64k, but limit paddr_guest2host mapping
72300829Sgrehan * to 4k to avoid going over the guest physical memory barrier.
73300829Sgrehan */
74300829Sgrehan#define	XHCI_PADDR_SZ		4096	/* paddr_guest2host max size */
75300829Sgrehan
76300829Sgrehan#define	XHCI_ERST_MAX		0	/* max 2^entries event ring seg tbl */
77300829Sgrehan
78300829Sgrehan#define	XHCI_CAPLEN		(4*8)	/* offset of op register space */
79300829Sgrehan#define	XHCI_HCCPRAMS2		0x1C	/* offset of HCCPARAMS2 register */
80300829Sgrehan#define	XHCI_PORTREGS_START	0x400
81300829Sgrehan#define	XHCI_DOORBELL_MAX	256
82300829Sgrehan
83300829Sgrehan#define	XHCI_STREAMS_MAX	1	/* 4-15 in XHCI spec */
84300829Sgrehan
85300829Sgrehan/* caplength and hci-version registers */
86300829Sgrehan#define	XHCI_SET_CAPLEN(x)		((x) & 0xFF)
87300829Sgrehan#define	XHCI_SET_HCIVERSION(x)		(((x) & 0xFFFF) << 16)
88300829Sgrehan#define	XHCI_GET_HCIVERSION(x)		(((x) >> 16) & 0xFFFF)
89300829Sgrehan
90300829Sgrehan/* hcsparams1 register */
91300829Sgrehan#define	XHCI_SET_HCSP1_MAXSLOTS(x)	((x) & 0xFF)
92300829Sgrehan#define	XHCI_SET_HCSP1_MAXINTR(x)	(((x) & 0x7FF) << 8)
93300829Sgrehan#define	XHCI_SET_HCSP1_MAXPORTS(x)	(((x) & 0xFF) << 24)
94300829Sgrehan
95300829Sgrehan/* hcsparams2 register */
96300829Sgrehan#define	XHCI_SET_HCSP2_IST(x)		((x) & 0x0F)
97300829Sgrehan#define	XHCI_SET_HCSP2_ERSTMAX(x)	(((x) & 0x0F) << 4)
98300829Sgrehan#define	XHCI_SET_HCSP2_MAXSCRATCH_HI(x)	(((x) & 0x1F) << 21)
99300829Sgrehan#define	XHCI_SET_HCSP2_MAXSCRATCH_LO(x)	(((x) & 0x1F) << 27)
100300829Sgrehan
101300829Sgrehan/* hcsparams3 register */
102300829Sgrehan#define	XHCI_SET_HCSP3_U1EXITLATENCY(x)	((x) & 0xFF)
103300829Sgrehan#define	XHCI_SET_HCSP3_U2EXITLATENCY(x)	(((x) & 0xFFFF) << 16)
104300829Sgrehan
105300829Sgrehan/* hccparams1 register */
106300829Sgrehan#define	XHCI_SET_HCCP1_AC64(x)		((x) & 0x01)
107300829Sgrehan#define	XHCI_SET_HCCP1_BNC(x)		(((x) & 0x01) << 1)
108300829Sgrehan#define	XHCI_SET_HCCP1_CSZ(x)		(((x) & 0x01) << 2)
109300829Sgrehan#define	XHCI_SET_HCCP1_PPC(x)		(((x) & 0x01) << 3)
110300829Sgrehan#define	XHCI_SET_HCCP1_PIND(x)		(((x) & 0x01) << 4)
111300829Sgrehan#define	XHCI_SET_HCCP1_LHRC(x)		(((x) & 0x01) << 5)
112300829Sgrehan#define	XHCI_SET_HCCP1_LTC(x)		(((x) & 0x01) << 6)
113300829Sgrehan#define	XHCI_SET_HCCP1_NSS(x)		(((x) & 0x01) << 7)
114300829Sgrehan#define	XHCI_SET_HCCP1_PAE(x)		(((x) & 0x01) << 8)
115300829Sgrehan#define	XHCI_SET_HCCP1_SPC(x)		(((x) & 0x01) << 9)
116300829Sgrehan#define	XHCI_SET_HCCP1_SEC(x)		(((x) & 0x01) << 10)
117300829Sgrehan#define	XHCI_SET_HCCP1_CFC(x)		(((x) & 0x01) << 11)
118300829Sgrehan#define	XHCI_SET_HCCP1_MAXPSA(x)	(((x) & 0x0F) << 12)
119300829Sgrehan#define	XHCI_SET_HCCP1_XECP(x)		(((x) & 0xFFFF) << 16)
120300829Sgrehan
121300829Sgrehan/* hccparams2 register */
122300829Sgrehan#define	XHCI_SET_HCCP2_U3C(x)		((x) & 0x01)
123300829Sgrehan#define	XHCI_SET_HCCP2_CMC(x)		(((x) & 0x01) << 1)
124300829Sgrehan#define	XHCI_SET_HCCP2_FSC(x)		(((x) & 0x01) << 2)
125300829Sgrehan#define	XHCI_SET_HCCP2_CTC(x)		(((x) & 0x01) << 3)
126300829Sgrehan#define	XHCI_SET_HCCP2_LEC(x)		(((x) & 0x01) << 4)
127300829Sgrehan#define	XHCI_SET_HCCP2_CIC(x)		(((x) & 0x01) << 5)
128300829Sgrehan
129300829Sgrehan/* other registers */
130300829Sgrehan#define	XHCI_SET_DOORBELL(x)		((x) & ~0x03)
131300829Sgrehan#define	XHCI_SET_RTSOFFSET(x)		((x) & ~0x0F)
132300829Sgrehan
133300829Sgrehan/* register masks */
134300829Sgrehan#define	XHCI_PS_PLS_MASK		(0xF << 5)	/* port link state */
135300829Sgrehan#define	XHCI_PS_SPEED_MASK		(0xF << 10)	/* port speed */
136300829Sgrehan#define	XHCI_PS_PIC_MASK		(0x3 << 14)	/* port indicator */
137300829Sgrehan
138300829Sgrehan/* port register set */
139300829Sgrehan#define	XHCI_PORTREGS_BASE		0x400		/* base offset */
140300829Sgrehan#define	XHCI_PORTREGS_PORT0		0x3F0
141300829Sgrehan#define	XHCI_PORTREGS_SETSZ		0x10		/* size of a set */
142300829Sgrehan
143300829Sgrehan#define	MASK_64_HI(x)			((x) & ~0xFFFFFFFFULL)
144300829Sgrehan#define	MASK_64_LO(x)			((x) & 0xFFFFFFFFULL)
145300829Sgrehan
146302366Sngie#define	FIELD_REPLACE(a,b,m,s)		(((a) & ~((m) << (s))) | \
147302366Sngie					(((b) & (m)) << (s)))
148302366Sngie#define	FIELD_COPY(a,b,m,s)		(((a) & ~((m) << (s))) | \
149302366Sngie					(((b) & ((m) << (s)))))
150300829Sgrehan
151300829Sgrehanstruct pci_xhci_trb_ring {
152300829Sgrehan	uint64_t ringaddr;		/* current dequeue guest address */
153300829Sgrehan	uint32_t ccs;			/* consumer cycle state */
154300829Sgrehan};
155300829Sgrehan
156300829Sgrehan/* device endpoint transfer/stream rings */
157300829Sgrehanstruct pci_xhci_dev_ep {
158300829Sgrehan	union {
159300829Sgrehan		struct xhci_trb		*_epu_tr;
160300829Sgrehan		struct xhci_stream_ctx	*_epu_sctx;
161300829Sgrehan	} _ep_trbsctx;
162300829Sgrehan#define	ep_tr		_ep_trbsctx._epu_tr
163300829Sgrehan#define	ep_sctx		_ep_trbsctx._epu_sctx
164300829Sgrehan
165300829Sgrehan	union {
166300829Sgrehan		struct pci_xhci_trb_ring _epu_trb;
167300829Sgrehan		struct pci_xhci_trb_ring *_epu_sctx_trbs;
168300829Sgrehan	} _ep_trb_rings;
169300829Sgrehan#define	ep_ringaddr	_ep_trb_rings._epu_trb.ringaddr
170300829Sgrehan#define	ep_ccs		_ep_trb_rings._epu_trb.ccs
171300829Sgrehan#define	ep_sctx_trbs	_ep_trb_rings._epu_sctx_trbs
172300829Sgrehan
173300829Sgrehan	struct usb_data_xfer *ep_xfer;	/* transfer chain */
174300829Sgrehan};
175300829Sgrehan
176300829Sgrehan/* device context base address array: maps slot->device context */
177300829Sgrehanstruct xhci_dcbaa {
178300829Sgrehan	uint64_t dcba[USB_MAX_DEVICES+1]; /* xhci_dev_ctx ptrs */
179300829Sgrehan};
180300829Sgrehan
181300829Sgrehan/* port status registers */
182300829Sgrehanstruct pci_xhci_portregs {
183300829Sgrehan	uint32_t	portsc;		/* port status and control */
184300829Sgrehan	uint32_t	portpmsc;	/* port pwr mgmt status & control */
185300829Sgrehan	uint32_t	portli;		/* port link info */
186300829Sgrehan	uint32_t	porthlpmc;	/* port hardware LPM control */
187300829Sgrehan} __packed;
188300829Sgrehan#define	XHCI_PS_SPEED_SET(x)	(((x) & 0xF) << 10)
189300829Sgrehan
190300829Sgrehan/* xHC operational registers */
191300829Sgrehanstruct pci_xhci_opregs {
192300829Sgrehan	uint32_t	usbcmd;		/* usb command */
193300829Sgrehan	uint32_t	usbsts;		/* usb status */
194300829Sgrehan	uint32_t	pgsz;		/* page size */
195300829Sgrehan	uint32_t	dnctrl;		/* device notification control */
196300829Sgrehan	uint64_t	crcr;		/* command ring control */
197300829Sgrehan	uint64_t	dcbaap;		/* device ctx base addr array ptr */
198300829Sgrehan	uint32_t	config;		/* configure */
199300829Sgrehan
200300829Sgrehan	/* guest mapped addresses: */
201300829Sgrehan	struct xhci_trb	*cr_p;		/* crcr dequeue */
202300829Sgrehan	struct xhci_dcbaa *dcbaa_p;	/* dev ctx array ptr */
203300829Sgrehan};
204300829Sgrehan
205300829Sgrehan/* xHC runtime registers */
206300829Sgrehanstruct pci_xhci_rtsregs {
207300829Sgrehan	uint32_t	mfindex;	/* microframe index */
208300829Sgrehan	struct {			/* interrupter register set */
209300829Sgrehan		uint32_t	iman;	/* interrupter management */
210300829Sgrehan		uint32_t	imod;	/* interrupter moderation */
211300829Sgrehan		uint32_t	erstsz;	/* event ring segment table size */
212300829Sgrehan		uint32_t	rsvd;
213300829Sgrehan		uint64_t	erstba;	/* event ring seg-tbl base addr */
214300829Sgrehan		uint64_t	erdp;	/* event ring dequeue ptr */
215300829Sgrehan	} intrreg __packed;
216300829Sgrehan
217300829Sgrehan	/* guest mapped addresses */
218300829Sgrehan	struct xhci_event_ring_seg *erstba_p;
219300829Sgrehan	struct xhci_trb *erst_p;	/* event ring segment tbl */
220300829Sgrehan	int		er_deq_seg;	/* event ring dequeue segment */
221300829Sgrehan	int		er_enq_idx;	/* event ring enqueue index - xHCI */
222300829Sgrehan	int		er_enq_seg;	/* event ring enqueue segment */
223300829Sgrehan	uint32_t	er_events_cnt;	/* number of events in ER */
224300829Sgrehan	uint32_t	event_pcs;	/* producer cycle state flag */
225300829Sgrehan};
226300829Sgrehan
227300829Sgrehan
228300829Sgrehanstruct pci_xhci_softc;
229300829Sgrehan
230300829Sgrehan
231300829Sgrehan/*
232300829Sgrehan * USB device emulation container.
233300829Sgrehan * This is referenced from usb_hci->hci_sc; 1 pci_xhci_dev_emu for each
234300829Sgrehan * emulated device instance.
235300829Sgrehan */
236300829Sgrehanstruct pci_xhci_dev_emu {
237300829Sgrehan	struct pci_xhci_softc	*xsc;
238300829Sgrehan
239300829Sgrehan	/* XHCI contexts */
240300829Sgrehan	struct xhci_dev_ctx	*dev_ctx;
241300829Sgrehan	struct pci_xhci_dev_ep	eps[XHCI_MAX_ENDPOINTS];
242300829Sgrehan	int			dev_slotstate;
243300829Sgrehan
244300829Sgrehan	struct usb_devemu	*dev_ue;	/* USB emulated dev */
245300829Sgrehan	void			*dev_sc;	/* device's softc */
246300829Sgrehan
247300829Sgrehan	struct usb_hci		hci;
248300829Sgrehan};
249300829Sgrehan
250300829Sgrehanstruct pci_xhci_softc {
251300829Sgrehan	struct pci_devinst *xsc_pi;
252300829Sgrehan
253300829Sgrehan	pthread_mutex_t	mtx;
254300829Sgrehan
255300829Sgrehan	uint32_t	caplength;	/* caplen & hciversion */
256300829Sgrehan	uint32_t	hcsparams1;	/* structural parameters 1 */
257300829Sgrehan	uint32_t	hcsparams2;	/* structural parameters 2 */
258300829Sgrehan	uint32_t	hcsparams3;	/* structural parameters 3 */
259300829Sgrehan	uint32_t	hccparams1;	/* capability parameters 1 */
260300829Sgrehan	uint32_t	dboff;		/* doorbell offset */
261300829Sgrehan	uint32_t	rtsoff;		/* runtime register space offset */
262300829Sgrehan	uint32_t	hccparams2;	/* capability parameters 2 */
263300829Sgrehan
264300829Sgrehan	uint32_t	regsend;	/* end of configuration registers */
265300829Sgrehan
266300829Sgrehan	struct pci_xhci_opregs  opregs;
267300829Sgrehan	struct pci_xhci_rtsregs rtsregs;
268300829Sgrehan
269300829Sgrehan	struct pci_xhci_portregs *portregs;
270300829Sgrehan	struct pci_xhci_dev_emu  **devices; /* XHCI[port] = device */
271300829Sgrehan	struct pci_xhci_dev_emu  **slots;   /* slots assigned from 1 */
272300829Sgrehan	int		ndevices;
273300829Sgrehan
274300829Sgrehan	int		usb2_port_start;
275300829Sgrehan	int		usb3_port_start;
276300829Sgrehan};
277300829Sgrehan
278300829Sgrehan
279300829Sgrehan/* portregs and devices arrays are set up to start from idx=1 */
280300829Sgrehan#define	XHCI_PORTREG_PTR(x,n)	&(x)->portregs[(n)]
281300829Sgrehan#define	XHCI_DEVINST_PTR(x,n)	(x)->devices[(n)]
282300829Sgrehan#define	XHCI_SLOTDEV_PTR(x,n)	(x)->slots[(n)]
283300829Sgrehan
284300829Sgrehan#define	XHCI_HALTED(sc)		((sc)->opregs.usbsts & XHCI_STS_HCH)
285300829Sgrehan
286300829Sgrehan#define	XHCI_GADDR(sc,a)	paddr_guest2host((sc)->xsc_pi->pi_vmctx, \
287300829Sgrehan				    (a),                                 \
288300829Sgrehan				    XHCI_PADDR_SZ - ((a) & (XHCI_PADDR_SZ-1)))
289300829Sgrehan
290300829Sgrehanstatic int xhci_in_use;
291300829Sgrehan
292300829Sgrehan/* map USB errors to XHCI */
293300829Sgrehanstatic const int xhci_usb_errors[USB_ERR_MAX] = {
294300829Sgrehan	[USB_ERR_NORMAL_COMPLETION]	= XHCI_TRB_ERROR_SUCCESS,
295300829Sgrehan	[USB_ERR_PENDING_REQUESTS]	= XHCI_TRB_ERROR_RESOURCE,
296300829Sgrehan	[USB_ERR_NOT_STARTED]		= XHCI_TRB_ERROR_ENDP_NOT_ON,
297300829Sgrehan	[USB_ERR_INVAL]			= XHCI_TRB_ERROR_INVALID,
298300829Sgrehan	[USB_ERR_NOMEM]			= XHCI_TRB_ERROR_RESOURCE,
299300829Sgrehan	[USB_ERR_CANCELLED]		= XHCI_TRB_ERROR_STOPPED,
300300829Sgrehan	[USB_ERR_BAD_ADDRESS]		= XHCI_TRB_ERROR_PARAMETER,
301300829Sgrehan	[USB_ERR_BAD_BUFSIZE]		= XHCI_TRB_ERROR_PARAMETER,
302300829Sgrehan	[USB_ERR_BAD_FLAG]		= XHCI_TRB_ERROR_PARAMETER,
303300829Sgrehan	[USB_ERR_NO_CALLBACK]		= XHCI_TRB_ERROR_STALL,
304300829Sgrehan	[USB_ERR_IN_USE]		= XHCI_TRB_ERROR_RESOURCE,
305300829Sgrehan	[USB_ERR_NO_ADDR]		= XHCI_TRB_ERROR_RESOURCE,
306300829Sgrehan	[USB_ERR_NO_PIPE]               = XHCI_TRB_ERROR_RESOURCE,
307300829Sgrehan	[USB_ERR_ZERO_NFRAMES]          = XHCI_TRB_ERROR_UNDEFINED,
308300829Sgrehan	[USB_ERR_ZERO_MAXP]             = XHCI_TRB_ERROR_UNDEFINED,
309300829Sgrehan	[USB_ERR_SET_ADDR_FAILED]       = XHCI_TRB_ERROR_RESOURCE,
310300829Sgrehan	[USB_ERR_NO_POWER]              = XHCI_TRB_ERROR_ENDP_NOT_ON,
311300829Sgrehan	[USB_ERR_TOO_DEEP]              = XHCI_TRB_ERROR_RESOURCE,
312300829Sgrehan	[USB_ERR_IOERROR]               = XHCI_TRB_ERROR_TRB,
313300829Sgrehan	[USB_ERR_NOT_CONFIGURED]        = XHCI_TRB_ERROR_ENDP_NOT_ON,
314300829Sgrehan	[USB_ERR_TIMEOUT]               = XHCI_TRB_ERROR_CMD_ABORTED,
315300829Sgrehan	[USB_ERR_SHORT_XFER]            = XHCI_TRB_ERROR_SHORT_PKT,
316300829Sgrehan	[USB_ERR_STALLED]               = XHCI_TRB_ERROR_STALL,
317300829Sgrehan	[USB_ERR_INTERRUPTED]           = XHCI_TRB_ERROR_CMD_ABORTED,
318300829Sgrehan	[USB_ERR_DMA_LOAD_FAILED]       = XHCI_TRB_ERROR_DATA_BUF,
319300829Sgrehan	[USB_ERR_BAD_CONTEXT]           = XHCI_TRB_ERROR_TRB,
320300829Sgrehan	[USB_ERR_NO_ROOT_HUB]           = XHCI_TRB_ERROR_UNDEFINED,
321300829Sgrehan	[USB_ERR_NO_INTR_THREAD]        = XHCI_TRB_ERROR_UNDEFINED,
322300829Sgrehan	[USB_ERR_NOT_LOCKED]            = XHCI_TRB_ERROR_UNDEFINED,
323300829Sgrehan};
324300829Sgrehan#define	USB_TO_XHCI_ERR(e)	((e) < USB_ERR_MAX ? xhci_usb_errors[(e)] : \
325300829Sgrehan				XHCI_TRB_ERROR_INVALID)
326300829Sgrehan
327300829Sgrehanstatic int pci_xhci_insert_event(struct pci_xhci_softc *sc,
328300829Sgrehan    struct xhci_trb *evtrb, int do_intr);
329300829Sgrehanstatic void pci_xhci_dump_trb(struct xhci_trb *trb);
330300829Sgrehanstatic void pci_xhci_assert_interrupt(struct pci_xhci_softc *sc);
331300829Sgrehanstatic void pci_xhci_reset_slot(struct pci_xhci_softc *sc, int slot);
332300829Sgrehanstatic void pci_xhci_reset_port(struct pci_xhci_softc *sc, int portn, int warm);
333300829Sgrehanstatic void pci_xhci_update_ep_ring(struct pci_xhci_softc *sc,
334300829Sgrehan    struct pci_xhci_dev_emu *dev, struct pci_xhci_dev_ep *devep,
335300829Sgrehan    struct xhci_endp_ctx *ep_ctx, uint32_t streamid,
336300829Sgrehan    uint64_t ringaddr, int ccs);
337300829Sgrehan
338300829Sgrehanstatic void
339300829Sgrehanpci_xhci_set_evtrb(struct xhci_trb *evtrb, uint64_t port, uint32_t errcode,
340300829Sgrehan    uint32_t evtype)
341300829Sgrehan{
342300829Sgrehan	evtrb->qwTrb0 = port << 24;
343300829Sgrehan	evtrb->dwTrb2 = XHCI_TRB_2_ERROR_SET(errcode);
344300829Sgrehan	evtrb->dwTrb3 = XHCI_TRB_3_TYPE_SET(evtype);
345300829Sgrehan}
346300829Sgrehan
347300829Sgrehan
348300829Sgrehan/* controller reset */
349300829Sgrehanstatic void
350300829Sgrehanpci_xhci_reset(struct pci_xhci_softc *sc)
351300829Sgrehan{
352300829Sgrehan	int i;
353300829Sgrehan
354300829Sgrehan	sc->rtsregs.er_enq_idx = 0;
355300829Sgrehan	sc->rtsregs.er_events_cnt = 0;
356300829Sgrehan	sc->rtsregs.event_pcs = 1;
357300829Sgrehan
358300829Sgrehan	for (i = 1; i <= XHCI_MAX_SLOTS; i++) {
359300829Sgrehan		pci_xhci_reset_slot(sc, i);
360300829Sgrehan	}
361300829Sgrehan}
362300829Sgrehan
363300829Sgrehanstatic uint32_t
364300829Sgrehanpci_xhci_usbcmd_write(struct pci_xhci_softc *sc, uint32_t cmd)
365300829Sgrehan{
366300829Sgrehan	int do_intr = 0;
367300829Sgrehan	int i;
368300829Sgrehan
369300829Sgrehan	if (cmd & XHCI_CMD_RS) {
370300829Sgrehan		do_intr = (sc->opregs.usbcmd & XHCI_CMD_RS) == 0;
371300829Sgrehan
372300829Sgrehan		sc->opregs.usbcmd |= XHCI_CMD_RS;
373300829Sgrehan		sc->opregs.usbsts &= ~XHCI_STS_HCH;
374300829Sgrehan		sc->opregs.usbsts |= XHCI_STS_PCD;
375300829Sgrehan
376300829Sgrehan		/* Queue port change event on controller run from stop */
377300829Sgrehan		if (do_intr)
378300829Sgrehan			for (i = 1; i <= XHCI_MAX_DEVS; i++) {
379300829Sgrehan				struct pci_xhci_dev_emu *dev;
380300829Sgrehan				struct pci_xhci_portregs *port;
381300829Sgrehan				struct xhci_trb		evtrb;
382300829Sgrehan
383300829Sgrehan				if ((dev = XHCI_DEVINST_PTR(sc, i)) == NULL)
384300829Sgrehan					continue;
385300829Sgrehan
386300829Sgrehan				port = XHCI_PORTREG_PTR(sc, i);
387300829Sgrehan				port->portsc |= XHCI_PS_CSC | XHCI_PS_CCS;
388300829Sgrehan				port->portsc &= ~XHCI_PS_PLS_MASK;
389300829Sgrehan
390300829Sgrehan				/*
391300829Sgrehan				 * XHCI 4.19.3 USB2 RxDetect->Polling,
392300829Sgrehan				 *             USB3 Polling->U0
393300829Sgrehan				 */
394300829Sgrehan				if (dev->dev_ue->ue_usbver == 2)
395300829Sgrehan					port->portsc |=
396300829Sgrehan					    XHCI_PS_PLS_SET(UPS_PORT_LS_POLL);
397300829Sgrehan				else
398300829Sgrehan					port->portsc |=
399300829Sgrehan					    XHCI_PS_PLS_SET(UPS_PORT_LS_U0);
400300829Sgrehan
401300829Sgrehan				pci_xhci_set_evtrb(&evtrb, i,
402300829Sgrehan				    XHCI_TRB_ERROR_SUCCESS,
403300829Sgrehan				    XHCI_TRB_EVENT_PORT_STS_CHANGE);
404300829Sgrehan
405300829Sgrehan				if (pci_xhci_insert_event(sc, &evtrb, 0) !=
406300829Sgrehan				    XHCI_TRB_ERROR_SUCCESS)
407300829Sgrehan					break;
408300829Sgrehan			}
409300829Sgrehan	} else {
410300829Sgrehan		sc->opregs.usbcmd &= ~XHCI_CMD_RS;
411300829Sgrehan		sc->opregs.usbsts |= XHCI_STS_HCH;
412300829Sgrehan		sc->opregs.usbsts &= ~XHCI_STS_PCD;
413300829Sgrehan	}
414300829Sgrehan
415300829Sgrehan	/* start execution of schedule; stop when set to 0 */
416300829Sgrehan	cmd |= sc->opregs.usbcmd & XHCI_CMD_RS;
417300829Sgrehan
418300829Sgrehan	if (cmd & XHCI_CMD_HCRST) {
419300829Sgrehan		/* reset controller */
420300829Sgrehan		pci_xhci_reset(sc);
421300829Sgrehan		cmd &= ~XHCI_CMD_HCRST;
422300829Sgrehan	}
423300829Sgrehan
424300829Sgrehan	cmd &= ~(XHCI_CMD_CSS | XHCI_CMD_CRS);
425300829Sgrehan
426300829Sgrehan	if (do_intr)
427300829Sgrehan		pci_xhci_assert_interrupt(sc);
428300829Sgrehan
429300829Sgrehan	return (cmd);
430300829Sgrehan}
431300829Sgrehan
432300829Sgrehanstatic void
433300829Sgrehanpci_xhci_portregs_write(struct pci_xhci_softc *sc, uint64_t offset,
434300829Sgrehan    uint64_t value)
435300829Sgrehan{
436300829Sgrehan	struct xhci_trb		evtrb;
437300829Sgrehan	struct pci_xhci_portregs *p;
438300829Sgrehan	int port;
439300829Sgrehan	uint32_t oldpls, newpls;
440300829Sgrehan
441300829Sgrehan	if (sc->portregs == NULL)
442300829Sgrehan		return;
443300829Sgrehan
444300829Sgrehan	port = (offset - XHCI_PORTREGS_PORT0) / XHCI_PORTREGS_SETSZ;
445300829Sgrehan	offset = (offset - XHCI_PORTREGS_PORT0) % XHCI_PORTREGS_SETSZ;
446300829Sgrehan
447300829Sgrehan	DPRINTF(("pci_xhci: portregs wr offset 0x%lx, port %u: 0x%lx\r\n",
448300829Sgrehan	        offset, port, value));
449300829Sgrehan
450300829Sgrehan	assert(port >= 0);
451300829Sgrehan
452300829Sgrehan	if (port > XHCI_MAX_DEVS) {
453300829Sgrehan		DPRINTF(("pci_xhci: portregs_write port %d > ndevices\r\n",
454300829Sgrehan		    port));
455300829Sgrehan		return;
456300829Sgrehan	}
457300829Sgrehan
458300829Sgrehan	if (XHCI_DEVINST_PTR(sc, port) == NULL) {
459300829Sgrehan		DPRINTF(("pci_xhci: portregs_write to unattached port %d\r\n",
460300829Sgrehan		     port));
461300829Sgrehan	}
462300829Sgrehan
463300829Sgrehan	p = XHCI_PORTREG_PTR(sc, port);
464300829Sgrehan	switch (offset) {
465300829Sgrehan	case 0:
466300829Sgrehan		/* port reset or warm reset */
467300829Sgrehan		if (value & (XHCI_PS_PR | XHCI_PS_WPR)) {
468300829Sgrehan			pci_xhci_reset_port(sc, port, value & XHCI_PS_WPR);
469300829Sgrehan			break;
470300829Sgrehan		}
471300829Sgrehan
472300829Sgrehan		if ((p->portsc & XHCI_PS_PP) == 0) {
473300829Sgrehan			WPRINTF(("pci_xhci: portregs_write to unpowered "
474300829Sgrehan			         "port %d\r\n", port));
475300829Sgrehan			break;
476300829Sgrehan		}
477300829Sgrehan
478300829Sgrehan		/* Port status and control register  */
479300829Sgrehan		oldpls = XHCI_PS_PLS_GET(p->portsc);
480300829Sgrehan		newpls = XHCI_PS_PLS_GET(value);
481300829Sgrehan
482300829Sgrehan		p->portsc &= XHCI_PS_PED | XHCI_PS_PLS_MASK |
483300829Sgrehan		             XHCI_PS_SPEED_MASK | XHCI_PS_PIC_MASK;
484300829Sgrehan
485300829Sgrehan		if (XHCI_DEVINST_PTR(sc, port))
486300829Sgrehan			p->portsc |= XHCI_PS_CCS;
487300829Sgrehan
488300829Sgrehan		p->portsc |= (value &
489300829Sgrehan		              ~(XHCI_PS_OCA |
490300829Sgrehan		                XHCI_PS_PR  |
491300829Sgrehan			        XHCI_PS_PED |
492300829Sgrehan			        XHCI_PS_PLS_MASK   |	/* link state */
493300829Sgrehan			        XHCI_PS_SPEED_MASK |
494300829Sgrehan			        XHCI_PS_PIC_MASK   |	/* port indicator */
495300829Sgrehan			        XHCI_PS_LWS | XHCI_PS_DR | XHCI_PS_WPR));
496300829Sgrehan
497300829Sgrehan		/* clear control bits */
498300829Sgrehan		p->portsc &= ~(value &
499300829Sgrehan		               (XHCI_PS_CSC |
500300829Sgrehan		                XHCI_PS_PEC |
501300829Sgrehan		                XHCI_PS_WRC |
502300829Sgrehan		                XHCI_PS_OCC |
503300829Sgrehan		                XHCI_PS_PRC |
504300829Sgrehan		                XHCI_PS_PLC |
505300829Sgrehan		                XHCI_PS_CEC |
506300829Sgrehan		                XHCI_PS_CAS));
507300829Sgrehan
508300829Sgrehan		/* port disable request; for USB3, don't care */
509300829Sgrehan		if (value & XHCI_PS_PED)
510300829Sgrehan			DPRINTF(("Disable port %d request\r\n", port));
511300829Sgrehan
512300829Sgrehan		if (!(value & XHCI_PS_LWS))
513300829Sgrehan			break;
514300829Sgrehan
515300829Sgrehan		DPRINTF(("Port new PLS: %d\r\n", newpls));
516300829Sgrehan		switch (newpls) {
517300829Sgrehan		case 0: /* U0 */
518300829Sgrehan		case 3: /* U3 */
519300829Sgrehan			if (oldpls != newpls) {
520300829Sgrehan				p->portsc &= ~XHCI_PS_PLS_MASK;
521300829Sgrehan				p->portsc |= XHCI_PS_PLS_SET(newpls) |
522300829Sgrehan				             XHCI_PS_PLC;
523300829Sgrehan
524300829Sgrehan				if (oldpls != 0 && newpls == 0) {
525300829Sgrehan					pci_xhci_set_evtrb(&evtrb, port,
526300829Sgrehan					    XHCI_TRB_ERROR_SUCCESS,
527300829Sgrehan					    XHCI_TRB_EVENT_PORT_STS_CHANGE);
528300829Sgrehan
529300829Sgrehan					pci_xhci_insert_event(sc, &evtrb, 1);
530300829Sgrehan				}
531300829Sgrehan			}
532300829Sgrehan			break;
533300829Sgrehan
534300829Sgrehan		default:
535300829Sgrehan			DPRINTF(("Unhandled change port %d PLS %u\r\n",
536300829Sgrehan			         port, newpls));
537300829Sgrehan			break;
538300829Sgrehan		}
539300829Sgrehan		break;
540300829Sgrehan	case 4:
541300829Sgrehan		/* Port power management status and control register  */
542300829Sgrehan		p->portpmsc = value;
543300829Sgrehan		break;
544300829Sgrehan	case 8:
545300829Sgrehan		/* Port link information register */
546300829Sgrehan		DPRINTF(("pci_xhci attempted write to PORTLI, port %d\r\n",
547300829Sgrehan		        port));
548300829Sgrehan		break;
549300829Sgrehan	case 12:
550300829Sgrehan		/*
551300829Sgrehan		 * Port hardware LPM control register.
552300829Sgrehan		 * For USB3, this register is reserved.
553300829Sgrehan		 */
554300829Sgrehan		p->porthlpmc = value;
555300829Sgrehan		break;
556300829Sgrehan	}
557300829Sgrehan}
558300829Sgrehan
559300829Sgrehanstruct xhci_dev_ctx *
560300829Sgrehanpci_xhci_get_dev_ctx(struct pci_xhci_softc *sc, uint32_t slot)
561300829Sgrehan{
562300829Sgrehan	uint64_t devctx_addr;
563300829Sgrehan	struct xhci_dev_ctx *devctx;
564300829Sgrehan
565300829Sgrehan	assert(slot > 0 && slot <= sc->ndevices);
566300829Sgrehan	assert(sc->opregs.dcbaa_p != NULL);
567300829Sgrehan
568300829Sgrehan	devctx_addr = sc->opregs.dcbaa_p->dcba[slot];
569300829Sgrehan
570300829Sgrehan	if (devctx_addr == 0) {
571300829Sgrehan		DPRINTF(("get_dev_ctx devctx_addr == 0\r\n"));
572300829Sgrehan		return (NULL);
573300829Sgrehan	}
574300829Sgrehan
575300829Sgrehan	DPRINTF(("pci_xhci: get dev ctx, slot %u devctx addr %016lx\r\n",
576300829Sgrehan	        slot, devctx_addr));
577300829Sgrehan	devctx = XHCI_GADDR(sc, devctx_addr & ~0x3FUL);
578300829Sgrehan
579300829Sgrehan	return (devctx);
580300829Sgrehan}
581300829Sgrehan
582300829Sgrehanstruct xhci_trb *
583300829Sgrehanpci_xhci_trb_next(struct pci_xhci_softc *sc, struct xhci_trb *curtrb,
584300829Sgrehan    uint64_t *guestaddr)
585300829Sgrehan{
586300829Sgrehan	struct xhci_trb *next;
587300829Sgrehan
588300829Sgrehan	assert(curtrb != NULL);
589300829Sgrehan
590300829Sgrehan	if (XHCI_TRB_3_TYPE_GET(curtrb->dwTrb3) == XHCI_TRB_TYPE_LINK) {
591300829Sgrehan		if (guestaddr)
592300829Sgrehan			*guestaddr = curtrb->qwTrb0 & ~0xFUL;
593300829Sgrehan
594300829Sgrehan		next = XHCI_GADDR(sc, curtrb->qwTrb0 & ~0xFUL);
595300829Sgrehan	} else {
596300829Sgrehan		if (guestaddr)
597300829Sgrehan			*guestaddr += sizeof(struct xhci_trb) & ~0xFUL;
598300829Sgrehan
599300829Sgrehan		next = curtrb + 1;
600300829Sgrehan	}
601300829Sgrehan
602300829Sgrehan	return (next);
603300829Sgrehan}
604300829Sgrehan
605300829Sgrehanstatic void
606300829Sgrehanpci_xhci_assert_interrupt(struct pci_xhci_softc *sc)
607300829Sgrehan{
608300829Sgrehan
609300829Sgrehan	sc->rtsregs.intrreg.erdp |= XHCI_ERDP_LO_BUSY;
610300829Sgrehan	sc->rtsregs.intrreg.iman |= XHCI_IMAN_INTR_PEND;
611300829Sgrehan	sc->opregs.usbsts |= XHCI_STS_EINT;
612300829Sgrehan
613300829Sgrehan	/* only trigger interrupt if permitted */
614300829Sgrehan	if ((sc->opregs.usbcmd & XHCI_CMD_INTE) &&
615300829Sgrehan	    (sc->rtsregs.intrreg.iman & XHCI_IMAN_INTR_ENA)) {
616300829Sgrehan		if (pci_msi_enabled(sc->xsc_pi))
617300829Sgrehan			pci_generate_msi(sc->xsc_pi, 0);
618300829Sgrehan		else
619300829Sgrehan			pci_lintr_assert(sc->xsc_pi);
620300829Sgrehan	}
621300829Sgrehan}
622300829Sgrehan
623300829Sgrehanstatic void
624300829Sgrehanpci_xhci_deassert_interrupt(struct pci_xhci_softc *sc)
625300829Sgrehan{
626300829Sgrehan
627300829Sgrehan	if (!pci_msi_enabled(sc->xsc_pi))
628300829Sgrehan		pci_lintr_assert(sc->xsc_pi);
629300829Sgrehan}
630300829Sgrehan
631300829Sgrehanstatic void
632300829Sgrehanpci_xhci_init_ep(struct pci_xhci_dev_emu *dev, int epid)
633300829Sgrehan{
634300829Sgrehan	struct xhci_dev_ctx    *dev_ctx;
635300829Sgrehan	struct pci_xhci_dev_ep *devep;
636300829Sgrehan	struct xhci_endp_ctx   *ep_ctx;
637300829Sgrehan	uint32_t	pstreams;
638300829Sgrehan	int		i;
639300829Sgrehan
640300829Sgrehan	dev_ctx = dev->dev_ctx;
641300829Sgrehan	ep_ctx = &dev_ctx->ctx_ep[epid];
642300829Sgrehan	devep = &dev->eps[epid];
643300829Sgrehan	pstreams = XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0);
644300829Sgrehan	if (pstreams > 0) {
645300829Sgrehan		DPRINTF(("init_ep %d with pstreams %d\r\n", epid, pstreams));
646300829Sgrehan		assert(devep->ep_sctx_trbs == NULL);
647300829Sgrehan
648300829Sgrehan		devep->ep_sctx = XHCI_GADDR(dev->xsc, ep_ctx->qwEpCtx2 &
649300829Sgrehan		                            XHCI_EPCTX_2_TR_DQ_PTR_MASK);
650300829Sgrehan		devep->ep_sctx_trbs = calloc(pstreams,
651300829Sgrehan		                      sizeof(struct pci_xhci_trb_ring));
652300829Sgrehan		for (i = 0; i < pstreams; i++) {
653300829Sgrehan			devep->ep_sctx_trbs[i].ringaddr =
654300829Sgrehan			                         devep->ep_sctx[i].qwSctx0 &
655300829Sgrehan			                         XHCI_SCTX_0_TR_DQ_PTR_MASK;
656300829Sgrehan			devep->ep_sctx_trbs[i].ccs =
657300829Sgrehan			     XHCI_SCTX_0_DCS_GET(devep->ep_sctx[i].qwSctx0);
658300829Sgrehan		}
659300829Sgrehan	} else {
660300829Sgrehan		DPRINTF(("init_ep %d with no pstreams\r\n", epid));
661300829Sgrehan		devep->ep_ringaddr = ep_ctx->qwEpCtx2 &
662300829Sgrehan		                     XHCI_EPCTX_2_TR_DQ_PTR_MASK;
663300829Sgrehan		devep->ep_ccs = XHCI_EPCTX_2_DCS_GET(ep_ctx->qwEpCtx2);
664300829Sgrehan		devep->ep_tr = XHCI_GADDR(dev->xsc, devep->ep_ringaddr);
665300829Sgrehan		DPRINTF(("init_ep tr DCS %x\r\n", devep->ep_ccs));
666300829Sgrehan	}
667300829Sgrehan
668300829Sgrehan	if (devep->ep_xfer == NULL) {
669300829Sgrehan		devep->ep_xfer = malloc(sizeof(struct usb_data_xfer));
670300829Sgrehan		USB_DATA_XFER_INIT(devep->ep_xfer);
671300829Sgrehan	}
672300829Sgrehan}
673300829Sgrehan
674300829Sgrehanstatic void
675300829Sgrehanpci_xhci_disable_ep(struct pci_xhci_dev_emu *dev, int epid)
676300829Sgrehan{
677300829Sgrehan	struct xhci_dev_ctx    *dev_ctx;
678300829Sgrehan	struct pci_xhci_dev_ep *devep;
679300829Sgrehan	struct xhci_endp_ctx   *ep_ctx;
680300829Sgrehan
681300829Sgrehan	DPRINTF(("pci_xhci disable_ep %d\r\n", epid));
682300829Sgrehan
683300829Sgrehan	dev_ctx = dev->dev_ctx;
684300829Sgrehan	ep_ctx = &dev_ctx->ctx_ep[epid];
685300829Sgrehan	ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_DISABLED;
686300829Sgrehan
687300829Sgrehan	devep = &dev->eps[epid];
688300829Sgrehan	if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) > 0 &&
689300829Sgrehan	    devep->ep_sctx_trbs != NULL)
690300829Sgrehan			free(devep->ep_sctx_trbs);
691300829Sgrehan
692300829Sgrehan	if (devep->ep_xfer != NULL) {
693300829Sgrehan		free(devep->ep_xfer);
694300829Sgrehan		devep->ep_xfer = NULL;
695300829Sgrehan	}
696300829Sgrehan
697300829Sgrehan	memset(devep, 0, sizeof(struct pci_xhci_dev_ep));
698300829Sgrehan}
699300829Sgrehan
700300829Sgrehan
701300829Sgrehan/* reset device at slot and data structures related to it */
702300829Sgrehanstatic void
703300829Sgrehanpci_xhci_reset_slot(struct pci_xhci_softc *sc, int slot)
704300829Sgrehan{
705300829Sgrehan	struct pci_xhci_dev_emu *dev;
706300829Sgrehan
707300829Sgrehan	dev = XHCI_SLOTDEV_PTR(sc, slot);
708300829Sgrehan
709300829Sgrehan	if (!dev) {
710300829Sgrehan		DPRINTF(("xhci reset unassigned slot (%d)?\r\n", slot));
711300829Sgrehan	} else {
712300829Sgrehan		dev->dev_slotstate = XHCI_ST_DISABLED;
713300829Sgrehan	}
714300829Sgrehan
715300829Sgrehan	/* TODO: reset ring buffer pointers */
716300829Sgrehan}
717300829Sgrehan
718300829Sgrehanstatic int
719300829Sgrehanpci_xhci_insert_event(struct pci_xhci_softc *sc, struct xhci_trb *evtrb,
720300829Sgrehan    int do_intr)
721300829Sgrehan{
722300829Sgrehan	struct pci_xhci_rtsregs *rts;
723300829Sgrehan	uint64_t	erdp;
724300829Sgrehan	int		erdp_idx;
725300829Sgrehan	int		err;
726300829Sgrehan	struct xhci_trb *evtrbptr;
727300829Sgrehan
728300829Sgrehan	err = XHCI_TRB_ERROR_SUCCESS;
729300829Sgrehan
730300829Sgrehan	rts = &sc->rtsregs;
731300829Sgrehan
732300829Sgrehan	erdp = rts->intrreg.erdp & ~0xF;
733300829Sgrehan	erdp_idx = (erdp - rts->erstba_p[rts->er_deq_seg].qwEvrsTablePtr) /
734300829Sgrehan	           sizeof(struct xhci_trb);
735300829Sgrehan
736300829Sgrehan	DPRINTF(("pci_xhci: insert event 0[%lx] 2[%x] 3[%x]\r\n"
737300829Sgrehan	         "\terdp idx %d/seg %d, enq idx %d/seg %d, pcs %u\r\n"
738300829Sgrehan	         "\t(erdp=0x%lx, erst=0x%lx, tblsz=%u, do_intr %d)\r\n",
739300829Sgrehan	         evtrb->qwTrb0, evtrb->dwTrb2, evtrb->dwTrb3,
740300829Sgrehan	         erdp_idx, rts->er_deq_seg, rts->er_enq_idx,
741300829Sgrehan	         rts->er_enq_seg,
742300829Sgrehan	         rts->event_pcs, erdp, rts->erstba_p->qwEvrsTablePtr,
743300829Sgrehan	         rts->erstba_p->dwEvrsTableSize, do_intr));
744300829Sgrehan
745300829Sgrehan	evtrbptr = &rts->erst_p[rts->er_enq_idx];
746300829Sgrehan
747300829Sgrehan	/* TODO: multi-segment table */
748300829Sgrehan	if (rts->er_events_cnt >= rts->erstba_p->dwEvrsTableSize) {
749300829Sgrehan		DPRINTF(("pci_xhci[%d] cannot insert event; ring full\r\n",
750300829Sgrehan		         __LINE__));
751300829Sgrehan		err = XHCI_TRB_ERROR_EV_RING_FULL;
752300829Sgrehan		goto done;
753300829Sgrehan	}
754300829Sgrehan
755300829Sgrehan	if (rts->er_events_cnt == rts->erstba_p->dwEvrsTableSize - 1) {
756300829Sgrehan		struct xhci_trb	errev;
757300829Sgrehan
758300829Sgrehan		if ((evtrbptr->dwTrb3 & 0x1) == (rts->event_pcs & 0x1)) {
759300829Sgrehan
760300829Sgrehan			DPRINTF(("pci_xhci[%d] insert evt err: ring full\r\n",
761300829Sgrehan			         __LINE__));
762300829Sgrehan
763300829Sgrehan			errev.qwTrb0 = 0;
764300829Sgrehan			errev.dwTrb2 = XHCI_TRB_2_ERROR_SET(
765300829Sgrehan			                    XHCI_TRB_ERROR_EV_RING_FULL);
766300829Sgrehan			errev.dwTrb3 = XHCI_TRB_3_TYPE_SET(
767300829Sgrehan			                    XHCI_TRB_EVENT_HOST_CTRL) |
768300829Sgrehan			               rts->event_pcs;
769300829Sgrehan			rts->er_events_cnt++;
770300829Sgrehan			memcpy(&rts->erst_p[rts->er_enq_idx], &errev,
771300829Sgrehan			       sizeof(struct xhci_trb));
772300829Sgrehan			rts->er_enq_idx = (rts->er_enq_idx + 1) %
773300829Sgrehan			                  rts->erstba_p->dwEvrsTableSize;
774300829Sgrehan			err = XHCI_TRB_ERROR_EV_RING_FULL;
775300829Sgrehan			do_intr = 1;
776300829Sgrehan
777300829Sgrehan			goto done;
778300829Sgrehan		}
779300829Sgrehan	} else {
780300829Sgrehan		rts->er_events_cnt++;
781300829Sgrehan	}
782300829Sgrehan
783300829Sgrehan	evtrb->dwTrb3 &= ~XHCI_TRB_3_CYCLE_BIT;
784300829Sgrehan	evtrb->dwTrb3 |= rts->event_pcs;
785300829Sgrehan
786300829Sgrehan	memcpy(&rts->erst_p[rts->er_enq_idx], evtrb, sizeof(struct xhci_trb));
787300829Sgrehan	rts->er_enq_idx = (rts->er_enq_idx + 1) %
788300829Sgrehan	                  rts->erstba_p->dwEvrsTableSize;
789300829Sgrehan
790300829Sgrehan	if (rts->er_enq_idx == 0)
791300829Sgrehan		rts->event_pcs ^= 1;
792300829Sgrehan
793300829Sgrehandone:
794300829Sgrehan	if (do_intr)
795300829Sgrehan		pci_xhci_assert_interrupt(sc);
796300829Sgrehan
797300829Sgrehan	return (err);
798300829Sgrehan}
799300829Sgrehan
800300829Sgrehanstatic uint32_t
801300829Sgrehanpci_xhci_cmd_enable_slot(struct pci_xhci_softc *sc, uint32_t *slot)
802300829Sgrehan{
803300829Sgrehan	struct pci_xhci_dev_emu *dev;
804300829Sgrehan	uint32_t	cmderr;
805300829Sgrehan	int		i;
806300829Sgrehan
807300829Sgrehan	cmderr = XHCI_TRB_ERROR_NO_SLOTS;
808300829Sgrehan	if (sc->portregs != NULL)
809300829Sgrehan		for (i = 1; i <= XHCI_MAX_SLOTS; i++) {
810300829Sgrehan			dev = XHCI_SLOTDEV_PTR(sc, i);
811300829Sgrehan			if (dev && dev->dev_slotstate == XHCI_ST_DISABLED) {
812300829Sgrehan				*slot = i;
813300829Sgrehan				dev->dev_slotstate = XHCI_ST_ENABLED;
814300829Sgrehan				cmderr = XHCI_TRB_ERROR_SUCCESS;
815300829Sgrehan				dev->hci.hci_address = i;
816300829Sgrehan				break;
817300829Sgrehan			}
818300829Sgrehan		}
819300829Sgrehan
820300829Sgrehan	DPRINTF(("pci_xhci enable slot (error=%d) slot %u\r\n",
821300829Sgrehan		cmderr != XHCI_TRB_ERROR_SUCCESS, *slot));
822300829Sgrehan
823300829Sgrehan	return (cmderr);
824300829Sgrehan}
825300829Sgrehan
826300829Sgrehanstatic uint32_t
827300829Sgrehanpci_xhci_cmd_disable_slot(struct pci_xhci_softc *sc, uint32_t slot)
828300829Sgrehan{
829300829Sgrehan	struct pci_xhci_dev_emu *dev;
830300829Sgrehan	uint32_t cmderr;
831300829Sgrehan
832300829Sgrehan	DPRINTF(("pci_xhci disable slot %u\r\n", slot));
833300829Sgrehan
834300829Sgrehan	cmderr = XHCI_TRB_ERROR_NO_SLOTS;
835300829Sgrehan	if (sc->portregs == NULL)
836300829Sgrehan		goto done;
837300829Sgrehan
838300829Sgrehan	if (slot > sc->ndevices) {
839300829Sgrehan		cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON;
840300829Sgrehan		goto done;
841300829Sgrehan	}
842300829Sgrehan
843300829Sgrehan	dev = XHCI_SLOTDEV_PTR(sc, slot);
844300829Sgrehan	if (dev) {
845300829Sgrehan		if (dev->dev_slotstate == XHCI_ST_DISABLED) {
846300829Sgrehan			cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON;
847300829Sgrehan		} else {
848300829Sgrehan			dev->dev_slotstate = XHCI_ST_DISABLED;
849300829Sgrehan			cmderr = XHCI_TRB_ERROR_SUCCESS;
850300829Sgrehan			/* TODO: reset events and endpoints */
851300829Sgrehan		}
852300829Sgrehan	}
853300829Sgrehan
854300829Sgrehandone:
855300829Sgrehan	return (cmderr);
856300829Sgrehan}
857300829Sgrehan
858300829Sgrehanstatic uint32_t
859300829Sgrehanpci_xhci_cmd_reset_device(struct pci_xhci_softc *sc, uint32_t slot)
860300829Sgrehan{
861300829Sgrehan	struct pci_xhci_dev_emu *dev;
862300829Sgrehan	struct xhci_dev_ctx     *dev_ctx;
863300829Sgrehan	struct xhci_endp_ctx    *ep_ctx;
864300829Sgrehan	uint32_t	cmderr;
865300829Sgrehan	int		i;
866300829Sgrehan
867300829Sgrehan	cmderr = XHCI_TRB_ERROR_NO_SLOTS;
868300829Sgrehan	if (sc->portregs == NULL)
869300829Sgrehan		goto done;
870300829Sgrehan
871300829Sgrehan	DPRINTF(("pci_xhci reset device slot %u\r\n", slot));
872300829Sgrehan
873300829Sgrehan	dev = XHCI_SLOTDEV_PTR(sc, slot);
874300829Sgrehan	if (!dev || dev->dev_slotstate == XHCI_ST_DISABLED)
875300829Sgrehan		cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON;
876300829Sgrehan	else {
877300829Sgrehan		dev->dev_slotstate = XHCI_ST_DEFAULT;
878300829Sgrehan
879300829Sgrehan		dev->hci.hci_address = 0;
880300829Sgrehan		dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
881300829Sgrehan
882300829Sgrehan		/* slot state */
883300829Sgrehan		dev_ctx->ctx_slot.dwSctx3 = FIELD_REPLACE(
884300829Sgrehan		    dev_ctx->ctx_slot.dwSctx3, XHCI_ST_SLCTX_DEFAULT,
885300829Sgrehan		    0x1F, 27);
886300829Sgrehan
887300829Sgrehan		/* number of contexts */
888300829Sgrehan		dev_ctx->ctx_slot.dwSctx0 = FIELD_REPLACE(
889300829Sgrehan		    dev_ctx->ctx_slot.dwSctx0, 1, 0x1F, 27);
890300829Sgrehan
891300829Sgrehan		/* reset all eps other than ep-0 */
892300829Sgrehan		for (i = 2; i <= 31; i++) {
893300829Sgrehan			ep_ctx = &dev_ctx->ctx_ep[i];
894300829Sgrehan			ep_ctx->dwEpCtx0 = FIELD_REPLACE( ep_ctx->dwEpCtx0,
895300829Sgrehan			    XHCI_ST_EPCTX_DISABLED, 0x7, 0);
896300829Sgrehan		}
897300829Sgrehan
898300829Sgrehan		cmderr = XHCI_TRB_ERROR_SUCCESS;
899300829Sgrehan	}
900300829Sgrehan
901300829Sgrehan	pci_xhci_reset_slot(sc, slot);
902300829Sgrehan
903300829Sgrehandone:
904300829Sgrehan	return (cmderr);
905300829Sgrehan}
906300829Sgrehan
907300829Sgrehanstatic uint32_t
908300829Sgrehanpci_xhci_cmd_address_device(struct pci_xhci_softc *sc, uint32_t slot,
909300829Sgrehan    struct xhci_trb *trb)
910300829Sgrehan{
911300829Sgrehan	struct pci_xhci_dev_emu	*dev;
912300829Sgrehan	struct xhci_input_dev_ctx *input_ctx;
913300829Sgrehan	struct xhci_slot_ctx	*islot_ctx;
914300829Sgrehan	struct xhci_dev_ctx	*dev_ctx;
915300829Sgrehan	struct xhci_endp_ctx	*ep0_ctx;
916300829Sgrehan	uint32_t		cmderr;
917300829Sgrehan
918300829Sgrehan	input_ctx = XHCI_GADDR(sc, trb->qwTrb0 & ~0xFUL);
919300829Sgrehan	islot_ctx = &input_ctx->ctx_slot;
920300829Sgrehan	ep0_ctx = &input_ctx->ctx_ep[1];
921300829Sgrehan
922300829Sgrehan	cmderr = XHCI_TRB_ERROR_SUCCESS;
923300829Sgrehan
924300829Sgrehan	DPRINTF(("pci_xhci: address device, input ctl: D 0x%08x A 0x%08x,\r\n"
925300829Sgrehan	         "          slot %08x %08x %08x %08x\r\n"
926300829Sgrehan	         "          ep0  %08x %08x %016lx %08x\r\n",
927300829Sgrehan	        input_ctx->ctx_input.dwInCtx0, input_ctx->ctx_input.dwInCtx1,
928300829Sgrehan	        islot_ctx->dwSctx0, islot_ctx->dwSctx1,
929300829Sgrehan	        islot_ctx->dwSctx2, islot_ctx->dwSctx3,
930300829Sgrehan	        ep0_ctx->dwEpCtx0, ep0_ctx->dwEpCtx1, ep0_ctx->qwEpCtx2,
931300829Sgrehan	        ep0_ctx->dwEpCtx4));
932300829Sgrehan
933300829Sgrehan	/* when setting address: drop-ctx=0, add-ctx=slot+ep0 */
934300829Sgrehan	if ((input_ctx->ctx_input.dwInCtx0 != 0) ||
935300829Sgrehan	    (input_ctx->ctx_input.dwInCtx1 & 0x03) != 0x03) {
936300829Sgrehan		DPRINTF(("pci_xhci: address device, input ctl invalid\r\n"));
937300829Sgrehan		cmderr = XHCI_TRB_ERROR_TRB;
938300829Sgrehan		goto done;
939300829Sgrehan	}
940300829Sgrehan
941300829Sgrehan	/* assign address to slot */
942300829Sgrehan	dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
943300829Sgrehan
944300829Sgrehan	DPRINTF(("pci_xhci: address device, dev ctx\r\n"
945300829Sgrehan	         "          slot %08x %08x %08x %08x\r\n",
946300829Sgrehan	        dev_ctx->ctx_slot.dwSctx0, dev_ctx->ctx_slot.dwSctx1,
947300829Sgrehan	        dev_ctx->ctx_slot.dwSctx2, dev_ctx->ctx_slot.dwSctx3));
948300829Sgrehan
949300829Sgrehan	dev = XHCI_SLOTDEV_PTR(sc, slot);
950300829Sgrehan	assert(dev != NULL);
951300829Sgrehan
952300829Sgrehan	dev->hci.hci_address = slot;
953300829Sgrehan	dev->dev_ctx = dev_ctx;
954300829Sgrehan
955300829Sgrehan	if (dev->dev_ue->ue_reset == NULL ||
956300829Sgrehan	    dev->dev_ue->ue_reset(dev->dev_sc) < 0) {
957300829Sgrehan		cmderr = XHCI_TRB_ERROR_ENDP_NOT_ON;
958300829Sgrehan		goto done;
959300829Sgrehan	}
960300829Sgrehan
961300829Sgrehan	memcpy(&dev_ctx->ctx_slot, islot_ctx, sizeof(struct xhci_slot_ctx));
962300829Sgrehan
963300829Sgrehan	dev_ctx->ctx_slot.dwSctx3 =
964300829Sgrehan	    XHCI_SCTX_3_SLOT_STATE_SET(XHCI_ST_SLCTX_ADDRESSED) |
965300829Sgrehan	    XHCI_SCTX_3_DEV_ADDR_SET(slot);
966300829Sgrehan
967300829Sgrehan	memcpy(&dev_ctx->ctx_ep[1], ep0_ctx, sizeof(struct xhci_endp_ctx));
968300829Sgrehan	ep0_ctx = &dev_ctx->ctx_ep[1];
969300829Sgrehan	ep0_ctx->dwEpCtx0 = (ep0_ctx->dwEpCtx0 & ~0x7) |
970300829Sgrehan	    XHCI_EPCTX_0_EPSTATE_SET(XHCI_ST_EPCTX_RUNNING);
971300829Sgrehan
972300829Sgrehan	pci_xhci_init_ep(dev, 1);
973300829Sgrehan
974300829Sgrehan	dev->dev_slotstate = XHCI_ST_ADDRESSED;
975300829Sgrehan
976300829Sgrehan	DPRINTF(("pci_xhci: address device, output ctx\r\n"
977300829Sgrehan	         "          slot %08x %08x %08x %08x\r\n"
978300829Sgrehan	         "          ep0  %08x %08x %016lx %08x\r\n",
979300829Sgrehan	        dev_ctx->ctx_slot.dwSctx0, dev_ctx->ctx_slot.dwSctx1,
980300829Sgrehan	        dev_ctx->ctx_slot.dwSctx2, dev_ctx->ctx_slot.dwSctx3,
981300829Sgrehan	        ep0_ctx->dwEpCtx0, ep0_ctx->dwEpCtx1, ep0_ctx->qwEpCtx2,
982300829Sgrehan	        ep0_ctx->dwEpCtx4));
983300829Sgrehan
984300829Sgrehandone:
985300829Sgrehan	return (cmderr);
986300829Sgrehan}
987300829Sgrehan
988300829Sgrehanstatic uint32_t
989300829Sgrehanpci_xhci_cmd_config_ep(struct pci_xhci_softc *sc, uint32_t slot,
990300829Sgrehan    struct xhci_trb *trb)
991300829Sgrehan{
992300829Sgrehan	struct xhci_input_dev_ctx *input_ctx;
993300829Sgrehan	struct pci_xhci_dev_emu	*dev;
994300829Sgrehan	struct xhci_dev_ctx	*dev_ctx;
995300829Sgrehan	struct xhci_endp_ctx	*ep_ctx, *iep_ctx;
996300829Sgrehan	uint32_t	cmderr;
997300829Sgrehan	int		i;
998300829Sgrehan
999300829Sgrehan	cmderr = XHCI_TRB_ERROR_SUCCESS;
1000300829Sgrehan
1001300829Sgrehan	DPRINTF(("pci_xhci config_ep slot %u\r\n", slot));
1002300829Sgrehan
1003300829Sgrehan	dev = XHCI_SLOTDEV_PTR(sc, slot);
1004300829Sgrehan	assert(dev != NULL);
1005300829Sgrehan
1006300829Sgrehan	if ((trb->dwTrb3 & XHCI_TRB_3_DCEP_BIT) != 0) {
1007300829Sgrehan		DPRINTF(("pci_xhci config_ep - deconfigure ep slot %u\r\n",
1008300829Sgrehan		        slot));
1009300829Sgrehan		if (dev->dev_ue->ue_stop != NULL)
1010300829Sgrehan			dev->dev_ue->ue_stop(dev->dev_sc);
1011300829Sgrehan
1012300829Sgrehan		dev->dev_slotstate = XHCI_ST_ADDRESSED;
1013300829Sgrehan
1014300829Sgrehan		dev->hci.hci_address = 0;
1015300829Sgrehan		dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
1016300829Sgrehan
1017300829Sgrehan		/* number of contexts */
1018300829Sgrehan		dev_ctx->ctx_slot.dwSctx0 = FIELD_REPLACE(
1019300829Sgrehan		    dev_ctx->ctx_slot.dwSctx0, 1, 0x1F, 27);
1020300829Sgrehan
1021300829Sgrehan		/* slot state */
1022300829Sgrehan		dev_ctx->ctx_slot.dwSctx3 = FIELD_REPLACE(
1023300829Sgrehan		    dev_ctx->ctx_slot.dwSctx3, XHCI_ST_SLCTX_ADDRESSED,
1024300829Sgrehan		    0x1F, 27);
1025300829Sgrehan
1026300829Sgrehan		/* disable endpoints */
1027300829Sgrehan		for (i = 2; i < 32; i++)
1028300829Sgrehan			pci_xhci_disable_ep(dev, i);
1029300829Sgrehan
1030300829Sgrehan		cmderr = XHCI_TRB_ERROR_SUCCESS;
1031300829Sgrehan
1032300829Sgrehan		goto done;
1033300829Sgrehan	}
1034300829Sgrehan
1035300829Sgrehan	if (dev->dev_slotstate < XHCI_ST_ADDRESSED) {
1036300829Sgrehan		DPRINTF(("pci_xhci: config_ep slotstate x%x != addressed\r\n",
1037300829Sgrehan		        dev->dev_slotstate));
1038300829Sgrehan		cmderr = XHCI_TRB_ERROR_SLOT_NOT_ON;
1039300829Sgrehan		goto done;
1040300829Sgrehan	}
1041300829Sgrehan
1042300829Sgrehan	/* In addressed/configured state;
1043300829Sgrehan	 * for each drop endpoint ctx flag:
1044300829Sgrehan	 *   ep->state = DISABLED
1045300829Sgrehan	 * for each add endpoint ctx flag:
1046300829Sgrehan	 *   cp(ep-in, ep-out)
1047300829Sgrehan	 *   ep->state = RUNNING
1048300829Sgrehan	 * for each drop+add endpoint flag:
1049300829Sgrehan	 *   reset ep resources
1050300829Sgrehan	 *   cp(ep-in, ep-out)
1051300829Sgrehan	 *   ep->state = RUNNING
1052300829Sgrehan	 * if input->DisabledCtx[2-31] < 30: (at least 1 ep not disabled)
1053300829Sgrehan	 *   slot->state = configured
1054300829Sgrehan	 */
1055300829Sgrehan
1056300829Sgrehan	input_ctx = XHCI_GADDR(sc, trb->qwTrb0 & ~0xFUL);
1057300829Sgrehan	dev_ctx = dev->dev_ctx;
1058300829Sgrehan	DPRINTF(("pci_xhci: config_ep inputctx: D:x%08x A:x%08x 7:x%08x\r\n",
1059300829Sgrehan		input_ctx->ctx_input.dwInCtx0, input_ctx->ctx_input.dwInCtx1,
1060300829Sgrehan	        input_ctx->ctx_input.dwInCtx7));
1061300829Sgrehan
1062300829Sgrehan	for (i = 2; i <= 31; i++) {
1063300829Sgrehan		ep_ctx = &dev_ctx->ctx_ep[i];
1064300829Sgrehan
1065300829Sgrehan		if (input_ctx->ctx_input.dwInCtx0 &
1066300829Sgrehan		    XHCI_INCTX_0_DROP_MASK(i)) {
1067300829Sgrehan			DPRINTF((" config ep - dropping ep %d\r\n", i));
1068300829Sgrehan			pci_xhci_disable_ep(dev, i);
1069300829Sgrehan		}
1070300829Sgrehan
1071300829Sgrehan		if (input_ctx->ctx_input.dwInCtx1 &
1072300829Sgrehan		    XHCI_INCTX_1_ADD_MASK(i)) {
1073300829Sgrehan			iep_ctx = &input_ctx->ctx_ep[i];
1074300829Sgrehan
1075300829Sgrehan			DPRINTF((" enable ep[%d]  %08x %08x %016lx %08x\r\n",
1076300829Sgrehan			   i, iep_ctx->dwEpCtx0, iep_ctx->dwEpCtx1,
1077300829Sgrehan			   iep_ctx->qwEpCtx2, iep_ctx->dwEpCtx4));
1078300829Sgrehan
1079300829Sgrehan			memcpy(ep_ctx, iep_ctx, sizeof(struct xhci_endp_ctx));
1080300829Sgrehan
1081300829Sgrehan			pci_xhci_init_ep(dev, i);
1082300829Sgrehan
1083300829Sgrehan			/* ep state */
1084300829Sgrehan			ep_ctx->dwEpCtx0 = FIELD_REPLACE(
1085300829Sgrehan			    ep_ctx->dwEpCtx0, XHCI_ST_EPCTX_RUNNING, 0x7, 0);
1086300829Sgrehan		}
1087300829Sgrehan	}
1088300829Sgrehan
1089300829Sgrehan	/* slot state to configured */
1090300829Sgrehan	dev_ctx->ctx_slot.dwSctx3 = FIELD_REPLACE(
1091300829Sgrehan	    dev_ctx->ctx_slot.dwSctx3, XHCI_ST_SLCTX_CONFIGURED, 0x1F, 27);
1092300829Sgrehan	dev_ctx->ctx_slot.dwSctx0 = FIELD_COPY(
1093300829Sgrehan	    dev_ctx->ctx_slot.dwSctx0, input_ctx->ctx_slot.dwSctx0, 0x1F, 27);
1094300829Sgrehan	dev->dev_slotstate = XHCI_ST_CONFIGURED;
1095300829Sgrehan
1096300829Sgrehan	DPRINTF(("EP configured; slot %u [0]=0x%08x [1]=0x%08x [2]=0x%08x "
1097300829Sgrehan	         "[3]=0x%08x\r\n",
1098300829Sgrehan	    slot, dev_ctx->ctx_slot.dwSctx0, dev_ctx->ctx_slot.dwSctx1,
1099300829Sgrehan	    dev_ctx->ctx_slot.dwSctx2, dev_ctx->ctx_slot.dwSctx3));
1100300829Sgrehan
1101300829Sgrehandone:
1102300829Sgrehan	return (cmderr);
1103300829Sgrehan}
1104300829Sgrehan
1105300829Sgrehanstatic uint32_t
1106300829Sgrehanpci_xhci_cmd_reset_ep(struct pci_xhci_softc *sc, uint32_t slot,
1107300829Sgrehan    struct xhci_trb *trb)
1108300829Sgrehan{
1109300829Sgrehan	struct pci_xhci_dev_emu	*dev;
1110300829Sgrehan	struct pci_xhci_dev_ep *devep;
1111300829Sgrehan	struct xhci_dev_ctx	*dev_ctx;
1112300829Sgrehan	struct xhci_endp_ctx	*ep_ctx;
1113300829Sgrehan	uint32_t	cmderr, epid;
1114300829Sgrehan	uint32_t	type;
1115300829Sgrehan
1116300829Sgrehan	epid = XHCI_TRB_3_EP_GET(trb->dwTrb3);
1117300829Sgrehan
1118300829Sgrehan	DPRINTF(("pci_xhci: reset ep %u: slot %u\r\n", epid, slot));
1119300829Sgrehan
1120300829Sgrehan	cmderr = XHCI_TRB_ERROR_SUCCESS;
1121300829Sgrehan
1122300829Sgrehan	type = XHCI_TRB_3_TYPE_GET(trb->dwTrb3);
1123300829Sgrehan
1124300829Sgrehan	dev = XHCI_SLOTDEV_PTR(sc, slot);
1125300829Sgrehan	assert(dev != NULL);
1126300829Sgrehan
1127300829Sgrehan	if (type == XHCI_TRB_TYPE_STOP_EP &&
1128300829Sgrehan	    (trb->dwTrb3 & XHCI_TRB_3_SUSP_EP_BIT) != 0) {
1129300829Sgrehan		/* XXX suspend endpoint for 10ms */
1130300829Sgrehan	}
1131300829Sgrehan
1132300829Sgrehan	if (epid < 1 || epid > 31) {
1133300829Sgrehan		DPRINTF(("pci_xhci: reset ep: invalid epid %u\r\n", epid));
1134300829Sgrehan		cmderr = XHCI_TRB_ERROR_TRB;
1135300829Sgrehan		goto done;
1136300829Sgrehan	}
1137300829Sgrehan
1138300829Sgrehan	devep = &dev->eps[epid];
1139300829Sgrehan	if (devep->ep_xfer != NULL)
1140300829Sgrehan		USB_DATA_XFER_RESET(devep->ep_xfer);
1141300829Sgrehan
1142300829Sgrehan	dev_ctx = dev->dev_ctx;
1143300829Sgrehan	assert(dev_ctx != NULL);
1144300829Sgrehan
1145300829Sgrehan	ep_ctx = &dev_ctx->ctx_ep[epid];
1146300829Sgrehan
1147300829Sgrehan	ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_STOPPED;
1148300829Sgrehan
1149300829Sgrehan	if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) == 0)
1150300829Sgrehan		ep_ctx->qwEpCtx2 = devep->ep_ringaddr | devep->ep_ccs;
1151300829Sgrehan
1152300829Sgrehan	DPRINTF(("pci_xhci: reset ep[%u] %08x %08x %016lx %08x\r\n",
1153300829Sgrehan	        epid, ep_ctx->dwEpCtx0, ep_ctx->dwEpCtx1, ep_ctx->qwEpCtx2,
1154300829Sgrehan	        ep_ctx->dwEpCtx4));
1155300829Sgrehan
1156300829Sgrehan	if (type == XHCI_TRB_TYPE_RESET_EP &&
1157300829Sgrehan	    (dev->dev_ue->ue_reset == NULL ||
1158300829Sgrehan	    dev->dev_ue->ue_reset(dev->dev_sc) < 0)) {
1159300829Sgrehan		cmderr = XHCI_TRB_ERROR_ENDP_NOT_ON;
1160300829Sgrehan		goto done;
1161300829Sgrehan	}
1162300829Sgrehan
1163300829Sgrehandone:
1164300829Sgrehan	return (cmderr);
1165300829Sgrehan}
1166300829Sgrehan
1167300829Sgrehan
1168300829Sgrehanstatic uint32_t
1169300829Sgrehanpci_xhci_find_stream(struct pci_xhci_softc *sc, struct xhci_endp_ctx *ep,
1170300829Sgrehan    uint32_t streamid, struct xhci_stream_ctx **osctx)
1171300829Sgrehan{
1172300829Sgrehan	struct xhci_stream_ctx *sctx;
1173300829Sgrehan	uint32_t	maxpstreams;
1174300829Sgrehan
1175300829Sgrehan	maxpstreams = XHCI_EPCTX_0_MAXP_STREAMS_GET(ep->dwEpCtx0);
1176300829Sgrehan	if (maxpstreams == 0)
1177300829Sgrehan		return (XHCI_TRB_ERROR_TRB);
1178300829Sgrehan
1179300829Sgrehan	if (maxpstreams > XHCI_STREAMS_MAX)
1180300829Sgrehan		return (XHCI_TRB_ERROR_INVALID_SID);
1181300829Sgrehan
1182300829Sgrehan	if (XHCI_EPCTX_0_LSA_GET(ep->dwEpCtx0) == 0) {
1183300829Sgrehan		DPRINTF(("pci_xhci: find_stream; LSA bit not set\r\n"));
1184300829Sgrehan		return (XHCI_TRB_ERROR_INVALID_SID);
1185300829Sgrehan	}
1186300829Sgrehan
1187300829Sgrehan	/* only support primary stream */
1188300829Sgrehan	if (streamid > maxpstreams)
1189300829Sgrehan		return (XHCI_TRB_ERROR_STREAM_TYPE);
1190300829Sgrehan
1191300829Sgrehan	sctx = XHCI_GADDR(sc, ep->qwEpCtx2 & ~0xFUL) + streamid;
1192300829Sgrehan	if (!XHCI_SCTX_0_SCT_GET(sctx->qwSctx0))
1193300829Sgrehan		return (XHCI_TRB_ERROR_STREAM_TYPE);
1194300829Sgrehan
1195300829Sgrehan	*osctx = sctx;
1196300829Sgrehan
1197300829Sgrehan	return (XHCI_TRB_ERROR_SUCCESS);
1198300829Sgrehan}
1199300829Sgrehan
1200300829Sgrehan
1201300829Sgrehanstatic uint32_t
1202300829Sgrehanpci_xhci_cmd_set_tr(struct pci_xhci_softc *sc, uint32_t slot,
1203300829Sgrehan    struct xhci_trb *trb)
1204300829Sgrehan{
1205300829Sgrehan	struct pci_xhci_dev_emu	*dev;
1206300829Sgrehan	struct pci_xhci_dev_ep	*devep;
1207300829Sgrehan	struct xhci_dev_ctx	*dev_ctx;
1208300829Sgrehan	struct xhci_endp_ctx	*ep_ctx;
1209300829Sgrehan	uint32_t	cmderr, epid;
1210300829Sgrehan	uint32_t	streamid;
1211300829Sgrehan
1212300829Sgrehan	cmderr = XHCI_TRB_ERROR_SUCCESS;
1213300829Sgrehan
1214300829Sgrehan	dev = XHCI_SLOTDEV_PTR(sc, slot);
1215300829Sgrehan	assert(dev != NULL);
1216300829Sgrehan
1217300829Sgrehan	DPRINTF(("pci_xhci set_tr: new-tr x%016lx, SCT %u DCS %u\r\n"
1218300829Sgrehan	         "                 stream-id %u, slot %u, epid %u, C %u\r\n",
1219300829Sgrehan	         (trb->qwTrb0 & ~0xF),  (uint32_t)((trb->qwTrb0 >> 1) & 0x7),
1220300829Sgrehan	         (uint32_t)(trb->qwTrb0 & 0x1), (trb->dwTrb2 >> 16) & 0xFFFF,
1221300829Sgrehan	         XHCI_TRB_3_SLOT_GET(trb->dwTrb3),
1222300829Sgrehan	         XHCI_TRB_3_EP_GET(trb->dwTrb3), trb->dwTrb3 & 0x1));
1223300829Sgrehan
1224300829Sgrehan	epid = XHCI_TRB_3_EP_GET(trb->dwTrb3);
1225300829Sgrehan	if (epid < 1 || epid > 31) {
1226300829Sgrehan		DPRINTF(("pci_xhci: set_tr_deq: invalid epid %u\r\n", epid));
1227300829Sgrehan		cmderr = XHCI_TRB_ERROR_TRB;
1228300829Sgrehan		goto done;
1229300829Sgrehan	}
1230300829Sgrehan
1231300829Sgrehan	dev_ctx = dev->dev_ctx;
1232300829Sgrehan	assert(dev_ctx != NULL);
1233300829Sgrehan
1234300829Sgrehan	ep_ctx = &dev_ctx->ctx_ep[epid];
1235300829Sgrehan	devep = &dev->eps[epid];
1236300829Sgrehan
1237300829Sgrehan	switch (XHCI_EPCTX_0_EPSTATE_GET(ep_ctx->dwEpCtx0)) {
1238300829Sgrehan	case XHCI_ST_EPCTX_STOPPED:
1239300829Sgrehan	case XHCI_ST_EPCTX_ERROR:
1240300829Sgrehan		break;
1241300829Sgrehan	default:
1242300829Sgrehan		DPRINTF(("pci_xhci cmd set_tr invalid state %x\r\n",
1243300829Sgrehan		        XHCI_EPCTX_0_EPSTATE_GET(ep_ctx->dwEpCtx0)));
1244300829Sgrehan		cmderr = XHCI_TRB_ERROR_CONTEXT_STATE;
1245300829Sgrehan		goto done;
1246300829Sgrehan	}
1247300829Sgrehan
1248300829Sgrehan	streamid = XHCI_TRB_2_STREAM_GET(trb->dwTrb2);
1249300829Sgrehan	if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) > 0) {
1250300829Sgrehan		struct xhci_stream_ctx *sctx;
1251300829Sgrehan
1252300829Sgrehan		sctx = NULL;
1253300829Sgrehan		cmderr = pci_xhci_find_stream(sc, ep_ctx, streamid, &sctx);
1254300829Sgrehan		if (sctx != NULL) {
1255300829Sgrehan			assert(devep->ep_sctx != NULL);
1256300829Sgrehan
1257300829Sgrehan			devep->ep_sctx[streamid].qwSctx0 = trb->qwTrb0;
1258300829Sgrehan			devep->ep_sctx_trbs[streamid].ringaddr =
1259300829Sgrehan			    trb->qwTrb0 & ~0xF;
1260300829Sgrehan			devep->ep_sctx_trbs[streamid].ccs =
1261300829Sgrehan			    XHCI_EPCTX_2_DCS_GET(trb->qwTrb0);
1262300829Sgrehan		}
1263300829Sgrehan	} else {
1264300829Sgrehan		if (streamid != 0) {
1265300829Sgrehan			DPRINTF(("pci_xhci cmd set_tr streamid %x != 0\r\n",
1266300829Sgrehan			        streamid));
1267300829Sgrehan		}
1268300829Sgrehan		ep_ctx->qwEpCtx2 = trb->qwTrb0 & ~0xFUL;
1269300829Sgrehan		devep->ep_ringaddr = ep_ctx->qwEpCtx2 & ~0xFUL;
1270300829Sgrehan		devep->ep_ccs = trb->qwTrb0 & 0x1;
1271300829Sgrehan		devep->ep_tr = XHCI_GADDR(sc, devep->ep_ringaddr);
1272300829Sgrehan
1273300829Sgrehan		DPRINTF(("pci_xhci set_tr first TRB:\r\n"));
1274300829Sgrehan		pci_xhci_dump_trb(devep->ep_tr);
1275300829Sgrehan	}
1276300829Sgrehan	ep_ctx->dwEpCtx0 = (ep_ctx->dwEpCtx0 & ~0x7) | XHCI_ST_EPCTX_STOPPED;
1277300829Sgrehan
1278300829Sgrehandone:
1279300829Sgrehan	return (cmderr);
1280300829Sgrehan}
1281300829Sgrehan
1282300829Sgrehanstatic uint32_t
1283300829Sgrehanpci_xhci_cmd_eval_ctx(struct pci_xhci_softc *sc, uint32_t slot,
1284300829Sgrehan    struct xhci_trb *trb)
1285300829Sgrehan{
1286300829Sgrehan	struct xhci_input_dev_ctx *input_ctx;
1287300829Sgrehan	struct xhci_slot_ctx      *islot_ctx;
1288300829Sgrehan	struct xhci_dev_ctx       *dev_ctx;
1289300829Sgrehan	struct xhci_endp_ctx      *ep0_ctx;
1290300829Sgrehan	uint32_t cmderr;
1291300829Sgrehan
1292300829Sgrehan	input_ctx = XHCI_GADDR(sc, trb->qwTrb0 & ~0xFUL);
1293300829Sgrehan	islot_ctx = &input_ctx->ctx_slot;
1294300829Sgrehan	ep0_ctx = &input_ctx->ctx_ep[1];
1295300829Sgrehan
1296300829Sgrehan	cmderr = XHCI_TRB_ERROR_SUCCESS;
1297300829Sgrehan	DPRINTF(("pci_xhci: eval ctx, input ctl: D 0x%08x A 0x%08x,\r\n"
1298300829Sgrehan	         "          slot %08x %08x %08x %08x\r\n"
1299300829Sgrehan	         "          ep0  %08x %08x %016lx %08x\r\n",
1300300829Sgrehan	        input_ctx->ctx_input.dwInCtx0, input_ctx->ctx_input.dwInCtx1,
1301300829Sgrehan	        islot_ctx->dwSctx0, islot_ctx->dwSctx1,
1302300829Sgrehan	        islot_ctx->dwSctx2, islot_ctx->dwSctx3,
1303300829Sgrehan	        ep0_ctx->dwEpCtx0, ep0_ctx->dwEpCtx1, ep0_ctx->qwEpCtx2,
1304300829Sgrehan	        ep0_ctx->dwEpCtx4));
1305300829Sgrehan
1306300829Sgrehan	/* this command expects drop-ctx=0 & add-ctx=slot+ep0 */
1307300829Sgrehan	if ((input_ctx->ctx_input.dwInCtx0 != 0) ||
1308300829Sgrehan	    (input_ctx->ctx_input.dwInCtx1 & 0x03) == 0) {
1309300829Sgrehan		DPRINTF(("pci_xhci: eval ctx, input ctl invalid\r\n"));
1310300829Sgrehan		cmderr = XHCI_TRB_ERROR_TRB;
1311300829Sgrehan		goto done;
1312300829Sgrehan	}
1313300829Sgrehan
1314300829Sgrehan	/* assign address to slot; in this emulation, slot_id = address */
1315300829Sgrehan	dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
1316300829Sgrehan
1317300829Sgrehan	DPRINTF(("pci_xhci: eval ctx, dev ctx\r\n"
1318300829Sgrehan	         "          slot %08x %08x %08x %08x\r\n",
1319300829Sgrehan	        dev_ctx->ctx_slot.dwSctx0, dev_ctx->ctx_slot.dwSctx1,
1320300829Sgrehan	        dev_ctx->ctx_slot.dwSctx2, dev_ctx->ctx_slot.dwSctx3));
1321300829Sgrehan
1322300829Sgrehan	if (input_ctx->ctx_input.dwInCtx1 & 0x01) {	/* slot ctx */
1323300829Sgrehan		/* set max exit latency */
1324300829Sgrehan		dev_ctx->ctx_slot.dwSctx1 = FIELD_COPY(
1325300829Sgrehan		    dev_ctx->ctx_slot.dwSctx1, input_ctx->ctx_slot.dwSctx1,
1326300829Sgrehan		    0xFFFF, 0);
1327300829Sgrehan
1328300829Sgrehan		/* set interrupter target */
1329300829Sgrehan		dev_ctx->ctx_slot.dwSctx2 = FIELD_COPY(
1330300829Sgrehan		    dev_ctx->ctx_slot.dwSctx2, input_ctx->ctx_slot.dwSctx2,
1331300829Sgrehan		    0x3FF, 22);
1332300829Sgrehan	}
1333300829Sgrehan	if (input_ctx->ctx_input.dwInCtx1 & 0x02) {	/* control ctx */
1334300829Sgrehan		/* set max packet size */
1335300829Sgrehan		dev_ctx->ctx_ep[1].dwEpCtx1 = FIELD_COPY(
1336300829Sgrehan		    dev_ctx->ctx_ep[1].dwEpCtx1, ep0_ctx->dwEpCtx1,
1337300829Sgrehan		    0xFFFF, 16);
1338300829Sgrehan
1339300829Sgrehan		ep0_ctx = &dev_ctx->ctx_ep[1];
1340300829Sgrehan	}
1341300829Sgrehan
1342300829Sgrehan	DPRINTF(("pci_xhci: eval ctx, output ctx\r\n"
1343300829Sgrehan	         "          slot %08x %08x %08x %08x\r\n"
1344300829Sgrehan	         "          ep0  %08x %08x %016lx %08x\r\n",
1345300829Sgrehan	        dev_ctx->ctx_slot.dwSctx0, dev_ctx->ctx_slot.dwSctx1,
1346300829Sgrehan	        dev_ctx->ctx_slot.dwSctx2, dev_ctx->ctx_slot.dwSctx3,
1347300829Sgrehan	        ep0_ctx->dwEpCtx0, ep0_ctx->dwEpCtx1, ep0_ctx->qwEpCtx2,
1348300829Sgrehan	        ep0_ctx->dwEpCtx4));
1349300829Sgrehan
1350300829Sgrehandone:
1351300829Sgrehan	return (cmderr);
1352300829Sgrehan}
1353300829Sgrehan
1354300829Sgrehanstatic int
1355300829Sgrehanpci_xhci_complete_commands(struct pci_xhci_softc *sc)
1356300829Sgrehan{
1357300829Sgrehan	struct xhci_trb	evtrb;
1358300829Sgrehan	struct xhci_trb	*trb;
1359300829Sgrehan	uint64_t	crcr;
1360300829Sgrehan	uint32_t	ccs;		/* cycle state (XHCI 4.9.2) */
1361300829Sgrehan	uint32_t	type;
1362300829Sgrehan	uint32_t	slot;
1363300829Sgrehan	uint32_t	cmderr;
1364300829Sgrehan	int		error;
1365300829Sgrehan
1366300829Sgrehan	error = 0;
1367300829Sgrehan	sc->opregs.crcr |= XHCI_CRCR_LO_CRR;
1368300829Sgrehan
1369300829Sgrehan	trb = sc->opregs.cr_p;
1370300829Sgrehan	ccs = sc->opregs.crcr & XHCI_CRCR_LO_RCS;
1371300829Sgrehan	crcr = sc->opregs.crcr & ~0xF;
1372300829Sgrehan
1373300829Sgrehan	while (1) {
1374300829Sgrehan		sc->opregs.cr_p = trb;
1375300829Sgrehan
1376300829Sgrehan		type = XHCI_TRB_3_TYPE_GET(trb->dwTrb3);
1377300829Sgrehan
1378300829Sgrehan		if ((trb->dwTrb3 & XHCI_TRB_3_CYCLE_BIT) !=
1379300829Sgrehan		    (ccs & XHCI_TRB_3_CYCLE_BIT))
1380300829Sgrehan			break;
1381300829Sgrehan
1382300829Sgrehan		DPRINTF(("pci_xhci: cmd type 0x%x, Trb0 x%016lx dwTrb2 x%08x"
1383300829Sgrehan		        " dwTrb3 x%08x, TRB_CYCLE %u/ccs %u\r\n",
1384300829Sgrehan		        type, trb->qwTrb0, trb->dwTrb2, trb->dwTrb3,
1385300829Sgrehan		        trb->dwTrb3 & XHCI_TRB_3_CYCLE_BIT, ccs));
1386300829Sgrehan
1387300829Sgrehan		cmderr = XHCI_TRB_ERROR_SUCCESS;
1388300829Sgrehan		evtrb.dwTrb2 = 0;
1389300829Sgrehan		evtrb.dwTrb3 = (ccs & XHCI_TRB_3_CYCLE_BIT) |
1390300829Sgrehan		      XHCI_TRB_3_TYPE_SET(XHCI_TRB_EVENT_CMD_COMPLETE);
1391300829Sgrehan		slot = 0;
1392300829Sgrehan
1393300829Sgrehan		switch (type) {
1394300829Sgrehan		case XHCI_TRB_TYPE_LINK:			/* 0x06 */
1395300829Sgrehan			if (trb->dwTrb3 & XHCI_TRB_3_TC_BIT)
1396300829Sgrehan				ccs ^= XHCI_CRCR_LO_RCS;
1397300829Sgrehan			break;
1398300829Sgrehan
1399300829Sgrehan		case XHCI_TRB_TYPE_ENABLE_SLOT:			/* 0x09 */
1400300829Sgrehan			cmderr = pci_xhci_cmd_enable_slot(sc, &slot);
1401300829Sgrehan			break;
1402300829Sgrehan
1403300829Sgrehan		case XHCI_TRB_TYPE_DISABLE_SLOT:		/* 0x0A */
1404300829Sgrehan			slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
1405300829Sgrehan			cmderr = pci_xhci_cmd_disable_slot(sc, slot);
1406300829Sgrehan			break;
1407300829Sgrehan
1408300829Sgrehan		case XHCI_TRB_TYPE_ADDRESS_DEVICE:		/* 0x0B */
1409300829Sgrehan			slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
1410300829Sgrehan			cmderr = pci_xhci_cmd_address_device(sc, slot, trb);
1411300829Sgrehan			break;
1412300829Sgrehan
1413300829Sgrehan		case XHCI_TRB_TYPE_CONFIGURE_EP:		/* 0x0C */
1414300829Sgrehan			slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
1415300829Sgrehan			cmderr = pci_xhci_cmd_config_ep(sc, slot, trb);
1416300829Sgrehan			break;
1417300829Sgrehan
1418300829Sgrehan		case XHCI_TRB_TYPE_EVALUATE_CTX:		/* 0x0D */
1419300829Sgrehan			slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
1420300829Sgrehan			cmderr = pci_xhci_cmd_eval_ctx(sc, slot, trb);
1421300829Sgrehan			break;
1422300829Sgrehan
1423300829Sgrehan		case XHCI_TRB_TYPE_RESET_EP:			/* 0x0E */
1424300829Sgrehan			DPRINTF(("Reset Endpoint on slot %d\r\n", slot));
1425300829Sgrehan			slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
1426300829Sgrehan			cmderr = pci_xhci_cmd_reset_ep(sc, slot, trb);
1427300829Sgrehan			break;
1428300829Sgrehan
1429300829Sgrehan		case XHCI_TRB_TYPE_STOP_EP:			/* 0x0F */
1430300829Sgrehan			DPRINTF(("Stop Endpoint on slot %d\r\n", slot));
1431300829Sgrehan			slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
1432300829Sgrehan			cmderr = pci_xhci_cmd_reset_ep(sc, slot, trb);
1433300829Sgrehan			break;
1434300829Sgrehan
1435300829Sgrehan		case XHCI_TRB_TYPE_SET_TR_DEQUEUE:		/* 0x10 */
1436300829Sgrehan			slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
1437300829Sgrehan			cmderr = pci_xhci_cmd_set_tr(sc, slot, trb);
1438300829Sgrehan			break;
1439300829Sgrehan
1440300829Sgrehan		case XHCI_TRB_TYPE_RESET_DEVICE:		/* 0x11 */
1441300829Sgrehan			slot = XHCI_TRB_3_SLOT_GET(trb->dwTrb3);
1442300829Sgrehan			cmderr = pci_xhci_cmd_reset_device(sc, slot);
1443300829Sgrehan			break;
1444300829Sgrehan
1445300829Sgrehan		case XHCI_TRB_TYPE_FORCE_EVENT:			/* 0x12 */
1446300829Sgrehan			/* TODO: */
1447300829Sgrehan			break;
1448300829Sgrehan
1449300829Sgrehan		case XHCI_TRB_TYPE_NEGOTIATE_BW:		/* 0x13 */
1450300829Sgrehan			break;
1451300829Sgrehan
1452300829Sgrehan		case XHCI_TRB_TYPE_SET_LATENCY_TOL:		/* 0x14 */
1453300829Sgrehan			break;
1454300829Sgrehan
1455300829Sgrehan		case XHCI_TRB_TYPE_GET_PORT_BW:			/* 0x15 */
1456300829Sgrehan			break;
1457300829Sgrehan
1458300829Sgrehan		case XHCI_TRB_TYPE_FORCE_HEADER:		/* 0x16 */
1459300829Sgrehan			break;
1460300829Sgrehan
1461300829Sgrehan		case XHCI_TRB_TYPE_NOOP_CMD:			/* 0x17 */
1462300829Sgrehan			break;
1463300829Sgrehan
1464300829Sgrehan		default:
1465300829Sgrehan			DPRINTF(("pci_xhci: unsupported cmd %x\r\n", type));
1466300829Sgrehan			break;
1467300829Sgrehan		}
1468300829Sgrehan
1469300829Sgrehan		if (type != XHCI_TRB_TYPE_LINK) {
1470300829Sgrehan			/*
1471300829Sgrehan			 * insert command completion event and assert intr
1472300829Sgrehan			 */
1473300829Sgrehan			evtrb.qwTrb0 = crcr;
1474300829Sgrehan			evtrb.dwTrb2 |= XHCI_TRB_2_ERROR_SET(cmderr);
1475300829Sgrehan			evtrb.dwTrb3 |= XHCI_TRB_3_SLOT_SET(slot);
1476300829Sgrehan			DPRINTF(("pci_xhci: command 0x%x result: 0x%x\r\n",
1477300829Sgrehan			        type, cmderr));
1478300829Sgrehan			pci_xhci_insert_event(sc, &evtrb, 1);
1479300829Sgrehan		}
1480300829Sgrehan
1481300829Sgrehan		trb = pci_xhci_trb_next(sc, trb, &crcr);
1482300829Sgrehan	}
1483300829Sgrehan
1484300829Sgrehan	sc->opregs.crcr = crcr | (sc->opregs.crcr & XHCI_CRCR_LO_CA) | ccs;
1485300829Sgrehan	sc->opregs.crcr &= ~XHCI_CRCR_LO_CRR;
1486300829Sgrehan	return (error);
1487300829Sgrehan}
1488300829Sgrehan
1489300829Sgrehanstatic void
1490300829Sgrehanpci_xhci_dump_trb(struct xhci_trb *trb)
1491300829Sgrehan{
1492300829Sgrehan	static const char *trbtypes[] = {
1493300829Sgrehan		"RESERVED",
1494300829Sgrehan		"NORMAL",
1495300829Sgrehan		"SETUP_STAGE",
1496300829Sgrehan		"DATA_STAGE",
1497300829Sgrehan		"STATUS_STAGE",
1498300829Sgrehan		"ISOCH",
1499300829Sgrehan		"LINK",
1500300829Sgrehan		"EVENT_DATA",
1501300829Sgrehan		"NOOP",
1502300829Sgrehan		"ENABLE_SLOT",
1503300829Sgrehan		"DISABLE_SLOT",
1504300829Sgrehan		"ADDRESS_DEVICE",
1505300829Sgrehan		"CONFIGURE_EP",
1506300829Sgrehan		"EVALUATE_CTX",
1507300829Sgrehan		"RESET_EP",
1508300829Sgrehan		"STOP_EP",
1509300829Sgrehan		"SET_TR_DEQUEUE",
1510300829Sgrehan		"RESET_DEVICE",
1511300829Sgrehan		"FORCE_EVENT",
1512300829Sgrehan		"NEGOTIATE_BW",
1513300829Sgrehan		"SET_LATENCY_TOL",
1514300829Sgrehan		"GET_PORT_BW",
1515300829Sgrehan		"FORCE_HEADER",
1516300829Sgrehan		"NOOP_CMD"
1517300829Sgrehan	};
1518300829Sgrehan	uint32_t type;
1519300829Sgrehan
1520300829Sgrehan	type = XHCI_TRB_3_TYPE_GET(trb->dwTrb3);
1521300829Sgrehan	DPRINTF(("pci_xhci: trb[@%p] type x%02x %s 0:x%016lx 2:x%08x 3:x%08x\r\n",
1522300829Sgrehan	         trb, type,
1523300829Sgrehan	         type <= XHCI_TRB_TYPE_NOOP_CMD ? trbtypes[type] : "INVALID",
1524300829Sgrehan	         trb->qwTrb0, trb->dwTrb2, trb->dwTrb3));
1525300829Sgrehan}
1526300829Sgrehan
1527300829Sgrehanstatic int
1528300829Sgrehanpci_xhci_xfer_complete(struct pci_xhci_softc *sc, struct usb_data_xfer *xfer,
1529300829Sgrehan     uint32_t slot, uint32_t epid, int *do_intr)
1530300829Sgrehan{
1531300829Sgrehan	struct pci_xhci_dev_emu *dev;
1532300829Sgrehan	struct pci_xhci_dev_ep	*devep;
1533300829Sgrehan	struct xhci_dev_ctx	*dev_ctx;
1534300829Sgrehan	struct xhci_endp_ctx	*ep_ctx;
1535300829Sgrehan	struct xhci_trb		*trb;
1536300829Sgrehan	struct xhci_trb		evtrb;
1537300829Sgrehan	uint32_t trbflags;
1538300829Sgrehan	uint32_t edtla;
1539300829Sgrehan	int i, err;
1540300829Sgrehan
1541300829Sgrehan	dev = XHCI_SLOTDEV_PTR(sc, slot);
1542300829Sgrehan	devep = &dev->eps[epid];
1543300829Sgrehan	dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
1544300829Sgrehan
1545300829Sgrehan	assert(dev_ctx != NULL);
1546300829Sgrehan
1547300829Sgrehan	ep_ctx = &dev_ctx->ctx_ep[epid];
1548300829Sgrehan
1549300829Sgrehan	err = XHCI_TRB_ERROR_SUCCESS;
1550300829Sgrehan	*do_intr = 0;
1551300829Sgrehan	edtla = 0;
1552300829Sgrehan
1553300829Sgrehan	/* go through list of TRBs and insert event(s) */
1554300829Sgrehan	for (i = xfer->head; xfer->ndata > 0; ) {
1555300829Sgrehan		evtrb.qwTrb0 = (uint64_t)xfer->data[i].hci_data;
1556300829Sgrehan		trb = XHCI_GADDR(sc, evtrb.qwTrb0);
1557300829Sgrehan		trbflags = trb->dwTrb3;
1558300829Sgrehan
1559300829Sgrehan		DPRINTF(("pci_xhci: xfer[%d] done?%u:%d trb %x %016lx %x "
1560300829Sgrehan		         "(err %d) IOC?%d\r\n",
1561300829Sgrehan		     i, xfer->data[i].processed, xfer->data[i].blen,
1562300829Sgrehan		     XHCI_TRB_3_TYPE_GET(trbflags), evtrb.qwTrb0,
1563300829Sgrehan		     trbflags, err,
1564300829Sgrehan		     trb->dwTrb3 & XHCI_TRB_3_IOC_BIT ? 1 : 0));
1565300829Sgrehan
1566300829Sgrehan		if (!xfer->data[i].processed) {
1567300829Sgrehan			xfer->head = i;
1568300829Sgrehan			break;
1569300829Sgrehan		}
1570300829Sgrehan
1571300829Sgrehan		xfer->ndata--;
1572300829Sgrehan		edtla += xfer->data[i].bdone;
1573300829Sgrehan
1574300829Sgrehan		trb->dwTrb3 = (trb->dwTrb3 & ~0x1) | (xfer->data[i].ccs);
1575300829Sgrehan
1576300829Sgrehan		pci_xhci_update_ep_ring(sc, dev, devep, ep_ctx,
1577300829Sgrehan		    xfer->data[i].streamid, xfer->data[i].trbnext,
1578300829Sgrehan		    xfer->data[i].ccs);
1579300829Sgrehan
1580300829Sgrehan		/* Only interrupt if IOC or short packet */
1581300829Sgrehan		if (!(trb->dwTrb3 & XHCI_TRB_3_IOC_BIT) &&
1582300829Sgrehan		    !((err == XHCI_TRB_ERROR_SHORT_PKT) &&
1583300829Sgrehan		      (trb->dwTrb3 & XHCI_TRB_3_ISP_BIT))) {
1584300829Sgrehan
1585300829Sgrehan			i = (i + 1) % USB_MAX_XFER_BLOCKS;
1586300829Sgrehan			continue;
1587300829Sgrehan		}
1588300829Sgrehan
1589300829Sgrehan		evtrb.dwTrb2 = XHCI_TRB_2_ERROR_SET(err) |
1590300829Sgrehan		               XHCI_TRB_2_REM_SET(xfer->data[i].blen);
1591300829Sgrehan
1592300829Sgrehan		evtrb.dwTrb3 = XHCI_TRB_3_TYPE_SET(XHCI_TRB_EVENT_TRANSFER) |
1593300829Sgrehan		    XHCI_TRB_3_SLOT_SET(slot) | XHCI_TRB_3_EP_SET(epid);
1594300829Sgrehan
1595300829Sgrehan		if (XHCI_TRB_3_TYPE_GET(trbflags) == XHCI_TRB_TYPE_EVENT_DATA) {
1596300829Sgrehan			DPRINTF(("pci_xhci EVENT_DATA edtla %u\r\n", edtla));
1597300829Sgrehan			evtrb.qwTrb0 = trb->qwTrb0;
1598300829Sgrehan			evtrb.dwTrb2 = (edtla & 0xFFFFF) |
1599300829Sgrehan			         XHCI_TRB_2_ERROR_SET(err);
1600300829Sgrehan			evtrb.dwTrb3 |= XHCI_TRB_3_ED_BIT;
1601300829Sgrehan			edtla = 0;
1602300829Sgrehan		}
1603300829Sgrehan
1604300829Sgrehan		*do_intr = 1;
1605300829Sgrehan
1606300829Sgrehan		err = pci_xhci_insert_event(sc, &evtrb, 0);
1607300829Sgrehan		if (err != XHCI_TRB_ERROR_SUCCESS) {
1608300829Sgrehan			break;
1609300829Sgrehan		}
1610300829Sgrehan
1611300829Sgrehan		i = (i + 1) % USB_MAX_XFER_BLOCKS;
1612300829Sgrehan	}
1613300829Sgrehan
1614300829Sgrehan	return (err);
1615300829Sgrehan}
1616300829Sgrehan
1617300829Sgrehanstatic void
1618300829Sgrehanpci_xhci_update_ep_ring(struct pci_xhci_softc *sc, struct pci_xhci_dev_emu *dev,
1619300829Sgrehan    struct pci_xhci_dev_ep *devep, struct xhci_endp_ctx *ep_ctx,
1620300829Sgrehan    uint32_t streamid, uint64_t ringaddr, int ccs)
1621300829Sgrehan{
1622300829Sgrehan
1623300829Sgrehan	if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) != 0) {
1624300829Sgrehan		devep->ep_sctx[streamid].qwSctx0 = (ringaddr & ~0xFUL) |
1625300829Sgrehan		                                   (ccs & 0x1);
1626300829Sgrehan
1627300829Sgrehan		devep->ep_sctx_trbs[streamid].ringaddr = ringaddr & ~0xFUL;
1628300829Sgrehan		devep->ep_sctx_trbs[streamid].ccs = ccs & 0x1;
1629300829Sgrehan		ep_ctx->qwEpCtx2 = (ep_ctx->qwEpCtx2 & ~0x1) | (ccs & 0x1);
1630300829Sgrehan
1631300829Sgrehan		DPRINTF(("xhci update ep-ring stream %d, addr %lx\r\n",
1632300829Sgrehan		    streamid, devep->ep_sctx[streamid].qwSctx0));
1633300829Sgrehan	} else {
1634300829Sgrehan		devep->ep_ringaddr = ringaddr & ~0xFUL;
1635300829Sgrehan		devep->ep_ccs = ccs & 0x1;
1636300829Sgrehan		devep->ep_tr = XHCI_GADDR(sc, ringaddr & ~0xFUL);
1637300829Sgrehan		ep_ctx->qwEpCtx2 = (ringaddr & ~0xFUL) | (ccs & 0x1);
1638300829Sgrehan
1639300829Sgrehan		DPRINTF(("xhci update ep-ring, addr %lx\r\n",
1640300829Sgrehan		    (devep->ep_ringaddr | devep->ep_ccs)));
1641300829Sgrehan	}
1642300829Sgrehan}
1643300829Sgrehan
1644300829Sgrehan/*
1645300829Sgrehan * Outstanding transfer still in progress (device NAK'd earlier) so retry
1646300829Sgrehan * the transfer again to see if it succeeds.
1647300829Sgrehan */
1648300829Sgrehanstatic int
1649300829Sgrehanpci_xhci_try_usb_xfer(struct pci_xhci_softc *sc,
1650300829Sgrehan    struct pci_xhci_dev_emu *dev, struct pci_xhci_dev_ep *devep,
1651300829Sgrehan    struct xhci_endp_ctx *ep_ctx, uint32_t slot, uint32_t epid)
1652300829Sgrehan{
1653300829Sgrehan	struct usb_data_xfer *xfer;
1654300829Sgrehan	int		err;
1655300829Sgrehan	int		do_intr;
1656300829Sgrehan
1657300829Sgrehan	ep_ctx->dwEpCtx0 = FIELD_REPLACE(
1658300829Sgrehan		    ep_ctx->dwEpCtx0, XHCI_ST_EPCTX_RUNNING, 0x7, 0);
1659300829Sgrehan
1660300829Sgrehan	err = 0;
1661300829Sgrehan	do_intr = 0;
1662300829Sgrehan
1663300829Sgrehan	xfer = devep->ep_xfer;
1664300829Sgrehan	USB_DATA_XFER_LOCK(xfer);
1665300829Sgrehan
1666300829Sgrehan	/* outstanding requests queued up */
1667300829Sgrehan	if (dev->dev_ue->ue_data != NULL) {
1668300829Sgrehan		err = dev->dev_ue->ue_data(dev->dev_sc, xfer,
1669300829Sgrehan		            epid & 0x1 ? USB_XFER_IN : USB_XFER_OUT, epid/2);
1670300829Sgrehan		if (err == USB_ERR_CANCELLED) {
1671300829Sgrehan			if (USB_DATA_GET_ERRCODE(&xfer->data[xfer->head]) ==
1672300829Sgrehan			    USB_NAK)
1673300829Sgrehan				err = XHCI_TRB_ERROR_SUCCESS;
1674300829Sgrehan		} else {
1675300829Sgrehan			err = pci_xhci_xfer_complete(sc, xfer, slot, epid,
1676300829Sgrehan			                             &do_intr);
1677300829Sgrehan			if (err == XHCI_TRB_ERROR_SUCCESS && do_intr) {
1678300829Sgrehan				pci_xhci_assert_interrupt(sc);
1679300829Sgrehan			}
1680300829Sgrehan
1681300829Sgrehan
1682300829Sgrehan			/* XXX should not do it if error? */
1683300829Sgrehan			USB_DATA_XFER_RESET(xfer);
1684300829Sgrehan		}
1685300829Sgrehan	}
1686300829Sgrehan
1687300829Sgrehan	USB_DATA_XFER_UNLOCK(xfer);
1688300829Sgrehan
1689300829Sgrehan
1690300829Sgrehan	return (err);
1691300829Sgrehan}
1692300829Sgrehan
1693300829Sgrehan
1694300829Sgrehanstatic int
1695300829Sgrehanpci_xhci_handle_transfer(struct pci_xhci_softc *sc,
1696300829Sgrehan    struct pci_xhci_dev_emu *dev, struct pci_xhci_dev_ep *devep,
1697300829Sgrehan    struct xhci_endp_ctx *ep_ctx, struct xhci_trb *trb, uint32_t slot,
1698300829Sgrehan    uint32_t epid, uint64_t addr, uint32_t ccs, uint32_t streamid)
1699300829Sgrehan{
1700300829Sgrehan	struct xhci_trb *setup_trb;
1701300829Sgrehan	struct usb_data_xfer *xfer;
1702300829Sgrehan	struct usb_data_xfer_block *xfer_block;
1703302366Sngie	uint64_t	val;
1704300829Sgrehan	uint32_t	trbflags;
1705300829Sgrehan	int		do_intr, err;
1706300829Sgrehan	int		do_retry;
1707300829Sgrehan
1708300829Sgrehan	ep_ctx->dwEpCtx0 = FIELD_REPLACE(ep_ctx->dwEpCtx0,
1709300829Sgrehan	                                 XHCI_ST_EPCTX_RUNNING, 0x7, 0);
1710300829Sgrehan
1711300829Sgrehan	xfer = devep->ep_xfer;
1712300829Sgrehan	USB_DATA_XFER_LOCK(xfer);
1713300829Sgrehan
1714300829Sgrehan	DPRINTF(("pci_xhci handle_transfer slot %u\r\n", slot));
1715300829Sgrehan
1716300829Sgrehanretry:
1717300829Sgrehan	err = 0;
1718300829Sgrehan	do_retry = 0;
1719300829Sgrehan	do_intr = 0;
1720300829Sgrehan	setup_trb = NULL;
1721300829Sgrehan
1722300829Sgrehan	while (1) {
1723300829Sgrehan		pci_xhci_dump_trb(trb);
1724300829Sgrehan
1725300829Sgrehan		trbflags = trb->dwTrb3;
1726300829Sgrehan
1727300829Sgrehan		if (XHCI_TRB_3_TYPE_GET(trbflags) != XHCI_TRB_TYPE_LINK &&
1728300829Sgrehan		    (trbflags & XHCI_TRB_3_CYCLE_BIT) !=
1729300829Sgrehan		    (ccs & XHCI_TRB_3_CYCLE_BIT)) {
1730300829Sgrehan			DPRINTF(("Cycle-bit changed trbflags %x, ccs %x\r\n",
1731300829Sgrehan			    trbflags & XHCI_TRB_3_CYCLE_BIT, ccs));
1732300829Sgrehan			break;
1733300829Sgrehan		}
1734300829Sgrehan
1735300829Sgrehan		xfer_block = NULL;
1736300829Sgrehan
1737300829Sgrehan		switch (XHCI_TRB_3_TYPE_GET(trbflags)) {
1738300829Sgrehan		case XHCI_TRB_TYPE_LINK:
1739300829Sgrehan			if (trb->dwTrb3 & XHCI_TRB_3_TC_BIT)
1740300829Sgrehan				ccs ^= 0x1;
1741300829Sgrehan
1742300829Sgrehan			xfer_block = usb_data_xfer_append(xfer, NULL, 0,
1743300829Sgrehan			                                  (void *)addr, ccs);
1744300829Sgrehan			xfer_block->processed = 1;
1745300829Sgrehan			break;
1746300829Sgrehan
1747300829Sgrehan		case XHCI_TRB_TYPE_SETUP_STAGE:
1748300829Sgrehan			if ((trbflags & XHCI_TRB_3_IDT_BIT) == 0 ||
1749300829Sgrehan			    XHCI_TRB_2_BYTES_GET(trb->dwTrb2) != 8) {
1750300829Sgrehan				DPRINTF(("pci_xhci: invalid setup trb\r\n"));
1751300829Sgrehan				err = XHCI_TRB_ERROR_TRB;
1752300829Sgrehan				goto errout;
1753300829Sgrehan			}
1754300829Sgrehan			setup_trb = trb;
1755300829Sgrehan
1756300829Sgrehan			val = trb->qwTrb0;
1757300829Sgrehan			if (!xfer->ureq)
1758300829Sgrehan				xfer->ureq = malloc(
1759300829Sgrehan				           sizeof(struct usb_device_request));
1760300829Sgrehan			memcpy(xfer->ureq, &val,
1761300829Sgrehan			       sizeof(struct usb_device_request));
1762300829Sgrehan
1763300829Sgrehan			xfer_block = usb_data_xfer_append(xfer, NULL, 0,
1764300829Sgrehan			                                  (void *)addr, ccs);
1765300829Sgrehan			xfer_block->processed = 1;
1766300829Sgrehan			break;
1767300829Sgrehan
1768300829Sgrehan		case XHCI_TRB_TYPE_NORMAL:
1769300829Sgrehan		case XHCI_TRB_TYPE_ISOCH:
1770300829Sgrehan			if (setup_trb != NULL) {
1771300829Sgrehan				DPRINTF(("pci_xhci: trb not supposed to be in "
1772300829Sgrehan				         "ctl scope\r\n"));
1773300829Sgrehan				err = XHCI_TRB_ERROR_TRB;
1774300829Sgrehan				goto errout;
1775300829Sgrehan			}
1776300829Sgrehan			/* fall through */
1777300829Sgrehan
1778300829Sgrehan		case XHCI_TRB_TYPE_DATA_STAGE:
1779300829Sgrehan			xfer_block = usb_data_xfer_append(xfer,
1780300829Sgrehan			     (void *)(trbflags & XHCI_TRB_3_IDT_BIT ?
1781300829Sgrehan			         &trb->qwTrb0 : XHCI_GADDR(sc, trb->qwTrb0)),
1782300829Sgrehan			     trb->dwTrb2 & 0x1FFFF, (void *)addr, ccs);
1783300829Sgrehan			break;
1784300829Sgrehan
1785300829Sgrehan		case XHCI_TRB_TYPE_STATUS_STAGE:
1786300829Sgrehan			xfer_block = usb_data_xfer_append(xfer, NULL, 0,
1787300829Sgrehan			                                  (void *)addr, ccs);
1788300829Sgrehan			break;
1789300829Sgrehan
1790300829Sgrehan		case XHCI_TRB_TYPE_NOOP:
1791300829Sgrehan			xfer_block = usb_data_xfer_append(xfer, NULL, 0,
1792300829Sgrehan			                                  (void *)addr, ccs);
1793300829Sgrehan			xfer_block->processed = 1;
1794300829Sgrehan			break;
1795300829Sgrehan
1796300829Sgrehan		case XHCI_TRB_TYPE_EVENT_DATA:
1797300829Sgrehan			xfer_block = usb_data_xfer_append(xfer, NULL, 0,
1798300829Sgrehan			                                  (void *)addr, ccs);
1799300829Sgrehan			if ((epid > 1) && (trbflags & XHCI_TRB_3_IOC_BIT)) {
1800300829Sgrehan				xfer_block->processed = 1;
1801300829Sgrehan			}
1802300829Sgrehan			break;
1803300829Sgrehan
1804300829Sgrehan		default:
1805300829Sgrehan			DPRINTF(("pci_xhci: handle xfer unexpected trb type "
1806300829Sgrehan			         "0x%x\r\n",
1807300829Sgrehan			         XHCI_TRB_3_TYPE_GET(trbflags)));
1808300829Sgrehan			err = XHCI_TRB_ERROR_TRB;
1809300829Sgrehan			goto errout;
1810300829Sgrehan		}
1811300829Sgrehan
1812300829Sgrehan		trb = pci_xhci_trb_next(sc, trb, &addr);
1813300829Sgrehan
1814300829Sgrehan		DPRINTF(("pci_xhci: next trb: 0x%lx\r\n", (uint64_t)trb));
1815300829Sgrehan
1816300829Sgrehan		if (xfer_block) {
1817300829Sgrehan			xfer_block->trbnext = addr;
1818300829Sgrehan			xfer_block->streamid = streamid;
1819300829Sgrehan		}
1820300829Sgrehan
1821300829Sgrehan		if (!setup_trb && !(trbflags & XHCI_TRB_3_CHAIN_BIT) &&
1822300829Sgrehan		    XHCI_TRB_3_TYPE_GET(trbflags) != XHCI_TRB_TYPE_LINK) {
1823300829Sgrehan			break;
1824300829Sgrehan		}
1825300829Sgrehan
1826300829Sgrehan		/* handle current batch that requires interrupt on complete */
1827300829Sgrehan		if (trbflags & XHCI_TRB_3_IOC_BIT) {
1828300829Sgrehan			DPRINTF(("pci_xhci: trb IOC bit set\r\n"));
1829300829Sgrehan			if (epid == 1)
1830300829Sgrehan				do_retry = 1;
1831300829Sgrehan			break;
1832300829Sgrehan		}
1833300829Sgrehan	}
1834300829Sgrehan
1835300829Sgrehan	DPRINTF(("pci_xhci[%d]: xfer->ndata %u\r\n", __LINE__, xfer->ndata));
1836300829Sgrehan
1837300829Sgrehan	if (epid == 1) {
1838300829Sgrehan		err = USB_ERR_NOT_STARTED;
1839300829Sgrehan		if (dev->dev_ue->ue_request != NULL)
1840300829Sgrehan			err = dev->dev_ue->ue_request(dev->dev_sc, xfer);
1841300829Sgrehan		setup_trb = NULL;
1842300829Sgrehan	} else {
1843300829Sgrehan		/* handle data transfer */
1844300829Sgrehan		pci_xhci_try_usb_xfer(sc, dev, devep, ep_ctx, slot, epid);
1845300829Sgrehan		err = XHCI_TRB_ERROR_SUCCESS;
1846300829Sgrehan		goto errout;
1847300829Sgrehan	}
1848300829Sgrehan
1849300829Sgrehan	err = USB_TO_XHCI_ERR(err);
1850300829Sgrehan	if ((err == XHCI_TRB_ERROR_SUCCESS) ||
1851300829Sgrehan	    (err == XHCI_TRB_ERROR_SHORT_PKT)) {
1852300829Sgrehan		err = pci_xhci_xfer_complete(sc, xfer, slot, epid, &do_intr);
1853300829Sgrehan		if (err != XHCI_TRB_ERROR_SUCCESS)
1854300829Sgrehan			do_retry = 0;
1855300829Sgrehan	}
1856300829Sgrehan
1857300829Sgrehanerrout:
1858300829Sgrehan	if (err == XHCI_TRB_ERROR_EV_RING_FULL)
1859300829Sgrehan		DPRINTF(("pci_xhci[%d]: event ring full\r\n", __LINE__));
1860300829Sgrehan
1861300829Sgrehan	if (!do_retry)
1862300829Sgrehan		USB_DATA_XFER_UNLOCK(xfer);
1863300829Sgrehan
1864300829Sgrehan	if (do_intr)
1865300829Sgrehan		pci_xhci_assert_interrupt(sc);
1866300829Sgrehan
1867300829Sgrehan	if (do_retry) {
1868300829Sgrehan		USB_DATA_XFER_RESET(xfer);
1869300829Sgrehan		DPRINTF(("pci_xhci[%d]: retry:continuing with next TRBs\r\n",
1870300829Sgrehan		         __LINE__));
1871300829Sgrehan		goto retry;
1872300829Sgrehan	}
1873300829Sgrehan
1874300829Sgrehan	if (epid == 1)
1875300829Sgrehan		USB_DATA_XFER_RESET(xfer);
1876300829Sgrehan
1877300829Sgrehan	return (err);
1878300829Sgrehan}
1879300829Sgrehan
1880300829Sgrehanstatic void
1881300829Sgrehanpci_xhci_device_doorbell(struct pci_xhci_softc *sc, uint32_t slot,
1882300829Sgrehan    uint32_t epid, uint32_t streamid)
1883300829Sgrehan{
1884300829Sgrehan	struct pci_xhci_dev_emu *dev;
1885300829Sgrehan	struct pci_xhci_dev_ep	*devep;
1886300829Sgrehan	struct xhci_dev_ctx	*dev_ctx;
1887300829Sgrehan	struct xhci_endp_ctx	*ep_ctx;
1888300829Sgrehan	struct pci_xhci_trb_ring *sctx_tr;
1889300829Sgrehan	struct xhci_trb	*trb;
1890300829Sgrehan	uint64_t	ringaddr;
1891300829Sgrehan	uint32_t	ccs;
1892300829Sgrehan
1893300829Sgrehan	DPRINTF(("pci_xhci doorbell slot %u epid %u stream %u\r\n",
1894300829Sgrehan	    slot, epid, streamid));
1895300829Sgrehan
1896300829Sgrehan	if (slot == 0 || slot > sc->ndevices) {
1897300829Sgrehan		DPRINTF(("pci_xhci: invalid doorbell slot %u\r\n", slot));
1898300829Sgrehan		return;
1899300829Sgrehan	}
1900300829Sgrehan
1901300829Sgrehan	dev = XHCI_SLOTDEV_PTR(sc, slot);
1902300829Sgrehan	devep = &dev->eps[epid];
1903300829Sgrehan	dev_ctx = pci_xhci_get_dev_ctx(sc, slot);
1904300829Sgrehan	if (!dev_ctx) {
1905300829Sgrehan		return;
1906300829Sgrehan	}
1907300829Sgrehan	ep_ctx = &dev_ctx->ctx_ep[epid];
1908300829Sgrehan
1909300829Sgrehan	sctx_tr = NULL;
1910300829Sgrehan
1911300829Sgrehan	DPRINTF(("pci_xhci: device doorbell ep[%u] %08x %08x %016lx %08x\r\n",
1912300829Sgrehan	        epid, ep_ctx->dwEpCtx0, ep_ctx->dwEpCtx1, ep_ctx->qwEpCtx2,
1913300829Sgrehan	        ep_ctx->dwEpCtx4));
1914300829Sgrehan
1915300829Sgrehan	if (ep_ctx->qwEpCtx2 == 0)
1916300829Sgrehan		return;
1917300829Sgrehan
1918300829Sgrehan	/* handle pending transfers */
1919300829Sgrehan	if (devep->ep_xfer->ndata > 0) {
1920300829Sgrehan		pci_xhci_try_usb_xfer(sc, dev, devep, ep_ctx, slot, epid);
1921300829Sgrehan		return;
1922300829Sgrehan	}
1923300829Sgrehan
1924300829Sgrehan	/* get next trb work item */
1925300829Sgrehan	if (XHCI_EPCTX_0_MAXP_STREAMS_GET(ep_ctx->dwEpCtx0) != 0) {
1926300829Sgrehan		sctx_tr = &devep->ep_sctx_trbs[streamid];
1927300829Sgrehan		ringaddr = sctx_tr->ringaddr;
1928300829Sgrehan		ccs = sctx_tr->ccs;
1929300829Sgrehan		trb = XHCI_GADDR(sc, sctx_tr->ringaddr & ~0xFUL);
1930300829Sgrehan		DPRINTF(("doorbell, stream %u, ccs %lx, trb ccs %x\r\n",
1931300829Sgrehan		        streamid, ep_ctx->qwEpCtx2 & XHCI_TRB_3_CYCLE_BIT,
1932300829Sgrehan		        trb->dwTrb3 & XHCI_TRB_3_CYCLE_BIT));
1933300829Sgrehan	} else {
1934300829Sgrehan		ringaddr = devep->ep_ringaddr;
1935300829Sgrehan		ccs = devep->ep_ccs;
1936300829Sgrehan		trb = devep->ep_tr;
1937300829Sgrehan		DPRINTF(("doorbell, ccs %lx, trb ccs %x\r\n",
1938300829Sgrehan		        ep_ctx->qwEpCtx2 & XHCI_TRB_3_CYCLE_BIT,
1939300829Sgrehan		        trb->dwTrb3 & XHCI_TRB_3_CYCLE_BIT));
1940300829Sgrehan	}
1941300829Sgrehan
1942300829Sgrehan	if (XHCI_TRB_3_TYPE_GET(trb->dwTrb3) == 0) {
1943300829Sgrehan		DPRINTF(("pci_xhci: ring %lx trb[%lx] EP %u is RESERVED?\r\n",
1944300829Sgrehan		        ep_ctx->qwEpCtx2, devep->ep_ringaddr, epid));
1945300829Sgrehan		return;
1946300829Sgrehan	}
1947300829Sgrehan
1948300829Sgrehan	pci_xhci_handle_transfer(sc, dev, devep, ep_ctx, trb, slot, epid,
1949300829Sgrehan	                         ringaddr, ccs, streamid);
1950300829Sgrehan}
1951300829Sgrehan
1952300829Sgrehanstatic void
1953300829Sgrehanpci_xhci_dbregs_write(struct pci_xhci_softc *sc, uint64_t offset,
1954300829Sgrehan    uint64_t value)
1955300829Sgrehan{
1956300829Sgrehan
1957300829Sgrehan	offset = (offset - sc->dboff) / sizeof(uint32_t);
1958300829Sgrehan
1959300829Sgrehan	DPRINTF(("pci_xhci: doorbell write offset 0x%lx: 0x%lx\r\n",
1960300829Sgrehan	        offset, value));
1961300829Sgrehan
1962300829Sgrehan	if (XHCI_HALTED(sc)) {
1963300829Sgrehan		DPRINTF(("pci_xhci: controller halted\r\n"));
1964300829Sgrehan		return;
1965300829Sgrehan	}
1966300829Sgrehan
1967300829Sgrehan	if (offset == 0)
1968300829Sgrehan		pci_xhci_complete_commands(sc);
1969300829Sgrehan	else if (sc->portregs != NULL)
1970300829Sgrehan		pci_xhci_device_doorbell(sc, offset,
1971300829Sgrehan		   XHCI_DB_TARGET_GET(value), XHCI_DB_SID_GET(value));
1972300829Sgrehan}
1973300829Sgrehan
1974300829Sgrehanstatic void
1975300829Sgrehanpci_xhci_rtsregs_write(struct pci_xhci_softc *sc, uint64_t offset,
1976300829Sgrehan    uint64_t value)
1977300829Sgrehan{
1978300829Sgrehan	struct pci_xhci_rtsregs *rts;
1979300829Sgrehan
1980300829Sgrehan	offset -= sc->rtsoff;
1981300829Sgrehan
1982300829Sgrehan	if (offset == 0) {
1983300829Sgrehan		DPRINTF(("pci_xhci attempted write to MFINDEX\r\n"));
1984300829Sgrehan		return;
1985300829Sgrehan	}
1986300829Sgrehan
1987300829Sgrehan	DPRINTF(("pci_xhci: runtime regs write offset 0x%lx: 0x%lx\r\n",
1988300829Sgrehan	        offset, value));
1989300829Sgrehan
1990300829Sgrehan	offset -= 0x20;		/* start of intrreg */
1991300829Sgrehan
1992300829Sgrehan	rts = &sc->rtsregs;
1993300829Sgrehan
1994300829Sgrehan	switch (offset) {
1995300829Sgrehan	case 0x00:
1996300829Sgrehan		if (value & XHCI_IMAN_INTR_PEND)
1997300829Sgrehan			rts->intrreg.iman &= ~XHCI_IMAN_INTR_PEND;
1998300829Sgrehan		rts->intrreg.iman = (value & XHCI_IMAN_INTR_ENA) |
1999300829Sgrehan		                    (rts->intrreg.iman & XHCI_IMAN_INTR_PEND);
2000300829Sgrehan
2001300829Sgrehan		if (!(value & XHCI_IMAN_INTR_ENA))
2002300829Sgrehan			pci_xhci_deassert_interrupt(sc);
2003300829Sgrehan
2004300829Sgrehan		break;
2005300829Sgrehan
2006300829Sgrehan	case 0x04:
2007300829Sgrehan		rts->intrreg.imod = value;
2008300829Sgrehan		break;
2009300829Sgrehan
2010300829Sgrehan	case 0x08:
2011300829Sgrehan		rts->intrreg.erstsz = value & 0xFFFF;
2012300829Sgrehan		break;
2013300829Sgrehan
2014300829Sgrehan	case 0x10:
2015300829Sgrehan		/* ERSTBA low bits */
2016300829Sgrehan		rts->intrreg.erstba = MASK_64_HI(sc->rtsregs.intrreg.erstba) |
2017300829Sgrehan		                      (value & ~0x3F);
2018300829Sgrehan		break;
2019300829Sgrehan
2020300829Sgrehan	case 0x14:
2021300829Sgrehan		/* ERSTBA high bits */
2022300829Sgrehan		rts->intrreg.erstba = (value << 32) |
2023300829Sgrehan		    MASK_64_LO(sc->rtsregs.intrreg.erstba);
2024300829Sgrehan
2025300829Sgrehan		rts->erstba_p = XHCI_GADDR(sc,
2026300829Sgrehan		                        sc->rtsregs.intrreg.erstba & ~0x3FUL);
2027300829Sgrehan
2028300829Sgrehan		rts->erst_p = XHCI_GADDR(sc,
2029300829Sgrehan		              sc->rtsregs.erstba_p->qwEvrsTablePtr & ~0x3FUL);
2030300829Sgrehan
2031300829Sgrehan		rts->er_enq_idx = 0;
2032300829Sgrehan		rts->er_events_cnt = 0;
2033300829Sgrehan
2034300829Sgrehan		DPRINTF(("pci_xhci: wr erstba erst (%p) ptr 0x%lx, sz %u\r\n",
2035300829Sgrehan		        rts->erstba_p,
2036300829Sgrehan		        rts->erstba_p->qwEvrsTablePtr,
2037300829Sgrehan		        rts->erstba_p->dwEvrsTableSize));
2038300829Sgrehan		break;
2039300829Sgrehan
2040300829Sgrehan	case 0x18:
2041300829Sgrehan		/* ERDP low bits */
2042300829Sgrehan		rts->intrreg.erdp =
2043300829Sgrehan		    MASK_64_HI(sc->rtsregs.intrreg.erdp) |
2044300829Sgrehan		    (rts->intrreg.erdp & XHCI_ERDP_LO_BUSY) |
2045300829Sgrehan		    (value & ~0xF);
2046300829Sgrehan		if (value & XHCI_ERDP_LO_BUSY) {
2047300829Sgrehan			rts->intrreg.erdp &= ~XHCI_ERDP_LO_BUSY;
2048300829Sgrehan			rts->intrreg.iman &= ~XHCI_IMAN_INTR_PEND;
2049300829Sgrehan		}
2050300829Sgrehan
2051300829Sgrehan		rts->er_deq_seg = XHCI_ERDP_LO_SINDEX(value);
2052300829Sgrehan
2053300829Sgrehan		break;
2054300829Sgrehan
2055300829Sgrehan	case 0x1C:
2056300829Sgrehan		/* ERDP high bits */
2057300829Sgrehan		rts->intrreg.erdp = (value << 32) |
2058300829Sgrehan		    MASK_64_LO(sc->rtsregs.intrreg.erdp);
2059300829Sgrehan
2060300829Sgrehan		if (rts->er_events_cnt > 0) {
2061300829Sgrehan			uint64_t erdp;
2062300829Sgrehan			uint32_t erdp_i;
2063300829Sgrehan
2064300829Sgrehan			erdp = rts->intrreg.erdp & ~0xF;
2065300829Sgrehan			erdp_i = (erdp - rts->erstba_p->qwEvrsTablePtr) /
2066300829Sgrehan			           sizeof(struct xhci_trb);
2067300829Sgrehan
2068300829Sgrehan			if (erdp_i <= rts->er_enq_idx)
2069300829Sgrehan				rts->er_events_cnt = rts->er_enq_idx - erdp_i;
2070300829Sgrehan			else
2071300829Sgrehan				rts->er_events_cnt =
2072300829Sgrehan				          rts->erstba_p->dwEvrsTableSize -
2073300829Sgrehan				          (erdp_i - rts->er_enq_idx);
2074300829Sgrehan
2075300829Sgrehan			DPRINTF(("pci_xhci: erdp 0x%lx, events cnt %u\r\n",
2076300829Sgrehan			        erdp, rts->er_events_cnt));
2077300829Sgrehan		}
2078300829Sgrehan
2079300829Sgrehan		break;
2080300829Sgrehan
2081300829Sgrehan	default:
2082300829Sgrehan		DPRINTF(("pci_xhci attempted write to RTS offset 0x%lx\r\n",
2083300829Sgrehan		        offset));
2084300829Sgrehan		break;
2085300829Sgrehan	}
2086300829Sgrehan}
2087300829Sgrehan
2088300829Sgrehanstatic uint64_t
2089300829Sgrehanpci_xhci_portregs_read(struct pci_xhci_softc *sc, uint64_t offset)
2090300829Sgrehan{
2091300829Sgrehan	int port;
2092300829Sgrehan	uint32_t *p;
2093300829Sgrehan
2094300829Sgrehan	if (sc->portregs == NULL)
2095300829Sgrehan		return (0);
2096300829Sgrehan
2097300829Sgrehan	port = (offset - 0x3F0) / 0x10;
2098300829Sgrehan
2099300829Sgrehan	if (port > XHCI_MAX_DEVS) {
2100300829Sgrehan		DPRINTF(("pci_xhci: portregs_read port %d >= XHCI_MAX_DEVS\r\n",
2101300829Sgrehan		    port));
2102300829Sgrehan
2103300829Sgrehan		/* return default value for unused port */
2104300829Sgrehan		return (XHCI_PS_SPEED_SET(3));
2105300829Sgrehan	}
2106300829Sgrehan
2107300829Sgrehan	offset = (offset - 0x3F0) % 0x10;
2108300829Sgrehan
2109300829Sgrehan	p = &sc->portregs[port].portsc;
2110300829Sgrehan	p += offset / sizeof(uint32_t);
2111300829Sgrehan
2112300829Sgrehan	DPRINTF(("pci_xhci: portregs read offset 0x%lx port %u -> 0x%x\r\n",
2113300829Sgrehan	        offset, port, *p));
2114300829Sgrehan
2115300829Sgrehan	return (*p);
2116300829Sgrehan}
2117300829Sgrehan
2118300829Sgrehanstatic void
2119300829Sgrehanpci_xhci_hostop_write(struct pci_xhci_softc *sc, uint64_t offset,
2120300829Sgrehan    uint64_t value)
2121300829Sgrehan{
2122300829Sgrehan	offset -= XHCI_CAPLEN;
2123300829Sgrehan
2124300829Sgrehan	if (offset < 0x400)
2125300829Sgrehan		DPRINTF(("pci_xhci: hostop write offset 0x%lx: 0x%lx\r\n",
2126300829Sgrehan		         offset, value));
2127300829Sgrehan
2128300829Sgrehan	switch (offset) {
2129300829Sgrehan	case XHCI_USBCMD:
2130300829Sgrehan		sc->opregs.usbcmd = pci_xhci_usbcmd_write(sc, value & 0x3F0F);
2131300829Sgrehan		break;
2132300829Sgrehan
2133300829Sgrehan	case XHCI_USBSTS:
2134300829Sgrehan		/* clear bits on write */
2135300829Sgrehan		sc->opregs.usbsts &= ~(value &
2136300829Sgrehan		      (XHCI_STS_HSE|XHCI_STS_EINT|XHCI_STS_PCD|XHCI_STS_SSS|
2137300829Sgrehan		       XHCI_STS_RSS|XHCI_STS_SRE|XHCI_STS_CNR));
2138300829Sgrehan		break;
2139300829Sgrehan
2140300829Sgrehan	case XHCI_PAGESIZE:
2141300829Sgrehan		/* read only */
2142300829Sgrehan		break;
2143300829Sgrehan
2144300829Sgrehan	case XHCI_DNCTRL:
2145300829Sgrehan		sc->opregs.dnctrl = value & 0xFFFF;
2146300829Sgrehan		break;
2147300829Sgrehan
2148300829Sgrehan	case XHCI_CRCR_LO:
2149300829Sgrehan		if (sc->opregs.crcr & XHCI_CRCR_LO_CRR) {
2150300829Sgrehan			sc->opregs.crcr &= ~(XHCI_CRCR_LO_CS|XHCI_CRCR_LO_CA);
2151300829Sgrehan			sc->opregs.crcr |= value &
2152300829Sgrehan			                   (XHCI_CRCR_LO_CS|XHCI_CRCR_LO_CA);
2153300829Sgrehan		} else {
2154300829Sgrehan			sc->opregs.crcr = MASK_64_HI(sc->opregs.crcr) |
2155300829Sgrehan			           (value & (0xFFFFFFC0 | XHCI_CRCR_LO_RCS));
2156300829Sgrehan		}
2157300829Sgrehan		break;
2158300829Sgrehan
2159300829Sgrehan	case XHCI_CRCR_HI:
2160300829Sgrehan		if (!(sc->opregs.crcr & XHCI_CRCR_LO_CRR)) {
2161300829Sgrehan			sc->opregs.crcr = MASK_64_LO(sc->opregs.crcr) |
2162300829Sgrehan			                  (value << 32);
2163300829Sgrehan
2164300829Sgrehan			sc->opregs.cr_p = XHCI_GADDR(sc,
2165300829Sgrehan			                  sc->opregs.crcr & ~0xF);
2166300829Sgrehan		}
2167300829Sgrehan
2168300829Sgrehan		if (sc->opregs.crcr & XHCI_CRCR_LO_CS) {
2169300829Sgrehan			/* Stop operation of Command Ring */
2170300829Sgrehan		}
2171300829Sgrehan
2172300829Sgrehan		if (sc->opregs.crcr & XHCI_CRCR_LO_CA) {
2173300829Sgrehan			/* Abort command */
2174300829Sgrehan		}
2175300829Sgrehan
2176300829Sgrehan		break;
2177300829Sgrehan
2178300829Sgrehan	case XHCI_DCBAAP_LO:
2179300829Sgrehan		sc->opregs.dcbaap = MASK_64_HI(sc->opregs.dcbaap) |
2180300829Sgrehan		                    (value & 0xFFFFFFC0);
2181300829Sgrehan		break;
2182300829Sgrehan
2183300829Sgrehan	case XHCI_DCBAAP_HI:
2184300829Sgrehan		sc->opregs.dcbaap =  MASK_64_LO(sc->opregs.dcbaap) |
2185300829Sgrehan		                     (value << 32);
2186300829Sgrehan		sc->opregs.dcbaa_p = XHCI_GADDR(sc, sc->opregs.dcbaap & ~0x3FUL);
2187300829Sgrehan
2188300829Sgrehan		DPRINTF(("pci_xhci: opregs dcbaap = 0x%lx (vaddr 0x%lx)\r\n",
2189300829Sgrehan		    sc->opregs.dcbaap, (uint64_t)sc->opregs.dcbaa_p));
2190300829Sgrehan		break;
2191300829Sgrehan
2192300829Sgrehan	case XHCI_CONFIG:
2193300829Sgrehan		sc->opregs.config = value & 0x03FF;
2194300829Sgrehan		break;
2195300829Sgrehan
2196300829Sgrehan	default:
2197300829Sgrehan		if (offset >= 0x400)
2198300829Sgrehan			pci_xhci_portregs_write(sc, offset, value);
2199300829Sgrehan
2200300829Sgrehan		break;
2201300829Sgrehan	}
2202300829Sgrehan}
2203300829Sgrehan
2204300829Sgrehan
2205300829Sgrehanstatic void
2206300829Sgrehanpci_xhci_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
2207300829Sgrehan                int baridx, uint64_t offset, int size, uint64_t value)
2208300829Sgrehan{
2209300829Sgrehan	struct pci_xhci_softc *sc;
2210300829Sgrehan
2211300829Sgrehan	sc = pi->pi_arg;
2212300829Sgrehan
2213300829Sgrehan        assert(baridx == 0);
2214300829Sgrehan
2215300829Sgrehan
2216300829Sgrehan        pthread_mutex_lock(&sc->mtx);
2217300829Sgrehan	if (offset < XHCI_CAPLEN)	/* read only registers */
2218300829Sgrehan                WPRINTF(("pci_xhci: write RO-CAPs offset %ld\r\n", offset));
2219300829Sgrehan	else if (offset < sc->dboff)
2220300829Sgrehan		pci_xhci_hostop_write(sc, offset, value);
2221300829Sgrehan	else if (offset < sc->rtsoff)
2222300829Sgrehan		pci_xhci_dbregs_write(sc, offset, value);
2223300829Sgrehan	else if (offset < sc->regsend)
2224300829Sgrehan		pci_xhci_rtsregs_write(sc, offset, value);
2225300829Sgrehan	else
2226300829Sgrehan                WPRINTF(("pci_xhci: write invalid offset %ld\r\n", offset));
2227300829Sgrehan
2228300829Sgrehan        pthread_mutex_unlock(&sc->mtx);
2229300829Sgrehan}
2230300829Sgrehan
2231300829Sgrehanstatic uint64_t
2232300829Sgrehanpci_xhci_hostcap_read(struct pci_xhci_softc *sc, uint64_t offset)
2233300829Sgrehan{
2234300829Sgrehan	uint64_t	value;
2235300829Sgrehan
2236300829Sgrehan	switch (offset) {
2237300829Sgrehan	case XHCI_CAPLENGTH:	/* 0x00 */
2238300829Sgrehan		value = sc->caplength;
2239300829Sgrehan		break;
2240300829Sgrehan
2241300829Sgrehan	case XHCI_HCSPARAMS1:	/* 0x04 */
2242300829Sgrehan		value = sc->hcsparams1;
2243300829Sgrehan		break;
2244300829Sgrehan
2245300829Sgrehan	case XHCI_HCSPARAMS2:	/* 0x08 */
2246300829Sgrehan		value = sc->hcsparams2;
2247300829Sgrehan		break;
2248300829Sgrehan
2249300829Sgrehan	case XHCI_HCSPARAMS3:	/* 0x0C */
2250300829Sgrehan		value = sc->hcsparams3;
2251300829Sgrehan		break;
2252300829Sgrehan
2253300829Sgrehan	case XHCI_HCSPARAMS0:	/* 0x10 */
2254300829Sgrehan		value = sc->hccparams1;
2255300829Sgrehan		break;
2256300829Sgrehan
2257300829Sgrehan	case XHCI_DBOFF:	/* 0x14 */
2258300829Sgrehan		value = sc->dboff;
2259300829Sgrehan		break;
2260300829Sgrehan
2261300829Sgrehan	case XHCI_RTSOFF:	/* 0x18 */
2262300829Sgrehan		value = sc->rtsoff;
2263300829Sgrehan		break;
2264300829Sgrehan
2265300829Sgrehan	case XHCI_HCCPRAMS2:	/* 0x1C */
2266300829Sgrehan		value = sc->hccparams2;
2267300829Sgrehan		break;
2268300829Sgrehan
2269300829Sgrehan	default:
2270300829Sgrehan		value = 0;
2271300829Sgrehan		break;
2272300829Sgrehan	}
2273300829Sgrehan
2274300829Sgrehan	DPRINTF(("pci_xhci: hostcap read offset 0x%lx -> 0x%lx\r\n",
2275300829Sgrehan	        offset, value));
2276300829Sgrehan
2277300829Sgrehan	return (value);
2278300829Sgrehan}
2279300829Sgrehan
2280300829Sgrehanstatic uint64_t
2281300829Sgrehanpci_xhci_hostop_read(struct pci_xhci_softc *sc, uint64_t offset)
2282300829Sgrehan{
2283300829Sgrehan	uint64_t value;
2284300829Sgrehan
2285300829Sgrehan	offset = (offset - XHCI_CAPLEN);
2286300829Sgrehan
2287300829Sgrehan	switch (offset) {
2288300829Sgrehan	case XHCI_USBCMD:	/* 0x00 */
2289300829Sgrehan		value = sc->opregs.usbcmd;
2290300829Sgrehan		break;
2291300829Sgrehan
2292300829Sgrehan	case XHCI_USBSTS:	/* 0x04 */
2293300829Sgrehan		value = sc->opregs.usbsts;
2294300829Sgrehan		break;
2295300829Sgrehan
2296300829Sgrehan	case XHCI_PAGESIZE:	/* 0x08 */
2297300829Sgrehan		value = sc->opregs.pgsz;
2298300829Sgrehan		break;
2299300829Sgrehan
2300300829Sgrehan	case XHCI_DNCTRL:	/* 0x14 */
2301300829Sgrehan		value = sc->opregs.dnctrl;
2302300829Sgrehan		break;
2303300829Sgrehan
2304300829Sgrehan	case XHCI_CRCR_LO:	/* 0x18 */
2305300829Sgrehan		value = sc->opregs.crcr & XHCI_CRCR_LO_CRR;
2306300829Sgrehan		break;
2307300829Sgrehan
2308300829Sgrehan	case XHCI_CRCR_HI:	/* 0x1C */
2309300829Sgrehan		value = 0;
2310300829Sgrehan		break;
2311300829Sgrehan
2312300829Sgrehan	case XHCI_DCBAAP_LO:	/* 0x30 */
2313300829Sgrehan		value = sc->opregs.dcbaap & 0xFFFFFFFF;
2314300829Sgrehan		break;
2315300829Sgrehan
2316300829Sgrehan	case XHCI_DCBAAP_HI:	/* 0x34 */
2317300829Sgrehan		value = (sc->opregs.dcbaap >> 32) & 0xFFFFFFFF;
2318300829Sgrehan		break;
2319300829Sgrehan
2320300829Sgrehan	case XHCI_CONFIG:	/* 0x38 */
2321300829Sgrehan		value = sc->opregs.config;
2322300829Sgrehan		break;
2323300829Sgrehan
2324300829Sgrehan	default:
2325300829Sgrehan		if (offset >= 0x400)
2326300829Sgrehan			value = pci_xhci_portregs_read(sc, offset);
2327300829Sgrehan		else
2328300829Sgrehan			value = 0;
2329300829Sgrehan
2330300829Sgrehan		break;
2331300829Sgrehan	}
2332300829Sgrehan
2333300829Sgrehan	if (offset < 0x400)
2334300829Sgrehan		DPRINTF(("pci_xhci: hostop read offset 0x%lx -> 0x%lx\r\n",
2335300829Sgrehan		        offset, value));
2336300829Sgrehan
2337300829Sgrehan	return (value);
2338300829Sgrehan}
2339300829Sgrehan
2340300829Sgrehanstatic uint64_t
2341300829Sgrehanpci_xhci_dbregs_read(struct pci_xhci_softc *sc, uint64_t offset)
2342300829Sgrehan{
2343300829Sgrehan
2344300829Sgrehan	/* read doorbell always returns 0 */
2345300829Sgrehan	return (0);
2346300829Sgrehan}
2347300829Sgrehan
2348300829Sgrehanstatic uint64_t
2349300829Sgrehanpci_xhci_rtsregs_read(struct pci_xhci_softc *sc, uint64_t offset)
2350300829Sgrehan{
2351300829Sgrehan	uint32_t	value;
2352300829Sgrehan
2353300829Sgrehan	offset -= sc->rtsoff;
2354300829Sgrehan	value = 0;
2355300829Sgrehan
2356300829Sgrehan	if (offset == XHCI_MFINDEX) {
2357300829Sgrehan		value = sc->rtsregs.mfindex;
2358300829Sgrehan	} else if (offset >= 0x20) {
2359300829Sgrehan		int item;
2360300829Sgrehan		uint32_t *p;
2361300829Sgrehan
2362300829Sgrehan		offset -= 0x20;
2363300829Sgrehan		item = offset % 32;
2364300829Sgrehan
2365300829Sgrehan		assert(offset < sizeof(sc->rtsregs.intrreg));
2366300829Sgrehan
2367300829Sgrehan		p = &sc->rtsregs.intrreg.iman;
2368300829Sgrehan		p += item / sizeof(uint32_t);
2369300829Sgrehan		value = *p;
2370300829Sgrehan	}
2371300829Sgrehan
2372300829Sgrehan	DPRINTF(("pci_xhci: rtsregs read offset 0x%lx -> 0x%x\r\n",
2373300829Sgrehan	        offset, value));
2374300829Sgrehan
2375300829Sgrehan	return (value);
2376300829Sgrehan}
2377300829Sgrehan
2378300829Sgrehanstatic uint64_t
2379300829Sgrehanpci_xhci_xecp_read(struct pci_xhci_softc *sc, uint64_t offset)
2380300829Sgrehan{
2381300829Sgrehan	uint32_t	value;
2382300829Sgrehan
2383300829Sgrehan	offset -= sc->regsend;
2384300829Sgrehan	value = 0;
2385300829Sgrehan
2386300829Sgrehan	switch (offset) {
2387300829Sgrehan	case 0:
2388300829Sgrehan		/* rev major | rev minor | next-cap | cap-id */
2389300829Sgrehan		value = (0x02 << 24) | (4 << 8) | XHCI_ID_PROTOCOLS;
2390300829Sgrehan		break;
2391300829Sgrehan	case 4:
2392300829Sgrehan		/* name string = "USB" */
2393300829Sgrehan		value = 0x20425355;
2394300829Sgrehan		break;
2395300829Sgrehan	case 8:
2396300829Sgrehan		/* psic | proto-defined | compat # | compat offset */
2397300829Sgrehan		value = ((XHCI_MAX_DEVS/2) << 8) | sc->usb2_port_start;
2398300829Sgrehan		break;
2399300829Sgrehan	case 12:
2400300829Sgrehan		break;
2401300829Sgrehan	case 16:
2402300829Sgrehan		/* rev major | rev minor | next-cap | cap-id */
2403300829Sgrehan		value = (0x03 << 24) | XHCI_ID_PROTOCOLS;
2404300829Sgrehan		break;
2405300829Sgrehan	case 20:
2406300829Sgrehan		/* name string = "USB" */
2407300829Sgrehan		value = 0x20425355;
2408300829Sgrehan		break;
2409300829Sgrehan	case 24:
2410300829Sgrehan		/* psic | proto-defined | compat # | compat offset */
2411300829Sgrehan		value = ((XHCI_MAX_DEVS/2) << 8) | sc->usb3_port_start;
2412300829Sgrehan		break;
2413300829Sgrehan	case 28:
2414300829Sgrehan		break;
2415300829Sgrehan	default:
2416300829Sgrehan		DPRINTF(("pci_xhci: xecp invalid offset 0x%lx\r\n", offset));
2417300829Sgrehan		break;
2418300829Sgrehan	}
2419300829Sgrehan
2420300829Sgrehan	DPRINTF(("pci_xhci: xecp read offset 0x%lx -> 0x%x\r\n",
2421300829Sgrehan	        offset, value));
2422300829Sgrehan
2423300829Sgrehan	return (value);
2424300829Sgrehan}
2425300829Sgrehan
2426300829Sgrehan
2427300829Sgrehanstatic uint64_t
2428300829Sgrehanpci_xhci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
2429300829Sgrehan    uint64_t offset, int size)
2430300829Sgrehan{
2431300829Sgrehan	struct pci_xhci_softc *sc;
2432300829Sgrehan	uint32_t	value;
2433300829Sgrehan
2434300829Sgrehan	sc = pi->pi_arg;
2435300829Sgrehan
2436300829Sgrehan        assert(baridx == 0);
2437300829Sgrehan
2438300829Sgrehan        pthread_mutex_lock(&sc->mtx);
2439300829Sgrehan	if (offset < XHCI_CAPLEN)
2440300829Sgrehan		value = pci_xhci_hostcap_read(sc, offset);
2441300829Sgrehan	else if (offset < sc->dboff)
2442300829Sgrehan		value = pci_xhci_hostop_read(sc, offset);
2443300829Sgrehan	else if (offset < sc->rtsoff)
2444300829Sgrehan		value = pci_xhci_dbregs_read(sc, offset);
2445300829Sgrehan	else if (offset < sc->regsend)
2446300829Sgrehan		value = pci_xhci_rtsregs_read(sc, offset);
2447300829Sgrehan	else if (offset < (sc->regsend + 4*32))
2448300829Sgrehan		value = pci_xhci_xecp_read(sc, offset);
2449300829Sgrehan	else {
2450300829Sgrehan		value = 0;
2451300829Sgrehan                WPRINTF(("pci_xhci: read invalid offset %ld\r\n", offset));
2452300829Sgrehan	}
2453300829Sgrehan
2454300829Sgrehan        pthread_mutex_unlock(&sc->mtx);
2455300829Sgrehan
2456300829Sgrehan	switch (size) {
2457300829Sgrehan	case 1:
2458300829Sgrehan		value &= 0xFF;
2459300829Sgrehan		break;
2460300829Sgrehan	case 2:
2461300829Sgrehan		value &= 0xFFFF;
2462300829Sgrehan		break;
2463300829Sgrehan	case 4:
2464300829Sgrehan		value &= 0xFFFFFFFF;
2465300829Sgrehan		break;
2466300829Sgrehan	}
2467300829Sgrehan
2468300829Sgrehan	return (value);
2469300829Sgrehan}
2470300829Sgrehan
2471300829Sgrehanstatic void
2472300829Sgrehanpci_xhci_reset_port(struct pci_xhci_softc *sc, int portn, int warm)
2473300829Sgrehan{
2474300829Sgrehan	struct pci_xhci_portregs *port;
2475300829Sgrehan	struct pci_xhci_dev_emu	*dev;
2476300829Sgrehan	struct xhci_trb		evtrb;
2477300829Sgrehan	int	error;
2478300829Sgrehan
2479300829Sgrehan	assert(portn <= XHCI_MAX_DEVS);
2480300829Sgrehan
2481300829Sgrehan	DPRINTF(("xhci reset port %d\r\n", portn));
2482300829Sgrehan
2483300829Sgrehan	port = XHCI_PORTREG_PTR(sc, portn);
2484300829Sgrehan	dev = XHCI_DEVINST_PTR(sc, portn);
2485300829Sgrehan	if (dev) {
2486300829Sgrehan		port->portsc &= ~(XHCI_PS_PLS_MASK | XHCI_PS_PR | XHCI_PS_PRC);
2487300829Sgrehan		port->portsc |= XHCI_PS_PED |
2488300829Sgrehan		    XHCI_PS_SPEED_SET(dev->dev_ue->ue_usbspeed);
2489300829Sgrehan
2490300829Sgrehan		if (warm && dev->dev_ue->ue_usbver == 3) {
2491300829Sgrehan			port->portsc |= XHCI_PS_WRC;
2492300829Sgrehan		}
2493300829Sgrehan
2494300829Sgrehan		if ((port->portsc & XHCI_PS_PRC) == 0) {
2495300829Sgrehan			port->portsc |= XHCI_PS_PRC;
2496300829Sgrehan
2497300829Sgrehan			pci_xhci_set_evtrb(&evtrb, portn,
2498300829Sgrehan			     XHCI_TRB_ERROR_SUCCESS,
2499300829Sgrehan			     XHCI_TRB_EVENT_PORT_STS_CHANGE);
2500300829Sgrehan			error = pci_xhci_insert_event(sc, &evtrb, 1);
2501300829Sgrehan			if (error != XHCI_TRB_ERROR_SUCCESS)
2502300829Sgrehan				DPRINTF(("xhci reset port insert event "
2503300829Sgrehan				         "failed\r\n"));
2504300829Sgrehan		}
2505300829Sgrehan	}
2506300829Sgrehan}
2507300829Sgrehan
2508300829Sgrehanstatic void
2509300829Sgrehanpci_xhci_init_port(struct pci_xhci_softc *sc, int portn)
2510300829Sgrehan{
2511300829Sgrehan	struct pci_xhci_portregs *port;
2512300829Sgrehan	struct pci_xhci_dev_emu	*dev;
2513300829Sgrehan
2514300829Sgrehan	port = XHCI_PORTREG_PTR(sc, portn);
2515300829Sgrehan	dev = XHCI_DEVINST_PTR(sc, portn);
2516300829Sgrehan	if (dev) {
2517300829Sgrehan		port->portsc = XHCI_PS_CCS |		/* connected */
2518300829Sgrehan		               XHCI_PS_PP;		/* port power */
2519300829Sgrehan
2520300829Sgrehan		if (dev->dev_ue->ue_usbver == 2) {
2521300829Sgrehan			port->portsc |= XHCI_PS_PLS_SET(UPS_PORT_LS_POLL) |
2522300829Sgrehan		               XHCI_PS_SPEED_SET(dev->dev_ue->ue_usbspeed);
2523300829Sgrehan		} else {
2524300829Sgrehan			port->portsc |= XHCI_PS_PLS_SET(UPS_PORT_LS_U0) |
2525300829Sgrehan		               XHCI_PS_PED |		/* enabled */
2526300829Sgrehan		               XHCI_PS_SPEED_SET(dev->dev_ue->ue_usbspeed);
2527300829Sgrehan		}
2528300829Sgrehan
2529300829Sgrehan		DPRINTF(("Init port %d 0x%x\n", portn, port->portsc));
2530300829Sgrehan	} else {
2531300829Sgrehan		port->portsc = XHCI_PS_PLS_SET(UPS_PORT_LS_RX_DET) | XHCI_PS_PP;
2532300829Sgrehan		DPRINTF(("Init empty port %d 0x%x\n", portn, port->portsc));
2533300829Sgrehan	}
2534300829Sgrehan}
2535300829Sgrehan
2536300829Sgrehanstatic int
2537300829Sgrehanpci_xhci_dev_intr(struct usb_hci *hci, int epctx)
2538300829Sgrehan{
2539300829Sgrehan	struct pci_xhci_dev_emu *dev;
2540300829Sgrehan	struct xhci_trb		evtrb;
2541300829Sgrehan	struct pci_xhci_softc	*sc;
2542300829Sgrehan	struct pci_xhci_portregs *p;
2543300829Sgrehan	int	error;
2544300829Sgrehan	int	dir_in;
2545300829Sgrehan	int	epid;
2546300829Sgrehan
2547300829Sgrehan	dir_in = epctx & 0x80;
2548300829Sgrehan	epid = epctx & ~0x80;
2549300829Sgrehan
2550300829Sgrehan	/* HW endpoint contexts are 0-15; convert to epid based on dir */
2551300829Sgrehan	epid = (epid * 2) + (dir_in ? 1 : 0);
2552300829Sgrehan
2553300829Sgrehan	assert(epid >= 1 && epid <= 31);
2554300829Sgrehan
2555300829Sgrehan	dev = hci->hci_sc;
2556300829Sgrehan	sc = dev->xsc;
2557300829Sgrehan
2558300829Sgrehan	/* check if device is ready; OS has to initialise it */
2559300829Sgrehan	if (sc->rtsregs.erstba_p == NULL ||
2560300829Sgrehan	    (sc->opregs.usbcmd & XHCI_CMD_RS) == 0)
2561300829Sgrehan		return (0);
2562300829Sgrehan
2563300829Sgrehan	p = XHCI_PORTREG_PTR(sc, hci->hci_port);
2564300829Sgrehan
2565300829Sgrehan	/* raise event if link U3 (suspended) state */
2566300829Sgrehan	if (XHCI_PS_PLS_GET(p->portsc) == 3) {
2567300829Sgrehan		p->portsc &= ~XHCI_PS_PLS_MASK;
2568300829Sgrehan		p->portsc |= XHCI_PS_PLS_SET(UPS_PORT_LS_RESUME);
2569300829Sgrehan		if ((p->portsc & XHCI_PS_PLC) != 0)
2570300829Sgrehan			return (0);
2571300829Sgrehan
2572300829Sgrehan		p->portsc |= XHCI_PS_PLC;
2573300829Sgrehan
2574300829Sgrehan		pci_xhci_set_evtrb(&evtrb, hci->hci_port,
2575300829Sgrehan		      XHCI_TRB_ERROR_SUCCESS, XHCI_TRB_EVENT_PORT_STS_CHANGE);
2576300829Sgrehan		error = pci_xhci_insert_event(sc, &evtrb, 0);
2577300829Sgrehan		if (error != XHCI_TRB_ERROR_SUCCESS)
2578300829Sgrehan			goto done;
2579300829Sgrehan	}
2580300829Sgrehan
2581300829Sgrehan	DPRINTF(("xhci device interrupt on endpoint %d\r\n", epid));
2582300829Sgrehan
2583300829Sgrehan	pci_xhci_device_doorbell(sc, hci->hci_port, epid, 0);
2584300829Sgrehan
2585300829Sgrehandone:
2586300829Sgrehan	return (error);
2587300829Sgrehan}
2588300829Sgrehan
2589300829Sgrehanstatic int
2590300829Sgrehanpci_xhci_dev_event(struct usb_hci *hci, enum hci_usbev evid, void *param)
2591300829Sgrehan{
2592300829Sgrehan
2593300829Sgrehan	DPRINTF(("xhci device event port %d\r\n", hci->hci_port));
2594300829Sgrehan	return (0);
2595300829Sgrehan}
2596300829Sgrehan
2597300829Sgrehan
2598300829Sgrehan
2599300829Sgrehanstatic void
2600300829Sgrehanpci_xhci_device_usage(char *opt)
2601300829Sgrehan{
2602300829Sgrehan
2603300829Sgrehan	fprintf(stderr, "Invalid USB emulation \"%s\"\r\n", opt);
2604300829Sgrehan}
2605300829Sgrehan
2606300829Sgrehanstatic int
2607300829Sgrehanpci_xhci_parse_opts(struct pci_xhci_softc *sc, char *opts)
2608300829Sgrehan{
2609300829Sgrehan	struct pci_xhci_dev_emu	**devices;
2610300829Sgrehan	struct pci_xhci_dev_emu	*dev;
2611300829Sgrehan	struct usb_devemu	*ue;
2612300829Sgrehan	void	*devsc;
2613300829Sgrehan	char	*uopt, *xopts, *config;
2614300829Sgrehan	int	usb3_port, usb2_port, i;
2615300829Sgrehan
2616300829Sgrehan	usb3_port = sc->usb3_port_start - 1;
2617300829Sgrehan	usb2_port = sc->usb2_port_start - 1;
2618300829Sgrehan	devices = NULL;
2619300829Sgrehan
2620300829Sgrehan	if (opts == NULL)
2621300829Sgrehan		goto portsfinal;
2622300829Sgrehan
2623300829Sgrehan	devices = calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_dev_emu *));
2624300829Sgrehan
2625300829Sgrehan	sc->slots = calloc(XHCI_MAX_SLOTS, sizeof(struct pci_xhci_dev_emu *));
2626300829Sgrehan	sc->devices = devices;
2627300829Sgrehan	sc->ndevices = 0;
2628300829Sgrehan
2629300829Sgrehan	uopt = strdup(opts);
2630300829Sgrehan	for (xopts = strtok(uopt, ",");
2631300829Sgrehan	     xopts != NULL;
2632300829Sgrehan	     xopts = strtok(NULL, ",")) {
2633300829Sgrehan		if (usb2_port == ((sc->usb2_port_start-1) + XHCI_MAX_DEVS/2) ||
2634300829Sgrehan		    usb3_port == ((sc->usb3_port_start-1) + XHCI_MAX_DEVS/2)) {
2635300829Sgrehan			WPRINTF(("pci_xhci max number of USB 2 or 3 "
2636300829Sgrehan			     "devices reached, max %d\r\n", XHCI_MAX_DEVS/2));
2637300829Sgrehan			usb2_port = usb3_port = -1;
2638300829Sgrehan			goto done;
2639300829Sgrehan		}
2640300829Sgrehan
2641300829Sgrehan		/* device[=<config>] */
2642300829Sgrehan		if ((config = strchr(xopts, '=')) == NULL)
2643300829Sgrehan			config = "";		/* no config */
2644300829Sgrehan		else
2645300829Sgrehan			*config++ = '\0';
2646300829Sgrehan
2647300829Sgrehan		ue = usb_emu_finddev(xopts);
2648300829Sgrehan		if (ue == NULL) {
2649300829Sgrehan			pci_xhci_device_usage(xopts);
2650300829Sgrehan			DPRINTF(("pci_xhci device not found %s\r\n", xopts));
2651300829Sgrehan			usb2_port = usb3_port = -1;
2652300829Sgrehan			goto done;
2653300829Sgrehan		}
2654300829Sgrehan
2655300829Sgrehan		DPRINTF(("pci_xhci adding device %s, opts \"%s\"\r\n",
2656300829Sgrehan		        xopts, config));
2657300829Sgrehan
2658300829Sgrehan		dev = calloc(1, sizeof(struct pci_xhci_dev_emu));
2659300829Sgrehan		dev->xsc = sc;
2660300829Sgrehan		dev->hci.hci_sc = dev;
2661300829Sgrehan		dev->hci.hci_intr = pci_xhci_dev_intr;
2662300829Sgrehan		dev->hci.hci_event = pci_xhci_dev_event;
2663300829Sgrehan
2664300829Sgrehan		if (ue->ue_usbver == 2) {
2665300829Sgrehan			dev->hci.hci_port = usb2_port + 1;
2666300829Sgrehan			devices[usb2_port] = dev;
2667300829Sgrehan			usb2_port++;
2668300829Sgrehan		} else {
2669300829Sgrehan			dev->hci.hci_port = usb3_port + 1;
2670300829Sgrehan			devices[usb3_port] = dev;
2671300829Sgrehan			usb3_port++;
2672300829Sgrehan		}
2673300829Sgrehan
2674300829Sgrehan		dev->hci.hci_address = 0;
2675300829Sgrehan		devsc = ue->ue_init(&dev->hci, config);
2676300829Sgrehan		if (devsc == NULL) {
2677300829Sgrehan			pci_xhci_device_usage(xopts);
2678300829Sgrehan			usb2_port = usb3_port = -1;
2679300829Sgrehan			goto done;
2680300829Sgrehan		}
2681300829Sgrehan
2682300829Sgrehan		dev->dev_ue = ue;
2683300829Sgrehan		dev->dev_sc = devsc;
2684300829Sgrehan
2685300829Sgrehan		/* assign slot number to device */
2686300829Sgrehan		sc->slots[sc->ndevices] = dev;
2687300829Sgrehan
2688300829Sgrehan		sc->ndevices++;
2689300829Sgrehan	}
2690300829Sgrehan
2691300829Sgrehanportsfinal:
2692300829Sgrehan	sc->portregs = calloc(XHCI_MAX_DEVS, sizeof(struct pci_xhci_portregs));
2693300829Sgrehan
2694300829Sgrehan	if (sc->ndevices > 0) {
2695300829Sgrehan		/* port and slot numbering start from 1 */
2696300829Sgrehan		sc->devices--;
2697300829Sgrehan		sc->portregs--;
2698300829Sgrehan		sc->slots--;
2699300829Sgrehan
2700300829Sgrehan		for (i = 1; i <= XHCI_MAX_DEVS; i++) {
2701300829Sgrehan			pci_xhci_init_port(sc, i);
2702300829Sgrehan		}
2703300829Sgrehan	} else {
2704300829Sgrehan		WPRINTF(("pci_xhci no USB devices configured\r\n"));
2705300829Sgrehan		sc->ndevices = 1;
2706300829Sgrehan	}
2707300829Sgrehan
2708300829Sgrehandone:
2709300829Sgrehan	if (devices != NULL) {
2710300829Sgrehan		if (usb2_port <= 0 && usb3_port <= 0) {
2711300829Sgrehan			sc->devices = NULL;
2712300829Sgrehan			for (i = 0; devices[i] != NULL; i++)
2713300829Sgrehan				free(devices[i]);
2714300829Sgrehan			sc->ndevices = -1;
2715300829Sgrehan
2716300829Sgrehan			free(devices);
2717300829Sgrehan		}
2718300829Sgrehan	}
2719300829Sgrehan	return (sc->ndevices);
2720300829Sgrehan}
2721300829Sgrehan
2722300829Sgrehanstatic int
2723300829Sgrehanpci_xhci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
2724300829Sgrehan{
2725300829Sgrehan	struct pci_xhci_softc *sc;
2726300829Sgrehan	int	error;
2727300829Sgrehan
2728300829Sgrehan	if (xhci_in_use) {
2729300829Sgrehan		WPRINTF(("pci_xhci controller already defined\r\n"));
2730300829Sgrehan		return (-1);
2731300829Sgrehan	}
2732300829Sgrehan	xhci_in_use = 1;
2733300829Sgrehan
2734300829Sgrehan	sc = calloc(1, sizeof(struct pci_xhci_softc));
2735300829Sgrehan	pi->pi_arg = sc;
2736300829Sgrehan	sc->xsc_pi = pi;
2737300829Sgrehan
2738300829Sgrehan	sc->usb2_port_start = (XHCI_MAX_DEVS/2) + 1;
2739300829Sgrehan	sc->usb3_port_start = 1;
2740300829Sgrehan
2741300829Sgrehan	/* discover devices */
2742300829Sgrehan	error = pci_xhci_parse_opts(sc, opts);
2743300829Sgrehan	if (error < 0)
2744300829Sgrehan		goto done;
2745300829Sgrehan	else
2746300829Sgrehan		error = 0;
2747300829Sgrehan
2748300829Sgrehan	sc->caplength = XHCI_SET_CAPLEN(XHCI_CAPLEN) |
2749300829Sgrehan	                XHCI_SET_HCIVERSION(0x0100);
2750300829Sgrehan	sc->hcsparams1 = XHCI_SET_HCSP1_MAXPORTS(XHCI_MAX_DEVS) |
2751300829Sgrehan	                 XHCI_SET_HCSP1_MAXINTR(1) |	/* interrupters */
2752300829Sgrehan	                 XHCI_SET_HCSP1_MAXSLOTS(XHCI_MAX_SLOTS);
2753300829Sgrehan	sc->hcsparams2 = XHCI_SET_HCSP2_ERSTMAX(XHCI_ERST_MAX) |
2754300829Sgrehan	                 XHCI_SET_HCSP2_IST(0x04);
2755300829Sgrehan	sc->hcsparams3 = 0;				/* no latency */
2756300829Sgrehan	sc->hccparams1 = XHCI_SET_HCCP1_NSS(1) |	/* no 2nd-streams */
2757300829Sgrehan	                 XHCI_SET_HCCP1_SPC(1) |	/* short packet */
2758300829Sgrehan	                 XHCI_SET_HCCP1_MAXPSA(XHCI_STREAMS_MAX);
2759300829Sgrehan	sc->hccparams2 = XHCI_SET_HCCP2_LEC(1) |
2760300829Sgrehan	                 XHCI_SET_HCCP2_U3C(1);
2761300829Sgrehan	sc->dboff = XHCI_SET_DOORBELL(XHCI_CAPLEN + XHCI_PORTREGS_START +
2762300829Sgrehan	            XHCI_MAX_DEVS * sizeof(struct pci_xhci_portregs));
2763300829Sgrehan
2764300829Sgrehan	/* dboff must be 32-bit aligned */
2765300829Sgrehan	if (sc->dboff & 0x3)
2766300829Sgrehan		sc->dboff = (sc->dboff + 0x3) & ~0x3;
2767300829Sgrehan
2768300829Sgrehan	/* rtsoff must be 32-bytes aligned */
2769300829Sgrehan	sc->rtsoff = XHCI_SET_RTSOFFSET(sc->dboff + (XHCI_MAX_SLOTS+1) * 32);
2770300829Sgrehan	if (sc->rtsoff & 0x1F)
2771300829Sgrehan		sc->rtsoff = (sc->rtsoff + 0x1F) & ~0x1F;
2772300829Sgrehan
2773300829Sgrehan	DPRINTF(("pci_xhci dboff: 0x%x, rtsoff: 0x%x\r\n", sc->dboff,
2774300829Sgrehan	        sc->rtsoff));
2775300829Sgrehan
2776300829Sgrehan	sc->opregs.usbsts = XHCI_STS_HCH;
2777300829Sgrehan	sc->opregs.pgsz = XHCI_PAGESIZE_4K;
2778300829Sgrehan
2779300829Sgrehan	pci_xhci_reset(sc);
2780300829Sgrehan
2781300829Sgrehan	sc->regsend = sc->rtsoff + 0x20 + 32;		/* only 1 intrpter */
2782300829Sgrehan
2783300829Sgrehan	/*
2784300829Sgrehan	 * Set extended capabilities pointer to be after regsend;
2785300829Sgrehan	 * value of xecp field is 32-bit offset.
2786300829Sgrehan	 */
2787300829Sgrehan	sc->hccparams1 |= XHCI_SET_HCCP1_XECP(sc->regsend/4);
2788300829Sgrehan
2789300829Sgrehan	pci_set_cfgdata16(pi, PCIR_DEVICE, 0x1E31);
2790300829Sgrehan	pci_set_cfgdata16(pi, PCIR_VENDOR, 0x8086);
2791300829Sgrehan	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_SERIALBUS);
2792300829Sgrehan	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_SERIALBUS_USB);
2793300829Sgrehan	pci_set_cfgdata8(pi, PCIR_PROGIF,PCIP_SERIALBUS_USB_XHCI);
2794300829Sgrehan	pci_set_cfgdata8(pi, PCI_USBREV, PCI_USB_REV_3_0);
2795300829Sgrehan
2796300829Sgrehan	pci_emul_add_msicap(pi, 1);
2797300829Sgrehan
2798300829Sgrehan	/* regsend + xecp registers */
2799300829Sgrehan	pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, sc->regsend + 4*32);
2800300829Sgrehan	DPRINTF(("pci_xhci pci_emu_alloc: %d\r\n", sc->regsend + 4*32));
2801300829Sgrehan
2802300829Sgrehan
2803300829Sgrehan	pci_lintr_request(pi);
2804300829Sgrehan
2805300829Sgrehan	pthread_mutex_init(&sc->mtx, NULL);
2806300829Sgrehan
2807300829Sgrehandone:
2808300829Sgrehan	if (error) {
2809300829Sgrehan		free(sc);
2810300829Sgrehan	}
2811300829Sgrehan
2812300829Sgrehan	return (error);
2813300829Sgrehan}
2814300829Sgrehan
2815300829Sgrehan
2816300829Sgrehan
2817300829Sgrehanstruct pci_devemu pci_de_xhci = {
2818300829Sgrehan	.pe_emu =	"xhci",
2819300829Sgrehan	.pe_init =	pci_xhci_init,
2820300829Sgrehan	.pe_barwrite =	pci_xhci_write,
2821300829Sgrehan	.pe_barread =	pci_xhci_read
2822300829Sgrehan};
2823300829SgrehanPCI_EMUL_SET(pci_de_xhci);
2824