1f9f848faSopenharmony_ci/*-
2f9f848faSopenharmony_ci * SPDX-License-Identifier: BSD-2-Clause
3f9f848faSopenharmony_ci *
4f9f848faSopenharmony_ci * Copyright (c) 2010-2022 Hans Petter Selasky
5f9f848faSopenharmony_ci *
6f9f848faSopenharmony_ci * Redistribution and use in source and binary forms, with or without
7f9f848faSopenharmony_ci * modification, are permitted provided that the following conditions
8f9f848faSopenharmony_ci * are met:
9f9f848faSopenharmony_ci * 1. Redistributions of source code must retain the above copyright
10f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer.
11f9f848faSopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
12f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
13f9f848faSopenharmony_ci *    documentation and/or other materials provided with the distribution.
14f9f848faSopenharmony_ci *
15f9f848faSopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16f9f848faSopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17f9f848faSopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18f9f848faSopenharmony_ci * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19f9f848faSopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20f9f848faSopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21f9f848faSopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22f9f848faSopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23f9f848faSopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24f9f848faSopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25f9f848faSopenharmony_ci * SUCH DAMAGE.
26f9f848faSopenharmony_ci */
27f9f848faSopenharmony_ci
28f9f848faSopenharmony_ci/*
29f9f848faSopenharmony_ci * USB eXtensible Host Controller Interface, a.k.a. USB 3.0 controller.
30f9f848faSopenharmony_ci *
31f9f848faSopenharmony_ci * The XHCI 1.0 spec can be found at
32f9f848faSopenharmony_ci * http://www.intel.com/technology/usb/download/xHCI_Specification_for_USB.pdf
33f9f848faSopenharmony_ci * and the USB 3.0 spec at
34f9f848faSopenharmony_ci * http://www.usb.org/developers/docs/usb_30_spec_060910.zip
35f9f848faSopenharmony_ci */
36f9f848faSopenharmony_ci
37f9f848faSopenharmony_ci/*
38f9f848faSopenharmony_ci * A few words about the design implementation: This driver emulates
39f9f848faSopenharmony_ci * the concept about TDs which is found in EHCI specification. This
40f9f848faSopenharmony_ci * way we achieve that the USB controller drivers look similar to
41f9f848faSopenharmony_ci * eachother which makes it easier to understand the code.
42f9f848faSopenharmony_ci */
43f9f848faSopenharmony_ci
44f9f848faSopenharmony_ci#include "implementation/global_implementation.h"
45f9f848faSopenharmony_ci#include "controller/xhci.h"
46f9f848faSopenharmony_ci#include "controller/xhcireg.h"
47f9f848faSopenharmony_ci
48f9f848faSopenharmony_ci#define XHCI_BUS2SC(bus) \
49f9f848faSopenharmony_ci   ((struct xhci_softc *)(((uint8_t *)(bus)) - \
50f9f848faSopenharmony_ci    ((uint8_t *)&(((struct xhci_softc *)0)->sc_bus))))
51f9f848faSopenharmony_ci
52f9f848faSopenharmony_cistatic int xhcistreams;
53f9f848faSopenharmony_ci
54f9f848faSopenharmony_ci#ifdef USB_DEBUG
55f9f848faSopenharmony_cistatic int xhcidebug = 20;
56f9f848faSopenharmony_cistatic int xhciroute = 0;
57f9f848faSopenharmony_cistatic int xhcipolling = 0;
58f9f848faSopenharmony_cistatic int xhcidma32 = 0;
59f9f848faSopenharmony_cistatic int xhcictlstep = 0;
60f9f848faSopenharmony_ci#else
61f9f848faSopenharmony_ci#define xhciroute 0
62f9f848faSopenharmony_ci#define xhcidma32 0
63f9f848faSopenharmony_ci#define xhcictlstep 0
64f9f848faSopenharmony_ci#endif
65f9f848faSopenharmony_ci
66f9f848faSopenharmony_ci#undef USB_DEBUG_VAR
67f9f848faSopenharmony_ci#define USB_DEBUG_VAR xhcidebug
68f9f848faSopenharmony_ci
69f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
70f9f848faSopenharmony_cistatic int xhcidebug = 0;
71f9f848faSopenharmony_ci#endif
72f9f848faSopenharmony_ci
73f9f848faSopenharmony_ci
74f9f848faSopenharmony_ci#define XHCI_INTR_ENDPT 1
75f9f848faSopenharmony_ci
76f9f848faSopenharmony_ci#define XHCI_DO_CMD_TIMEOUT  1000
77f9f848faSopenharmony_ci
78f9f848faSopenharmony_cistruct xhci_std_temp {
79f9f848faSopenharmony_ci	struct xhci_softc	*sc;
80f9f848faSopenharmony_ci	struct usb_page_cache	*pc;
81f9f848faSopenharmony_ci	struct xhci_td		*td;
82f9f848faSopenharmony_ci	struct xhci_td		*td_next;
83f9f848faSopenharmony_ci	uint32_t		len;
84f9f848faSopenharmony_ci	uint32_t		offset;
85f9f848faSopenharmony_ci	uint32_t		max_packet_size;
86f9f848faSopenharmony_ci	uint32_t		average;
87f9f848faSopenharmony_ci	uint16_t		isoc_delta;
88f9f848faSopenharmony_ci	uint16_t		isoc_frame;
89f9f848faSopenharmony_ci	uint8_t			shortpkt;
90f9f848faSopenharmony_ci	uint8_t			multishort;
91f9f848faSopenharmony_ci	uint8_t			last_frame;
92f9f848faSopenharmony_ci	uint8_t			trb_type;
93f9f848faSopenharmony_ci	uint8_t			direction;
94f9f848faSopenharmony_ci	uint8_t			tbc;
95f9f848faSopenharmony_ci	uint8_t			tlbpc;
96f9f848faSopenharmony_ci	uint8_t			step_td;
97f9f848faSopenharmony_ci	uint8_t			do_isoc_sync;
98f9f848faSopenharmony_ci};
99f9f848faSopenharmony_ci
100f9f848faSopenharmony_cistatic void xhci_do_poll(struct usb_bus *);
101f9f848faSopenharmony_cistatic void xhci_device_done(struct usb_xfer *, usb_error_t);
102f9f848faSopenharmony_cistatic void xhci_root_intr(struct xhci_softc *);
103f9f848faSopenharmony_cistatic void xhci_free_device_ext(struct usb_device *);
104f9f848faSopenharmony_cistatic struct xhci_endpoint_ext *xhci_get_endpoint_ext(struct usb_device *,
105f9f848faSopenharmony_ci		    struct usb_endpoint_descriptor *);
106f9f848faSopenharmony_cistatic usb_proc_callback_t xhci_configure_msg;
107f9f848faSopenharmony_cistatic usb_error_t xhci_configure_device(struct usb_device *);
108f9f848faSopenharmony_cistatic usb_error_t xhci_configure_endpoint(struct usb_device *,
109f9f848faSopenharmony_ci		    struct usb_endpoint_descriptor *, struct xhci_endpoint_ext *,
110f9f848faSopenharmony_ci		    uint16_t, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t,
111f9f848faSopenharmony_ci		    uint8_t);
112f9f848faSopenharmony_cistatic usb_error_t xhci_configure_mask(struct usb_device *,
113f9f848faSopenharmony_ci		    uint32_t, uint8_t);
114f9f848faSopenharmony_cistatic usb_error_t xhci_cmd_evaluate_ctx(struct xhci_softc *,
115f9f848faSopenharmony_ci		    uint64_t, uint8_t);
116f9f848faSopenharmony_cistatic void xhci_endpoint_doorbell(struct usb_xfer *);
117f9f848faSopenharmony_cistatic void xhci_ctx_set_le32(struct xhci_softc *sc, volatile uint32_t *ptr, uint32_t val);
118f9f848faSopenharmony_cistatic uint32_t xhci_ctx_get_le32(struct xhci_softc *sc, volatile uint32_t *ptr);
119f9f848faSopenharmony_cistatic void xhci_ctx_set_le64(struct xhci_softc *sc, volatile uint64_t *ptr, uint64_t val);
120f9f848faSopenharmony_ci#ifdef USB_DEBUG
121f9f848faSopenharmony_cistatic uint64_t xhci_ctx_get_le64(struct xhci_softc *sc, volatile uint64_t *ptr);
122f9f848faSopenharmony_ci#endif
123f9f848faSopenharmony_ci
124f9f848faSopenharmony_ciextern struct usb_bus_methods xhci_bus_methods;
125f9f848faSopenharmony_ci
126f9f848faSopenharmony_ci#ifdef USB_DEBUG
127f9f848faSopenharmony_cistatic void
128f9f848faSopenharmony_cixhci_dump_trb(struct xhci_trb *trb)
129f9f848faSopenharmony_ci{
130f9f848faSopenharmony_ci	DPRINTFN(5, "trb = %p\n", trb);
131f9f848faSopenharmony_ci	DPRINTFN(5, "qwTrb0 = 0x%016llx\n", (long long)le64toh(trb->qwTrb0));
132f9f848faSopenharmony_ci	DPRINTFN(5, "dwTrb2 = 0x%08x\n", le32toh(trb->dwTrb2));
133f9f848faSopenharmony_ci	DPRINTFN(5, "dwTrb3 = 0x%08x\n", le32toh(trb->dwTrb3));
134f9f848faSopenharmony_ci}
135f9f848faSopenharmony_ci
136f9f848faSopenharmony_cistatic void
137f9f848faSopenharmony_cixhci_dump_endpoint(struct xhci_softc *sc, struct xhci_endp_ctx *pep)
138f9f848faSopenharmony_ci{
139f9f848faSopenharmony_ci	DPRINTFN(5, "pep = %p\n", pep);
140f9f848faSopenharmony_ci	DPRINTFN(5, "dwEpCtx0=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx0));
141f9f848faSopenharmony_ci	DPRINTFN(5, "dwEpCtx1=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx1));
142f9f848faSopenharmony_ci	DPRINTFN(5, "qwEpCtx2=0x%016llx\n", (long long)xhci_ctx_get_le64(sc, &pep->qwEpCtx2));
143f9f848faSopenharmony_ci	DPRINTFN(5, "dwEpCtx4=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx4));
144f9f848faSopenharmony_ci	DPRINTFN(5, "dwEpCtx5=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx5));
145f9f848faSopenharmony_ci	DPRINTFN(5, "dwEpCtx6=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx6));
146f9f848faSopenharmony_ci	DPRINTFN(5, "dwEpCtx7=0x%08x\n", xhci_ctx_get_le32(sc, &pep->dwEpCtx7));
147f9f848faSopenharmony_ci}
148f9f848faSopenharmony_ci
149f9f848faSopenharmony_cistatic void
150f9f848faSopenharmony_cixhci_dump_device(struct xhci_softc *sc, struct xhci_slot_ctx *psl)
151f9f848faSopenharmony_ci{
152f9f848faSopenharmony_ci	DPRINTFN(5, "psl = %p\n", psl);
153f9f848faSopenharmony_ci	DPRINTFN(5, "dwSctx0=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx0));
154f9f848faSopenharmony_ci	DPRINTFN(5, "dwSctx1=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx1));
155f9f848faSopenharmony_ci	DPRINTFN(5, "dwSctx2=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx2));
156f9f848faSopenharmony_ci	DPRINTFN(5, "dwSctx3=0x%08x\n", xhci_ctx_get_le32(sc, &psl->dwSctx3));
157f9f848faSopenharmony_ci}
158f9f848faSopenharmony_ci#endif
159f9f848faSopenharmony_ci
160f9f848faSopenharmony_ciuint8_t
161f9f848faSopenharmony_cixhci_use_polling(void)
162f9f848faSopenharmony_ci{
163f9f848faSopenharmony_ci#ifdef USB_DEBUG
164f9f848faSopenharmony_ci	return (xhcipolling != 0);
165f9f848faSopenharmony_ci#else
166f9f848faSopenharmony_ci	return (0);
167f9f848faSopenharmony_ci#endif
168f9f848faSopenharmony_ci}
169f9f848faSopenharmony_ci
170f9f848faSopenharmony_cistatic void
171f9f848faSopenharmony_cixhci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb)
172f9f848faSopenharmony_ci{
173f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(bus);
174f9f848faSopenharmony_ci	uint8_t i;
175f9f848faSopenharmony_ci
176f9f848faSopenharmony_ci	cb(bus, &sc->sc_hw.root_pc, &sc->sc_hw.root_pg,
177f9f848faSopenharmony_ci	    sizeof(struct xhci_hw_root), XHCI_PAGE_SIZE);
178f9f848faSopenharmony_ci
179f9f848faSopenharmony_ci	cb(bus, &sc->sc_hw.ctx_pc, &sc->sc_hw.ctx_pg,
180f9f848faSopenharmony_ci	    sizeof(struct xhci_dev_ctx_addr), XHCI_PAGE_SIZE);
181f9f848faSopenharmony_ci
182f9f848faSopenharmony_ci	for (i = 0; i != sc->sc_noscratch; i++) {
183f9f848faSopenharmony_ci		cb(bus, &sc->sc_hw.scratch_pc[i], &sc->sc_hw.scratch_pg[i],
184f9f848faSopenharmony_ci		    XHCI_PAGE_SIZE, XHCI_PAGE_SIZE);
185f9f848faSopenharmony_ci	}
186f9f848faSopenharmony_ci}
187f9f848faSopenharmony_ci
188f9f848faSopenharmony_cistatic void
189f9f848faSopenharmony_cixhci_ctx_set_le32(struct xhci_softc *sc, volatile uint32_t *ptr, uint32_t val)
190f9f848faSopenharmony_ci{
191f9f848faSopenharmony_ci	uint32_t offset;
192f9f848faSopenharmony_ci	if (sc->sc_ctx_is_64_byte) {
193f9f848faSopenharmony_ci		/* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */
194f9f848faSopenharmony_ci		/* all contexts are initially 32-bytes */
195f9f848faSopenharmony_ci		offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U));
196f9f848faSopenharmony_ci		ptr = (volatile uint32_t *)(((volatile uint8_t *)ptr) + offset);
197f9f848faSopenharmony_ci	}
198f9f848faSopenharmony_ci	*ptr = htole32(val);
199f9f848faSopenharmony_ci}
200f9f848faSopenharmony_ci
201f9f848faSopenharmony_cistatic uint32_t
202f9f848faSopenharmony_cixhci_ctx_get_le32(struct xhci_softc *sc, volatile uint32_t *ptr)
203f9f848faSopenharmony_ci{
204f9f848faSopenharmony_ci	uint32_t offset;
205f9f848faSopenharmony_ci	if (sc->sc_ctx_is_64_byte) {
206f9f848faSopenharmony_ci		/* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */
207f9f848faSopenharmony_ci		/* all contexts are initially 32-bytes */
208f9f848faSopenharmony_ci		offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U));
209f9f848faSopenharmony_ci		ptr = (volatile uint32_t *)(((volatile uint8_t *)ptr) + offset);
210f9f848faSopenharmony_ci	}
211f9f848faSopenharmony_ci	return (le32toh(*ptr));
212f9f848faSopenharmony_ci}
213f9f848faSopenharmony_ci
214f9f848faSopenharmony_cistatic void
215f9f848faSopenharmony_cixhci_ctx_set_le64(struct xhci_softc *sc, volatile uint64_t *ptr, uint64_t val)
216f9f848faSopenharmony_ci{
217f9f848faSopenharmony_ci	uint32_t offset;
218f9f848faSopenharmony_ci	if (sc->sc_ctx_is_64_byte) {
219f9f848faSopenharmony_ci		/* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */
220f9f848faSopenharmony_ci		/* all contexts are initially 32-bytes */
221f9f848faSopenharmony_ci		offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U));
222f9f848faSopenharmony_ci		ptr = (volatile uint64_t *)(((volatile uint8_t *)ptr) + offset);
223f9f848faSopenharmony_ci	}
224f9f848faSopenharmony_ci	*ptr = htole64(val);
225f9f848faSopenharmony_ci}
226f9f848faSopenharmony_ci
227f9f848faSopenharmony_ci#ifdef USB_DEBUG
228f9f848faSopenharmony_cistatic uint64_t
229f9f848faSopenharmony_cixhci_ctx_get_le64(struct xhci_softc *sc, volatile uint64_t *ptr)
230f9f848faSopenharmony_ci{
231f9f848faSopenharmony_ci	uint32_t offset;
232f9f848faSopenharmony_ci	if (sc->sc_ctx_is_64_byte) {
233f9f848faSopenharmony_ci		/* exploit the fact that our structures are XHCI_PAGE_SIZE aligned */
234f9f848faSopenharmony_ci		/* all contexts are initially 32-bytes */
235f9f848faSopenharmony_ci		offset = ((uintptr_t)ptr) & ((XHCI_PAGE_SIZE - 1) & ~(31U));
236f9f848faSopenharmony_ci		ptr = (volatile uint64_t *)(((volatile uint8_t *)ptr) + offset);
237f9f848faSopenharmony_ci	}
238f9f848faSopenharmony_ci	return (le64toh(*ptr));
239f9f848faSopenharmony_ci}
240f9f848faSopenharmony_ci#endif
241f9f848faSopenharmony_ci
242f9f848faSopenharmony_cistatic int
243f9f848faSopenharmony_cixhci_reset_command_queue_locked(struct xhci_softc *sc)
244f9f848faSopenharmony_ci{
245f9f848faSopenharmony_ci	struct usb_page_search buf_res;
246f9f848faSopenharmony_ci	struct xhci_hw_root *phwr;
247f9f848faSopenharmony_ci	uint64_t addr;
248f9f848faSopenharmony_ci	uint32_t temp;
249f9f848faSopenharmony_ci
250f9f848faSopenharmony_ci	DPRINTF("\n");
251f9f848faSopenharmony_ci
252f9f848faSopenharmony_ci	temp = XREAD4(sc, oper, XHCI_CRCR_LO);
253f9f848faSopenharmony_ci	if (temp & XHCI_CRCR_LO_CRR) {
254f9f848faSopenharmony_ci		DPRINTF("Command ring running\n");
255f9f848faSopenharmony_ci		temp &= ~(XHCI_CRCR_LO_CS | XHCI_CRCR_LO_CA);
256f9f848faSopenharmony_ci
257f9f848faSopenharmony_ci		/*
258f9f848faSopenharmony_ci		 * Try to abort the last command as per section
259f9f848faSopenharmony_ci		 * 4.6.1.2 "Aborting a Command" of the XHCI
260f9f848faSopenharmony_ci		 * specification:
261f9f848faSopenharmony_ci		 */
262f9f848faSopenharmony_ci
263f9f848faSopenharmony_ci		/* stop and cancel */
264f9f848faSopenharmony_ci		XWRITE4(sc, oper, XHCI_CRCR_LO, temp | XHCI_CRCR_LO_CS);
265f9f848faSopenharmony_ci		XWRITE4(sc, oper, XHCI_CRCR_HI, 0);
266f9f848faSopenharmony_ci
267f9f848faSopenharmony_ci		XWRITE4(sc, oper, XHCI_CRCR_LO, temp | XHCI_CRCR_LO_CA);
268f9f848faSopenharmony_ci		XWRITE4(sc, oper, XHCI_CRCR_HI, 0);
269f9f848faSopenharmony_ci
270f9f848faSopenharmony_ci		/* wait 250ms */
271f9f848faSopenharmony_ci		usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 4);
272f9f848faSopenharmony_ci
273f9f848faSopenharmony_ci		/* check if command ring is still running */
274f9f848faSopenharmony_ci		temp = XREAD4(sc, oper, XHCI_CRCR_LO);
275f9f848faSopenharmony_ci		if (temp & XHCI_CRCR_LO_CRR) {
276f9f848faSopenharmony_ci			DPRINTF("Comand ring still running\n");
277f9f848faSopenharmony_ci			return (USB_ERR_IOERROR);
278f9f848faSopenharmony_ci		}
279f9f848faSopenharmony_ci	}
280f9f848faSopenharmony_ci
281f9f848faSopenharmony_ci	/* reset command ring */
282f9f848faSopenharmony_ci	sc->sc_command_ccs = 1;
283f9f848faSopenharmony_ci	sc->sc_command_idx = 0;
284f9f848faSopenharmony_ci
285f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
286f9f848faSopenharmony_ci
287f9f848faSopenharmony_ci	/* set up command ring control base address */
288f9f848faSopenharmony_ci	addr = buf_res.physaddr;
289f9f848faSopenharmony_ci	phwr = buf_res.buffer;
290f9f848faSopenharmony_ci	addr += __offsetof(struct xhci_hw_root, hwr_commands[0]);
291f9f848faSopenharmony_ci
292f9f848faSopenharmony_ci	DPRINTF("CRCR=0x%016llx\n", (unsigned long long)addr);
293f9f848faSopenharmony_ci
294f9f848faSopenharmony_ci	(void)memset_s(phwr->hwr_commands, sizeof(phwr->hwr_commands), 0, sizeof(phwr->hwr_commands));
295f9f848faSopenharmony_ci	phwr->hwr_commands[XHCI_MAX_COMMANDS - 1].qwTrb0 = htole64(addr);
296f9f848faSopenharmony_ci
297f9f848faSopenharmony_ci	usb_pc_cpu_flush(&sc->sc_hw.root_pc);
298f9f848faSopenharmony_ci
299f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_CRCR_LO, ((uint32_t)addr) | XHCI_CRCR_LO_RCS);
300f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_CRCR_HI, (uint32_t)(addr >> 32));
301f9f848faSopenharmony_ci
302f9f848faSopenharmony_ci	return (0);
303f9f848faSopenharmony_ci}
304f9f848faSopenharmony_ci
305f9f848faSopenharmony_ciusb_error_t
306f9f848faSopenharmony_cixhci_start_controller(struct xhci_softc *sc)
307f9f848faSopenharmony_ci{
308f9f848faSopenharmony_ci	struct usb_page_search buf_res;
309f9f848faSopenharmony_ci	struct xhci_hw_root *phwr;
310f9f848faSopenharmony_ci	struct xhci_dev_ctx_addr *pdctxa;
311f9f848faSopenharmony_ci	uint64_t addr;
312f9f848faSopenharmony_ci	uint32_t temp;
313f9f848faSopenharmony_ci	uint16_t i;
314f9f848faSopenharmony_ci	int ret;
315f9f848faSopenharmony_ci
316f9f848faSopenharmony_ci	DPRINTF("\n");
317f9f848faSopenharmony_ci
318f9f848faSopenharmony_ci	sc->sc_event_ccs = 1;
319f9f848faSopenharmony_ci	sc->sc_event_idx = 0;
320f9f848faSopenharmony_ci	sc->sc_command_ccs = 1;
321f9f848faSopenharmony_ci	sc->sc_command_idx = 0;
322f9f848faSopenharmony_ci
323f9f848faSopenharmony_ci	/* Reset controller */
324f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_USBCMD, XHCI_CMD_HCRST);
325f9f848faSopenharmony_ci
326f9f848faSopenharmony_ci	for (i = 0; i != 100; i++) {
327f9f848faSopenharmony_ci		usb_pause_mtx(NULL, hz / 100);
328f9f848faSopenharmony_ci		temp = (XREAD4(sc, oper, XHCI_USBCMD) & XHCI_CMD_HCRST) |
329f9f848faSopenharmony_ci		    (XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_CNR);
330f9f848faSopenharmony_ci		if (!temp)
331f9f848faSopenharmony_ci			break;
332f9f848faSopenharmony_ci	}
333f9f848faSopenharmony_ci
334f9f848faSopenharmony_ci	if (temp) {
335f9f848faSopenharmony_ci		device_printf(sc->sc_bus.parent, "Controller "
336f9f848faSopenharmony_ci		    "reset timeout.\n");
337f9f848faSopenharmony_ci		return (USB_ERR_IOERROR);
338f9f848faSopenharmony_ci	}
339f9f848faSopenharmony_ci
340f9f848faSopenharmony_ci	/* set up number of device slots */
341f9f848faSopenharmony_ci	DPRINTF("CONFIG=0x%08x -> 0x%08x\n",
342f9f848faSopenharmony_ci	    XREAD4(sc, oper, XHCI_CONFIG), sc->sc_noslot);
343f9f848faSopenharmony_ci
344f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_CONFIG, sc->sc_noslot);
345f9f848faSopenharmony_ci
346f9f848faSopenharmony_ci	temp = XREAD4(sc, oper, XHCI_USBSTS);
347f9f848faSopenharmony_ci
348f9f848faSopenharmony_ci	/* clear interrupts */
349f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_USBSTS, temp);
350f9f848faSopenharmony_ci	/* disable all device notifications */
351f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_DNCTRL, 0);
352f9f848faSopenharmony_ci
353f9f848faSopenharmony_ci	/* set up device context base address */
354f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.ctx_pc, 0, &buf_res);
355f9f848faSopenharmony_ci	pdctxa = buf_res.buffer;
356f9f848faSopenharmony_ci	ret = memset_s(pdctxa, USB_PAGE_SIZE, 0, sizeof(*pdctxa));
357f9f848faSopenharmony_ci	if (ret != EOK) {
358f9f848faSopenharmony_ci		usb_err("memset_s failed, ret:%d\n", ret);
359f9f848faSopenharmony_ci		return (USB_ERR_BAD_BUFSIZE);
360f9f848faSopenharmony_ci	}
361f9f848faSopenharmony_ci
362f9f848faSopenharmony_ci	addr = buf_res.physaddr;
363f9f848faSopenharmony_ci	addr += __offsetof(struct xhci_dev_ctx_addr, qwSpBufPtr[0]);
364f9f848faSopenharmony_ci
365f9f848faSopenharmony_ci	/* slot 0 points to the table of scratchpad pointers */
366f9f848faSopenharmony_ci	pdctxa->qwBaaDevCtxAddr[0] = htole64(addr);
367f9f848faSopenharmony_ci
368f9f848faSopenharmony_ci	for (i = 0; i != sc->sc_noscratch; i++) {
369f9f848faSopenharmony_ci		struct usb_page_search buf_scp;
370f9f848faSopenharmony_ci		usbd_get_page(&sc->sc_hw.scratch_pc[i], 0, &buf_scp);
371f9f848faSopenharmony_ci		pdctxa->qwSpBufPtr[i] = htole64((uint64_t)buf_scp.physaddr);
372f9f848faSopenharmony_ci	}
373f9f848faSopenharmony_ci
374f9f848faSopenharmony_ci	addr = buf_res.physaddr;
375f9f848faSopenharmony_ci
376f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_DCBAAP_LO, (uint32_t)addr);
377f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_DCBAAP_HI, (uint32_t)(addr >> 32));
378f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_DCBAAP_LO, (uint32_t)addr);
379f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_DCBAAP_HI, (uint32_t)(addr >> 32));
380f9f848faSopenharmony_ci
381f9f848faSopenharmony_ci	/* set up event table size */
382f9f848faSopenharmony_ci	DPRINTF("ERSTSZ=0x%08x -> 0x%08x\n",
383f9f848faSopenharmony_ci	    XREAD4(sc, runt, XHCI_ERSTSZ(0)), sc->sc_erst_max);
384f9f848faSopenharmony_ci
385f9f848faSopenharmony_ci	XWRITE4(sc, runt, XHCI_ERSTSZ(0), XHCI_ERSTS_SET(sc->sc_erst_max));
386f9f848faSopenharmony_ci
387f9f848faSopenharmony_ci	/* set up interrupt rate */
388f9f848faSopenharmony_ci	XWRITE4(sc, runt, XHCI_IMOD(0), sc->sc_imod_default);
389f9f848faSopenharmony_ci
390f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
391f9f848faSopenharmony_ci
392f9f848faSopenharmony_ci	phwr = buf_res.buffer;
393f9f848faSopenharmony_ci	addr = buf_res.physaddr;
394f9f848faSopenharmony_ci	addr += __offsetof(struct xhci_hw_root, hwr_events[0]);
395f9f848faSopenharmony_ci
396f9f848faSopenharmony_ci	/* reset hardware root structure */
397f9f848faSopenharmony_ci	ret = memset_s(phwr, USB_PAGE_SIZE, 0, sizeof(*phwr));
398f9f848faSopenharmony_ci	if (ret != EOK) {
399f9f848faSopenharmony_ci		usb_err("memset_s failed, ret:%d\n", ret);
400f9f848faSopenharmony_ci		return (USB_ERR_BAD_BUFSIZE);
401f9f848faSopenharmony_ci	}
402f9f848faSopenharmony_ci
403f9f848faSopenharmony_ci	phwr->hwr_ring_seg[0].qwEvrsTablePtr = htole64(addr);
404f9f848faSopenharmony_ci	phwr->hwr_ring_seg[0].dwEvrsTableSize = htole32(XHCI_MAX_EVENTS);
405f9f848faSopenharmony_ci
406f9f848faSopenharmony_ci	DPRINTF("ERDP(0)=0x%016llx\n", (unsigned long long)addr);
407f9f848faSopenharmony_ci
408f9f848faSopenharmony_ci	XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr);
409f9f848faSopenharmony_ci	XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32));
410f9f848faSopenharmony_ci
411f9f848faSopenharmony_ci	addr = buf_res.physaddr;
412f9f848faSopenharmony_ci
413f9f848faSopenharmony_ci	DPRINTF("ERSTBA(0)=0x%016llx\n", (unsigned long long)addr);
414f9f848faSopenharmony_ci
415f9f848faSopenharmony_ci	XWRITE4(sc, runt, XHCI_ERSTBA_LO(0), (uint32_t)addr);
416f9f848faSopenharmony_ci	XWRITE4(sc, runt, XHCI_ERSTBA_HI(0), (uint32_t)(addr >> 32));
417f9f848faSopenharmony_ci
418f9f848faSopenharmony_ci	/* set up interrupter registers */
419f9f848faSopenharmony_ci	temp = XREAD4(sc, runt, XHCI_IMAN(0));
420f9f848faSopenharmony_ci	temp |= XHCI_IMAN_INTR_ENA;
421f9f848faSopenharmony_ci	XWRITE4(sc, runt, XHCI_IMAN(0), temp);
422f9f848faSopenharmony_ci
423f9f848faSopenharmony_ci	/* set up command ring control base address */
424f9f848faSopenharmony_ci	addr = buf_res.physaddr;
425f9f848faSopenharmony_ci	addr += __offsetof(struct xhci_hw_root, hwr_commands[0]);
426f9f848faSopenharmony_ci
427f9f848faSopenharmony_ci	DPRINTF("CRCR=0x%016llx\n", (unsigned long long)addr);
428f9f848faSopenharmony_ci
429f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_CRCR_LO, ((uint32_t)addr) | XHCI_CRCR_LO_RCS);
430f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_CRCR_HI, (uint32_t)(addr >> 32));
431f9f848faSopenharmony_ci
432f9f848faSopenharmony_ci	phwr->hwr_commands[XHCI_MAX_COMMANDS - 1].qwTrb0 = htole64(addr);
433f9f848faSopenharmony_ci
434f9f848faSopenharmony_ci	usb_bus_mem_flush_all(&sc->sc_bus, &xhci_iterate_hw_softc);
435f9f848faSopenharmony_ci
436f9f848faSopenharmony_ci	/* Go! */
437f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_USBCMD, XHCI_CMD_RS |
438f9f848faSopenharmony_ci	    XHCI_CMD_INTE | XHCI_CMD_HSEE);
439f9f848faSopenharmony_ci
440f9f848faSopenharmony_ci	for (i = 0; i != 100; i++) {
441f9f848faSopenharmony_ci		usb_pause_mtx(NULL, hz / 100);
442f9f848faSopenharmony_ci		temp = XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_HCH;
443f9f848faSopenharmony_ci		if (!temp)
444f9f848faSopenharmony_ci			break;
445f9f848faSopenharmony_ci	}
446f9f848faSopenharmony_ci	if (temp) {
447f9f848faSopenharmony_ci		XWRITE4(sc, oper, XHCI_USBCMD, 0);
448f9f848faSopenharmony_ci		device_printf(sc->sc_bus.parent, "Run timeout.\n");
449f9f848faSopenharmony_ci		return (USB_ERR_IOERROR);
450f9f848faSopenharmony_ci	}
451f9f848faSopenharmony_ci
452f9f848faSopenharmony_ci	/* catch any lost interrupts */
453f9f848faSopenharmony_ci	xhci_do_poll(&sc->sc_bus);
454f9f848faSopenharmony_ci
455f9f848faSopenharmony_ci	if (sc->sc_port_route != NULL) {
456f9f848faSopenharmony_ci		/* Route all ports to the XHCI by default */
457f9f848faSopenharmony_ci		(void)sc->sc_port_route(sc->sc_bus.parent,
458f9f848faSopenharmony_ci		    ~xhciroute, xhciroute);
459f9f848faSopenharmony_ci	}
460f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
461f9f848faSopenharmony_ci}
462f9f848faSopenharmony_ci
463f9f848faSopenharmony_ciusb_error_t
464f9f848faSopenharmony_cixhci_halt_controller(struct xhci_softc *sc)
465f9f848faSopenharmony_ci{
466f9f848faSopenharmony_ci	uint32_t temp;
467f9f848faSopenharmony_ci	uint16_t i;
468f9f848faSopenharmony_ci
469f9f848faSopenharmony_ci	DPRINTF("\n");
470f9f848faSopenharmony_ci
471f9f848faSopenharmony_ci	sc->sc_capa_off = 0;
472f9f848faSopenharmony_ci	sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH);
473f9f848faSopenharmony_ci	sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0xF;
474f9f848faSopenharmony_ci	sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3;
475f9f848faSopenharmony_ci
476f9f848faSopenharmony_ci	/* Halt controller */
477f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_USBCMD, 0);
478f9f848faSopenharmony_ci
479f9f848faSopenharmony_ci	for (i = 0; i != 100; i++) {
480f9f848faSopenharmony_ci		usb_pause_mtx(NULL, hz / 100);
481f9f848faSopenharmony_ci		temp = XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_HCH;
482f9f848faSopenharmony_ci		if (temp)
483f9f848faSopenharmony_ci			break;
484f9f848faSopenharmony_ci	}
485f9f848faSopenharmony_ci
486f9f848faSopenharmony_ci	if (!temp) {
487f9f848faSopenharmony_ci		device_printf(sc->sc_bus.parent, "Controller halt timeout.\n");
488f9f848faSopenharmony_ci		return (USB_ERR_IOERROR);
489f9f848faSopenharmony_ci	}
490f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
491f9f848faSopenharmony_ci}
492f9f848faSopenharmony_ci
493f9f848faSopenharmony_ciusb_error_t
494f9f848faSopenharmony_cixhci_reset_controller(struct xhci_softc *sc)
495f9f848faSopenharmony_ci{
496f9f848faSopenharmony_ci	uint32_t temp = 0;
497f9f848faSopenharmony_ci	uint16_t i;
498f9f848faSopenharmony_ci
499f9f848faSopenharmony_ci	DPRINTF("\n");
500f9f848faSopenharmony_ci
501f9f848faSopenharmony_ci	/* Reset controller */
502f9f848faSopenharmony_ci	XWRITE4(sc, oper, XHCI_USBCMD, XHCI_CMD_HCRST);
503f9f848faSopenharmony_ci
504f9f848faSopenharmony_ci	for (i = 0; i != 100; i++) {
505f9f848faSopenharmony_ci		usb_pause_mtx(NULL, hz / 100);
506f9f848faSopenharmony_ci		temp = (XREAD4(sc, oper, XHCI_USBCMD) & XHCI_CMD_HCRST) |
507f9f848faSopenharmony_ci		    (XREAD4(sc, oper, XHCI_USBSTS) & XHCI_STS_CNR);
508f9f848faSopenharmony_ci		if (!temp)
509f9f848faSopenharmony_ci			break;
510f9f848faSopenharmony_ci	}
511f9f848faSopenharmony_ci
512f9f848faSopenharmony_ci	if (temp) {
513f9f848faSopenharmony_ci		device_printf(sc->sc_bus.parent, "Controller "
514f9f848faSopenharmony_ci		    "reset timeout.\n");
515f9f848faSopenharmony_ci		return (USB_ERR_IOERROR);
516f9f848faSopenharmony_ci	}
517f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
518f9f848faSopenharmony_ci}
519f9f848faSopenharmony_ci
520f9f848faSopenharmony_ciusb_error_t
521f9f848faSopenharmony_cixhci_init(struct xhci_softc *sc, device_t self, uint8_t dma32)
522f9f848faSopenharmony_ci{
523f9f848faSopenharmony_ci	uint32_t temp;
524f9f848faSopenharmony_ci
525f9f848faSopenharmony_ci	DPRINTF("\n");
526f9f848faSopenharmony_ci
527f9f848faSopenharmony_ci	/* initialize some bus fields */
528f9f848faSopenharmony_ci	sc->sc_bus.parent = self;
529f9f848faSopenharmony_ci
530f9f848faSopenharmony_ci	/* set the bus revision */
531f9f848faSopenharmony_ci	sc->sc_bus.usbrev = USB_REV_3_0;
532f9f848faSopenharmony_ci
533f9f848faSopenharmony_ci	/* set up the bus struct */
534f9f848faSopenharmony_ci	sc->sc_bus.methods = &xhci_bus_methods;
535f9f848faSopenharmony_ci
536f9f848faSopenharmony_ci	/* set up devices array */
537f9f848faSopenharmony_ci	sc->sc_bus.devices = sc->sc_devices;
538f9f848faSopenharmony_ci	sc->sc_bus.devices_max = XHCI_MAX_DEVICES;
539f9f848faSopenharmony_ci
540f9f848faSopenharmony_ci	/* set default cycle state in case of early interrupts */
541f9f848faSopenharmony_ci	sc->sc_event_ccs = 1;
542f9f848faSopenharmony_ci	sc->sc_command_ccs = 1;
543f9f848faSopenharmony_ci
544f9f848faSopenharmony_ci	/* set up bus space offsets */
545f9f848faSopenharmony_ci	sc->sc_capa_off = 0;
546f9f848faSopenharmony_ci	sc->sc_oper_off = XREAD1(sc, capa, XHCI_CAPLENGTH);
547f9f848faSopenharmony_ci	sc->sc_runt_off = XREAD4(sc, capa, XHCI_RTSOFF) & ~0x1F;
548f9f848faSopenharmony_ci	sc->sc_door_off = XREAD4(sc, capa, XHCI_DBOFF) & ~0x3;
549f9f848faSopenharmony_ci
550f9f848faSopenharmony_ci	DPRINTF("CAPLENGTH=0x%x\n", sc->sc_oper_off);
551f9f848faSopenharmony_ci	DPRINTF("RUNTIMEOFFSET=0x%x\n", sc->sc_runt_off);
552f9f848faSopenharmony_ci	DPRINTF("DOOROFFSET=0x%x\n", sc->sc_door_off);
553f9f848faSopenharmony_ci
554f9f848faSopenharmony_ci	DPRINTF("xHCI version = 0x%04x\n", XREAD2(sc, capa, XHCI_HCIVERSION));
555f9f848faSopenharmony_ci
556f9f848faSopenharmony_ci	if (!(XREAD4(sc, oper, XHCI_PAGESIZE) & XHCI_PAGESIZE_4K)) {
557f9f848faSopenharmony_ci		device_printf(sc->sc_bus.parent, "Controller does "
558f9f848faSopenharmony_ci		    "not support 4K page size.\n");
559f9f848faSopenharmony_ci		return (usb_error_t)(ENXIO);
560f9f848faSopenharmony_ci	}
561f9f848faSopenharmony_ci
562f9f848faSopenharmony_ci	temp = XREAD4(sc, capa, XHCI_HCSPARAMS0);
563f9f848faSopenharmony_ci
564f9f848faSopenharmony_ci	DPRINTF("HCS0 = 0x%08x\n", temp);
565f9f848faSopenharmony_ci
566f9f848faSopenharmony_ci	/* set up context size */
567f9f848faSopenharmony_ci	if (XHCI_HCS0_CSZ(temp)) {
568f9f848faSopenharmony_ci		sc->sc_ctx_is_64_byte = 1;
569f9f848faSopenharmony_ci	} else {
570f9f848faSopenharmony_ci		sc->sc_ctx_is_64_byte = 0;
571f9f848faSopenharmony_ci	}
572f9f848faSopenharmony_ci
573f9f848faSopenharmony_ci	/* get DMA bits */
574f9f848faSopenharmony_ci	sc->sc_bus.dma_bits = (XHCI_HCS0_AC64(temp) &&
575f9f848faSopenharmony_ci	    xhcidma32 == 0 && dma32 == 0) ? 64 : 32;
576f9f848faSopenharmony_ci
577f9f848faSopenharmony_ci	device_printf(self, "%d bytes context size, %d-bit DMA\n",
578f9f848faSopenharmony_ci	    sc->sc_ctx_is_64_byte ? 64 : 32, (int)sc->sc_bus.dma_bits);
579f9f848faSopenharmony_ci
580f9f848faSopenharmony_ci	temp = XREAD4(sc, capa, XHCI_HCSPARAMS1);
581f9f848faSopenharmony_ci
582f9f848faSopenharmony_ci	/* get number of device slots */
583f9f848faSopenharmony_ci	sc->sc_noport = XHCI_HCS1_N_PORTS(temp);
584f9f848faSopenharmony_ci
585f9f848faSopenharmony_ci	if (sc->sc_noport == 0) {
586f9f848faSopenharmony_ci		device_printf(sc->sc_bus.parent, "Invalid number "
587f9f848faSopenharmony_ci		    "of ports: %u\n", sc->sc_noport);
588f9f848faSopenharmony_ci		return (usb_error_t)(ENXIO);
589f9f848faSopenharmony_ci	}
590f9f848faSopenharmony_ci
591f9f848faSopenharmony_ci	sc->sc_noslot = XHCI_HCS1_DEVSLOT_MAX(temp);
592f9f848faSopenharmony_ci
593f9f848faSopenharmony_ci	DPRINTF("Max slots: %u\n", sc->sc_noslot);
594f9f848faSopenharmony_ci
595f9f848faSopenharmony_ci	if (sc->sc_noslot > XHCI_MAX_DEVICES)
596f9f848faSopenharmony_ci		sc->sc_noslot = XHCI_MAX_DEVICES;
597f9f848faSopenharmony_ci
598f9f848faSopenharmony_ci	temp = XREAD4(sc, capa, XHCI_HCSPARAMS2);
599f9f848faSopenharmony_ci
600f9f848faSopenharmony_ci	DPRINTF("HCS2=0x%08x\n", temp);
601f9f848faSopenharmony_ci
602f9f848faSopenharmony_ci	/* get number of scratchpads */
603f9f848faSopenharmony_ci	sc->sc_noscratch = XHCI_HCS2_SPB_MAX(temp);
604f9f848faSopenharmony_ci
605f9f848faSopenharmony_ci	if (sc->sc_noscratch > XHCI_MAX_SCRATCHPADS) {
606f9f848faSopenharmony_ci		device_printf(sc->sc_bus.parent, "XHCI request "
607f9f848faSopenharmony_ci		    "too many scratchpads\n");
608f9f848faSopenharmony_ci		return (usb_error_t)(ENOMEM);
609f9f848faSopenharmony_ci	}
610f9f848faSopenharmony_ci
611f9f848faSopenharmony_ci	DPRINTF("Max scratch: %u\n", sc->sc_noscratch);
612f9f848faSopenharmony_ci
613f9f848faSopenharmony_ci	/* get event table size */
614f9f848faSopenharmony_ci	sc->sc_erst_max = 1U << XHCI_HCS2_ERST_MAX(temp);
615f9f848faSopenharmony_ci	if (sc->sc_erst_max > XHCI_MAX_RSEG)
616f9f848faSopenharmony_ci		sc->sc_erst_max = XHCI_MAX_RSEG;
617f9f848faSopenharmony_ci
618f9f848faSopenharmony_ci	temp = XREAD4(sc, capa, XHCI_HCSPARAMS3);
619f9f848faSopenharmony_ci
620f9f848faSopenharmony_ci	/* get maximum exit latency */
621f9f848faSopenharmony_ci	sc->sc_exit_lat_max = XHCI_HCS3_U1_DEL(temp) +
622f9f848faSopenharmony_ci	    XHCI_HCS3_U2_DEL(temp) + 250 /* us */;
623f9f848faSopenharmony_ci
624f9f848faSopenharmony_ci	/* Check if we should use the default IMOD value. */
625f9f848faSopenharmony_ci	if (sc->sc_imod_default == 0)
626f9f848faSopenharmony_ci		sc->sc_imod_default = XHCI_IMOD_DEFAULT;
627f9f848faSopenharmony_ci
628f9f848faSopenharmony_ci	/* get all DMA memory */
629f9f848faSopenharmony_ci	if (usb_bus_mem_alloc_all(&sc->sc_bus,
630f9f848faSopenharmony_ci	    USB_GET_DMA_TAG(self), &xhci_iterate_hw_softc)) {
631f9f848faSopenharmony_ci		return (usb_error_t)(ENOMEM);
632f9f848faSopenharmony_ci	}
633f9f848faSopenharmony_ci
634f9f848faSopenharmony_ci	/* set up command queue mutex and condition varible */
635f9f848faSopenharmony_ci	cv_init(&sc->sc_cmd_cv, "CMDQ");
636f9f848faSopenharmony_ci	sx_init(&sc->sc_cmd_sx, "CMDQ lock");
637f9f848faSopenharmony_ci
638f9f848faSopenharmony_ci	sc->sc_config_msg[0].hdr.pm_callback = &xhci_configure_msg;
639f9f848faSopenharmony_ci	sc->sc_config_msg[0].bus = &sc->sc_bus;
640f9f848faSopenharmony_ci	sc->sc_config_msg[1].hdr.pm_callback = &xhci_configure_msg;
641f9f848faSopenharmony_ci	sc->sc_config_msg[1].bus = &sc->sc_bus;
642f9f848faSopenharmony_ci
643f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
644f9f848faSopenharmony_ci}
645f9f848faSopenharmony_ci
646f9f848faSopenharmony_civoid
647f9f848faSopenharmony_cixhci_uninit(struct xhci_softc *sc)
648f9f848faSopenharmony_ci{
649f9f848faSopenharmony_ci	/*
650f9f848faSopenharmony_ci	 * NOTE: At this point the control transfer process is gone
651f9f848faSopenharmony_ci	 * and "xhci_configure_msg" is no longer called. Consequently
652f9f848faSopenharmony_ci	 * waiting for the configuration messages to complete is not
653f9f848faSopenharmony_ci	 * needed.
654f9f848faSopenharmony_ci	 */
655f9f848faSopenharmony_ci	usb_bus_mem_free_all(&sc->sc_bus, &xhci_iterate_hw_softc);
656f9f848faSopenharmony_ci
657f9f848faSopenharmony_ci	cv_destroy(&sc->sc_cmd_cv);
658f9f848faSopenharmony_ci	sx_destroy(&sc->sc_cmd_sx);
659f9f848faSopenharmony_ci}
660f9f848faSopenharmony_ci
661f9f848faSopenharmony_cistatic void
662f9f848faSopenharmony_cixhci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state)
663f9f848faSopenharmony_ci{
664f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(bus);
665f9f848faSopenharmony_ci
666f9f848faSopenharmony_ci	switch (state) {
667f9f848faSopenharmony_ci	case USB_HW_POWER_SUSPEND:
668f9f848faSopenharmony_ci		DPRINTF("Stopping the XHCI\n");
669f9f848faSopenharmony_ci		(void)xhci_halt_controller(sc);
670f9f848faSopenharmony_ci		(void)xhci_reset_controller(sc);
671f9f848faSopenharmony_ci		break;
672f9f848faSopenharmony_ci	case USB_HW_POWER_SHUTDOWN:
673f9f848faSopenharmony_ci		DPRINTF("Stopping the XHCI\n");
674f9f848faSopenharmony_ci		(void)xhci_halt_controller(sc);
675f9f848faSopenharmony_ci		(void)xhci_reset_controller(sc);
676f9f848faSopenharmony_ci		break;
677f9f848faSopenharmony_ci	case USB_HW_POWER_RESUME:
678f9f848faSopenharmony_ci		DPRINTF("Starting the XHCI\n");
679f9f848faSopenharmony_ci		(void)xhci_start_controller(sc);
680f9f848faSopenharmony_ci		break;
681f9f848faSopenharmony_ci	default:
682f9f848faSopenharmony_ci		break;
683f9f848faSopenharmony_ci	}
684f9f848faSopenharmony_ci}
685f9f848faSopenharmony_ci
686f9f848faSopenharmony_cistatic usb_error_t
687f9f848faSopenharmony_cixhci_generic_done_sub(struct usb_xfer *xfer)
688f9f848faSopenharmony_ci{
689f9f848faSopenharmony_ci	struct xhci_td *td;
690f9f848faSopenharmony_ci	struct xhci_td *td_alt_next;
691f9f848faSopenharmony_ci	uint32_t len;
692f9f848faSopenharmony_ci	uint8_t status;
693f9f848faSopenharmony_ci
694f9f848faSopenharmony_ci	td = xfer->td_transfer_cache;
695f9f848faSopenharmony_ci	td_alt_next = td->alt_next;
696f9f848faSopenharmony_ci
697f9f848faSopenharmony_ci	if (xfer->aframes != xfer->nframes)
698f9f848faSopenharmony_ci		usbd_xfer_set_frame_len(xfer, xfer->aframes, 0);
699f9f848faSopenharmony_ci
700f9f848faSopenharmony_ci	while (1) {
701f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(td->page_cache);
702f9f848faSopenharmony_ci
703f9f848faSopenharmony_ci		status = td->status;
704f9f848faSopenharmony_ci		len = td->remainder;
705f9f848faSopenharmony_ci
706f9f848faSopenharmony_ci		DPRINTFN(4, "xfer=%p[%u/%u] rem=%u/%u status=%u\n",
707f9f848faSopenharmony_ci		    xfer, (unsigned int)xfer->aframes,
708f9f848faSopenharmony_ci		    (unsigned int)xfer->nframes,
709f9f848faSopenharmony_ci		    (unsigned int)len, (unsigned int)td->len,
710f9f848faSopenharmony_ci		    (unsigned int)status);
711f9f848faSopenharmony_ci
712f9f848faSopenharmony_ci		/*
713f9f848faSopenharmony_ci	         * Verify the status length and
714f9f848faSopenharmony_ci		 * add the length to "frlengths[]":
715f9f848faSopenharmony_ci	         */
716f9f848faSopenharmony_ci		if (len > td->len) {
717f9f848faSopenharmony_ci			/* should not happen */
718f9f848faSopenharmony_ci			DPRINTF("Invalid status length, "
719f9f848faSopenharmony_ci			    "0x%04x/0x%04x bytes\n", len, td->len);
720f9f848faSopenharmony_ci			status = XHCI_TRB_ERROR_LENGTH;
721f9f848faSopenharmony_ci		} else if (xfer->aframes != xfer->nframes) {
722f9f848faSopenharmony_ci			xfer->frlengths[xfer->aframes] += td->len - len;
723f9f848faSopenharmony_ci		}
724f9f848faSopenharmony_ci		/* Check for last transfer */
725f9f848faSopenharmony_ci		if (((void *)td) == xfer->td_transfer_last) {
726f9f848faSopenharmony_ci			td = NULL;
727f9f848faSopenharmony_ci			break;
728f9f848faSopenharmony_ci		}
729f9f848faSopenharmony_ci		/* Check for transfer error */
730f9f848faSopenharmony_ci		if (status != XHCI_TRB_ERROR_SHORT_PKT &&
731f9f848faSopenharmony_ci		    status != XHCI_TRB_ERROR_SUCCESS) {
732f9f848faSopenharmony_ci			/* the transfer is finished */
733f9f848faSopenharmony_ci			td = NULL;
734f9f848faSopenharmony_ci			break;
735f9f848faSopenharmony_ci		}
736f9f848faSopenharmony_ci		/* Check for short transfer */
737f9f848faSopenharmony_ci		if (len > 0) {
738f9f848faSopenharmony_ci			if (xfer->flags_int.short_frames_ok ||
739f9f848faSopenharmony_ci			    xfer->flags_int.isochronous_xfr ||
740f9f848faSopenharmony_ci			    xfer->flags_int.control_xfr) {
741f9f848faSopenharmony_ci				/* follow alt next */
742f9f848faSopenharmony_ci				td = td->alt_next;
743f9f848faSopenharmony_ci			} else {
744f9f848faSopenharmony_ci				/* the transfer is finished */
745f9f848faSopenharmony_ci				td = NULL;
746f9f848faSopenharmony_ci			}
747f9f848faSopenharmony_ci			break;
748f9f848faSopenharmony_ci		}
749f9f848faSopenharmony_ci		td = td->obj_next;
750f9f848faSopenharmony_ci
751f9f848faSopenharmony_ci		if (td->alt_next != td_alt_next) {
752f9f848faSopenharmony_ci			/* this USB frame is complete */
753f9f848faSopenharmony_ci			break;
754f9f848faSopenharmony_ci		}
755f9f848faSopenharmony_ci	}
756f9f848faSopenharmony_ci
757f9f848faSopenharmony_ci	/* update transfer cache */
758f9f848faSopenharmony_ci
759f9f848faSopenharmony_ci	xfer->td_transfer_cache = td;
760f9f848faSopenharmony_ci
761f9f848faSopenharmony_ci	return ((status == XHCI_TRB_ERROR_STALL) ? USB_ERR_STALLED :
762f9f848faSopenharmony_ci	    (status != XHCI_TRB_ERROR_SHORT_PKT &&
763f9f848faSopenharmony_ci	    status != XHCI_TRB_ERROR_SUCCESS) ? USB_ERR_IOERROR :
764f9f848faSopenharmony_ci	    USB_ERR_NORMAL_COMPLETION);
765f9f848faSopenharmony_ci}
766f9f848faSopenharmony_ci
767f9f848faSopenharmony_cistatic void
768f9f848faSopenharmony_cixhci_generic_done(struct usb_xfer *xfer)
769f9f848faSopenharmony_ci{
770f9f848faSopenharmony_ci	usb_error_t err = USB_ERR_NORMAL_COMPLETION;
771f9f848faSopenharmony_ci
772f9f848faSopenharmony_ci	DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n",
773f9f848faSopenharmony_ci	    xfer, xfer->endpoint);
774f9f848faSopenharmony_ci
775f9f848faSopenharmony_ci	/* reset scanner */
776f9f848faSopenharmony_ci
777f9f848faSopenharmony_ci	xfer->td_transfer_cache = xfer->td_transfer_first;
778f9f848faSopenharmony_ci
779f9f848faSopenharmony_ci	if (xfer->flags_int.control_xfr) {
780f9f848faSopenharmony_ci		if (xfer->flags_int.control_hdr)
781f9f848faSopenharmony_ci			err = xhci_generic_done_sub(xfer);
782f9f848faSopenharmony_ci
783f9f848faSopenharmony_ci		xfer->aframes = 1;
784f9f848faSopenharmony_ci
785f9f848faSopenharmony_ci		if (xfer->td_transfer_cache == NULL)
786f9f848faSopenharmony_ci			goto done;
787f9f848faSopenharmony_ci	}
788f9f848faSopenharmony_ci
789f9f848faSopenharmony_ci	while (xfer->aframes != xfer->nframes) {
790f9f848faSopenharmony_ci		err = xhci_generic_done_sub(xfer);
791f9f848faSopenharmony_ci		xfer->aframes++;
792f9f848faSopenharmony_ci
793f9f848faSopenharmony_ci		if (xfer->td_transfer_cache == NULL)
794f9f848faSopenharmony_ci			goto done;
795f9f848faSopenharmony_ci	}
796f9f848faSopenharmony_ci
797f9f848faSopenharmony_ci	if (xfer->flags_int.control_xfr &&
798f9f848faSopenharmony_ci	    !xfer->flags_int.control_act)
799f9f848faSopenharmony_ci		err = xhci_generic_done_sub(xfer);
800f9f848faSopenharmony_cidone:
801f9f848faSopenharmony_ci	/* transfer is complete */
802f9f848faSopenharmony_ci	xhci_device_done(xfer, err);
803f9f848faSopenharmony_ci}
804f9f848faSopenharmony_ci
805f9f848faSopenharmony_cistatic void
806f9f848faSopenharmony_cixhci_activate_transfer(struct usb_xfer *xfer)
807f9f848faSopenharmony_ci{
808f9f848faSopenharmony_ci	struct xhci_td *td;
809f9f848faSopenharmony_ci
810f9f848faSopenharmony_ci	td = xfer->td_transfer_cache;
811f9f848faSopenharmony_ci
812f9f848faSopenharmony_ci	usb_pc_cpu_invalidate(td->page_cache);
813f9f848faSopenharmony_ci
814f9f848faSopenharmony_ci	if (!(td->td_trb[0].dwTrb3 & htole32(XHCI_TRB_3_CYCLE_BIT))) {
815f9f848faSopenharmony_ci		/* activate the transfer */
816f9f848faSopenharmony_ci		td->td_trb[0].dwTrb3 |= htole32(XHCI_TRB_3_CYCLE_BIT);
817f9f848faSopenharmony_ci		usb_pc_cpu_flush(td->page_cache);
818f9f848faSopenharmony_ci
819f9f848faSopenharmony_ci		xhci_endpoint_doorbell(xfer);
820f9f848faSopenharmony_ci	}
821f9f848faSopenharmony_ci}
822f9f848faSopenharmony_ci
823f9f848faSopenharmony_cistatic void
824f9f848faSopenharmony_cixhci_skip_transfer(struct usb_xfer *xfer)
825f9f848faSopenharmony_ci{
826f9f848faSopenharmony_ci	struct xhci_td *td;
827f9f848faSopenharmony_ci	struct xhci_td *td_last;
828f9f848faSopenharmony_ci
829f9f848faSopenharmony_ci	td = xfer->td_transfer_cache;
830f9f848faSopenharmony_ci	td_last = xfer->td_transfer_last;
831f9f848faSopenharmony_ci
832f9f848faSopenharmony_ci	td = td->alt_next;
833f9f848faSopenharmony_ci
834f9f848faSopenharmony_ci	usb_pc_cpu_invalidate(td->page_cache);
835f9f848faSopenharmony_ci
836f9f848faSopenharmony_ci	if (!(td->td_trb[0].dwTrb3 & htole32(XHCI_TRB_3_CYCLE_BIT))) {
837f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(td_last->page_cache);
838f9f848faSopenharmony_ci
839f9f848faSopenharmony_ci		/* copy LINK TRB to current waiting location */
840f9f848faSopenharmony_ci
841f9f848faSopenharmony_ci		td->td_trb[0].qwTrb0 = td_last->td_trb[td_last->ntrb].qwTrb0;
842f9f848faSopenharmony_ci		td->td_trb[0].dwTrb2 = td_last->td_trb[td_last->ntrb].dwTrb2;
843f9f848faSopenharmony_ci		usb_pc_cpu_flush(td->page_cache);
844f9f848faSopenharmony_ci
845f9f848faSopenharmony_ci		td->td_trb[0].dwTrb3 = td_last->td_trb[td_last->ntrb].dwTrb3;
846f9f848faSopenharmony_ci		usb_pc_cpu_flush(td->page_cache);
847f9f848faSopenharmony_ci
848f9f848faSopenharmony_ci		xhci_endpoint_doorbell(xfer);
849f9f848faSopenharmony_ci	}
850f9f848faSopenharmony_ci}
851f9f848faSopenharmony_ci
852f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
853f9f848faSopenharmony_ci *	xhci_check_transfer
854f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
855f9f848faSopenharmony_cistatic void
856f9f848faSopenharmony_cixhci_check_transfer(struct xhci_softc *sc, struct xhci_trb *trb)
857f9f848faSopenharmony_ci{
858f9f848faSopenharmony_ci	struct xhci_endpoint_ext *pepext;
859f9f848faSopenharmony_ci	int64_t offset;
860f9f848faSopenharmony_ci	uint64_t td_event;
861f9f848faSopenharmony_ci	uint32_t temp;
862f9f848faSopenharmony_ci	uint32_t remainder;
863f9f848faSopenharmony_ci	uint16_t stream_id = 0;
864f9f848faSopenharmony_ci	uint16_t i;
865f9f848faSopenharmony_ci	uint8_t status;
866f9f848faSopenharmony_ci	uint8_t halted;
867f9f848faSopenharmony_ci	uint8_t epno;
868f9f848faSopenharmony_ci	uint8_t index;
869f9f848faSopenharmony_ci
870f9f848faSopenharmony_ci	/* decode TRB */
871f9f848faSopenharmony_ci	td_event = le64toh(trb->qwTrb0);
872f9f848faSopenharmony_ci	temp = le32toh(trb->dwTrb2);
873f9f848faSopenharmony_ci
874f9f848faSopenharmony_ci	remainder = XHCI_TRB_2_REM_GET(temp);
875f9f848faSopenharmony_ci	status = XHCI_TRB_2_ERROR_GET(temp);
876f9f848faSopenharmony_ci
877f9f848faSopenharmony_ci	temp = le32toh(trb->dwTrb3);
878f9f848faSopenharmony_ci	epno = XHCI_TRB_3_EP_GET(temp);
879f9f848faSopenharmony_ci	index = XHCI_TRB_3_SLOT_GET(temp);
880f9f848faSopenharmony_ci
881f9f848faSopenharmony_ci	/* check if error means halted */
882f9f848faSopenharmony_ci	halted = (status != XHCI_TRB_ERROR_SHORT_PKT &&
883f9f848faSopenharmony_ci	    status != XHCI_TRB_ERROR_SUCCESS);
884f9f848faSopenharmony_ci
885f9f848faSopenharmony_ci	DPRINTF("slot=%u epno=%u remainder=%u status=%u\n",
886f9f848faSopenharmony_ci	    index, epno, remainder, status);
887f9f848faSopenharmony_ci
888f9f848faSopenharmony_ci	if (index > sc->sc_noslot) {
889f9f848faSopenharmony_ci		DPRINTF("Invalid slot.\n");
890f9f848faSopenharmony_ci		return;
891f9f848faSopenharmony_ci	}
892f9f848faSopenharmony_ci
893f9f848faSopenharmony_ci	if ((epno == 0) || (epno >= XHCI_MAX_ENDPOINTS)) {
894f9f848faSopenharmony_ci		DPRINTF("Invalid endpoint.\n");
895f9f848faSopenharmony_ci		return;
896f9f848faSopenharmony_ci	}
897f9f848faSopenharmony_ci
898f9f848faSopenharmony_ci	pepext = &sc->sc_hw.devs[index].endp[epno];
899f9f848faSopenharmony_ci
900f9f848faSopenharmony_ci	/* try to find the USB transfer that generated the event */
901f9f848faSopenharmony_ci	for (i = 0;; i++) {
902f9f848faSopenharmony_ci		struct usb_xfer *xfer;
903f9f848faSopenharmony_ci		struct xhci_td *td;
904f9f848faSopenharmony_ci
905f9f848faSopenharmony_ci		if (i == (XHCI_MAX_TRANSFERS - 1)) {
906f9f848faSopenharmony_ci			if (pepext->trb_ep_mode != USB_EP_MODE_STREAMS ||
907f9f848faSopenharmony_ci			    stream_id == (XHCI_MAX_STREAMS - 1))
908f9f848faSopenharmony_ci				break;
909f9f848faSopenharmony_ci			stream_id++;
910f9f848faSopenharmony_ci			i = 0;
911f9f848faSopenharmony_ci			DPRINTFN(5, "stream_id=%u\n", stream_id);
912f9f848faSopenharmony_ci		}
913f9f848faSopenharmony_ci
914f9f848faSopenharmony_ci		xfer = pepext->xfer[i + (XHCI_MAX_TRANSFERS * stream_id)];
915f9f848faSopenharmony_ci		if (xfer == NULL)
916f9f848faSopenharmony_ci			continue;
917f9f848faSopenharmony_ci
918f9f848faSopenharmony_ci		td = xfer->td_transfer_cache;
919f9f848faSopenharmony_ci		if (td == NULL) {
920f9f848faSopenharmony_ci			continue;
921f9f848faSopenharmony_ci		}
922f9f848faSopenharmony_ci
923f9f848faSopenharmony_ci		DPRINTFN(5, "Checking if 0x%016llx == (0x%016llx .. 0x%016llx)\n",
924f9f848faSopenharmony_ci			(long long)td_event,
925f9f848faSopenharmony_ci			(long long)td->td_self,
926f9f848faSopenharmony_ci			(long long)td->td_self + sizeof(td->td_trb));
927f9f848faSopenharmony_ci
928f9f848faSopenharmony_ci		/*
929f9f848faSopenharmony_ci		 * NOTE: Some XHCI implementations might not trigger
930f9f848faSopenharmony_ci		 * an event on the last LINK TRB so we need to
931f9f848faSopenharmony_ci		 * consider both the last and second last event
932f9f848faSopenharmony_ci		 * address as conditions for a successful transfer.
933f9f848faSopenharmony_ci		 *
934f9f848faSopenharmony_ci		 * NOTE: We assume that the XHCI will only trigger one
935f9f848faSopenharmony_ci		 * event per chain of TRBs.
936f9f848faSopenharmony_ci		 */
937f9f848faSopenharmony_ci
938f9f848faSopenharmony_ci		offset = td_event - td->td_self;
939f9f848faSopenharmony_ci
940f9f848faSopenharmony_ci		if ((offset >= 0) &&
941f9f848faSopenharmony_ci		    (offset < (int64_t)sizeof(td->td_trb))) {
942f9f848faSopenharmony_ci			usb_pc_cpu_invalidate(td->page_cache);
943f9f848faSopenharmony_ci
944f9f848faSopenharmony_ci			/* compute rest of remainder, if any */
945f9f848faSopenharmony_ci			for (i = (offset / 16) + 1; i < td->ntrb; i++) {
946f9f848faSopenharmony_ci				temp = le32toh(td->td_trb[i].dwTrb2);
947f9f848faSopenharmony_ci				remainder += XHCI_TRB_2_BYTES_GET(temp);
948f9f848faSopenharmony_ci			}
949f9f848faSopenharmony_ci
950f9f848faSopenharmony_ci			DPRINTFN(5, "New remainder: %u\n", remainder);
951f9f848faSopenharmony_ci
952f9f848faSopenharmony_ci			/* clear isochronous transfer errors */
953f9f848faSopenharmony_ci			if (xfer->flags_int.isochronous_xfr) {
954f9f848faSopenharmony_ci				if (halted) {
955f9f848faSopenharmony_ci					halted = 0;
956f9f848faSopenharmony_ci					status = XHCI_TRB_ERROR_SUCCESS;
957f9f848faSopenharmony_ci					remainder = td->len;
958f9f848faSopenharmony_ci				}
959f9f848faSopenharmony_ci			}
960f9f848faSopenharmony_ci
961f9f848faSopenharmony_ci			/* "td->remainder" is verified later */
962f9f848faSopenharmony_ci			td->remainder = remainder;
963f9f848faSopenharmony_ci			td->status = status;
964f9f848faSopenharmony_ci
965f9f848faSopenharmony_ci			usb_pc_cpu_flush(td->page_cache);
966f9f848faSopenharmony_ci
967f9f848faSopenharmony_ci			/*
968f9f848faSopenharmony_ci			 * 1) Last transfer descriptor makes the
969f9f848faSopenharmony_ci			 * transfer done
970f9f848faSopenharmony_ci			 */
971f9f848faSopenharmony_ci			if (((void *)td) == xfer->td_transfer_last) {
972f9f848faSopenharmony_ci				DPRINTF("TD is last\n");
973f9f848faSopenharmony_ci				xhci_generic_done(xfer);
974f9f848faSopenharmony_ci				break;
975f9f848faSopenharmony_ci			}
976f9f848faSopenharmony_ci
977f9f848faSopenharmony_ci			/*
978f9f848faSopenharmony_ci			 * 2) Any kind of error makes the transfer
979f9f848faSopenharmony_ci			 * done
980f9f848faSopenharmony_ci			 */
981f9f848faSopenharmony_ci			if (halted) {
982f9f848faSopenharmony_ci				DPRINTF("TD has I/O error\n");
983f9f848faSopenharmony_ci				xhci_generic_done(xfer);
984f9f848faSopenharmony_ci				break;
985f9f848faSopenharmony_ci			}
986f9f848faSopenharmony_ci
987f9f848faSopenharmony_ci			/*
988f9f848faSopenharmony_ci			 * 3) If there is no alternate next transfer,
989f9f848faSopenharmony_ci			 * a short packet also makes the transfer done
990f9f848faSopenharmony_ci			 */
991f9f848faSopenharmony_ci			if (td->remainder > 0) {
992f9f848faSopenharmony_ci				if (td->alt_next == NULL) {
993f9f848faSopenharmony_ci					DPRINTF(
994f9f848faSopenharmony_ci					    "short TD has no alternate next\n");
995f9f848faSopenharmony_ci					xhci_generic_done(xfer);
996f9f848faSopenharmony_ci					break;
997f9f848faSopenharmony_ci				}
998f9f848faSopenharmony_ci				DPRINTF("TD has short pkt\n");
999f9f848faSopenharmony_ci				if (xfer->flags_int.short_frames_ok ||
1000f9f848faSopenharmony_ci				    xfer->flags_int.isochronous_xfr ||
1001f9f848faSopenharmony_ci				    xfer->flags_int.control_xfr) {
1002f9f848faSopenharmony_ci					/* follow the alt next */
1003f9f848faSopenharmony_ci					xfer->td_transfer_cache = td->alt_next;
1004f9f848faSopenharmony_ci					xhci_activate_transfer(xfer);
1005f9f848faSopenharmony_ci					break;
1006f9f848faSopenharmony_ci				}
1007f9f848faSopenharmony_ci				xhci_skip_transfer(xfer);
1008f9f848faSopenharmony_ci				xhci_generic_done(xfer);
1009f9f848faSopenharmony_ci				break;
1010f9f848faSopenharmony_ci			}
1011f9f848faSopenharmony_ci
1012f9f848faSopenharmony_ci			/*
1013f9f848faSopenharmony_ci			 * 4) Transfer complete - go to next TD
1014f9f848faSopenharmony_ci			 */
1015f9f848faSopenharmony_ci			DPRINTF("Following next TD\n");
1016f9f848faSopenharmony_ci			xfer->td_transfer_cache = td->obj_next;
1017f9f848faSopenharmony_ci			xhci_activate_transfer(xfer);
1018f9f848faSopenharmony_ci			break;		/* there should only be one match */
1019f9f848faSopenharmony_ci		}
1020f9f848faSopenharmony_ci	}
1021f9f848faSopenharmony_ci}
1022f9f848faSopenharmony_ci
1023f9f848faSopenharmony_cistatic int
1024f9f848faSopenharmony_cixhci_check_command(struct xhci_softc *sc, struct xhci_trb *trb)
1025f9f848faSopenharmony_ci{
1026f9f848faSopenharmony_ci	if (sc->sc_cmd_addr == trb->qwTrb0) {
1027f9f848faSopenharmony_ci		DPRINTF("Received command event\n");
1028f9f848faSopenharmony_ci		sc->sc_cmd_result[0] = trb->dwTrb2;
1029f9f848faSopenharmony_ci		sc->sc_cmd_result[1] = trb->dwTrb3;
1030f9f848faSopenharmony_ci		(void)cv_signal(&sc->sc_cmd_cv);
1031f9f848faSopenharmony_ci		return (1);	/* command match */
1032f9f848faSopenharmony_ci	}
1033f9f848faSopenharmony_ci	return (0);
1034f9f848faSopenharmony_ci}
1035f9f848faSopenharmony_ci
1036f9f848faSopenharmony_cistatic int
1037f9f848faSopenharmony_cixhci_interrupt_poll(struct xhci_softc *sc)
1038f9f848faSopenharmony_ci{
1039f9f848faSopenharmony_ci	struct usb_page_search buf_res;
1040f9f848faSopenharmony_ci	struct xhci_hw_root *phwr;
1041f9f848faSopenharmony_ci	uint64_t addr;
1042f9f848faSopenharmony_ci	uint32_t temp;
1043f9f848faSopenharmony_ci	int retval = 0;
1044f9f848faSopenharmony_ci	uint16_t i;
1045f9f848faSopenharmony_ci	uint8_t event;
1046f9f848faSopenharmony_ci	uint8_t j;
1047f9f848faSopenharmony_ci	uint8_t k;
1048f9f848faSopenharmony_ci	uint8_t t;
1049f9f848faSopenharmony_ci
1050f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
1051f9f848faSopenharmony_ci
1052f9f848faSopenharmony_ci	phwr = buf_res.buffer;
1053f9f848faSopenharmony_ci
1054f9f848faSopenharmony_ci	/* Receive any events */
1055f9f848faSopenharmony_ci
1056f9f848faSopenharmony_ci	usb_pc_cpu_invalidate(&sc->sc_hw.root_pc);
1057f9f848faSopenharmony_ci
1058f9f848faSopenharmony_ci	i = sc->sc_event_idx;
1059f9f848faSopenharmony_ci	j = sc->sc_event_ccs;
1060f9f848faSopenharmony_ci	t = 2;
1061f9f848faSopenharmony_ci
1062f9f848faSopenharmony_ci	while (1) {
1063f9f848faSopenharmony_ci		temp = le32toh(phwr->hwr_events[i].dwTrb3);
1064f9f848faSopenharmony_ci
1065f9f848faSopenharmony_ci		k = (temp & XHCI_TRB_3_CYCLE_BIT) ? 1 : 0;
1066f9f848faSopenharmony_ci
1067f9f848faSopenharmony_ci		if (j != k)
1068f9f848faSopenharmony_ci			break;
1069f9f848faSopenharmony_ci
1070f9f848faSopenharmony_ci		event = XHCI_TRB_3_TYPE_GET(temp);
1071f9f848faSopenharmony_ci
1072f9f848faSopenharmony_ci		DPRINTFN(10, "event[%u] = %u (0x%016llx 0x%08lx 0x%08lx)\n",
1073f9f848faSopenharmony_ci		    i, event, (long long)le64toh(phwr->hwr_events[i].qwTrb0),
1074f9f848faSopenharmony_ci		    (long)le32toh(phwr->hwr_events[i].dwTrb2),
1075f9f848faSopenharmony_ci		    (long)le32toh(phwr->hwr_events[i].dwTrb3));
1076f9f848faSopenharmony_ci
1077f9f848faSopenharmony_ci		switch (event) {
1078f9f848faSopenharmony_ci		case XHCI_TRB_EVENT_TRANSFER:
1079f9f848faSopenharmony_ci			xhci_check_transfer(sc, &phwr->hwr_events[i]);
1080f9f848faSopenharmony_ci			break;
1081f9f848faSopenharmony_ci		case XHCI_TRB_EVENT_CMD_COMPLETE:
1082f9f848faSopenharmony_ci			retval |= xhci_check_command(sc, &phwr->hwr_events[i]);
1083f9f848faSopenharmony_ci			break;
1084f9f848faSopenharmony_ci		default:
1085f9f848faSopenharmony_ci			DPRINTF("Unhandled event = %u\n", event);
1086f9f848faSopenharmony_ci			break;
1087f9f848faSopenharmony_ci		}
1088f9f848faSopenharmony_ci
1089f9f848faSopenharmony_ci		i++;
1090f9f848faSopenharmony_ci
1091f9f848faSopenharmony_ci		if (i == XHCI_MAX_EVENTS) {
1092f9f848faSopenharmony_ci			i = 0;
1093f9f848faSopenharmony_ci			j ^= 1;
1094f9f848faSopenharmony_ci
1095f9f848faSopenharmony_ci			/* check for timeout */
1096f9f848faSopenharmony_ci			if (!--t)
1097f9f848faSopenharmony_ci				break;
1098f9f848faSopenharmony_ci		}
1099f9f848faSopenharmony_ci	}
1100f9f848faSopenharmony_ci
1101f9f848faSopenharmony_ci	sc->sc_event_idx = i;
1102f9f848faSopenharmony_ci	sc->sc_event_ccs = j;
1103f9f848faSopenharmony_ci
1104f9f848faSopenharmony_ci	/*
1105f9f848faSopenharmony_ci	 * NOTE: The Event Ring Dequeue Pointer Register is 64-bit
1106f9f848faSopenharmony_ci	 * latched. That means to activate the register we need to
1107f9f848faSopenharmony_ci	 * write both the low and high double word of the 64-bit
1108f9f848faSopenharmony_ci	 * register.
1109f9f848faSopenharmony_ci	 */
1110f9f848faSopenharmony_ci
1111f9f848faSopenharmony_ci	addr = buf_res.physaddr;
1112f9f848faSopenharmony_ci	addr += __offsetof(struct xhci_hw_root, hwr_events[i]);
1113f9f848faSopenharmony_ci
1114f9f848faSopenharmony_ci	/* try to clear busy bit */
1115f9f848faSopenharmony_ci	addr |= XHCI_ERDP_LO_BUSY;
1116f9f848faSopenharmony_ci
1117f9f848faSopenharmony_ci	XWRITE4(sc, runt, XHCI_ERDP_LO(0), (uint32_t)addr);
1118f9f848faSopenharmony_ci	XWRITE4(sc, runt, XHCI_ERDP_HI(0), (uint32_t)(addr >> 32));
1119f9f848faSopenharmony_ci
1120f9f848faSopenharmony_ci	return (retval);
1121f9f848faSopenharmony_ci}
1122f9f848faSopenharmony_ci
1123f9f848faSopenharmony_cistatic usb_error_t
1124f9f848faSopenharmony_cixhci_do_command(struct xhci_softc *sc, struct xhci_trb *trb,
1125f9f848faSopenharmony_ci    uint16_t timeout_ms)
1126f9f848faSopenharmony_ci{
1127f9f848faSopenharmony_ci	struct usb_page_search buf_res;
1128f9f848faSopenharmony_ci	struct xhci_hw_root *phwr;
1129f9f848faSopenharmony_ci	uint64_t addr;
1130f9f848faSopenharmony_ci	uint32_t temp;
1131f9f848faSopenharmony_ci	uint8_t i;
1132f9f848faSopenharmony_ci	uint8_t j;
1133f9f848faSopenharmony_ci	uint8_t timeout = 0;
1134f9f848faSopenharmony_ci	usb_error_t err;
1135f9f848faSopenharmony_ci
1136f9f848faSopenharmony_ci	XHCI_CMD_ASSERT_LOCKED(sc);
1137f9f848faSopenharmony_ci
1138f9f848faSopenharmony_ci	/* get hardware root structure */
1139f9f848faSopenharmony_ci
1140f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.root_pc, 0, &buf_res);
1141f9f848faSopenharmony_ci
1142f9f848faSopenharmony_ci	phwr = buf_res.buffer;
1143f9f848faSopenharmony_ci
1144f9f848faSopenharmony_ci	/* Queue command */
1145f9f848faSopenharmony_ci
1146f9f848faSopenharmony_ci	USB_BUS_LOCK(&sc->sc_bus);
1147f9f848faSopenharmony_ciretry:
1148f9f848faSopenharmony_ci	i = sc->sc_command_idx;
1149f9f848faSopenharmony_ci	j = sc->sc_command_ccs;
1150f9f848faSopenharmony_ci
1151f9f848faSopenharmony_ci	DPRINTFN(10, "command[%u] = %u (0x%016llx, 0x%08lx, 0x%08lx)\n",
1152f9f848faSopenharmony_ci	    i, XHCI_TRB_3_TYPE_GET(le32toh(trb->dwTrb3)),
1153f9f848faSopenharmony_ci	    (long long)le64toh(trb->qwTrb0),
1154f9f848faSopenharmony_ci	    (long)le32toh(trb->dwTrb2),
1155f9f848faSopenharmony_ci	    (long)le32toh(trb->dwTrb3));
1156f9f848faSopenharmony_ci
1157f9f848faSopenharmony_ci	phwr->hwr_commands[i].qwTrb0 = trb->qwTrb0;
1158f9f848faSopenharmony_ci	phwr->hwr_commands[i].dwTrb2 = trb->dwTrb2;
1159f9f848faSopenharmony_ci
1160f9f848faSopenharmony_ci	usb_pc_cpu_flush(&sc->sc_hw.root_pc);
1161f9f848faSopenharmony_ci
1162f9f848faSopenharmony_ci	temp = trb->dwTrb3;
1163f9f848faSopenharmony_ci
1164f9f848faSopenharmony_ci	if (j)
1165f9f848faSopenharmony_ci		temp |= htole32(XHCI_TRB_3_CYCLE_BIT);
1166f9f848faSopenharmony_ci	else
1167f9f848faSopenharmony_ci		temp &= ~htole32(XHCI_TRB_3_CYCLE_BIT);
1168f9f848faSopenharmony_ci
1169f9f848faSopenharmony_ci	temp &= ~htole32(XHCI_TRB_3_TC_BIT);
1170f9f848faSopenharmony_ci
1171f9f848faSopenharmony_ci	phwr->hwr_commands[i].dwTrb3 = temp;
1172f9f848faSopenharmony_ci
1173f9f848faSopenharmony_ci	usb_pc_cpu_flush(&sc->sc_hw.root_pc);
1174f9f848faSopenharmony_ci
1175f9f848faSopenharmony_ci	addr = buf_res.physaddr;
1176f9f848faSopenharmony_ci	addr += __offsetof(struct xhci_hw_root, hwr_commands[i]);
1177f9f848faSopenharmony_ci
1178f9f848faSopenharmony_ci	sc->sc_cmd_addr = htole64(addr);
1179f9f848faSopenharmony_ci
1180f9f848faSopenharmony_ci	i++;
1181f9f848faSopenharmony_ci
1182f9f848faSopenharmony_ci	if (i == (XHCI_MAX_COMMANDS - 1)) {
1183f9f848faSopenharmony_ci		if (j) {
1184f9f848faSopenharmony_ci			temp = htole32(XHCI_TRB_3_TC_BIT |
1185f9f848faSopenharmony_ci			    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) |
1186f9f848faSopenharmony_ci			    XHCI_TRB_3_CYCLE_BIT);
1187f9f848faSopenharmony_ci		} else {
1188f9f848faSopenharmony_ci			temp = htole32(XHCI_TRB_3_TC_BIT |
1189f9f848faSopenharmony_ci			    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK));
1190f9f848faSopenharmony_ci		}
1191f9f848faSopenharmony_ci
1192f9f848faSopenharmony_ci		phwr->hwr_commands[i].dwTrb3 = temp;
1193f9f848faSopenharmony_ci
1194f9f848faSopenharmony_ci		usb_pc_cpu_flush(&sc->sc_hw.root_pc);
1195f9f848faSopenharmony_ci
1196f9f848faSopenharmony_ci		i = 0;
1197f9f848faSopenharmony_ci		j ^= 1;
1198f9f848faSopenharmony_ci	}
1199f9f848faSopenharmony_ci
1200f9f848faSopenharmony_ci	sc->sc_command_idx = i;
1201f9f848faSopenharmony_ci	sc->sc_command_ccs = j;
1202f9f848faSopenharmony_ci
1203f9f848faSopenharmony_ci	XWRITE4(sc, door, XHCI_DOORBELL(0), 0);
1204f9f848faSopenharmony_ci
1205f9f848faSopenharmony_ci	err = (usb_error_t)cv_timedwait(&sc->sc_cmd_cv, &sc->sc_bus.bus_mtx,
1206f9f848faSopenharmony_ci	    USB_MS_TO_TICKS(timeout_ms));
1207f9f848faSopenharmony_ci
1208f9f848faSopenharmony_ci	/*
1209f9f848faSopenharmony_ci	 * In some error cases event interrupts are not generated.
1210f9f848faSopenharmony_ci	 * Poll one time to see if the command has completed.
1211f9f848faSopenharmony_ci	 */
1212f9f848faSopenharmony_ci	if ((err != 0) && (xhci_interrupt_poll(sc) != 0)) {
1213f9f848faSopenharmony_ci		DPRINTF("Command was completed when polling\n");
1214f9f848faSopenharmony_ci		err = USB_ERR_NORMAL_COMPLETION;
1215f9f848faSopenharmony_ci	}
1216f9f848faSopenharmony_ci	if (err != 0) {
1217f9f848faSopenharmony_ci		DPRINTF("Command timeout!\n");
1218f9f848faSopenharmony_ci		/*
1219f9f848faSopenharmony_ci		 * After some weeks of continuous operation, it has
1220f9f848faSopenharmony_ci		 * been observed that the ASMedia Technology, ASM1042
1221f9f848faSopenharmony_ci		 * SuperSpeed USB Host Controller can suddenly stop
1222f9f848faSopenharmony_ci		 * accepting commands via the command queue. Try to
1223f9f848faSopenharmony_ci		 * first reset the command queue. If that fails do a
1224f9f848faSopenharmony_ci		 * host controller reset.
1225f9f848faSopenharmony_ci		 */
1226f9f848faSopenharmony_ci		if ((timeout == 0) &&
1227f9f848faSopenharmony_ci		    (xhci_reset_command_queue_locked(sc) == 0)) {
1228f9f848faSopenharmony_ci			temp = le32toh(trb->dwTrb3);
1229f9f848faSopenharmony_ci
1230f9f848faSopenharmony_ci			/*
1231f9f848faSopenharmony_ci			 * Avoid infinite XHCI reset loops if the set
1232f9f848faSopenharmony_ci			 * address command fails to respond due to a
1233f9f848faSopenharmony_ci			 * non-enumerating device:
1234f9f848faSopenharmony_ci			 */
1235f9f848faSopenharmony_ci			if ((XHCI_TRB_3_TYPE_GET(temp) == XHCI_TRB_TYPE_ADDRESS_DEVICE) &&
1236f9f848faSopenharmony_ci			    ((temp & XHCI_TRB_3_BSR_BIT) == 0)) {
1237f9f848faSopenharmony_ci				DPRINTF("Set address timeout\n");
1238f9f848faSopenharmony_ci			} else {
1239f9f848faSopenharmony_ci				timeout = 1;
1240f9f848faSopenharmony_ci				goto retry;
1241f9f848faSopenharmony_ci			}
1242f9f848faSopenharmony_ci		} else {
1243f9f848faSopenharmony_ci			DPRINTF("Controller reset!\n");
1244f9f848faSopenharmony_ci			usb_bus_reset_async_locked(&sc->sc_bus);
1245f9f848faSopenharmony_ci		}
1246f9f848faSopenharmony_ci		err = USB_ERR_TIMEOUT;
1247f9f848faSopenharmony_ci		trb->dwTrb2 = 0;
1248f9f848faSopenharmony_ci		trb->dwTrb3 = 0;
1249f9f848faSopenharmony_ci	} else {
1250f9f848faSopenharmony_ci		temp = le32toh(sc->sc_cmd_result[0]);
1251f9f848faSopenharmony_ci		if (XHCI_TRB_2_ERROR_GET(temp) != XHCI_TRB_ERROR_SUCCESS) {
1252f9f848faSopenharmony_ci			if (!((XHCI_TRB_2_ERROR_GET(temp) == XHCI_TRB_ERROR_SLOT_NOT_ON) &&
1253f9f848faSopenharmony_ci			    (XHCI_TRB_3_SLOT_GET(le32toh(sc->sc_cmd_result[1]))))) {
1254f9f848faSopenharmony_ci				err = USB_ERR_IOERROR;
1255f9f848faSopenharmony_ci			}
1256f9f848faSopenharmony_ci		}
1257f9f848faSopenharmony_ci
1258f9f848faSopenharmony_ci		trb->dwTrb2 = sc->sc_cmd_result[0];
1259f9f848faSopenharmony_ci		trb->dwTrb3 = sc->sc_cmd_result[1];
1260f9f848faSopenharmony_ci	}
1261f9f848faSopenharmony_ci
1262f9f848faSopenharmony_ci	USB_BUS_UNLOCK(&sc->sc_bus);
1263f9f848faSopenharmony_ci
1264f9f848faSopenharmony_ci	return (err);
1265f9f848faSopenharmony_ci}
1266f9f848faSopenharmony_ci
1267f9f848faSopenharmony_cistatic usb_error_t
1268f9f848faSopenharmony_cixhci_cmd_enable_slot(struct xhci_softc *sc, uint8_t *pslot)
1269f9f848faSopenharmony_ci{
1270f9f848faSopenharmony_ci	struct xhci_trb trb;
1271f9f848faSopenharmony_ci	uint32_t temp;
1272f9f848faSopenharmony_ci	usb_error_t err;
1273f9f848faSopenharmony_ci
1274f9f848faSopenharmony_ci	DPRINTF("\n");
1275f9f848faSopenharmony_ci
1276f9f848faSopenharmony_ci	trb.qwTrb0 = 0;
1277f9f848faSopenharmony_ci	trb.dwTrb2 = 0;
1278f9f848faSopenharmony_ci	trb.dwTrb3 = htole32(XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ENABLE_SLOT));
1279f9f848faSopenharmony_ci
1280f9f848faSopenharmony_ci	err = xhci_do_command(sc, &trb, XHCI_DO_CMD_TIMEOUT/* ms */);
1281f9f848faSopenharmony_ci	if (err)
1282f9f848faSopenharmony_ci		goto done;
1283f9f848faSopenharmony_ci
1284f9f848faSopenharmony_ci	temp = le32toh(trb.dwTrb3);
1285f9f848faSopenharmony_ci
1286f9f848faSopenharmony_ci	*pslot = XHCI_TRB_3_SLOT_GET(temp);
1287f9f848faSopenharmony_ci
1288f9f848faSopenharmony_cidone:
1289f9f848faSopenharmony_ci	return (err);
1290f9f848faSopenharmony_ci}
1291f9f848faSopenharmony_ci
1292f9f848faSopenharmony_cistatic usb_error_t
1293f9f848faSopenharmony_cixhci_cmd_disable_slot(struct xhci_softc *sc, uint8_t slot_id)
1294f9f848faSopenharmony_ci{
1295f9f848faSopenharmony_ci	struct xhci_trb trb;
1296f9f848faSopenharmony_ci	uint32_t temp;
1297f9f848faSopenharmony_ci
1298f9f848faSopenharmony_ci	DPRINTF("\n");
1299f9f848faSopenharmony_ci
1300f9f848faSopenharmony_ci	trb.qwTrb0 = 0;
1301f9f848faSopenharmony_ci	trb.dwTrb2 = 0;
1302f9f848faSopenharmony_ci	temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DISABLE_SLOT) |
1303f9f848faSopenharmony_ci	    XHCI_TRB_3_SLOT_SET(slot_id);
1304f9f848faSopenharmony_ci
1305f9f848faSopenharmony_ci	trb.dwTrb3 = htole32(temp);
1306f9f848faSopenharmony_ci
1307f9f848faSopenharmony_ci	return (xhci_do_command(sc, &trb, XHCI_DO_CMD_TIMEOUT/* ms */));
1308f9f848faSopenharmony_ci}
1309f9f848faSopenharmony_ci
1310f9f848faSopenharmony_cistatic usb_error_t
1311f9f848faSopenharmony_cixhci_cmd_set_address(struct xhci_softc *sc, uint64_t input_ctx,
1312f9f848faSopenharmony_ci    uint8_t bsr, uint8_t slot_id)
1313f9f848faSopenharmony_ci{
1314f9f848faSopenharmony_ci	struct xhci_trb trb;
1315f9f848faSopenharmony_ci	uint32_t temp;
1316f9f848faSopenharmony_ci
1317f9f848faSopenharmony_ci	DPRINTF("\n");
1318f9f848faSopenharmony_ci
1319f9f848faSopenharmony_ci	trb.qwTrb0 = htole64(input_ctx);
1320f9f848faSopenharmony_ci	trb.dwTrb2 = 0;
1321f9f848faSopenharmony_ci	temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ADDRESS_DEVICE) |
1322f9f848faSopenharmony_ci	    XHCI_TRB_3_SLOT_SET(slot_id);
1323f9f848faSopenharmony_ci
1324f9f848faSopenharmony_ci	if (bsr)
1325f9f848faSopenharmony_ci		temp |= XHCI_TRB_3_BSR_BIT;
1326f9f848faSopenharmony_ci
1327f9f848faSopenharmony_ci	trb.dwTrb3 = htole32(temp);
1328f9f848faSopenharmony_ci
1329f9f848faSopenharmony_ci	return (xhci_do_command(sc, &trb, XHCI_DO_CMD_TIMEOUT/* ms */));
1330f9f848faSopenharmony_ci}
1331f9f848faSopenharmony_ci
1332f9f848faSopenharmony_cistatic usb_error_t
1333f9f848faSopenharmony_cixhci_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t address)
1334f9f848faSopenharmony_ci{
1335f9f848faSopenharmony_ci	struct usb_page_search buf_inp;
1336f9f848faSopenharmony_ci	struct usb_page_search buf_dev;
1337f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
1338f9f848faSopenharmony_ci	struct xhci_hw_dev *hdev;
1339f9f848faSopenharmony_ci	struct xhci_dev_ctx *pdev;
1340f9f848faSopenharmony_ci	struct xhci_endpoint_ext *pepext;
1341f9f848faSopenharmony_ci	uint32_t temp;
1342f9f848faSopenharmony_ci	uint16_t mps;
1343f9f848faSopenharmony_ci	usb_error_t err;
1344f9f848faSopenharmony_ci	uint8_t index;
1345f9f848faSopenharmony_ci
1346f9f848faSopenharmony_ci	/* the root HUB case is not handled here */
1347f9f848faSopenharmony_ci	if (udev->parent_hub == NULL)
1348f9f848faSopenharmony_ci		return (USB_ERR_INVAL);
1349f9f848faSopenharmony_ci
1350f9f848faSopenharmony_ci	index = udev->controller_slot_id;
1351f9f848faSopenharmony_ci
1352f9f848faSopenharmony_ci	hdev = &sc->sc_hw.devs[index];
1353f9f848faSopenharmony_ci
1354f9f848faSopenharmony_ci	if (mtx != NULL)
1355f9f848faSopenharmony_ci		mtx_unlock(mtx);
1356f9f848faSopenharmony_ci
1357f9f848faSopenharmony_ci	XHCI_CMD_LOCK(sc);
1358f9f848faSopenharmony_ci
1359f9f848faSopenharmony_ci	switch (hdev->state) {
1360f9f848faSopenharmony_ci	case XHCI_ST_DEFAULT:
1361f9f848faSopenharmony_ci	case XHCI_ST_ENABLED:
1362f9f848faSopenharmony_ci
1363f9f848faSopenharmony_ci		hdev->state = XHCI_ST_ENABLED;
1364f9f848faSopenharmony_ci
1365f9f848faSopenharmony_ci		/* set configure mask to slot and EP0 */
1366f9f848faSopenharmony_ci		(void)xhci_configure_mask(udev, 3, 0);
1367f9f848faSopenharmony_ci
1368f9f848faSopenharmony_ci		/* configure input slot context structure */
1369f9f848faSopenharmony_ci		err = xhci_configure_device(udev);
1370f9f848faSopenharmony_ci
1371f9f848faSopenharmony_ci		if (err != 0) {
1372f9f848faSopenharmony_ci			DPRINTF("Could not configure device\n");
1373f9f848faSopenharmony_ci			break;
1374f9f848faSopenharmony_ci		}
1375f9f848faSopenharmony_ci
1376f9f848faSopenharmony_ci		/* configure input endpoint context structure */
1377f9f848faSopenharmony_ci		switch (udev->speed) {
1378f9f848faSopenharmony_ci		case USB_SPEED_LOW:
1379f9f848faSopenharmony_ci		case USB_SPEED_FULL:
1380f9f848faSopenharmony_ci			mps = 8;
1381f9f848faSopenharmony_ci			break;
1382f9f848faSopenharmony_ci		case USB_SPEED_HIGH:
1383f9f848faSopenharmony_ci			mps = 64;
1384f9f848faSopenharmony_ci			break;
1385f9f848faSopenharmony_ci		default:
1386f9f848faSopenharmony_ci			mps = 512;
1387f9f848faSopenharmony_ci			break;
1388f9f848faSopenharmony_ci		}
1389f9f848faSopenharmony_ci
1390f9f848faSopenharmony_ci		pepext = xhci_get_endpoint_ext(udev,
1391f9f848faSopenharmony_ci		    &udev->ctrl_ep_desc);
1392f9f848faSopenharmony_ci
1393f9f848faSopenharmony_ci		/* ensure the control endpoint is setup again */
1394f9f848faSopenharmony_ci		USB_BUS_LOCK(udev->bus);
1395f9f848faSopenharmony_ci		pepext->trb_halted = 1;
1396f9f848faSopenharmony_ci		pepext->trb_running = 0;
1397f9f848faSopenharmony_ci		USB_BUS_UNLOCK(udev->bus);
1398f9f848faSopenharmony_ci
1399f9f848faSopenharmony_ci		err = xhci_configure_endpoint(udev,
1400f9f848faSopenharmony_ci		    &udev->ctrl_ep_desc, pepext,
1401f9f848faSopenharmony_ci		    0, 1, 1, 0, mps, mps, USB_EP_MODE_DEFAULT);
1402f9f848faSopenharmony_ci
1403f9f848faSopenharmony_ci		if (err != 0) {
1404f9f848faSopenharmony_ci			DPRINTF("Could not configure default endpoint\n");
1405f9f848faSopenharmony_ci			break;
1406f9f848faSopenharmony_ci		}
1407f9f848faSopenharmony_ci
1408f9f848faSopenharmony_ci		/* execute set address command */
1409f9f848faSopenharmony_ci		usbd_get_page(&hdev->input_pc, 0, &buf_inp);
1410f9f848faSopenharmony_ci
1411f9f848faSopenharmony_ci		err = xhci_cmd_set_address(sc, buf_inp.physaddr,
1412f9f848faSopenharmony_ci		    (address == 0), index);
1413f9f848faSopenharmony_ci
1414f9f848faSopenharmony_ci		if (err != 0) {
1415f9f848faSopenharmony_ci			temp = le32toh(sc->sc_cmd_result[0]);
1416f9f848faSopenharmony_ci			if ((address == 0) && (sc->sc_port_route != NULL) &&
1417f9f848faSopenharmony_ci			    (XHCI_TRB_2_ERROR_GET(temp) ==
1418f9f848faSopenharmony_ci			    XHCI_TRB_ERROR_PARAMETER)) {
1419f9f848faSopenharmony_ci				/* LynxPoint XHCI - ports are not switchable */
1420f9f848faSopenharmony_ci				/* Un-route all ports from the XHCI */
1421f9f848faSopenharmony_ci				(void)sc->sc_port_route(sc->sc_bus.parent, 0, ~0);
1422f9f848faSopenharmony_ci			}
1423f9f848faSopenharmony_ci			DPRINTF("Could not set address "
1424f9f848faSopenharmony_ci			    "for slot %u.\n", index);
1425f9f848faSopenharmony_ci			if (address != 0)
1426f9f848faSopenharmony_ci				break;
1427f9f848faSopenharmony_ci		}
1428f9f848faSopenharmony_ci
1429f9f848faSopenharmony_ci		/* update device address to new value */
1430f9f848faSopenharmony_ci
1431f9f848faSopenharmony_ci		usbd_get_page(&hdev->device_pc, 0, &buf_dev);
1432f9f848faSopenharmony_ci		pdev = buf_dev.buffer;
1433f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(&hdev->device_pc);
1434f9f848faSopenharmony_ci
1435f9f848faSopenharmony_ci		temp = xhci_ctx_get_le32(sc, &pdev->ctx_slot.dwSctx3);
1436f9f848faSopenharmony_ci		udev->address = XHCI_SCTX_3_DEV_ADDR_GET(temp);
1437f9f848faSopenharmony_ci
1438f9f848faSopenharmony_ci		/* update device state to new value */
1439f9f848faSopenharmony_ci
1440f9f848faSopenharmony_ci		if (address != 0)
1441f9f848faSopenharmony_ci			hdev->state = XHCI_ST_ADDRESSED;
1442f9f848faSopenharmony_ci		else
1443f9f848faSopenharmony_ci			hdev->state = XHCI_ST_DEFAULT;
1444f9f848faSopenharmony_ci		break;
1445f9f848faSopenharmony_ci
1446f9f848faSopenharmony_ci	default:
1447f9f848faSopenharmony_ci		DPRINTF("Wrong state for set address.\n");
1448f9f848faSopenharmony_ci		err = USB_ERR_IOERROR;
1449f9f848faSopenharmony_ci		break;
1450f9f848faSopenharmony_ci	}
1451f9f848faSopenharmony_ci	XHCI_CMD_UNLOCK(sc);
1452f9f848faSopenharmony_ci
1453f9f848faSopenharmony_ci	if (mtx != NULL)
1454f9f848faSopenharmony_ci		mtx_lock(mtx);
1455f9f848faSopenharmony_ci
1456f9f848faSopenharmony_ci	return (err);
1457f9f848faSopenharmony_ci}
1458f9f848faSopenharmony_ci
1459f9f848faSopenharmony_cistatic usb_error_t
1460f9f848faSopenharmony_cixhci_cmd_configure_ep(struct xhci_softc *sc, uint64_t input_ctx,
1461f9f848faSopenharmony_ci    uint8_t deconfigure, uint8_t slot_id)
1462f9f848faSopenharmony_ci{
1463f9f848faSopenharmony_ci	struct xhci_trb trb;
1464f9f848faSopenharmony_ci	uint32_t temp;
1465f9f848faSopenharmony_ci
1466f9f848faSopenharmony_ci	DPRINTF("\n");
1467f9f848faSopenharmony_ci
1468f9f848faSopenharmony_ci	trb.qwTrb0 = htole64(input_ctx);
1469f9f848faSopenharmony_ci	trb.dwTrb2 = 0;
1470f9f848faSopenharmony_ci	temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_CONFIGURE_EP) |
1471f9f848faSopenharmony_ci	    XHCI_TRB_3_SLOT_SET(slot_id);
1472f9f848faSopenharmony_ci
1473f9f848faSopenharmony_ci	if (deconfigure)
1474f9f848faSopenharmony_ci		temp |= XHCI_TRB_3_DCEP_BIT;
1475f9f848faSopenharmony_ci
1476f9f848faSopenharmony_ci	trb.dwTrb3 = htole32(temp);
1477f9f848faSopenharmony_ci
1478f9f848faSopenharmony_ci	return (xhci_do_command(sc, &trb, XHCI_DO_CMD_TIMEOUT/* ms */));
1479f9f848faSopenharmony_ci}
1480f9f848faSopenharmony_ci
1481f9f848faSopenharmony_cistatic usb_error_t
1482f9f848faSopenharmony_cixhci_cmd_evaluate_ctx(struct xhci_softc *sc, uint64_t input_ctx,
1483f9f848faSopenharmony_ci    uint8_t slot_id)
1484f9f848faSopenharmony_ci{
1485f9f848faSopenharmony_ci	struct xhci_trb trb;
1486f9f848faSopenharmony_ci	uint32_t temp;
1487f9f848faSopenharmony_ci
1488f9f848faSopenharmony_ci	DPRINTF("\n");
1489f9f848faSopenharmony_ci
1490f9f848faSopenharmony_ci	trb.qwTrb0 = htole64(input_ctx);
1491f9f848faSopenharmony_ci	trb.dwTrb2 = 0;
1492f9f848faSopenharmony_ci	temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_EVALUATE_CTX) |
1493f9f848faSopenharmony_ci	    XHCI_TRB_3_SLOT_SET(slot_id);
1494f9f848faSopenharmony_ci	trb.dwTrb3 = htole32(temp);
1495f9f848faSopenharmony_ci
1496f9f848faSopenharmony_ci	return (xhci_do_command(sc, &trb, XHCI_DO_CMD_TIMEOUT/* ms */));
1497f9f848faSopenharmony_ci}
1498f9f848faSopenharmony_ci
1499f9f848faSopenharmony_cistatic usb_error_t
1500f9f848faSopenharmony_cixhci_cmd_reset_ep(struct xhci_softc *sc, uint8_t preserve,
1501f9f848faSopenharmony_ci    uint8_t ep_id, uint8_t slot_id)
1502f9f848faSopenharmony_ci{
1503f9f848faSopenharmony_ci	struct xhci_trb trb;
1504f9f848faSopenharmony_ci	uint32_t temp;
1505f9f848faSopenharmony_ci
1506f9f848faSopenharmony_ci	DPRINTF("\n");
1507f9f848faSopenharmony_ci
1508f9f848faSopenharmony_ci	trb.qwTrb0 = 0;
1509f9f848faSopenharmony_ci	trb.dwTrb2 = 0;
1510f9f848faSopenharmony_ci	temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_EP) |
1511f9f848faSopenharmony_ci	    XHCI_TRB_3_SLOT_SET(slot_id) |
1512f9f848faSopenharmony_ci	    XHCI_TRB_3_EP_SET(ep_id);
1513f9f848faSopenharmony_ci
1514f9f848faSopenharmony_ci	if (preserve)
1515f9f848faSopenharmony_ci		temp |= XHCI_TRB_3_PRSV_BIT;
1516f9f848faSopenharmony_ci
1517f9f848faSopenharmony_ci	trb.dwTrb3 = htole32(temp);
1518f9f848faSopenharmony_ci
1519f9f848faSopenharmony_ci	return (xhci_do_command(sc, &trb, XHCI_DO_CMD_TIMEOUT/* ms */));
1520f9f848faSopenharmony_ci}
1521f9f848faSopenharmony_ci
1522f9f848faSopenharmony_cistatic usb_error_t
1523f9f848faSopenharmony_cixhci_cmd_set_tr_dequeue_ptr(struct xhci_softc *sc, uint64_t dequeue_ptr,
1524f9f848faSopenharmony_ci    uint16_t stream_id, uint8_t ep_id, uint8_t slot_id)
1525f9f848faSopenharmony_ci{
1526f9f848faSopenharmony_ci	struct xhci_trb trb;
1527f9f848faSopenharmony_ci	uint32_t temp;
1528f9f848faSopenharmony_ci
1529f9f848faSopenharmony_ci	DPRINTF("\n");
1530f9f848faSopenharmony_ci
1531f9f848faSopenharmony_ci	trb.qwTrb0 = htole64(dequeue_ptr);
1532f9f848faSopenharmony_ci
1533f9f848faSopenharmony_ci	temp = XHCI_TRB_2_STREAM_SET(stream_id);
1534f9f848faSopenharmony_ci	trb.dwTrb2 = htole32(temp);
1535f9f848faSopenharmony_ci
1536f9f848faSopenharmony_ci	temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SET_TR_DEQUEUE) |
1537f9f848faSopenharmony_ci	    XHCI_TRB_3_SLOT_SET(slot_id) |
1538f9f848faSopenharmony_ci	    XHCI_TRB_3_EP_SET(ep_id);
1539f9f848faSopenharmony_ci	trb.dwTrb3 = htole32(temp);
1540f9f848faSopenharmony_ci
1541f9f848faSopenharmony_ci	return (xhci_do_command(sc, &trb, XHCI_DO_CMD_TIMEOUT/* ms */));
1542f9f848faSopenharmony_ci}
1543f9f848faSopenharmony_ci
1544f9f848faSopenharmony_cistatic usb_error_t
1545f9f848faSopenharmony_cixhci_cmd_stop_ep(struct xhci_softc *sc, uint8_t suspend,
1546f9f848faSopenharmony_ci    uint8_t ep_id, uint8_t slot_id)
1547f9f848faSopenharmony_ci{
1548f9f848faSopenharmony_ci	struct xhci_trb trb;
1549f9f848faSopenharmony_ci	uint32_t temp;
1550f9f848faSopenharmony_ci
1551f9f848faSopenharmony_ci	DPRINTF("\n");
1552f9f848faSopenharmony_ci
1553f9f848faSopenharmony_ci	trb.qwTrb0 = 0;
1554f9f848faSopenharmony_ci	trb.dwTrb2 = 0;
1555f9f848faSopenharmony_ci	temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STOP_EP) |
1556f9f848faSopenharmony_ci	    XHCI_TRB_3_SLOT_SET(slot_id) |
1557f9f848faSopenharmony_ci	    XHCI_TRB_3_EP_SET(ep_id);
1558f9f848faSopenharmony_ci
1559f9f848faSopenharmony_ci	if (suspend)
1560f9f848faSopenharmony_ci		temp |= XHCI_TRB_3_SUSP_EP_BIT;
1561f9f848faSopenharmony_ci
1562f9f848faSopenharmony_ci	trb.dwTrb3 = htole32(temp);
1563f9f848faSopenharmony_ci
1564f9f848faSopenharmony_ci	return (xhci_do_command(sc, &trb, XHCI_DO_CMD_TIMEOUT/* ms */));
1565f9f848faSopenharmony_ci}
1566f9f848faSopenharmony_ci
1567f9f848faSopenharmony_cistatic usb_error_t
1568f9f848faSopenharmony_cixhci_cmd_reset_dev(struct xhci_softc *sc, uint8_t slot_id)
1569f9f848faSopenharmony_ci{
1570f9f848faSopenharmony_ci	struct xhci_trb trb;
1571f9f848faSopenharmony_ci	uint32_t temp;
1572f9f848faSopenharmony_ci
1573f9f848faSopenharmony_ci	DPRINTF("\n");
1574f9f848faSopenharmony_ci
1575f9f848faSopenharmony_ci	trb.qwTrb0 = 0;
1576f9f848faSopenharmony_ci	trb.dwTrb2 = 0;
1577f9f848faSopenharmony_ci	temp = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_RESET_DEVICE) |
1578f9f848faSopenharmony_ci	    XHCI_TRB_3_SLOT_SET(slot_id);
1579f9f848faSopenharmony_ci
1580f9f848faSopenharmony_ci	trb.dwTrb3 = htole32(temp);
1581f9f848faSopenharmony_ci
1582f9f848faSopenharmony_ci	return (xhci_do_command(sc, &trb, XHCI_DO_CMD_TIMEOUT/* ms */));
1583f9f848faSopenharmony_ci}
1584f9f848faSopenharmony_ci
1585f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1586f9f848faSopenharmony_ci *	xhci_interrupt - XHCI interrupt handler
1587f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1588f9f848faSopenharmony_civoid
1589f9f848faSopenharmony_cixhci_interrupt(unsigned int irq, struct xhci_softc *sc)
1590f9f848faSopenharmony_ci{
1591f9f848faSopenharmony_ci	uint32_t status;
1592f9f848faSopenharmony_ci	uint32_t temp;
1593f9f848faSopenharmony_ci
1594f9f848faSopenharmony_ci	USB_BUS_LOCK(&sc->sc_bus);
1595f9f848faSopenharmony_ci
1596f9f848faSopenharmony_ci	status = XREAD4(sc, oper, XHCI_USBSTS);
1597f9f848faSopenharmony_ci
1598f9f848faSopenharmony_ci	/* acknowledge interrupts, if any */
1599f9f848faSopenharmony_ci	if (status != 0) {
1600f9f848faSopenharmony_ci		XWRITE4(sc, oper, XHCI_USBSTS, status);
1601f9f848faSopenharmony_ci		DPRINTFN(16, "real interrupt (status=0x%08x)\n", status);
1602f9f848faSopenharmony_ci	}
1603f9f848faSopenharmony_ci
1604f9f848faSopenharmony_ci	temp = XREAD4(sc, runt, XHCI_IMAN(0));
1605f9f848faSopenharmony_ci
1606f9f848faSopenharmony_ci	/* force clearing of pending interrupts */
1607f9f848faSopenharmony_ci	if (temp & XHCI_IMAN_INTR_PEND)
1608f9f848faSopenharmony_ci		XWRITE4(sc, runt, XHCI_IMAN(0), temp);
1609f9f848faSopenharmony_ci
1610f9f848faSopenharmony_ci	/* check for event(s) */
1611f9f848faSopenharmony_ci	(void)xhci_interrupt_poll(sc);
1612f9f848faSopenharmony_ci
1613f9f848faSopenharmony_ci	if (status & (XHCI_STS_PCD | XHCI_STS_HCH |
1614f9f848faSopenharmony_ci	    XHCI_STS_HSE | XHCI_STS_HCE)) {
1615f9f848faSopenharmony_ci		if (status & XHCI_STS_PCD) {
1616f9f848faSopenharmony_ci			xhci_root_intr(sc);
1617f9f848faSopenharmony_ci		}
1618f9f848faSopenharmony_ci
1619f9f848faSopenharmony_ci		if (status & XHCI_STS_HCH) {
1620f9f848faSopenharmony_ci			PRINTK("%s: host controller halted\n",
1621f9f848faSopenharmony_ci			    __FUNCTION__);
1622f9f848faSopenharmony_ci		}
1623f9f848faSopenharmony_ci
1624f9f848faSopenharmony_ci		if (status & XHCI_STS_HSE) {
1625f9f848faSopenharmony_ci			PRINTK("%s: host system error\n",
1626f9f848faSopenharmony_ci			    __FUNCTION__);
1627f9f848faSopenharmony_ci		}
1628f9f848faSopenharmony_ci
1629f9f848faSopenharmony_ci		if (status & XHCI_STS_HCE) {
1630f9f848faSopenharmony_ci			PRINTK("%s: host controller error\n",
1631f9f848faSopenharmony_ci			    __FUNCTION__);
1632f9f848faSopenharmony_ci		}
1633f9f848faSopenharmony_ci	}
1634f9f848faSopenharmony_ci	USB_BUS_UNLOCK(&sc->sc_bus);
1635f9f848faSopenharmony_ci}
1636f9f848faSopenharmony_ci
1637f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1638f9f848faSopenharmony_ci *	xhci_timeout - XHCI timeout handler
1639f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1640f9f848faSopenharmony_cistatic void
1641f9f848faSopenharmony_cixhci_timeout(void *arg)
1642f9f848faSopenharmony_ci{
1643f9f848faSopenharmony_ci	struct usb_xfer *xfer = arg;
1644f9f848faSopenharmony_ci
1645f9f848faSopenharmony_ci	DPRINTF("xfer=%p\n", xfer);
1646f9f848faSopenharmony_ci
1647f9f848faSopenharmony_ci	USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
1648f9f848faSopenharmony_ci
1649f9f848faSopenharmony_ci	/* transfer is transferred */
1650f9f848faSopenharmony_ci	xhci_device_done(xfer, USB_ERR_TIMEOUT);
1651f9f848faSopenharmony_ci}
1652f9f848faSopenharmony_ci
1653f9f848faSopenharmony_cistatic void
1654f9f848faSopenharmony_cixhci_do_poll(struct usb_bus *bus)
1655f9f848faSopenharmony_ci{
1656f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(bus);
1657f9f848faSopenharmony_ci
1658f9f848faSopenharmony_ci	USB_BUS_LOCK(&sc->sc_bus);
1659f9f848faSopenharmony_ci	(void)xhci_interrupt_poll(sc);
1660f9f848faSopenharmony_ci	USB_BUS_UNLOCK(&sc->sc_bus);
1661f9f848faSopenharmony_ci}
1662f9f848faSopenharmony_ci
1663f9f848faSopenharmony_cistatic void
1664f9f848faSopenharmony_cixhci_setup_generic_chain_sub(struct xhci_std_temp *temp)
1665f9f848faSopenharmony_ci{
1666f9f848faSopenharmony_ci	struct usb_page_search buf_res;
1667f9f848faSopenharmony_ci	struct xhci_td *td;
1668f9f848faSopenharmony_ci	struct xhci_td *td_next;
1669f9f848faSopenharmony_ci	struct xhci_td *td_alt_next;
1670f9f848faSopenharmony_ci	struct xhci_td *td_first;
1671f9f848faSopenharmony_ci	uint32_t buf_offset;
1672f9f848faSopenharmony_ci	uint32_t average;
1673f9f848faSopenharmony_ci	uint32_t len_old;
1674f9f848faSopenharmony_ci	uint32_t npkt_off;
1675f9f848faSopenharmony_ci	uint32_t dword;
1676f9f848faSopenharmony_ci	uint8_t shortpkt_old;
1677f9f848faSopenharmony_ci	uint8_t precompute;
1678f9f848faSopenharmony_ci	uint8_t x;
1679f9f848faSopenharmony_ci
1680f9f848faSopenharmony_ci	td_alt_next = NULL;
1681f9f848faSopenharmony_ci	buf_offset = 0;
1682f9f848faSopenharmony_ci	shortpkt_old = temp->shortpkt;
1683f9f848faSopenharmony_ci	len_old = temp->len;
1684f9f848faSopenharmony_ci	npkt_off = 0;
1685f9f848faSopenharmony_ci	precompute = 1;
1686f9f848faSopenharmony_ci
1687f9f848faSopenharmony_cirestart:
1688f9f848faSopenharmony_ci
1689f9f848faSopenharmony_ci	td = temp->td;
1690f9f848faSopenharmony_ci	td_next = td_first = temp->td_next;
1691f9f848faSopenharmony_ci
1692f9f848faSopenharmony_ci	while (1) {
1693f9f848faSopenharmony_ci		if (temp->len == 0) {
1694f9f848faSopenharmony_ci			if (temp->shortpkt)
1695f9f848faSopenharmony_ci				break;
1696f9f848faSopenharmony_ci
1697f9f848faSopenharmony_ci			/* send a Zero Length Packet, ZLP, last */
1698f9f848faSopenharmony_ci
1699f9f848faSopenharmony_ci			temp->shortpkt = 1;
1700f9f848faSopenharmony_ci			average = 0;
1701f9f848faSopenharmony_ci
1702f9f848faSopenharmony_ci		} else {
1703f9f848faSopenharmony_ci			average = temp->average;
1704f9f848faSopenharmony_ci
1705f9f848faSopenharmony_ci			if (temp->len < average) {
1706f9f848faSopenharmony_ci				if (temp->len % temp->max_packet_size) {
1707f9f848faSopenharmony_ci					temp->shortpkt = 1;
1708f9f848faSopenharmony_ci				}
1709f9f848faSopenharmony_ci				average = temp->len;
1710f9f848faSopenharmony_ci			}
1711f9f848faSopenharmony_ci		}
1712f9f848faSopenharmony_ci
1713f9f848faSopenharmony_ci		if (td_next == NULL)
1714f9f848faSopenharmony_ci			panic("%s: out of XHCI transfer descriptors!", __FUNCTION__);
1715f9f848faSopenharmony_ci
1716f9f848faSopenharmony_ci		/* get next TD */
1717f9f848faSopenharmony_ci
1718f9f848faSopenharmony_ci		td = td_next;
1719f9f848faSopenharmony_ci		td_next = td->obj_next;
1720f9f848faSopenharmony_ci
1721f9f848faSopenharmony_ci		/* check if we are pre-computing */
1722f9f848faSopenharmony_ci
1723f9f848faSopenharmony_ci		if (precompute) {
1724f9f848faSopenharmony_ci			/* update remaining length */
1725f9f848faSopenharmony_ci
1726f9f848faSopenharmony_ci			temp->len -= average;
1727f9f848faSopenharmony_ci
1728f9f848faSopenharmony_ci			continue;
1729f9f848faSopenharmony_ci		}
1730f9f848faSopenharmony_ci		/* fill out current TD */
1731f9f848faSopenharmony_ci
1732f9f848faSopenharmony_ci		td->len = average;
1733f9f848faSopenharmony_ci		td->remainder = 0;
1734f9f848faSopenharmony_ci		td->status = 0;
1735f9f848faSopenharmony_ci
1736f9f848faSopenharmony_ci		/* update remaining length */
1737f9f848faSopenharmony_ci
1738f9f848faSopenharmony_ci		temp->len -= average;
1739f9f848faSopenharmony_ci
1740f9f848faSopenharmony_ci		/* reset TRB index */
1741f9f848faSopenharmony_ci
1742f9f848faSopenharmony_ci		x = 0;
1743f9f848faSopenharmony_ci
1744f9f848faSopenharmony_ci		if (temp->trb_type == XHCI_TRB_TYPE_SETUP_STAGE) {
1745f9f848faSopenharmony_ci			/* immediate data */
1746f9f848faSopenharmony_ci
1747f9f848faSopenharmony_ci			if (average > 8)
1748f9f848faSopenharmony_ci				average = 8;
1749f9f848faSopenharmony_ci
1750f9f848faSopenharmony_ci			td->td_trb[0].qwTrb0 = 0;
1751f9f848faSopenharmony_ci
1752f9f848faSopenharmony_ci			usbd_copy_out(temp->pc, temp->offset + buf_offset,
1753f9f848faSopenharmony_ci			   (uint8_t *)(uintptr_t)&td->td_trb[0].qwTrb0,
1754f9f848faSopenharmony_ci			   average);
1755f9f848faSopenharmony_ci
1756f9f848faSopenharmony_ci			dword = XHCI_TRB_2_BYTES_SET(8) |
1757f9f848faSopenharmony_ci			    XHCI_TRB_2_TDSZ_SET(0) |
1758f9f848faSopenharmony_ci			    XHCI_TRB_2_IRQ_SET(0);
1759f9f848faSopenharmony_ci
1760f9f848faSopenharmony_ci			td->td_trb[0].dwTrb2 = htole32(dword);
1761f9f848faSopenharmony_ci
1762f9f848faSopenharmony_ci			dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_SETUP_STAGE) |
1763f9f848faSopenharmony_ci			    XHCI_TRB_3_IDT_BIT | XHCI_TRB_3_CYCLE_BIT;
1764f9f848faSopenharmony_ci
1765f9f848faSopenharmony_ci			/* check wLength */
1766f9f848faSopenharmony_ci			if (td->td_trb[0].qwTrb0 &
1767f9f848faSopenharmony_ci			    htole64(XHCI_TRB_0_WLENGTH_MASK)) {
1768f9f848faSopenharmony_ci				if (td->td_trb[0].qwTrb0 &
1769f9f848faSopenharmony_ci				    htole64(XHCI_TRB_0_DIR_IN_MASK))
1770f9f848faSopenharmony_ci					dword |= XHCI_TRB_3_TRT_IN;
1771f9f848faSopenharmony_ci				else
1772f9f848faSopenharmony_ci					dword |= XHCI_TRB_3_TRT_OUT;
1773f9f848faSopenharmony_ci			}
1774f9f848faSopenharmony_ci
1775f9f848faSopenharmony_ci			td->td_trb[0].dwTrb3 = htole32(dword);
1776f9f848faSopenharmony_ci#ifdef USB_DEBUG
1777f9f848faSopenharmony_ci			xhci_dump_trb(&td->td_trb[x]);
1778f9f848faSopenharmony_ci#endif
1779f9f848faSopenharmony_ci			x++;
1780f9f848faSopenharmony_ci
1781f9f848faSopenharmony_ci		} else do {
1782f9f848faSopenharmony_ci			uint32_t npkt;
1783f9f848faSopenharmony_ci
1784f9f848faSopenharmony_ci			/* fill out buffer pointers */
1785f9f848faSopenharmony_ci
1786f9f848faSopenharmony_ci			if (average == 0) {
1787f9f848faSopenharmony_ci				(void)memset_s(&buf_res, sizeof(buf_res), 0, sizeof(buf_res));
1788f9f848faSopenharmony_ci			} else {
1789f9f848faSopenharmony_ci				usbd_get_page(temp->pc, temp->offset +
1790f9f848faSopenharmony_ci				    buf_offset, &buf_res);
1791f9f848faSopenharmony_ci
1792f9f848faSopenharmony_ci				/* get length to end of page */
1793f9f848faSopenharmony_ci				if (buf_res.length > average)
1794f9f848faSopenharmony_ci					buf_res.length = average;
1795f9f848faSopenharmony_ci
1796f9f848faSopenharmony_ci				/* check for maximum length */
1797f9f848faSopenharmony_ci				if (buf_res.length > XHCI_TD_PAGE_SIZE)
1798f9f848faSopenharmony_ci					buf_res.length = XHCI_TD_PAGE_SIZE;
1799f9f848faSopenharmony_ci
1800f9f848faSopenharmony_ci				npkt_off += buf_res.length;
1801f9f848faSopenharmony_ci			}
1802f9f848faSopenharmony_ci
1803f9f848faSopenharmony_ci			/* set up npkt */
1804f9f848faSopenharmony_ci			npkt = (len_old - npkt_off + temp->max_packet_size - 1) /
1805f9f848faSopenharmony_ci			    temp->max_packet_size;
1806f9f848faSopenharmony_ci
1807f9f848faSopenharmony_ci			if (npkt == 0)
1808f9f848faSopenharmony_ci				npkt = 1;
1809f9f848faSopenharmony_ci			else if (npkt > 31)
1810f9f848faSopenharmony_ci				npkt = 31;
1811f9f848faSopenharmony_ci
1812f9f848faSopenharmony_ci			/* fill out TRB's */
1813f9f848faSopenharmony_ci			td->td_trb[x].qwTrb0 =
1814f9f848faSopenharmony_ci			    htole64((uint64_t)buf_res.physaddr);
1815f9f848faSopenharmony_ci
1816f9f848faSopenharmony_ci			dword =
1817f9f848faSopenharmony_ci			    XHCI_TRB_2_BYTES_SET(buf_res.length) |
1818f9f848faSopenharmony_ci			    XHCI_TRB_2_TDSZ_SET(npkt) |
1819f9f848faSopenharmony_ci			    XHCI_TRB_2_IRQ_SET(0);
1820f9f848faSopenharmony_ci
1821f9f848faSopenharmony_ci			td->td_trb[x].dwTrb2 = htole32(dword);
1822f9f848faSopenharmony_ci
1823f9f848faSopenharmony_ci			switch (temp->trb_type) {
1824f9f848faSopenharmony_ci			case XHCI_TRB_TYPE_ISOCH:
1825f9f848faSopenharmony_ci				dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT |
1826f9f848faSopenharmony_ci				    XHCI_TRB_3_TBC_SET(temp->tbc) |
1827f9f848faSopenharmony_ci				    XHCI_TRB_3_TLBPC_SET(temp->tlbpc);
1828f9f848faSopenharmony_ci				if (td != td_first) {
1829f9f848faSopenharmony_ci					dword |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL);
1830f9f848faSopenharmony_ci				} else if (temp->do_isoc_sync != 0) {
1831f9f848faSopenharmony_ci					temp->do_isoc_sync = 0;
1832f9f848faSopenharmony_ci					/* wait until "isoc_frame" */
1833f9f848faSopenharmony_ci					dword |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ISOCH) |
1834f9f848faSopenharmony_ci					    XHCI_TRB_3_FRID_SET(temp->isoc_frame / 8);
1835f9f848faSopenharmony_ci				} else {
1836f9f848faSopenharmony_ci					/* start data transfer at next interval */
1837f9f848faSopenharmony_ci					dword |= XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_ISOCH) |
1838f9f848faSopenharmony_ci					    XHCI_TRB_3_ISO_SIA_BIT;
1839f9f848faSopenharmony_ci				}
1840f9f848faSopenharmony_ci				if (temp->direction == UE_DIR_IN)
1841f9f848faSopenharmony_ci					dword |= XHCI_TRB_3_ISP_BIT;
1842f9f848faSopenharmony_ci				break;
1843f9f848faSopenharmony_ci			case XHCI_TRB_TYPE_DATA_STAGE:
1844f9f848faSopenharmony_ci				dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT |
1845f9f848faSopenharmony_ci				    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DATA_STAGE);
1846f9f848faSopenharmony_ci				if (temp->direction == UE_DIR_IN)
1847f9f848faSopenharmony_ci					dword |= XHCI_TRB_3_DIR_IN | XHCI_TRB_3_ISP_BIT;
1848f9f848faSopenharmony_ci				/*
1849f9f848faSopenharmony_ci				 * Section 3.2.9 in the XHCI
1850f9f848faSopenharmony_ci				 * specification about control
1851f9f848faSopenharmony_ci				 * transfers says that we should use a
1852f9f848faSopenharmony_ci				 * normal-TRB if there are more TRBs
1853f9f848faSopenharmony_ci				 * extending the data-stage
1854f9f848faSopenharmony_ci				 * TRB. Update the "trb_type".
1855f9f848faSopenharmony_ci				 */
1856f9f848faSopenharmony_ci				temp->trb_type = XHCI_TRB_TYPE_NORMAL;
1857f9f848faSopenharmony_ci				break;
1858f9f848faSopenharmony_ci			case XHCI_TRB_TYPE_STATUS_STAGE:
1859f9f848faSopenharmony_ci				dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT |
1860f9f848faSopenharmony_ci				    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_STATUS_STAGE);
1861f9f848faSopenharmony_ci				if (temp->direction == UE_DIR_IN)
1862f9f848faSopenharmony_ci					dword |= XHCI_TRB_3_DIR_IN;
1863f9f848faSopenharmony_ci				break;
1864f9f848faSopenharmony_ci			default:	/* XHCI_TRB_TYPE_NORMAL */
1865f9f848faSopenharmony_ci				dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT |
1866f9f848faSopenharmony_ci				    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_NORMAL);
1867f9f848faSopenharmony_ci				if (temp->direction == UE_DIR_IN)
1868f9f848faSopenharmony_ci					dword |= XHCI_TRB_3_ISP_BIT;
1869f9f848faSopenharmony_ci				break;
1870f9f848faSopenharmony_ci			}
1871f9f848faSopenharmony_ci			td->td_trb[x].dwTrb3 = htole32(dword);
1872f9f848faSopenharmony_ci
1873f9f848faSopenharmony_ci			average -= buf_res.length;
1874f9f848faSopenharmony_ci			buf_offset += buf_res.length;
1875f9f848faSopenharmony_ci#ifdef USB_DEBUG
1876f9f848faSopenharmony_ci			xhci_dump_trb(&td->td_trb[x]);
1877f9f848faSopenharmony_ci#endif
1878f9f848faSopenharmony_ci			x++;
1879f9f848faSopenharmony_ci
1880f9f848faSopenharmony_ci		} while (average != 0);
1881f9f848faSopenharmony_ci
1882f9f848faSopenharmony_ci		td->td_trb[x-1].dwTrb3 |= htole32(XHCI_TRB_3_IOC_BIT);
1883f9f848faSopenharmony_ci
1884f9f848faSopenharmony_ci		/* store number of data TRB's */
1885f9f848faSopenharmony_ci
1886f9f848faSopenharmony_ci		td->ntrb = x;
1887f9f848faSopenharmony_ci
1888f9f848faSopenharmony_ci		DPRINTF("NTRB=%u\n", x);
1889f9f848faSopenharmony_ci
1890f9f848faSopenharmony_ci		/* fill out link TRB */
1891f9f848faSopenharmony_ci
1892f9f848faSopenharmony_ci		if (td_next != NULL) {
1893f9f848faSopenharmony_ci			/* link the current TD with the next one */
1894f9f848faSopenharmony_ci			td->td_trb[x].qwTrb0 = htole64((uint64_t)td_next->td_self);
1895f9f848faSopenharmony_ci			DPRINTF("LINK=0x%08llx\n", (long long)td_next->td_self);
1896f9f848faSopenharmony_ci		} else {
1897f9f848faSopenharmony_ci			/* this field will get updated later */
1898f9f848faSopenharmony_ci			DPRINTF("NOLINK\n");
1899f9f848faSopenharmony_ci		}
1900f9f848faSopenharmony_ci
1901f9f848faSopenharmony_ci		dword = XHCI_TRB_2_IRQ_SET(0);
1902f9f848faSopenharmony_ci
1903f9f848faSopenharmony_ci		td->td_trb[x].dwTrb2 = htole32(dword);
1904f9f848faSopenharmony_ci
1905f9f848faSopenharmony_ci		dword = XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK) |
1906f9f848faSopenharmony_ci		    XHCI_TRB_3_CYCLE_BIT | XHCI_TRB_3_IOC_BIT |
1907f9f848faSopenharmony_ci		    /*
1908f9f848faSopenharmony_ci		     * CHAIN-BIT: Ensure that a multi-TRB IN-endpoint
1909f9f848faSopenharmony_ci		     * frame only receives a single short packet event
1910f9f848faSopenharmony_ci		     * by setting the CHAIN bit in the LINK field. In
1911f9f848faSopenharmony_ci		     * addition some XHCI controllers have problems
1912f9f848faSopenharmony_ci		     * sending a ZLP unless the CHAIN-BIT is set in
1913f9f848faSopenharmony_ci		     * the LINK TRB.
1914f9f848faSopenharmony_ci		     */
1915f9f848faSopenharmony_ci		    XHCI_TRB_3_CHAIN_BIT;
1916f9f848faSopenharmony_ci
1917f9f848faSopenharmony_ci		td->td_trb[x].dwTrb3 = htole32(dword);
1918f9f848faSopenharmony_ci
1919f9f848faSopenharmony_ci		td->alt_next = td_alt_next;
1920f9f848faSopenharmony_ci#ifdef USB_DEBUG
1921f9f848faSopenharmony_ci		xhci_dump_trb(&td->td_trb[x]);
1922f9f848faSopenharmony_ci#endif
1923f9f848faSopenharmony_ci		usb_pc_cpu_flush(td->page_cache);
1924f9f848faSopenharmony_ci	}
1925f9f848faSopenharmony_ci
1926f9f848faSopenharmony_ci	if (precompute) {
1927f9f848faSopenharmony_ci		precompute = 0;
1928f9f848faSopenharmony_ci
1929f9f848faSopenharmony_ci		/* set up alt next pointer, if any */
1930f9f848faSopenharmony_ci		if (temp->last_frame) {
1931f9f848faSopenharmony_ci			td_alt_next = NULL;
1932f9f848faSopenharmony_ci		} else {
1933f9f848faSopenharmony_ci			/* we use this field internally */
1934f9f848faSopenharmony_ci			td_alt_next = td_next;
1935f9f848faSopenharmony_ci		}
1936f9f848faSopenharmony_ci
1937f9f848faSopenharmony_ci		/* restore */
1938f9f848faSopenharmony_ci		temp->shortpkt = shortpkt_old;
1939f9f848faSopenharmony_ci		temp->len = len_old;
1940f9f848faSopenharmony_ci		goto restart;
1941f9f848faSopenharmony_ci	}
1942f9f848faSopenharmony_ci
1943f9f848faSopenharmony_ci	/*
1944f9f848faSopenharmony_ci	 * Remove cycle bit from the first TRB if we are
1945f9f848faSopenharmony_ci	 * stepping them:
1946f9f848faSopenharmony_ci	 */
1947f9f848faSopenharmony_ci	if (temp->step_td != 0) {
1948f9f848faSopenharmony_ci		td_first->td_trb[0].dwTrb3 &= ~htole32(XHCI_TRB_3_CYCLE_BIT);
1949f9f848faSopenharmony_ci		usb_pc_cpu_flush(td_first->page_cache);
1950f9f848faSopenharmony_ci	}
1951f9f848faSopenharmony_ci
1952f9f848faSopenharmony_ci	/* clear TD SIZE to zero, hence this is the last TRB */
1953f9f848faSopenharmony_ci	/* remove chain bit because this is the last data TRB in the chain */
1954f9f848faSopenharmony_ci	td->td_trb[td->ntrb - 1].dwTrb2 &= ~htole32(XHCI_TRB_2_TDSZ_SET(15));
1955f9f848faSopenharmony_ci	td->td_trb[td->ntrb - 1].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT);
1956f9f848faSopenharmony_ci	/* remove CHAIN-BIT from last LINK TRB */
1957f9f848faSopenharmony_ci	td->td_trb[td->ntrb].dwTrb3 &= ~htole32(XHCI_TRB_3_CHAIN_BIT);
1958f9f848faSopenharmony_ci
1959f9f848faSopenharmony_ci	usb_pc_cpu_flush(td->page_cache);
1960f9f848faSopenharmony_ci
1961f9f848faSopenharmony_ci	temp->td = td;
1962f9f848faSopenharmony_ci	temp->td_next = td_next;
1963f9f848faSopenharmony_ci}
1964f9f848faSopenharmony_ci
1965f9f848faSopenharmony_cistatic void
1966f9f848faSopenharmony_cixhci_setup_generic_chain(struct usb_xfer *xfer)
1967f9f848faSopenharmony_ci{
1968f9f848faSopenharmony_ci	struct xhci_std_temp temp;
1969f9f848faSopenharmony_ci	struct xhci_td *td;
1970f9f848faSopenharmony_ci	uint32_t x;
1971f9f848faSopenharmony_ci	uint32_t y;
1972f9f848faSopenharmony_ci	uint8_t mult;
1973f9f848faSopenharmony_ci
1974f9f848faSopenharmony_ci	temp.do_isoc_sync = 0;
1975f9f848faSopenharmony_ci	temp.step_td = 0;
1976f9f848faSopenharmony_ci	temp.tbc = 0;
1977f9f848faSopenharmony_ci	temp.tlbpc = 0;
1978f9f848faSopenharmony_ci	temp.average = xfer->max_hc_frame_size;
1979f9f848faSopenharmony_ci	temp.max_packet_size = xfer->max_packet_size;
1980f9f848faSopenharmony_ci	temp.sc = XHCI_BUS2SC(xfer->xroot->bus);
1981f9f848faSopenharmony_ci	temp.pc = NULL;
1982f9f848faSopenharmony_ci	temp.last_frame = 0;
1983f9f848faSopenharmony_ci	temp.offset = 0;
1984f9f848faSopenharmony_ci	temp.multishort = xfer->flags_int.isochronous_xfr ||
1985f9f848faSopenharmony_ci	    xfer->flags_int.control_xfr ||
1986f9f848faSopenharmony_ci	    xfer->flags_int.short_frames_ok;
1987f9f848faSopenharmony_ci
1988f9f848faSopenharmony_ci	/* toggle the DMA set we are using */
1989f9f848faSopenharmony_ci	xfer->flags_int.curr_dma_set ^= 1;
1990f9f848faSopenharmony_ci
1991f9f848faSopenharmony_ci	/* get next DMA set */
1992f9f848faSopenharmony_ci	td = xfer->td_start[xfer->flags_int.curr_dma_set];
1993f9f848faSopenharmony_ci
1994f9f848faSopenharmony_ci	temp.td = NULL;
1995f9f848faSopenharmony_ci	temp.td_next = td;
1996f9f848faSopenharmony_ci
1997f9f848faSopenharmony_ci	xfer->td_transfer_first = td;
1998f9f848faSopenharmony_ci	xfer->td_transfer_cache = td;
1999f9f848faSopenharmony_ci
2000f9f848faSopenharmony_ci	if (xfer->flags_int.isochronous_xfr) {
2001f9f848faSopenharmony_ci		uint8_t shift;
2002f9f848faSopenharmony_ci
2003f9f848faSopenharmony_ci		/* compute multiplier for ISOCHRONOUS transfers */
2004f9f848faSopenharmony_ci		mult = xfer->endpoint->ecomp ?
2005f9f848faSopenharmony_ci		    UE_GET_SS_ISO_MULT(xfer->endpoint->ecomp->bmAttributes)
2006f9f848faSopenharmony_ci		    : 0;
2007f9f848faSopenharmony_ci		/* check for USB 2.0 multiplier */
2008f9f848faSopenharmony_ci		if (mult == 0) {
2009f9f848faSopenharmony_ci			mult = (xfer->endpoint->edesc->
2010f9f848faSopenharmony_ci			    wMaxPacketSize[1] >> 3) & 3;
2011f9f848faSopenharmony_ci		}
2012f9f848faSopenharmony_ci		/* range check */
2013f9f848faSopenharmony_ci		if (mult > 2)
2014f9f848faSopenharmony_ci			mult = 3;
2015f9f848faSopenharmony_ci		else
2016f9f848faSopenharmony_ci			mult++;
2017f9f848faSopenharmony_ci
2018f9f848faSopenharmony_ci		x = XREAD4(temp.sc, runt, XHCI_MFINDEX);
2019f9f848faSopenharmony_ci
2020f9f848faSopenharmony_ci		DPRINTF("MFINDEX=0x%08x\n", x);
2021f9f848faSopenharmony_ci
2022f9f848faSopenharmony_ci		switch (usbd_get_speed(xfer->xroot->udev)) {
2023f9f848faSopenharmony_ci		case USB_SPEED_FULL:
2024f9f848faSopenharmony_ci			shift = 3;
2025f9f848faSopenharmony_ci			temp.isoc_delta = 8;	/* 1ms */
2026f9f848faSopenharmony_ci			x += temp.isoc_delta - 1;
2027f9f848faSopenharmony_ci			x &= ~(temp.isoc_delta - 1);
2028f9f848faSopenharmony_ci			break;
2029f9f848faSopenharmony_ci		default:
2030f9f848faSopenharmony_ci			shift = usbd_xfer_get_fps_shift(xfer);
2031f9f848faSopenharmony_ci			temp.isoc_delta = 1U << shift;
2032f9f848faSopenharmony_ci			x += temp.isoc_delta - 1;
2033f9f848faSopenharmony_ci			x &= ~(temp.isoc_delta - 1);
2034f9f848faSopenharmony_ci			/* simple frame load balancing */
2035f9f848faSopenharmony_ci			x += xfer->endpoint->usb_uframe;
2036f9f848faSopenharmony_ci			break;
2037f9f848faSopenharmony_ci		}
2038f9f848faSopenharmony_ci
2039f9f848faSopenharmony_ci		y = XHCI_MFINDEX_GET(x - xfer->endpoint->isoc_next);
2040f9f848faSopenharmony_ci
2041f9f848faSopenharmony_ci		if ((xfer->endpoint->is_synced == 0) ||
2042f9f848faSopenharmony_ci		    (y < (xfer->nframes << shift)) ||
2043f9f848faSopenharmony_ci		    (XHCI_MFINDEX_GET(-y) >= (128 * 8))) {
2044f9f848faSopenharmony_ci			/*
2045f9f848faSopenharmony_ci			 * If there is data underflow or the pipe
2046f9f848faSopenharmony_ci			 * queue is empty we schedule the transfer a
2047f9f848faSopenharmony_ci			 * few frames ahead of the current frame
2048f9f848faSopenharmony_ci			 * position. Else two isochronous transfers
2049f9f848faSopenharmony_ci			 * might overlap.
2050f9f848faSopenharmony_ci			 */
2051f9f848faSopenharmony_ci			xfer->endpoint->isoc_next = XHCI_MFINDEX_GET(x + (3 * 8));
2052f9f848faSopenharmony_ci			xfer->endpoint->is_synced = 1;
2053f9f848faSopenharmony_ci			temp.do_isoc_sync = 1;
2054f9f848faSopenharmony_ci
2055f9f848faSopenharmony_ci			DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
2056f9f848faSopenharmony_ci		}
2057f9f848faSopenharmony_ci
2058f9f848faSopenharmony_ci		/* compute isochronous completion time */
2059f9f848faSopenharmony_ci
2060f9f848faSopenharmony_ci		y = XHCI_MFINDEX_GET(xfer->endpoint->isoc_next - (x & ~7));
2061f9f848faSopenharmony_ci
2062f9f848faSopenharmony_ci		xfer->isoc_time_complete =
2063f9f848faSopenharmony_ci		    usb_isoc_time_expand(&temp.sc->sc_bus, x / 8) +
2064f9f848faSopenharmony_ci		    (y / 8) + (((xfer->nframes << shift) + 7) / 8);
2065f9f848faSopenharmony_ci
2066f9f848faSopenharmony_ci		x = 0;
2067f9f848faSopenharmony_ci		temp.isoc_frame = xfer->endpoint->isoc_next;
2068f9f848faSopenharmony_ci		temp.trb_type = XHCI_TRB_TYPE_ISOCH;
2069f9f848faSopenharmony_ci
2070f9f848faSopenharmony_ci		xfer->endpoint->isoc_next += xfer->nframes << shift;
2071f9f848faSopenharmony_ci
2072f9f848faSopenharmony_ci	} else if (xfer->flags_int.control_xfr) {
2073f9f848faSopenharmony_ci		/* check if we should prepend a setup message */
2074f9f848faSopenharmony_ci
2075f9f848faSopenharmony_ci		if (xfer->flags_int.control_hdr) {
2076f9f848faSopenharmony_ci			temp.len = xfer->frlengths[0];
2077f9f848faSopenharmony_ci			temp.pc = xfer->frbuffers + 0;
2078f9f848faSopenharmony_ci			temp.shortpkt = temp.len ? 1 : 0;
2079f9f848faSopenharmony_ci			temp.trb_type = XHCI_TRB_TYPE_SETUP_STAGE;
2080f9f848faSopenharmony_ci			temp.direction = 0;
2081f9f848faSopenharmony_ci
2082f9f848faSopenharmony_ci			/* check for last frame */
2083f9f848faSopenharmony_ci			if (xfer->nframes == 1) {
2084f9f848faSopenharmony_ci				/* no STATUS stage yet, SETUP is last */
2085f9f848faSopenharmony_ci				if (xfer->flags_int.control_act)
2086f9f848faSopenharmony_ci					temp.last_frame = 1;
2087f9f848faSopenharmony_ci			}
2088f9f848faSopenharmony_ci
2089f9f848faSopenharmony_ci			xhci_setup_generic_chain_sub(&temp);
2090f9f848faSopenharmony_ci		}
2091f9f848faSopenharmony_ci		x = 1;
2092f9f848faSopenharmony_ci		mult = 1;
2093f9f848faSopenharmony_ci		temp.isoc_delta = 0;
2094f9f848faSopenharmony_ci		temp.isoc_frame = 0;
2095f9f848faSopenharmony_ci		temp.trb_type = xfer->flags_int.control_did_data ?
2096f9f848faSopenharmony_ci		    XHCI_TRB_TYPE_NORMAL : XHCI_TRB_TYPE_DATA_STAGE;
2097f9f848faSopenharmony_ci	} else {
2098f9f848faSopenharmony_ci		x = 0;
2099f9f848faSopenharmony_ci		mult = 1;
2100f9f848faSopenharmony_ci		temp.isoc_delta = 0;
2101f9f848faSopenharmony_ci		temp.isoc_frame = 0;
2102f9f848faSopenharmony_ci		temp.trb_type = XHCI_TRB_TYPE_NORMAL;
2103f9f848faSopenharmony_ci	}
2104f9f848faSopenharmony_ci
2105f9f848faSopenharmony_ci	if (x != xfer->nframes) {
2106f9f848faSopenharmony_ci		/* set up page_cache pointer */
2107f9f848faSopenharmony_ci		temp.pc = xfer->frbuffers + x;
2108f9f848faSopenharmony_ci		/* set endpoint direction */
2109f9f848faSopenharmony_ci		temp.direction = UE_GET_DIR(xfer->endpointno);
2110f9f848faSopenharmony_ci	}
2111f9f848faSopenharmony_ci
2112f9f848faSopenharmony_ci	while (x != xfer->nframes) {
2113f9f848faSopenharmony_ci		/* DATA0 / DATA1 message */
2114f9f848faSopenharmony_ci
2115f9f848faSopenharmony_ci		temp.len = xfer->frlengths[x];
2116f9f848faSopenharmony_ci		temp.step_td = ((xfer->endpointno & UE_DIR_IN) &&
2117f9f848faSopenharmony_ci		    x != 0 && temp.multishort == 0);
2118f9f848faSopenharmony_ci
2119f9f848faSopenharmony_ci		x++;
2120f9f848faSopenharmony_ci
2121f9f848faSopenharmony_ci		if (x == xfer->nframes) {
2122f9f848faSopenharmony_ci			if (xfer->flags_int.control_xfr) {
2123f9f848faSopenharmony_ci				/* no STATUS stage yet, DATA is last */
2124f9f848faSopenharmony_ci				if (xfer->flags_int.control_act)
2125f9f848faSopenharmony_ci					temp.last_frame = 1;
2126f9f848faSopenharmony_ci			} else {
2127f9f848faSopenharmony_ci				temp.last_frame = 1;
2128f9f848faSopenharmony_ci			}
2129f9f848faSopenharmony_ci		}
2130f9f848faSopenharmony_ci		if (temp.len == 0) {
2131f9f848faSopenharmony_ci			/* make sure that we send an USB packet */
2132f9f848faSopenharmony_ci
2133f9f848faSopenharmony_ci			temp.shortpkt = 0;
2134f9f848faSopenharmony_ci
2135f9f848faSopenharmony_ci			temp.tbc = 0;
2136f9f848faSopenharmony_ci			temp.tlbpc = mult - 1;
2137f9f848faSopenharmony_ci
2138f9f848faSopenharmony_ci		} else if (xfer->flags_int.isochronous_xfr) {
2139f9f848faSopenharmony_ci			uint8_t tdpc;
2140f9f848faSopenharmony_ci
2141f9f848faSopenharmony_ci			/*
2142f9f848faSopenharmony_ci			 * Isochronous transfers don't have short
2143f9f848faSopenharmony_ci			 * packet termination:
2144f9f848faSopenharmony_ci			 */
2145f9f848faSopenharmony_ci
2146f9f848faSopenharmony_ci			temp.shortpkt = 1;
2147f9f848faSopenharmony_ci
2148f9f848faSopenharmony_ci			/* isochronous transfers have a transfer limit */
2149f9f848faSopenharmony_ci
2150f9f848faSopenharmony_ci			if (temp.len > xfer->max_frame_size)
2151f9f848faSopenharmony_ci				temp.len = xfer->max_frame_size;
2152f9f848faSopenharmony_ci
2153f9f848faSopenharmony_ci			/* compute TD packet count */
2154f9f848faSopenharmony_ci			tdpc = (temp.len + xfer->max_packet_size - 1) /
2155f9f848faSopenharmony_ci				xfer->max_packet_size;
2156f9f848faSopenharmony_ci
2157f9f848faSopenharmony_ci			temp.tbc = ((tdpc + mult - 1) / mult) - 1;
2158f9f848faSopenharmony_ci			temp.tlbpc = (tdpc % mult);
2159f9f848faSopenharmony_ci
2160f9f848faSopenharmony_ci			if (temp.tlbpc == 0)
2161f9f848faSopenharmony_ci				temp.tlbpc = mult - 1;
2162f9f848faSopenharmony_ci			else
2163f9f848faSopenharmony_ci				temp.tlbpc--;
2164f9f848faSopenharmony_ci		} else {
2165f9f848faSopenharmony_ci			/* regular data transfer */
2166f9f848faSopenharmony_ci
2167f9f848faSopenharmony_ci			temp.shortpkt = xfer->flags.force_short_xfer ? 0 : 1;
2168f9f848faSopenharmony_ci		}
2169f9f848faSopenharmony_ci
2170f9f848faSopenharmony_ci		xhci_setup_generic_chain_sub(&temp);
2171f9f848faSopenharmony_ci
2172f9f848faSopenharmony_ci		if (xfer->flags_int.isochronous_xfr) {
2173f9f848faSopenharmony_ci			temp.offset += xfer->frlengths[x - 1];
2174f9f848faSopenharmony_ci			temp.isoc_frame += temp.isoc_delta;
2175f9f848faSopenharmony_ci		} else {
2176f9f848faSopenharmony_ci			/* get next Page Cache pointer */
2177f9f848faSopenharmony_ci			temp.pc = xfer->frbuffers + x;
2178f9f848faSopenharmony_ci		}
2179f9f848faSopenharmony_ci	}
2180f9f848faSopenharmony_ci
2181f9f848faSopenharmony_ci	/* check if we should append a status stage */
2182f9f848faSopenharmony_ci
2183f9f848faSopenharmony_ci	if (xfer->flags_int.control_xfr &&
2184f9f848faSopenharmony_ci	    !xfer->flags_int.control_act) {
2185f9f848faSopenharmony_ci		/*
2186f9f848faSopenharmony_ci		 * Send a DATA1 message and invert the current
2187f9f848faSopenharmony_ci		 * endpoint direction.
2188f9f848faSopenharmony_ci		 */
2189f9f848faSopenharmony_ci		if (xhcictlstep || temp.sc->sc_ctlstep) {
2190f9f848faSopenharmony_ci			/*
2191f9f848faSopenharmony_ci			 * Some XHCI controllers will not delay the
2192f9f848faSopenharmony_ci			 * status stage until the next SOF. Force this
2193f9f848faSopenharmony_ci			 * behaviour to avoid failed control
2194f9f848faSopenharmony_ci			 * transfers.
2195f9f848faSopenharmony_ci			 */
2196f9f848faSopenharmony_ci			temp.step_td = (xfer->nframes != 0);
2197f9f848faSopenharmony_ci		} else {
2198f9f848faSopenharmony_ci			temp.step_td = 0;
2199f9f848faSopenharmony_ci		}
2200f9f848faSopenharmony_ci		temp.direction = UE_GET_DIR(xfer->endpointno) ^ UE_DIR_IN;
2201f9f848faSopenharmony_ci		temp.len = 0;
2202f9f848faSopenharmony_ci		temp.pc = NULL;
2203f9f848faSopenharmony_ci		temp.shortpkt = 0;
2204f9f848faSopenharmony_ci		temp.last_frame = 1;
2205f9f848faSopenharmony_ci		temp.trb_type = XHCI_TRB_TYPE_STATUS_STAGE;
2206f9f848faSopenharmony_ci
2207f9f848faSopenharmony_ci		xhci_setup_generic_chain_sub(&temp);
2208f9f848faSopenharmony_ci	}
2209f9f848faSopenharmony_ci
2210f9f848faSopenharmony_ci	td = temp.td;
2211f9f848faSopenharmony_ci
2212f9f848faSopenharmony_ci	/* must have at least one frame! */
2213f9f848faSopenharmony_ci
2214f9f848faSopenharmony_ci	xfer->td_transfer_last = td;
2215f9f848faSopenharmony_ci
2216f9f848faSopenharmony_ci	DPRINTF("first=%p last=%p\n", xfer->td_transfer_first, td);
2217f9f848faSopenharmony_ci}
2218f9f848faSopenharmony_ci
2219f9f848faSopenharmony_cistatic void
2220f9f848faSopenharmony_cixhci_set_slot_pointer(struct xhci_softc *sc, uint8_t index, uint64_t dev_addr)
2221f9f848faSopenharmony_ci{
2222f9f848faSopenharmony_ci	struct usb_page_search buf_res;
2223f9f848faSopenharmony_ci	struct xhci_dev_ctx_addr *pdctxa;
2224f9f848faSopenharmony_ci
2225f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.ctx_pc, 0, &buf_res);
2226f9f848faSopenharmony_ci
2227f9f848faSopenharmony_ci	pdctxa = buf_res.buffer;
2228f9f848faSopenharmony_ci
2229f9f848faSopenharmony_ci	DPRINTF("addr[%u]=0x%016llx\n", index, (long long)dev_addr);
2230f9f848faSopenharmony_ci
2231f9f848faSopenharmony_ci	pdctxa->qwBaaDevCtxAddr[index] = htole64(dev_addr);
2232f9f848faSopenharmony_ci
2233f9f848faSopenharmony_ci	usb_pc_cpu_flush(&sc->sc_hw.ctx_pc);
2234f9f848faSopenharmony_ci}
2235f9f848faSopenharmony_ci
2236f9f848faSopenharmony_cistatic usb_error_t
2237f9f848faSopenharmony_cixhci_configure_mask(struct usb_device *udev, uint32_t mask, uint8_t drop)
2238f9f848faSopenharmony_ci{
2239f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
2240f9f848faSopenharmony_ci	struct usb_page_search buf_inp;
2241f9f848faSopenharmony_ci	struct xhci_input_dev_ctx *pinp;
2242f9f848faSopenharmony_ci	uint32_t temp;
2243f9f848faSopenharmony_ci	uint8_t index;
2244f9f848faSopenharmony_ci	uint8_t x;
2245f9f848faSopenharmony_ci
2246f9f848faSopenharmony_ci	index = udev->controller_slot_id;
2247f9f848faSopenharmony_ci
2248f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp);
2249f9f848faSopenharmony_ci
2250f9f848faSopenharmony_ci	pinp = buf_inp.buffer;
2251f9f848faSopenharmony_ci
2252f9f848faSopenharmony_ci	if (drop) {
2253f9f848faSopenharmony_ci		mask &= XHCI_INCTX_NON_CTRL_MASK;
2254f9f848faSopenharmony_ci		xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx0, mask);
2255f9f848faSopenharmony_ci		xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx1, 0);
2256f9f848faSopenharmony_ci	} else {
2257f9f848faSopenharmony_ci		/*
2258f9f848faSopenharmony_ci		 * Some hardware requires that we drop the endpoint
2259f9f848faSopenharmony_ci		 * context before adding it again:
2260f9f848faSopenharmony_ci		 */
2261f9f848faSopenharmony_ci		xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx0,
2262f9f848faSopenharmony_ci		    mask & XHCI_INCTX_NON_CTRL_MASK);
2263f9f848faSopenharmony_ci
2264f9f848faSopenharmony_ci		/* Add new endpoint context */
2265f9f848faSopenharmony_ci		xhci_ctx_set_le32(sc, &pinp->ctx_input.dwInCtx1, mask);
2266f9f848faSopenharmony_ci
2267f9f848faSopenharmony_ci		/* find most significant set bit */
2268f9f848faSopenharmony_ci		for (x = 31; x != 1; x--) {
2269f9f848faSopenharmony_ci			if (mask & (1 << x))
2270f9f848faSopenharmony_ci				break;
2271f9f848faSopenharmony_ci		}
2272f9f848faSopenharmony_ci
2273f9f848faSopenharmony_ci		/* adjust */
2274f9f848faSopenharmony_ci		x--;
2275f9f848faSopenharmony_ci
2276f9f848faSopenharmony_ci		/* figure out the maximum number of contexts */
2277f9f848faSopenharmony_ci		if (x > sc->sc_hw.devs[index].context_num)
2278f9f848faSopenharmony_ci			sc->sc_hw.devs[index].context_num = x;
2279f9f848faSopenharmony_ci		else
2280f9f848faSopenharmony_ci			x = sc->sc_hw.devs[index].context_num;
2281f9f848faSopenharmony_ci
2282f9f848faSopenharmony_ci		/* update number of contexts */
2283f9f848faSopenharmony_ci		temp = xhci_ctx_get_le32(sc, &pinp->ctx_slot.dwSctx0);
2284f9f848faSopenharmony_ci		temp &= ~XHCI_SCTX_0_CTX_NUM_SET(31);
2285f9f848faSopenharmony_ci		temp |= XHCI_SCTX_0_CTX_NUM_SET(x + 1);
2286f9f848faSopenharmony_ci		xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx0, temp);
2287f9f848faSopenharmony_ci	}
2288f9f848faSopenharmony_ci	usb_pc_cpu_flush(&sc->sc_hw.devs[index].input_pc);
2289f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
2290f9f848faSopenharmony_ci}
2291f9f848faSopenharmony_ci
2292f9f848faSopenharmony_cistatic usb_error_t
2293f9f848faSopenharmony_cixhci_configure_endpoint(struct usb_device *udev,
2294f9f848faSopenharmony_ci    struct usb_endpoint_descriptor *edesc, struct xhci_endpoint_ext *pepext,
2295f9f848faSopenharmony_ci    uint16_t interval, uint8_t max_packet_count,
2296f9f848faSopenharmony_ci    uint8_t mult, uint8_t fps_shift, uint16_t max_packet_size,
2297f9f848faSopenharmony_ci    uint16_t max_frame_size, uint8_t ep_mode)
2298f9f848faSopenharmony_ci{
2299f9f848faSopenharmony_ci	struct usb_page_search buf_inp;
2300f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
2301f9f848faSopenharmony_ci	struct xhci_input_dev_ctx *pinp;
2302f9f848faSopenharmony_ci	uint64_t ring_addr = pepext->physaddr;
2303f9f848faSopenharmony_ci	uint32_t temp;
2304f9f848faSopenharmony_ci	uint8_t index;
2305f9f848faSopenharmony_ci	uint8_t epno;
2306f9f848faSopenharmony_ci	uint8_t type;
2307f9f848faSopenharmony_ci
2308f9f848faSopenharmony_ci	index = udev->controller_slot_id;
2309f9f848faSopenharmony_ci
2310f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp);
2311f9f848faSopenharmony_ci
2312f9f848faSopenharmony_ci	pinp = buf_inp.buffer;
2313f9f848faSopenharmony_ci
2314f9f848faSopenharmony_ci	epno = edesc->bEndpointAddress;
2315f9f848faSopenharmony_ci	type = edesc->bmAttributes & UE_XFERTYPE;
2316f9f848faSopenharmony_ci
2317f9f848faSopenharmony_ci	if (type == UE_CONTROL)
2318f9f848faSopenharmony_ci		epno |= UE_DIR_IN;
2319f9f848faSopenharmony_ci
2320f9f848faSopenharmony_ci	epno = XHCI_EPNO2EPID(epno);
2321f9f848faSopenharmony_ci
2322f9f848faSopenharmony_ci	if (epno == 0)
2323f9f848faSopenharmony_ci		return (USB_ERR_NO_PIPE);		/* invalid */
2324f9f848faSopenharmony_ci
2325f9f848faSopenharmony_ci	if (max_packet_count == 0)
2326f9f848faSopenharmony_ci		return (USB_ERR_BAD_BUFSIZE);
2327f9f848faSopenharmony_ci
2328f9f848faSopenharmony_ci	max_packet_count--;
2329f9f848faSopenharmony_ci
2330f9f848faSopenharmony_ci	if (mult == 0)
2331f9f848faSopenharmony_ci		return (USB_ERR_BAD_BUFSIZE);
2332f9f848faSopenharmony_ci
2333f9f848faSopenharmony_ci	/* store endpoint mode */
2334f9f848faSopenharmony_ci	pepext->trb_ep_mode = ep_mode;
2335f9f848faSopenharmony_ci	/* store bMaxPacketSize for control endpoints */
2336f9f848faSopenharmony_ci	pepext->trb_ep_maxp = edesc->wMaxPacketSize[0];
2337f9f848faSopenharmony_ci	usb_pc_cpu_flush(pepext->page_cache);
2338f9f848faSopenharmony_ci
2339f9f848faSopenharmony_ci	if (ep_mode == USB_EP_MODE_STREAMS) {
2340f9f848faSopenharmony_ci		temp = XHCI_EPCTX_0_EPSTATE_SET(0) |
2341f9f848faSopenharmony_ci		    XHCI_EPCTX_0_MAXP_STREAMS_SET(XHCI_MAX_STREAMS_LOG - 1) |
2342f9f848faSopenharmony_ci		    XHCI_EPCTX_0_LSA_SET(1);
2343f9f848faSopenharmony_ci
2344f9f848faSopenharmony_ci		ring_addr += sizeof(struct xhci_trb) *
2345f9f848faSopenharmony_ci		    XHCI_MAX_TRANSFERS * XHCI_MAX_STREAMS;
2346f9f848faSopenharmony_ci	} else {
2347f9f848faSopenharmony_ci		temp = XHCI_EPCTX_0_EPSTATE_SET(0) |
2348f9f848faSopenharmony_ci		    XHCI_EPCTX_0_MAXP_STREAMS_SET(0) |
2349f9f848faSopenharmony_ci		    XHCI_EPCTX_0_LSA_SET(0);
2350f9f848faSopenharmony_ci
2351f9f848faSopenharmony_ci		ring_addr |= XHCI_EPCTX_2_DCS_SET(1);
2352f9f848faSopenharmony_ci	}
2353f9f848faSopenharmony_ci
2354f9f848faSopenharmony_ci	switch (udev->speed) {
2355f9f848faSopenharmony_ci	case USB_SPEED_FULL:
2356f9f848faSopenharmony_ci	case USB_SPEED_LOW:
2357f9f848faSopenharmony_ci		/* 1ms -> 125us */
2358f9f848faSopenharmony_ci		fps_shift += 3;
2359f9f848faSopenharmony_ci		break;
2360f9f848faSopenharmony_ci	default:
2361f9f848faSopenharmony_ci		break;
2362f9f848faSopenharmony_ci	}
2363f9f848faSopenharmony_ci
2364f9f848faSopenharmony_ci	switch (type) {
2365f9f848faSopenharmony_ci	case UE_INTERRUPT:
2366f9f848faSopenharmony_ci		if (fps_shift > 3)
2367f9f848faSopenharmony_ci			fps_shift--;
2368f9f848faSopenharmony_ci		temp |= XHCI_EPCTX_0_IVAL_SET(fps_shift);
2369f9f848faSopenharmony_ci		break;
2370f9f848faSopenharmony_ci	case UE_ISOCHRONOUS:
2371f9f848faSopenharmony_ci		temp |= XHCI_EPCTX_0_IVAL_SET(fps_shift);
2372f9f848faSopenharmony_ci
2373f9f848faSopenharmony_ci		switch (udev->speed) {
2374f9f848faSopenharmony_ci		case USB_SPEED_SUPER:
2375f9f848faSopenharmony_ci			if (mult > 3)
2376f9f848faSopenharmony_ci				mult = 3;
2377f9f848faSopenharmony_ci			temp |= XHCI_EPCTX_0_MULT_SET(mult - 1);
2378f9f848faSopenharmony_ci			max_packet_count /= mult;
2379f9f848faSopenharmony_ci			break;
2380f9f848faSopenharmony_ci		default:
2381f9f848faSopenharmony_ci			break;
2382f9f848faSopenharmony_ci		}
2383f9f848faSopenharmony_ci		break;
2384f9f848faSopenharmony_ci	default:
2385f9f848faSopenharmony_ci		break;
2386f9f848faSopenharmony_ci	}
2387f9f848faSopenharmony_ci
2388f9f848faSopenharmony_ci	xhci_ctx_set_le32(sc, &pinp->ctx_ep[epno - 1].dwEpCtx0, temp);
2389f9f848faSopenharmony_ci
2390f9f848faSopenharmony_ci	temp =
2391f9f848faSopenharmony_ci	    XHCI_EPCTX_1_HID_SET(0) |
2392f9f848faSopenharmony_ci	    XHCI_EPCTX_1_MAXB_SET(max_packet_count) |
2393f9f848faSopenharmony_ci	    XHCI_EPCTX_1_MAXP_SIZE_SET(max_packet_size);
2394f9f848faSopenharmony_ci
2395f9f848faSopenharmony_ci	/*
2396f9f848faSopenharmony_ci	 * Always enable the "three strikes and you are gone" feature
2397f9f848faSopenharmony_ci	 * except for ISOCHRONOUS endpoints. This is suggested by
2398f9f848faSopenharmony_ci	 * section 4.3.3 in the XHCI specification about device slot
2399f9f848faSopenharmony_ci	 * initialisation.
2400f9f848faSopenharmony_ci	 */
2401f9f848faSopenharmony_ci	if (type != UE_ISOCHRONOUS)
2402f9f848faSopenharmony_ci		temp |= XHCI_EPCTX_1_CERR_SET(3);
2403f9f848faSopenharmony_ci
2404f9f848faSopenharmony_ci	switch (type) {
2405f9f848faSopenharmony_ci	case UE_CONTROL:
2406f9f848faSopenharmony_ci		temp |= XHCI_EPCTX_1_EPTYPE_SET(4);
2407f9f848faSopenharmony_ci		break;
2408f9f848faSopenharmony_ci	case UE_ISOCHRONOUS:
2409f9f848faSopenharmony_ci		temp |= XHCI_EPCTX_1_EPTYPE_SET(1);
2410f9f848faSopenharmony_ci		break;
2411f9f848faSopenharmony_ci	case UE_BULK:
2412f9f848faSopenharmony_ci		temp |= XHCI_EPCTX_1_EPTYPE_SET(2);
2413f9f848faSopenharmony_ci		break;
2414f9f848faSopenharmony_ci	default:
2415f9f848faSopenharmony_ci		temp |= XHCI_EPCTX_1_EPTYPE_SET(3);
2416f9f848faSopenharmony_ci		break;
2417f9f848faSopenharmony_ci	}
2418f9f848faSopenharmony_ci
2419f9f848faSopenharmony_ci	/* check for IN direction */
2420f9f848faSopenharmony_ci	if (epno & 1)
2421f9f848faSopenharmony_ci		temp |= XHCI_EPCTX_1_EPTYPE_SET(4);
2422f9f848faSopenharmony_ci
2423f9f848faSopenharmony_ci	xhci_ctx_set_le32(sc, &pinp->ctx_ep[epno - 1].dwEpCtx1, temp);
2424f9f848faSopenharmony_ci	xhci_ctx_set_le64(sc, &pinp->ctx_ep[epno - 1].qwEpCtx2, ring_addr);
2425f9f848faSopenharmony_ci
2426f9f848faSopenharmony_ci	switch (edesc->bmAttributes & UE_XFERTYPE) {
2427f9f848faSopenharmony_ci	case UE_INTERRUPT:
2428f9f848faSopenharmony_ci	case UE_ISOCHRONOUS:
2429f9f848faSopenharmony_ci		temp = XHCI_EPCTX_4_MAX_ESIT_PAYLOAD_SET(max_frame_size) |
2430f9f848faSopenharmony_ci		    XHCI_EPCTX_4_AVG_TRB_LEN_SET(MIN(XHCI_PAGE_SIZE,
2431f9f848faSopenharmony_ci		    max_frame_size));
2432f9f848faSopenharmony_ci		break;
2433f9f848faSopenharmony_ci	case UE_CONTROL:
2434f9f848faSopenharmony_ci		temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(8);
2435f9f848faSopenharmony_ci		break;
2436f9f848faSopenharmony_ci	default:
2437f9f848faSopenharmony_ci		temp = XHCI_EPCTX_4_AVG_TRB_LEN_SET(XHCI_PAGE_SIZE);
2438f9f848faSopenharmony_ci		break;
2439f9f848faSopenharmony_ci	}
2440f9f848faSopenharmony_ci
2441f9f848faSopenharmony_ci	xhci_ctx_set_le32(sc, &pinp->ctx_ep[epno - 1].dwEpCtx4, temp);
2442f9f848faSopenharmony_ci
2443f9f848faSopenharmony_ci#ifdef USB_DEBUG
2444f9f848faSopenharmony_ci	xhci_dump_endpoint(sc, &pinp->ctx_ep[epno - 1]);
2445f9f848faSopenharmony_ci#endif
2446f9f848faSopenharmony_ci	usb_pc_cpu_flush(&sc->sc_hw.devs[index].input_pc);
2447f9f848faSopenharmony_ci
2448f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);		/* success */
2449f9f848faSopenharmony_ci}
2450f9f848faSopenharmony_ci
2451f9f848faSopenharmony_cistatic usb_error_t
2452f9f848faSopenharmony_cixhci_configure_endpoint_by_xfer(struct usb_xfer *xfer)
2453f9f848faSopenharmony_ci{
2454f9f848faSopenharmony_ci	struct xhci_endpoint_ext *pepext;
2455f9f848faSopenharmony_ci	struct usb_endpoint_ss_comp_descriptor *ecomp;
2456f9f848faSopenharmony_ci	usb_stream_t x;
2457f9f848faSopenharmony_ci
2458f9f848faSopenharmony_ci	pepext = xhci_get_endpoint_ext(xfer->xroot->udev,
2459f9f848faSopenharmony_ci	    xfer->endpoint->edesc);
2460f9f848faSopenharmony_ci
2461f9f848faSopenharmony_ci	ecomp = xfer->endpoint->ecomp;
2462f9f848faSopenharmony_ci
2463f9f848faSopenharmony_ci	for (x = 0; x != XHCI_MAX_STREAMS; x++) {
2464f9f848faSopenharmony_ci		uint64_t temp;
2465f9f848faSopenharmony_ci
2466f9f848faSopenharmony_ci		/* halt any transfers */
2467f9f848faSopenharmony_ci		pepext->trb[x * XHCI_MAX_TRANSFERS].dwTrb3 = 0;
2468f9f848faSopenharmony_ci
2469f9f848faSopenharmony_ci		/* compute start of TRB ring for stream "x" */
2470f9f848faSopenharmony_ci		temp = pepext->physaddr +
2471f9f848faSopenharmony_ci		    (x * XHCI_MAX_TRANSFERS * sizeof(struct xhci_trb)) +
2472f9f848faSopenharmony_ci		    XHCI_SCTX_0_SCT_SEC_TR_RING;
2473f9f848faSopenharmony_ci
2474f9f848faSopenharmony_ci		/* make tree structure */
2475f9f848faSopenharmony_ci		pepext->trb[(XHCI_MAX_TRANSFERS *
2476f9f848faSopenharmony_ci		    XHCI_MAX_STREAMS) + x].qwTrb0 = htole64(temp);
2477f9f848faSopenharmony_ci
2478f9f848faSopenharmony_ci		/* reserved fields */
2479f9f848faSopenharmony_ci		pepext->trb[(XHCI_MAX_TRANSFERS *
2480f9f848faSopenharmony_ci		    XHCI_MAX_STREAMS) + x].dwTrb2 = 0;
2481f9f848faSopenharmony_ci		pepext->trb[(XHCI_MAX_TRANSFERS *
2482f9f848faSopenharmony_ci		    XHCI_MAX_STREAMS) + x].dwTrb3 = 0;
2483f9f848faSopenharmony_ci	}
2484f9f848faSopenharmony_ci	usb_pc_cpu_flush(pepext->page_cache);
2485f9f848faSopenharmony_ci
2486f9f848faSopenharmony_ci	return (xhci_configure_endpoint(xfer->xroot->udev,
2487f9f848faSopenharmony_ci	    xfer->endpoint->edesc, pepext,
2488f9f848faSopenharmony_ci	    xfer->interval, xfer->max_packet_count,
2489f9f848faSopenharmony_ci	    (ecomp != NULL) ? UE_GET_SS_ISO_MULT(ecomp->bmAttributes) + 1 : 1,
2490f9f848faSopenharmony_ci	    usbd_xfer_get_fps_shift(xfer), xfer->max_packet_size,
2491f9f848faSopenharmony_ci	    xfer->max_frame_size, xfer->endpoint->ep_mode));
2492f9f848faSopenharmony_ci}
2493f9f848faSopenharmony_ci
2494f9f848faSopenharmony_cistatic usb_error_t
2495f9f848faSopenharmony_cixhci_configure_device(struct usb_device *udev)
2496f9f848faSopenharmony_ci{
2497f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
2498f9f848faSopenharmony_ci	struct usb_page_search buf_inp;
2499f9f848faSopenharmony_ci	struct usb_page_cache *pcinp;
2500f9f848faSopenharmony_ci	struct xhci_input_dev_ctx *pinp;
2501f9f848faSopenharmony_ci	struct usb_device *hubdev;
2502f9f848faSopenharmony_ci	uint32_t temp;
2503f9f848faSopenharmony_ci	uint32_t route;
2504f9f848faSopenharmony_ci	uint32_t rh_port;
2505f9f848faSopenharmony_ci	uint8_t is_hub;
2506f9f848faSopenharmony_ci	uint8_t index;
2507f9f848faSopenharmony_ci	uint8_t depth;
2508f9f848faSopenharmony_ci
2509f9f848faSopenharmony_ci	index = udev->controller_slot_id;
2510f9f848faSopenharmony_ci
2511f9f848faSopenharmony_ci	DPRINTF("index=%u\n", index);
2512f9f848faSopenharmony_ci
2513f9f848faSopenharmony_ci	pcinp = &sc->sc_hw.devs[index].input_pc;
2514f9f848faSopenharmony_ci
2515f9f848faSopenharmony_ci	usbd_get_page(pcinp, 0, &buf_inp);
2516f9f848faSopenharmony_ci
2517f9f848faSopenharmony_ci	pinp = buf_inp.buffer;
2518f9f848faSopenharmony_ci
2519f9f848faSopenharmony_ci	rh_port = 0;
2520f9f848faSopenharmony_ci	route = 0;
2521f9f848faSopenharmony_ci
2522f9f848faSopenharmony_ci	/* figure out route string and root HUB port number */
2523f9f848faSopenharmony_ci
2524f9f848faSopenharmony_ci	for (hubdev = udev; hubdev != NULL; hubdev = hubdev->parent_hub) {
2525f9f848faSopenharmony_ci		if (hubdev->parent_hub == NULL)
2526f9f848faSopenharmony_ci			break;
2527f9f848faSopenharmony_ci
2528f9f848faSopenharmony_ci		depth = hubdev->parent_hub->depth;
2529f9f848faSopenharmony_ci
2530f9f848faSopenharmony_ci		/*
2531f9f848faSopenharmony_ci		 * NOTE: HS/FS/LS devices and the SS root HUB can have
2532f9f848faSopenharmony_ci		 * more than 15 ports
2533f9f848faSopenharmony_ci		 */
2534f9f848faSopenharmony_ci
2535f9f848faSopenharmony_ci		rh_port = hubdev->port_no;
2536f9f848faSopenharmony_ci
2537f9f848faSopenharmony_ci		if (depth == 0)
2538f9f848faSopenharmony_ci			break;
2539f9f848faSopenharmony_ci
2540f9f848faSopenharmony_ci		if (rh_port > 15)
2541f9f848faSopenharmony_ci			rh_port = 15;
2542f9f848faSopenharmony_ci
2543f9f848faSopenharmony_ci		if (depth < 6)
2544f9f848faSopenharmony_ci			route |= rh_port << (4 * (depth - 1));
2545f9f848faSopenharmony_ci	}
2546f9f848faSopenharmony_ci
2547f9f848faSopenharmony_ci	DPRINTF("Route=0x%08x\n", route);
2548f9f848faSopenharmony_ci
2549f9f848faSopenharmony_ci	temp = XHCI_SCTX_0_ROUTE_SET(route) |
2550f9f848faSopenharmony_ci	    XHCI_SCTX_0_CTX_NUM_SET(
2551f9f848faSopenharmony_ci	    sc->sc_hw.devs[index].context_num + 1);
2552f9f848faSopenharmony_ci
2553f9f848faSopenharmony_ci	switch (udev->speed) {
2554f9f848faSopenharmony_ci	case USB_SPEED_LOW:
2555f9f848faSopenharmony_ci		temp |= XHCI_SCTX_0_SPEED_SET(2);
2556f9f848faSopenharmony_ci		if ((udev->parent_hs_hub != NULL) &&
2557f9f848faSopenharmony_ci		    (udev->parent_hs_hub->ddesc.bDeviceProtocol ==
2558f9f848faSopenharmony_ci		    UDPROTO_HSHUBMTT)) {
2559f9f848faSopenharmony_ci			DPRINTF("Device inherits MTT\n");
2560f9f848faSopenharmony_ci			temp |= XHCI_SCTX_0_MTT_SET(1);
2561f9f848faSopenharmony_ci		}
2562f9f848faSopenharmony_ci		break;
2563f9f848faSopenharmony_ci	case USB_SPEED_HIGH:
2564f9f848faSopenharmony_ci		temp |= XHCI_SCTX_0_SPEED_SET(3);
2565f9f848faSopenharmony_ci		if ((sc->sc_hw.devs[index].nports != 0) &&
2566f9f848faSopenharmony_ci		    (udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBMTT)) {
2567f9f848faSopenharmony_ci			DPRINTF("HUB supports MTT\n");
2568f9f848faSopenharmony_ci			temp |= XHCI_SCTX_0_MTT_SET(1);
2569f9f848faSopenharmony_ci		}
2570f9f848faSopenharmony_ci		break;
2571f9f848faSopenharmony_ci	case USB_SPEED_FULL:
2572f9f848faSopenharmony_ci		temp |= XHCI_SCTX_0_SPEED_SET(1);
2573f9f848faSopenharmony_ci		if ((udev->parent_hs_hub != NULL) &&
2574f9f848faSopenharmony_ci		    (udev->parent_hs_hub->ddesc.bDeviceProtocol ==
2575f9f848faSopenharmony_ci		    UDPROTO_HSHUBMTT)) {
2576f9f848faSopenharmony_ci			DPRINTF("Device inherits MTT\n");
2577f9f848faSopenharmony_ci			temp |= XHCI_SCTX_0_MTT_SET(1);
2578f9f848faSopenharmony_ci		}
2579f9f848faSopenharmony_ci		break;
2580f9f848faSopenharmony_ci	default:
2581f9f848faSopenharmony_ci		temp |= XHCI_SCTX_0_SPEED_SET(4);
2582f9f848faSopenharmony_ci		break;
2583f9f848faSopenharmony_ci	}
2584f9f848faSopenharmony_ci
2585f9f848faSopenharmony_ci	is_hub = sc->sc_hw.devs[index].nports != 0 &&
2586f9f848faSopenharmony_ci	    (udev->speed == USB_SPEED_SUPER ||
2587f9f848faSopenharmony_ci	    udev->speed == USB_SPEED_HIGH);
2588f9f848faSopenharmony_ci
2589f9f848faSopenharmony_ci	if (is_hub)
2590f9f848faSopenharmony_ci		temp |= XHCI_SCTX_0_HUB_SET(1);
2591f9f848faSopenharmony_ci
2592f9f848faSopenharmony_ci	xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx0, temp);
2593f9f848faSopenharmony_ci
2594f9f848faSopenharmony_ci	temp = XHCI_SCTX_1_RH_PORT_SET(rh_port);
2595f9f848faSopenharmony_ci
2596f9f848faSopenharmony_ci	if (is_hub) {
2597f9f848faSopenharmony_ci		temp |= XHCI_SCTX_1_NUM_PORTS_SET(
2598f9f848faSopenharmony_ci		    sc->sc_hw.devs[index].nports);
2599f9f848faSopenharmony_ci	}
2600f9f848faSopenharmony_ci
2601f9f848faSopenharmony_ci	switch (udev->speed) {
2602f9f848faSopenharmony_ci	case USB_SPEED_SUPER:
2603f9f848faSopenharmony_ci		switch (sc->sc_hw.devs[index].state) {
2604f9f848faSopenharmony_ci		case XHCI_ST_ADDRESSED:
2605f9f848faSopenharmony_ci		case XHCI_ST_CONFIGURED:
2606f9f848faSopenharmony_ci			/* enable power save */
2607f9f848faSopenharmony_ci			temp |= XHCI_SCTX_1_MAX_EL_SET(sc->sc_exit_lat_max);
2608f9f848faSopenharmony_ci			break;
2609f9f848faSopenharmony_ci		default:
2610f9f848faSopenharmony_ci			/* disable power save */
2611f9f848faSopenharmony_ci			break;
2612f9f848faSopenharmony_ci		}
2613f9f848faSopenharmony_ci		break;
2614f9f848faSopenharmony_ci	default:
2615f9f848faSopenharmony_ci		break;
2616f9f848faSopenharmony_ci	}
2617f9f848faSopenharmony_ci
2618f9f848faSopenharmony_ci	xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx1, temp);
2619f9f848faSopenharmony_ci
2620f9f848faSopenharmony_ci	temp = XHCI_SCTX_2_IRQ_TARGET_SET(0);
2621f9f848faSopenharmony_ci
2622f9f848faSopenharmony_ci	if (is_hub) {
2623f9f848faSopenharmony_ci		temp |= XHCI_SCTX_2_TT_THINK_TIME_SET(
2624f9f848faSopenharmony_ci		    sc->sc_hw.devs[index].tt);
2625f9f848faSopenharmony_ci	}
2626f9f848faSopenharmony_ci
2627f9f848faSopenharmony_ci	hubdev = udev->parent_hs_hub;
2628f9f848faSopenharmony_ci
2629f9f848faSopenharmony_ci	/* check if we should activate the transaction translator */
2630f9f848faSopenharmony_ci	switch (udev->speed) {
2631f9f848faSopenharmony_ci	case USB_SPEED_FULL:
2632f9f848faSopenharmony_ci	case USB_SPEED_LOW:
2633f9f848faSopenharmony_ci		if (hubdev != NULL) {
2634f9f848faSopenharmony_ci			temp |= XHCI_SCTX_2_TT_HUB_SID_SET(
2635f9f848faSopenharmony_ci			    hubdev->controller_slot_id);
2636f9f848faSopenharmony_ci			temp |= XHCI_SCTX_2_TT_PORT_NUM_SET(
2637f9f848faSopenharmony_ci			    udev->hs_port_no);
2638f9f848faSopenharmony_ci		}
2639f9f848faSopenharmony_ci		break;
2640f9f848faSopenharmony_ci	default:
2641f9f848faSopenharmony_ci		break;
2642f9f848faSopenharmony_ci	}
2643f9f848faSopenharmony_ci
2644f9f848faSopenharmony_ci	xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx2, temp);
2645f9f848faSopenharmony_ci
2646f9f848faSopenharmony_ci	/*
2647f9f848faSopenharmony_ci	 * These fields should be initialized to zero, according to
2648f9f848faSopenharmony_ci	 * XHCI section 6.2.2 - slot context:
2649f9f848faSopenharmony_ci	 */
2650f9f848faSopenharmony_ci	temp = XHCI_SCTX_3_DEV_ADDR_SET(0) |
2651f9f848faSopenharmony_ci	    XHCI_SCTX_3_SLOT_STATE_SET(0);
2652f9f848faSopenharmony_ci
2653f9f848faSopenharmony_ci	xhci_ctx_set_le32(sc, &pinp->ctx_slot.dwSctx3, temp);
2654f9f848faSopenharmony_ci
2655f9f848faSopenharmony_ci#ifdef USB_DEBUG
2656f9f848faSopenharmony_ci	xhci_dump_device(sc, &pinp->ctx_slot);
2657f9f848faSopenharmony_ci#endif
2658f9f848faSopenharmony_ci	usb_pc_cpu_flush(pcinp);
2659f9f848faSopenharmony_ci
2660f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);		/* success */
2661f9f848faSopenharmony_ci}
2662f9f848faSopenharmony_ci
2663f9f848faSopenharmony_cistatic usb_error_t
2664f9f848faSopenharmony_cixhci_alloc_device_ext(struct usb_device *udev)
2665f9f848faSopenharmony_ci{
2666f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
2667f9f848faSopenharmony_ci	struct usb_page_search buf_dev;
2668f9f848faSopenharmony_ci	struct usb_page_search buf_ep;
2669f9f848faSopenharmony_ci	struct xhci_trb *trb;
2670f9f848faSopenharmony_ci	struct usb_page_cache *pc;
2671f9f848faSopenharmony_ci	struct usb_page *pg;
2672f9f848faSopenharmony_ci	uint64_t addr;
2673f9f848faSopenharmony_ci	uint8_t index;
2674f9f848faSopenharmony_ci	uint8_t i;
2675f9f848faSopenharmony_ci
2676f9f848faSopenharmony_ci	index = udev->controller_slot_id;
2677f9f848faSopenharmony_ci
2678f9f848faSopenharmony_ci	pc = &sc->sc_hw.devs[index].device_pc;
2679f9f848faSopenharmony_ci	pg = &sc->sc_hw.devs[index].device_pg;
2680f9f848faSopenharmony_ci
2681f9f848faSopenharmony_ci	/* need to initialize the page cache */
2682f9f848faSopenharmony_ci	pc->tag_parent = sc->sc_bus.dma_parent_tag;
2683f9f848faSopenharmony_ci
2684f9f848faSopenharmony_ci	if (usb_pc_alloc_mem(pc, pg, sc->sc_ctx_is_64_byte ?
2685f9f848faSopenharmony_ci	    (2 * sizeof(struct xhci_dev_ctx)) :
2686f9f848faSopenharmony_ci	    sizeof(struct xhci_dev_ctx), XHCI_PAGE_SIZE))
2687f9f848faSopenharmony_ci		goto error;
2688f9f848faSopenharmony_ci
2689f9f848faSopenharmony_ci	usbd_get_page(pc, 0, &buf_dev);
2690f9f848faSopenharmony_ci
2691f9f848faSopenharmony_ci	pc = &sc->sc_hw.devs[index].input_pc;
2692f9f848faSopenharmony_ci	pg = &sc->sc_hw.devs[index].input_pg;
2693f9f848faSopenharmony_ci
2694f9f848faSopenharmony_ci	/* need to initialize the page cache */
2695f9f848faSopenharmony_ci	pc->tag_parent = sc->sc_bus.dma_parent_tag;
2696f9f848faSopenharmony_ci
2697f9f848faSopenharmony_ci	if (usb_pc_alloc_mem(pc, pg, sc->sc_ctx_is_64_byte ?
2698f9f848faSopenharmony_ci	    (2 * sizeof(struct xhci_input_dev_ctx)) :
2699f9f848faSopenharmony_ci	    sizeof(struct xhci_input_dev_ctx), XHCI_PAGE_SIZE)) {
2700f9f848faSopenharmony_ci		goto error;
2701f9f848faSopenharmony_ci	}
2702f9f848faSopenharmony_ci
2703f9f848faSopenharmony_ci	/* initialize all endpoint LINK TRBs */
2704f9f848faSopenharmony_ci
2705f9f848faSopenharmony_ci	for (i = 0; i != XHCI_MAX_ENDPOINTS; i++) {
2706f9f848faSopenharmony_ci		pc = &sc->sc_hw.devs[index].endpoint_pc[i];
2707f9f848faSopenharmony_ci		pg = &sc->sc_hw.devs[index].endpoint_pg[i];
2708f9f848faSopenharmony_ci
2709f9f848faSopenharmony_ci		/* need to initialize the page cache */
2710f9f848faSopenharmony_ci		pc->tag_parent = sc->sc_bus.dma_parent_tag;
2711f9f848faSopenharmony_ci
2712f9f848faSopenharmony_ci		if (usb_pc_alloc_mem(pc, pg,
2713f9f848faSopenharmony_ci		    sizeof(struct xhci_dev_endpoint_trbs), XHCI_TRB_ALIGN)) {
2714f9f848faSopenharmony_ci			goto error;
2715f9f848faSopenharmony_ci		}
2716f9f848faSopenharmony_ci
2717f9f848faSopenharmony_ci		/* lookup endpoint TRB ring */
2718f9f848faSopenharmony_ci		usbd_get_page(pc, 0, &buf_ep);
2719f9f848faSopenharmony_ci
2720f9f848faSopenharmony_ci		/* get TRB pointer */
2721f9f848faSopenharmony_ci		trb = buf_ep.buffer;
2722f9f848faSopenharmony_ci		trb += XHCI_MAX_TRANSFERS - 1;
2723f9f848faSopenharmony_ci
2724f9f848faSopenharmony_ci		/* get TRB start address */
2725f9f848faSopenharmony_ci		addr = buf_ep.physaddr;
2726f9f848faSopenharmony_ci
2727f9f848faSopenharmony_ci		/* create LINK TRB */
2728f9f848faSopenharmony_ci		trb->qwTrb0 = htole64(addr);
2729f9f848faSopenharmony_ci		trb->dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0));
2730f9f848faSopenharmony_ci		trb->dwTrb3 = htole32(XHCI_TRB_3_CYCLE_BIT |
2731f9f848faSopenharmony_ci		    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK));
2732f9f848faSopenharmony_ci
2733f9f848faSopenharmony_ci		usb_pc_cpu_flush(pc);
2734f9f848faSopenharmony_ci	}
2735f9f848faSopenharmony_ci
2736f9f848faSopenharmony_ci	xhci_set_slot_pointer(sc, index, buf_dev.physaddr);
2737f9f848faSopenharmony_ci
2738f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
2739f9f848faSopenharmony_ci
2740f9f848faSopenharmony_cierror:
2741f9f848faSopenharmony_ci	xhci_free_device_ext(udev);
2742f9f848faSopenharmony_ci
2743f9f848faSopenharmony_ci	return (USB_ERR_NOMEM);
2744f9f848faSopenharmony_ci}
2745f9f848faSopenharmony_ci
2746f9f848faSopenharmony_cistatic void
2747f9f848faSopenharmony_cixhci_free_device_ext(struct usb_device *udev)
2748f9f848faSopenharmony_ci{
2749f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
2750f9f848faSopenharmony_ci	uint8_t index;
2751f9f848faSopenharmony_ci	uint8_t i;
2752f9f848faSopenharmony_ci
2753f9f848faSopenharmony_ci	index = udev->controller_slot_id;
2754f9f848faSopenharmony_ci	xhci_set_slot_pointer(sc, index, 0);
2755f9f848faSopenharmony_ci
2756f9f848faSopenharmony_ci	usb_pc_free_mem(&sc->sc_hw.devs[index].device_pc);
2757f9f848faSopenharmony_ci	usb_pc_free_mem(&sc->sc_hw.devs[index].input_pc);
2758f9f848faSopenharmony_ci	for (i = 0; i != XHCI_MAX_ENDPOINTS; i++)
2759f9f848faSopenharmony_ci		usb_pc_free_mem(&sc->sc_hw.devs[index].endpoint_pc[i]);
2760f9f848faSopenharmony_ci}
2761f9f848faSopenharmony_ci
2762f9f848faSopenharmony_cistatic struct xhci_endpoint_ext *
2763f9f848faSopenharmony_cixhci_get_endpoint_ext(struct usb_device *udev, struct usb_endpoint_descriptor *edesc)
2764f9f848faSopenharmony_ci{
2765f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
2766f9f848faSopenharmony_ci	struct xhci_endpoint_ext *pepext;
2767f9f848faSopenharmony_ci	struct usb_page_cache *pc;
2768f9f848faSopenharmony_ci	struct usb_page_search buf_ep;
2769f9f848faSopenharmony_ci	uint8_t epno;
2770f9f848faSopenharmony_ci	uint8_t index;
2771f9f848faSopenharmony_ci
2772f9f848faSopenharmony_ci	epno = edesc->bEndpointAddress;
2773f9f848faSopenharmony_ci	if ((edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL)
2774f9f848faSopenharmony_ci		epno |= UE_DIR_IN;
2775f9f848faSopenharmony_ci
2776f9f848faSopenharmony_ci	epno = XHCI_EPNO2EPID(epno);
2777f9f848faSopenharmony_ci
2778f9f848faSopenharmony_ci	index = udev->controller_slot_id;
2779f9f848faSopenharmony_ci
2780f9f848faSopenharmony_ci	pc = &sc->sc_hw.devs[index].endpoint_pc[epno];
2781f9f848faSopenharmony_ci
2782f9f848faSopenharmony_ci	usbd_get_page(pc, 0, &buf_ep);
2783f9f848faSopenharmony_ci
2784f9f848faSopenharmony_ci	pepext = &sc->sc_hw.devs[index].endp[epno];
2785f9f848faSopenharmony_ci	pepext->page_cache = pc;
2786f9f848faSopenharmony_ci	pepext->trb = buf_ep.buffer;
2787f9f848faSopenharmony_ci	pepext->physaddr = buf_ep.physaddr;
2788f9f848faSopenharmony_ci
2789f9f848faSopenharmony_ci	return (pepext);
2790f9f848faSopenharmony_ci}
2791f9f848faSopenharmony_ci
2792f9f848faSopenharmony_cistatic void
2793f9f848faSopenharmony_cixhci_endpoint_doorbell(struct usb_xfer *xfer)
2794f9f848faSopenharmony_ci{
2795f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus);
2796f9f848faSopenharmony_ci	uint8_t epno;
2797f9f848faSopenharmony_ci	uint8_t index;
2798f9f848faSopenharmony_ci
2799f9f848faSopenharmony_ci	epno = xfer->endpointno;
2800f9f848faSopenharmony_ci	if (xfer->flags_int.control_xfr)
2801f9f848faSopenharmony_ci		epno |= UE_DIR_IN;
2802f9f848faSopenharmony_ci
2803f9f848faSopenharmony_ci	epno = XHCI_EPNO2EPID(epno);
2804f9f848faSopenharmony_ci	index = xfer->xroot->udev->controller_slot_id;
2805f9f848faSopenharmony_ci
2806f9f848faSopenharmony_ci	if (xfer->xroot->udev->flags.self_suspended == 0) {
2807f9f848faSopenharmony_ci		XWRITE4(sc, door, XHCI_DOORBELL(index),
2808f9f848faSopenharmony_ci		    epno | XHCI_DB_SID_SET(xfer->stream_id));
2809f9f848faSopenharmony_ci	}
2810f9f848faSopenharmony_ci}
2811f9f848faSopenharmony_ci
2812f9f848faSopenharmony_cistatic void
2813f9f848faSopenharmony_cixhci_transfer_remove(struct usb_xfer *xfer, usb_error_t error)
2814f9f848faSopenharmony_ci{
2815f9f848faSopenharmony_ci	struct xhci_endpoint_ext *pepext;
2816f9f848faSopenharmony_ci
2817f9f848faSopenharmony_ci	if (xfer->flags_int.bandwidth_reclaimed) {
2818f9f848faSopenharmony_ci		xfer->flags_int.bandwidth_reclaimed = 0;
2819f9f848faSopenharmony_ci
2820f9f848faSopenharmony_ci		pepext = xhci_get_endpoint_ext(xfer->xroot->udev,
2821f9f848faSopenharmony_ci		    xfer->endpoint->edesc);
2822f9f848faSopenharmony_ci
2823f9f848faSopenharmony_ci		pepext->trb_used[xfer->stream_id]--;
2824f9f848faSopenharmony_ci
2825f9f848faSopenharmony_ci		pepext->xfer[xfer->qh_pos] = NULL;
2826f9f848faSopenharmony_ci
2827f9f848faSopenharmony_ci		if (error && (pepext->trb_running != 0)) {
2828f9f848faSopenharmony_ci			pepext->trb_halted = 1;
2829f9f848faSopenharmony_ci			pepext->trb_running = 0;
2830f9f848faSopenharmony_ci		}
2831f9f848faSopenharmony_ci	}
2832f9f848faSopenharmony_ci}
2833f9f848faSopenharmony_ci
2834f9f848faSopenharmony_cistatic usb_error_t
2835f9f848faSopenharmony_cixhci_transfer_insert(struct usb_xfer *xfer)
2836f9f848faSopenharmony_ci{
2837f9f848faSopenharmony_ci	struct xhci_td *td_first;
2838f9f848faSopenharmony_ci	struct xhci_td *td_last;
2839f9f848faSopenharmony_ci	struct xhci_trb *trb_link;
2840f9f848faSopenharmony_ci	struct xhci_endpoint_ext *pepext;
2841f9f848faSopenharmony_ci	uint64_t addr;
2842f9f848faSopenharmony_ci	usb_stream_t id;
2843f9f848faSopenharmony_ci	uint8_t i;
2844f9f848faSopenharmony_ci	uint8_t inext;
2845f9f848faSopenharmony_ci	uint8_t trb_limit;
2846f9f848faSopenharmony_ci
2847f9f848faSopenharmony_ci	DPRINTFN(8, "\n");
2848f9f848faSopenharmony_ci
2849f9f848faSopenharmony_ci	id = xfer->stream_id;
2850f9f848faSopenharmony_ci
2851f9f848faSopenharmony_ci	/* check if already inserted */
2852f9f848faSopenharmony_ci	if (xfer->flags_int.bandwidth_reclaimed) {
2853f9f848faSopenharmony_ci		DPRINTFN(8, "Already in schedule\n");
2854f9f848faSopenharmony_ci		return (USB_ERR_NORMAL_COMPLETION);
2855f9f848faSopenharmony_ci	}
2856f9f848faSopenharmony_ci
2857f9f848faSopenharmony_ci	pepext = xhci_get_endpoint_ext(xfer->xroot->udev,
2858f9f848faSopenharmony_ci	    xfer->endpoint->edesc);
2859f9f848faSopenharmony_ci
2860f9f848faSopenharmony_ci	td_first = xfer->td_transfer_first;
2861f9f848faSopenharmony_ci	td_last = xfer->td_transfer_last;
2862f9f848faSopenharmony_ci	addr = pepext->physaddr;
2863f9f848faSopenharmony_ci
2864f9f848faSopenharmony_ci	switch (xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE) {
2865f9f848faSopenharmony_ci	case UE_CONTROL:
2866f9f848faSopenharmony_ci	case UE_INTERRUPT:
2867f9f848faSopenharmony_ci		/* single buffered */
2868f9f848faSopenharmony_ci		trb_limit = 1;
2869f9f848faSopenharmony_ci		break;
2870f9f848faSopenharmony_ci	default:
2871f9f848faSopenharmony_ci		/* multi buffered */
2872f9f848faSopenharmony_ci		trb_limit = (XHCI_MAX_TRANSFERS - 2);
2873f9f848faSopenharmony_ci		break;
2874f9f848faSopenharmony_ci	}
2875f9f848faSopenharmony_ci
2876f9f848faSopenharmony_ci	if (pepext->trb_used[id] >= trb_limit) {
2877f9f848faSopenharmony_ci		DPRINTFN(8, "Too many TDs queued.\n");
2878f9f848faSopenharmony_ci		return (USB_ERR_NOMEM);
2879f9f848faSopenharmony_ci	}
2880f9f848faSopenharmony_ci
2881f9f848faSopenharmony_ci	/* check if bMaxPacketSize changed */
2882f9f848faSopenharmony_ci	if (xfer->flags_int.control_xfr != 0 &&
2883f9f848faSopenharmony_ci	    pepext->trb_ep_maxp != xfer->endpoint->edesc->wMaxPacketSize[0]) {
2884f9f848faSopenharmony_ci		DPRINTFN(8, "Reconfigure control endpoint\n");
2885f9f848faSopenharmony_ci
2886f9f848faSopenharmony_ci		/* force driver to reconfigure endpoint */
2887f9f848faSopenharmony_ci		pepext->trb_halted = 1;
2888f9f848faSopenharmony_ci		pepext->trb_running = 0;
2889f9f848faSopenharmony_ci	}
2890f9f848faSopenharmony_ci
2891f9f848faSopenharmony_ci	/* check for stopped condition, after putting transfer on interrupt queue */
2892f9f848faSopenharmony_ci	if (pepext->trb_running == 0) {
2893f9f848faSopenharmony_ci		struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus);
2894f9f848faSopenharmony_ci
2895f9f848faSopenharmony_ci		DPRINTFN(8, "Not running\n");
2896f9f848faSopenharmony_ci
2897f9f848faSopenharmony_ci		/* start configuration */
2898f9f848faSopenharmony_ci		(void)usb_proc_msignal(USB_BUS_CONTROL_XFER_PROC(&sc->sc_bus),
2899f9f848faSopenharmony_ci		    &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
2900f9f848faSopenharmony_ci		return (USB_ERR_NORMAL_COMPLETION);
2901f9f848faSopenharmony_ci	}
2902f9f848faSopenharmony_ci
2903f9f848faSopenharmony_ci	pepext->trb_used[id]++;
2904f9f848faSopenharmony_ci
2905f9f848faSopenharmony_ci	/* get current TRB index */
2906f9f848faSopenharmony_ci	i = pepext->trb_index[id];
2907f9f848faSopenharmony_ci
2908f9f848faSopenharmony_ci	/* get next TRB index */
2909f9f848faSopenharmony_ci	inext = (i + 1);
2910f9f848faSopenharmony_ci
2911f9f848faSopenharmony_ci	/* the last entry of the ring is a hardcoded link TRB */
2912f9f848faSopenharmony_ci	if (inext >= (XHCI_MAX_TRANSFERS - 1))
2913f9f848faSopenharmony_ci		inext = 0;
2914f9f848faSopenharmony_ci
2915f9f848faSopenharmony_ci	/* store next TRB index, before stream ID offset is added */
2916f9f848faSopenharmony_ci	pepext->trb_index[id] = inext;
2917f9f848faSopenharmony_ci
2918f9f848faSopenharmony_ci	/* offset for stream */
2919f9f848faSopenharmony_ci	i += id * XHCI_MAX_TRANSFERS;
2920f9f848faSopenharmony_ci	inext += id * XHCI_MAX_TRANSFERS;
2921f9f848faSopenharmony_ci
2922f9f848faSopenharmony_ci	/* compute terminating return address */
2923f9f848faSopenharmony_ci	addr += (inext * sizeof(struct xhci_trb));
2924f9f848faSopenharmony_ci
2925f9f848faSopenharmony_ci	/* compute link TRB pointer */
2926f9f848faSopenharmony_ci	trb_link = td_last->td_trb + td_last->ntrb;
2927f9f848faSopenharmony_ci
2928f9f848faSopenharmony_ci	/* update next pointer of last link TRB */
2929f9f848faSopenharmony_ci	trb_link->qwTrb0 = htole64(addr);
2930f9f848faSopenharmony_ci	trb_link->dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0));
2931f9f848faSopenharmony_ci	trb_link->dwTrb3 = htole32(XHCI_TRB_3_IOC_BIT |
2932f9f848faSopenharmony_ci	    XHCI_TRB_3_CYCLE_BIT |
2933f9f848faSopenharmony_ci	    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK));
2934f9f848faSopenharmony_ci
2935f9f848faSopenharmony_ci#ifdef USB_DEBUG
2936f9f848faSopenharmony_ci	xhci_dump_trb(&td_last->td_trb[td_last->ntrb]);
2937f9f848faSopenharmony_ci#endif
2938f9f848faSopenharmony_ci	usb_pc_cpu_flush(td_last->page_cache);
2939f9f848faSopenharmony_ci
2940f9f848faSopenharmony_ci	/* write ahead chain end marker */
2941f9f848faSopenharmony_ci
2942f9f848faSopenharmony_ci	pepext->trb[inext].qwTrb0 = 0;
2943f9f848faSopenharmony_ci	pepext->trb[inext].dwTrb2 = 0;
2944f9f848faSopenharmony_ci	pepext->trb[inext].dwTrb3 = 0;
2945f9f848faSopenharmony_ci
2946f9f848faSopenharmony_ci	/* update next pointer of link TRB */
2947f9f848faSopenharmony_ci
2948f9f848faSopenharmony_ci	pepext->trb[i].qwTrb0 = htole64((uint64_t)td_first->td_self);
2949f9f848faSopenharmony_ci	pepext->trb[i].dwTrb2 = htole32(XHCI_TRB_2_IRQ_SET(0));
2950f9f848faSopenharmony_ci
2951f9f848faSopenharmony_ci#ifdef USB_DEBUG
2952f9f848faSopenharmony_ci	xhci_dump_trb(&pepext->trb[i]);
2953f9f848faSopenharmony_ci#endif
2954f9f848faSopenharmony_ci	usb_pc_cpu_flush(pepext->page_cache);
2955f9f848faSopenharmony_ci
2956f9f848faSopenharmony_ci	/* toggle cycle bit which activates the transfer chain */
2957f9f848faSopenharmony_ci
2958f9f848faSopenharmony_ci	pepext->trb[i].dwTrb3 = htole32(XHCI_TRB_3_CYCLE_BIT |
2959f9f848faSopenharmony_ci	    XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_LINK));
2960f9f848faSopenharmony_ci
2961f9f848faSopenharmony_ci	usb_pc_cpu_flush(pepext->page_cache);
2962f9f848faSopenharmony_ci
2963f9f848faSopenharmony_ci	DPRINTF("qh_pos = %u\n", i);
2964f9f848faSopenharmony_ci
2965f9f848faSopenharmony_ci	pepext->xfer[i] = xfer;
2966f9f848faSopenharmony_ci
2967f9f848faSopenharmony_ci	xfer->qh_pos = i;
2968f9f848faSopenharmony_ci
2969f9f848faSopenharmony_ci	xfer->flags_int.bandwidth_reclaimed = 1;
2970f9f848faSopenharmony_ci
2971f9f848faSopenharmony_ci	xhci_endpoint_doorbell(xfer);
2972f9f848faSopenharmony_ci
2973f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
2974f9f848faSopenharmony_ci}
2975f9f848faSopenharmony_ci
2976f9f848faSopenharmony_cistatic void
2977f9f848faSopenharmony_cixhci_root_intr(struct xhci_softc *sc)
2978f9f848faSopenharmony_ci{
2979f9f848faSopenharmony_ci	uint16_t i;
2980f9f848faSopenharmony_ci
2981f9f848faSopenharmony_ci	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
2982f9f848faSopenharmony_ci
2983f9f848faSopenharmony_ci	/* clear any old interrupt data */
2984f9f848faSopenharmony_ci	(void)memset_s(sc->sc_hub_idata, sizeof(sc->sc_hub_idata), 0, sizeof(sc->sc_hub_idata));
2985f9f848faSopenharmony_ci
2986f9f848faSopenharmony_ci	for (i = 1; i <= sc->sc_noport; i++) {
2987f9f848faSopenharmony_ci		/* pick out CHANGE bits from the status register */
2988f9f848faSopenharmony_ci		if (XREAD4(sc, oper, XHCI_PORTSC(i)) & (
2989f9f848faSopenharmony_ci		    XHCI_PS_CSC | XHCI_PS_PEC |
2990f9f848faSopenharmony_ci		    XHCI_PS_OCC | XHCI_PS_WRC |
2991f9f848faSopenharmony_ci		    XHCI_PS_PRC | XHCI_PS_PLC |
2992f9f848faSopenharmony_ci		    XHCI_PS_CEC)) {
2993f9f848faSopenharmony_ci			sc->sc_hub_idata[i / 8] |= 1 << (i % 8);
2994f9f848faSopenharmony_ci			DPRINTF("port %d changed\n", i);
2995f9f848faSopenharmony_ci		}
2996f9f848faSopenharmony_ci	}
2997f9f848faSopenharmony_ci	uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata,
2998f9f848faSopenharmony_ci	    sizeof(sc->sc_hub_idata));
2999f9f848faSopenharmony_ci}
3000f9f848faSopenharmony_ci
3001f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
3002f9f848faSopenharmony_ci *	xhci_device_done - XHCI done handler
3003f9f848faSopenharmony_ci *
3004f9f848faSopenharmony_ci * NOTE: This function can be called two times in a row on
3005f9f848faSopenharmony_ci * the same USB transfer. From close and from interrupt.
3006f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
3007f9f848faSopenharmony_cistatic void
3008f9f848faSopenharmony_cixhci_device_done(struct usb_xfer *xfer, usb_error_t error)
3009f9f848faSopenharmony_ci{
3010f9f848faSopenharmony_ci	DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n",
3011f9f848faSopenharmony_ci	    xfer, xfer->endpoint, error);
3012f9f848faSopenharmony_ci
3013f9f848faSopenharmony_ci	/* remove transfer from HW queue */
3014f9f848faSopenharmony_ci	xhci_transfer_remove(xfer, error);
3015f9f848faSopenharmony_ci
3016f9f848faSopenharmony_ci	/* dequeue transfer and start next transfer */
3017f9f848faSopenharmony_ci	usbd_transfer_done(xfer, error);
3018f9f848faSopenharmony_ci}
3019f9f848faSopenharmony_ci
3020f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
3021f9f848faSopenharmony_ci * XHCI data transfer support (generic type)
3022f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
3023f9f848faSopenharmony_cistatic void
3024f9f848faSopenharmony_cixhci_device_generic_open(struct usb_xfer *xfer)
3025f9f848faSopenharmony_ci{
3026f9f848faSopenharmony_ci	if (xfer->flags_int.isochronous_xfr) {
3027f9f848faSopenharmony_ci		switch (xfer->xroot->udev->speed) {
3028f9f848faSopenharmony_ci		case USB_SPEED_FULL:
3029f9f848faSopenharmony_ci			break;
3030f9f848faSopenharmony_ci		default:
3031f9f848faSopenharmony_ci			usb_hs_bandwidth_alloc(xfer);
3032f9f848faSopenharmony_ci			break;
3033f9f848faSopenharmony_ci		}
3034f9f848faSopenharmony_ci	}
3035f9f848faSopenharmony_ci}
3036f9f848faSopenharmony_ci
3037f9f848faSopenharmony_cistatic void
3038f9f848faSopenharmony_cixhci_device_generic_close(struct usb_xfer *xfer)
3039f9f848faSopenharmony_ci{
3040f9f848faSopenharmony_ci	DPRINTF("\n");
3041f9f848faSopenharmony_ci
3042f9f848faSopenharmony_ci	xhci_device_done(xfer, USB_ERR_CANCELLED);
3043f9f848faSopenharmony_ci	if (xfer->flags_int.isochronous_xfr) {
3044f9f848faSopenharmony_ci		switch (xfer->xroot->udev->speed) {
3045f9f848faSopenharmony_ci		case USB_SPEED_FULL:
3046f9f848faSopenharmony_ci			break;
3047f9f848faSopenharmony_ci		default:
3048f9f848faSopenharmony_ci			usb_hs_bandwidth_free(xfer);
3049f9f848faSopenharmony_ci			break;
3050f9f848faSopenharmony_ci		}
3051f9f848faSopenharmony_ci	}
3052f9f848faSopenharmony_ci}
3053f9f848faSopenharmony_ci
3054f9f848faSopenharmony_cistatic void
3055f9f848faSopenharmony_cixhci_device_generic_multi_enter(struct usb_endpoint *ep,
3056f9f848faSopenharmony_ci    usb_stream_t stream_id, struct usb_xfer *enter_xfer)
3057f9f848faSopenharmony_ci{
3058f9f848faSopenharmony_ci	struct usb_xfer *xfer;
3059f9f848faSopenharmony_ci
3060f9f848faSopenharmony_ci	/* check if there is a current transfer */
3061f9f848faSopenharmony_ci	xfer = ep->endpoint_q[stream_id].curr;
3062f9f848faSopenharmony_ci	if (xfer == NULL)
3063f9f848faSopenharmony_ci		return;
3064f9f848faSopenharmony_ci
3065f9f848faSopenharmony_ci	/*
3066f9f848faSopenharmony_ci	 * Check if the current transfer is started and then pickup
3067f9f848faSopenharmony_ci	 * the next one, if any. Else wait for next start event due to
3068f9f848faSopenharmony_ci	 * block on failure feature.
3069f9f848faSopenharmony_ci	 */
3070f9f848faSopenharmony_ci	if (!xfer->flags_int.bandwidth_reclaimed)
3071f9f848faSopenharmony_ci		return;
3072f9f848faSopenharmony_ci
3073f9f848faSopenharmony_ci	xfer = TAILQ_FIRST(&ep->endpoint_q[stream_id].head);
3074f9f848faSopenharmony_ci	if (xfer == NULL) {
3075f9f848faSopenharmony_ci		/*
3076f9f848faSopenharmony_ci		 * In case of enter we have to consider that the
3077f9f848faSopenharmony_ci		 * transfer is queued by the USB core after the enter
3078f9f848faSopenharmony_ci		 * method is called.
3079f9f848faSopenharmony_ci		 */
3080f9f848faSopenharmony_ci		xfer = enter_xfer;
3081f9f848faSopenharmony_ci
3082f9f848faSopenharmony_ci		if (xfer == NULL)
3083f9f848faSopenharmony_ci			return;
3084f9f848faSopenharmony_ci	}
3085f9f848faSopenharmony_ci
3086f9f848faSopenharmony_ci	/* try to multi buffer */
3087f9f848faSopenharmony_ci	(void)xhci_transfer_insert(xfer);
3088f9f848faSopenharmony_ci}
3089f9f848faSopenharmony_ci
3090f9f848faSopenharmony_cistatic void
3091f9f848faSopenharmony_cixhci_device_generic_enter(struct usb_xfer *xfer)
3092f9f848faSopenharmony_ci{
3093f9f848faSopenharmony_ci	DPRINTF("\n");
3094f9f848faSopenharmony_ci
3095f9f848faSopenharmony_ci	/* set up TD's and QH */
3096f9f848faSopenharmony_ci	xhci_setup_generic_chain(xfer);
3097f9f848faSopenharmony_ci
3098f9f848faSopenharmony_ci	xhci_device_generic_multi_enter(xfer->endpoint,
3099f9f848faSopenharmony_ci	    xfer->stream_id, xfer);
3100f9f848faSopenharmony_ci}
3101f9f848faSopenharmony_ci
3102f9f848faSopenharmony_cistatic void
3103f9f848faSopenharmony_cixhci_device_generic_start(struct usb_xfer *xfer)
3104f9f848faSopenharmony_ci{
3105f9f848faSopenharmony_ci	DPRINTF("\n");
3106f9f848faSopenharmony_ci
3107f9f848faSopenharmony_ci	/* try to insert xfer on HW queue */
3108f9f848faSopenharmony_ci	(void)xhci_transfer_insert(xfer);
3109f9f848faSopenharmony_ci
3110f9f848faSopenharmony_ci	/* try to multi buffer */
3111f9f848faSopenharmony_ci	xhci_device_generic_multi_enter(xfer->endpoint,
3112f9f848faSopenharmony_ci	    xfer->stream_id, NULL);
3113f9f848faSopenharmony_ci
3114f9f848faSopenharmony_ci	/* add transfer last on interrupt queue */
3115f9f848faSopenharmony_ci	usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
3116f9f848faSopenharmony_ci
3117f9f848faSopenharmony_ci	/* start timeout, if any */
3118f9f848faSopenharmony_ci	if (xfer->timeout != 0)
3119f9f848faSopenharmony_ci		usbd_transfer_timeout_ms(xfer, &xhci_timeout, xfer->timeout);
3120f9f848faSopenharmony_ci}
3121f9f848faSopenharmony_ci
3122f9f848faSopenharmony_cistruct usb_pipe_methods xhci_device_generic_methods = {
3123f9f848faSopenharmony_ci	.open = xhci_device_generic_open,
3124f9f848faSopenharmony_ci	.close = xhci_device_generic_close,
3125f9f848faSopenharmony_ci	.enter = xhci_device_generic_enter,
3126f9f848faSopenharmony_ci	.start = xhci_device_generic_start,
3127f9f848faSopenharmony_ci};
3128f9f848faSopenharmony_ci
3129f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
3130f9f848faSopenharmony_ci * xhci root HUB support
3131f9f848faSopenharmony_ci *------------------------------------------------------------------------*
3132f9f848faSopenharmony_ci * Simulate a hardware HUB by handling all the necessary requests.
3133f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
3134f9f848faSopenharmony_ci#define	HSETW(ptr, val) ptr = { (uint8_t)(val), (uint8_t)((val) >> 8) }
3135f9f848faSopenharmony_ci
3136f9f848faSopenharmony_cistatic const
3137f9f848faSopenharmony_cistruct usb_device_descriptor xhci_devd = {
3138f9f848faSopenharmony_ci	.bLength = sizeof(xhci_devd),
3139f9f848faSopenharmony_ci	.bDescriptorType = UDESC_DEVICE,	/* type */
3140f9f848faSopenharmony_ci	HSETW(.bcdUSB, 0x0300),			/* USB version */
3141f9f848faSopenharmony_ci	.bDeviceClass = UDCLASS_HUB,		/* class */
3142f9f848faSopenharmony_ci	.bDeviceSubClass = UDSUBCLASS_HUB,	/* subclass */
3143f9f848faSopenharmony_ci	.bDeviceProtocol = UDPROTO_SSHUB,	/* protocol */
3144f9f848faSopenharmony_ci	.bMaxPacketSize = 9,			/* max packet size */
3145f9f848faSopenharmony_ci	HSETW(.idVendor, 0x0000),		/* vendor */
3146f9f848faSopenharmony_ci	HSETW(.idProduct, 0x0000),		/* product */
3147f9f848faSopenharmony_ci	HSETW(.bcdDevice, 0x0100),		/* device version */
3148f9f848faSopenharmony_ci	.iManufacturer = 1,
3149f9f848faSopenharmony_ci	.iProduct = 2,
3150f9f848faSopenharmony_ci	.iSerialNumber = 0,
3151f9f848faSopenharmony_ci	.bNumConfigurations = 1,	/* # of configurations */
3152f9f848faSopenharmony_ci};
3153f9f848faSopenharmony_ci
3154f9f848faSopenharmony_cistatic const
3155f9f848faSopenharmony_cistruct xhci_bos_desc xhci_bosd = {
3156f9f848faSopenharmony_ci	.bosd = {
3157f9f848faSopenharmony_ci		.bLength = sizeof(xhci_bosd.bosd),
3158f9f848faSopenharmony_ci		.bDescriptorType = UDESC_BOS,
3159f9f848faSopenharmony_ci		HSETW(.wTotalLength, sizeof(xhci_bosd)),
3160f9f848faSopenharmony_ci		.bNumDeviceCaps = 3,
3161f9f848faSopenharmony_ci	},
3162f9f848faSopenharmony_ci	.usb2extd = {
3163f9f848faSopenharmony_ci		.bLength = sizeof(xhci_bosd.usb2extd),
3164f9f848faSopenharmony_ci		.bDescriptorType = 1,
3165f9f848faSopenharmony_ci		.bDevCapabilityType = 2,
3166f9f848faSopenharmony_ci		.bmAttributes[0] = 2,
3167f9f848faSopenharmony_ci	},
3168f9f848faSopenharmony_ci	.usbdcd = {
3169f9f848faSopenharmony_ci		.bLength = sizeof(xhci_bosd.usbdcd),
3170f9f848faSopenharmony_ci		.bDescriptorType = UDESC_DEVICE_CAPABILITY,
3171f9f848faSopenharmony_ci		.bDevCapabilityType = 3,
3172f9f848faSopenharmony_ci		.bmAttributes = 0,
3173f9f848faSopenharmony_ci		HSETW(.wSpeedsSupported, 0x000C),
3174f9f848faSopenharmony_ci		.bFunctionalitySupport = 8,
3175f9f848faSopenharmony_ci		.bU1DevExitLat = 255,	/* dummy - not used */
3176f9f848faSopenharmony_ci		.wU2DevExitLat = { 0x00, 0x08 },
3177f9f848faSopenharmony_ci	},
3178f9f848faSopenharmony_ci	.cidd = {
3179f9f848faSopenharmony_ci		.bLength = sizeof(xhci_bosd.cidd),
3180f9f848faSopenharmony_ci		.bDescriptorType = 1,
3181f9f848faSopenharmony_ci		.bDevCapabilityType = 4,
3182f9f848faSopenharmony_ci		.bReserved = 0,
3183f9f848faSopenharmony_ci		.bContainerID = 0,
3184f9f848faSopenharmony_ci	},
3185f9f848faSopenharmony_ci};
3186f9f848faSopenharmony_ci
3187f9f848faSopenharmony_cistatic const
3188f9f848faSopenharmony_cistruct xhci_config_desc xhci_confd = {
3189f9f848faSopenharmony_ci	.confd = {
3190f9f848faSopenharmony_ci		.bLength = sizeof(xhci_confd.confd),
3191f9f848faSopenharmony_ci		.bDescriptorType = UDESC_CONFIG,
3192f9f848faSopenharmony_ci		.wTotalLength[0] = sizeof(xhci_confd),
3193f9f848faSopenharmony_ci		.bNumInterface = 1,
3194f9f848faSopenharmony_ci		.bConfigurationValue = 1,
3195f9f848faSopenharmony_ci		.iConfiguration = 0,
3196f9f848faSopenharmony_ci		.bmAttributes = UC_SELF_POWERED,
3197f9f848faSopenharmony_ci		.bMaxPower = 0		/* max power */
3198f9f848faSopenharmony_ci	},
3199f9f848faSopenharmony_ci	.ifcd = {
3200f9f848faSopenharmony_ci		.bLength = sizeof(xhci_confd.ifcd),
3201f9f848faSopenharmony_ci		.bDescriptorType = UDESC_INTERFACE,
3202f9f848faSopenharmony_ci		.bNumEndpoints = 1,
3203f9f848faSopenharmony_ci		.bInterfaceClass = UICLASS_HUB,
3204f9f848faSopenharmony_ci		.bInterfaceSubClass = UISUBCLASS_HUB,
3205f9f848faSopenharmony_ci		.bInterfaceProtocol = 0,
3206f9f848faSopenharmony_ci	},
3207f9f848faSopenharmony_ci	.endpd = {
3208f9f848faSopenharmony_ci		.bLength = sizeof(xhci_confd.endpd),
3209f9f848faSopenharmony_ci		.bDescriptorType = UDESC_ENDPOINT,
3210f9f848faSopenharmony_ci		.bEndpointAddress = UE_DIR_IN | XHCI_INTR_ENDPT,
3211f9f848faSopenharmony_ci		.bmAttributes = UE_INTERRUPT,
3212f9f848faSopenharmony_ci		.wMaxPacketSize[0] = 2,		/* max 15 ports */
3213f9f848faSopenharmony_ci		.bInterval = 255,
3214f9f848faSopenharmony_ci	},
3215f9f848faSopenharmony_ci	.endpcd = {
3216f9f848faSopenharmony_ci		.bLength = sizeof(xhci_confd.endpcd),
3217f9f848faSopenharmony_ci		.bDescriptorType = UDESC_ENDPOINT_SS_COMP,
3218f9f848faSopenharmony_ci		.bMaxBurst = 0,
3219f9f848faSopenharmony_ci		.bmAttributes = 0,
3220f9f848faSopenharmony_ci	},
3221f9f848faSopenharmony_ci};
3222f9f848faSopenharmony_ci
3223f9f848faSopenharmony_cistatic const
3224f9f848faSopenharmony_cistruct usb_hub_ss_descriptor xhci_hubd = {
3225f9f848faSopenharmony_ci	.bLength = sizeof(xhci_hubd),
3226f9f848faSopenharmony_ci	.bDescriptorType = UDESC_SS_HUB,
3227f9f848faSopenharmony_ci};
3228f9f848faSopenharmony_ci
3229f9f848faSopenharmony_cistatic usb_error_t
3230f9f848faSopenharmony_cixhci_roothub_exec(struct usb_device *udev,
3231f9f848faSopenharmony_ci    struct usb_device_request *req, const void **pptr, uint16_t *plength)
3232f9f848faSopenharmony_ci{
3233f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
3234f9f848faSopenharmony_ci	const char *str_ptr;
3235f9f848faSopenharmony_ci	const void *ptr;
3236f9f848faSopenharmony_ci	uint32_t port;
3237f9f848faSopenharmony_ci	uint32_t v;
3238f9f848faSopenharmony_ci	uint16_t len;
3239f9f848faSopenharmony_ci	uint16_t i;
3240f9f848faSopenharmony_ci	uint16_t value;
3241f9f848faSopenharmony_ci	uint16_t index;
3242f9f848faSopenharmony_ci	uint8_t j;
3243f9f848faSopenharmony_ci	usb_error_t err;
3244f9f848faSopenharmony_ci
3245f9f848faSopenharmony_ci	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
3246f9f848faSopenharmony_ci
3247f9f848faSopenharmony_ci	/* buffer reset */
3248f9f848faSopenharmony_ci	ptr = (const void *)&sc->sc_hub_desc;
3249f9f848faSopenharmony_ci	len = 0;
3250f9f848faSopenharmony_ci	err = USB_ERR_NORMAL_COMPLETION;
3251f9f848faSopenharmony_ci
3252f9f848faSopenharmony_ci	value = UGETW(req->wValue);
3253f9f848faSopenharmony_ci	index = UGETW(req->wIndex);
3254f9f848faSopenharmony_ci
3255f9f848faSopenharmony_ci	DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x "
3256f9f848faSopenharmony_ci	    "wValue=0x%04x wIndex=0x%04x\n",
3257f9f848faSopenharmony_ci	    req->bmRequestType, req->bRequest,
3258f9f848faSopenharmony_ci	    UGETW(req->wLength), value, index);
3259f9f848faSopenharmony_ci
3260f9f848faSopenharmony_ci#define	C(x,y) ((x) | ((y) << 8))
3261f9f848faSopenharmony_ci	switch (C(req->bRequest, req->bmRequestType)) {
3262f9f848faSopenharmony_ci	case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
3263f9f848faSopenharmony_ci	case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
3264f9f848faSopenharmony_ci	case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
3265f9f848faSopenharmony_ci		/*
3266f9f848faSopenharmony_ci		 * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
3267f9f848faSopenharmony_ci		 * for the integrated root hub.
3268f9f848faSopenharmony_ci		 */
3269f9f848faSopenharmony_ci		break;
3270f9f848faSopenharmony_ci	case C(UR_GET_CONFIG, UT_READ_DEVICE):
3271f9f848faSopenharmony_ci		len = 1;
3272f9f848faSopenharmony_ci		sc->sc_hub_desc.temp[0] = sc->sc_conf;
3273f9f848faSopenharmony_ci		break;
3274f9f848faSopenharmony_ci	case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
3275f9f848faSopenharmony_ci		switch (value >> 8) {
3276f9f848faSopenharmony_ci		case UDESC_DEVICE:
3277f9f848faSopenharmony_ci			if ((value & 0xff) != 0) {
3278f9f848faSopenharmony_ci				err = USB_ERR_IOERROR;
3279f9f848faSopenharmony_ci				goto done;
3280f9f848faSopenharmony_ci			}
3281f9f848faSopenharmony_ci			len = sizeof(xhci_devd);
3282f9f848faSopenharmony_ci			ptr = (const void *)&xhci_devd;
3283f9f848faSopenharmony_ci			break;
3284f9f848faSopenharmony_ci
3285f9f848faSopenharmony_ci		case UDESC_BOS:
3286f9f848faSopenharmony_ci			if ((value & 0xff) != 0) {
3287f9f848faSopenharmony_ci				err = USB_ERR_IOERROR;
3288f9f848faSopenharmony_ci				goto done;
3289f9f848faSopenharmony_ci			}
3290f9f848faSopenharmony_ci			len = sizeof(xhci_bosd);
3291f9f848faSopenharmony_ci			ptr = (const void *)&xhci_bosd;
3292f9f848faSopenharmony_ci			break;
3293f9f848faSopenharmony_ci
3294f9f848faSopenharmony_ci		case UDESC_CONFIG:
3295f9f848faSopenharmony_ci			if ((value & 0xff) != 0) {
3296f9f848faSopenharmony_ci				err = USB_ERR_IOERROR;
3297f9f848faSopenharmony_ci				goto done;
3298f9f848faSopenharmony_ci			}
3299f9f848faSopenharmony_ci			len = sizeof(xhci_confd);
3300f9f848faSopenharmony_ci			ptr = (const void *)&xhci_confd;
3301f9f848faSopenharmony_ci			break;
3302f9f848faSopenharmony_ci
3303f9f848faSopenharmony_ci		case UDESC_STRING:
3304f9f848faSopenharmony_ci			switch (value & 0xff) {
3305f9f848faSopenharmony_ci			case 0:	/* Language table */
3306f9f848faSopenharmony_ci				str_ptr = "\001";
3307f9f848faSopenharmony_ci				break;
3308f9f848faSopenharmony_ci
3309f9f848faSopenharmony_ci			case 1:	/* Vendor */
3310f9f848faSopenharmony_ci				str_ptr = sc->sc_vendor;
3311f9f848faSopenharmony_ci				break;
3312f9f848faSopenharmony_ci
3313f9f848faSopenharmony_ci			case 2:	/* Product */
3314f9f848faSopenharmony_ci				str_ptr = "XHCI root HUB";
3315f9f848faSopenharmony_ci				break;
3316f9f848faSopenharmony_ci
3317f9f848faSopenharmony_ci			default:
3318f9f848faSopenharmony_ci				str_ptr = "";
3319f9f848faSopenharmony_ci				break;
3320f9f848faSopenharmony_ci			}
3321f9f848faSopenharmony_ci
3322f9f848faSopenharmony_ci			len = usb_make_str_desc(
3323f9f848faSopenharmony_ci			    sc->sc_hub_desc.temp,
3324f9f848faSopenharmony_ci			    sizeof(sc->sc_hub_desc.temp),
3325f9f848faSopenharmony_ci			    str_ptr);
3326f9f848faSopenharmony_ci			break;
3327f9f848faSopenharmony_ci
3328f9f848faSopenharmony_ci		default:
3329f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3330f9f848faSopenharmony_ci			goto done;
3331f9f848faSopenharmony_ci		}
3332f9f848faSopenharmony_ci		break;
3333f9f848faSopenharmony_ci	case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
3334f9f848faSopenharmony_ci		len = 1;
3335f9f848faSopenharmony_ci		sc->sc_hub_desc.temp[0] = 0;
3336f9f848faSopenharmony_ci		break;
3337f9f848faSopenharmony_ci	case C(UR_GET_STATUS, UT_READ_DEVICE):
3338f9f848faSopenharmony_ci		len = 2;
3339f9f848faSopenharmony_ci		USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED);
3340f9f848faSopenharmony_ci		break;
3341f9f848faSopenharmony_ci	case C(UR_GET_STATUS, UT_READ_INTERFACE):
3342f9f848faSopenharmony_ci	case C(UR_GET_STATUS, UT_READ_ENDPOINT):
3343f9f848faSopenharmony_ci		len = 2;
3344f9f848faSopenharmony_ci		USETW(sc->sc_hub_desc.stat.wStatus, 0);
3345f9f848faSopenharmony_ci		break;
3346f9f848faSopenharmony_ci	case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
3347f9f848faSopenharmony_ci		if (value >= XHCI_MAX_DEVICES) {
3348f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3349f9f848faSopenharmony_ci			goto done;
3350f9f848faSopenharmony_ci		}
3351f9f848faSopenharmony_ci		break;
3352f9f848faSopenharmony_ci	case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
3353f9f848faSopenharmony_ci		if ((value != 0) && (value != 1)) {
3354f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3355f9f848faSopenharmony_ci			goto done;
3356f9f848faSopenharmony_ci		}
3357f9f848faSopenharmony_ci		sc->sc_conf = value;
3358f9f848faSopenharmony_ci		break;
3359f9f848faSopenharmony_ci	case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
3360f9f848faSopenharmony_ci		break;
3361f9f848faSopenharmony_ci	case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
3362f9f848faSopenharmony_ci	case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
3363f9f848faSopenharmony_ci	case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
3364f9f848faSopenharmony_ci		err = USB_ERR_IOERROR;
3365f9f848faSopenharmony_ci		goto done;
3366f9f848faSopenharmony_ci	case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
3367f9f848faSopenharmony_ci		break;
3368f9f848faSopenharmony_ci	case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
3369f9f848faSopenharmony_ci		break;
3370f9f848faSopenharmony_ci		/* Hub requests */
3371f9f848faSopenharmony_ci	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
3372f9f848faSopenharmony_ci		break;
3373f9f848faSopenharmony_ci	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
3374f9f848faSopenharmony_ci		DPRINTFN(9, "UR_CLEAR_PORT_FEATURE\n");
3375f9f848faSopenharmony_ci
3376f9f848faSopenharmony_ci		if ((index < 1) ||
3377f9f848faSopenharmony_ci		    (index > sc->sc_noport)) {
3378f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3379f9f848faSopenharmony_ci			goto done;
3380f9f848faSopenharmony_ci		}
3381f9f848faSopenharmony_ci		port = XHCI_PORTSC(index);
3382f9f848faSopenharmony_ci
3383f9f848faSopenharmony_ci		v = XREAD4(sc, oper, port);
3384f9f848faSopenharmony_ci		i = XHCI_PS_PLS_GET(v);
3385f9f848faSopenharmony_ci		v &= ~XHCI_PS_CLEAR;
3386f9f848faSopenharmony_ci
3387f9f848faSopenharmony_ci		switch (value) {
3388f9f848faSopenharmony_ci		case UHF_C_BH_PORT_RESET:
3389f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_WRC);
3390f9f848faSopenharmony_ci			break;
3391f9f848faSopenharmony_ci		case UHF_C_PORT_CONFIG_ERROR:
3392f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_CEC);
3393f9f848faSopenharmony_ci			break;
3394f9f848faSopenharmony_ci		case UHF_C_PORT_SUSPEND:
3395f9f848faSopenharmony_ci		case UHF_C_PORT_LINK_STATE:
3396f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_PLC);
3397f9f848faSopenharmony_ci			break;
3398f9f848faSopenharmony_ci		case UHF_C_PORT_CONNECTION:
3399f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_CSC);
3400f9f848faSopenharmony_ci			break;
3401f9f848faSopenharmony_ci		case UHF_C_PORT_ENABLE:
3402f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_PEC);
3403f9f848faSopenharmony_ci			break;
3404f9f848faSopenharmony_ci		case UHF_C_PORT_OVER_CURRENT:
3405f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_OCC);
3406f9f848faSopenharmony_ci			break;
3407f9f848faSopenharmony_ci		case UHF_C_PORT_RESET:
3408f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_PRC);
3409f9f848faSopenharmony_ci			break;
3410f9f848faSopenharmony_ci		case UHF_PORT_ENABLE:
3411f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_PED);
3412f9f848faSopenharmony_ci			break;
3413f9f848faSopenharmony_ci		case UHF_PORT_POWER:
3414f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v & ~XHCI_PS_PP);
3415f9f848faSopenharmony_ci			break;
3416f9f848faSopenharmony_ci		case UHF_PORT_INDICATOR:
3417f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v & ~XHCI_PS_PIC_SET(3));
3418f9f848faSopenharmony_ci			break;
3419f9f848faSopenharmony_ci		case UHF_PORT_SUSPEND:
3420f9f848faSopenharmony_ci
3421f9f848faSopenharmony_ci			/* U3 -> U15 */
3422f9f848faSopenharmony_ci			if (i == 3) {
3423f9f848faSopenharmony_ci				XWRITE4(sc, oper, port, v |
3424f9f848faSopenharmony_ci				    XHCI_PS_PLS_SET(0xF) | XHCI_PS_LWS);
3425f9f848faSopenharmony_ci			}
3426f9f848faSopenharmony_ci
3427f9f848faSopenharmony_ci			/* wait 20ms for resume sequence to complete */
3428f9f848faSopenharmony_ci			usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50);
3429f9f848faSopenharmony_ci
3430f9f848faSopenharmony_ci			/* U0 */
3431f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v |
3432f9f848faSopenharmony_ci			    XHCI_PS_PLS_SET(0) | XHCI_PS_LWS);
3433f9f848faSopenharmony_ci			break;
3434f9f848faSopenharmony_ci		default:
3435f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3436f9f848faSopenharmony_ci			goto done;
3437f9f848faSopenharmony_ci		}
3438f9f848faSopenharmony_ci		break;
3439f9f848faSopenharmony_ci
3440f9f848faSopenharmony_ci	case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
3441f9f848faSopenharmony_ci		if ((value & 0xff) != 0) {
3442f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3443f9f848faSopenharmony_ci			goto done;
3444f9f848faSopenharmony_ci		}
3445f9f848faSopenharmony_ci
3446f9f848faSopenharmony_ci		v = XREAD4(sc, capa, XHCI_HCSPARAMS0);
3447f9f848faSopenharmony_ci
3448f9f848faSopenharmony_ci		sc->sc_hub_desc.hubd = xhci_hubd;
3449f9f848faSopenharmony_ci
3450f9f848faSopenharmony_ci		sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport;
3451f9f848faSopenharmony_ci
3452f9f848faSopenharmony_ci		if (XHCI_HCS0_PPC(v))
3453f9f848faSopenharmony_ci			i = UHD_PWR_INDIVIDUAL;
3454f9f848faSopenharmony_ci		else
3455f9f848faSopenharmony_ci			i = UHD_PWR_GANGED;
3456f9f848faSopenharmony_ci
3457f9f848faSopenharmony_ci		if (XHCI_HCS0_PIND(v))
3458f9f848faSopenharmony_ci			i |= UHD_PORT_IND;
3459f9f848faSopenharmony_ci
3460f9f848faSopenharmony_ci		i |= UHD_OC_INDIVIDUAL;
3461f9f848faSopenharmony_ci
3462f9f848faSopenharmony_ci		USETW(sc->sc_hub_desc.hubd.wHubCharacteristics, i);
3463f9f848faSopenharmony_ci
3464f9f848faSopenharmony_ci		/* see XHCI section 5.4.9: */
3465f9f848faSopenharmony_ci		sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 10;
3466f9f848faSopenharmony_ci
3467f9f848faSopenharmony_ci		for (j = 1; j <= sc->sc_noport; j++) {
3468f9f848faSopenharmony_ci			v = XREAD4(sc, oper, XHCI_PORTSC(j));
3469f9f848faSopenharmony_ci			if (v & XHCI_PS_DR) {
3470f9f848faSopenharmony_ci				sc->sc_hub_desc.hubd.
3471f9f848faSopenharmony_ci				    DeviceRemovable[j / 8] |= 1U << (j % 8);
3472f9f848faSopenharmony_ci			}
3473f9f848faSopenharmony_ci		}
3474f9f848faSopenharmony_ci		len = sc->sc_hub_desc.hubd.bLength;
3475f9f848faSopenharmony_ci		break;
3476f9f848faSopenharmony_ci
3477f9f848faSopenharmony_ci	case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
3478f9f848faSopenharmony_ci		len = 16;
3479f9f848faSopenharmony_ci		(void)memset_s(sc->sc_hub_desc.temp, sizeof(sc->sc_hub_desc.temp), 0, len);
3480f9f848faSopenharmony_ci		break;
3481f9f848faSopenharmony_ci
3482f9f848faSopenharmony_ci	case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
3483f9f848faSopenharmony_ci		DPRINTFN(9, "UR_GET_STATUS i=%d\n", index);
3484f9f848faSopenharmony_ci
3485f9f848faSopenharmony_ci		if ((index < 1) ||
3486f9f848faSopenharmony_ci		    (index > sc->sc_noport)) {
3487f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3488f9f848faSopenharmony_ci			goto done;
3489f9f848faSopenharmony_ci		}
3490f9f848faSopenharmony_ci
3491f9f848faSopenharmony_ci		v = XREAD4(sc, oper, XHCI_PORTSC(index));
3492f9f848faSopenharmony_ci
3493f9f848faSopenharmony_ci		DPRINTFN(9, "port status=0x%08x\n", v);
3494f9f848faSopenharmony_ci
3495f9f848faSopenharmony_ci		i = UPS_PORT_LINK_STATE_SET(XHCI_PS_PLS_GET(v));
3496f9f848faSopenharmony_ci
3497f9f848faSopenharmony_ci		switch (XHCI_PS_SPEED_GET(v)) {
3498f9f848faSopenharmony_ci		case 3:
3499f9f848faSopenharmony_ci			i |= UPS_HIGH_SPEED;
3500f9f848faSopenharmony_ci			break;
3501f9f848faSopenharmony_ci		case 2:
3502f9f848faSopenharmony_ci			i |= UPS_LOW_SPEED;
3503f9f848faSopenharmony_ci			break;
3504f9f848faSopenharmony_ci		case 1:
3505f9f848faSopenharmony_ci			/* FULL speed */
3506f9f848faSopenharmony_ci			break;
3507f9f848faSopenharmony_ci		default:
3508f9f848faSopenharmony_ci			i |= UPS_OTHER_SPEED;
3509f9f848faSopenharmony_ci			break;
3510f9f848faSopenharmony_ci		}
3511f9f848faSopenharmony_ci
3512f9f848faSopenharmony_ci		if (v & XHCI_PS_CCS)
3513f9f848faSopenharmony_ci			i |= UPS_CURRENT_CONNECT_STATUS;
3514f9f848faSopenharmony_ci		if (v & XHCI_PS_PED)
3515f9f848faSopenharmony_ci			i |= UPS_PORT_ENABLED;
3516f9f848faSopenharmony_ci		if (v & XHCI_PS_OCA)
3517f9f848faSopenharmony_ci			i |= UPS_OVERCURRENT_INDICATOR;
3518f9f848faSopenharmony_ci		if (v & XHCI_PS_PR)
3519f9f848faSopenharmony_ci			i |= UPS_RESET;
3520f9f848faSopenharmony_ci		if (v & XHCI_PS_PP) {
3521f9f848faSopenharmony_ci			/*
3522f9f848faSopenharmony_ci			 * The USB 3.0 RH is using the
3523f9f848faSopenharmony_ci			 * USB 2.0's power bit
3524f9f848faSopenharmony_ci			 */
3525f9f848faSopenharmony_ci			i |= UPS_PORT_POWER;
3526f9f848faSopenharmony_ci		}
3527f9f848faSopenharmony_ci		USETW(sc->sc_hub_desc.ps.wPortStatus, i);
3528f9f848faSopenharmony_ci
3529f9f848faSopenharmony_ci		i = 0;
3530f9f848faSopenharmony_ci		if (v & XHCI_PS_CSC)
3531f9f848faSopenharmony_ci			i |= UPS_C_CONNECT_STATUS;
3532f9f848faSopenharmony_ci		if (v & XHCI_PS_PEC)
3533f9f848faSopenharmony_ci			i |= UPS_C_PORT_ENABLED;
3534f9f848faSopenharmony_ci		if (v & XHCI_PS_OCC)
3535f9f848faSopenharmony_ci			i |= UPS_C_OVERCURRENT_INDICATOR;
3536f9f848faSopenharmony_ci		if (v & XHCI_PS_WRC)
3537f9f848faSopenharmony_ci			i |= UPS_C_BH_PORT_RESET;
3538f9f848faSopenharmony_ci		if (v & XHCI_PS_PRC)
3539f9f848faSopenharmony_ci			i |= UPS_C_PORT_RESET;
3540f9f848faSopenharmony_ci		if (v & XHCI_PS_PLC)
3541f9f848faSopenharmony_ci			i |= UPS_C_PORT_LINK_STATE;
3542f9f848faSopenharmony_ci		if (v & XHCI_PS_CEC)
3543f9f848faSopenharmony_ci			i |= UPS_C_PORT_CONFIG_ERROR;
3544f9f848faSopenharmony_ci
3545f9f848faSopenharmony_ci		USETW(sc->sc_hub_desc.ps.wPortChange, i);
3546f9f848faSopenharmony_ci		len = sizeof(sc->sc_hub_desc.ps);
3547f9f848faSopenharmony_ci		break;
3548f9f848faSopenharmony_ci
3549f9f848faSopenharmony_ci	case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
3550f9f848faSopenharmony_ci		err = USB_ERR_IOERROR;
3551f9f848faSopenharmony_ci		goto done;
3552f9f848faSopenharmony_ci
3553f9f848faSopenharmony_ci	case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
3554f9f848faSopenharmony_ci		break;
3555f9f848faSopenharmony_ci
3556f9f848faSopenharmony_ci	case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
3557f9f848faSopenharmony_ci
3558f9f848faSopenharmony_ci		i = index >> 8;
3559f9f848faSopenharmony_ci		index &= 0x00FF;
3560f9f848faSopenharmony_ci
3561f9f848faSopenharmony_ci		if ((index < 1) ||
3562f9f848faSopenharmony_ci		    (index > sc->sc_noport)) {
3563f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3564f9f848faSopenharmony_ci			goto done;
3565f9f848faSopenharmony_ci		}
3566f9f848faSopenharmony_ci
3567f9f848faSopenharmony_ci		port = XHCI_PORTSC(index);
3568f9f848faSopenharmony_ci		v = XREAD4(sc, oper, port) & ~XHCI_PS_CLEAR;
3569f9f848faSopenharmony_ci
3570f9f848faSopenharmony_ci		switch (value) {
3571f9f848faSopenharmony_ci		case UHF_PORT_U1_TIMEOUT:
3572f9f848faSopenharmony_ci			if (XHCI_PS_SPEED_GET(v) != 4) {
3573f9f848faSopenharmony_ci				err = USB_ERR_IOERROR;
3574f9f848faSopenharmony_ci				goto done;
3575f9f848faSopenharmony_ci			}
3576f9f848faSopenharmony_ci			port = XHCI_PORTPMSC(index);
3577f9f848faSopenharmony_ci			v = XREAD4(sc, oper, port);
3578f9f848faSopenharmony_ci			v &= ~XHCI_PM3_U1TO_SET(0xFF);
3579f9f848faSopenharmony_ci			v |= XHCI_PM3_U1TO_SET(i);
3580f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v);
3581f9f848faSopenharmony_ci			break;
3582f9f848faSopenharmony_ci		case UHF_PORT_U2_TIMEOUT:
3583f9f848faSopenharmony_ci			if (XHCI_PS_SPEED_GET(v) != 4) {
3584f9f848faSopenharmony_ci				err = USB_ERR_IOERROR;
3585f9f848faSopenharmony_ci				goto done;
3586f9f848faSopenharmony_ci			}
3587f9f848faSopenharmony_ci			port = XHCI_PORTPMSC(index);
3588f9f848faSopenharmony_ci			v = XREAD4(sc, oper, port);
3589f9f848faSopenharmony_ci			v &= ~XHCI_PM3_U2TO_SET(0xFF);
3590f9f848faSopenharmony_ci			v |= XHCI_PM3_U2TO_SET(i);
3591f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v);
3592f9f848faSopenharmony_ci			break;
3593f9f848faSopenharmony_ci		case UHF_BH_PORT_RESET:
3594f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_WPR);
3595f9f848faSopenharmony_ci			break;
3596f9f848faSopenharmony_ci		case UHF_PORT_LINK_STATE:
3597f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v |
3598f9f848faSopenharmony_ci			    XHCI_PS_PLS_SET(i) | XHCI_PS_LWS);
3599f9f848faSopenharmony_ci			/* 4ms settle time */
3600f9f848faSopenharmony_ci			usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250);
3601f9f848faSopenharmony_ci			break;
3602f9f848faSopenharmony_ci		case UHF_PORT_ENABLE:
3603f9f848faSopenharmony_ci			DPRINTFN(3, "set port enable %d\n", index);
3604f9f848faSopenharmony_ci			break;
3605f9f848faSopenharmony_ci		case UHF_PORT_SUSPEND:
3606f9f848faSopenharmony_ci			DPRINTFN(6, "suspend port %u (LPM=%u)\n", index, i);
3607f9f848faSopenharmony_ci			j = XHCI_PS_SPEED_GET(v);
3608f9f848faSopenharmony_ci			if ((j < 1) || (j > 3)) {
3609f9f848faSopenharmony_ci				/* non-supported speed */
3610f9f848faSopenharmony_ci				err = USB_ERR_IOERROR;
3611f9f848faSopenharmony_ci				goto done;
3612f9f848faSopenharmony_ci			}
3613f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v |
3614f9f848faSopenharmony_ci			    XHCI_PS_PLS_SET(i ? 2 /* LPM */ : 3) | XHCI_PS_LWS);
3615f9f848faSopenharmony_ci			break;
3616f9f848faSopenharmony_ci		case UHF_PORT_RESET:
3617f9f848faSopenharmony_ci			DPRINTFN(6, "reset port %d\n", index);
3618f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_PR);
3619f9f848faSopenharmony_ci			break;
3620f9f848faSopenharmony_ci		case UHF_PORT_POWER:
3621f9f848faSopenharmony_ci			DPRINTFN(3, "set port power %d\n", index);
3622f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v | XHCI_PS_PP);
3623f9f848faSopenharmony_ci			break;
3624f9f848faSopenharmony_ci		case UHF_PORT_TEST:
3625f9f848faSopenharmony_ci			DPRINTFN(3, "set port test %d\n", index);
3626f9f848faSopenharmony_ci			break;
3627f9f848faSopenharmony_ci		case UHF_PORT_INDICATOR:
3628f9f848faSopenharmony_ci			DPRINTFN(3, "set port indicator %d\n", index);
3629f9f848faSopenharmony_ci
3630f9f848faSopenharmony_ci			v &= ~XHCI_PS_PIC_SET(3);
3631f9f848faSopenharmony_ci			v |= XHCI_PS_PIC_SET(1);
3632f9f848faSopenharmony_ci
3633f9f848faSopenharmony_ci			XWRITE4(sc, oper, port, v);
3634f9f848faSopenharmony_ci			break;
3635f9f848faSopenharmony_ci		default:
3636f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3637f9f848faSopenharmony_ci			goto done;
3638f9f848faSopenharmony_ci		}
3639f9f848faSopenharmony_ci		break;
3640f9f848faSopenharmony_ci
3641f9f848faSopenharmony_ci	case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER):
3642f9f848faSopenharmony_ci	case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER):
3643f9f848faSopenharmony_ci	case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER):
3644f9f848faSopenharmony_ci	case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER):
3645f9f848faSopenharmony_ci		break;
3646f9f848faSopenharmony_ci	default:
3647f9f848faSopenharmony_ci		err = USB_ERR_IOERROR;
3648f9f848faSopenharmony_ci		goto done;
3649f9f848faSopenharmony_ci	}
3650f9f848faSopenharmony_cidone:
3651f9f848faSopenharmony_ci	*plength = len;
3652f9f848faSopenharmony_ci	*pptr = ptr;
3653f9f848faSopenharmony_ci	return (err);
3654f9f848faSopenharmony_ci}
3655f9f848faSopenharmony_ci
3656f9f848faSopenharmony_cistatic void
3657f9f848faSopenharmony_cixhci_xfer_setup(struct usb_setup_params *parm)
3658f9f848faSopenharmony_ci{
3659f9f848faSopenharmony_ci	struct usb_page_search page_info;
3660f9f848faSopenharmony_ci	struct usb_page_cache *pc;
3661f9f848faSopenharmony_ci	struct usb_xfer *xfer;
3662f9f848faSopenharmony_ci	void *last_obj;
3663f9f848faSopenharmony_ci	uint32_t ntd;
3664f9f848faSopenharmony_ci	uint32_t n;
3665f9f848faSopenharmony_ci
3666f9f848faSopenharmony_ci	xfer = parm->curr_xfer;
3667f9f848faSopenharmony_ci
3668f9f848faSopenharmony_ci	/*
3669f9f848faSopenharmony_ci	 * The proof for the "ntd" formula is illustrated like this:
3670f9f848faSopenharmony_ci	 *
3671f9f848faSopenharmony_ci	 * +------------------------------------+
3672f9f848faSopenharmony_ci	 * |                                    |
3673f9f848faSopenharmony_ci	 * |         |remainder ->              |
3674f9f848faSopenharmony_ci	 * |   +-----+---+                      |
3675f9f848faSopenharmony_ci	 * |   | xxx | x | frm 0                |
3676f9f848faSopenharmony_ci	 * |   +-----+---++                     |
3677f9f848faSopenharmony_ci	 * |   | xxx | xx | frm 1               |
3678f9f848faSopenharmony_ci	 * |   +-----+----+                     |
3679f9f848faSopenharmony_ci	 * |            ...                     |
3680f9f848faSopenharmony_ci	 * +------------------------------------+
3681f9f848faSopenharmony_ci	 *
3682f9f848faSopenharmony_ci	 * "xxx" means a completely full USB transfer descriptor
3683f9f848faSopenharmony_ci	 *
3684f9f848faSopenharmony_ci	 * "x" and "xx" means a short USB packet
3685f9f848faSopenharmony_ci	 *
3686f9f848faSopenharmony_ci	 * For the remainder of an USB transfer modulo
3687f9f848faSopenharmony_ci	 * "max_data_length" we need two USB transfer descriptors.
3688f9f848faSopenharmony_ci	 * One to transfer the remaining data and one to finalise with
3689f9f848faSopenharmony_ci	 * a zero length packet in case the "force_short_xfer" flag is
3690f9f848faSopenharmony_ci	 * set. We only need two USB transfer descriptors in the case
3691f9f848faSopenharmony_ci	 * where the transfer length of the first one is a factor of
3692f9f848faSopenharmony_ci	 * "max_frame_size". The rest of the needed USB transfer
3693f9f848faSopenharmony_ci	 * descriptors is given by the buffer size divided by the
3694f9f848faSopenharmony_ci	 * maximum data payload.
3695f9f848faSopenharmony_ci	 */
3696f9f848faSopenharmony_ci	parm->hc_max_packet_size = 0x400;
3697f9f848faSopenharmony_ci	parm->hc_max_packet_count = 16 * 3;
3698f9f848faSopenharmony_ci	parm->hc_max_frame_size = XHCI_TD_PAYLOAD_MAX;
3699f9f848faSopenharmony_ci
3700f9f848faSopenharmony_ci	xfer->flags_int.bdma_enable = 1;
3701f9f848faSopenharmony_ci
3702f9f848faSopenharmony_ci	usbd_transfer_setup_sub(parm);
3703f9f848faSopenharmony_ci
3704f9f848faSopenharmony_ci	if (xfer->flags_int.isochronous_xfr) {
3705f9f848faSopenharmony_ci		ntd = ((1 * xfer->nframes)
3706f9f848faSopenharmony_ci		    + (xfer->max_data_length / xfer->max_hc_frame_size));
3707f9f848faSopenharmony_ci	} else if (xfer->flags_int.control_xfr) {
3708f9f848faSopenharmony_ci		ntd = ((2 * xfer->nframes) + 1	/* STATUS */
3709f9f848faSopenharmony_ci		    + (xfer->max_data_length / xfer->max_hc_frame_size));
3710f9f848faSopenharmony_ci	} else {
3711f9f848faSopenharmony_ci		ntd = ((2 * xfer->nframes)
3712f9f848faSopenharmony_ci		    + (xfer->max_data_length / xfer->max_hc_frame_size));
3713f9f848faSopenharmony_ci	}
3714f9f848faSopenharmony_ci
3715f9f848faSopenharmony_cialloc_dma_set:
3716f9f848faSopenharmony_ci
3717f9f848faSopenharmony_ci	if (parm->err)
3718f9f848faSopenharmony_ci		return;
3719f9f848faSopenharmony_ci
3720f9f848faSopenharmony_ci	/*
3721f9f848faSopenharmony_ci	 * Allocate queue heads and transfer descriptors
3722f9f848faSopenharmony_ci	 */
3723f9f848faSopenharmony_ci	last_obj = NULL;
3724f9f848faSopenharmony_ci
3725f9f848faSopenharmony_ci	if (usbd_transfer_setup_sub_malloc(
3726f9f848faSopenharmony_ci	    parm, &pc, sizeof(struct xhci_td),
3727f9f848faSopenharmony_ci	    XHCI_TD_ALIGN, ntd)) {
3728f9f848faSopenharmony_ci		parm->err = USB_ERR_NOMEM;
3729f9f848faSopenharmony_ci		return;
3730f9f848faSopenharmony_ci	}
3731f9f848faSopenharmony_ci	if (parm->buf) {
3732f9f848faSopenharmony_ci		for (n = 0; n != ntd; n++) {
3733f9f848faSopenharmony_ci			struct xhci_td *td;
3734f9f848faSopenharmony_ci
3735f9f848faSopenharmony_ci			usbd_get_page(pc + n, 0, &page_info);
3736f9f848faSopenharmony_ci
3737f9f848faSopenharmony_ci			td = page_info.buffer;
3738f9f848faSopenharmony_ci
3739f9f848faSopenharmony_ci			/* init TD */
3740f9f848faSopenharmony_ci			td->td_self = page_info.physaddr;
3741f9f848faSopenharmony_ci			td->obj_next = last_obj;
3742f9f848faSopenharmony_ci			td->page_cache = pc + n;
3743f9f848faSopenharmony_ci
3744f9f848faSopenharmony_ci			last_obj = td;
3745f9f848faSopenharmony_ci
3746f9f848faSopenharmony_ci			usb_pc_cpu_flush(pc + n);
3747f9f848faSopenharmony_ci		}
3748f9f848faSopenharmony_ci	}
3749f9f848faSopenharmony_ci	xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj;
3750f9f848faSopenharmony_ci
3751f9f848faSopenharmony_ci	if (!xfer->flags_int.curr_dma_set) {
3752f9f848faSopenharmony_ci		xfer->flags_int.curr_dma_set = 1;
3753f9f848faSopenharmony_ci		goto alloc_dma_set;
3754f9f848faSopenharmony_ci	}
3755f9f848faSopenharmony_ci}
3756f9f848faSopenharmony_ci
3757f9f848faSopenharmony_cistatic usb_error_t
3758f9f848faSopenharmony_cixhci_configure_reset_endpoint(struct usb_xfer *xfer)
3759f9f848faSopenharmony_ci{
3760f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus);
3761f9f848faSopenharmony_ci	struct usb_page_search buf_inp;
3762f9f848faSopenharmony_ci	struct usb_device *udev;
3763f9f848faSopenharmony_ci	struct xhci_endpoint_ext *pepext;
3764f9f848faSopenharmony_ci	struct usb_endpoint_descriptor *edesc;
3765f9f848faSopenharmony_ci	struct usb_page_cache *pcinp;
3766f9f848faSopenharmony_ci	usb_error_t err;
3767f9f848faSopenharmony_ci	usb_stream_t stream_id;
3768f9f848faSopenharmony_ci	uint8_t index;
3769f9f848faSopenharmony_ci	uint8_t epno;
3770f9f848faSopenharmony_ci
3771f9f848faSopenharmony_ci	pepext = xhci_get_endpoint_ext(xfer->xroot->udev,
3772f9f848faSopenharmony_ci	    xfer->endpoint->edesc);
3773f9f848faSopenharmony_ci
3774f9f848faSopenharmony_ci	udev = xfer->xroot->udev;
3775f9f848faSopenharmony_ci	index = udev->controller_slot_id;
3776f9f848faSopenharmony_ci
3777f9f848faSopenharmony_ci	pcinp = &sc->sc_hw.devs[index].input_pc;
3778f9f848faSopenharmony_ci
3779f9f848faSopenharmony_ci	usbd_get_page(pcinp, 0, &buf_inp);
3780f9f848faSopenharmony_ci
3781f9f848faSopenharmony_ci	edesc = xfer->endpoint->edesc;
3782f9f848faSopenharmony_ci
3783f9f848faSopenharmony_ci	epno = edesc->bEndpointAddress;
3784f9f848faSopenharmony_ci	stream_id = xfer->stream_id;
3785f9f848faSopenharmony_ci
3786f9f848faSopenharmony_ci	if ((edesc->bmAttributes & UE_XFERTYPE) == UE_CONTROL)
3787f9f848faSopenharmony_ci		epno |= UE_DIR_IN;
3788f9f848faSopenharmony_ci
3789f9f848faSopenharmony_ci	epno = XHCI_EPNO2EPID(epno);
3790f9f848faSopenharmony_ci
3791f9f848faSopenharmony_ci	if (epno == 0)
3792f9f848faSopenharmony_ci		return (USB_ERR_NO_PIPE);		/* invalid */
3793f9f848faSopenharmony_ci
3794f9f848faSopenharmony_ci	XHCI_CMD_LOCK(sc);
3795f9f848faSopenharmony_ci
3796f9f848faSopenharmony_ci	/* configure endpoint */
3797f9f848faSopenharmony_ci
3798f9f848faSopenharmony_ci	err = xhci_configure_endpoint_by_xfer(xfer);
3799f9f848faSopenharmony_ci
3800f9f848faSopenharmony_ci	if (err != 0) {
3801f9f848faSopenharmony_ci		XHCI_CMD_UNLOCK(sc);
3802f9f848faSopenharmony_ci		return (err);
3803f9f848faSopenharmony_ci	}
3804f9f848faSopenharmony_ci
3805f9f848faSopenharmony_ci	/*
3806f9f848faSopenharmony_ci	 * Get the endpoint into the stopped state according to the
3807f9f848faSopenharmony_ci	 * endpoint context state diagram in the XHCI specification:
3808f9f848faSopenharmony_ci	 */
3809f9f848faSopenharmony_ci
3810f9f848faSopenharmony_ci	err = xhci_cmd_stop_ep(sc, 0, epno, index);
3811f9f848faSopenharmony_ci
3812f9f848faSopenharmony_ci	if (err != 0)
3813f9f848faSopenharmony_ci		DPRINTF("Could not stop endpoint %u\n", epno);
3814f9f848faSopenharmony_ci
3815f9f848faSopenharmony_ci	err = xhci_cmd_reset_ep(sc, 0, epno, index);
3816f9f848faSopenharmony_ci
3817f9f848faSopenharmony_ci	if (err != 0)
3818f9f848faSopenharmony_ci		DPRINTF("Could not reset endpoint %u\n", epno);
3819f9f848faSopenharmony_ci
3820f9f848faSopenharmony_ci	err = xhci_cmd_set_tr_dequeue_ptr(sc,
3821f9f848faSopenharmony_ci	    (pepext->physaddr + (stream_id * sizeof(struct xhci_trb) *
3822f9f848faSopenharmony_ci	    XHCI_MAX_TRANSFERS)) | XHCI_EPCTX_2_DCS_SET(1),
3823f9f848faSopenharmony_ci	    stream_id, epno, index);
3824f9f848faSopenharmony_ci
3825f9f848faSopenharmony_ci	if (err != 0)
3826f9f848faSopenharmony_ci		DPRINTF("Could not set dequeue ptr for endpoint %u\n", epno);
3827f9f848faSopenharmony_ci
3828f9f848faSopenharmony_ci	/*
3829f9f848faSopenharmony_ci	 * Get the endpoint into the running state according to the
3830f9f848faSopenharmony_ci	 * endpoint context state diagram in the XHCI specification:
3831f9f848faSopenharmony_ci	 */
3832f9f848faSopenharmony_ci
3833f9f848faSopenharmony_ci	(void)xhci_configure_mask(udev, (1U << epno) | 1U, 0);
3834f9f848faSopenharmony_ci
3835f9f848faSopenharmony_ci	if (epno > 1)
3836f9f848faSopenharmony_ci		err = xhci_cmd_configure_ep(sc, buf_inp.physaddr, 0, index);
3837f9f848faSopenharmony_ci	else
3838f9f848faSopenharmony_ci		err = xhci_cmd_evaluate_ctx(sc, buf_inp.physaddr, index);
3839f9f848faSopenharmony_ci
3840f9f848faSopenharmony_ci	if (err != 0)
3841f9f848faSopenharmony_ci		DPRINTF("Could not configure endpoint %u\n", epno);
3842f9f848faSopenharmony_ci
3843f9f848faSopenharmony_ci	XHCI_CMD_UNLOCK(sc);
3844f9f848faSopenharmony_ci
3845f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
3846f9f848faSopenharmony_ci}
3847f9f848faSopenharmony_ci
3848f9f848faSopenharmony_cistatic void
3849f9f848faSopenharmony_cixhci_xfer_unsetup(struct usb_xfer *xfer)
3850f9f848faSopenharmony_ci{
3851f9f848faSopenharmony_ci	return;
3852f9f848faSopenharmony_ci}
3853f9f848faSopenharmony_ci
3854f9f848faSopenharmony_cistatic void
3855f9f848faSopenharmony_cixhci_start_dma_delay(struct usb_xfer *xfer)
3856f9f848faSopenharmony_ci{
3857f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(xfer->xroot->bus);
3858f9f848faSopenharmony_ci
3859f9f848faSopenharmony_ci	/* put transfer on interrupt queue (again) */
3860f9f848faSopenharmony_ci	usbd_transfer_enqueue(&sc->sc_bus.intr_q, xfer);
3861f9f848faSopenharmony_ci
3862f9f848faSopenharmony_ci	(void)usb_proc_msignal(USB_BUS_CONTROL_XFER_PROC(&sc->sc_bus),
3863f9f848faSopenharmony_ci	    &sc->sc_config_msg[0], &sc->sc_config_msg[1]);
3864f9f848faSopenharmony_ci}
3865f9f848faSopenharmony_ci
3866f9f848faSopenharmony_cistatic void
3867f9f848faSopenharmony_cixhci_configure_msg(struct usb_proc_msg *pm)
3868f9f848faSopenharmony_ci{
3869f9f848faSopenharmony_ci	struct xhci_softc *sc;
3870f9f848faSopenharmony_ci	struct xhci_endpoint_ext *pepext;
3871f9f848faSopenharmony_ci	struct usb_xfer *xfer;
3872f9f848faSopenharmony_ci
3873f9f848faSopenharmony_ci	sc = XHCI_BUS2SC(((struct usb_bus_msg *)pm)->bus);
3874f9f848faSopenharmony_ci
3875f9f848faSopenharmony_cirestart:
3876f9f848faSopenharmony_ci	TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
3877f9f848faSopenharmony_ci		pepext = xhci_get_endpoint_ext(xfer->xroot->udev,
3878f9f848faSopenharmony_ci		    xfer->endpoint->edesc);
3879f9f848faSopenharmony_ci
3880f9f848faSopenharmony_ci		if ((pepext->trb_halted != 0) ||
3881f9f848faSopenharmony_ci		    (pepext->trb_running == 0)) {
3882f9f848faSopenharmony_ci			uint16_t i;
3883f9f848faSopenharmony_ci
3884f9f848faSopenharmony_ci			/* clear halted and running */
3885f9f848faSopenharmony_ci			pepext->trb_halted = 0;
3886f9f848faSopenharmony_ci			pepext->trb_running = 0;
3887f9f848faSopenharmony_ci
3888f9f848faSopenharmony_ci			/* nuke remaining buffered transfers */
3889f9f848faSopenharmony_ci
3890f9f848faSopenharmony_ci			for (i = 0; i != (XHCI_MAX_TRANSFERS *
3891f9f848faSopenharmony_ci			    XHCI_MAX_STREAMS); i++) {
3892f9f848faSopenharmony_ci				/*
3893f9f848faSopenharmony_ci				 * NOTE: We need to use the timeout
3894f9f848faSopenharmony_ci				 * error code here else existing
3895f9f848faSopenharmony_ci				 * isochronous clients can get
3896f9f848faSopenharmony_ci				 * confused:
3897f9f848faSopenharmony_ci				 */
3898f9f848faSopenharmony_ci				if (pepext->xfer[i] != NULL) {
3899f9f848faSopenharmony_ci					xhci_device_done(pepext->xfer[i],
3900f9f848faSopenharmony_ci					    USB_ERR_TIMEOUT);
3901f9f848faSopenharmony_ci				}
3902f9f848faSopenharmony_ci			}
3903f9f848faSopenharmony_ci
3904f9f848faSopenharmony_ci			/*
3905f9f848faSopenharmony_ci			 * NOTE: The USB transfer cannot vanish in
3906f9f848faSopenharmony_ci			 * this state!
3907f9f848faSopenharmony_ci			 */
3908f9f848faSopenharmony_ci
3909f9f848faSopenharmony_ci			USB_BUS_UNLOCK(&sc->sc_bus);
3910f9f848faSopenharmony_ci
3911f9f848faSopenharmony_ci			(void)xhci_configure_reset_endpoint(xfer);
3912f9f848faSopenharmony_ci
3913f9f848faSopenharmony_ci			USB_BUS_LOCK(&sc->sc_bus);
3914f9f848faSopenharmony_ci
3915f9f848faSopenharmony_ci			/* check if halted is still cleared */
3916f9f848faSopenharmony_ci			if (pepext->trb_halted == 0) {
3917f9f848faSopenharmony_ci				pepext->trb_running = 1;
3918f9f848faSopenharmony_ci				(void)memset_s(pepext->trb_index, sizeof(pepext->trb_index),
3919f9f848faSopenharmony_ci				    0, sizeof(pepext->trb_index));
3920f9f848faSopenharmony_ci			}
3921f9f848faSopenharmony_ci			goto restart;
3922f9f848faSopenharmony_ci		}
3923f9f848faSopenharmony_ci
3924f9f848faSopenharmony_ci		if (xfer->flags_int.did_dma_delay) {
3925f9f848faSopenharmony_ci			/* remove transfer from interrupt queue (again) */
3926f9f848faSopenharmony_ci			usbd_transfer_dequeue(xfer);
3927f9f848faSopenharmony_ci
3928f9f848faSopenharmony_ci			/* we are finally done */
3929f9f848faSopenharmony_ci			usb_dma_delay_done_cb(xfer);
3930f9f848faSopenharmony_ci
3931f9f848faSopenharmony_ci			/* queue changed - restart */
3932f9f848faSopenharmony_ci			goto restart;
3933f9f848faSopenharmony_ci		}
3934f9f848faSopenharmony_ci	}
3935f9f848faSopenharmony_ci
3936f9f848faSopenharmony_ci	TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
3937f9f848faSopenharmony_ci		/* try to insert xfer on HW queue */
3938f9f848faSopenharmony_ci		(void)xhci_transfer_insert(xfer);
3939f9f848faSopenharmony_ci
3940f9f848faSopenharmony_ci		/* try to multi buffer */
3941f9f848faSopenharmony_ci		xhci_device_generic_multi_enter(xfer->endpoint,
3942f9f848faSopenharmony_ci		    xfer->stream_id, NULL);
3943f9f848faSopenharmony_ci	}
3944f9f848faSopenharmony_ci}
3945f9f848faSopenharmony_ci
3946f9f848faSopenharmony_cistatic void
3947f9f848faSopenharmony_cixhci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
3948f9f848faSopenharmony_ci    struct usb_endpoint *ep)
3949f9f848faSopenharmony_ci{
3950f9f848faSopenharmony_ci	struct xhci_endpoint_ext *pepext;
3951f9f848faSopenharmony_ci
3952f9f848faSopenharmony_ci	DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d\n",
3953f9f848faSopenharmony_ci	    ep, udev->address, edesc->bEndpointAddress, udev->flags.usb_mode);
3954f9f848faSopenharmony_ci
3955f9f848faSopenharmony_ci	if (udev->parent_hub == NULL) {
3956f9f848faSopenharmony_ci		/* root HUB has special endpoint handling */
3957f9f848faSopenharmony_ci		return;
3958f9f848faSopenharmony_ci	}
3959f9f848faSopenharmony_ci
3960f9f848faSopenharmony_ci	ep->methods = &xhci_device_generic_methods;
3961f9f848faSopenharmony_ci
3962f9f848faSopenharmony_ci	pepext = xhci_get_endpoint_ext(udev, edesc);
3963f9f848faSopenharmony_ci
3964f9f848faSopenharmony_ci	USB_BUS_LOCK(udev->bus);
3965f9f848faSopenharmony_ci	pepext->trb_halted = 1;
3966f9f848faSopenharmony_ci	pepext->trb_running = 0;
3967f9f848faSopenharmony_ci	USB_BUS_UNLOCK(udev->bus);
3968f9f848faSopenharmony_ci}
3969f9f848faSopenharmony_ci
3970f9f848faSopenharmony_cistatic void
3971f9f848faSopenharmony_cixhci_ep_uninit(struct usb_device *udev, struct usb_endpoint *ep)
3972f9f848faSopenharmony_ci{
3973f9f848faSopenharmony_ci
3974f9f848faSopenharmony_ci}
3975f9f848faSopenharmony_ci
3976f9f848faSopenharmony_cistatic void
3977f9f848faSopenharmony_cixhci_ep_clear_stall(struct usb_device *udev, struct usb_endpoint *ep)
3978f9f848faSopenharmony_ci{
3979f9f848faSopenharmony_ci	struct xhci_endpoint_ext *pepext;
3980f9f848faSopenharmony_ci
3981f9f848faSopenharmony_ci	DPRINTF("\n");
3982f9f848faSopenharmony_ci
3983f9f848faSopenharmony_ci	if (udev->flags.usb_mode != USB_MODE_HOST) {
3984f9f848faSopenharmony_ci		/* not supported */
3985f9f848faSopenharmony_ci		return;
3986f9f848faSopenharmony_ci	}
3987f9f848faSopenharmony_ci	if (udev->parent_hub == NULL) {
3988f9f848faSopenharmony_ci		/* root HUB has special endpoint handling */
3989f9f848faSopenharmony_ci		return;
3990f9f848faSopenharmony_ci	}
3991f9f848faSopenharmony_ci
3992f9f848faSopenharmony_ci	pepext = xhci_get_endpoint_ext(udev, ep->edesc);
3993f9f848faSopenharmony_ci
3994f9f848faSopenharmony_ci	USB_BUS_LOCK(udev->bus);
3995f9f848faSopenharmony_ci	pepext->trb_halted = 1;
3996f9f848faSopenharmony_ci	pepext->trb_running = 0;
3997f9f848faSopenharmony_ci	USB_BUS_UNLOCK(udev->bus);
3998f9f848faSopenharmony_ci}
3999f9f848faSopenharmony_ci
4000f9f848faSopenharmony_cistatic usb_error_t
4001f9f848faSopenharmony_cixhci_device_init(struct usb_device *udev)
4002f9f848faSopenharmony_ci{
4003f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
4004f9f848faSopenharmony_ci	usb_error_t err;
4005f9f848faSopenharmony_ci	uint8_t temp;
4006f9f848faSopenharmony_ci
4007f9f848faSopenharmony_ci	/* no init for root HUB */
4008f9f848faSopenharmony_ci	if (udev->parent_hub == NULL)
4009f9f848faSopenharmony_ci		return (USB_ERR_NORMAL_COMPLETION);
4010f9f848faSopenharmony_ci
4011f9f848faSopenharmony_ci	XHCI_CMD_LOCK(sc);
4012f9f848faSopenharmony_ci
4013f9f848faSopenharmony_ci	/* set invalid default */
4014f9f848faSopenharmony_ci
4015f9f848faSopenharmony_ci	udev->controller_slot_id = sc->sc_noslot + 1;
4016f9f848faSopenharmony_ci
4017f9f848faSopenharmony_ci	/* try to get a new slot ID from the XHCI */
4018f9f848faSopenharmony_ci
4019f9f848faSopenharmony_ci	err = xhci_cmd_enable_slot(sc, &temp);
4020f9f848faSopenharmony_ci
4021f9f848faSopenharmony_ci	if (err) {
4022f9f848faSopenharmony_ci		XHCI_CMD_UNLOCK(sc);
4023f9f848faSopenharmony_ci		return (err);
4024f9f848faSopenharmony_ci	}
4025f9f848faSopenharmony_ci
4026f9f848faSopenharmony_ci	if (temp > sc->sc_noslot) {
4027f9f848faSopenharmony_ci		XHCI_CMD_UNLOCK(sc);
4028f9f848faSopenharmony_ci		return (USB_ERR_BAD_ADDRESS);
4029f9f848faSopenharmony_ci	}
4030f9f848faSopenharmony_ci
4031f9f848faSopenharmony_ci	if (sc->sc_hw.devs[temp].state != XHCI_ST_DISABLED) {
4032f9f848faSopenharmony_ci		DPRINTF("slot %u already allocated.\n", temp);
4033f9f848faSopenharmony_ci		XHCI_CMD_UNLOCK(sc);
4034f9f848faSopenharmony_ci		return (USB_ERR_BAD_ADDRESS);
4035f9f848faSopenharmony_ci	}
4036f9f848faSopenharmony_ci
4037f9f848faSopenharmony_ci	/* store slot ID for later reference */
4038f9f848faSopenharmony_ci
4039f9f848faSopenharmony_ci	udev->controller_slot_id = temp;
4040f9f848faSopenharmony_ci
4041f9f848faSopenharmony_ci	/* reset data structure */
4042f9f848faSopenharmony_ci
4043f9f848faSopenharmony_ci	(void)memset_s(&sc->sc_hw.devs[temp], sizeof(sc->sc_hw.devs[0]), 0, sizeof(sc->sc_hw.devs[0]));
4044f9f848faSopenharmony_ci
4045f9f848faSopenharmony_ci	/* set mark slot allocated */
4046f9f848faSopenharmony_ci
4047f9f848faSopenharmony_ci	sc->sc_hw.devs[temp].state = XHCI_ST_ENABLED;
4048f9f848faSopenharmony_ci
4049f9f848faSopenharmony_ci	err = xhci_alloc_device_ext(udev);
4050f9f848faSopenharmony_ci
4051f9f848faSopenharmony_ci	XHCI_CMD_UNLOCK(sc);
4052f9f848faSopenharmony_ci
4053f9f848faSopenharmony_ci	/* get device into default state */
4054f9f848faSopenharmony_ci
4055f9f848faSopenharmony_ci	if (err == 0)
4056f9f848faSopenharmony_ci		err = xhci_set_address(udev, NULL, 0);
4057f9f848faSopenharmony_ci
4058f9f848faSopenharmony_ci	return (err);
4059f9f848faSopenharmony_ci}
4060f9f848faSopenharmony_ci
4061f9f848faSopenharmony_cistatic void
4062f9f848faSopenharmony_cixhci_device_uninit(struct usb_device *udev)
4063f9f848faSopenharmony_ci{
4064f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
4065f9f848faSopenharmony_ci	uint8_t index;
4066f9f848faSopenharmony_ci
4067f9f848faSopenharmony_ci	/* no init for root HUB */
4068f9f848faSopenharmony_ci	if (udev->parent_hub == NULL)
4069f9f848faSopenharmony_ci		return;
4070f9f848faSopenharmony_ci
4071f9f848faSopenharmony_ci	XHCI_CMD_LOCK(sc);
4072f9f848faSopenharmony_ci
4073f9f848faSopenharmony_ci	index = udev->controller_slot_id;
4074f9f848faSopenharmony_ci
4075f9f848faSopenharmony_ci	if (index <= sc->sc_noslot) {
4076f9f848faSopenharmony_ci		(void)xhci_cmd_disable_slot(sc, index);
4077f9f848faSopenharmony_ci		sc->sc_hw.devs[index].state = XHCI_ST_DISABLED;
4078f9f848faSopenharmony_ci
4079f9f848faSopenharmony_ci		/* free device extension */
4080f9f848faSopenharmony_ci		xhci_free_device_ext(udev);
4081f9f848faSopenharmony_ci	}
4082f9f848faSopenharmony_ci
4083f9f848faSopenharmony_ci	XHCI_CMD_UNLOCK(sc);
4084f9f848faSopenharmony_ci}
4085f9f848faSopenharmony_ci
4086f9f848faSopenharmony_cistatic void
4087f9f848faSopenharmony_cixhci_get_dma_delay(struct usb_device *udev, uint32_t *pus)
4088f9f848faSopenharmony_ci{
4089f9f848faSopenharmony_ci	/*
4090f9f848faSopenharmony_ci	 * Wait until the hardware has finished any possible use of
4091f9f848faSopenharmony_ci	 * the transfer descriptor(s)
4092f9f848faSopenharmony_ci	 */
4093f9f848faSopenharmony_ci	*pus = 2048;	/* microseconds */
4094f9f848faSopenharmony_ci}
4095f9f848faSopenharmony_ci
4096f9f848faSopenharmony_cistatic void
4097f9f848faSopenharmony_cixhci_device_resume(struct usb_device *udev)
4098f9f848faSopenharmony_ci{
4099f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
4100f9f848faSopenharmony_ci	uint8_t index;
4101f9f848faSopenharmony_ci	uint8_t n;
4102f9f848faSopenharmony_ci	uint8_t p;
4103f9f848faSopenharmony_ci
4104f9f848faSopenharmony_ci	DPRINTF("\n");
4105f9f848faSopenharmony_ci
4106f9f848faSopenharmony_ci	/* check for root HUB */
4107f9f848faSopenharmony_ci	if (udev->parent_hub == NULL)
4108f9f848faSopenharmony_ci		return;
4109f9f848faSopenharmony_ci
4110f9f848faSopenharmony_ci	index = udev->controller_slot_id;
4111f9f848faSopenharmony_ci
4112f9f848faSopenharmony_ci	XHCI_CMD_LOCK(sc);
4113f9f848faSopenharmony_ci
4114f9f848faSopenharmony_ci	/* blindly resume all endpoints */
4115f9f848faSopenharmony_ci
4116f9f848faSopenharmony_ci	USB_BUS_LOCK(udev->bus);
4117f9f848faSopenharmony_ci
4118f9f848faSopenharmony_ci	for (n = 1; n != XHCI_MAX_ENDPOINTS; n++) {
4119f9f848faSopenharmony_ci		for (p = 0; p != XHCI_MAX_STREAMS; p++) {
4120f9f848faSopenharmony_ci			XWRITE4(sc, door, XHCI_DOORBELL(index),
4121f9f848faSopenharmony_ci			    n | XHCI_DB_SID_SET(p));
4122f9f848faSopenharmony_ci		}
4123f9f848faSopenharmony_ci	}
4124f9f848faSopenharmony_ci
4125f9f848faSopenharmony_ci	USB_BUS_UNLOCK(udev->bus);
4126f9f848faSopenharmony_ci
4127f9f848faSopenharmony_ci	XHCI_CMD_UNLOCK(sc);
4128f9f848faSopenharmony_ci}
4129f9f848faSopenharmony_ci
4130f9f848faSopenharmony_cistatic void
4131f9f848faSopenharmony_cixhci_device_suspend(struct usb_device *udev)
4132f9f848faSopenharmony_ci{
4133f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
4134f9f848faSopenharmony_ci	uint8_t index;
4135f9f848faSopenharmony_ci	uint8_t n;
4136f9f848faSopenharmony_ci	usb_error_t err;
4137f9f848faSopenharmony_ci
4138f9f848faSopenharmony_ci	DPRINTF("\n");
4139f9f848faSopenharmony_ci
4140f9f848faSopenharmony_ci	/* check for root HUB */
4141f9f848faSopenharmony_ci	if (udev->parent_hub == NULL)
4142f9f848faSopenharmony_ci		return;
4143f9f848faSopenharmony_ci
4144f9f848faSopenharmony_ci	index = udev->controller_slot_id;
4145f9f848faSopenharmony_ci
4146f9f848faSopenharmony_ci	XHCI_CMD_LOCK(sc);
4147f9f848faSopenharmony_ci
4148f9f848faSopenharmony_ci	/* blindly suspend all endpoints */
4149f9f848faSopenharmony_ci
4150f9f848faSopenharmony_ci	for (n = 1; n != XHCI_MAX_ENDPOINTS; n++) {
4151f9f848faSopenharmony_ci		err = xhci_cmd_stop_ep(sc, 1, n, index);
4152f9f848faSopenharmony_ci		if (err != 0) {
4153f9f848faSopenharmony_ci			DPRINTF("Failed to suspend endpoint "
4154f9f848faSopenharmony_ci			    "%u on slot %u (ignored).\n", n, index);
4155f9f848faSopenharmony_ci		}
4156f9f848faSopenharmony_ci	}
4157f9f848faSopenharmony_ci
4158f9f848faSopenharmony_ci	XHCI_CMD_UNLOCK(sc);
4159f9f848faSopenharmony_ci}
4160f9f848faSopenharmony_ci
4161f9f848faSopenharmony_cistatic void
4162f9f848faSopenharmony_cixhci_set_hw_power(struct usb_bus *bus)
4163f9f848faSopenharmony_ci{
4164f9f848faSopenharmony_ci	DPRINTF("\n");
4165f9f848faSopenharmony_ci}
4166f9f848faSopenharmony_ci
4167f9f848faSopenharmony_cistatic void
4168f9f848faSopenharmony_cixhci_device_state_change(struct usb_device *udev)
4169f9f848faSopenharmony_ci{
4170f9f848faSopenharmony_ci	struct xhci_softc *sc = XHCI_BUS2SC(udev->bus);
4171f9f848faSopenharmony_ci	struct usb_page_search buf_inp;
4172f9f848faSopenharmony_ci	usb_error_t err;
4173f9f848faSopenharmony_ci	uint8_t index;
4174f9f848faSopenharmony_ci
4175f9f848faSopenharmony_ci	/* check for root HUB */
4176f9f848faSopenharmony_ci	if (udev->parent_hub == NULL)
4177f9f848faSopenharmony_ci		return;
4178f9f848faSopenharmony_ci
4179f9f848faSopenharmony_ci	index = udev->controller_slot_id;
4180f9f848faSopenharmony_ci
4181f9f848faSopenharmony_ci	DPRINTF("\n");
4182f9f848faSopenharmony_ci
4183f9f848faSopenharmony_ci	if (usb_get_device_state(udev) == USB_STATE_CONFIGURED) {
4184f9f848faSopenharmony_ci		err = uhub_query_info(udev, &sc->sc_hw.devs[index].nports,
4185f9f848faSopenharmony_ci		    &sc->sc_hw.devs[index].tt);
4186f9f848faSopenharmony_ci		if (err != 0)
4187f9f848faSopenharmony_ci			sc->sc_hw.devs[index].nports = 0;
4188f9f848faSopenharmony_ci	}
4189f9f848faSopenharmony_ci
4190f9f848faSopenharmony_ci	XHCI_CMD_LOCK(sc);
4191f9f848faSopenharmony_ci
4192f9f848faSopenharmony_ci	switch (usb_get_device_state(udev)) {
4193f9f848faSopenharmony_ci	case USB_STATE_POWERED:
4194f9f848faSopenharmony_ci		if (sc->sc_hw.devs[index].state == XHCI_ST_DEFAULT)
4195f9f848faSopenharmony_ci			break;
4196f9f848faSopenharmony_ci
4197f9f848faSopenharmony_ci		/* set default state */
4198f9f848faSopenharmony_ci		sc->sc_hw.devs[index].state = XHCI_ST_DEFAULT;
4199f9f848faSopenharmony_ci
4200f9f848faSopenharmony_ci		/* reset number of contexts */
4201f9f848faSopenharmony_ci		sc->sc_hw.devs[index].context_num = 0;
4202f9f848faSopenharmony_ci
4203f9f848faSopenharmony_ci		err = xhci_cmd_reset_dev(sc, index);
4204f9f848faSopenharmony_ci
4205f9f848faSopenharmony_ci		if (err != 0) {
4206f9f848faSopenharmony_ci			DPRINTF("Device reset failed "
4207f9f848faSopenharmony_ci			    "for slot %u.\n", index);
4208f9f848faSopenharmony_ci		}
4209f9f848faSopenharmony_ci		break;
4210f9f848faSopenharmony_ci
4211f9f848faSopenharmony_ci	case USB_STATE_ADDRESSED:
4212f9f848faSopenharmony_ci		if (sc->sc_hw.devs[index].state == XHCI_ST_ADDRESSED)
4213f9f848faSopenharmony_ci			break;
4214f9f848faSopenharmony_ci
4215f9f848faSopenharmony_ci		sc->sc_hw.devs[index].state = XHCI_ST_ADDRESSED;
4216f9f848faSopenharmony_ci
4217f9f848faSopenharmony_ci		/* set configure mask to slot only */
4218f9f848faSopenharmony_ci		(void)xhci_configure_mask(udev, 1, 0);
4219f9f848faSopenharmony_ci
4220f9f848faSopenharmony_ci		err = xhci_cmd_configure_ep(sc, 0, 1, index);
4221f9f848faSopenharmony_ci
4222f9f848faSopenharmony_ci		if (err) {
4223f9f848faSopenharmony_ci			DPRINTF("Failed to deconfigure "
4224f9f848faSopenharmony_ci			    "slot %u.\n", index);
4225f9f848faSopenharmony_ci		}
4226f9f848faSopenharmony_ci		break;
4227f9f848faSopenharmony_ci
4228f9f848faSopenharmony_ci	case USB_STATE_CONFIGURED:
4229f9f848faSopenharmony_ci		if (sc->sc_hw.devs[index].state == XHCI_ST_CONFIGURED)
4230f9f848faSopenharmony_ci			break;
4231f9f848faSopenharmony_ci
4232f9f848faSopenharmony_ci		/* set configured state */
4233f9f848faSopenharmony_ci		sc->sc_hw.devs[index].state = XHCI_ST_CONFIGURED;
4234f9f848faSopenharmony_ci
4235f9f848faSopenharmony_ci		/* reset number of contexts */
4236f9f848faSopenharmony_ci		sc->sc_hw.devs[index].context_num = 0;
4237f9f848faSopenharmony_ci
4238f9f848faSopenharmony_ci		usbd_get_page(&sc->sc_hw.devs[index].input_pc, 0, &buf_inp);
4239f9f848faSopenharmony_ci
4240f9f848faSopenharmony_ci		(void)xhci_configure_mask(udev, 3, 0);
4241f9f848faSopenharmony_ci
4242f9f848faSopenharmony_ci		err = xhci_configure_device(udev);
4243f9f848faSopenharmony_ci		if (err != 0) {
4244f9f848faSopenharmony_ci			DPRINTF("Could not configure device "
4245f9f848faSopenharmony_ci			    "at slot %u.\n", index);
4246f9f848faSopenharmony_ci		}
4247f9f848faSopenharmony_ci
4248f9f848faSopenharmony_ci		err = xhci_cmd_evaluate_ctx(sc, buf_inp.physaddr, index);
4249f9f848faSopenharmony_ci		if (err != 0) {
4250f9f848faSopenharmony_ci			DPRINTF("Could not evaluate device "
4251f9f848faSopenharmony_ci			    "context at slot %u.\n", index);
4252f9f848faSopenharmony_ci		}
4253f9f848faSopenharmony_ci		break;
4254f9f848faSopenharmony_ci
4255f9f848faSopenharmony_ci	default:
4256f9f848faSopenharmony_ci		break;
4257f9f848faSopenharmony_ci	}
4258f9f848faSopenharmony_ci	XHCI_CMD_UNLOCK(sc);
4259f9f848faSopenharmony_ci}
4260f9f848faSopenharmony_ci
4261f9f848faSopenharmony_cistatic usb_error_t
4262f9f848faSopenharmony_cixhci_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep,
4263f9f848faSopenharmony_ci    uint8_t ep_mode)
4264f9f848faSopenharmony_ci{
4265f9f848faSopenharmony_ci	switch (ep_mode) {
4266f9f848faSopenharmony_ci	case USB_EP_MODE_DEFAULT:
4267f9f848faSopenharmony_ci		return (USB_ERR_NORMAL_COMPLETION);
4268f9f848faSopenharmony_ci	case USB_EP_MODE_STREAMS:
4269f9f848faSopenharmony_ci		if ((xhcistreams == 0) ||
4270f9f848faSopenharmony_ci		    ((ep->edesc->bmAttributes & UE_XFERTYPE) != UE_BULK) ||
4271f9f848faSopenharmony_ci		    (udev->speed != USB_SPEED_SUPER))
4272f9f848faSopenharmony_ci			return (USB_ERR_INVAL);
4273f9f848faSopenharmony_ci		return (USB_ERR_NORMAL_COMPLETION);
4274f9f848faSopenharmony_ci	default:
4275f9f848faSopenharmony_ci		return (USB_ERR_INVAL);
4276f9f848faSopenharmony_ci	}
4277f9f848faSopenharmony_ci}
4278f9f848faSopenharmony_ci
4279f9f848faSopenharmony_cistruct usb_bus_methods xhci_bus_methods = {
4280f9f848faSopenharmony_ci	.endpoint_init = xhci_ep_init,
4281f9f848faSopenharmony_ci	.endpoint_uninit = xhci_ep_uninit,
4282f9f848faSopenharmony_ci	.xfer_setup = xhci_xfer_setup,
4283f9f848faSopenharmony_ci	.xfer_unsetup = xhci_xfer_unsetup,
4284f9f848faSopenharmony_ci	.get_dma_delay = xhci_get_dma_delay,
4285f9f848faSopenharmony_ci	.device_init = xhci_device_init,
4286f9f848faSopenharmony_ci	.device_uninit = xhci_device_uninit,
4287f9f848faSopenharmony_ci	.device_resume = xhci_device_resume,
4288f9f848faSopenharmony_ci	.device_suspend = xhci_device_suspend,
4289f9f848faSopenharmony_ci	.set_hw_power = xhci_set_hw_power,
4290f9f848faSopenharmony_ci	.roothub_exec = xhci_roothub_exec,
4291f9f848faSopenharmony_ci	.xfer_poll = xhci_do_poll,
4292f9f848faSopenharmony_ci	.start_dma_delay = xhci_start_dma_delay,
4293f9f848faSopenharmony_ci	.set_address = xhci_set_address,
4294f9f848faSopenharmony_ci	.clear_stall = xhci_ep_clear_stall,
4295f9f848faSopenharmony_ci	.device_state_change = xhci_device_state_change,
4296f9f848faSopenharmony_ci	.set_hw_power_sleep = xhci_set_hw_power_sleep,
4297f9f848faSopenharmony_ci	.set_endpoint_mode = xhci_set_endpoint_mode,
4298f9f848faSopenharmony_ci};
4299