1f9f848faSopenharmony_ci/*-
2f9f848faSopenharmony_ci * SPDX-License-Identifier: BSD-2-Clause
3f9f848faSopenharmony_ci *
4f9f848faSopenharmony_ci * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
5f9f848faSopenharmony_ci * Copyright (c) 2004 The NetBSD Foundation, Inc. All rights reserved.
6f9f848faSopenharmony_ci * Copyright (c) 2004 Lennart Augustsson. All rights reserved.
7f9f848faSopenharmony_ci * Copyright (c) 2004 Charles M. Hannum. All rights reserved.
8f9f848faSopenharmony_ci *
9f9f848faSopenharmony_ci * Redistribution and use in source and binary forms, with or without
10f9f848faSopenharmony_ci * modification, are permitted provided that the following conditions
11f9f848faSopenharmony_ci * are met:
12f9f848faSopenharmony_ci * 1. Redistributions of source code must retain the above copyright
13f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer.
14f9f848faSopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
15f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
16f9f848faSopenharmony_ci *    documentation and/or other materials provided with the distribution.
17f9f848faSopenharmony_ci *
18f9f848faSopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19f9f848faSopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20f9f848faSopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21f9f848faSopenharmony_ci * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22f9f848faSopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23f9f848faSopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24f9f848faSopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25f9f848faSopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26f9f848faSopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27f9f848faSopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28f9f848faSopenharmony_ci * SUCH DAMAGE.
29f9f848faSopenharmony_ci */
30f9f848faSopenharmony_ci
31f9f848faSopenharmony_ci/*
32f9f848faSopenharmony_ci * USB Enhanced Host Controller Driver, a.k.a. USB 2.0 controller.
33f9f848faSopenharmony_ci *
34f9f848faSopenharmony_ci * The EHCI 0.96 spec can be found at
35f9f848faSopenharmony_ci * http://developer.intel.com/technology/usb/download/ehci-r096.pdf
36f9f848faSopenharmony_ci * The EHCI 1.0 spec can be found at
37f9f848faSopenharmony_ci * http://developer.intel.com/technology/usb/download/ehci-r10.pdf
38f9f848faSopenharmony_ci * and the USB 2.0 spec at
39f9f848faSopenharmony_ci * http://www.usb.org/developers/docs/usb_20.zip
40f9f848faSopenharmony_ci *
41f9f848faSopenharmony_ci */
42f9f848faSopenharmony_ci
43f9f848faSopenharmony_ci#include "implementation/global_implementation.h"
44f9f848faSopenharmony_ci#include "controller/ehci.h"
45f9f848faSopenharmony_ci#include "controller/ehcireg.h"
46f9f848faSopenharmony_ci
47f9f848faSopenharmony_ci
48f9f848faSopenharmony_ci#define	EHCI_BUS2SC(bus) \
49f9f848faSopenharmony_ci   ((ehci_softc_t *)(((uint8_t *)(bus)) - \
50f9f848faSopenharmony_ci    ((uint8_t *)&(((ehci_softc_t *)0)->sc_bus))))
51f9f848faSopenharmony_ci
52f9f848faSopenharmony_ci#undef	USB_DEBUG_VAR
53f9f848faSopenharmony_ci#define	USB_DEBUG_VAR   ehcidebug
54f9f848faSopenharmony_ci
55f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
56f9f848faSopenharmony_cistatic int ehcidebug = 0;
57f9f848faSopenharmony_cistatic int ehciiaadbug = 0;
58f9f848faSopenharmony_cistatic int ehcilostintrbug = 0;
59f9f848faSopenharmony_ci
60f9f848faSopenharmony_cistatic void ehci_dump_regs(ehci_softc_t *sc);
61f9f848faSopenharmony_cistatic void ehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *sqh);
62f9f848faSopenharmony_ci
63f9f848faSopenharmony_civoid
64f9f848faSopenharmony_ciusb_ehci_debug_func(int level)
65f9f848faSopenharmony_ci{
66f9f848faSopenharmony_ci	ehcidebug = level;
67f9f848faSopenharmony_ci	PRINTK("The level of usb ehci debug is %d\n", level);
68f9f848faSopenharmony_ci}
69f9f848faSopenharmony_ciDEBUG_MODULE(ehci, usb_ehci_debug_func);
70f9f848faSopenharmony_ci#endif
71f9f848faSopenharmony_ci
72f9f848faSopenharmony_ciSPIN_LOCK_INIT(g_usb_ehci_qh_spinlock);
73f9f848faSopenharmony_ci
74f9f848faSopenharmony_ci#define	EHCI_INTR_ENDPT 1
75f9f848faSopenharmony_ci
76f9f848faSopenharmony_ciextern const struct usb_bus_methods ehci_bus_methods;
77f9f848faSopenharmony_ciextern const struct usb_pipe_methods ehci_device_bulk_methods;
78f9f848faSopenharmony_ciextern const struct usb_pipe_methods ehci_device_ctrl_methods;
79f9f848faSopenharmony_ciextern const struct usb_pipe_methods ehci_device_intr_methods;
80f9f848faSopenharmony_ciextern const struct usb_pipe_methods ehci_device_isoc_fs_methods;
81f9f848faSopenharmony_ciextern const struct usb_pipe_methods ehci_device_isoc_hs_methods;
82f9f848faSopenharmony_ci
83f9f848faSopenharmony_cistatic void ehci_do_poll(struct usb_bus *);
84f9f848faSopenharmony_cistatic void ehci_device_done(struct usb_xfer *, usb_error_t);
85f9f848faSopenharmony_cistatic uint8_t ehci_check_transfer(struct usb_xfer *);
86f9f848faSopenharmony_cistatic void ehci_timeout(void *);
87f9f848faSopenharmony_cistatic void ehci_poll_timeout(void *);
88f9f848faSopenharmony_ci
89f9f848faSopenharmony_cistatic void ehci_root_intr(ehci_softc_t *sc);
90f9f848faSopenharmony_ci
91f9f848faSopenharmony_cistruct ehci_std_temp {
92f9f848faSopenharmony_ci	ehci_softc_t *sc;
93f9f848faSopenharmony_ci	struct usb_page_cache *pc;
94f9f848faSopenharmony_ci	ehci_qtd_t *td;
95f9f848faSopenharmony_ci	ehci_qtd_t *td_next;
96f9f848faSopenharmony_ci	uint32_t average;
97f9f848faSopenharmony_ci	uint32_t qtd_status;
98f9f848faSopenharmony_ci	uint32_t len;
99f9f848faSopenharmony_ci	uint16_t max_frame_size;
100f9f848faSopenharmony_ci	uint8_t	shortpkt;
101f9f848faSopenharmony_ci	uint8_t	auto_data_toggle;
102f9f848faSopenharmony_ci	uint8_t	setup_alt_next;
103f9f848faSopenharmony_ci	uint8_t	last_frame;
104f9f848faSopenharmony_ci};
105f9f848faSopenharmony_ci
106f9f848faSopenharmony_ci/* cb: usb_bus_mem_alloc_all_cb */
107f9f848faSopenharmony_ci/* usb_bus_mem_flush_all_cb */
108f9f848faSopenharmony_civoid
109f9f848faSopenharmony_ciehci_iterate_hw_softc(struct usb_bus *bus, usb_bus_mem_sub_cb_t *cb)
110f9f848faSopenharmony_ci{
111f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(bus);
112f9f848faSopenharmony_ci	uint32_t i;
113f9f848faSopenharmony_ci
114f9f848faSopenharmony_ci	cb(bus, &sc->sc_hw.pframes_pc, &sc->sc_hw.pframes_pg,
115f9f848faSopenharmony_ci	    sizeof(uint32_t) * EHCI_FRAMELIST_COUNT, EHCI_FRAMELIST_ALIGN);
116f9f848faSopenharmony_ci
117f9f848faSopenharmony_ci	cb(bus, &sc->sc_hw.terminate_pc, &sc->sc_hw.terminate_pg,
118f9f848faSopenharmony_ci	    sizeof(struct ehci_qh_sub), EHCI_QH_ALIGN);
119f9f848faSopenharmony_ci
120f9f848faSopenharmony_ci	cb(bus, &sc->sc_hw.async_start_pc, &sc->sc_hw.async_start_pg,
121f9f848faSopenharmony_ci	    sizeof(ehci_qh_t), EHCI_QH_ALIGN);
122f9f848faSopenharmony_ci
123f9f848faSopenharmony_ci	for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
124f9f848faSopenharmony_ci		cb(bus, sc->sc_hw.intr_start_pc + i,
125f9f848faSopenharmony_ci		    sc->sc_hw.intr_start_pg + i,
126f9f848faSopenharmony_ci		    sizeof(ehci_qh_t), EHCI_QH_ALIGN);
127f9f848faSopenharmony_ci	}
128f9f848faSopenharmony_ci
129f9f848faSopenharmony_ci	for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
130f9f848faSopenharmony_ci		cb(bus, sc->sc_hw.isoc_hs_start_pc + i,
131f9f848faSopenharmony_ci		    sc->sc_hw.isoc_hs_start_pg + i,
132f9f848faSopenharmony_ci		    sizeof(ehci_itd_t), EHCI_ITD_ALIGN);
133f9f848faSopenharmony_ci	}
134f9f848faSopenharmony_ci
135f9f848faSopenharmony_ci	for (i = 0; i != EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
136f9f848faSopenharmony_ci		cb(bus, sc->sc_hw.isoc_fs_start_pc + i,
137f9f848faSopenharmony_ci		    sc->sc_hw.isoc_fs_start_pg + i,
138f9f848faSopenharmony_ci		    sizeof(ehci_sitd_t), EHCI_SITD_ALIGN);
139f9f848faSopenharmony_ci	}
140f9f848faSopenharmony_ci}
141f9f848faSopenharmony_ci
142f9f848faSopenharmony_ciusb_error_t
143f9f848faSopenharmony_ciehci_reset(ehci_softc_t *sc)
144f9f848faSopenharmony_ci{
145f9f848faSopenharmony_ci	uint32_t hcr;
146f9f848faSopenharmony_ci	int i;
147f9f848faSopenharmony_ci
148f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_USBCMD, EHCI_CMD_HCRESET);
149f9f848faSopenharmony_ci	for (i = 0; i < 100; i++) {
150f9f848faSopenharmony_ci		usb_pause_mtx(NULL, hz / 128);
151f9f848faSopenharmony_ci		hcr = EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_HCRESET;
152f9f848faSopenharmony_ci		if (!hcr) {
153f9f848faSopenharmony_ci			if (sc->sc_vendor_post_reset != NULL)
154f9f848faSopenharmony_ci				sc->sc_vendor_post_reset(sc);
155f9f848faSopenharmony_ci			return (USB_ERR_NORMAL_COMPLETION);
156f9f848faSopenharmony_ci		}
157f9f848faSopenharmony_ci	}
158f9f848faSopenharmony_ci	device_printf(sc->sc_bus.bdev, "Reset timeout\n");
159f9f848faSopenharmony_ci	return (USB_ERR_IOERROR);
160f9f848faSopenharmony_ci}
161f9f848faSopenharmony_ci
162f9f848faSopenharmony_cistatic usb_error_t
163f9f848faSopenharmony_ciehci_hcreset(ehci_softc_t *sc)
164f9f848faSopenharmony_ci{
165f9f848faSopenharmony_ci	uint32_t hcr = 0;
166f9f848faSopenharmony_ci	int i;
167f9f848faSopenharmony_ci
168f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_USBCMD, 0);	/* Halt controller */
169f9f848faSopenharmony_ci	for (i = 0; i < 100; i++) {
170f9f848faSopenharmony_ci		usb_pause_mtx(NULL, hz / 128);
171f9f848faSopenharmony_ci		hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
172f9f848faSopenharmony_ci		if (hcr)
173f9f848faSopenharmony_ci			break;
174f9f848faSopenharmony_ci	}
175f9f848faSopenharmony_ci	if (!hcr)
176f9f848faSopenharmony_ci		/*
177f9f848faSopenharmony_ci		 * Fall through and try reset anyway even though
178f9f848faSopenharmony_ci		 * Table 2-9 in the EHCI spec says this will result
179f9f848faSopenharmony_ci		 * in undefined behavior.
180f9f848faSopenharmony_ci		 */
181f9f848faSopenharmony_ci		device_printf(sc->sc_bus.bdev, "stop timeout\n");
182f9f848faSopenharmony_ci
183f9f848faSopenharmony_ci	return (ehci_reset(sc));
184f9f848faSopenharmony_ci}
185f9f848faSopenharmony_ci
186f9f848faSopenharmony_cistatic usb_error_t
187f9f848faSopenharmony_ciehci_init_sub(struct ehci_softc *sc)
188f9f848faSopenharmony_ci{
189f9f848faSopenharmony_ci	struct usb_page_search buf_res;
190f9f848faSopenharmony_ci	uint32_t cparams;
191f9f848faSopenharmony_ci	uint32_t hcr = 0;
192f9f848faSopenharmony_ci	uint8_t i;
193f9f848faSopenharmony_ci
194f9f848faSopenharmony_ci	cparams = EREAD4(sc, EHCI_HCCPARAMS);
195f9f848faSopenharmony_ci
196f9f848faSopenharmony_ci	DPRINTF("cparams=0x%x\n", cparams);
197f9f848faSopenharmony_ci
198f9f848faSopenharmony_ci	if (EHCI_HCC_64BIT(cparams)) {
199f9f848faSopenharmony_ci		DPRINTF("HCC uses 64-bit structures\n");
200f9f848faSopenharmony_ci
201f9f848faSopenharmony_ci		/* MUST clear segment register if 64 bit capable */
202f9f848faSopenharmony_ci		EOWRITE4(sc, EHCI_CTRLDSSEGMENT, 0);
203f9f848faSopenharmony_ci	}
204f9f848faSopenharmony_ci
205f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res);
206f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_PERIODICLISTBASE, buf_res.physaddr);
207f9f848faSopenharmony_ci
208f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res);
209f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_ASYNCLISTADDR, buf_res.physaddr | EHCI_LINK_QH);
210f9f848faSopenharmony_ci
211f9f848faSopenharmony_ci	/* enable interrupts */
212f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
213f9f848faSopenharmony_ci
214f9f848faSopenharmony_ci	/* turn on controller */
215f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_USBCMD,
216f9f848faSopenharmony_ci	    EHCI_CMD_ITC_1 |		/* 1 microframes interrupt delay */
217f9f848faSopenharmony_ci	    (EOREAD4(sc, EHCI_USBCMD) & EHCI_CMD_FLS_M) |
218f9f848faSopenharmony_ci	    EHCI_CMD_ASE |
219f9f848faSopenharmony_ci	    EHCI_CMD_PSE |
220f9f848faSopenharmony_ci	    EHCI_CMD_RS);
221f9f848faSopenharmony_ci
222f9f848faSopenharmony_ci	/* Take over port ownership */
223f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_CONFIGFLAG, EHCI_CONF_CF);
224f9f848faSopenharmony_ci
225f9f848faSopenharmony_ci	for (i = 0; i < 100; i++) {
226f9f848faSopenharmony_ci		usb_pause_mtx(NULL, hz / 128);
227f9f848faSopenharmony_ci		hcr = EOREAD4(sc, EHCI_USBSTS) & EHCI_STS_HCH;
228f9f848faSopenharmony_ci		if (!hcr) {
229f9f848faSopenharmony_ci			break;
230f9f848faSopenharmony_ci		}
231f9f848faSopenharmony_ci	}
232f9f848faSopenharmony_ci	if (hcr) {
233f9f848faSopenharmony_ci		device_printf(sc->sc_bus.bdev, "run timeout\n");
234f9f848faSopenharmony_ci		return (USB_ERR_IOERROR);
235f9f848faSopenharmony_ci	}
236f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
237f9f848faSopenharmony_ci}
238f9f848faSopenharmony_ci
239f9f848faSopenharmony_ciusb_error_t
240f9f848faSopenharmony_ciehci_init(ehci_softc_t *sc)
241f9f848faSopenharmony_ci{
242f9f848faSopenharmony_ci	struct usb_page_search buf_res;
243f9f848faSopenharmony_ci	struct ehci_qh_sub *qh_sub;
244f9f848faSopenharmony_ci	ehci_qh_t *qh_t;
245f9f848faSopenharmony_ci	uint32_t version;
246f9f848faSopenharmony_ci	uint32_t sparams;
247f9f848faSopenharmony_ci	uint32_t *pframes;
248f9f848faSopenharmony_ci	uint16_t i;
249f9f848faSopenharmony_ci	uint16_t x;
250f9f848faSopenharmony_ci	uint16_t y;
251f9f848faSopenharmony_ci	uint16_t bit;
252f9f848faSopenharmony_ci	usb_error_t err = USB_ERR_NORMAL_COMPLETION;
253f9f848faSopenharmony_ci
254f9f848faSopenharmony_ci	DPRINTF("start\n");
255f9f848faSopenharmony_ci
256f9f848faSopenharmony_ci	callout_init_mtx(&sc->sc_tmo_pcd, &sc->sc_bus.bus_mtx, 0);
257f9f848faSopenharmony_ci	callout_init_mtx(&sc->sc_tmo_poll, &sc->sc_bus.bus_mtx, 0);
258f9f848faSopenharmony_ci
259f9f848faSopenharmony_ci	sc->sc_offs = EHCI_CAPLENGTH(EREAD4(sc, EHCI_CAPLEN_HCIVERSION));
260f9f848faSopenharmony_ci
261f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
262f9f848faSopenharmony_ci	if (ehciiaadbug)
263f9f848faSopenharmony_ci		sc->sc_flags |= EHCI_SCFLG_IAADBUG;
264f9f848faSopenharmony_ci	if (ehcilostintrbug)
265f9f848faSopenharmony_ci		sc->sc_flags |= EHCI_SCFLG_LOSTINTRBUG;
266f9f848faSopenharmony_ci	if (ehcidebug > 2) {
267f9f848faSopenharmony_ci		ehci_dump_regs(sc);
268f9f848faSopenharmony_ci	}
269f9f848faSopenharmony_ci#endif
270f9f848faSopenharmony_ci
271f9f848faSopenharmony_ci	version = EHCI_HCIVERSION(EREAD4(sc, EHCI_CAPLEN_HCIVERSION));
272f9f848faSopenharmony_ci	device_printf(sc->sc_bus.bdev, "EHCI version %x.%x\n",
273f9f848faSopenharmony_ci	    version >> 8, version & 0xff);
274f9f848faSopenharmony_ci
275f9f848faSopenharmony_ci	sparams = EREAD4(sc, EHCI_HCSPARAMS);
276f9f848faSopenharmony_ci	DPRINTF("sparams=0x%x\n", sparams);
277f9f848faSopenharmony_ci
278f9f848faSopenharmony_ci	sc->sc_noport = EHCI_HCS_N_PORTS(sparams);
279f9f848faSopenharmony_ci	sc->sc_bus.usbrev = USB_REV_2_0;
280f9f848faSopenharmony_ci	if (!(sc->sc_flags & EHCI_SCFLG_DONTRESET)) {
281f9f848faSopenharmony_ci		/* Reset the controller */
282f9f848faSopenharmony_ci		DPRINTF("%s: resetting\n",
283f9f848faSopenharmony_ci		    device_get_nameunit(sc->sc_bus.bdev));
284f9f848faSopenharmony_ci		err = ehci_hcreset(sc);
285f9f848faSopenharmony_ci		if (err) {
286f9f848faSopenharmony_ci			device_printf(sc->sc_bus.bdev, "reset timeout\n");
287f9f848faSopenharmony_ci			return (err);
288f9f848faSopenharmony_ci		}
289f9f848faSopenharmony_ci	}
290f9f848faSopenharmony_ci
291f9f848faSopenharmony_ci	/*
292f9f848faSopenharmony_ci	 * use current frame-list-size selection 0: 1024*4 bytes 1:  512*4
293f9f848faSopenharmony_ci	 * bytes 2:  256*4 bytes 3:	  unknown
294f9f848faSopenharmony_ci	 */
295f9f848faSopenharmony_ci	if (EHCI_CMD_FLS(EOREAD4(sc, EHCI_USBCMD)) == 3) {
296f9f848faSopenharmony_ci		device_printf(sc->sc_bus.bdev, "invalid frame-list-size\n");
297f9f848faSopenharmony_ci		return (USB_ERR_IOERROR);
298f9f848faSopenharmony_ci	}
299f9f848faSopenharmony_ci	/* set up the bus struct */
300f9f848faSopenharmony_ci	sc->sc_bus.methods = &ehci_bus_methods;
301f9f848faSopenharmony_ci
302f9f848faSopenharmony_ci	sc->sc_eintrs = EHCI_NORMAL_INTRS;
303f9f848faSopenharmony_ci
304f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.terminate_pc, 0, &buf_res);
305f9f848faSopenharmony_ci	qh_sub = (struct ehci_qh_sub *)buf_res.buffer;
306f9f848faSopenharmony_ci
307f9f848faSopenharmony_ci	sc->sc_terminate_self = htohc32(sc, buf_res.physaddr);
308f9f848faSopenharmony_ci
309f9f848faSopenharmony_ci	/* init terminate TD */
310f9f848faSopenharmony_ci	qh_sub->qtd_next =
311f9f848faSopenharmony_ci	    htohc32(sc, EHCI_LINK_TERMINATE);
312f9f848faSopenharmony_ci	qh_sub->qtd_altnext =
313f9f848faSopenharmony_ci	    htohc32(sc, EHCI_LINK_TERMINATE);
314f9f848faSopenharmony_ci	qh_sub->qtd_status =
315f9f848faSopenharmony_ci	    htohc32(sc, EHCI_QTD_HALTED);
316f9f848faSopenharmony_ci
317f9f848faSopenharmony_ci	for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
318f9f848faSopenharmony_ci		usbd_get_page(sc->sc_hw.intr_start_pc + i, 0, &buf_res);
319f9f848faSopenharmony_ci
320f9f848faSopenharmony_ci		qh_t = (ehci_qh_t *)buf_res.buffer;
321f9f848faSopenharmony_ci
322f9f848faSopenharmony_ci		/* initialize page cache pointer */
323f9f848faSopenharmony_ci
324f9f848faSopenharmony_ci		qh_t->page_cache = sc->sc_hw.intr_start_pc + i;
325f9f848faSopenharmony_ci
326f9f848faSopenharmony_ci		/* store a pointer to queue head */
327f9f848faSopenharmony_ci
328f9f848faSopenharmony_ci		sc->sc_intr_p_last[i] = qh_t;
329f9f848faSopenharmony_ci
330f9f848faSopenharmony_ci		qh_t->qh_self =
331f9f848faSopenharmony_ci		    htohc32(sc, buf_res.physaddr) |
332f9f848faSopenharmony_ci		    htohc32(sc, EHCI_LINK_QH);
333f9f848faSopenharmony_ci
334f9f848faSopenharmony_ci		qh_t->qh_endp =
335f9f848faSopenharmony_ci		    htohc32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH));
336f9f848faSopenharmony_ci		qh_t->qh_endphub =
337f9f848faSopenharmony_ci		    htohc32(sc, EHCI_QH_SET_MULT(1));
338f9f848faSopenharmony_ci		qh_t->qh_curqtd = 0;
339f9f848faSopenharmony_ci
340f9f848faSopenharmony_ci		qh_t->qh_qtd.qtd_next =
341f9f848faSopenharmony_ci		    htohc32(sc, EHCI_LINK_TERMINATE);
342f9f848faSopenharmony_ci		qh_t->qh_qtd.qtd_altnext =
343f9f848faSopenharmony_ci		    htohc32(sc, EHCI_LINK_TERMINATE);
344f9f848faSopenharmony_ci		qh_t->qh_qtd.qtd_status =
345f9f848faSopenharmony_ci		    htohc32(sc, EHCI_QTD_HALTED);
346f9f848faSopenharmony_ci	}
347f9f848faSopenharmony_ci
348f9f848faSopenharmony_ci	/*
349f9f848faSopenharmony_ci	 * the QHs are arranged to give poll intervals that are
350f9f848faSopenharmony_ci	 * powers of 2 times 1ms
351f9f848faSopenharmony_ci	 */
352f9f848faSopenharmony_ci	bit = EHCI_VIRTUAL_FRAMELIST_COUNT / 2;
353f9f848faSopenharmony_ci	while (bit) {
354f9f848faSopenharmony_ci		x = bit;
355f9f848faSopenharmony_ci		while (x & bit) {
356f9f848faSopenharmony_ci			ehci_qh_t *qh_x;
357f9f848faSopenharmony_ci			ehci_qh_t *qh_y;
358f9f848faSopenharmony_ci
359f9f848faSopenharmony_ci			y = (x ^ bit) | (bit / 2);
360f9f848faSopenharmony_ci
361f9f848faSopenharmony_ci			qh_x = sc->sc_intr_p_last[x];
362f9f848faSopenharmony_ci			qh_y = sc->sc_intr_p_last[y];
363f9f848faSopenharmony_ci
364f9f848faSopenharmony_ci			/*
365f9f848faSopenharmony_ci			 * the next QH has half the poll interval
366f9f848faSopenharmony_ci			 */
367f9f848faSopenharmony_ci			qh_x->qh_link = qh_y->qh_self;
368f9f848faSopenharmony_ci
369f9f848faSopenharmony_ci			x++;
370f9f848faSopenharmony_ci		}
371f9f848faSopenharmony_ci		bit >>= 1;
372f9f848faSopenharmony_ci	}
373f9f848faSopenharmony_ci
374f9f848faSopenharmony_ci	qh_t = sc->sc_intr_p_last[0];
375f9f848faSopenharmony_ci
376f9f848faSopenharmony_ci	/* the last (1ms) QH terminates */
377f9f848faSopenharmony_ci	qh_t->qh_link = htohc32(sc, EHCI_LINK_TERMINATE);
378f9f848faSopenharmony_ci
379f9f848faSopenharmony_ci	for (i = 0; i < EHCI_VIRTUAL_FRAMELIST_COUNT; i++) {
380f9f848faSopenharmony_ci		ehci_sitd_t *sitd;
381f9f848faSopenharmony_ci		ehci_itd_t *itd;
382f9f848faSopenharmony_ci
383f9f848faSopenharmony_ci		usbd_get_page(sc->sc_hw.isoc_fs_start_pc + i, 0, &buf_res);
384f9f848faSopenharmony_ci		sitd = (ehci_sitd_t *)buf_res.buffer;
385f9f848faSopenharmony_ci
386f9f848faSopenharmony_ci		/* initialize page cache pointer */
387f9f848faSopenharmony_ci
388f9f848faSopenharmony_ci		sitd->page_cache = sc->sc_hw.isoc_fs_start_pc + i;
389f9f848faSopenharmony_ci
390f9f848faSopenharmony_ci		/* store a pointer to the transfer descriptor */
391f9f848faSopenharmony_ci
392f9f848faSopenharmony_ci		sc->sc_isoc_fs_p_last[i] = sitd;
393f9f848faSopenharmony_ci
394f9f848faSopenharmony_ci		/* initialize full speed isochronous */
395f9f848faSopenharmony_ci
396f9f848faSopenharmony_ci		sitd->sitd_self =
397f9f848faSopenharmony_ci		    htohc32(sc, buf_res.physaddr) |
398f9f848faSopenharmony_ci		    htohc32(sc, EHCI_LINK_SITD);
399f9f848faSopenharmony_ci
400f9f848faSopenharmony_ci		sitd->sitd_back =
401f9f848faSopenharmony_ci		    htohc32(sc, EHCI_LINK_TERMINATE);
402f9f848faSopenharmony_ci
403f9f848faSopenharmony_ci		sitd->sitd_next =
404f9f848faSopenharmony_ci		    sc->sc_intr_p_last[i | (EHCI_VIRTUAL_FRAMELIST_COUNT / 2)]->qh_self;
405f9f848faSopenharmony_ci
406f9f848faSopenharmony_ci		usbd_get_page(sc->sc_hw.isoc_hs_start_pc + i, 0, &buf_res);
407f9f848faSopenharmony_ci
408f9f848faSopenharmony_ci		itd = (ehci_itd_t *)buf_res.buffer;
409f9f848faSopenharmony_ci
410f9f848faSopenharmony_ci		/* initialize page cache pointer */
411f9f848faSopenharmony_ci
412f9f848faSopenharmony_ci		itd->page_cache = sc->sc_hw.isoc_hs_start_pc + i;
413f9f848faSopenharmony_ci
414f9f848faSopenharmony_ci		/* store a pointer to the transfer descriptor */
415f9f848faSopenharmony_ci
416f9f848faSopenharmony_ci		sc->sc_isoc_hs_p_last[i] = itd;
417f9f848faSopenharmony_ci
418f9f848faSopenharmony_ci		/* initialize high speed isochronous */
419f9f848faSopenharmony_ci
420f9f848faSopenharmony_ci		itd->itd_self =
421f9f848faSopenharmony_ci		    htohc32(sc, buf_res.physaddr) |
422f9f848faSopenharmony_ci		    htohc32(sc, EHCI_LINK_ITD);
423f9f848faSopenharmony_ci
424f9f848faSopenharmony_ci		itd->itd_next =
425f9f848faSopenharmony_ci		    sitd->sitd_self;
426f9f848faSopenharmony_ci	}
427f9f848faSopenharmony_ci
428f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.pframes_pc, 0, &buf_res);
429f9f848faSopenharmony_ci
430f9f848faSopenharmony_ci	pframes = (uint32_t *)buf_res.buffer;
431f9f848faSopenharmony_ci
432f9f848faSopenharmony_ci	/*
433f9f848faSopenharmony_ci	 * execution order:
434f9f848faSopenharmony_ci	 * pframes -> high speed isochronous ->
435f9f848faSopenharmony_ci	 *    full speed isochronous -> interrupt QH's
436f9f848faSopenharmony_ci	 */
437f9f848faSopenharmony_ci	for (i = 0; i < EHCI_FRAMELIST_COUNT; i++) {
438f9f848faSopenharmony_ci		pframes[i] = sc->sc_isoc_hs_p_last
439f9f848faSopenharmony_ci		    [i & (EHCI_VIRTUAL_FRAMELIST_COUNT - 1)]->itd_self;
440f9f848faSopenharmony_ci	}
441f9f848faSopenharmony_ci
442f9f848faSopenharmony_ci	usbd_get_page(&sc->sc_hw.async_start_pc, 0, &buf_res);
443f9f848faSopenharmony_ci
444f9f848faSopenharmony_ci	qh_t = (ehci_qh_t *)buf_res.buffer;
445f9f848faSopenharmony_ci
446f9f848faSopenharmony_ci	/* initialize page cache pointer */
447f9f848faSopenharmony_ci
448f9f848faSopenharmony_ci	qh_t->page_cache = &sc->sc_hw.async_start_pc;
449f9f848faSopenharmony_ci
450f9f848faSopenharmony_ci	/* store a pointer to the queue head */
451f9f848faSopenharmony_ci
452f9f848faSopenharmony_ci	sc->sc_async_p_last = qh_t;
453f9f848faSopenharmony_ci
454f9f848faSopenharmony_ci	/* init dummy QH that starts the async list */
455f9f848faSopenharmony_ci
456f9f848faSopenharmony_ci	qh_t->qh_self =
457f9f848faSopenharmony_ci	    htohc32(sc, buf_res.physaddr) |
458f9f848faSopenharmony_ci	    htohc32(sc, EHCI_LINK_QH);
459f9f848faSopenharmony_ci
460f9f848faSopenharmony_ci	/* fill the QH */
461f9f848faSopenharmony_ci	qh_t->qh_endp =
462f9f848faSopenharmony_ci	    htohc32(sc, EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH) | EHCI_QH_HRECL);
463f9f848faSopenharmony_ci	qh_t->qh_endphub = htohc32(sc, EHCI_QH_SET_MULT(1));
464f9f848faSopenharmony_ci	qh_t->qh_link = qh_t->qh_self;
465f9f848faSopenharmony_ci	qh_t->qh_curqtd = 0;
466f9f848faSopenharmony_ci
467f9f848faSopenharmony_ci	/* fill the overlay qTD */
468f9f848faSopenharmony_ci	qh_t->qh_qtd.qtd_next = htohc32(sc, EHCI_LINK_TERMINATE);
469f9f848faSopenharmony_ci	qh_t->qh_qtd.qtd_altnext = htohc32(sc, EHCI_LINK_TERMINATE);
470f9f848faSopenharmony_ci	qh_t->qh_qtd.qtd_status = htohc32(sc, EHCI_QTD_HALTED);
471f9f848faSopenharmony_ci
472f9f848faSopenharmony_ci	/* flush all cache into memory */
473f9f848faSopenharmony_ci
474f9f848faSopenharmony_ci	usb_bus_mem_flush_all(&sc->sc_bus, &ehci_iterate_hw_softc);
475f9f848faSopenharmony_ci
476f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
477f9f848faSopenharmony_ci	if (ehcidebug) {
478f9f848faSopenharmony_ci		ehci_dump_sqh(sc, sc->sc_async_p_last);
479f9f848faSopenharmony_ci	}
480f9f848faSopenharmony_ci#endif
481f9f848faSopenharmony_ci
482f9f848faSopenharmony_ci	/* finial setup */
483f9f848faSopenharmony_ci	err = ehci_init_sub(sc);
484f9f848faSopenharmony_ci
485f9f848faSopenharmony_ci	if (!err) {
486f9f848faSopenharmony_ci		/* catch any lost interrupts */
487f9f848faSopenharmony_ci		ehci_do_poll(&sc->sc_bus);
488f9f848faSopenharmony_ci	}
489f9f848faSopenharmony_ci	return (err);
490f9f848faSopenharmony_ci}
491f9f848faSopenharmony_ci
492f9f848faSopenharmony_ci/*
493f9f848faSopenharmony_ci * shut down the controller when the system is going down
494f9f848faSopenharmony_ci */
495f9f848faSopenharmony_civoid
496f9f848faSopenharmony_ciehci_detach(ehci_softc_t *sc)
497f9f848faSopenharmony_ci{
498f9f848faSopenharmony_ci	USB_BUS_LOCK(&sc->sc_bus);
499f9f848faSopenharmony_ci
500f9f848faSopenharmony_ci	callout_stop(&sc->sc_tmo_pcd);
501f9f848faSopenharmony_ci	callout_stop(&sc->sc_tmo_poll);
502f9f848faSopenharmony_ci
503f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_USBINTR, 0);
504f9f848faSopenharmony_ci	USB_BUS_UNLOCK(&sc->sc_bus);
505f9f848faSopenharmony_ci
506f9f848faSopenharmony_ci	if (ehci_hcreset(sc)) {
507f9f848faSopenharmony_ci		DPRINTF("reset failed!\n");
508f9f848faSopenharmony_ci	}
509f9f848faSopenharmony_ci
510f9f848faSopenharmony_ci	/* XXX let stray task complete */
511f9f848faSopenharmony_ci	usb_pause_mtx(NULL, hz / 20);
512f9f848faSopenharmony_ci
513f9f848faSopenharmony_ci	callout_drain(&sc->sc_tmo_pcd);
514f9f848faSopenharmony_ci	callout_drain(&sc->sc_tmo_poll);
515f9f848faSopenharmony_ci}
516f9f848faSopenharmony_ci
517f9f848faSopenharmony_cistatic void
518f9f848faSopenharmony_ciehci_suspend(ehci_softc_t *sc)
519f9f848faSopenharmony_ci{
520f9f848faSopenharmony_ci	DPRINTF("stopping the HC\n");
521f9f848faSopenharmony_ci
522f9f848faSopenharmony_ci	/* reset HC */
523f9f848faSopenharmony_ci	(void)ehci_hcreset(sc);
524f9f848faSopenharmony_ci}
525f9f848faSopenharmony_ci
526f9f848faSopenharmony_cistatic void
527f9f848faSopenharmony_ciehci_resume(ehci_softc_t *sc)
528f9f848faSopenharmony_ci{
529f9f848faSopenharmony_ci	/* reset HC */
530f9f848faSopenharmony_ci	(void)ehci_hcreset(sc);
531f9f848faSopenharmony_ci
532f9f848faSopenharmony_ci	/* setup HC */
533f9f848faSopenharmony_ci	(void)ehci_init_sub(sc);
534f9f848faSopenharmony_ci
535f9f848faSopenharmony_ci	/* catch any lost interrupts */
536f9f848faSopenharmony_ci	ehci_do_poll(&sc->sc_bus);
537f9f848faSopenharmony_ci}
538f9f848faSopenharmony_ci
539f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
540f9f848faSopenharmony_cistatic void
541f9f848faSopenharmony_ciehci_dump_regs(ehci_softc_t *sc)
542f9f848faSopenharmony_ci{
543f9f848faSopenharmony_ci	uint32_t i;
544f9f848faSopenharmony_ci
545f9f848faSopenharmony_ci	i = EOREAD4(sc, EHCI_USBCMD);
546f9f848faSopenharmony_ci	PRINTK("cmd=0x%08x\n", i);
547f9f848faSopenharmony_ci
548f9f848faSopenharmony_ci	if (i & EHCI_CMD_ITC_1)
549f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_ITC_1\n");
550f9f848faSopenharmony_ci	if (i & EHCI_CMD_ITC_2)
551f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_ITC_2\n");
552f9f848faSopenharmony_ci	if (i & EHCI_CMD_ITC_4)
553f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_ITC_4\n");
554f9f848faSopenharmony_ci	if (i & EHCI_CMD_ITC_8)
555f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_ITC_8\n");
556f9f848faSopenharmony_ci	if (i & EHCI_CMD_ITC_16)
557f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_ITC_16\n");
558f9f848faSopenharmony_ci	if (i & EHCI_CMD_ITC_32)
559f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_ITC_32\n");
560f9f848faSopenharmony_ci	if (i & EHCI_CMD_ITC_64)
561f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_ITC_64\n");
562f9f848faSopenharmony_ci	if (i & EHCI_CMD_ASPME)
563f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_ASPME\n");
564f9f848faSopenharmony_ci	if (i & EHCI_CMD_ASPMC)
565f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_ASPMC\n");
566f9f848faSopenharmony_ci	if (i & EHCI_CMD_LHCR)
567f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_LHCR\n");
568f9f848faSopenharmony_ci	if (i & EHCI_CMD_IAAD)
569f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_IAAD\n");
570f9f848faSopenharmony_ci	if (i & EHCI_CMD_ASE)
571f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_ASE\n");
572f9f848faSopenharmony_ci	if (i & EHCI_CMD_PSE)
573f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_PSE\n");
574f9f848faSopenharmony_ci	if (i & EHCI_CMD_FLS_M)
575f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_FLS_M\n");
576f9f848faSopenharmony_ci	if (i & EHCI_CMD_HCRESET)
577f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_HCRESET\n");
578f9f848faSopenharmony_ci	if (i & EHCI_CMD_RS)
579f9f848faSopenharmony_ci		PRINTK(" EHCI_CMD_RS\n");
580f9f848faSopenharmony_ci
581f9f848faSopenharmony_ci	i = EOREAD4(sc, EHCI_USBSTS);
582f9f848faSopenharmony_ci
583f9f848faSopenharmony_ci	PRINTK("sts=0x%08x\n", i);
584f9f848faSopenharmony_ci
585f9f848faSopenharmony_ci	if (i & EHCI_STS_ASS)
586f9f848faSopenharmony_ci		PRINTK(" EHCI_STS_ASS\n");
587f9f848faSopenharmony_ci	if (i & EHCI_STS_PSS)
588f9f848faSopenharmony_ci		PRINTK(" EHCI_STS_PSS\n");
589f9f848faSopenharmony_ci	if (i & EHCI_STS_REC)
590f9f848faSopenharmony_ci		PRINTK(" EHCI_STS_REC\n");
591f9f848faSopenharmony_ci	if (i & EHCI_STS_HCH)
592f9f848faSopenharmony_ci		PRINTK(" EHCI_STS_HCH\n");
593f9f848faSopenharmony_ci	if (i & EHCI_STS_IAA)
594f9f848faSopenharmony_ci		PRINTK(" EHCI_STS_IAA\n");
595f9f848faSopenharmony_ci	if (i & EHCI_STS_HSE)
596f9f848faSopenharmony_ci		PRINTK(" EHCI_STS_HSE\n");
597f9f848faSopenharmony_ci	if (i & EHCI_STS_FLR)
598f9f848faSopenharmony_ci		PRINTK(" EHCI_STS_FLR\n");
599f9f848faSopenharmony_ci	if (i & EHCI_STS_PCD)
600f9f848faSopenharmony_ci		PRINTK(" EHCI_STS_PCD\n");
601f9f848faSopenharmony_ci	if (i & EHCI_STS_ERRINT)
602f9f848faSopenharmony_ci		PRINTK(" EHCI_STS_ERRINT\n");
603f9f848faSopenharmony_ci	if (i & EHCI_STS_INT)
604f9f848faSopenharmony_ci		PRINTK(" EHCI_STS_INT\n");
605f9f848faSopenharmony_ci
606f9f848faSopenharmony_ci	PRINTK("intr=0x%08x\n",
607f9f848faSopenharmony_ci	    EOREAD4(sc, EHCI_USBINTR));
608f9f848faSopenharmony_ci	PRINTK("frindex=0x%08x ctrdsegm=0x%08x periodic=0x%08x async=0x%08x\n",
609f9f848faSopenharmony_ci	    EOREAD4(sc, EHCI_FRINDEX),
610f9f848faSopenharmony_ci	    EOREAD4(sc, EHCI_CTRLDSSEGMENT),
611f9f848faSopenharmony_ci	    EOREAD4(sc, EHCI_PERIODICLISTBASE),
612f9f848faSopenharmony_ci	    EOREAD4(sc, EHCI_ASYNCLISTADDR));
613f9f848faSopenharmony_ci	for (i = 1; i <= sc->sc_noport; i++) {
614f9f848faSopenharmony_ci		PRINTK("port %d status=0x%08x\n", i,
615f9f848faSopenharmony_ci		    EOREAD4(sc, EHCI_PORTSC(i)));
616f9f848faSopenharmony_ci	}
617f9f848faSopenharmony_ci}
618f9f848faSopenharmony_ci
619f9f848faSopenharmony_cistatic void
620f9f848faSopenharmony_ciehci_dump_link(ehci_softc_t *sc, uint32_t link, int type)
621f9f848faSopenharmony_ci{
622f9f848faSopenharmony_ci	link = hc32toh(sc, link);
623f9f848faSopenharmony_ci	PRINTK("0x%08x", link);
624f9f848faSopenharmony_ci	if (link & EHCI_LINK_TERMINATE)
625f9f848faSopenharmony_ci		PRINTK("<T>");
626f9f848faSopenharmony_ci	else {
627f9f848faSopenharmony_ci		PRINTK("<");
628f9f848faSopenharmony_ci		if (type) {
629f9f848faSopenharmony_ci			switch (EHCI_LINK_TYPE(link)) {
630f9f848faSopenharmony_ci			case EHCI_LINK_ITD:
631f9f848faSopenharmony_ci				PRINTK("ITD");
632f9f848faSopenharmony_ci				break;
633f9f848faSopenharmony_ci			case EHCI_LINK_QH:
634f9f848faSopenharmony_ci				PRINTK("QH");
635f9f848faSopenharmony_ci				break;
636f9f848faSopenharmony_ci			case EHCI_LINK_SITD:
637f9f848faSopenharmony_ci				PRINTK("SITD");
638f9f848faSopenharmony_ci				break;
639f9f848faSopenharmony_ci			case EHCI_LINK_FSTN:
640f9f848faSopenharmony_ci				PRINTK("FSTN");
641f9f848faSopenharmony_ci				break;
642f9f848faSopenharmony_ci			}
643f9f848faSopenharmony_ci		}
644f9f848faSopenharmony_ci		PRINTK(">");
645f9f848faSopenharmony_ci	}
646f9f848faSopenharmony_ci}
647f9f848faSopenharmony_ci
648f9f848faSopenharmony_cistatic void
649f9f848faSopenharmony_ciehci_dump_qtd(ehci_softc_t *sc, ehci_qtd_t *qtd)
650f9f848faSopenharmony_ci{
651f9f848faSopenharmony_ci	uint32_t s;
652f9f848faSopenharmony_ci
653f9f848faSopenharmony_ci	PRINTK("  next=");
654f9f848faSopenharmony_ci	ehci_dump_link(sc, qtd->qtd_next, 0);
655f9f848faSopenharmony_ci	PRINTK(" altnext=");
656f9f848faSopenharmony_ci	ehci_dump_link(sc, qtd->qtd_altnext, 0);
657f9f848faSopenharmony_ci	PRINTK("\n");
658f9f848faSopenharmony_ci	s = hc32toh(sc, qtd->qtd_status);
659f9f848faSopenharmony_ci	PRINTK("  status=0x%08x: toggle=%d bytes=0x%x ioc=%d c_page=0x%x\n",
660f9f848faSopenharmony_ci	    s, EHCI_QTD_GET_TOGGLE(s), EHCI_QTD_GET_BYTES(s),
661f9f848faSopenharmony_ci	    EHCI_QTD_GET_IOC(s), EHCI_QTD_GET_C_PAGE(s));
662f9f848faSopenharmony_ci	PRINTK("    cerr=%d pid=%d stat=%s%s%s%s%s%s%s%s\n",
663f9f848faSopenharmony_ci	    EHCI_QTD_GET_CERR(s), EHCI_QTD_GET_PID(s),
664f9f848faSopenharmony_ci	    (s & EHCI_QTD_ACTIVE) ? "ACTIVE" : "NOT_ACTIVE",
665f9f848faSopenharmony_ci	    (s & EHCI_QTD_HALTED) ? "-HALTED" : "",
666f9f848faSopenharmony_ci	    (s & EHCI_QTD_BUFERR) ? "-BUFERR" : "",
667f9f848faSopenharmony_ci	    (s & EHCI_QTD_BABBLE) ? "-BABBLE" : "",
668f9f848faSopenharmony_ci	    (s & EHCI_QTD_XACTERR) ? "-XACTERR" : "",
669f9f848faSopenharmony_ci	    (s & EHCI_QTD_MISSEDMICRO) ? "-MISSED" : "",
670f9f848faSopenharmony_ci	    (s & EHCI_QTD_SPLITXSTATE) ? "-SPLIT" : "",
671f9f848faSopenharmony_ci	    (s & EHCI_QTD_PINGSTATE) ? "-PING" : "");
672f9f848faSopenharmony_ci
673f9f848faSopenharmony_ci	for (s = 0; s < 5; s++) {
674f9f848faSopenharmony_ci		PRINTK("  buffer[%d]=0x%08x\n", s,
675f9f848faSopenharmony_ci		    hc32toh(sc, qtd->qtd_buffer[s]));
676f9f848faSopenharmony_ci	}
677f9f848faSopenharmony_ci	for (s = 0; s < 5; s++) {
678f9f848faSopenharmony_ci		PRINTK("  buffer_hi[%d]=0x%08x\n", s,
679f9f848faSopenharmony_ci		    hc32toh(sc, qtd->qtd_buffer_hi[s]));
680f9f848faSopenharmony_ci	}
681f9f848faSopenharmony_ci}
682f9f848faSopenharmony_ci
683f9f848faSopenharmony_cistatic uint8_t
684f9f848faSopenharmony_ciehci_dump_sqtd(ehci_softc_t *sc, ehci_qtd_t *sqtd)
685f9f848faSopenharmony_ci{
686f9f848faSopenharmony_ci	uint8_t temp;
687f9f848faSopenharmony_ci
688f9f848faSopenharmony_ci	usb_pc_cpu_invalidate(sqtd->page_cache);
689f9f848faSopenharmony_ci	PRINTK("QTD(%p) at 0x%08x:\n", sqtd, hc32toh(sc, sqtd->qtd_self));
690f9f848faSopenharmony_ci	ehci_dump_qtd(sc, sqtd);
691f9f848faSopenharmony_ci	temp = (sqtd->qtd_next & htohc32(sc, EHCI_LINK_TERMINATE)) ? 1 : 0;
692f9f848faSopenharmony_ci	return (temp);
693f9f848faSopenharmony_ci}
694f9f848faSopenharmony_ci
695f9f848faSopenharmony_cistatic void
696f9f848faSopenharmony_ciehci_dump_sqtds(ehci_softc_t *sc, ehci_qtd_t *sqtd)
697f9f848faSopenharmony_ci{
698f9f848faSopenharmony_ci	uint16_t i;
699f9f848faSopenharmony_ci	uint8_t stop;
700f9f848faSopenharmony_ci
701f9f848faSopenharmony_ci	stop = 0;
702f9f848faSopenharmony_ci	for (i = 0; sqtd && (i < 20) && !stop; sqtd = sqtd->obj_next, i++) {
703f9f848faSopenharmony_ci		stop = ehci_dump_sqtd(sc, sqtd);
704f9f848faSopenharmony_ci	}
705f9f848faSopenharmony_ci	if (sqtd) {
706f9f848faSopenharmony_ci		PRINTK("dump aborted, too many TDs\n");
707f9f848faSopenharmony_ci	}
708f9f848faSopenharmony_ci}
709f9f848faSopenharmony_ci
710f9f848faSopenharmony_cistatic void
711f9f848faSopenharmony_ciehci_dump_sqh(ehci_softc_t *sc, ehci_qh_t *qh)
712f9f848faSopenharmony_ci{
713f9f848faSopenharmony_ci	uint32_t endp;
714f9f848faSopenharmony_ci	uint32_t endphub;
715f9f848faSopenharmony_ci
716f9f848faSopenharmony_ci	usb_pc_cpu_invalidate(qh->page_cache);
717f9f848faSopenharmony_ci	PRINTK("QH(%p) at 0x%08x:\n", qh, hc32toh(sc, qh->qh_self) & ~0x1F);
718f9f848faSopenharmony_ci	PRINTK("  link=");
719f9f848faSopenharmony_ci	ehci_dump_link(sc, qh->qh_link, 1);
720f9f848faSopenharmony_ci	PRINTK("\n");
721f9f848faSopenharmony_ci	endp = hc32toh(sc, qh->qh_endp);
722f9f848faSopenharmony_ci	PRINTK("  endp=0x%08x\n", endp);
723f9f848faSopenharmony_ci	PRINTK("    addr=0x%02x inact=%d endpt=%d eps=%d dtc=%d hrecl=%d\n",
724f9f848faSopenharmony_ci	    EHCI_QH_GET_ADDR(endp), EHCI_QH_GET_INACT(endp),
725f9f848faSopenharmony_ci	    EHCI_QH_GET_ENDPT(endp), EHCI_QH_GET_EPS(endp),
726f9f848faSopenharmony_ci	    EHCI_QH_GET_DTC(endp), EHCI_QH_GET_HRECL(endp));
727f9f848faSopenharmony_ci	PRINTK("    mpl=0x%x ctl=%d nrl=%d\n",
728f9f848faSopenharmony_ci	    EHCI_QH_GET_MPL(endp), EHCI_QH_GET_CTL(endp),
729f9f848faSopenharmony_ci	    EHCI_QH_GET_NRL(endp));
730f9f848faSopenharmony_ci	endphub = hc32toh(sc, qh->qh_endphub);
731f9f848faSopenharmony_ci	PRINTK("  endphub=0x%08x\n", endphub);
732f9f848faSopenharmony_ci	PRINTK("    smask=0x%02x cmask=0x%02x huba=0x%02x port=%d mult=%d\n",
733f9f848faSopenharmony_ci	    EHCI_QH_GET_SMASK(endphub), EHCI_QH_GET_CMASK(endphub),
734f9f848faSopenharmony_ci	    EHCI_QH_GET_HUBA(endphub), EHCI_QH_GET_PORT(endphub),
735f9f848faSopenharmony_ci	    EHCI_QH_GET_MULT(endphub));
736f9f848faSopenharmony_ci	PRINTK("  curqtd=");
737f9f848faSopenharmony_ci	ehci_dump_link(sc, qh->qh_curqtd, 0);
738f9f848faSopenharmony_ci	PRINTK("\n");
739f9f848faSopenharmony_ci	PRINTK("Overlay qTD:\n");
740f9f848faSopenharmony_ci	ehci_dump_qtd(sc, (void *)&qh->qh_qtd);
741f9f848faSopenharmony_ci}
742f9f848faSopenharmony_ci
743f9f848faSopenharmony_cistatic void
744f9f848faSopenharmony_ciehci_dump_sitd(ehci_softc_t *sc, ehci_sitd_t *sitd)
745f9f848faSopenharmony_ci{
746f9f848faSopenharmony_ci	usb_pc_cpu_invalidate(sitd->page_cache);
747f9f848faSopenharmony_ci	PRINTK("SITD(%p) at 0x%08x\n", sitd, hc32toh(sc, sitd->sitd_self) & ~0x1F);
748f9f848faSopenharmony_ci	PRINTK(" next=0x%08x\n", hc32toh(sc, sitd->sitd_next));
749f9f848faSopenharmony_ci	PRINTK(" portaddr=0x%08x dir=%s addr=%d endpt=0x%x port=0x%x huba=0x%x\n",
750f9f848faSopenharmony_ci	    hc32toh(sc, sitd->sitd_portaddr),
751f9f848faSopenharmony_ci	    (sitd->sitd_portaddr & htohc32(sc, EHCI_SITD_SET_DIR_IN))
752f9f848faSopenharmony_ci	    ? "in" : "out",
753f9f848faSopenharmony_ci	    EHCI_SITD_GET_ADDR(hc32toh(sc, sitd->sitd_portaddr)),
754f9f848faSopenharmony_ci	    EHCI_SITD_GET_ENDPT(hc32toh(sc, sitd->sitd_portaddr)),
755f9f848faSopenharmony_ci	    EHCI_SITD_GET_PORT(hc32toh(sc, sitd->sitd_portaddr)),
756f9f848faSopenharmony_ci	    EHCI_SITD_GET_HUBA(hc32toh(sc, sitd->sitd_portaddr)));
757f9f848faSopenharmony_ci	PRINTK(" mask=0x%08x\n", hc32toh(sc, sitd->sitd_mask));
758f9f848faSopenharmony_ci	PRINTK(" status=0x%08x <%s> len=0x%x\n", hc32toh(sc, sitd->sitd_status),
759f9f848faSopenharmony_ci	    (sitd->sitd_status & htohc32(sc, EHCI_SITD_ACTIVE)) ? "ACTIVE" : "",
760f9f848faSopenharmony_ci	    EHCI_SITD_GET_LEN(hc32toh(sc, sitd->sitd_status)));
761f9f848faSopenharmony_ci	PRINTK(" back=0x%08x, bp=0x%08x,0x%08x,0x%08x,0x%08x\n",
762f9f848faSopenharmony_ci	    hc32toh(sc, sitd->sitd_back),
763f9f848faSopenharmony_ci	    hc32toh(sc, sitd->sitd_bp[0]),
764f9f848faSopenharmony_ci	    hc32toh(sc, sitd->sitd_bp[1]),
765f9f848faSopenharmony_ci	    hc32toh(sc, sitd->sitd_bp_hi[0]),
766f9f848faSopenharmony_ci	    hc32toh(sc, sitd->sitd_bp_hi[1]));
767f9f848faSopenharmony_ci}
768f9f848faSopenharmony_ci
769f9f848faSopenharmony_cistatic void
770f9f848faSopenharmony_ciehci_dump_itd(ehci_softc_t *sc, ehci_itd_t *itd)
771f9f848faSopenharmony_ci{
772f9f848faSopenharmony_ci	usb_pc_cpu_invalidate(itd->page_cache);
773f9f848faSopenharmony_ci	PRINTK("ITD(%p) at 0x%08x\n", itd, hc32toh(sc, itd->itd_self) & ~0x1F);
774f9f848faSopenharmony_ci	PRINTK(" next=0x%08x\n", hc32toh(sc, itd->itd_next));
775f9f848faSopenharmony_ci	PRINTK(" status[0]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[0]),
776f9f848faSopenharmony_ci	    (itd->itd_status[0] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
777f9f848faSopenharmony_ci	PRINTK(" status[1]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[1]),
778f9f848faSopenharmony_ci	    (itd->itd_status[1] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
779f9f848faSopenharmony_ci	PRINTK(" status[2]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[2]),
780f9f848faSopenharmony_ci	    (itd->itd_status[2] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
781f9f848faSopenharmony_ci	PRINTK(" status[3]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[3]),
782f9f848faSopenharmony_ci	    (itd->itd_status[3] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
783f9f848faSopenharmony_ci	PRINTK(" status[4]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[4]),
784f9f848faSopenharmony_ci	    (itd->itd_status[4] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
785f9f848faSopenharmony_ci	PRINTK(" status[5]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[5]),
786f9f848faSopenharmony_ci	    (itd->itd_status[5] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
787f9f848faSopenharmony_ci	PRINTK(" status[6]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[6]),
788f9f848faSopenharmony_ci	    (itd->itd_status[6] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
789f9f848faSopenharmony_ci	PRINTK(" status[7]=0x%08x; <%s>\n", hc32toh(sc, itd->itd_status[7]),
790f9f848faSopenharmony_ci	    (itd->itd_status[7] & htohc32(sc, EHCI_ITD_ACTIVE)) ? "ACTIVE" : "");
791f9f848faSopenharmony_ci	PRINTK(" bp[0]=0x%08x\n", hc32toh(sc, itd->itd_bp[0]));
792f9f848faSopenharmony_ci	PRINTK("  addr=0x%02x; endpt=0x%01x\n",
793f9f848faSopenharmony_ci	    EHCI_ITD_GET_ADDR(hc32toh(sc, itd->itd_bp[0])),
794f9f848faSopenharmony_ci	    EHCI_ITD_GET_ENDPT(hc32toh(sc, itd->itd_bp[0])));
795f9f848faSopenharmony_ci	PRINTK(" bp[1]=0x%08x\n", hc32toh(sc, itd->itd_bp[1]));
796f9f848faSopenharmony_ci	PRINTK(" dir=%s; mpl=0x%02x\n",
797f9f848faSopenharmony_ci	    (hc32toh(sc, itd->itd_bp[1]) & EHCI_ITD_SET_DIR_IN) ? "in" : "out",
798f9f848faSopenharmony_ci	    EHCI_ITD_GET_MPL(hc32toh(sc, itd->itd_bp[1])));
799f9f848faSopenharmony_ci	PRINTK(" bp[2..6]=0x%08x,0x%08x,0x%08x,0x%08x,0x%08x\n",
800f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp[2]),
801f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp[3]),
802f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp[4]),
803f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp[5]),
804f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp[6]));
805f9f848faSopenharmony_ci	PRINTK(" bp_hi=0x%08x,0x%08x,0x%08x,0x%08x,\n"
806f9f848faSopenharmony_ci	    "      0x%08x,0x%08x,0x%08x\n",
807f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp_hi[0]),
808f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp_hi[1]),
809f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp_hi[2]),
810f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp_hi[3]),
811f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp_hi[4]),
812f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp_hi[5]),
813f9f848faSopenharmony_ci	    hc32toh(sc, itd->itd_bp_hi[6]));
814f9f848faSopenharmony_ci}
815f9f848faSopenharmony_ci
816f9f848faSopenharmony_cistatic void
817f9f848faSopenharmony_ciehci_dump_isoc(ehci_softc_t *sc)
818f9f848faSopenharmony_ci{
819f9f848faSopenharmony_ci	ehci_itd_t *itd;
820f9f848faSopenharmony_ci	ehci_sitd_t *sitd;
821f9f848faSopenharmony_ci	uint16_t max = 1000;
822f9f848faSopenharmony_ci	uint16_t pos;
823f9f848faSopenharmony_ci
824f9f848faSopenharmony_ci	pos = (EOREAD4(sc, EHCI_FRINDEX) / 8) &
825f9f848faSopenharmony_ci	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
826f9f848faSopenharmony_ci
827f9f848faSopenharmony_ci	PRINTK("%s: isochronous dump from frame 0x%03x:\n",
828f9f848faSopenharmony_ci	    __FUNCTION__, pos);
829f9f848faSopenharmony_ci
830f9f848faSopenharmony_ci	itd = sc->sc_isoc_hs_p_last[pos];
831f9f848faSopenharmony_ci	sitd = sc->sc_isoc_fs_p_last[pos];
832f9f848faSopenharmony_ci
833f9f848faSopenharmony_ci	while (itd && max && max--) {
834f9f848faSopenharmony_ci		ehci_dump_itd(sc, itd);
835f9f848faSopenharmony_ci		itd = itd->prev;
836f9f848faSopenharmony_ci	}
837f9f848faSopenharmony_ci
838f9f848faSopenharmony_ci	while (sitd && max && max--) {
839f9f848faSopenharmony_ci		ehci_dump_sitd(sc, sitd);
840f9f848faSopenharmony_ci		sitd = sitd->prev;
841f9f848faSopenharmony_ci	}
842f9f848faSopenharmony_ci}
843f9f848faSopenharmony_ci
844f9f848faSopenharmony_ci#endif
845f9f848faSopenharmony_ci
846f9f848faSopenharmony_cistatic void
847f9f848faSopenharmony_ciehci_transfer_intr_enqueue(struct usb_xfer *xfer)
848f9f848faSopenharmony_ci{
849f9f848faSopenharmony_ci	/* check for early completion */
850f9f848faSopenharmony_ci	if (ehci_check_transfer(xfer)) {
851f9f848faSopenharmony_ci		DPRINTFN(0, " ehci_check_transfer return\n");
852f9f848faSopenharmony_ci		return;
853f9f848faSopenharmony_ci	}
854f9f848faSopenharmony_ci	DPRINTFN(7, " enqueue\n");
855f9f848faSopenharmony_ci	/* put transfer on interrupt queue */
856f9f848faSopenharmony_ci	usbd_transfer_enqueue(&xfer->xroot->bus->intr_q, xfer);
857f9f848faSopenharmony_ci
858f9f848faSopenharmony_ci	/* start timeout, if any */
859f9f848faSopenharmony_ci	if (xfer->timeout != 0) {
860f9f848faSopenharmony_ci		usbd_transfer_timeout_ms(xfer, &ehci_timeout, xfer->timeout);
861f9f848faSopenharmony_ci	}
862f9f848faSopenharmony_ci}
863f9f848faSopenharmony_ci
864f9f848faSopenharmony_ci#define	EHCI_APPEND_FS_TD(std, last) (last) = _ehci_append_fs_td(std, last)
865f9f848faSopenharmony_cistatic ehci_sitd_t *
866f9f848faSopenharmony_ci_ehci_append_fs_td(ehci_sitd_t *ehci_std, ehci_sitd_t *last)
867f9f848faSopenharmony_ci{
868f9f848faSopenharmony_ci	DPRINTFN(11, "%p to %p\n", ehci_std, last);
869f9f848faSopenharmony_ci
870f9f848faSopenharmony_ci	/* (sc->sc_bus.mtx) must be locked */
871f9f848faSopenharmony_ci
872f9f848faSopenharmony_ci	ehci_std->next = last->next;
873f9f848faSopenharmony_ci	ehci_std->sitd_next = last->sitd_next;
874f9f848faSopenharmony_ci
875f9f848faSopenharmony_ci	ehci_std->prev = last;
876f9f848faSopenharmony_ci
877f9f848faSopenharmony_ci	usb_pc_cpu_flush(ehci_std->page_cache);
878f9f848faSopenharmony_ci
879f9f848faSopenharmony_ci	/*
880f9f848faSopenharmony_ci	 * the last->next->prev is never followed: std->next->prev = std;
881f9f848faSopenharmony_ci	 */
882f9f848faSopenharmony_ci	last->next = ehci_std;
883f9f848faSopenharmony_ci	last->sitd_next = ehci_std->sitd_self;
884f9f848faSopenharmony_ci
885f9f848faSopenharmony_ci	usb_pc_cpu_flush(last->page_cache);
886f9f848faSopenharmony_ci
887f9f848faSopenharmony_ci	return (ehci_std);
888f9f848faSopenharmony_ci}
889f9f848faSopenharmony_ci
890f9f848faSopenharmony_ci#define	EHCI_APPEND_HS_TD(std, last) (last) = _ehci_append_hs_td(std, last)
891f9f848faSopenharmony_cistatic ehci_itd_t *
892f9f848faSopenharmony_ci_ehci_append_hs_td(ehci_itd_t *ehci_std, ehci_itd_t *last)
893f9f848faSopenharmony_ci{
894f9f848faSopenharmony_ci	DPRINTFN(11, "%p to %p\n", ehci_std, last);
895f9f848faSopenharmony_ci
896f9f848faSopenharmony_ci	/* (sc->sc_bus.mtx) must be locked */
897f9f848faSopenharmony_ci
898f9f848faSopenharmony_ci	ehci_std->next = last->next;
899f9f848faSopenharmony_ci	ehci_std->itd_next = last->itd_next;
900f9f848faSopenharmony_ci
901f9f848faSopenharmony_ci	ehci_std->prev = last;
902f9f848faSopenharmony_ci
903f9f848faSopenharmony_ci	usb_pc_cpu_flush(ehci_std->page_cache);
904f9f848faSopenharmony_ci
905f9f848faSopenharmony_ci	/*
906f9f848faSopenharmony_ci	 * the last->next->prev is never followed: std->next->prev = std;
907f9f848faSopenharmony_ci	 */
908f9f848faSopenharmony_ci	last->next = ehci_std;
909f9f848faSopenharmony_ci	last->itd_next = ehci_std->itd_self;
910f9f848faSopenharmony_ci
911f9f848faSopenharmony_ci	usb_pc_cpu_flush(last->page_cache);
912f9f848faSopenharmony_ci
913f9f848faSopenharmony_ci	return (ehci_std);
914f9f848faSopenharmony_ci}
915f9f848faSopenharmony_ci
916f9f848faSopenharmony_ci#define	EHCI_APPEND_QH(sqh, last) (last) = _ehci_append_qh(sqh, last)
917f9f848faSopenharmony_cistatic ehci_qh_t *
918f9f848faSopenharmony_ci_ehci_append_qh(ehci_qh_t *sqh, ehci_qh_t *last)
919f9f848faSopenharmony_ci{
920f9f848faSopenharmony_ci	DPRINTFN(11, "%p to %p\n", sqh, last);
921f9f848faSopenharmony_ci
922f9f848faSopenharmony_ci	if (sqh->prev != NULL) {
923f9f848faSopenharmony_ci		/* should not happen */
924f9f848faSopenharmony_ci		DPRINTFN(0, "QH already linked!\n");
925f9f848faSopenharmony_ci		return (last);
926f9f848faSopenharmony_ci	}
927f9f848faSopenharmony_ci	/* (sc->sc_bus.mtx) must be locked */
928f9f848faSopenharmony_ci
929f9f848faSopenharmony_ci	sqh->next = last->next;
930f9f848faSopenharmony_ci	sqh->qh_link = last->qh_link;
931f9f848faSopenharmony_ci
932f9f848faSopenharmony_ci	sqh->prev = last;
933f9f848faSopenharmony_ci
934f9f848faSopenharmony_ci	usb_pc_cpu_flush(sqh->page_cache);
935f9f848faSopenharmony_ci
936f9f848faSopenharmony_ci	/*
937f9f848faSopenharmony_ci	 * the last->next->prev is never followed: sqh->next->prev = sqh;
938f9f848faSopenharmony_ci	 */
939f9f848faSopenharmony_ci
940f9f848faSopenharmony_ci	last->next = sqh;
941f9f848faSopenharmony_ci	last->qh_link = sqh->qh_self;
942f9f848faSopenharmony_ci
943f9f848faSopenharmony_ci	usb_pc_cpu_flush(last->page_cache);
944f9f848faSopenharmony_ci
945f9f848faSopenharmony_ci	return (sqh);
946f9f848faSopenharmony_ci}
947f9f848faSopenharmony_ci
948f9f848faSopenharmony_ci#define	EHCI_REMOVE_FS_TD(std, last) (last) = _ehci_remove_fs_td(std, last)
949f9f848faSopenharmony_cistatic ehci_sitd_t *
950f9f848faSopenharmony_ci_ehci_remove_fs_td(ehci_sitd_t *ehci_std, ehci_sitd_t *last)
951f9f848faSopenharmony_ci{
952f9f848faSopenharmony_ci	DPRINTFN(11, "%p from %p\n", ehci_std, last);
953f9f848faSopenharmony_ci
954f9f848faSopenharmony_ci	/* (sc->sc_bus.mtx) must be locked */
955f9f848faSopenharmony_ci
956f9f848faSopenharmony_ci	ehci_std->prev->next = ehci_std->next;
957f9f848faSopenharmony_ci	ehci_std->prev->sitd_next = ehci_std->sitd_next;
958f9f848faSopenharmony_ci
959f9f848faSopenharmony_ci	usb_pc_cpu_flush(ehci_std->prev->page_cache);
960f9f848faSopenharmony_ci
961f9f848faSopenharmony_ci	if (ehci_std->next) {
962f9f848faSopenharmony_ci		ehci_std->next->prev = ehci_std->prev;
963f9f848faSopenharmony_ci		usb_pc_cpu_flush(ehci_std->next->page_cache);
964f9f848faSopenharmony_ci	}
965f9f848faSopenharmony_ci	return ((last == ehci_std) ? ehci_std->prev : last);
966f9f848faSopenharmony_ci}
967f9f848faSopenharmony_ci
968f9f848faSopenharmony_ci#define	EHCI_REMOVE_HS_TD(std, last) (last) = _ehci_remove_hs_td(std, last)
969f9f848faSopenharmony_cistatic ehci_itd_t *
970f9f848faSopenharmony_ci_ehci_remove_hs_td(ehci_itd_t *ehci_std, ehci_itd_t *last)
971f9f848faSopenharmony_ci{
972f9f848faSopenharmony_ci	DPRINTFN(11, "%p from %p\n", ehci_std, last);
973f9f848faSopenharmony_ci
974f9f848faSopenharmony_ci	/* (sc->sc_bus.mtx) must be locked */
975f9f848faSopenharmony_ci
976f9f848faSopenharmony_ci	ehci_std->prev->next = ehci_std->next;
977f9f848faSopenharmony_ci	ehci_std->prev->itd_next = ehci_std->itd_next;
978f9f848faSopenharmony_ci
979f9f848faSopenharmony_ci	usb_pc_cpu_flush(ehci_std->prev->page_cache);
980f9f848faSopenharmony_ci
981f9f848faSopenharmony_ci	if (ehci_std->next) {
982f9f848faSopenharmony_ci		ehci_std->next->prev = ehci_std->prev;
983f9f848faSopenharmony_ci		usb_pc_cpu_flush(ehci_std->next->page_cache);
984f9f848faSopenharmony_ci	}
985f9f848faSopenharmony_ci	return ((last == ehci_std) ? ehci_std->prev : last);
986f9f848faSopenharmony_ci}
987f9f848faSopenharmony_ci
988f9f848faSopenharmony_ci#define	EHCI_REMOVE_QH(sqh, last) (last) = _ehci_remove_qh(sqh, last)
989f9f848faSopenharmony_cistatic ehci_qh_t *
990f9f848faSopenharmony_ci_ehci_remove_qh(ehci_qh_t *sqh, ehci_qh_t *last)
991f9f848faSopenharmony_ci{
992f9f848faSopenharmony_ci	DPRINTFN(11, "%p from %p\n", sqh, last);
993f9f848faSopenharmony_ci
994f9f848faSopenharmony_ci	/* (sc->sc_bus.mtx) must be locked */
995f9f848faSopenharmony_ci
996f9f848faSopenharmony_ci	/* only remove if not removed from a queue */
997f9f848faSopenharmony_ci	if (sqh->prev) {
998f9f848faSopenharmony_ci		sqh->prev->next = sqh->next;
999f9f848faSopenharmony_ci		sqh->prev->qh_link = sqh->qh_link;
1000f9f848faSopenharmony_ci
1001f9f848faSopenharmony_ci		usb_pc_cpu_flush(sqh->prev->page_cache);
1002f9f848faSopenharmony_ci
1003f9f848faSopenharmony_ci		if (sqh->next) {
1004f9f848faSopenharmony_ci			sqh->next->prev = sqh->prev;
1005f9f848faSopenharmony_ci			usb_pc_cpu_flush(sqh->next->page_cache);
1006f9f848faSopenharmony_ci		}
1007f9f848faSopenharmony_ci		last = ((last == sqh) ? sqh->prev : last);
1008f9f848faSopenharmony_ci
1009f9f848faSopenharmony_ci		sqh->prev = 0;
1010f9f848faSopenharmony_ci
1011f9f848faSopenharmony_ci		usb_pc_cpu_flush(sqh->page_cache);
1012f9f848faSopenharmony_ci	}
1013f9f848faSopenharmony_ci	return (last);
1014f9f848faSopenharmony_ci}
1015f9f848faSopenharmony_ci
1016f9f848faSopenharmony_cistatic void
1017f9f848faSopenharmony_ciehci_data_toggle_update(struct usb_xfer *xfer, uint16_t actlen, uint16_t xlen)
1018f9f848faSopenharmony_ci{
1019f9f848faSopenharmony_ci	uint16_t rem;
1020f9f848faSopenharmony_ci	uint8_t dt;
1021f9f848faSopenharmony_ci
1022f9f848faSopenharmony_ci	/* count number of full packets */
1023f9f848faSopenharmony_ci	dt = (actlen / xfer->max_packet_size) & 1;
1024f9f848faSopenharmony_ci
1025f9f848faSopenharmony_ci	/* compute remainder */
1026f9f848faSopenharmony_ci	rem = actlen % xfer->max_packet_size;
1027f9f848faSopenharmony_ci
1028f9f848faSopenharmony_ci	if (rem > 0)
1029f9f848faSopenharmony_ci		dt ^= 1;	/* short packet at the end */
1030f9f848faSopenharmony_ci	else if (actlen != xlen)
1031f9f848faSopenharmony_ci		dt ^= 1;	/* zero length packet at the end */
1032f9f848faSopenharmony_ci	else if (xlen == 0)
1033f9f848faSopenharmony_ci		dt ^= 1;	/* zero length transfer */
1034f9f848faSopenharmony_ci
1035f9f848faSopenharmony_ci	xfer->endpoint->toggle_next ^= dt;
1036f9f848faSopenharmony_ci}
1037f9f848faSopenharmony_ci
1038f9f848faSopenharmony_cistatic usb_error_t
1039f9f848faSopenharmony_ciehci_non_isoc_done_sub(struct usb_xfer *xfer)
1040f9f848faSopenharmony_ci{
1041f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
1042f9f848faSopenharmony_ci	ehci_qtd_t *td;
1043f9f848faSopenharmony_ci	ehci_qtd_t *td_alt_next;
1044f9f848faSopenharmony_ci	uint32_t status;
1045f9f848faSopenharmony_ci	uint16_t len;
1046f9f848faSopenharmony_ci
1047f9f848faSopenharmony_ci	td = (ehci_qtd_t *)xfer->td_transfer_cache;
1048f9f848faSopenharmony_ci	td_alt_next = td->alt_next;
1049f9f848faSopenharmony_ci
1050f9f848faSopenharmony_ci	if (xfer->aframes != xfer->nframes) {
1051f9f848faSopenharmony_ci		usbd_xfer_set_frame_len(xfer, xfer->aframes, 0);
1052f9f848faSopenharmony_ci	}
1053f9f848faSopenharmony_ci	while (1) {
1054f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(td->page_cache);
1055f9f848faSopenharmony_ci		status = hc32toh(sc, td->qtd_status);
1056f9f848faSopenharmony_ci
1057f9f848faSopenharmony_ci		len = EHCI_QTD_GET_BYTES(status);
1058f9f848faSopenharmony_ci
1059f9f848faSopenharmony_ci		/*
1060f9f848faSopenharmony_ci	         * Verify the status length and
1061f9f848faSopenharmony_ci		 * add the length to "frlengths[]":
1062f9f848faSopenharmony_ci	         */
1063f9f848faSopenharmony_ci		if (len > td->len) {
1064f9f848faSopenharmony_ci			/* should not happen */
1065f9f848faSopenharmony_ci			DPRINTF("Invalid status length, "
1066f9f848faSopenharmony_ci			    "0x%04x/0x%04x bytes\n", len, td->len);
1067f9f848faSopenharmony_ci			status |= EHCI_QTD_HALTED;
1068f9f848faSopenharmony_ci		} else if (xfer->aframes != xfer->nframes) {
1069f9f848faSopenharmony_ci			xfer->frlengths[xfer->aframes] += td->len - len;
1070f9f848faSopenharmony_ci			/* manually update data toggle */
1071f9f848faSopenharmony_ci			ehci_data_toggle_update(xfer, td->len - len, td->len);
1072f9f848faSopenharmony_ci		}
1073f9f848faSopenharmony_ci
1074f9f848faSopenharmony_ci		/* Check for last transfer */
1075f9f848faSopenharmony_ci		if (((void *)td) == xfer->td_transfer_last) {
1076f9f848faSopenharmony_ci			td = NULL;
1077f9f848faSopenharmony_ci			break;
1078f9f848faSopenharmony_ci		}
1079f9f848faSopenharmony_ci		/* Check for transfer error */
1080f9f848faSopenharmony_ci		if (status & EHCI_QTD_HALTED) {
1081f9f848faSopenharmony_ci			/* the transfer is finished */
1082f9f848faSopenharmony_ci			td = NULL;
1083f9f848faSopenharmony_ci			break;
1084f9f848faSopenharmony_ci		}
1085f9f848faSopenharmony_ci		/* Check for short transfer */
1086f9f848faSopenharmony_ci		if (len > 0) {
1087f9f848faSopenharmony_ci			if (xfer->flags_int.short_frames_ok) {
1088f9f848faSopenharmony_ci				/* follow alt next */
1089f9f848faSopenharmony_ci				td = td->alt_next;
1090f9f848faSopenharmony_ci			} else {
1091f9f848faSopenharmony_ci				/* the transfer is finished */
1092f9f848faSopenharmony_ci				td = NULL;
1093f9f848faSopenharmony_ci			}
1094f9f848faSopenharmony_ci			break;
1095f9f848faSopenharmony_ci		}
1096f9f848faSopenharmony_ci		td = td->obj_next;
1097f9f848faSopenharmony_ci
1098f9f848faSopenharmony_ci		if (td->alt_next != td_alt_next) {
1099f9f848faSopenharmony_ci			/* this USB frame is complete */
1100f9f848faSopenharmony_ci			break;
1101f9f848faSopenharmony_ci		}
1102f9f848faSopenharmony_ci	}
1103f9f848faSopenharmony_ci
1104f9f848faSopenharmony_ci	/* update transfer cache */
1105f9f848faSopenharmony_ci
1106f9f848faSopenharmony_ci	xfer->td_transfer_cache = td;
1107f9f848faSopenharmony_ci
1108f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
1109f9f848faSopenharmony_ci	if (status & EHCI_QTD_STATERRS) {
1110f9f848faSopenharmony_ci		DPRINTFN(11, "error, addr=%d, endpt=0x%02x, frame=0x%02x"
1111f9f848faSopenharmony_ci		    "status=%s%s%s%s%s%s%s%s\n",
1112f9f848faSopenharmony_ci		    xfer->address, xfer->endpointno, xfer->aframes,
1113f9f848faSopenharmony_ci		    (status & EHCI_QTD_ACTIVE) ? "[ACTIVE]" : "[NOT_ACTIVE]",
1114f9f848faSopenharmony_ci		    (status & EHCI_QTD_HALTED) ? "[HALTED]" : "",
1115f9f848faSopenharmony_ci		    (status & EHCI_QTD_BUFERR) ? "[BUFERR]" : "",
1116f9f848faSopenharmony_ci		    (status & EHCI_QTD_BABBLE) ? "[BABBLE]" : "",
1117f9f848faSopenharmony_ci		    (status & EHCI_QTD_XACTERR) ? "[XACTERR]" : "",
1118f9f848faSopenharmony_ci		    (status & EHCI_QTD_MISSEDMICRO) ? "[MISSED]" : "",
1119f9f848faSopenharmony_ci		    (status & EHCI_QTD_SPLITXSTATE) ? "[SPLIT]" : "",
1120f9f848faSopenharmony_ci		    (status & EHCI_QTD_PINGSTATE) ? "[PING]" : "");
1121f9f848faSopenharmony_ci	}
1122f9f848faSopenharmony_ci#endif
1123f9f848faSopenharmony_ci	if (status & EHCI_QTD_HALTED) {
1124f9f848faSopenharmony_ci		if ((xfer->xroot->udev->parent_hs_hub != NULL) ||
1125f9f848faSopenharmony_ci		    (xfer->xroot->udev->address != 0)) {
1126f9f848faSopenharmony_ci			/* try to separate I/O errors from STALL */
1127f9f848faSopenharmony_ci			if (EHCI_QTD_GET_CERR(status) == 0)
1128f9f848faSopenharmony_ci				return (USB_ERR_IOERROR);
1129f9f848faSopenharmony_ci		}
1130f9f848faSopenharmony_ci		return (USB_ERR_STALLED);
1131f9f848faSopenharmony_ci	}
1132f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
1133f9f848faSopenharmony_ci}
1134f9f848faSopenharmony_ci
1135f9f848faSopenharmony_cistatic void
1136f9f848faSopenharmony_ciehci_non_isoc_done(struct usb_xfer *xfer)
1137f9f848faSopenharmony_ci{
1138f9f848faSopenharmony_ci	ehci_qh_t *qh;
1139f9f848faSopenharmony_ci	usb_error_t err = USB_ERR_NORMAL_COMPLETION;
1140f9f848faSopenharmony_ci
1141f9f848faSopenharmony_ci	DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n", xfer, xfer->endpoint);
1142f9f848faSopenharmony_ci
1143f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
1144f9f848faSopenharmony_ci	if (ehcidebug > 10) {
1145f9f848faSopenharmony_ci		ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
1146f9f848faSopenharmony_ci		ehci_dump_sqtds(sc, xfer->td_transfer_first);
1147f9f848faSopenharmony_ci	}
1148f9f848faSopenharmony_ci#endif
1149f9f848faSopenharmony_ci
1150f9f848faSopenharmony_ci	/* extract data toggle directly from the QH's overlay area */
1151f9f848faSopenharmony_ci	qh = (ehci_qh_t *)xfer->qh_start[xfer->flags_int.curr_dma_set];
1152f9f848faSopenharmony_ci
1153f9f848faSopenharmony_ci	usb_pc_cpu_invalidate(qh->page_cache);
1154f9f848faSopenharmony_ci
1155f9f848faSopenharmony_ci	/* reset scanner */
1156f9f848faSopenharmony_ci	xfer->td_transfer_cache = xfer->td_transfer_first;
1157f9f848faSopenharmony_ci	if (xfer->flags_int.control_xfr) {
1158f9f848faSopenharmony_ci		if (xfer->flags_int.control_hdr) {
1159f9f848faSopenharmony_ci			err = ehci_non_isoc_done_sub(xfer);
1160f9f848faSopenharmony_ci		}
1161f9f848faSopenharmony_ci		xfer->aframes = 1;
1162f9f848faSopenharmony_ci
1163f9f848faSopenharmony_ci		if (xfer->td_transfer_cache == NULL) {
1164f9f848faSopenharmony_ci			goto done;
1165f9f848faSopenharmony_ci		}
1166f9f848faSopenharmony_ci	}
1167f9f848faSopenharmony_ci	while (xfer->aframes != xfer->nframes) {
1168f9f848faSopenharmony_ci		err = ehci_non_isoc_done_sub(xfer);
1169f9f848faSopenharmony_ci		xfer->aframes++;
1170f9f848faSopenharmony_ci
1171f9f848faSopenharmony_ci		if (xfer->td_transfer_cache == NULL) {
1172f9f848faSopenharmony_ci			goto done;
1173f9f848faSopenharmony_ci		}
1174f9f848faSopenharmony_ci	}
1175f9f848faSopenharmony_ci
1176f9f848faSopenharmony_ci	if (xfer->flags_int.control_xfr &&
1177f9f848faSopenharmony_ci	    !xfer->flags_int.control_act) {
1178f9f848faSopenharmony_ci		err = ehci_non_isoc_done_sub(xfer);
1179f9f848faSopenharmony_ci	}
1180f9f848faSopenharmony_cidone:
1181f9f848faSopenharmony_ci	ehci_device_done(xfer, err);
1182f9f848faSopenharmony_ci}
1183f9f848faSopenharmony_ci
1184f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1185f9f848faSopenharmony_ci *	ehci_check_transfer
1186f9f848faSopenharmony_ci *
1187f9f848faSopenharmony_ci * Return values:
1188f9f848faSopenharmony_ci *    0: USB transfer is not finished
1189f9f848faSopenharmony_ci * Else: USB transfer is finished
1190f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1191f9f848faSopenharmony_cistatic uint8_t
1192f9f848faSopenharmony_ciehci_check_transfer(struct usb_xfer *xfer)
1193f9f848faSopenharmony_ci{
1194f9f848faSopenharmony_ci	const struct usb_pipe_methods *methods = xfer->endpoint->methods;
1195f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
1196f9f848faSopenharmony_ci
1197f9f848faSopenharmony_ci	uint32_t status;
1198f9f848faSopenharmony_ci
1199f9f848faSopenharmony_ci	DPRINTFN(13, "xfer=%p checking transfer\n", xfer);
1200f9f848faSopenharmony_ci
1201f9f848faSopenharmony_ci	if (methods == &ehci_device_isoc_fs_methods) {
1202f9f848faSopenharmony_ci		ehci_sitd_t *td;
1203f9f848faSopenharmony_ci
1204f9f848faSopenharmony_ci		/* isochronous full speed transfer */
1205f9f848faSopenharmony_ci
1206f9f848faSopenharmony_ci		td = (ehci_sitd_t *)xfer->td_transfer_last;
1207f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(td->page_cache);
1208f9f848faSopenharmony_ci		status = hc32toh(sc, td->sitd_status);
1209f9f848faSopenharmony_ci
1210f9f848faSopenharmony_ci		/* also check if first is complete */
1211f9f848faSopenharmony_ci
1212f9f848faSopenharmony_ci		td = (ehci_sitd_t *)xfer->td_transfer_first;
1213f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(td->page_cache);
1214f9f848faSopenharmony_ci		status |= hc32toh(sc, td->sitd_status);
1215f9f848faSopenharmony_ci
1216f9f848faSopenharmony_ci		if (!(status & EHCI_SITD_ACTIVE)) {
1217f9f848faSopenharmony_ci			ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION);
1218f9f848faSopenharmony_ci			goto transferred;
1219f9f848faSopenharmony_ci		}
1220f9f848faSopenharmony_ci	} else if (methods == &ehci_device_isoc_hs_methods) {
1221f9f848faSopenharmony_ci		ehci_itd_t *td;
1222f9f848faSopenharmony_ci
1223f9f848faSopenharmony_ci		/* isochronous high speed transfer */
1224f9f848faSopenharmony_ci
1225f9f848faSopenharmony_ci		/* check last transfer */
1226f9f848faSopenharmony_ci		td = (ehci_itd_t *)xfer->td_transfer_last;
1227f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(td->page_cache);
1228f9f848faSopenharmony_ci		status = td->itd_status[0];
1229f9f848faSopenharmony_ci		status |= td->itd_status[1];
1230f9f848faSopenharmony_ci		status |= td->itd_status[2];
1231f9f848faSopenharmony_ci		status |= td->itd_status[3];
1232f9f848faSopenharmony_ci		status |= td->itd_status[4];
1233f9f848faSopenharmony_ci		status |= td->itd_status[5];
1234f9f848faSopenharmony_ci		status |= td->itd_status[6];
1235f9f848faSopenharmony_ci		status |= td->itd_status[7];
1236f9f848faSopenharmony_ci
1237f9f848faSopenharmony_ci		/* also check first transfer */
1238f9f848faSopenharmony_ci		td = (ehci_itd_t *)xfer->td_transfer_first;
1239f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(td->page_cache);
1240f9f848faSopenharmony_ci		status |= td->itd_status[0];
1241f9f848faSopenharmony_ci		status |= td->itd_status[1];
1242f9f848faSopenharmony_ci		status |= td->itd_status[2];
1243f9f848faSopenharmony_ci		status |= td->itd_status[3];
1244f9f848faSopenharmony_ci		status |= td->itd_status[4];
1245f9f848faSopenharmony_ci		status |= td->itd_status[5];
1246f9f848faSopenharmony_ci		status |= td->itd_status[6];
1247f9f848faSopenharmony_ci		status |= td->itd_status[7];
1248f9f848faSopenharmony_ci
1249f9f848faSopenharmony_ci		/* if no transactions are active we continue */
1250f9f848faSopenharmony_ci		if (!(status & htohc32(sc, EHCI_ITD_ACTIVE))) {
1251f9f848faSopenharmony_ci			ehci_device_done(xfer, USB_ERR_NORMAL_COMPLETION);
1252f9f848faSopenharmony_ci			goto transferred;
1253f9f848faSopenharmony_ci		}
1254f9f848faSopenharmony_ci	} else {
1255f9f848faSopenharmony_ci		ehci_qtd_t *td;
1256f9f848faSopenharmony_ci		ehci_qh_t *qh;
1257f9f848faSopenharmony_ci
1258f9f848faSopenharmony_ci		/* non-isochronous transfer */
1259f9f848faSopenharmony_ci
1260f9f848faSopenharmony_ci		/*
1261f9f848faSopenharmony_ci		 * check whether there is an error somewhere in the middle,
1262f9f848faSopenharmony_ci		 * or whether there was a short packet (SPD and not ACTIVE)
1263f9f848faSopenharmony_ci		 */
1264f9f848faSopenharmony_ci		td = (ehci_qtd_t *)xfer->td_transfer_cache;
1265f9f848faSopenharmony_ci
1266f9f848faSopenharmony_ci		qh = (ehci_qh_t *)xfer->qh_start[xfer->flags_int.curr_dma_set];
1267f9f848faSopenharmony_ci
1268f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(qh->page_cache);
1269f9f848faSopenharmony_ci
1270f9f848faSopenharmony_ci		status = hc32toh(sc, qh->qh_qtd.qtd_status);
1271f9f848faSopenharmony_ci		if (status & EHCI_QTD_ACTIVE) {
1272f9f848faSopenharmony_ci			/* transfer is pending */
1273f9f848faSopenharmony_ci			goto done;
1274f9f848faSopenharmony_ci		}
1275f9f848faSopenharmony_ci
1276f9f848faSopenharmony_ci		while (1) {
1277f9f848faSopenharmony_ci			usb_pc_cpu_invalidate(td->page_cache);
1278f9f848faSopenharmony_ci			status = hc32toh(sc, td->qtd_status);
1279f9f848faSopenharmony_ci
1280f9f848faSopenharmony_ci			/*
1281f9f848faSopenharmony_ci			 * Check if there is an active TD which
1282f9f848faSopenharmony_ci			 * indicates that the transfer isn't done.
1283f9f848faSopenharmony_ci			 */
1284f9f848faSopenharmony_ci			if (status & EHCI_QTD_ACTIVE) {
1285f9f848faSopenharmony_ci				/* update cache */
1286f9f848faSopenharmony_ci				xfer->td_transfer_cache = td;
1287f9f848faSopenharmony_ci				goto done;
1288f9f848faSopenharmony_ci			}
1289f9f848faSopenharmony_ci			/*
1290f9f848faSopenharmony_ci			 * last transfer descriptor makes the transfer done
1291f9f848faSopenharmony_ci			 */
1292f9f848faSopenharmony_ci			if (((void *)td) == xfer->td_transfer_last) {
1293f9f848faSopenharmony_ci				break;
1294f9f848faSopenharmony_ci			}
1295f9f848faSopenharmony_ci			/*
1296f9f848faSopenharmony_ci			 * any kind of error makes the transfer done
1297f9f848faSopenharmony_ci			 */
1298f9f848faSopenharmony_ci			if (status & EHCI_QTD_HALTED) {
1299f9f848faSopenharmony_ci				break;
1300f9f848faSopenharmony_ci			}
1301f9f848faSopenharmony_ci			/*
1302f9f848faSopenharmony_ci			 * if there is no alternate next transfer, a short
1303f9f848faSopenharmony_ci			 * packet also makes the transfer done
1304f9f848faSopenharmony_ci			 */
1305f9f848faSopenharmony_ci			if (EHCI_QTD_GET_BYTES(status)) {
1306f9f848faSopenharmony_ci				if (xfer->flags_int.short_frames_ok) {
1307f9f848faSopenharmony_ci					/* follow alt next */
1308f9f848faSopenharmony_ci					if (td->alt_next) {
1309f9f848faSopenharmony_ci						td = td->alt_next;
1310f9f848faSopenharmony_ci						continue;
1311f9f848faSopenharmony_ci					}
1312f9f848faSopenharmony_ci				}
1313f9f848faSopenharmony_ci				/* transfer is done */
1314f9f848faSopenharmony_ci				break;
1315f9f848faSopenharmony_ci			}
1316f9f848faSopenharmony_ci			td = td->obj_next;
1317f9f848faSopenharmony_ci		}
1318f9f848faSopenharmony_ci		ehci_non_isoc_done(xfer);
1319f9f848faSopenharmony_ci		goto transferred;
1320f9f848faSopenharmony_ci	}
1321f9f848faSopenharmony_ci
1322f9f848faSopenharmony_cidone:
1323f9f848faSopenharmony_ci	DPRINTFN(13, "xfer=%p is still active\n", xfer);
1324f9f848faSopenharmony_ci	return (0);
1325f9f848faSopenharmony_ci
1326f9f848faSopenharmony_citransferred:
1327f9f848faSopenharmony_ci	return (1);
1328f9f848faSopenharmony_ci}
1329f9f848faSopenharmony_ci
1330f9f848faSopenharmony_cistatic void
1331f9f848faSopenharmony_ciehci_pcd_enable(ehci_softc_t *sc)
1332f9f848faSopenharmony_ci{
1333f9f848faSopenharmony_ci	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
1334f9f848faSopenharmony_ci
1335f9f848faSopenharmony_ci	sc->sc_eintrs |= EHCI_STS_PCD;
1336f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
1337f9f848faSopenharmony_ci
1338f9f848faSopenharmony_ci	/* acknowledge any PCD interrupt */
1339f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_USBSTS, EHCI_STS_PCD);
1340f9f848faSopenharmony_ci
1341f9f848faSopenharmony_ci	ehci_root_intr(sc);
1342f9f848faSopenharmony_ci}
1343f9f848faSopenharmony_ci
1344f9f848faSopenharmony_cistatic void
1345f9f848faSopenharmony_ciehci_interrupt_poll(ehci_softc_t *sc)
1346f9f848faSopenharmony_ci{
1347f9f848faSopenharmony_ci	struct usb_xfer *xfer;
1348f9f848faSopenharmony_ci
1349f9f848faSopenharmony_cirepeat:
1350f9f848faSopenharmony_ci	TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
1351f9f848faSopenharmony_ci		/*
1352f9f848faSopenharmony_ci		 * check if transfer is transferred
1353f9f848faSopenharmony_ci		 */
1354f9f848faSopenharmony_ci		if (ehci_check_transfer(xfer)) {
1355f9f848faSopenharmony_ci			/* queue has been modified */
1356f9f848faSopenharmony_ci			goto repeat;
1357f9f848faSopenharmony_ci		}
1358f9f848faSopenharmony_ci	}
1359f9f848faSopenharmony_ci}
1360f9f848faSopenharmony_ci
1361f9f848faSopenharmony_ci/*
1362f9f848faSopenharmony_ci * Some EHCI chips from VIA / ATI seem to trigger interrupts before
1363f9f848faSopenharmony_ci * writing back the qTD status, or miss signalling occasionally under
1364f9f848faSopenharmony_ci * heavy load.  If the host machine is too fast, we can miss
1365f9f848faSopenharmony_ci * transaction completion - when we scan the active list the
1366f9f848faSopenharmony_ci * transaction still seems to be active. This generally exhibits
1367f9f848faSopenharmony_ci * itself as a umass stall that never recovers.
1368f9f848faSopenharmony_ci *
1369f9f848faSopenharmony_ci * We work around this behaviour by setting up this callback after any
1370f9f848faSopenharmony_ci * softintr that completes with transactions still pending, giving us
1371f9f848faSopenharmony_ci * another chance to check for completion after the writeback has
1372f9f848faSopenharmony_ci * taken place.
1373f9f848faSopenharmony_ci */
1374f9f848faSopenharmony_cistatic void
1375f9f848faSopenharmony_ciehci_poll_timeout(void *ehci_arg)
1376f9f848faSopenharmony_ci{
1377f9f848faSopenharmony_ci	ehci_softc_t *sc = (ehci_softc_t *)ehci_arg;
1378f9f848faSopenharmony_ci
1379f9f848faSopenharmony_ci	DPRINTFN(3, "\n");
1380f9f848faSopenharmony_ci	ehci_interrupt_poll(sc);
1381f9f848faSopenharmony_ci}
1382f9f848faSopenharmony_ci
1383f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
1384f9f848faSopenharmony_ci *	ehci_interrupt - EHCI interrupt handler
1385f9f848faSopenharmony_ci *
1386f9f848faSopenharmony_ci * NOTE: Do not access "sc->sc_bus.bdev" inside the interrupt handler,
1387f9f848faSopenharmony_ci * hence the interrupt handler will be setup before "sc->sc_bus.bdev"
1388f9f848faSopenharmony_ci * is present !
1389f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
1390f9f848faSopenharmony_civoid
1391f9f848faSopenharmony_ciehci_interrupt(unsigned int irq, ehci_softc_t *sc)
1392f9f848faSopenharmony_ci{
1393f9f848faSopenharmony_ci	uint32_t status;
1394f9f848faSopenharmony_ci
1395f9f848faSopenharmony_ci	USB_BUS_LOCK(&sc->sc_bus);
1396f9f848faSopenharmony_ci	DPRINTFN(16, "real interrupt\n");
1397f9f848faSopenharmony_ci
1398f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
1399f9f848faSopenharmony_ci	if (ehcidebug > 15) {
1400f9f848faSopenharmony_ci		ehci_dump_regs(sc);
1401f9f848faSopenharmony_ci	}
1402f9f848faSopenharmony_ci#endif
1403f9f848faSopenharmony_ci
1404f9f848faSopenharmony_ci	status = EHCI_STS_INTRS(EOREAD4(sc, EHCI_USBSTS));
1405f9f848faSopenharmony_ci
1406f9f848faSopenharmony_ci	if (status == 0) {
1407f9f848faSopenharmony_ci		/* the interrupt was not for us */
1408f9f848faSopenharmony_ci		goto done;
1409f9f848faSopenharmony_ci	}
1410f9f848faSopenharmony_ci	if (!(status & sc->sc_eintrs)) {
1411f9f848faSopenharmony_ci		goto done;
1412f9f848faSopenharmony_ci	}
1413f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_USBSTS, status);	/* acknowledge */
1414f9f848faSopenharmony_ci
1415f9f848faSopenharmony_ci	status &= sc->sc_eintrs;
1416f9f848faSopenharmony_ci
1417f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
1418f9f848faSopenharmony_ci	if (status & EHCI_STS_HSE) {
1419f9f848faSopenharmony_ci		ehci_dump_regs(sc);
1420f9f848faSopenharmony_ci		ehci_dump_isoc(sc);
1421f9f848faSopenharmony_ci	}
1422f9f848faSopenharmony_ci#endif
1423f9f848faSopenharmony_ci	if (status & EHCI_STS_PCD) {
1424f9f848faSopenharmony_ci		/*
1425f9f848faSopenharmony_ci		 * Disable PCD interrupt for now, because it will be
1426f9f848faSopenharmony_ci		 * on until the port has been reset.
1427f9f848faSopenharmony_ci		 */
1428f9f848faSopenharmony_ci		sc->sc_eintrs &= ~EHCI_STS_PCD;
1429f9f848faSopenharmony_ci		EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
1430f9f848faSopenharmony_ci
1431f9f848faSopenharmony_ci		ehci_root_intr(sc);
1432f9f848faSopenharmony_ci
1433f9f848faSopenharmony_ci		/* do not allow RHSC interrupts > 1 per second */
1434f9f848faSopenharmony_ci		callout_reset(&sc->sc_tmo_pcd, hz,
1435f9f848faSopenharmony_ci		    (void *)&ehci_pcd_enable, sc);
1436f9f848faSopenharmony_ci	}
1437f9f848faSopenharmony_ci	status &= ~(EHCI_STS_INT | EHCI_STS_ERRINT | EHCI_STS_PCD | EHCI_STS_IAA);
1438f9f848faSopenharmony_ci
1439f9f848faSopenharmony_ci	if (status != 0) {
1440f9f848faSopenharmony_ci		/* block unprocessed interrupts */
1441f9f848faSopenharmony_ci		sc->sc_eintrs &= ~status;
1442f9f848faSopenharmony_ci		EOWRITE4(sc, EHCI_USBINTR, sc->sc_eintrs);
1443f9f848faSopenharmony_ci	}
1444f9f848faSopenharmony_ci	/* poll all the USB transfers */
1445f9f848faSopenharmony_ci	ehci_interrupt_poll(sc);
1446f9f848faSopenharmony_ci
1447f9f848faSopenharmony_ci	if (sc->sc_flags & EHCI_SCFLG_LOSTINTRBUG) {
1448f9f848faSopenharmony_ci		callout_reset(&sc->sc_tmo_poll, hz / 128,
1449f9f848faSopenharmony_ci		    (void *)&ehci_poll_timeout, sc);
1450f9f848faSopenharmony_ci	}
1451f9f848faSopenharmony_ci
1452f9f848faSopenharmony_cidone:
1453f9f848faSopenharmony_ci	USB_BUS_UNLOCK(&sc->sc_bus);
1454f9f848faSopenharmony_ci}
1455f9f848faSopenharmony_ci
1456f9f848faSopenharmony_ci/*
1457f9f848faSopenharmony_ci * called when a request does not complete
1458f9f848faSopenharmony_ci */
1459f9f848faSopenharmony_cistatic void
1460f9f848faSopenharmony_ciehci_timeout(void *ehci_arg)
1461f9f848faSopenharmony_ci{
1462f9f848faSopenharmony_ci	struct usb_xfer *xfer = (struct usb_xfer *)ehci_arg;
1463f9f848faSopenharmony_ci
1464f9f848faSopenharmony_ci	DPRINTF("xfer=%p\n", xfer);
1465f9f848faSopenharmony_ci
1466f9f848faSopenharmony_ci	USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
1467f9f848faSopenharmony_ci
1468f9f848faSopenharmony_ci	/* transfer is transferred */
1469f9f848faSopenharmony_ci	ehci_device_done(xfer, USB_ERR_TIMEOUT);
1470f9f848faSopenharmony_ci}
1471f9f848faSopenharmony_ci
1472f9f848faSopenharmony_cistatic void
1473f9f848faSopenharmony_ciehci_do_poll(struct usb_bus *bus)
1474f9f848faSopenharmony_ci{
1475f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(bus);
1476f9f848faSopenharmony_ci
1477f9f848faSopenharmony_ci	USB_BUS_LOCK(&sc->sc_bus);
1478f9f848faSopenharmony_ci	ehci_interrupt_poll(sc);
1479f9f848faSopenharmony_ci	USB_BUS_UNLOCK(&sc->sc_bus);
1480f9f848faSopenharmony_ci}
1481f9f848faSopenharmony_ci
1482f9f848faSopenharmony_cistatic void
1483f9f848faSopenharmony_ciehci_setup_standard_chain_sub(struct ehci_std_temp *temp)
1484f9f848faSopenharmony_ci{
1485f9f848faSopenharmony_ci	struct usb_page_search buf_res;
1486f9f848faSopenharmony_ci	ehci_qtd_t *td;
1487f9f848faSopenharmony_ci	ehci_qtd_t *td_next;
1488f9f848faSopenharmony_ci	ehci_qtd_t *td_alt_next;
1489f9f848faSopenharmony_ci	uint32_t buf_offset;
1490f9f848faSopenharmony_ci	uint32_t average;
1491f9f848faSopenharmony_ci	uint32_t len_old;
1492f9f848faSopenharmony_ci	uint32_t terminate;
1493f9f848faSopenharmony_ci	uint32_t qtd_altnext;
1494f9f848faSopenharmony_ci	uint8_t shortpkt_old;
1495f9f848faSopenharmony_ci	uint8_t precompute;
1496f9f848faSopenharmony_ci
1497f9f848faSopenharmony_ci	terminate = temp->sc->sc_terminate_self;
1498f9f848faSopenharmony_ci	qtd_altnext = temp->sc->sc_terminate_self;
1499f9f848faSopenharmony_ci	td_alt_next = NULL;
1500f9f848faSopenharmony_ci	buf_offset = 0;
1501f9f848faSopenharmony_ci	shortpkt_old = temp->shortpkt;
1502f9f848faSopenharmony_ci	len_old = temp->len;
1503f9f848faSopenharmony_ci	precompute = 1;
1504f9f848faSopenharmony_ci
1505f9f848faSopenharmony_cirestart:
1506f9f848faSopenharmony_ci
1507f9f848faSopenharmony_ci	td = temp->td;
1508f9f848faSopenharmony_ci	td_next = temp->td_next;
1509f9f848faSopenharmony_ci
1510f9f848faSopenharmony_ci	while (1) {
1511f9f848faSopenharmony_ci		if (temp->len == 0) {
1512f9f848faSopenharmony_ci			if (temp->shortpkt) {
1513f9f848faSopenharmony_ci				break;
1514f9f848faSopenharmony_ci			}
1515f9f848faSopenharmony_ci			/* send a Zero Length Packet, ZLP, last */
1516f9f848faSopenharmony_ci
1517f9f848faSopenharmony_ci			temp->shortpkt = 1;
1518f9f848faSopenharmony_ci			average = 0;
1519f9f848faSopenharmony_ci
1520f9f848faSopenharmony_ci		} else {
1521f9f848faSopenharmony_ci			average = temp->average;
1522f9f848faSopenharmony_ci
1523f9f848faSopenharmony_ci			if (temp->len < average) {
1524f9f848faSopenharmony_ci				if (temp->len % temp->max_frame_size) {
1525f9f848faSopenharmony_ci					temp->shortpkt = 1;
1526f9f848faSopenharmony_ci				}
1527f9f848faSopenharmony_ci				average = temp->len;
1528f9f848faSopenharmony_ci			}
1529f9f848faSopenharmony_ci		}
1530f9f848faSopenharmony_ci
1531f9f848faSopenharmony_ci		if (td_next == NULL) {
1532f9f848faSopenharmony_ci			panic("%s: out of EHCI transfer descriptors!", __FUNCTION__);
1533f9f848faSopenharmony_ci		}
1534f9f848faSopenharmony_ci		/* get next TD */
1535f9f848faSopenharmony_ci
1536f9f848faSopenharmony_ci		td = td_next;
1537f9f848faSopenharmony_ci		td_next = td->obj_next;
1538f9f848faSopenharmony_ci
1539f9f848faSopenharmony_ci		/* check if we are pre-computing */
1540f9f848faSopenharmony_ci
1541f9f848faSopenharmony_ci		if (precompute) {
1542f9f848faSopenharmony_ci			/* update remaining length */
1543f9f848faSopenharmony_ci
1544f9f848faSopenharmony_ci			temp->len -= average;
1545f9f848faSopenharmony_ci
1546f9f848faSopenharmony_ci			continue;
1547f9f848faSopenharmony_ci		}
1548f9f848faSopenharmony_ci		/* fill out current TD */
1549f9f848faSopenharmony_ci
1550f9f848faSopenharmony_ci		td->qtd_status =
1551f9f848faSopenharmony_ci		    temp->qtd_status |
1552f9f848faSopenharmony_ci		    htohc32(temp->sc, EHCI_QTD_IOC |
1553f9f848faSopenharmony_ci		    EHCI_QTD_SET_BYTES(average));
1554f9f848faSopenharmony_ci
1555f9f848faSopenharmony_ci		if (average == 0) {
1556f9f848faSopenharmony_ci			if (temp->auto_data_toggle == 0) {
1557f9f848faSopenharmony_ci				/* update data toggle, ZLP case */
1558f9f848faSopenharmony_ci
1559f9f848faSopenharmony_ci				temp->qtd_status ^=
1560f9f848faSopenharmony_ci				    htohc32(temp->sc, EHCI_QTD_TOGGLE_MASK);
1561f9f848faSopenharmony_ci			}
1562f9f848faSopenharmony_ci			td->len = 0;
1563f9f848faSopenharmony_ci
1564f9f848faSopenharmony_ci			/* properly reset reserved fields */
1565f9f848faSopenharmony_ci			td->qtd_buffer[0] = 0;
1566f9f848faSopenharmony_ci			td->qtd_buffer[1] = 0;
1567f9f848faSopenharmony_ci			td->qtd_buffer[2] = 0;
1568f9f848faSopenharmony_ci			td->qtd_buffer[3] = 0;
1569f9f848faSopenharmony_ci			td->qtd_buffer[4] = 0;
1570f9f848faSopenharmony_ci			td->qtd_buffer_hi[0] = 0;
1571f9f848faSopenharmony_ci			td->qtd_buffer_hi[1] = 0;
1572f9f848faSopenharmony_ci			td->qtd_buffer_hi[2] = 0;
1573f9f848faSopenharmony_ci			td->qtd_buffer_hi[3] = 0;
1574f9f848faSopenharmony_ci			td->qtd_buffer_hi[4] = 0;
1575f9f848faSopenharmony_ci		} else {
1576f9f848faSopenharmony_ci			uint8_t x;
1577f9f848faSopenharmony_ci
1578f9f848faSopenharmony_ci			if (temp->auto_data_toggle == 0) {
1579f9f848faSopenharmony_ci				/* update data toggle */
1580f9f848faSopenharmony_ci
1581f9f848faSopenharmony_ci				if (((average + temp->max_frame_size - 1) /
1582f9f848faSopenharmony_ci				    temp->max_frame_size) & 1) {
1583f9f848faSopenharmony_ci					temp->qtd_status ^=
1584f9f848faSopenharmony_ci					    htohc32(temp->sc, EHCI_QTD_TOGGLE_MASK);
1585f9f848faSopenharmony_ci				}
1586f9f848faSopenharmony_ci			}
1587f9f848faSopenharmony_ci			td->len = average;
1588f9f848faSopenharmony_ci
1589f9f848faSopenharmony_ci			/* update remaining length */
1590f9f848faSopenharmony_ci
1591f9f848faSopenharmony_ci			temp->len -= average;
1592f9f848faSopenharmony_ci
1593f9f848faSopenharmony_ci			/* fill out buffer pointers */
1594f9f848faSopenharmony_ci
1595f9f848faSopenharmony_ci			usbd_get_page(temp->pc, buf_offset, &buf_res);
1596f9f848faSopenharmony_ci			td->qtd_buffer[0] =
1597f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
1598f9f848faSopenharmony_ci			    htohc32(temp->sc, buf_res.physaddr);
1599f9f848faSopenharmony_ci#else
1600f9f848faSopenharmony_ci			    htohc32(temp->sc, (unsigned int)buf_res.buffer);
1601f9f848faSopenharmony_ci#endif
1602f9f848faSopenharmony_ci			td->qtd_buffer_hi[0] = 0;
1603f9f848faSopenharmony_ci
1604f9f848faSopenharmony_ci			x = 1;
1605f9f848faSopenharmony_ci
1606f9f848faSopenharmony_ci			while (average > EHCI_PAGE_SIZE) {
1607f9f848faSopenharmony_ci				average -= EHCI_PAGE_SIZE;
1608f9f848faSopenharmony_ci				buf_offset += EHCI_PAGE_SIZE;
1609f9f848faSopenharmony_ci				usbd_get_page(temp->pc, buf_offset, &buf_res);
1610f9f848faSopenharmony_ci				td->qtd_buffer[x] =
1611f9f848faSopenharmony_ci				    htohc32(temp->sc,
1612f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
1613f9f848faSopenharmony_ci				    buf_res.physaddr & (~0xFFF));
1614f9f848faSopenharmony_ci#else
1615f9f848faSopenharmony_ci				    (unsigned int)buf_res.buffer & (~0xFFF));
1616f9f848faSopenharmony_ci#endif
1617f9f848faSopenharmony_ci				td->qtd_buffer_hi[x] = 0;
1618f9f848faSopenharmony_ci				x++;
1619f9f848faSopenharmony_ci			}
1620f9f848faSopenharmony_ci
1621f9f848faSopenharmony_ci			/*
1622f9f848faSopenharmony_ci			 * NOTE: The "average" variable is never zero after
1623f9f848faSopenharmony_ci			 * exiting the loop above !
1624f9f848faSopenharmony_ci			 *
1625f9f848faSopenharmony_ci			 * NOTE: We have to subtract one from the offset to
1626f9f848faSopenharmony_ci			 * ensure that we are computing the physical address
1627f9f848faSopenharmony_ci			 * of a valid page !
1628f9f848faSopenharmony_ci			 */
1629f9f848faSopenharmony_ci			buf_offset += average;
1630f9f848faSopenharmony_ci			usbd_get_page(temp->pc, buf_offset - 1, &buf_res);
1631f9f848faSopenharmony_ci			td->qtd_buffer[x] =
1632f9f848faSopenharmony_ci			    htohc32(temp->sc,
1633f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
1634f9f848faSopenharmony_ci			    buf_res.physaddr & (~0xFFF));
1635f9f848faSopenharmony_ci#else
1636f9f848faSopenharmony_ci			    (unsigned int)buf_res.buffer & (~0xFFF));
1637f9f848faSopenharmony_ci#endif
1638f9f848faSopenharmony_ci			td->qtd_buffer_hi[x] = 0;
1639f9f848faSopenharmony_ci
1640f9f848faSopenharmony_ci			/* properly reset reserved fields */
1641f9f848faSopenharmony_ci			while (++x < EHCI_QTD_NBUFFERS) {
1642f9f848faSopenharmony_ci				td->qtd_buffer[x] = 0;
1643f9f848faSopenharmony_ci				td->qtd_buffer_hi[x] = 0;
1644f9f848faSopenharmony_ci			}
1645f9f848faSopenharmony_ci		}
1646f9f848faSopenharmony_ci
1647f9f848faSopenharmony_ci		if (td_next) {
1648f9f848faSopenharmony_ci
1649f9f848faSopenharmony_ci			/* link the current TD with the next one */
1650f9f848faSopenharmony_ci			td->qtd_next = td_next->qtd_self;
1651f9f848faSopenharmony_ci		}
1652f9f848faSopenharmony_ci
1653f9f848faSopenharmony_ci		td->qtd_altnext = qtd_altnext;
1654f9f848faSopenharmony_ci		td->alt_next = td_alt_next;
1655f9f848faSopenharmony_ci
1656f9f848faSopenharmony_ci		usb_pc_cpu_flush(td->page_cache);
1657f9f848faSopenharmony_ci	}
1658f9f848faSopenharmony_ci
1659f9f848faSopenharmony_ci	if (precompute) {
1660f9f848faSopenharmony_ci		precompute = 0;
1661f9f848faSopenharmony_ci
1662f9f848faSopenharmony_ci		/* setup alt next pointer, if any */
1663f9f848faSopenharmony_ci		if (temp->last_frame) {
1664f9f848faSopenharmony_ci			td_alt_next = NULL;
1665f9f848faSopenharmony_ci			qtd_altnext = terminate;
1666f9f848faSopenharmony_ci		} else {
1667f9f848faSopenharmony_ci			/* we use this field internally */
1668f9f848faSopenharmony_ci			td_alt_next = td_next;
1669f9f848faSopenharmony_ci			if (temp->setup_alt_next && td_next) {
1670f9f848faSopenharmony_ci				qtd_altnext = td_next->qtd_self;
1671f9f848faSopenharmony_ci			} else {
1672f9f848faSopenharmony_ci				qtd_altnext = terminate;
1673f9f848faSopenharmony_ci			}
1674f9f848faSopenharmony_ci		}
1675f9f848faSopenharmony_ci
1676f9f848faSopenharmony_ci		/* restore */
1677f9f848faSopenharmony_ci		temp->shortpkt = shortpkt_old;
1678f9f848faSopenharmony_ci		temp->len = len_old;
1679f9f848faSopenharmony_ci		goto restart;
1680f9f848faSopenharmony_ci	}
1681f9f848faSopenharmony_ci	temp->td = td;
1682f9f848faSopenharmony_ci	temp->td_next = td_next;
1683f9f848faSopenharmony_ci}
1684f9f848faSopenharmony_ci
1685f9f848faSopenharmony_cistatic void
1686f9f848faSopenharmony_ciehci_setup_standard_chain(struct usb_xfer *xfer, ehci_qh_t **qh_last)
1687f9f848faSopenharmony_ci{
1688f9f848faSopenharmony_ci	struct ehci_std_temp temp;
1689f9f848faSopenharmony_ci	const struct usb_pipe_methods *methods;
1690f9f848faSopenharmony_ci	ehci_qh_t *qh;
1691f9f848faSopenharmony_ci	ehci_qtd_t *td;
1692f9f848faSopenharmony_ci	uint32_t qh_endp;
1693f9f848faSopenharmony_ci	uint32_t qh_endphub;
1694f9f848faSopenharmony_ci	uint32_t x;
1695f9f848faSopenharmony_ci	uint32_t int_save;
1696f9f848faSopenharmony_ci
1697f9f848faSopenharmony_ci	DPRINTFN(9, "addr=%d endpt=%d sumlen=%d speed=%d\n",
1698f9f848faSopenharmony_ci	    xfer->address, UE_GET_ADDR(xfer->endpointno),
1699f9f848faSopenharmony_ci	    xfer->sumlen, usbd_get_speed(xfer->xroot->udev));
1700f9f848faSopenharmony_ci
1701f9f848faSopenharmony_ci	temp.average = xfer->max_hc_frame_size;
1702f9f848faSopenharmony_ci	temp.max_frame_size = xfer->max_frame_size;
1703f9f848faSopenharmony_ci	temp.sc = EHCI_BUS2SC(xfer->xroot->bus);
1704f9f848faSopenharmony_ci
1705f9f848faSopenharmony_ci	/* toggle the DMA set we are using */
1706f9f848faSopenharmony_ci	xfer->flags_int.curr_dma_set ^= 1;
1707f9f848faSopenharmony_ci
1708f9f848faSopenharmony_ci	/* get next DMA set */
1709f9f848faSopenharmony_ci	td = (ehci_qtd_t *)xfer->td_start[xfer->flags_int.curr_dma_set];
1710f9f848faSopenharmony_ci
1711f9f848faSopenharmony_ci	xfer->td_transfer_first = td;
1712f9f848faSopenharmony_ci	xfer->td_transfer_cache = td;
1713f9f848faSopenharmony_ci
1714f9f848faSopenharmony_ci	temp.td = NULL;
1715f9f848faSopenharmony_ci	temp.td_next = td;
1716f9f848faSopenharmony_ci	temp.qtd_status = 0;
1717f9f848faSopenharmony_ci	temp.last_frame = 0;
1718f9f848faSopenharmony_ci	temp.setup_alt_next = xfer->flags_int.short_frames_ok;
1719f9f848faSopenharmony_ci
1720f9f848faSopenharmony_ci	if (xfer->flags_int.control_xfr) {
1721f9f848faSopenharmony_ci		if (xfer->endpoint->toggle_next) {
1722f9f848faSopenharmony_ci			/* DATA1 is next */
1723f9f848faSopenharmony_ci			temp.qtd_status |=
1724f9f848faSopenharmony_ci			    htohc32(temp.sc, EHCI_QTD_SET_TOGGLE(1U));
1725f9f848faSopenharmony_ci		}
1726f9f848faSopenharmony_ci		temp.auto_data_toggle = 0;
1727f9f848faSopenharmony_ci	} else {
1728f9f848faSopenharmony_ci		temp.auto_data_toggle = 1;
1729f9f848faSopenharmony_ci	}
1730f9f848faSopenharmony_ci
1731f9f848faSopenharmony_ci	if ((xfer->xroot->udev->parent_hs_hub != NULL) ||
1732f9f848faSopenharmony_ci	    (xfer->xroot->udev->address != 0)) {
1733f9f848faSopenharmony_ci		/* max 3 retries */
1734f9f848faSopenharmony_ci		temp.qtd_status |=
1735f9f848faSopenharmony_ci		    htohc32(temp.sc, EHCI_QTD_SET_CERR(3));
1736f9f848faSopenharmony_ci	}
1737f9f848faSopenharmony_ci	/* check if we should prepend a setup message */
1738f9f848faSopenharmony_ci
1739f9f848faSopenharmony_ci	if (xfer->flags_int.control_xfr) {
1740f9f848faSopenharmony_ci		if (xfer->flags_int.control_hdr) {
1741f9f848faSopenharmony_ci			xfer->endpoint->toggle_next = 0;
1742f9f848faSopenharmony_ci
1743f9f848faSopenharmony_ci			temp.qtd_status &=
1744f9f848faSopenharmony_ci			    htohc32(temp.sc, EHCI_QTD_SET_CERR(3));
1745f9f848faSopenharmony_ci			temp.qtd_status |= htohc32(temp.sc,
1746f9f848faSopenharmony_ci			    EHCI_QTD_ACTIVE |
1747f9f848faSopenharmony_ci			    EHCI_QTD_SET_PID(EHCI_QTD_PID_SETUP) |
1748f9f848faSopenharmony_ci			    EHCI_QTD_SET_TOGGLE(0));
1749f9f848faSopenharmony_ci
1750f9f848faSopenharmony_ci			temp.len = xfer->frlengths[0];
1751f9f848faSopenharmony_ci			temp.pc = xfer->frbuffers + 0;
1752f9f848faSopenharmony_ci			temp.shortpkt = temp.len ? 1 : 0;
1753f9f848faSopenharmony_ci			/* check for last frame */
1754f9f848faSopenharmony_ci			if (xfer->nframes == 1) {
1755f9f848faSopenharmony_ci				/* no STATUS stage yet, SETUP is last */
1756f9f848faSopenharmony_ci				if (xfer->flags_int.control_act) {
1757f9f848faSopenharmony_ci					temp.last_frame = 1;
1758f9f848faSopenharmony_ci					temp.setup_alt_next = 0;
1759f9f848faSopenharmony_ci				}
1760f9f848faSopenharmony_ci			}
1761f9f848faSopenharmony_ci			ehci_setup_standard_chain_sub(&temp);
1762f9f848faSopenharmony_ci		}
1763f9f848faSopenharmony_ci		x = 1;
1764f9f848faSopenharmony_ci	} else {
1765f9f848faSopenharmony_ci		x = 0;
1766f9f848faSopenharmony_ci	}
1767f9f848faSopenharmony_ci
1768f9f848faSopenharmony_ci	while (x != xfer->nframes) {
1769f9f848faSopenharmony_ci		/* DATA0 / DATA1 message */
1770f9f848faSopenharmony_ci
1771f9f848faSopenharmony_ci		temp.len = xfer->frlengths[x];
1772f9f848faSopenharmony_ci		temp.pc = xfer->frbuffers + x;
1773f9f848faSopenharmony_ci
1774f9f848faSopenharmony_ci		x++;
1775f9f848faSopenharmony_ci
1776f9f848faSopenharmony_ci		if (x == xfer->nframes) {
1777f9f848faSopenharmony_ci			if (xfer->flags_int.control_xfr) {
1778f9f848faSopenharmony_ci				/* no STATUS stage yet, DATA is last */
1779f9f848faSopenharmony_ci				if (xfer->flags_int.control_act) {
1780f9f848faSopenharmony_ci					temp.last_frame = 1;
1781f9f848faSopenharmony_ci					temp.setup_alt_next = 0;
1782f9f848faSopenharmony_ci				}
1783f9f848faSopenharmony_ci			} else {
1784f9f848faSopenharmony_ci				temp.last_frame = 1;
1785f9f848faSopenharmony_ci				temp.setup_alt_next = 0;
1786f9f848faSopenharmony_ci			}
1787f9f848faSopenharmony_ci		}
1788f9f848faSopenharmony_ci		/* keep previous data toggle and error count */
1789f9f848faSopenharmony_ci
1790f9f848faSopenharmony_ci		temp.qtd_status &=
1791f9f848faSopenharmony_ci		    htohc32(temp.sc, EHCI_QTD_SET_CERR(3) |
1792f9f848faSopenharmony_ci		    EHCI_QTD_SET_TOGGLE(1U));
1793f9f848faSopenharmony_ci
1794f9f848faSopenharmony_ci		if (temp.len == 0) {
1795f9f848faSopenharmony_ci			/* make sure that we send an USB packet */
1796f9f848faSopenharmony_ci
1797f9f848faSopenharmony_ci			temp.shortpkt = 0;
1798f9f848faSopenharmony_ci
1799f9f848faSopenharmony_ci		} else {
1800f9f848faSopenharmony_ci			/* regular data transfer */
1801f9f848faSopenharmony_ci
1802f9f848faSopenharmony_ci			temp.shortpkt = (xfer->flags.force_short_xfer) ? 0 : 1;
1803f9f848faSopenharmony_ci		}
1804f9f848faSopenharmony_ci
1805f9f848faSopenharmony_ci		/* set endpoint direction */
1806f9f848faSopenharmony_ci
1807f9f848faSopenharmony_ci		temp.qtd_status |=
1808f9f848faSopenharmony_ci		    (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) ?
1809f9f848faSopenharmony_ci		    htohc32(temp.sc, EHCI_QTD_ACTIVE |
1810f9f848faSopenharmony_ci		    EHCI_QTD_SET_PID(EHCI_QTD_PID_IN)) :
1811f9f848faSopenharmony_ci		    htohc32(temp.sc, EHCI_QTD_ACTIVE |
1812f9f848faSopenharmony_ci		    EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT));
1813f9f848faSopenharmony_ci
1814f9f848faSopenharmony_ci		ehci_setup_standard_chain_sub(&temp);
1815f9f848faSopenharmony_ci	}
1816f9f848faSopenharmony_ci
1817f9f848faSopenharmony_ci	/* check if we should append a status stage */
1818f9f848faSopenharmony_ci
1819f9f848faSopenharmony_ci	if (xfer->flags_int.control_xfr &&
1820f9f848faSopenharmony_ci	    !xfer->flags_int.control_act) {
1821f9f848faSopenharmony_ci		/*
1822f9f848faSopenharmony_ci		 * Send a DATA1 message and invert the current endpoint
1823f9f848faSopenharmony_ci		 * direction.
1824f9f848faSopenharmony_ci		 */
1825f9f848faSopenharmony_ci
1826f9f848faSopenharmony_ci		temp.qtd_status &= htohc32(temp.sc, EHCI_QTD_SET_CERR(3) |
1827f9f848faSopenharmony_ci		    EHCI_QTD_SET_TOGGLE(1U));
1828f9f848faSopenharmony_ci		temp.qtd_status |=
1829f9f848faSopenharmony_ci		    (UE_GET_DIR(xfer->endpointno) == UE_DIR_OUT) ?
1830f9f848faSopenharmony_ci		    htohc32(temp.sc, EHCI_QTD_ACTIVE |
1831f9f848faSopenharmony_ci		    EHCI_QTD_SET_PID(EHCI_QTD_PID_IN) |
1832f9f848faSopenharmony_ci		    EHCI_QTD_SET_TOGGLE(1U)) :
1833f9f848faSopenharmony_ci		    htohc32(temp.sc, EHCI_QTD_ACTIVE |
1834f9f848faSopenharmony_ci		    EHCI_QTD_SET_PID(EHCI_QTD_PID_OUT) |
1835f9f848faSopenharmony_ci		    EHCI_QTD_SET_TOGGLE(1U));
1836f9f848faSopenharmony_ci
1837f9f848faSopenharmony_ci		temp.len = 0;
1838f9f848faSopenharmony_ci		temp.pc = NULL;
1839f9f848faSopenharmony_ci		temp.shortpkt = 0;
1840f9f848faSopenharmony_ci		temp.last_frame = 1;
1841f9f848faSopenharmony_ci		temp.setup_alt_next = 0;
1842f9f848faSopenharmony_ci
1843f9f848faSopenharmony_ci		ehci_setup_standard_chain_sub(&temp);
1844f9f848faSopenharmony_ci	}
1845f9f848faSopenharmony_ci	td = temp.td;
1846f9f848faSopenharmony_ci	if (td == NULL)
1847f9f848faSopenharmony_ci		return;
1848f9f848faSopenharmony_ci
1849f9f848faSopenharmony_ci	/* the last TD terminates the transfer: */
1850f9f848faSopenharmony_ci	td->qtd_next = htohc32(temp.sc, EHCI_LINK_TERMINATE);
1851f9f848faSopenharmony_ci	td->qtd_altnext = htohc32(temp.sc, EHCI_LINK_TERMINATE);
1852f9f848faSopenharmony_ci
1853f9f848faSopenharmony_ci	usb_pc_cpu_flush(td->page_cache);
1854f9f848faSopenharmony_ci
1855f9f848faSopenharmony_ci	/* must have at least one frame! */
1856f9f848faSopenharmony_ci
1857f9f848faSopenharmony_ci	xfer->td_transfer_last = td;
1858f9f848faSopenharmony_ci
1859f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
1860f9f848faSopenharmony_ci	if (ehcidebug > 8) {
1861f9f848faSopenharmony_ci		DPRINTF("nexttog=%d; data before transfer:\n",
1862f9f848faSopenharmony_ci		    xfer->endpoint->toggle_next);
1863f9f848faSopenharmony_ci		ehci_dump_sqtds(temp.sc,
1864f9f848faSopenharmony_ci		    xfer->td_transfer_first);
1865f9f848faSopenharmony_ci	}
1866f9f848faSopenharmony_ci#endif
1867f9f848faSopenharmony_ci
1868f9f848faSopenharmony_ci	methods = xfer->endpoint->methods;
1869f9f848faSopenharmony_ci
1870f9f848faSopenharmony_ci	qh = (ehci_qh_t *)xfer->qh_start[xfer->flags_int.curr_dma_set];
1871f9f848faSopenharmony_ci
1872f9f848faSopenharmony_ci	/* the "qh_link" field is filled when the QH is added */
1873f9f848faSopenharmony_ci
1874f9f848faSopenharmony_ci	qh_endp =
1875f9f848faSopenharmony_ci	    (EHCI_QH_SET_ADDR(xfer->address) |
1876f9f848faSopenharmony_ci	    EHCI_QH_SET_ENDPT(UE_GET_ADDR(xfer->endpointno)) |
1877f9f848faSopenharmony_ci	    EHCI_QH_SET_MPL(xfer->max_packet_size));
1878f9f848faSopenharmony_ci
1879f9f848faSopenharmony_ci	if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_HIGH) {
1880f9f848faSopenharmony_ci		qh_endp |= EHCI_QH_SET_EPS(EHCI_QH_SPEED_HIGH);
1881f9f848faSopenharmony_ci		if (methods != &ehci_device_intr_methods)
1882f9f848faSopenharmony_ci			qh_endp |= EHCI_QH_SET_NRL(8U);
1883f9f848faSopenharmony_ci	} else {
1884f9f848faSopenharmony_ci		if (usbd_get_speed(xfer->xroot->udev) == USB_SPEED_FULL) {
1885f9f848faSopenharmony_ci			qh_endp |= EHCI_QH_SET_EPS(EHCI_QH_SPEED_FULL);
1886f9f848faSopenharmony_ci		} else {
1887f9f848faSopenharmony_ci			qh_endp |= EHCI_QH_SET_EPS(EHCI_QH_SPEED_LOW);
1888f9f848faSopenharmony_ci		}
1889f9f848faSopenharmony_ci
1890f9f848faSopenharmony_ci		if (methods == &ehci_device_ctrl_methods) {
1891f9f848faSopenharmony_ci			qh_endp |= EHCI_QH_CTL;
1892f9f848faSopenharmony_ci		}
1893f9f848faSopenharmony_ci		if (methods != &ehci_device_intr_methods) {
1894f9f848faSopenharmony_ci			/* Only try one time per microframe! */
1895f9f848faSopenharmony_ci			qh_endp |= EHCI_QH_SET_NRL(1);
1896f9f848faSopenharmony_ci		}
1897f9f848faSopenharmony_ci	}
1898f9f848faSopenharmony_ci
1899f9f848faSopenharmony_ci	if (temp.auto_data_toggle == 0) {
1900f9f848faSopenharmony_ci		/* software computes the data toggle */
1901f9f848faSopenharmony_ci		qh_endp |= EHCI_QH_DTC;
1902f9f848faSopenharmony_ci	}
1903f9f848faSopenharmony_ci
1904f9f848faSopenharmony_ci	qh->qh_endp = htohc32(temp.sc, qh_endp);
1905f9f848faSopenharmony_ci
1906f9f848faSopenharmony_ci	qh_endphub =
1907f9f848faSopenharmony_ci	    (EHCI_QH_SET_MULT(xfer->max_packet_count & 3) |
1908f9f848faSopenharmony_ci	    EHCI_QH_SET_CMASK(xfer->endpoint->usb_cmask) |
1909f9f848faSopenharmony_ci	    EHCI_QH_SET_SMASK(xfer->endpoint->usb_smask) |
1910f9f848faSopenharmony_ci	    EHCI_QH_SET_HUBA(xfer->xroot->udev->hs_hub_addr) |
1911f9f848faSopenharmony_ci	    EHCI_QH_SET_PORT(xfer->xroot->udev->hs_port_no));
1912f9f848faSopenharmony_ci
1913f9f848faSopenharmony_ci	qh->qh_endphub = htohc32(temp.sc, qh_endphub);
1914f9f848faSopenharmony_ci	qh->qh_curqtd = 0;
1915f9f848faSopenharmony_ci
1916f9f848faSopenharmony_ci	/* fill the overlay qTD */
1917f9f848faSopenharmony_ci
1918f9f848faSopenharmony_ci	if (temp.auto_data_toggle && xfer->endpoint->toggle_next) {
1919f9f848faSopenharmony_ci		/* DATA1 is next */
1920f9f848faSopenharmony_ci		qh->qh_qtd.qtd_status = htohc32(temp.sc, EHCI_QTD_SET_TOGGLE(1U));
1921f9f848faSopenharmony_ci	} else {
1922f9f848faSopenharmony_ci		qh->qh_qtd.qtd_status = 0;
1923f9f848faSopenharmony_ci	}
1924f9f848faSopenharmony_ci
1925f9f848faSopenharmony_ci	td = (ehci_qtd_t *)xfer->td_transfer_first;
1926f9f848faSopenharmony_ci
1927f9f848faSopenharmony_ci	qh->qh_qtd.qtd_next = td->qtd_self;
1928f9f848faSopenharmony_ci	qh->qh_qtd.qtd_altnext =
1929f9f848faSopenharmony_ci	    htohc32(temp.sc, EHCI_LINK_TERMINATE);
1930f9f848faSopenharmony_ci
1931f9f848faSopenharmony_ci	/* properly reset reserved fields */
1932f9f848faSopenharmony_ci	qh->qh_qtd.qtd_buffer[0] = 0;
1933f9f848faSopenharmony_ci	qh->qh_qtd.qtd_buffer[1] = 0;
1934f9f848faSopenharmony_ci	qh->qh_qtd.qtd_buffer[2] = 0;
1935f9f848faSopenharmony_ci	qh->qh_qtd.qtd_buffer[3] = 0;
1936f9f848faSopenharmony_ci	qh->qh_qtd.qtd_buffer[4] = 0;
1937f9f848faSopenharmony_ci	qh->qh_qtd.qtd_buffer_hi[0] = 0;
1938f9f848faSopenharmony_ci	qh->qh_qtd.qtd_buffer_hi[1] = 0;
1939f9f848faSopenharmony_ci	qh->qh_qtd.qtd_buffer_hi[2] = 0;
1940f9f848faSopenharmony_ci	qh->qh_qtd.qtd_buffer_hi[3] = 0;
1941f9f848faSopenharmony_ci	qh->qh_qtd.qtd_buffer_hi[4] = 0;
1942f9f848faSopenharmony_ci
1943f9f848faSopenharmony_ci	usb_pc_cpu_flush(qh->page_cache);
1944f9f848faSopenharmony_ci
1945f9f848faSopenharmony_ci	if (xfer->xroot->udev->flags.self_suspended == 0) {
1946f9f848faSopenharmony_ci		spin_lock_irqsave(&g_usb_ehci_qh_spinlock, int_save);
1947f9f848faSopenharmony_ci		EHCI_APPEND_QH(qh, *qh_last);
1948f9f848faSopenharmony_ci		spin_unlock_irqrestore(&g_usb_ehci_qh_spinlock, int_save);
1949f9f848faSopenharmony_ci	}
1950f9f848faSopenharmony_ci}
1951f9f848faSopenharmony_ci
1952f9f848faSopenharmony_cistatic void
1953f9f848faSopenharmony_ciehci_root_intr(ehci_softc_t *sc)
1954f9f848faSopenharmony_ci{
1955f9f848faSopenharmony_ci	uint16_t i;
1956f9f848faSopenharmony_ci	uint16_t m;
1957f9f848faSopenharmony_ci	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
1958f9f848faSopenharmony_ci
1959f9f848faSopenharmony_ci	/* clear any old interrupt data */
1960f9f848faSopenharmony_ci	(void)memset_s(sc->sc_hub_idata, sizeof(sc->sc_hub_idata), 0, sizeof(sc->sc_hub_idata));
1961f9f848faSopenharmony_ci
1962f9f848faSopenharmony_ci	/* set bits */
1963f9f848faSopenharmony_ci	m = (sc->sc_noport + 1);
1964f9f848faSopenharmony_ci	if (m > (8 * sizeof(sc->sc_hub_idata))) {
1965f9f848faSopenharmony_ci		m = (8 * sizeof(sc->sc_hub_idata));
1966f9f848faSopenharmony_ci	}
1967f9f848faSopenharmony_ci	for (i = 1; i < m; i++) {
1968f9f848faSopenharmony_ci		/* pick out CHANGE bits from the status register */
1969f9f848faSopenharmony_ci		if (EOREAD4(sc, EHCI_PORTSC(i)) & EHCI_PS_CLEAR) {
1970f9f848faSopenharmony_ci			sc->sc_hub_idata[i / 8] |= 1 << (i % 8);
1971f9f848faSopenharmony_ci			DPRINTF("port %d changed\n", i);
1972f9f848faSopenharmony_ci		}
1973f9f848faSopenharmony_ci	}
1974f9f848faSopenharmony_ci	uhub_root_intr(&sc->sc_bus, sc->sc_hub_idata,
1975f9f848faSopenharmony_ci	    sizeof(sc->sc_hub_idata));
1976f9f848faSopenharmony_ci}
1977f9f848faSopenharmony_ci
1978f9f848faSopenharmony_cistatic void
1979f9f848faSopenharmony_ciehci_isoc_fs_done(ehci_softc_t *sc, struct usb_xfer *xfer)
1980f9f848faSopenharmony_ci{
1981f9f848faSopenharmony_ci	uint32_t nframes = xfer->nframes;
1982f9f848faSopenharmony_ci	uint32_t status;
1983f9f848faSopenharmony_ci	uint32_t *plen = xfer->frlengths;
1984f9f848faSopenharmony_ci	uint16_t len = 0;
1985f9f848faSopenharmony_ci	ehci_sitd_t *td = (ehci_sitd_t *)xfer->td_transfer_first;
1986f9f848faSopenharmony_ci	ehci_sitd_t **pp_last = &sc->sc_isoc_fs_p_last[xfer->qh_pos];
1987f9f848faSopenharmony_ci
1988f9f848faSopenharmony_ci	DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n",
1989f9f848faSopenharmony_ci	    xfer, xfer->endpoint);
1990f9f848faSopenharmony_ci
1991f9f848faSopenharmony_ci	while (nframes--) {
1992f9f848faSopenharmony_ci		if (td == NULL) {
1993f9f848faSopenharmony_ci			panic("%s:%d: out of TD's\n",
1994f9f848faSopenharmony_ci			    __FUNCTION__, __LINE__);
1995f9f848faSopenharmony_ci		}
1996f9f848faSopenharmony_ci		if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) {
1997f9f848faSopenharmony_ci			pp_last = &sc->sc_isoc_fs_p_last[0];
1998f9f848faSopenharmony_ci		}
1999f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
2000f9f848faSopenharmony_ci		if (ehcidebug > 15) {
2001f9f848faSopenharmony_ci			DPRINTF("isoc FS-TD\n");
2002f9f848faSopenharmony_ci			ehci_dump_sitd(sc, td);
2003f9f848faSopenharmony_ci		}
2004f9f848faSopenharmony_ci#endif
2005f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(td->page_cache);
2006f9f848faSopenharmony_ci		status = hc32toh(sc, td->sitd_status);
2007f9f848faSopenharmony_ci
2008f9f848faSopenharmony_ci		len = EHCI_SITD_GET_LEN(status);
2009f9f848faSopenharmony_ci
2010f9f848faSopenharmony_ci		DPRINTFN(2, "status=0x%08x, rem=%u\n", status, len);
2011f9f848faSopenharmony_ci
2012f9f848faSopenharmony_ci		if (*plen >= len) {
2013f9f848faSopenharmony_ci			len = *plen - len;
2014f9f848faSopenharmony_ci		} else {
2015f9f848faSopenharmony_ci			len = 0;
2016f9f848faSopenharmony_ci		}
2017f9f848faSopenharmony_ci
2018f9f848faSopenharmony_ci		*plen = len;
2019f9f848faSopenharmony_ci
2020f9f848faSopenharmony_ci		/* remove FS-TD from schedule */
2021f9f848faSopenharmony_ci		EHCI_REMOVE_FS_TD(td, *pp_last);
2022f9f848faSopenharmony_ci
2023f9f848faSopenharmony_ci		pp_last++;
2024f9f848faSopenharmony_ci		plen++;
2025f9f848faSopenharmony_ci		td = td->obj_next;
2026f9f848faSopenharmony_ci	}
2027f9f848faSopenharmony_ci
2028f9f848faSopenharmony_ci	xfer->aframes = xfer->nframes;
2029f9f848faSopenharmony_ci}
2030f9f848faSopenharmony_ci
2031f9f848faSopenharmony_cistatic void
2032f9f848faSopenharmony_ciehci_isoc_hs_done(ehci_softc_t *sc, struct usb_xfer *xfer)
2033f9f848faSopenharmony_ci{
2034f9f848faSopenharmony_ci	uint32_t nframes = xfer->nframes;
2035f9f848faSopenharmony_ci	uint32_t status;
2036f9f848faSopenharmony_ci	uint32_t *plen = xfer->frlengths;
2037f9f848faSopenharmony_ci	uint16_t len = 0;
2038f9f848faSopenharmony_ci	uint8_t td_no = 0;
2039f9f848faSopenharmony_ci	ehci_itd_t *td = (ehci_itd_t *)xfer->td_transfer_first;
2040f9f848faSopenharmony_ci	ehci_itd_t **pp_last = &sc->sc_isoc_hs_p_last[xfer->qh_pos];
2041f9f848faSopenharmony_ci
2042f9f848faSopenharmony_ci	DPRINTFN(13, "xfer=%p endpoint=%p transfer done\n",
2043f9f848faSopenharmony_ci	    xfer, xfer->endpoint);
2044f9f848faSopenharmony_ci
2045f9f848faSopenharmony_ci	while (nframes) {
2046f9f848faSopenharmony_ci		if (td == NULL) {
2047f9f848faSopenharmony_ci			panic("%s:%d: out of TD's\n",
2048f9f848faSopenharmony_ci			    __FUNCTION__, __LINE__);
2049f9f848faSopenharmony_ci		}
2050f9f848faSopenharmony_ci		if (pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) {
2051f9f848faSopenharmony_ci			pp_last = &sc->sc_isoc_hs_p_last[0];
2052f9f848faSopenharmony_ci		}
2053f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
2054f9f848faSopenharmony_ci		if (ehcidebug > 15) {
2055f9f848faSopenharmony_ci			DPRINTF("isoc HS-TD\n");
2056f9f848faSopenharmony_ci			ehci_dump_itd(sc, td);
2057f9f848faSopenharmony_ci		}
2058f9f848faSopenharmony_ci#endif
2059f9f848faSopenharmony_ci
2060f9f848faSopenharmony_ci		usb_pc_cpu_invalidate(td->page_cache);
2061f9f848faSopenharmony_ci		status = hc32toh(sc, td->itd_status[td_no]);
2062f9f848faSopenharmony_ci
2063f9f848faSopenharmony_ci		len = EHCI_ITD_GET_LEN(status);
2064f9f848faSopenharmony_ci
2065f9f848faSopenharmony_ci		DPRINTFN(2, "status=0x%08x, len=%u\n", status, len);
2066f9f848faSopenharmony_ci
2067f9f848faSopenharmony_ci		if (xfer->endpoint->usb_smask & (1 << td_no)) {
2068f9f848faSopenharmony_ci			if (*plen >= len) {
2069f9f848faSopenharmony_ci				/*
2070f9f848faSopenharmony_ci				 * The length is valid. NOTE: The
2071f9f848faSopenharmony_ci				 * complete length is written back
2072f9f848faSopenharmony_ci				 * into the status field, and not the
2073f9f848faSopenharmony_ci				 * remainder like with other transfer
2074f9f848faSopenharmony_ci				 * descriptor types.
2075f9f848faSopenharmony_ci				 */
2076f9f848faSopenharmony_ci			} else {
2077f9f848faSopenharmony_ci				/* Invalid length - truncate */
2078f9f848faSopenharmony_ci				len = 0;
2079f9f848faSopenharmony_ci			}
2080f9f848faSopenharmony_ci
2081f9f848faSopenharmony_ci			*plen = len;
2082f9f848faSopenharmony_ci			plen++;
2083f9f848faSopenharmony_ci			nframes--;
2084f9f848faSopenharmony_ci		}
2085f9f848faSopenharmony_ci
2086f9f848faSopenharmony_ci		td_no++;
2087f9f848faSopenharmony_ci
2088f9f848faSopenharmony_ci		if ((td_no == 8) || (nframes == 0)) {
2089f9f848faSopenharmony_ci			/* remove HS-TD from schedule */
2090f9f848faSopenharmony_ci			EHCI_REMOVE_HS_TD(td, *pp_last);
2091f9f848faSopenharmony_ci			pp_last++;
2092f9f848faSopenharmony_ci
2093f9f848faSopenharmony_ci			td_no = 0;
2094f9f848faSopenharmony_ci			td = td->obj_next;
2095f9f848faSopenharmony_ci		}
2096f9f848faSopenharmony_ci	}
2097f9f848faSopenharmony_ci	xfer->aframes = xfer->nframes;
2098f9f848faSopenharmony_ci}
2099f9f848faSopenharmony_ci
2100f9f848faSopenharmony_ci/* NOTE: "done" can be run two times in a row,
2101f9f848faSopenharmony_ci * from close and from interrupt
2102f9f848faSopenharmony_ci */
2103f9f848faSopenharmony_cistatic void
2104f9f848faSopenharmony_ciehci_device_done(struct usb_xfer *xfer, usb_error_t error)
2105f9f848faSopenharmony_ci{
2106f9f848faSopenharmony_ci	uintptr_t interrupt_ret;
2107f9f848faSopenharmony_ci	const struct usb_pipe_methods *methods = xfer->endpoint->methods;
2108f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
2109f9f848faSopenharmony_ci
2110f9f848faSopenharmony_ci	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
2111f9f848faSopenharmony_ci
2112f9f848faSopenharmony_ci	DPRINTFN(2, "xfer=%p, endpoint=%p, error=%d\n",
2113f9f848faSopenharmony_ci	    xfer, xfer->endpoint, error);
2114f9f848faSopenharmony_ci
2115f9f848faSopenharmony_ci	if ((methods == &ehci_device_bulk_methods) ||
2116f9f848faSopenharmony_ci	    (methods == &ehci_device_ctrl_methods)) {
2117f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
2118f9f848faSopenharmony_ci		if (ehcidebug > 8) {
2119f9f848faSopenharmony_ci			DPRINTF("nexttog=%d; data after transfer:\n",
2120f9f848faSopenharmony_ci			    xfer->endpoint->toggle_next);
2121f9f848faSopenharmony_ci			ehci_dump_sqtds(sc,
2122f9f848faSopenharmony_ci			    xfer->td_transfer_first);
2123f9f848faSopenharmony_ci		}
2124f9f848faSopenharmony_ci#endif
2125f9f848faSopenharmony_ci
2126f9f848faSopenharmony_ci		spin_lock_irqsave(&g_usb_ehci_qh_spinlock, interrupt_ret);
2127f9f848faSopenharmony_ci		EHCI_REMOVE_QH((ehci_qh_t *)xfer->qh_start[xfer->flags_int.curr_dma_set],
2128f9f848faSopenharmony_ci		    sc->sc_async_p_last);
2129f9f848faSopenharmony_ci		spin_unlock_irqrestore(&g_usb_ehci_qh_spinlock, interrupt_ret);
2130f9f848faSopenharmony_ci	}
2131f9f848faSopenharmony_ci	if (methods == &ehci_device_intr_methods) {
2132f9f848faSopenharmony_ci		spin_lock_irqsave(&g_usb_ehci_qh_spinlock, interrupt_ret);
2133f9f848faSopenharmony_ci		EHCI_REMOVE_QH((ehci_qh_t *)xfer->qh_start[xfer->flags_int.curr_dma_set],
2134f9f848faSopenharmony_ci		    sc->sc_intr_p_last[xfer->qh_pos]);
2135f9f848faSopenharmony_ci		spin_unlock_irqrestore(&g_usb_ehci_qh_spinlock, interrupt_ret);
2136f9f848faSopenharmony_ci	}
2137f9f848faSopenharmony_ci	/*
2138f9f848faSopenharmony_ci	 * Only finish isochronous transfers once which will update
2139f9f848faSopenharmony_ci	 * "xfer->frlengths".
2140f9f848faSopenharmony_ci	 */
2141f9f848faSopenharmony_ci	if (xfer->td_transfer_first &&
2142f9f848faSopenharmony_ci	    xfer->td_transfer_last) {
2143f9f848faSopenharmony_ci		if (methods == &ehci_device_isoc_fs_methods) {
2144f9f848faSopenharmony_ci			ehci_isoc_fs_done(sc, xfer);
2145f9f848faSopenharmony_ci		}
2146f9f848faSopenharmony_ci		if (methods == &ehci_device_isoc_hs_methods) {
2147f9f848faSopenharmony_ci			ehci_isoc_hs_done(sc, xfer);
2148f9f848faSopenharmony_ci		}
2149f9f848faSopenharmony_ci		xfer->td_transfer_first = NULL;
2150f9f848faSopenharmony_ci		xfer->td_transfer_last = NULL;
2151f9f848faSopenharmony_ci	}
2152f9f848faSopenharmony_ci	/* dequeue transfer and start next transfer */
2153f9f848faSopenharmony_ci	usbd_transfer_done(xfer, error);
2154f9f848faSopenharmony_ci}
2155f9f848faSopenharmony_ci
2156f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
2157f9f848faSopenharmony_ci * ehci bulk support
2158f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
2159f9f848faSopenharmony_cistatic void
2160f9f848faSopenharmony_ciehci_device_bulk_open(struct usb_xfer *xfer)
2161f9f848faSopenharmony_ci{
2162f9f848faSopenharmony_ci	return;
2163f9f848faSopenharmony_ci}
2164f9f848faSopenharmony_ci
2165f9f848faSopenharmony_cistatic void
2166f9f848faSopenharmony_ciehci_device_bulk_close(struct usb_xfer *xfer)
2167f9f848faSopenharmony_ci{
2168f9f848faSopenharmony_ci	ehci_device_done(xfer, USB_ERR_CANCELLED);
2169f9f848faSopenharmony_ci}
2170f9f848faSopenharmony_ci
2171f9f848faSopenharmony_cistatic void
2172f9f848faSopenharmony_ciehci_device_bulk_enter(struct usb_xfer *xfer)
2173f9f848faSopenharmony_ci{
2174f9f848faSopenharmony_ci	return;
2175f9f848faSopenharmony_ci}
2176f9f848faSopenharmony_ci
2177f9f848faSopenharmony_cistatic void
2178f9f848faSopenharmony_ciehci_doorbell_async(struct ehci_softc *sc)
2179f9f848faSopenharmony_ci{
2180f9f848faSopenharmony_ci	uint32_t temp;
2181f9f848faSopenharmony_ci
2182f9f848faSopenharmony_ci	/*
2183f9f848faSopenharmony_ci	 * XXX Performance quirk: Some Host Controllers have a too low
2184f9f848faSopenharmony_ci	 * interrupt rate. Issue an IAAD to stimulate the Host
2185f9f848faSopenharmony_ci	 * Controller after queueing the BULK transfer.
2186f9f848faSopenharmony_ci	 *
2187f9f848faSopenharmony_ci	 * XXX Force the host controller to refresh any QH caches.
2188f9f848faSopenharmony_ci	 */
2189f9f848faSopenharmony_ci	temp = EOREAD4(sc, EHCI_USBCMD);
2190f9f848faSopenharmony_ci	if (!(temp & EHCI_CMD_IAAD))
2191f9f848faSopenharmony_ci		EOWRITE4(sc, EHCI_USBCMD, temp | EHCI_CMD_IAAD);
2192f9f848faSopenharmony_ci}
2193f9f848faSopenharmony_ci
2194f9f848faSopenharmony_cistatic void
2195f9f848faSopenharmony_ciehci_device_bulk_start(struct usb_xfer *xfer)
2196f9f848faSopenharmony_ci{
2197f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
2198f9f848faSopenharmony_ci
2199f9f848faSopenharmony_ci	/* setup TD's and QH */
2200f9f848faSopenharmony_ci	ehci_setup_standard_chain(xfer, &sc->sc_async_p_last);
2201f9f848faSopenharmony_ci
2202f9f848faSopenharmony_ci	/* put transfer on interrupt queue */
2203f9f848faSopenharmony_ci	ehci_transfer_intr_enqueue(xfer);
2204f9f848faSopenharmony_ci
2205f9f848faSopenharmony_ci	/*
2206f9f848faSopenharmony_ci	 * XXX Certain nVidia chipsets choke when using the IAAD
2207f9f848faSopenharmony_ci	 * feature too frequently.
2208f9f848faSopenharmony_ci	 */
2209f9f848faSopenharmony_ci	if (sc->sc_flags & EHCI_SCFLG_IAADBUG)
2210f9f848faSopenharmony_ci		return;
2211f9f848faSopenharmony_ci
2212f9f848faSopenharmony_ci	ehci_doorbell_async(sc);
2213f9f848faSopenharmony_ci}
2214f9f848faSopenharmony_ci
2215f9f848faSopenharmony_ciconst struct usb_pipe_methods ehci_device_bulk_methods =
2216f9f848faSopenharmony_ci{
2217f9f848faSopenharmony_ci	.open = ehci_device_bulk_open,
2218f9f848faSopenharmony_ci	.close = ehci_device_bulk_close,
2219f9f848faSopenharmony_ci	.enter = ehci_device_bulk_enter,
2220f9f848faSopenharmony_ci	.start = ehci_device_bulk_start,
2221f9f848faSopenharmony_ci};
2222f9f848faSopenharmony_ci
2223f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
2224f9f848faSopenharmony_ci * ehci control support
2225f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
2226f9f848faSopenharmony_cistatic void
2227f9f848faSopenharmony_ciehci_device_ctrl_open(struct usb_xfer *xfer)
2228f9f848faSopenharmony_ci{
2229f9f848faSopenharmony_ci	return;
2230f9f848faSopenharmony_ci}
2231f9f848faSopenharmony_ci
2232f9f848faSopenharmony_cistatic void
2233f9f848faSopenharmony_ciehci_device_ctrl_close(struct usb_xfer *xfer)
2234f9f848faSopenharmony_ci{
2235f9f848faSopenharmony_ci	ehci_device_done(xfer, USB_ERR_CANCELLED);
2236f9f848faSopenharmony_ci}
2237f9f848faSopenharmony_ci
2238f9f848faSopenharmony_cistatic void
2239f9f848faSopenharmony_ciehci_device_ctrl_enter(struct usb_xfer *xfer)
2240f9f848faSopenharmony_ci{
2241f9f848faSopenharmony_ci	return;
2242f9f848faSopenharmony_ci}
2243f9f848faSopenharmony_ci
2244f9f848faSopenharmony_cistatic void
2245f9f848faSopenharmony_ciehci_device_ctrl_start(struct usb_xfer *xfer)
2246f9f848faSopenharmony_ci{
2247f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
2248f9f848faSopenharmony_ci
2249f9f848faSopenharmony_ci	/* setup TD's and QH */
2250f9f848faSopenharmony_ci	ehci_setup_standard_chain(xfer, &sc->sc_async_p_last);
2251f9f848faSopenharmony_ci
2252f9f848faSopenharmony_ci	/* put transfer on interrupt queue */
2253f9f848faSopenharmony_ci	ehci_transfer_intr_enqueue(xfer);
2254f9f848faSopenharmony_ci}
2255f9f848faSopenharmony_ci
2256f9f848faSopenharmony_ciconst struct usb_pipe_methods ehci_device_ctrl_methods =
2257f9f848faSopenharmony_ci{
2258f9f848faSopenharmony_ci	.open = ehci_device_ctrl_open,
2259f9f848faSopenharmony_ci	.close = ehci_device_ctrl_close,
2260f9f848faSopenharmony_ci	.enter = ehci_device_ctrl_enter,
2261f9f848faSopenharmony_ci	.start = ehci_device_ctrl_start,
2262f9f848faSopenharmony_ci};
2263f9f848faSopenharmony_ci
2264f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
2265f9f848faSopenharmony_ci * ehci interrupt support
2266f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
2267f9f848faSopenharmony_cistatic void
2268f9f848faSopenharmony_ciehci_device_intr_open(struct usb_xfer *xfer)
2269f9f848faSopenharmony_ci{
2270f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
2271f9f848faSopenharmony_ci	uint16_t best;
2272f9f848faSopenharmony_ci	uint16_t bit;
2273f9f848faSopenharmony_ci	uint16_t x;
2274f9f848faSopenharmony_ci
2275f9f848faSopenharmony_ci	usb_hs_bandwidth_alloc(xfer);
2276f9f848faSopenharmony_ci
2277f9f848faSopenharmony_ci	/*
2278f9f848faSopenharmony_ci	 * Find the best QH position corresponding to the given interval:
2279f9f848faSopenharmony_ci	 */
2280f9f848faSopenharmony_ci
2281f9f848faSopenharmony_ci	best = 0;
2282f9f848faSopenharmony_ci	bit = EHCI_VIRTUAL_FRAMELIST_COUNT / 2;
2283f9f848faSopenharmony_ci	while (bit) {
2284f9f848faSopenharmony_ci		if (xfer->interval >= bit) {
2285f9f848faSopenharmony_ci			x = bit;
2286f9f848faSopenharmony_ci			best = bit;
2287f9f848faSopenharmony_ci			while (x & bit) {
2288f9f848faSopenharmony_ci				if (sc->sc_intr_stat[x] <
2289f9f848faSopenharmony_ci				    sc->sc_intr_stat[best]) {
2290f9f848faSopenharmony_ci					best = x;
2291f9f848faSopenharmony_ci				}
2292f9f848faSopenharmony_ci				x++;
2293f9f848faSopenharmony_ci			}
2294f9f848faSopenharmony_ci			break;
2295f9f848faSopenharmony_ci		}
2296f9f848faSopenharmony_ci		bit >>= 1;
2297f9f848faSopenharmony_ci	}
2298f9f848faSopenharmony_ci
2299f9f848faSopenharmony_ci	sc->sc_intr_stat[best]++;
2300f9f848faSopenharmony_ci	xfer->qh_pos = best;
2301f9f848faSopenharmony_ci
2302f9f848faSopenharmony_ci	DPRINTFN(3, "best=%d interval=%d\n",
2303f9f848faSopenharmony_ci	    best, xfer->interval);
2304f9f848faSopenharmony_ci}
2305f9f848faSopenharmony_ci
2306f9f848faSopenharmony_cistatic void
2307f9f848faSopenharmony_ciehci_device_intr_close(struct usb_xfer *xfer)
2308f9f848faSopenharmony_ci{
2309f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
2310f9f848faSopenharmony_ci
2311f9f848faSopenharmony_ci	sc->sc_intr_stat[xfer->qh_pos]--;
2312f9f848faSopenharmony_ci
2313f9f848faSopenharmony_ci	ehci_device_done(xfer, USB_ERR_CANCELLED);
2314f9f848faSopenharmony_ci
2315f9f848faSopenharmony_ci	/* bandwidth must be freed after device done */
2316f9f848faSopenharmony_ci	usb_hs_bandwidth_free(xfer);
2317f9f848faSopenharmony_ci}
2318f9f848faSopenharmony_ci
2319f9f848faSopenharmony_cistatic void
2320f9f848faSopenharmony_ciehci_device_intr_enter(struct usb_xfer *xfer)
2321f9f848faSopenharmony_ci{
2322f9f848faSopenharmony_ci	return;
2323f9f848faSopenharmony_ci}
2324f9f848faSopenharmony_ci
2325f9f848faSopenharmony_cistatic void
2326f9f848faSopenharmony_ciehci_device_intr_start(struct usb_xfer *xfer)
2327f9f848faSopenharmony_ci{
2328f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
2329f9f848faSopenharmony_ci
2330f9f848faSopenharmony_ci	/* setup TD's and QH */
2331f9f848faSopenharmony_ci	ehci_setup_standard_chain(xfer, &sc->sc_intr_p_last[xfer->qh_pos]);
2332f9f848faSopenharmony_ci
2333f9f848faSopenharmony_ci	/* put transfer on interrupt queue */
2334f9f848faSopenharmony_ci	ehci_transfer_intr_enqueue(xfer);
2335f9f848faSopenharmony_ci}
2336f9f848faSopenharmony_ci
2337f9f848faSopenharmony_ciconst struct usb_pipe_methods ehci_device_intr_methods =
2338f9f848faSopenharmony_ci{
2339f9f848faSopenharmony_ci	.open = ehci_device_intr_open,
2340f9f848faSopenharmony_ci	.close = ehci_device_intr_close,
2341f9f848faSopenharmony_ci	.enter = ehci_device_intr_enter,
2342f9f848faSopenharmony_ci	.start = ehci_device_intr_start,
2343f9f848faSopenharmony_ci};
2344f9f848faSopenharmony_ci
2345f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
2346f9f848faSopenharmony_ci * ehci full speed isochronous support
2347f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
2348f9f848faSopenharmony_cistatic void
2349f9f848faSopenharmony_ciehci_device_isoc_fs_open(struct usb_xfer *xfer)
2350f9f848faSopenharmony_ci{
2351f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
2352f9f848faSopenharmony_ci	ehci_sitd_t *td;
2353f9f848faSopenharmony_ci	uint32_t sitd_portaddr;
2354f9f848faSopenharmony_ci	uint8_t ds;
2355f9f848faSopenharmony_ci
2356f9f848faSopenharmony_ci	sitd_portaddr =
2357f9f848faSopenharmony_ci	    EHCI_SITD_SET_ADDR(xfer->address) |
2358f9f848faSopenharmony_ci	    EHCI_SITD_SET_ENDPT(UE_GET_ADDR(xfer->endpointno)) |
2359f9f848faSopenharmony_ci	    EHCI_SITD_SET_HUBA(xfer->xroot->udev->hs_hub_addr) |
2360f9f848faSopenharmony_ci	    EHCI_SITD_SET_PORT(xfer->xroot->udev->hs_port_no);
2361f9f848faSopenharmony_ci
2362f9f848faSopenharmony_ci	if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN)
2363f9f848faSopenharmony_ci		sitd_portaddr |= EHCI_SITD_SET_DIR_IN;
2364f9f848faSopenharmony_ci
2365f9f848faSopenharmony_ci	sitd_portaddr = htohc32(sc, sitd_portaddr);
2366f9f848faSopenharmony_ci
2367f9f848faSopenharmony_ci	/* initialize all TD's */
2368f9f848faSopenharmony_ci
2369f9f848faSopenharmony_ci	for (ds = 0; ds != 2; ds++) {
2370f9f848faSopenharmony_ci		for (td = (ehci_sitd_t *)xfer->td_start[ds]; td; td = td->obj_next) {
2371f9f848faSopenharmony_ci			td->sitd_portaddr = sitd_portaddr;
2372f9f848faSopenharmony_ci
2373f9f848faSopenharmony_ci			/*
2374f9f848faSopenharmony_ci			 * TODO: make some kind of automatic
2375f9f848faSopenharmony_ci			 * SMASK/CMASK selection based on micro-frame
2376f9f848faSopenharmony_ci			 * usage
2377f9f848faSopenharmony_ci			 *
2378f9f848faSopenharmony_ci			 * micro-frame usage (8 microframes per 1ms)
2379f9f848faSopenharmony_ci			 */
2380f9f848faSopenharmony_ci			td->sitd_back = htohc32(sc, EHCI_LINK_TERMINATE);
2381f9f848faSopenharmony_ci
2382f9f848faSopenharmony_ci			usb_pc_cpu_flush(td->page_cache);
2383f9f848faSopenharmony_ci		}
2384f9f848faSopenharmony_ci	}
2385f9f848faSopenharmony_ci}
2386f9f848faSopenharmony_ci
2387f9f848faSopenharmony_cistatic void
2388f9f848faSopenharmony_ciehci_device_isoc_fs_close(struct usb_xfer *xfer)
2389f9f848faSopenharmony_ci{
2390f9f848faSopenharmony_ci	ehci_device_done(xfer, USB_ERR_CANCELLED);
2391f9f848faSopenharmony_ci}
2392f9f848faSopenharmony_ci
2393f9f848faSopenharmony_cistatic void
2394f9f848faSopenharmony_ciehci_device_isoc_fs_enter(struct usb_xfer *xfer)
2395f9f848faSopenharmony_ci{
2396f9f848faSopenharmony_ci	struct usb_page_search buf_res;
2397f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
2398f9f848faSopenharmony_ci	ehci_sitd_t *td;
2399f9f848faSopenharmony_ci	ehci_sitd_t *td_last = NULL;
2400f9f848faSopenharmony_ci	ehci_sitd_t **pp_last;
2401f9f848faSopenharmony_ci	uint32_t *plen;
2402f9f848faSopenharmony_ci	uint32_t buf_offset;
2403f9f848faSopenharmony_ci	uint32_t nframes;
2404f9f848faSopenharmony_ci	uint32_t temp;
2405f9f848faSopenharmony_ci	uint32_t sitd_mask;
2406f9f848faSopenharmony_ci	uint16_t tlen;
2407f9f848faSopenharmony_ci	uint8_t sa;
2408f9f848faSopenharmony_ci	uint8_t sb;
2409f9f848faSopenharmony_ci
2410f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
2411f9f848faSopenharmony_ci	uint8_t once = 1;
2412f9f848faSopenharmony_ci#endif
2413f9f848faSopenharmony_ci
2414f9f848faSopenharmony_ci	DPRINTFN(6, "xfer=%p next=%d nframes=%d\n",
2415f9f848faSopenharmony_ci	    xfer, xfer->endpoint->isoc_next, xfer->nframes);
2416f9f848faSopenharmony_ci
2417f9f848faSopenharmony_ci	/* get the current frame index */
2418f9f848faSopenharmony_ci
2419f9f848faSopenharmony_ci	nframes = EOREAD4(sc, EHCI_FRINDEX) / 8;
2420f9f848faSopenharmony_ci
2421f9f848faSopenharmony_ci	/*
2422f9f848faSopenharmony_ci	 * check if the frame index is within the window where the frames
2423f9f848faSopenharmony_ci	 * will be inserted
2424f9f848faSopenharmony_ci	 */
2425f9f848faSopenharmony_ci	buf_offset = (nframes - xfer->endpoint->isoc_next) &
2426f9f848faSopenharmony_ci	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
2427f9f848faSopenharmony_ci
2428f9f848faSopenharmony_ci	if ((xfer->endpoint->is_synced == 0) ||
2429f9f848faSopenharmony_ci	    (buf_offset < xfer->nframes)) {
2430f9f848faSopenharmony_ci		/*
2431f9f848faSopenharmony_ci		 * If there is data underflow or the pipe queue is empty we
2432f9f848faSopenharmony_ci		 * schedule the transfer a few frames ahead of the current
2433f9f848faSopenharmony_ci		 * frame position. Else two isochronous transfers might
2434f9f848faSopenharmony_ci		 * overlap.
2435f9f848faSopenharmony_ci		 */
2436f9f848faSopenharmony_ci		xfer->endpoint->isoc_next = (nframes + 3) &
2437f9f848faSopenharmony_ci		    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
2438f9f848faSopenharmony_ci		xfer->endpoint->is_synced = 1;
2439f9f848faSopenharmony_ci		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
2440f9f848faSopenharmony_ci	}
2441f9f848faSopenharmony_ci	/*
2442f9f848faSopenharmony_ci	 * compute how many milliseconds the insertion is ahead of the
2443f9f848faSopenharmony_ci	 * current frame position:
2444f9f848faSopenharmony_ci	 */
2445f9f848faSopenharmony_ci	buf_offset = (xfer->endpoint->isoc_next - nframes) &
2446f9f848faSopenharmony_ci	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
2447f9f848faSopenharmony_ci
2448f9f848faSopenharmony_ci	/*
2449f9f848faSopenharmony_ci	 * pre-compute when the isochronous transfer will be finished:
2450f9f848faSopenharmony_ci	 */
2451f9f848faSopenharmony_ci	xfer->isoc_time_complete =
2452f9f848faSopenharmony_ci	    usb_isoc_time_expand(&sc->sc_bus, nframes) +
2453f9f848faSopenharmony_ci	    buf_offset + xfer->nframes;
2454f9f848faSopenharmony_ci
2455f9f848faSopenharmony_ci	/* get the real number of frames */
2456f9f848faSopenharmony_ci
2457f9f848faSopenharmony_ci	nframes = xfer->nframes;
2458f9f848faSopenharmony_ci
2459f9f848faSopenharmony_ci	buf_offset = 0;
2460f9f848faSopenharmony_ci
2461f9f848faSopenharmony_ci	plen = xfer->frlengths;
2462f9f848faSopenharmony_ci
2463f9f848faSopenharmony_ci	/* toggle the DMA set we are using */
2464f9f848faSopenharmony_ci	xfer->flags_int.curr_dma_set ^= 1;
2465f9f848faSopenharmony_ci
2466f9f848faSopenharmony_ci	/* get next DMA set */
2467f9f848faSopenharmony_ci	td = (ehci_sitd_t *)xfer->td_start[xfer->flags_int.curr_dma_set];
2468f9f848faSopenharmony_ci	xfer->td_transfer_first = td;
2469f9f848faSopenharmony_ci
2470f9f848faSopenharmony_ci	pp_last = &sc->sc_isoc_fs_p_last[xfer->endpoint->isoc_next];
2471f9f848faSopenharmony_ci
2472f9f848faSopenharmony_ci	/* store starting position */
2473f9f848faSopenharmony_ci
2474f9f848faSopenharmony_ci	xfer->qh_pos = xfer->endpoint->isoc_next;
2475f9f848faSopenharmony_ci
2476f9f848faSopenharmony_ci	while (nframes--) {
2477f9f848faSopenharmony_ci		if (td == NULL) {
2478f9f848faSopenharmony_ci			panic("%s:%d: out of TD's\n",
2479f9f848faSopenharmony_ci			    __FUNCTION__, __LINE__);
2480f9f848faSopenharmony_ci		}
2481f9f848faSopenharmony_ci		if (pp_last >= &sc->sc_isoc_fs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT])
2482f9f848faSopenharmony_ci			pp_last = &sc->sc_isoc_fs_p_last[0];
2483f9f848faSopenharmony_ci
2484f9f848faSopenharmony_ci		/* reuse sitd_portaddr and sitd_back from last transfer */
2485f9f848faSopenharmony_ci
2486f9f848faSopenharmony_ci		if (*plen > xfer->max_frame_size) {
2487f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
2488f9f848faSopenharmony_ci			if (once) {
2489f9f848faSopenharmony_ci				once = 0;
2490f9f848faSopenharmony_ci				PRINTK("%s: frame length(%d) exceeds %d "
2491f9f848faSopenharmony_ci				    "bytes (frame truncated)\n",
2492f9f848faSopenharmony_ci				    __FUNCTION__, *plen,
2493f9f848faSopenharmony_ci				    xfer->max_frame_size);
2494f9f848faSopenharmony_ci			}
2495f9f848faSopenharmony_ci#endif
2496f9f848faSopenharmony_ci			*plen = xfer->max_frame_size;
2497f9f848faSopenharmony_ci		}
2498f9f848faSopenharmony_ci
2499f9f848faSopenharmony_ci		/* allocate a slot */
2500f9f848faSopenharmony_ci
2501f9f848faSopenharmony_ci		sa = usbd_fs_isoc_schedule_alloc_slot(xfer,
2502f9f848faSopenharmony_ci		    xfer->isoc_time_complete - nframes - 1);
2503f9f848faSopenharmony_ci
2504f9f848faSopenharmony_ci		if (sa == 255) {
2505f9f848faSopenharmony_ci			/*
2506f9f848faSopenharmony_ci			 * Schedule is FULL, set length to zero:
2507f9f848faSopenharmony_ci			 */
2508f9f848faSopenharmony_ci
2509f9f848faSopenharmony_ci			*plen = 0;
2510f9f848faSopenharmony_ci			sa = USB_FS_ISOC_UFRAME_MAX - 1;
2511f9f848faSopenharmony_ci		}
2512f9f848faSopenharmony_ci		if (*plen) {
2513f9f848faSopenharmony_ci			/*
2514f9f848faSopenharmony_ci			 * only call "usbd_get_page()" when we have a
2515f9f848faSopenharmony_ci			 * non-zero length
2516f9f848faSopenharmony_ci			 */
2517f9f848faSopenharmony_ci			usbd_get_page(xfer->frbuffers, buf_offset, &buf_res);
2518f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
2519f9f848faSopenharmony_ci			td->sitd_bp[0] = htohc32(sc, buf_res.physaddr);
2520f9f848faSopenharmony_ci#else
2521f9f848faSopenharmony_ci			td->sitd_bp[0] = htohc32(sc, (unsigned int)buf_res.buffer);
2522f9f848faSopenharmony_ci#endif
2523f9f848faSopenharmony_ci			buf_offset += *plen;
2524f9f848faSopenharmony_ci			/*
2525f9f848faSopenharmony_ci			 * NOTE: We need to subtract one from the offset so
2526f9f848faSopenharmony_ci			 * that we are on a valid page!
2527f9f848faSopenharmony_ci			 */
2528f9f848faSopenharmony_ci			usbd_get_page(xfer->frbuffers, buf_offset - 1,
2529f9f848faSopenharmony_ci			    &buf_res);
2530f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
2531f9f848faSopenharmony_ci			temp = buf_res.physaddr & ~0xFFF;
2532f9f848faSopenharmony_ci#else
2533f9f848faSopenharmony_ci			temp = (unsigned int)buf_res.buffer & ~0xFFF;
2534f9f848faSopenharmony_ci#endif
2535f9f848faSopenharmony_ci		} else {
2536f9f848faSopenharmony_ci			td->sitd_bp[0] = 0;
2537f9f848faSopenharmony_ci			temp = 0;
2538f9f848faSopenharmony_ci		}
2539f9f848faSopenharmony_ci
2540f9f848faSopenharmony_ci		if (UE_GET_DIR(xfer->endpointno) == UE_DIR_OUT) {
2541f9f848faSopenharmony_ci			tlen = *plen;
2542f9f848faSopenharmony_ci			if (tlen <= 188) {
2543f9f848faSopenharmony_ci				temp |= 1;	/* T-count = 1, TP = ALL */
2544f9f848faSopenharmony_ci				tlen = 1;
2545f9f848faSopenharmony_ci			} else {
2546f9f848faSopenharmony_ci				tlen += 187;
2547f9f848faSopenharmony_ci				tlen /= 188;
2548f9f848faSopenharmony_ci				temp |= (uint32_t)tlen;	/* T-count = [1..6] */
2549f9f848faSopenharmony_ci				temp |= 8;	/* TP = Begin */
2550f9f848faSopenharmony_ci			}
2551f9f848faSopenharmony_ci
2552f9f848faSopenharmony_ci			tlen += sa;
2553f9f848faSopenharmony_ci
2554f9f848faSopenharmony_ci			if (tlen >= 8) {
2555f9f848faSopenharmony_ci				sb = 0;
2556f9f848faSopenharmony_ci			} else {
2557f9f848faSopenharmony_ci				sb = (1 << tlen);
2558f9f848faSopenharmony_ci			}
2559f9f848faSopenharmony_ci
2560f9f848faSopenharmony_ci			sa = (1 << sa);
2561f9f848faSopenharmony_ci			sa = (sb - sa) & 0x3F;
2562f9f848faSopenharmony_ci			sb = 0;
2563f9f848faSopenharmony_ci		} else {
2564f9f848faSopenharmony_ci			sb = (-(4 << sa)) & 0xFE;
2565f9f848faSopenharmony_ci			sa = (1 << sa) & 0x3F;
2566f9f848faSopenharmony_ci		}
2567f9f848faSopenharmony_ci
2568f9f848faSopenharmony_ci		sitd_mask = (EHCI_SITD_SET_SMASK(sa) |
2569f9f848faSopenharmony_ci		    EHCI_SITD_SET_CMASK(sb));
2570f9f848faSopenharmony_ci
2571f9f848faSopenharmony_ci		td->sitd_bp[1] = htohc32(sc, temp);
2572f9f848faSopenharmony_ci
2573f9f848faSopenharmony_ci		td->sitd_mask = htohc32(sc, sitd_mask);
2574f9f848faSopenharmony_ci
2575f9f848faSopenharmony_ci		if (nframes == 0) {
2576f9f848faSopenharmony_ci			td->sitd_status = htohc32(sc,
2577f9f848faSopenharmony_ci			    EHCI_SITD_IOC |
2578f9f848faSopenharmony_ci			    EHCI_SITD_ACTIVE |
2579f9f848faSopenharmony_ci			    EHCI_SITD_SET_LEN(*plen));
2580f9f848faSopenharmony_ci		} else {
2581f9f848faSopenharmony_ci			td->sitd_status = htohc32(sc,
2582f9f848faSopenharmony_ci			    EHCI_SITD_ACTIVE |
2583f9f848faSopenharmony_ci			    EHCI_SITD_SET_LEN(*plen));
2584f9f848faSopenharmony_ci		}
2585f9f848faSopenharmony_ci		usb_pc_cpu_flush(td->page_cache);
2586f9f848faSopenharmony_ci
2587f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
2588f9f848faSopenharmony_ci		if (ehcidebug > 15) {
2589f9f848faSopenharmony_ci			DPRINTF("FS-TD %d\n", nframes);
2590f9f848faSopenharmony_ci			ehci_dump_sitd(sc, td);
2591f9f848faSopenharmony_ci		}
2592f9f848faSopenharmony_ci#endif
2593f9f848faSopenharmony_ci		/* insert TD into schedule */
2594f9f848faSopenharmony_ci		EHCI_APPEND_FS_TD(td, *pp_last);
2595f9f848faSopenharmony_ci		pp_last++;
2596f9f848faSopenharmony_ci
2597f9f848faSopenharmony_ci		plen++;
2598f9f848faSopenharmony_ci		td_last = td;
2599f9f848faSopenharmony_ci		td = td->obj_next;
2600f9f848faSopenharmony_ci	}
2601f9f848faSopenharmony_ci
2602f9f848faSopenharmony_ci	xfer->td_transfer_last = td_last;
2603f9f848faSopenharmony_ci
2604f9f848faSopenharmony_ci	/* update isoc_next */
2605f9f848faSopenharmony_ci	xfer->endpoint->isoc_next = (pp_last - &sc->sc_isoc_fs_p_last[0]) &
2606f9f848faSopenharmony_ci	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
2607f9f848faSopenharmony_ci
2608f9f848faSopenharmony_ci	/*
2609f9f848faSopenharmony_ci	 * We don't allow cancelling of the SPLIT transaction USB FULL
2610f9f848faSopenharmony_ci	 * speed transfer, because it disturbs the bandwidth
2611f9f848faSopenharmony_ci	 * computation algorithm.
2612f9f848faSopenharmony_ci	 */
2613f9f848faSopenharmony_ci	xfer->flags_int.can_cancel_immed = 0;
2614f9f848faSopenharmony_ci}
2615f9f848faSopenharmony_ci
2616f9f848faSopenharmony_cistatic void
2617f9f848faSopenharmony_ciehci_device_isoc_fs_start(struct usb_xfer *xfer)
2618f9f848faSopenharmony_ci{
2619f9f848faSopenharmony_ci	/*
2620f9f848faSopenharmony_ci	 * We don't allow cancelling of the SPLIT transaction USB FULL
2621f9f848faSopenharmony_ci	 * speed transfer, because it disturbs the bandwidth
2622f9f848faSopenharmony_ci	 * computation algorithm.
2623f9f848faSopenharmony_ci	 */
2624f9f848faSopenharmony_ci	xfer->flags_int.can_cancel_immed = 0;
2625f9f848faSopenharmony_ci
2626f9f848faSopenharmony_ci	/* set a default timeout */
2627f9f848faSopenharmony_ci	if (xfer->timeout == 0)
2628f9f848faSopenharmony_ci		xfer->timeout = 500; /* ms */
2629f9f848faSopenharmony_ci
2630f9f848faSopenharmony_ci	/* put transfer on interrupt queue */
2631f9f848faSopenharmony_ci	ehci_transfer_intr_enqueue(xfer);
2632f9f848faSopenharmony_ci}
2633f9f848faSopenharmony_ci
2634f9f848faSopenharmony_ciconst struct usb_pipe_methods ehci_device_isoc_fs_methods = {
2635f9f848faSopenharmony_ci	.open = ehci_device_isoc_fs_open,
2636f9f848faSopenharmony_ci	.close = ehci_device_isoc_fs_close,
2637f9f848faSopenharmony_ci	.enter = ehci_device_isoc_fs_enter,
2638f9f848faSopenharmony_ci	.start = ehci_device_isoc_fs_start,
2639f9f848faSopenharmony_ci};
2640f9f848faSopenharmony_ci
2641f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
2642f9f848faSopenharmony_ci * ehci high speed isochronous support
2643f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
2644f9f848faSopenharmony_cistatic void
2645f9f848faSopenharmony_ciehci_device_isoc_hs_open(struct usb_xfer *xfer)
2646f9f848faSopenharmony_ci{
2647f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
2648f9f848faSopenharmony_ci	ehci_itd_t *td;
2649f9f848faSopenharmony_ci	uint32_t temp;
2650f9f848faSopenharmony_ci	uint8_t ds;
2651f9f848faSopenharmony_ci
2652f9f848faSopenharmony_ci	usb_hs_bandwidth_alloc(xfer);
2653f9f848faSopenharmony_ci
2654f9f848faSopenharmony_ci	/* initialize all TD's */
2655f9f848faSopenharmony_ci
2656f9f848faSopenharmony_ci	for (ds = 0; ds != 2; ds++) {
2657f9f848faSopenharmony_ci		for (td = (ehci_itd_t *)xfer->td_start[ds]; td; td = td->obj_next) {
2658f9f848faSopenharmony_ci			/* set TD inactive */
2659f9f848faSopenharmony_ci			td->itd_status[0] = 0;
2660f9f848faSopenharmony_ci			td->itd_status[1] = 0;
2661f9f848faSopenharmony_ci			td->itd_status[2] = 0;
2662f9f848faSopenharmony_ci			td->itd_status[3] = 0;
2663f9f848faSopenharmony_ci			td->itd_status[4] = 0;
2664f9f848faSopenharmony_ci			td->itd_status[5] = 0;
2665f9f848faSopenharmony_ci			td->itd_status[6] = 0;
2666f9f848faSopenharmony_ci			td->itd_status[7] = 0;
2667f9f848faSopenharmony_ci
2668f9f848faSopenharmony_ci			/* set endpoint and address */
2669f9f848faSopenharmony_ci			td->itd_bp[0] = htohc32(sc,
2670f9f848faSopenharmony_ci			    EHCI_ITD_SET_ADDR(xfer->address) |
2671f9f848faSopenharmony_ci			    EHCI_ITD_SET_ENDPT(UE_GET_ADDR(xfer->endpointno)));
2672f9f848faSopenharmony_ci
2673f9f848faSopenharmony_ci			temp =
2674f9f848faSopenharmony_ci			    EHCI_ITD_SET_MPL(xfer->max_packet_size & 0x7FF);
2675f9f848faSopenharmony_ci
2676f9f848faSopenharmony_ci			/* set direction */
2677f9f848faSopenharmony_ci			if (UE_GET_DIR(xfer->endpointno) == UE_DIR_IN) {
2678f9f848faSopenharmony_ci				temp |= EHCI_ITD_SET_DIR_IN;
2679f9f848faSopenharmony_ci			}
2680f9f848faSopenharmony_ci			/* set maximum packet size */
2681f9f848faSopenharmony_ci			td->itd_bp[1] = htohc32(sc, temp);
2682f9f848faSopenharmony_ci
2683f9f848faSopenharmony_ci			/* set transfer multiplier */
2684f9f848faSopenharmony_ci			td->itd_bp[2] = htohc32(sc, xfer->max_packet_count & 3);
2685f9f848faSopenharmony_ci
2686f9f848faSopenharmony_ci			usb_pc_cpu_flush(td->page_cache);
2687f9f848faSopenharmony_ci		}
2688f9f848faSopenharmony_ci	}
2689f9f848faSopenharmony_ci}
2690f9f848faSopenharmony_ci
2691f9f848faSopenharmony_cistatic void
2692f9f848faSopenharmony_ciehci_device_isoc_hs_close(struct usb_xfer *xfer)
2693f9f848faSopenharmony_ci{
2694f9f848faSopenharmony_ci	ehci_device_done(xfer, USB_ERR_CANCELLED);
2695f9f848faSopenharmony_ci
2696f9f848faSopenharmony_ci	/* bandwidth must be freed after device done */
2697f9f848faSopenharmony_ci	usb_hs_bandwidth_free(xfer);
2698f9f848faSopenharmony_ci}
2699f9f848faSopenharmony_ci
2700f9f848faSopenharmony_cistatic void
2701f9f848faSopenharmony_ciehci_device_isoc_hs_enter(struct usb_xfer *xfer)
2702f9f848faSopenharmony_ci{
2703f9f848faSopenharmony_ci	struct usb_page_search buf_res;
2704f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(xfer->xroot->bus);
2705f9f848faSopenharmony_ci	ehci_itd_t *td;
2706f9f848faSopenharmony_ci	ehci_itd_t *td_last = NULL;
2707f9f848faSopenharmony_ci	ehci_itd_t **pp_last;
2708f9f848faSopenharmony_ci	bus_size_t page_addr;
2709f9f848faSopenharmony_ci	uint32_t *plen;
2710f9f848faSopenharmony_ci	uint32_t status;
2711f9f848faSopenharmony_ci	uint32_t buf_offset;
2712f9f848faSopenharmony_ci	uint32_t nframes;
2713f9f848faSopenharmony_ci	uint32_t itd_offset[8 + 1];
2714f9f848faSopenharmony_ci	uint8_t x;
2715f9f848faSopenharmony_ci	uint8_t td_no;
2716f9f848faSopenharmony_ci	uint8_t page_no;
2717f9f848faSopenharmony_ci
2718f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
2719f9f848faSopenharmony_ci	uint8_t once = 1;
2720f9f848faSopenharmony_ci#endif
2721f9f848faSopenharmony_ci
2722f9f848faSopenharmony_ci	DPRINTFN(6, "xfer=%p next=%d nframes=%d shift=%d\n",
2723f9f848faSopenharmony_ci		xfer, xfer->endpoint->isoc_next, xfer->nframes,
2724f9f848faSopenharmony_ci	    usbd_xfer_get_fps_shift(xfer));
2725f9f848faSopenharmony_ci
2726f9f848faSopenharmony_ci	/* get the current frame index */
2727f9f848faSopenharmony_ci
2728f9f848faSopenharmony_ci	nframes = EOREAD4(sc, EHCI_FRINDEX) / 8;
2729f9f848faSopenharmony_ci
2730f9f848faSopenharmony_ci	/*
2731f9f848faSopenharmony_ci	 * check if the frame index is within the window where the frames
2732f9f848faSopenharmony_ci	 * will be inserted
2733f9f848faSopenharmony_ci	 */
2734f9f848faSopenharmony_ci	buf_offset = (nframes - xfer->endpoint->isoc_next) &
2735f9f848faSopenharmony_ci	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
2736f9f848faSopenharmony_ci
2737f9f848faSopenharmony_ci	if ((xfer->endpoint->is_synced == 0) ||
2738f9f848faSopenharmony_ci	    (buf_offset < (((xfer->nframes << shift) + 7) / 8))) {
2739f9f848faSopenharmony_ci		/*
2740f9f848faSopenharmony_ci		 * If there is data underflow or the pipe queue is empty we
2741f9f848faSopenharmony_ci		 * schedule the transfer a few frames ahead of the current
2742f9f848faSopenharmony_ci		 * frame position. Else two isochronous transfers might
2743f9f848faSopenharmony_ci		 * overlap.
2744f9f848faSopenharmony_ci		 */
2745f9f848faSopenharmony_ci		xfer->endpoint->isoc_next = (nframes + 3) &
2746f9f848faSopenharmony_ci		    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
2747f9f848faSopenharmony_ci		xfer->endpoint->is_synced = 1;
2748f9f848faSopenharmony_ci		DPRINTFN(3, "start next=%d\n", xfer->endpoint->isoc_next);
2749f9f848faSopenharmony_ci	}
2750f9f848faSopenharmony_ci	/*
2751f9f848faSopenharmony_ci	 * compute how many milliseconds the insertion is ahead of the
2752f9f848faSopenharmony_ci	 * current frame position:
2753f9f848faSopenharmony_ci	 */
2754f9f848faSopenharmony_ci	buf_offset = (xfer->endpoint->isoc_next - nframes) &
2755f9f848faSopenharmony_ci	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
2756f9f848faSopenharmony_ci
2757f9f848faSopenharmony_ci	/*
2758f9f848faSopenharmony_ci	 * pre-compute when the isochronous transfer will be finished:
2759f9f848faSopenharmony_ci	 */
2760f9f848faSopenharmony_ci	xfer->isoc_time_complete =
2761f9f848faSopenharmony_ci	    usb_isoc_time_expand(&sc->sc_bus, nframes) + buf_offset +
2762f9f848faSopenharmony_ci	    (((xfer->nframes << shift) + 7) / 8);
2763f9f848faSopenharmony_ci
2764f9f848faSopenharmony_ci	/* get the real number of frames */
2765f9f848faSopenharmony_ci
2766f9f848faSopenharmony_ci	nframes = xfer->nframes;
2767f9f848faSopenharmony_ci
2768f9f848faSopenharmony_ci	buf_offset = 0;
2769f9f848faSopenharmony_ci	td_no = 0;
2770f9f848faSopenharmony_ci
2771f9f848faSopenharmony_ci	plen = xfer->frlengths;
2772f9f848faSopenharmony_ci
2773f9f848faSopenharmony_ci	/* toggle the DMA set we are using */
2774f9f848faSopenharmony_ci	xfer->flags_int.curr_dma_set ^= 1;
2775f9f848faSopenharmony_ci
2776f9f848faSopenharmony_ci	/* get next DMA set */
2777f9f848faSopenharmony_ci	td = (ehci_itd_t *)xfer->td_start[xfer->flags_int.curr_dma_set];
2778f9f848faSopenharmony_ci	xfer->td_transfer_first = td;
2779f9f848faSopenharmony_ci
2780f9f848faSopenharmony_ci	pp_last = &sc->sc_isoc_hs_p_last[xfer->endpoint->isoc_next];
2781f9f848faSopenharmony_ci
2782f9f848faSopenharmony_ci	/* store starting position */
2783f9f848faSopenharmony_ci
2784f9f848faSopenharmony_ci	xfer->qh_pos = xfer->endpoint->isoc_next;
2785f9f848faSopenharmony_ci
2786f9f848faSopenharmony_ci	while (nframes) {
2787f9f848faSopenharmony_ci		if (td == NULL) {
2788f9f848faSopenharmony_ci			panic("%s:%d: out of TD's\n",
2789f9f848faSopenharmony_ci			    __FUNCTION__, __LINE__);
2790f9f848faSopenharmony_ci		}
2791f9f848faSopenharmony_ci		if (pp_last >= &sc->sc_isoc_hs_p_last[EHCI_VIRTUAL_FRAMELIST_COUNT]) {
2792f9f848faSopenharmony_ci			pp_last = &sc->sc_isoc_hs_p_last[0];
2793f9f848faSopenharmony_ci		}
2794f9f848faSopenharmony_ci		/* range check */
2795f9f848faSopenharmony_ci		if (*plen > xfer->max_frame_size) {
2796f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
2797f9f848faSopenharmony_ci			if (once) {
2798f9f848faSopenharmony_ci				once = 0;
2799f9f848faSopenharmony_ci				PRINTK("%s: frame length(%d) exceeds %d bytes "
2800f9f848faSopenharmony_ci				    "(frame truncated)\n",
2801f9f848faSopenharmony_ci				    __FUNCTION__, *plen, xfer->max_frame_size);
2802f9f848faSopenharmony_ci			}
2803f9f848faSopenharmony_ci#endif
2804f9f848faSopenharmony_ci			*plen = xfer->max_frame_size;
2805f9f848faSopenharmony_ci		}
2806f9f848faSopenharmony_ci
2807f9f848faSopenharmony_ci		if (xfer->endpoint->usb_smask & (1 << td_no)) {
2808f9f848faSopenharmony_ci			status = (EHCI_ITD_SET_LEN(*plen) |
2809f9f848faSopenharmony_ci			    EHCI_ITD_ACTIVE |
2810f9f848faSopenharmony_ci			    EHCI_ITD_SET_PG(0));
2811f9f848faSopenharmony_ci			td->itd_status[td_no] = htohc32(sc, status);
2812f9f848faSopenharmony_ci			itd_offset[td_no] = buf_offset;
2813f9f848faSopenharmony_ci			buf_offset += *plen;
2814f9f848faSopenharmony_ci			plen++;
2815f9f848faSopenharmony_ci			nframes --;
2816f9f848faSopenharmony_ci		} else {
2817f9f848faSopenharmony_ci			td->itd_status[td_no] = 0;	/* not active */
2818f9f848faSopenharmony_ci			itd_offset[td_no] = buf_offset;
2819f9f848faSopenharmony_ci		}
2820f9f848faSopenharmony_ci
2821f9f848faSopenharmony_ci		td_no++;
2822f9f848faSopenharmony_ci
2823f9f848faSopenharmony_ci		if ((td_no == 8) || (nframes == 0)) {
2824f9f848faSopenharmony_ci			/* the rest of the transfers are not active, if any */
2825f9f848faSopenharmony_ci			for (x = td_no; x != 8; x++) {
2826f9f848faSopenharmony_ci				td->itd_status[x] = 0;	/* not active */
2827f9f848faSopenharmony_ci			}
2828f9f848faSopenharmony_ci
2829f9f848faSopenharmony_ci			/* check if there is any data to be transferred */
2830f9f848faSopenharmony_ci			if (itd_offset[0] != buf_offset) {
2831f9f848faSopenharmony_ci				page_no = 0;
2832f9f848faSopenharmony_ci				itd_offset[td_no] = buf_offset;
2833f9f848faSopenharmony_ci
2834f9f848faSopenharmony_ci				/* get first page offset */
2835f9f848faSopenharmony_ci				usbd_get_page(xfer->frbuffers, itd_offset[0], &buf_res);
2836f9f848faSopenharmony_ci				/* get page address */
2837f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
2838f9f848faSopenharmony_ci				page_addr = buf_res.physaddr & ~0xFFF;
2839f9f848faSopenharmony_ci#else
2840f9f848faSopenharmony_ci				page_addr = (unsigned int)buf_res.buffer & ~0xFFF;
2841f9f848faSopenharmony_ci#endif
2842f9f848faSopenharmony_ci				/* update page address */
2843f9f848faSopenharmony_ci				td->itd_bp[0] &= htohc32(sc, 0xFFF);
2844f9f848faSopenharmony_ci				td->itd_bp[0] |= htohc32(sc, page_addr);
2845f9f848faSopenharmony_ci
2846f9f848faSopenharmony_ci				for (x = 0; x != td_no; x++) {
2847f9f848faSopenharmony_ci					/* set page number and page offset */
2848f9f848faSopenharmony_ci					status = (EHCI_ITD_SET_PG(page_no) |
2849f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
2850f9f848faSopenharmony_ci					    (buf_res.physaddr & 0xFFF));
2851f9f848faSopenharmony_ci#else
2852f9f848faSopenharmony_ci					((unsigned int)buf_res.buffer & 0xFFF));
2853f9f848faSopenharmony_ci#endif
2854f9f848faSopenharmony_ci					td->itd_status[x] |= htohc32(sc, status);
2855f9f848faSopenharmony_ci
2856f9f848faSopenharmony_ci					/* get next page offset */
2857f9f848faSopenharmony_ci					if (itd_offset[x + 1] == buf_offset) {
2858f9f848faSopenharmony_ci						/*
2859f9f848faSopenharmony_ci						 * We subtract one so that
2860f9f848faSopenharmony_ci						 * we don't go off the last
2861f9f848faSopenharmony_ci						 * page!
2862f9f848faSopenharmony_ci						 */
2863f9f848faSopenharmony_ci						usbd_get_page(xfer->frbuffers, buf_offset - 1, &buf_res);
2864f9f848faSopenharmony_ci					} else {
2865f9f848faSopenharmony_ci						usbd_get_page(xfer->frbuffers, itd_offset[x + 1], &buf_res);
2866f9f848faSopenharmony_ci					}
2867f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
2868f9f848faSopenharmony_ci					/* check if we need a new page */
2869f9f848faSopenharmony_ci					if ((buf_res.physaddr ^ page_addr) & ~0xFFF) {
2870f9f848faSopenharmony_ci						/* new page needed */
2871f9f848faSopenharmony_ci						page_addr = buf_res.physaddr & ~0xFFF;
2872f9f848faSopenharmony_ci#else
2873f9f848faSopenharmony_ci					/* check if we need a new page */
2874f9f848faSopenharmony_ci					if (((unsigned int)buf_res.buffer ^ page_addr) & ~0xFFF) {
2875f9f848faSopenharmony_ci						/* new page needed */
2876f9f848faSopenharmony_ci						page_addr = (unsigned int)buf_res.buffer & ~0xFFF;
2877f9f848faSopenharmony_ci#endif
2878f9f848faSopenharmony_ci						if (page_no == 6) {
2879f9f848faSopenharmony_ci							panic("%s: too many pages\n", __FUNCTION__);
2880f9f848faSopenharmony_ci						}
2881f9f848faSopenharmony_ci						page_no++;
2882f9f848faSopenharmony_ci						page_no%= EHCI_ITD_BP_MAX;
2883f9f848faSopenharmony_ci						/* update page address */
2884f9f848faSopenharmony_ci						td->itd_bp[page_no] &= htohc32(sc, 0xFFF);
2885f9f848faSopenharmony_ci						td->itd_bp[page_no] |= htohc32(sc, page_addr);
2886f9f848faSopenharmony_ci					}
2887f9f848faSopenharmony_ci				}
2888f9f848faSopenharmony_ci			}
2889f9f848faSopenharmony_ci			/* set IOC bit if we are complete */
2890f9f848faSopenharmony_ci			if (nframes == 0) {
2891f9f848faSopenharmony_ci				td->itd_status[td_no - 1] |= htohc32(sc, EHCI_ITD_IOC);
2892f9f848faSopenharmony_ci			}
2893f9f848faSopenharmony_ci			usb_pc_cpu_flush(td->page_cache);
2894f9f848faSopenharmony_ci#ifdef LOSCFG_USB_DEBUG
2895f9f848faSopenharmony_ci			if (ehcidebug > 15) {
2896f9f848faSopenharmony_ci				DPRINTF("HS-TD %d\n", nframes);
2897f9f848faSopenharmony_ci				ehci_dump_itd(sc, td);
2898f9f848faSopenharmony_ci			}
2899f9f848faSopenharmony_ci#endif
2900f9f848faSopenharmony_ci			/* insert TD into schedule */
2901f9f848faSopenharmony_ci			EHCI_APPEND_HS_TD(td, *pp_last);
2902f9f848faSopenharmony_ci			pp_last++;
2903f9f848faSopenharmony_ci
2904f9f848faSopenharmony_ci			td_no = 0;
2905f9f848faSopenharmony_ci			td_last = td;
2906f9f848faSopenharmony_ci			td = td->obj_next;
2907f9f848faSopenharmony_ci		}
2908f9f848faSopenharmony_ci	}
2909f9f848faSopenharmony_ci
2910f9f848faSopenharmony_ci	xfer->td_transfer_last = td_last;
2911f9f848faSopenharmony_ci
2912f9f848faSopenharmony_ci	/* update isoc_next */
2913f9f848faSopenharmony_ci	xfer->endpoint->isoc_next = (pp_last - &sc->sc_isoc_hs_p_last[0]) &
2914f9f848faSopenharmony_ci	    (EHCI_VIRTUAL_FRAMELIST_COUNT - 1);
2915f9f848faSopenharmony_ci}
2916f9f848faSopenharmony_ci
2917f9f848faSopenharmony_cistatic void
2918f9f848faSopenharmony_ciehci_device_isoc_hs_start(struct usb_xfer *xfer)
2919f9f848faSopenharmony_ci{
2920f9f848faSopenharmony_ci	/* put transfer on interrupt queue */
2921f9f848faSopenharmony_ci	ehci_transfer_intr_enqueue(xfer);
2922f9f848faSopenharmony_ci}
2923f9f848faSopenharmony_ci
2924f9f848faSopenharmony_ciconst struct usb_pipe_methods ehci_device_isoc_hs_methods = {
2925f9f848faSopenharmony_ci	.open = ehci_device_isoc_hs_open,
2926f9f848faSopenharmony_ci	.close = ehci_device_isoc_hs_close,
2927f9f848faSopenharmony_ci	.enter = ehci_device_isoc_hs_enter,
2928f9f848faSopenharmony_ci	.start = ehci_device_isoc_hs_start,
2929f9f848faSopenharmony_ci};
2930f9f848faSopenharmony_ci
2931f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
2932f9f848faSopenharmony_ci * ehci root control support
2933f9f848faSopenharmony_ci *------------------------------------------------------------------------*
2934f9f848faSopenharmony_ci * Simulate a hardware hub by handling all the necessary requests.
2935f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
2936f9f848faSopenharmony_ci
2937f9f848faSopenharmony_cistatic const
2938f9f848faSopenharmony_cistruct usb_device_descriptor ehci_devd = {
2939f9f848faSopenharmony_ci	sizeof(struct usb_device_descriptor),
2940f9f848faSopenharmony_ci	UDESC_DEVICE,			/* type */
2941f9f848faSopenharmony_ci	{0x00, 0x02},			/* USB version */
2942f9f848faSopenharmony_ci	UDCLASS_HUB,			/* class */
2943f9f848faSopenharmony_ci	UDSUBCLASS_HUB,			/* subclass */
2944f9f848faSopenharmony_ci	UDPROTO_HSHUBSTT,		/* protocol */
2945f9f848faSopenharmony_ci	64,				/* max packet */
2946f9f848faSopenharmony_ci	{0}, {0}, {0x00, 0x01},		/* device id */
2947f9f848faSopenharmony_ci	1, 2, 0,			/* string indicies */
2948f9f848faSopenharmony_ci	1				/* # of configurations */
2949f9f848faSopenharmony_ci};
2950f9f848faSopenharmony_ci
2951f9f848faSopenharmony_cistatic const
2952f9f848faSopenharmony_cistruct usb_device_qualifier ehci_odevd = {
2953f9f848faSopenharmony_ci	sizeof(struct usb_device_qualifier),
2954f9f848faSopenharmony_ci	UDESC_DEVICE_QUALIFIER,		/* type */
2955f9f848faSopenharmony_ci	{0x00, 0x02},			/* USB version */
2956f9f848faSopenharmony_ci	UDCLASS_HUB,			/* class */
2957f9f848faSopenharmony_ci	UDSUBCLASS_HUB,			/* subclass */
2958f9f848faSopenharmony_ci	UDPROTO_FSHUB,			/* protocol */
2959f9f848faSopenharmony_ci	0,				/* max packet */
2960f9f848faSopenharmony_ci	0,				/* # of configurations */
2961f9f848faSopenharmony_ci	0
2962f9f848faSopenharmony_ci};
2963f9f848faSopenharmony_ci
2964f9f848faSopenharmony_cistatic const struct ehci_config_desc ehci_confd = {
2965f9f848faSopenharmony_ci	.confd = {
2966f9f848faSopenharmony_ci		.bLength = sizeof(struct usb_config_descriptor),
2967f9f848faSopenharmony_ci		.bDescriptorType = UDESC_CONFIG,
2968f9f848faSopenharmony_ci		.wTotalLength[0] = sizeof(ehci_confd),
2969f9f848faSopenharmony_ci		.bNumInterface = 1,
2970f9f848faSopenharmony_ci		.bConfigurationValue = 1,
2971f9f848faSopenharmony_ci		.iConfiguration = 0,
2972f9f848faSopenharmony_ci		.bmAttributes = UC_SELF_POWERED,
2973f9f848faSopenharmony_ci		.bMaxPower = 0		/* max power */
2974f9f848faSopenharmony_ci	},
2975f9f848faSopenharmony_ci	.ifcd = {
2976f9f848faSopenharmony_ci		.bLength = sizeof(struct usb_interface_descriptor),
2977f9f848faSopenharmony_ci		.bDescriptorType = UDESC_INTERFACE,
2978f9f848faSopenharmony_ci		.bNumEndpoints = 1,
2979f9f848faSopenharmony_ci		.bInterfaceClass = UICLASS_HUB,
2980f9f848faSopenharmony_ci		.bInterfaceSubClass = UISUBCLASS_HUB,
2981f9f848faSopenharmony_ci		.bInterfaceProtocol = 0,
2982f9f848faSopenharmony_ci	},
2983f9f848faSopenharmony_ci	.endpd = {
2984f9f848faSopenharmony_ci		.bLength = sizeof(struct usb_endpoint_descriptor),
2985f9f848faSopenharmony_ci		.bDescriptorType = UDESC_ENDPOINT,
2986f9f848faSopenharmony_ci		.bEndpointAddress = UE_DIR_IN | EHCI_INTR_ENDPT,
2987f9f848faSopenharmony_ci		.bmAttributes = UE_INTERRUPT,
2988f9f848faSopenharmony_ci		.wMaxPacketSize[0] = 8,	/* max packet (63 ports) */
2989f9f848faSopenharmony_ci		.bInterval = 255,
2990f9f848faSopenharmony_ci	},
2991f9f848faSopenharmony_ci};
2992f9f848faSopenharmony_ci
2993f9f848faSopenharmony_cistatic const
2994f9f848faSopenharmony_cistruct usb_hub_descriptor ehci_hubd = {
2995f9f848faSopenharmony_ci	.bDescLength = 0,		/* dynamic length */
2996f9f848faSopenharmony_ci	.bDescriptorType = UDESC_HUB,
2997f9f848faSopenharmony_ci};
2998f9f848faSopenharmony_ci
2999f9f848faSopenharmony_ciuint16_t
3000f9f848faSopenharmony_ciehci_get_port_speed_portsc(struct ehci_softc *sc, uint16_t index)
3001f9f848faSopenharmony_ci{
3002f9f848faSopenharmony_ci	uint32_t v;
3003f9f848faSopenharmony_ci
3004f9f848faSopenharmony_ci	v = EOREAD4(sc, EHCI_PORTSC(index));
3005f9f848faSopenharmony_ci	v = (v >> EHCI_PORTSC_PSPD_SHIFT) & EHCI_PORTSC_PSPD_MASK;
3006f9f848faSopenharmony_ci
3007f9f848faSopenharmony_ci	if (v == EHCI_PORT_SPEED_HIGH)
3008f9f848faSopenharmony_ci		return (UPS_HIGH_SPEED);
3009f9f848faSopenharmony_ci	if (v == EHCI_PORT_SPEED_LOW)
3010f9f848faSopenharmony_ci		return (UPS_LOW_SPEED);
3011f9f848faSopenharmony_ci	return (0);
3012f9f848faSopenharmony_ci}
3013f9f848faSopenharmony_ci
3014f9f848faSopenharmony_ciuint16_t
3015f9f848faSopenharmony_ciehci_get_port_speed_hostc(struct ehci_softc *sc, uint16_t index)
3016f9f848faSopenharmony_ci{
3017f9f848faSopenharmony_ci	uint32_t v;
3018f9f848faSopenharmony_ci
3019f9f848faSopenharmony_ci	v = EOREAD4(sc, EHCI_HOSTC(index));
3020f9f848faSopenharmony_ci	v = (v >> EHCI_HOSTC_PSPD_SHIFT) & EHCI_HOSTC_PSPD_MASK;
3021f9f848faSopenharmony_ci
3022f9f848faSopenharmony_ci	if (v == EHCI_PORT_SPEED_HIGH)
3023f9f848faSopenharmony_ci		return (UPS_HIGH_SPEED);
3024f9f848faSopenharmony_ci	if (v == EHCI_PORT_SPEED_LOW)
3025f9f848faSopenharmony_ci		return (UPS_LOW_SPEED);
3026f9f848faSopenharmony_ci	return (0);
3027f9f848faSopenharmony_ci}
3028f9f848faSopenharmony_ci
3029f9f848faSopenharmony_cistatic usb_error_t
3030f9f848faSopenharmony_ciehci_roothub_exec(struct usb_device *udev,
3031f9f848faSopenharmony_ci    struct usb_device_request *req, const void **pptr, uint16_t *plength)
3032f9f848faSopenharmony_ci{
3033f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(udev->bus);
3034f9f848faSopenharmony_ci	const char *str_ptr;
3035f9f848faSopenharmony_ci	const void *ptr;
3036f9f848faSopenharmony_ci	uint32_t port;
3037f9f848faSopenharmony_ci	uint32_t v;
3038f9f848faSopenharmony_ci	uint16_t len;
3039f9f848faSopenharmony_ci	uint16_t i;
3040f9f848faSopenharmony_ci	uint16_t value;
3041f9f848faSopenharmony_ci	uint16_t index;
3042f9f848faSopenharmony_ci	usb_error_t err = USB_ERR_NORMAL_COMPLETION;
3043f9f848faSopenharmony_ci
3044f9f848faSopenharmony_ci	USB_BUS_LOCK_ASSERT(&sc->sc_bus, MA_OWNED);
3045f9f848faSopenharmony_ci
3046f9f848faSopenharmony_ci	/* buffer reset */
3047f9f848faSopenharmony_ci	ptr = (const void *)&sc->sc_hub_desc;
3048f9f848faSopenharmony_ci	len = 0;
3049f9f848faSopenharmony_ci
3050f9f848faSopenharmony_ci	value = UGETW(req->wValue);
3051f9f848faSopenharmony_ci	index = UGETW(req->wIndex);
3052f9f848faSopenharmony_ci
3053f9f848faSopenharmony_ci	DPRINTFN(3, "type=0x%02x request=0x%02x wLen=0x%04x "
3054f9f848faSopenharmony_ci	    "wValue=0x%04x wIndex=0x%04x\n",
3055f9f848faSopenharmony_ci	    req->bmRequestType, req->bRequest,
3056f9f848faSopenharmony_ci	    UGETW(req->wLength), value, index);
3057f9f848faSopenharmony_ci
3058f9f848faSopenharmony_ci#define	C(x,y) ((x) | ((y) << 8))
3059f9f848faSopenharmony_ci	switch (C(req->bRequest, req->bmRequestType)) {
3060f9f848faSopenharmony_ci	case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
3061f9f848faSopenharmony_ci	case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
3062f9f848faSopenharmony_ci	case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
3063f9f848faSopenharmony_ci		/*
3064f9f848faSopenharmony_ci		 * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
3065f9f848faSopenharmony_ci		 * for the integrated root hub.
3066f9f848faSopenharmony_ci		 */
3067f9f848faSopenharmony_ci		break;
3068f9f848faSopenharmony_ci	case C(UR_GET_CONFIG, UT_READ_DEVICE):
3069f9f848faSopenharmony_ci		len = 1;
3070f9f848faSopenharmony_ci		sc->sc_hub_desc.temp[0] = sc->sc_conf;
3071f9f848faSopenharmony_ci		break;
3072f9f848faSopenharmony_ci	case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
3073f9f848faSopenharmony_ci		switch (value >> 8) {
3074f9f848faSopenharmony_ci		case UDESC_DEVICE:
3075f9f848faSopenharmony_ci			if ((value & 0xff) != 0) {
3076f9f848faSopenharmony_ci				err = USB_ERR_IOERROR;
3077f9f848faSopenharmony_ci				goto done;
3078f9f848faSopenharmony_ci			}
3079f9f848faSopenharmony_ci			len = sizeof(ehci_devd);
3080f9f848faSopenharmony_ci			ptr = (const void *)&ehci_devd;
3081f9f848faSopenharmony_ci			break;
3082f9f848faSopenharmony_ci			/*
3083f9f848faSopenharmony_ci			 * We can't really operate at another speed,
3084f9f848faSopenharmony_ci			 * but the specification says we need this
3085f9f848faSopenharmony_ci			 * descriptor:
3086f9f848faSopenharmony_ci			 */
3087f9f848faSopenharmony_ci		case UDESC_DEVICE_QUALIFIER:
3088f9f848faSopenharmony_ci			if ((value & 0xff) != 0) {
3089f9f848faSopenharmony_ci				err = USB_ERR_IOERROR;
3090f9f848faSopenharmony_ci				goto done;
3091f9f848faSopenharmony_ci			}
3092f9f848faSopenharmony_ci			len = sizeof(ehci_odevd);
3093f9f848faSopenharmony_ci			ptr = (const void *)&ehci_odevd;
3094f9f848faSopenharmony_ci			break;
3095f9f848faSopenharmony_ci
3096f9f848faSopenharmony_ci		case UDESC_CONFIG:
3097f9f848faSopenharmony_ci			if ((value & 0xff) != 0) {
3098f9f848faSopenharmony_ci				err = USB_ERR_IOERROR;
3099f9f848faSopenharmony_ci				goto done;
3100f9f848faSopenharmony_ci			}
3101f9f848faSopenharmony_ci			len = sizeof(ehci_confd);
3102f9f848faSopenharmony_ci			ptr = (const void *)&ehci_confd;
3103f9f848faSopenharmony_ci			break;
3104f9f848faSopenharmony_ci
3105f9f848faSopenharmony_ci		case UDESC_STRING:
3106f9f848faSopenharmony_ci			switch (value & 0xff) {
3107f9f848faSopenharmony_ci			case 0:	/* Language table */
3108f9f848faSopenharmony_ci				str_ptr = "\001";
3109f9f848faSopenharmony_ci				break;
3110f9f848faSopenharmony_ci
3111f9f848faSopenharmony_ci			case 1:	/* Vendor */
3112f9f848faSopenharmony_ci				str_ptr = sc->sc_vendor;
3113f9f848faSopenharmony_ci				break;
3114f9f848faSopenharmony_ci
3115f9f848faSopenharmony_ci			case 2:	/* Product */
3116f9f848faSopenharmony_ci				str_ptr = "EHCI root HUB";
3117f9f848faSopenharmony_ci				break;
3118f9f848faSopenharmony_ci
3119f9f848faSopenharmony_ci			default:
3120f9f848faSopenharmony_ci				str_ptr = "";
3121f9f848faSopenharmony_ci				break;
3122f9f848faSopenharmony_ci			}
3123f9f848faSopenharmony_ci
3124f9f848faSopenharmony_ci			len = usb_make_str_desc(
3125f9f848faSopenharmony_ci			    sc->sc_hub_desc.temp,
3126f9f848faSopenharmony_ci			    sizeof(sc->sc_hub_desc.temp),
3127f9f848faSopenharmony_ci			    str_ptr);
3128f9f848faSopenharmony_ci			break;
3129f9f848faSopenharmony_ci		default:
3130f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3131f9f848faSopenharmony_ci			goto done;
3132f9f848faSopenharmony_ci		}
3133f9f848faSopenharmony_ci		break;
3134f9f848faSopenharmony_ci	case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
3135f9f848faSopenharmony_ci		len = 1;
3136f9f848faSopenharmony_ci		sc->sc_hub_desc.temp[0] = 0;
3137f9f848faSopenharmony_ci		break;
3138f9f848faSopenharmony_ci	case C(UR_GET_STATUS, UT_READ_DEVICE):
3139f9f848faSopenharmony_ci		len = 2;
3140f9f848faSopenharmony_ci		USETW(sc->sc_hub_desc.stat.wStatus, UDS_SELF_POWERED);
3141f9f848faSopenharmony_ci		break;
3142f9f848faSopenharmony_ci	case C(UR_GET_STATUS, UT_READ_INTERFACE):
3143f9f848faSopenharmony_ci	case C(UR_GET_STATUS, UT_READ_ENDPOINT):
3144f9f848faSopenharmony_ci		len = 2;
3145f9f848faSopenharmony_ci		USETW(sc->sc_hub_desc.stat.wStatus, 0);
3146f9f848faSopenharmony_ci		break;
3147f9f848faSopenharmony_ci	case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
3148f9f848faSopenharmony_ci		if (value >= EHCI_MAX_DEVICES) {
3149f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3150f9f848faSopenharmony_ci			goto done;
3151f9f848faSopenharmony_ci		}
3152f9f848faSopenharmony_ci		sc->sc_addr = value;
3153f9f848faSopenharmony_ci		break;
3154f9f848faSopenharmony_ci	case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
3155f9f848faSopenharmony_ci		if ((value != 0) && (value != 1)) {
3156f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3157f9f848faSopenharmony_ci			goto done;
3158f9f848faSopenharmony_ci		}
3159f9f848faSopenharmony_ci		sc->sc_conf = value;
3160f9f848faSopenharmony_ci		break;
3161f9f848faSopenharmony_ci	case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
3162f9f848faSopenharmony_ci		break;
3163f9f848faSopenharmony_ci	case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
3164f9f848faSopenharmony_ci	case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
3165f9f848faSopenharmony_ci	case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
3166f9f848faSopenharmony_ci		err = USB_ERR_IOERROR;
3167f9f848faSopenharmony_ci		goto done;
3168f9f848faSopenharmony_ci	case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
3169f9f848faSopenharmony_ci		break;
3170f9f848faSopenharmony_ci	case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
3171f9f848faSopenharmony_ci		break;
3172f9f848faSopenharmony_ci		/* Hub requests */
3173f9f848faSopenharmony_ci	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE):
3174f9f848faSopenharmony_ci		break;
3175f9f848faSopenharmony_ci	case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER):
3176f9f848faSopenharmony_ci		DPRINTFN(9, "UR_CLEAR_PORT_FEATURE\n");
3177f9f848faSopenharmony_ci
3178f9f848faSopenharmony_ci		if ((index < 1) ||
3179f9f848faSopenharmony_ci		    (index > sc->sc_noport)) {
3180f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3181f9f848faSopenharmony_ci			goto done;
3182f9f848faSopenharmony_ci		}
3183f9f848faSopenharmony_ci		port = EHCI_PORTSC(index);
3184f9f848faSopenharmony_ci		v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR;
3185f9f848faSopenharmony_ci		switch (value) {
3186f9f848faSopenharmony_ci		case UHF_PORT_ENABLE:
3187f9f848faSopenharmony_ci			EOWRITE4(sc, port, v & ~EHCI_PS_PE);
3188f9f848faSopenharmony_ci			break;
3189f9f848faSopenharmony_ci		case UHF_PORT_SUSPEND:
3190f9f848faSopenharmony_ci			if ((v & EHCI_PS_SUSP) && (!(v & EHCI_PS_FPR))) {
3191f9f848faSopenharmony_ci				/*
3192f9f848faSopenharmony_ci				 * waking up a High Speed device is rather
3193f9f848faSopenharmony_ci				 * complicated if
3194f9f848faSopenharmony_ci				 */
3195f9f848faSopenharmony_ci				EOWRITE4(sc, port, v | EHCI_PS_FPR);
3196f9f848faSopenharmony_ci			}
3197f9f848faSopenharmony_ci			/* wait 20ms for resume sequence to complete */
3198f9f848faSopenharmony_ci			usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 50);
3199f9f848faSopenharmony_ci
3200f9f848faSopenharmony_ci			EOWRITE4(sc, port, v & ~(EHCI_PS_SUSP |
3201f9f848faSopenharmony_ci			    EHCI_PS_FPR | (3 << 10) /* High Speed */ ));
3202f9f848faSopenharmony_ci
3203f9f848faSopenharmony_ci			/* 4ms settle time */
3204f9f848faSopenharmony_ci			usb_pause_mtx(&sc->sc_bus.bus_mtx, hz / 250);
3205f9f848faSopenharmony_ci			break;
3206f9f848faSopenharmony_ci		case UHF_PORT_POWER:
3207f9f848faSopenharmony_ci			EOWRITE4(sc, port, v & ~EHCI_PS_PP);
3208f9f848faSopenharmony_ci			break;
3209f9f848faSopenharmony_ci		case UHF_PORT_TEST:
3210f9f848faSopenharmony_ci			DPRINTFN(3, "clear port test "
3211f9f848faSopenharmony_ci			    "%d\n", index);
3212f9f848faSopenharmony_ci			break;
3213f9f848faSopenharmony_ci		case UHF_PORT_INDICATOR:
3214f9f848faSopenharmony_ci			DPRINTFN(3, "clear port ind "
3215f9f848faSopenharmony_ci			    "%d\n", index);
3216f9f848faSopenharmony_ci			EOWRITE4(sc, port, v & ~EHCI_PS_PIC);
3217f9f848faSopenharmony_ci			break;
3218f9f848faSopenharmony_ci		case UHF_C_PORT_CONNECTION:
3219f9f848faSopenharmony_ci			EOWRITE4(sc, port, v | EHCI_PS_CSC);
3220f9f848faSopenharmony_ci			break;
3221f9f848faSopenharmony_ci		case UHF_C_PORT_ENABLE:
3222f9f848faSopenharmony_ci			EOWRITE4(sc, port, v | EHCI_PS_PEC);
3223f9f848faSopenharmony_ci			break;
3224f9f848faSopenharmony_ci		case UHF_C_PORT_SUSPEND:
3225f9f848faSopenharmony_ci			EOWRITE4(sc, port, v | EHCI_PS_SUSP);
3226f9f848faSopenharmony_ci			break;
3227f9f848faSopenharmony_ci		case UHF_C_PORT_OVER_CURRENT:
3228f9f848faSopenharmony_ci			EOWRITE4(sc, port, v | EHCI_PS_OCC);
3229f9f848faSopenharmony_ci			break;
3230f9f848faSopenharmony_ci		case UHF_C_PORT_RESET:
3231f9f848faSopenharmony_ci			sc->sc_isreset = 0;
3232f9f848faSopenharmony_ci			break;
3233f9f848faSopenharmony_ci		default:
3234f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3235f9f848faSopenharmony_ci			goto done;
3236f9f848faSopenharmony_ci		}
3237f9f848faSopenharmony_ci		break;
3238f9f848faSopenharmony_ci	case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
3239f9f848faSopenharmony_ci		if ((value & 0xff) != 0) {
3240f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3241f9f848faSopenharmony_ci			goto done;
3242f9f848faSopenharmony_ci		}
3243f9f848faSopenharmony_ci		v = EREAD4(sc, EHCI_HCSPARAMS);
3244f9f848faSopenharmony_ci
3245f9f848faSopenharmony_ci		sc->sc_hub_desc.hubd = ehci_hubd;
3246f9f848faSopenharmony_ci		sc->sc_hub_desc.hubd.bNbrPorts = sc->sc_noport;
3247f9f848faSopenharmony_ci
3248f9f848faSopenharmony_ci		if (EHCI_HCS_PPC(v))
3249f9f848faSopenharmony_ci			i = UHD_PWR_INDIVIDUAL;
3250f9f848faSopenharmony_ci		else
3251f9f848faSopenharmony_ci			i = UHD_PWR_NO_SWITCH;
3252f9f848faSopenharmony_ci
3253f9f848faSopenharmony_ci		if (EHCI_HCS_P_INDICATOR(v))
3254f9f848faSopenharmony_ci			i |= UHD_PORT_IND;
3255f9f848faSopenharmony_ci
3256f9f848faSopenharmony_ci		USETW(sc->sc_hub_desc.hubd.wHubCharacteristics, i);
3257f9f848faSopenharmony_ci		/* XXX can't find out? */
3258f9f848faSopenharmony_ci		sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200;
3259f9f848faSopenharmony_ci		/* XXX don't know if ports are removable or not */
3260f9f848faSopenharmony_ci		sc->sc_hub_desc.hubd.bDescLength =
3261f9f848faSopenharmony_ci		    8 + ((sc->sc_noport + 7) / 8);
3262f9f848faSopenharmony_ci		len = sc->sc_hub_desc.hubd.bDescLength;
3263f9f848faSopenharmony_ci		break;
3264f9f848faSopenharmony_ci	case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE):
3265f9f848faSopenharmony_ci		len = 16;
3266f9f848faSopenharmony_ci		(void)memset_s(sc->sc_hub_desc.temp, sizeof(sc->sc_hub_desc.temp), 0, len);
3267f9f848faSopenharmony_ci		break;
3268f9f848faSopenharmony_ci	case C(UR_GET_STATUS, UT_READ_CLASS_OTHER):
3269f9f848faSopenharmony_ci		DPRINTFN(9, "get port status i=%d\n",
3270f9f848faSopenharmony_ci		    index);
3271f9f848faSopenharmony_ci		if ((index < 1) ||
3272f9f848faSopenharmony_ci		    (index > sc->sc_noport)) {
3273f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3274f9f848faSopenharmony_ci			goto done;
3275f9f848faSopenharmony_ci		}
3276f9f848faSopenharmony_ci		v = EOREAD4(sc, EHCI_PORTSC(index));
3277f9f848faSopenharmony_ci		DPRINTFN(1, "port status=0x%04x\n", v);
3278f9f848faSopenharmony_ci		if (sc->sc_flags & EHCI_SCFLG_TT) {
3279f9f848faSopenharmony_ci			if (sc->sc_vendor_get_port_speed != NULL) {
3280f9f848faSopenharmony_ci				i = sc->sc_vendor_get_port_speed(sc, index);
3281f9f848faSopenharmony_ci			} else {
3282f9f848faSopenharmony_ci				device_printf(sc->sc_bus.bdev,
3283f9f848faSopenharmony_ci				    "EHCI_SCFLG_TT quirk is set but "
3284f9f848faSopenharmony_ci				    "sc_vendor_get_hub_speed() is NULL\n");
3285f9f848faSopenharmony_ci				i = UPS_HIGH_SPEED;
3286f9f848faSopenharmony_ci			}
3287f9f848faSopenharmony_ci		} else {
3288f9f848faSopenharmony_ci			i = UPS_HIGH_SPEED;
3289f9f848faSopenharmony_ci		}
3290f9f848faSopenharmony_ci		if (v & EHCI_PS_CS)
3291f9f848faSopenharmony_ci			i |= UPS_CURRENT_CONNECT_STATUS;
3292f9f848faSopenharmony_ci		if (v & EHCI_PS_PE)
3293f9f848faSopenharmony_ci			i |= UPS_PORT_ENABLED;
3294f9f848faSopenharmony_ci		if ((v & EHCI_PS_SUSP) && !(v & EHCI_PS_FPR))
3295f9f848faSopenharmony_ci			i |= UPS_SUSPEND;
3296f9f848faSopenharmony_ci		if (v & EHCI_PS_OCA)
3297f9f848faSopenharmony_ci			i |= UPS_OVERCURRENT_INDICATOR;
3298f9f848faSopenharmony_ci		if (v & EHCI_PS_PR)
3299f9f848faSopenharmony_ci			i |= UPS_RESET;
3300f9f848faSopenharmony_ci		if (v & EHCI_PS_PP)
3301f9f848faSopenharmony_ci			i |= UPS_PORT_POWER;
3302f9f848faSopenharmony_ci		USETW(sc->sc_hub_desc.ps.wPortStatus, i);
3303f9f848faSopenharmony_ci		i = 0;
3304f9f848faSopenharmony_ci		if (v & EHCI_PS_CSC)
3305f9f848faSopenharmony_ci			i |= UPS_C_CONNECT_STATUS;
3306f9f848faSopenharmony_ci		if (v & EHCI_PS_PEC)
3307f9f848faSopenharmony_ci			i |= UPS_C_PORT_ENABLED;
3308f9f848faSopenharmony_ci		if (v & EHCI_PS_OCC)
3309f9f848faSopenharmony_ci			i |= UPS_C_OVERCURRENT_INDICATOR;
3310f9f848faSopenharmony_ci		if (v & EHCI_PS_FPR)
3311f9f848faSopenharmony_ci			i |= UPS_C_SUSPEND;
3312f9f848faSopenharmony_ci		if (sc->sc_isreset)
3313f9f848faSopenharmony_ci			i |= UPS_C_PORT_RESET;
3314f9f848faSopenharmony_ci		USETW(sc->sc_hub_desc.ps.wPortChange, i);
3315f9f848faSopenharmony_ci		len = sizeof(sc->sc_hub_desc.ps);
3316f9f848faSopenharmony_ci		break;
3317f9f848faSopenharmony_ci	case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE):
3318f9f848faSopenharmony_ci		err = USB_ERR_IOERROR;
3319f9f848faSopenharmony_ci		goto done;
3320f9f848faSopenharmony_ci	case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE):
3321f9f848faSopenharmony_ci		break;
3322f9f848faSopenharmony_ci	case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER):
3323f9f848faSopenharmony_ci		if ((index < 1) ||
3324f9f848faSopenharmony_ci		    (index > sc->sc_noport)) {
3325f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3326f9f848faSopenharmony_ci			goto done;
3327f9f848faSopenharmony_ci		}
3328f9f848faSopenharmony_ci		port = EHCI_PORTSC(index);
3329f9f848faSopenharmony_ci		v = EOREAD4(sc, port) & ~EHCI_PS_CLEAR;
3330f9f848faSopenharmony_ci		switch (value) {
3331f9f848faSopenharmony_ci		case UHF_PORT_ENABLE:
3332f9f848faSopenharmony_ci			EOWRITE4(sc, port, v | EHCI_PS_PE);
3333f9f848faSopenharmony_ci			break;
3334f9f848faSopenharmony_ci		case UHF_PORT_SUSPEND:
3335f9f848faSopenharmony_ci			EOWRITE4(sc, port, v | EHCI_PS_SUSP);
3336f9f848faSopenharmony_ci			break;
3337f9f848faSopenharmony_ci		case UHF_PORT_RESET:
3338f9f848faSopenharmony_ci			DPRINTFN(6, "reset port %d\n", index);
3339f9f848faSopenharmony_ci			if (EHCI_PS_IS_LOWSPEED(v) &&
3340f9f848faSopenharmony_ci			    (sc->sc_flags & EHCI_SCFLG_TT) == 0) {
3341f9f848faSopenharmony_ci				/* Low speed device, give up ownership. */
3342f9f848faSopenharmony_ci				DPRINTFN(6, "Low speed device is not support!!!!\n");
3343f9f848faSopenharmony_ci				err = USB_ERR_INVAL;
3344f9f848faSopenharmony_ci				goto done;
3345f9f848faSopenharmony_ci			}
3346f9f848faSopenharmony_ci			/* Start reset sequence. */
3347f9f848faSopenharmony_ci			v &= ~(EHCI_PS_PE | EHCI_PS_PR);
3348f9f848faSopenharmony_ci			EOWRITE4(sc, port, v | EHCI_PS_PR);
3349f9f848faSopenharmony_ci
3350f9f848faSopenharmony_ci			/* Wait for reset to complete. */
3351f9f848faSopenharmony_ci			usb_pause_mtx(&sc->sc_bus.bus_mtx,
3352f9f848faSopenharmony_ci			    USB_MS_TO_TICKS(usb_port_root_reset_delay));
3353f9f848faSopenharmony_ci
3354f9f848faSopenharmony_ci			/* Terminate reset sequence. */
3355f9f848faSopenharmony_ci			if (!(sc->sc_flags & EHCI_SCFLG_NORESTERM))
3356f9f848faSopenharmony_ci				EOWRITE4(sc, port, v);
3357f9f848faSopenharmony_ci
3358f9f848faSopenharmony_ci			/* Wait for HC to complete reset. */
3359f9f848faSopenharmony_ci			usb_pause_mtx(&sc->sc_bus.bus_mtx,
3360f9f848faSopenharmony_ci			    USB_MS_TO_TICKS(EHCI_PORT_RESET_COMPLETE));
3361f9f848faSopenharmony_ci
3362f9f848faSopenharmony_ci			v = EOREAD4(sc, port);
3363f9f848faSopenharmony_ci			DPRINTF("ehci after reset, status=0x%08x\n", v);
3364f9f848faSopenharmony_ci			if (v & EHCI_PS_PR) {
3365f9f848faSopenharmony_ci				device_printf(sc->sc_bus.bdev,
3366f9f848faSopenharmony_ci				    "port reset timeout\n");
3367f9f848faSopenharmony_ci				err = USB_ERR_TIMEOUT;
3368f9f848faSopenharmony_ci				goto done;
3369f9f848faSopenharmony_ci			}
3370f9f848faSopenharmony_ci			if (!(v & EHCI_PS_PE) &&
3371f9f848faSopenharmony_ci			    (sc->sc_flags & EHCI_SCFLG_TT) == 0) {
3372f9f848faSopenharmony_ci				/* Not a high speed device, give up ownership.*/
3373f9f848faSopenharmony_ci				DPRINTFN(6, "Full speed device is not support!!!!\n");
3374f9f848faSopenharmony_ci				err = USB_ERR_INVAL;
3375f9f848faSopenharmony_ci				goto done;
3376f9f848faSopenharmony_ci			}
3377f9f848faSopenharmony_ci			sc->sc_isreset = 1;
3378f9f848faSopenharmony_ci			DPRINTF("ehci port %d reset, status = 0x%08x\n",
3379f9f848faSopenharmony_ci			    index, v);
3380f9f848faSopenharmony_ci			break;
3381f9f848faSopenharmony_ci
3382f9f848faSopenharmony_ci		case UHF_PORT_POWER:
3383f9f848faSopenharmony_ci			DPRINTFN(3, "set port power %d\n", index);
3384f9f848faSopenharmony_ci			EOWRITE4(sc, port, v | EHCI_PS_PP);
3385f9f848faSopenharmony_ci			break;
3386f9f848faSopenharmony_ci
3387f9f848faSopenharmony_ci		case UHF_PORT_TEST:
3388f9f848faSopenharmony_ci			DPRINTFN(3, "set port test %d\n", index);
3389f9f848faSopenharmony_ci			break;
3390f9f848faSopenharmony_ci
3391f9f848faSopenharmony_ci		case UHF_PORT_INDICATOR:
3392f9f848faSopenharmony_ci			DPRINTFN(3, "set port ind %d\n", index);
3393f9f848faSopenharmony_ci			EOWRITE4(sc, port, v | EHCI_PS_PIC);
3394f9f848faSopenharmony_ci			break;
3395f9f848faSopenharmony_ci
3396f9f848faSopenharmony_ci		default:
3397f9f848faSopenharmony_ci			err = USB_ERR_IOERROR;
3398f9f848faSopenharmony_ci			goto done;
3399f9f848faSopenharmony_ci		}
3400f9f848faSopenharmony_ci		break;
3401f9f848faSopenharmony_ci	case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER):
3402f9f848faSopenharmony_ci	case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER):
3403f9f848faSopenharmony_ci	case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER):
3404f9f848faSopenharmony_ci	case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER):
3405f9f848faSopenharmony_ci		break;
3406f9f848faSopenharmony_ci	default:
3407f9f848faSopenharmony_ci		err = USB_ERR_IOERROR;
3408f9f848faSopenharmony_ci		goto done;
3409f9f848faSopenharmony_ci	}
3410f9f848faSopenharmony_cidone:
3411f9f848faSopenharmony_ci	*plength = len;
3412f9f848faSopenharmony_ci	*pptr = ptr;
3413f9f848faSopenharmony_ci	return (err);
3414f9f848faSopenharmony_ci}
3415f9f848faSopenharmony_ci
3416f9f848faSopenharmony_cistatic void
3417f9f848faSopenharmony_ciehci_xfer_setup(struct usb_setup_params *parm)
3418f9f848faSopenharmony_ci{
3419f9f848faSopenharmony_ci	struct usb_page_search page_info;
3420f9f848faSopenharmony_ci	struct usb_page_cache *pc;
3421f9f848faSopenharmony_ci	ehci_softc_t *sc;
3422f9f848faSopenharmony_ci	struct usb_xfer *xfer;
3423f9f848faSopenharmony_ci	void *last_obj;
3424f9f848faSopenharmony_ci	uint32_t nqtd;
3425f9f848faSopenharmony_ci	uint32_t nqh;
3426f9f848faSopenharmony_ci	uint32_t nsitd;
3427f9f848faSopenharmony_ci	uint32_t nitd;
3428f9f848faSopenharmony_ci	uint32_t n;
3429f9f848faSopenharmony_ci
3430f9f848faSopenharmony_ci	sc = EHCI_BUS2SC(parm->udev->bus);
3431f9f848faSopenharmony_ci	xfer = parm->curr_xfer;
3432f9f848faSopenharmony_ci
3433f9f848faSopenharmony_ci	nqtd = 0;
3434f9f848faSopenharmony_ci	nqh = 0;
3435f9f848faSopenharmony_ci	nsitd = 0;
3436f9f848faSopenharmony_ci	nitd = 0;
3437f9f848faSopenharmony_ci
3438f9f848faSopenharmony_ci	/*
3439f9f848faSopenharmony_ci	 * compute maximum number of some structures
3440f9f848faSopenharmony_ci	 */
3441f9f848faSopenharmony_ci	if (parm->methods == &ehci_device_ctrl_methods) {
3442f9f848faSopenharmony_ci		/*
3443f9f848faSopenharmony_ci		 * The proof for the "nqtd" formula is illustrated like
3444f9f848faSopenharmony_ci		 * this:
3445f9f848faSopenharmony_ci		 *
3446f9f848faSopenharmony_ci		 * +------------------------------------+
3447f9f848faSopenharmony_ci		 * |                                    |
3448f9f848faSopenharmony_ci		 * |         |remainder ->              |
3449f9f848faSopenharmony_ci		 * |   +-----+---+                      |
3450f9f848faSopenharmony_ci		 * |   | xxx | x | frm 0                |
3451f9f848faSopenharmony_ci		 * |   +-----+---++                     |
3452f9f848faSopenharmony_ci		 * |   | xxx | xx | frm 1               |
3453f9f848faSopenharmony_ci		 * |   +-----+----+                     |
3454f9f848faSopenharmony_ci		 * |            ...                     |
3455f9f848faSopenharmony_ci		 * +------------------------------------+
3456f9f848faSopenharmony_ci		 *
3457f9f848faSopenharmony_ci		 * "xxx" means a completely full USB transfer descriptor
3458f9f848faSopenharmony_ci		 *
3459f9f848faSopenharmony_ci		 * "x" and "xx" means a short USB packet
3460f9f848faSopenharmony_ci		 *
3461f9f848faSopenharmony_ci		 * For the remainder of an USB transfer modulo
3462f9f848faSopenharmony_ci		 * "max_data_length" we need two USB transfer descriptors.
3463f9f848faSopenharmony_ci		 * One to transfer the remaining data and one to finalise
3464f9f848faSopenharmony_ci		 * with a zero length packet in case the "force_short_xfer"
3465f9f848faSopenharmony_ci		 * flag is set. We only need two USB transfer descriptors in
3466f9f848faSopenharmony_ci		 * the case where the transfer length of the first one is a
3467f9f848faSopenharmony_ci		 * factor of "max_frame_size". The rest of the needed USB
3468f9f848faSopenharmony_ci		 * transfer descriptors is given by the buffer size divided
3469f9f848faSopenharmony_ci		 * by the maximum data payload.
3470f9f848faSopenharmony_ci		 */
3471f9f848faSopenharmony_ci		parm->hc_max_packet_size = 0x400;
3472f9f848faSopenharmony_ci		parm->hc_max_packet_count = 1;
3473f9f848faSopenharmony_ci		parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX;
3474f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
3475f9f848faSopenharmony_ci		xfer->flags_int.bdma_enable = 1;
3476f9f848faSopenharmony_ci#endif
3477f9f848faSopenharmony_ci		usbd_transfer_setup_sub(parm);
3478f9f848faSopenharmony_ci
3479f9f848faSopenharmony_ci		nqh = 1;
3480f9f848faSopenharmony_ci		nqtd = ((2 * xfer->nframes) + 1	/* STATUS */
3481f9f848faSopenharmony_ci		    + (xfer->max_data_length / xfer->max_hc_frame_size));
3482f9f848faSopenharmony_ci
3483f9f848faSopenharmony_ci	} else if (parm->methods == &ehci_device_bulk_methods) {
3484f9f848faSopenharmony_ci		parm->hc_max_packet_size = 0x400;
3485f9f848faSopenharmony_ci		parm->hc_max_packet_count = 1;
3486f9f848faSopenharmony_ci		parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX;
3487f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
3488f9f848faSopenharmony_ci		xfer->flags_int.bdma_enable = 1;
3489f9f848faSopenharmony_ci#endif
3490f9f848faSopenharmony_ci
3491f9f848faSopenharmony_ci		usbd_transfer_setup_sub(parm);
3492f9f848faSopenharmony_ci
3493f9f848faSopenharmony_ci		nqh = 1;
3494f9f848faSopenharmony_ci		nqtd = ((2 * xfer->nframes)
3495f9f848faSopenharmony_ci		    + (xfer->max_data_length / xfer->max_hc_frame_size));
3496f9f848faSopenharmony_ci
3497f9f848faSopenharmony_ci	} else if (parm->methods == &ehci_device_intr_methods) {
3498f9f848faSopenharmony_ci		if (parm->speed == USB_SPEED_HIGH) {
3499f9f848faSopenharmony_ci			parm->hc_max_packet_size = 0x400;
3500f9f848faSopenharmony_ci			parm->hc_max_packet_count = 3;
3501f9f848faSopenharmony_ci		} else if (parm->speed == USB_SPEED_FULL) {
3502f9f848faSopenharmony_ci			parm->hc_max_packet_size = USB_FS_BYTES_PER_HS_UFRAME;
3503f9f848faSopenharmony_ci			parm->hc_max_packet_count = 1;
3504f9f848faSopenharmony_ci		} else {
3505f9f848faSopenharmony_ci			parm->hc_max_packet_size = USB_FS_BYTES_PER_HS_UFRAME / 8;
3506f9f848faSopenharmony_ci			parm->hc_max_packet_count = 1;
3507f9f848faSopenharmony_ci		}
3508f9f848faSopenharmony_ci
3509f9f848faSopenharmony_ci		parm->hc_max_frame_size = EHCI_QTD_PAYLOAD_MAX;
3510f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
3511f9f848faSopenharmony_ci		xfer->flags_int.bdma_enable = 1;
3512f9f848faSopenharmony_ci#endif
3513f9f848faSopenharmony_ci
3514f9f848faSopenharmony_ci		usbd_transfer_setup_sub(parm);
3515f9f848faSopenharmony_ci
3516f9f848faSopenharmony_ci		nqh = 1;
3517f9f848faSopenharmony_ci		nqtd = ((2 * xfer->nframes)
3518f9f848faSopenharmony_ci		    + (xfer->max_data_length / xfer->max_hc_frame_size));
3519f9f848faSopenharmony_ci
3520f9f848faSopenharmony_ci	} else if (parm->methods == &ehci_device_isoc_fs_methods) {
3521f9f848faSopenharmony_ci		parm->hc_max_packet_size = 0x3FF;
3522f9f848faSopenharmony_ci		parm->hc_max_packet_count = 1;
3523f9f848faSopenharmony_ci		parm->hc_max_frame_size = 0x3FF;
3524f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
3525f9f848faSopenharmony_ci		xfer->flags_int.bdma_enable = 1;
3526f9f848faSopenharmony_ci#endif
3527f9f848faSopenharmony_ci
3528f9f848faSopenharmony_ci		usbd_transfer_setup_sub(parm);
3529f9f848faSopenharmony_ci
3530f9f848faSopenharmony_ci		nsitd = xfer->nframes;
3531f9f848faSopenharmony_ci
3532f9f848faSopenharmony_ci	} else if (parm->methods == &ehci_device_isoc_hs_methods) {
3533f9f848faSopenharmony_ci		parm->hc_max_packet_size = 0x400;
3534f9f848faSopenharmony_ci		parm->hc_max_packet_count = 3;
3535f9f848faSopenharmony_ci		parm->hc_max_frame_size = 0xC00;
3536f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
3537f9f848faSopenharmony_ci		xfer->flags_int.bdma_enable = 1;
3538f9f848faSopenharmony_ci#endif
3539f9f848faSopenharmony_ci
3540f9f848faSopenharmony_ci		usbd_transfer_setup_sub(parm);
3541f9f848faSopenharmony_ci
3542f9f848faSopenharmony_ci		nitd = ((xfer->nframes + 7) / 8) <<
3543f9f848faSopenharmony_ci		    usbd_xfer_get_fps_shift(xfer);
3544f9f848faSopenharmony_ci
3545f9f848faSopenharmony_ci	} else {
3546f9f848faSopenharmony_ci		parm->hc_max_packet_size = 0x400;
3547f9f848faSopenharmony_ci		parm->hc_max_packet_count = 1;
3548f9f848faSopenharmony_ci		parm->hc_max_frame_size = 0x400;
3549f9f848faSopenharmony_ci
3550f9f848faSopenharmony_ci		usbd_transfer_setup_sub(parm);
3551f9f848faSopenharmony_ci	}
3552f9f848faSopenharmony_ci
3553f9f848faSopenharmony_cialloc_dma_set:
3554f9f848faSopenharmony_ci
3555f9f848faSopenharmony_ci	if (parm->err) {
3556f9f848faSopenharmony_ci		return;
3557f9f848faSopenharmony_ci	}
3558f9f848faSopenharmony_ci	/*
3559f9f848faSopenharmony_ci	 * Allocate queue heads and transfer descriptors
3560f9f848faSopenharmony_ci	 */
3561f9f848faSopenharmony_ci	last_obj = NULL;
3562f9f848faSopenharmony_ci
3563f9f848faSopenharmony_ci	if (usbd_transfer_setup_sub_malloc(
3564f9f848faSopenharmony_ci	    parm, &pc, sizeof(ehci_itd_t),
3565f9f848faSopenharmony_ci	    EHCI_ITD_ALIGN, nitd)) {
3566f9f848faSopenharmony_ci		parm->err = USB_ERR_NOMEM;
3567f9f848faSopenharmony_ci		return;
3568f9f848faSopenharmony_ci	}
3569f9f848faSopenharmony_ci	if (parm->buf) {
3570f9f848faSopenharmony_ci		for (n = 0; n != nitd; n++) {
3571f9f848faSopenharmony_ci			ehci_itd_t *td;
3572f9f848faSopenharmony_ci
3573f9f848faSopenharmony_ci			usbd_get_page(pc + n, 0, &page_info);
3574f9f848faSopenharmony_ci
3575f9f848faSopenharmony_ci			td = (ehci_itd_t *)page_info.buffer;
3576f9f848faSopenharmony_ci
3577f9f848faSopenharmony_ci			/* init TD */
3578f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
3579f9f848faSopenharmony_ci			td->itd_self = htohc32(sc, page_info.physaddr | EHCI_LINK_ITD);
3580f9f848faSopenharmony_ci#else
3581f9f848faSopenharmony_ci			td->itd_self = htohc32(sc, (unsigned int)page_info.buffer | EHCI_LINK_ITD);
3582f9f848faSopenharmony_ci#endif
3583f9f848faSopenharmony_ci			td->obj_next = (ehci_itd_t *)last_obj;
3584f9f848faSopenharmony_ci			td->page_cache = pc + n;
3585f9f848faSopenharmony_ci
3586f9f848faSopenharmony_ci			last_obj = td;
3587f9f848faSopenharmony_ci
3588f9f848faSopenharmony_ci			usb_pc_cpu_flush(pc + n);
3589f9f848faSopenharmony_ci		}
3590f9f848faSopenharmony_ci	}
3591f9f848faSopenharmony_ci	if (usbd_transfer_setup_sub_malloc(
3592f9f848faSopenharmony_ci	    parm, &pc, sizeof(ehci_sitd_t),
3593f9f848faSopenharmony_ci	    EHCI_SITD_ALIGN, nsitd)) {
3594f9f848faSopenharmony_ci		parm->err = USB_ERR_NOMEM;
3595f9f848faSopenharmony_ci		return;
3596f9f848faSopenharmony_ci	}
3597f9f848faSopenharmony_ci	if (parm->buf) {
3598f9f848faSopenharmony_ci		for (n = 0; n != nsitd; n++) {
3599f9f848faSopenharmony_ci			ehci_sitd_t *td;
3600f9f848faSopenharmony_ci
3601f9f848faSopenharmony_ci			usbd_get_page(pc + n, 0, &page_info);
3602f9f848faSopenharmony_ci
3603f9f848faSopenharmony_ci			td = (ehci_sitd_t *)page_info.buffer;
3604f9f848faSopenharmony_ci
3605f9f848faSopenharmony_ci			/* init TD */
3606f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
3607f9f848faSopenharmony_ci			td->sitd_self = htohc32(sc, page_info.physaddr | EHCI_LINK_SITD);
3608f9f848faSopenharmony_ci#else
3609f9f848faSopenharmony_ci			td->sitd_self = htohc32(sc, (unsigned int)page_info.buffer | EHCI_LINK_SITD);
3610f9f848faSopenharmony_ci#endif
3611f9f848faSopenharmony_ci			td->obj_next = (ehci_sitd_t *)last_obj;
3612f9f848faSopenharmony_ci			td->page_cache = pc + n;
3613f9f848faSopenharmony_ci
3614f9f848faSopenharmony_ci			last_obj = td;
3615f9f848faSopenharmony_ci
3616f9f848faSopenharmony_ci			usb_pc_cpu_flush(pc + n);
3617f9f848faSopenharmony_ci		}
3618f9f848faSopenharmony_ci	}
3619f9f848faSopenharmony_ci	if (usbd_transfer_setup_sub_malloc(
3620f9f848faSopenharmony_ci	    parm, &pc, sizeof(ehci_qtd_t),
3621f9f848faSopenharmony_ci	    EHCI_QTD_ALIGN, nqtd)) {
3622f9f848faSopenharmony_ci		parm->err = USB_ERR_NOMEM;
3623f9f848faSopenharmony_ci		return;
3624f9f848faSopenharmony_ci	}
3625f9f848faSopenharmony_ci	if (parm->buf) {
3626f9f848faSopenharmony_ci		for (n = 0; n != nqtd; n++) {
3627f9f848faSopenharmony_ci			ehci_qtd_t *qtd;
3628f9f848faSopenharmony_ci
3629f9f848faSopenharmony_ci			usbd_get_page(pc + n, 0, &page_info);
3630f9f848faSopenharmony_ci
3631f9f848faSopenharmony_ci			qtd = (ehci_qtd_t *)page_info.buffer;
3632f9f848faSopenharmony_ci
3633f9f848faSopenharmony_ci			/* init TD */
3634f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
3635f9f848faSopenharmony_ci			qtd->qtd_self = htohc32(sc, page_info.physaddr);
3636f9f848faSopenharmony_ci#else
3637f9f848faSopenharmony_ci			qtd->qtd_self = htohc32(sc, (unsigned int)page_info.buffer);
3638f9f848faSopenharmony_ci#endif
3639f9f848faSopenharmony_ci			qtd->obj_next = (ehci_qtd_t *)last_obj;
3640f9f848faSopenharmony_ci			qtd->page_cache = pc + n;
3641f9f848faSopenharmony_ci
3642f9f848faSopenharmony_ci			last_obj = qtd;
3643f9f848faSopenharmony_ci
3644f9f848faSopenharmony_ci			usb_pc_cpu_flush(pc + n);
3645f9f848faSopenharmony_ci		}
3646f9f848faSopenharmony_ci	}
3647f9f848faSopenharmony_ci	xfer->td_start[xfer->flags_int.curr_dma_set] = last_obj;
3648f9f848faSopenharmony_ci
3649f9f848faSopenharmony_ci	last_obj = NULL;
3650f9f848faSopenharmony_ci
3651f9f848faSopenharmony_ci	if (usbd_transfer_setup_sub_malloc(
3652f9f848faSopenharmony_ci	    parm, &pc, sizeof(ehci_qh_t),
3653f9f848faSopenharmony_ci	    EHCI_QH_ALIGN, nqh)) {
3654f9f848faSopenharmony_ci		parm->err = USB_ERR_NOMEM;
3655f9f848faSopenharmony_ci		return;
3656f9f848faSopenharmony_ci	}
3657f9f848faSopenharmony_ci	if (parm->buf) {
3658f9f848faSopenharmony_ci		for (n = 0; n != nqh; n++) {
3659f9f848faSopenharmony_ci			ehci_qh_t *qh;
3660f9f848faSopenharmony_ci
3661f9f848faSopenharmony_ci			usbd_get_page(pc + n, 0, &page_info);
3662f9f848faSopenharmony_ci
3663f9f848faSopenharmony_ci			qh = (ehci_qh_t *)page_info.buffer;
3664f9f848faSopenharmony_ci
3665f9f848faSopenharmony_ci			/* init QH */
3666f9f848faSopenharmony_ci#if USB_HAVE_BUSDMA
3667f9f848faSopenharmony_ci			qh->qh_self = htohc32(sc, page_info.physaddr | EHCI_LINK_QH);
3668f9f848faSopenharmony_ci#else
3669f9f848faSopenharmony_ci			qh->qh_self = htohc32(sc, (unsigned int)page_info.buffer | EHCI_LINK_QH);
3670f9f848faSopenharmony_ci#endif
3671f9f848faSopenharmony_ci			qh->obj_next = (ehci_qh_t *)last_obj;
3672f9f848faSopenharmony_ci			qh->page_cache = pc + n;
3673f9f848faSopenharmony_ci
3674f9f848faSopenharmony_ci			last_obj = qh;
3675f9f848faSopenharmony_ci
3676f9f848faSopenharmony_ci			usb_pc_cpu_flush(pc + n);
3677f9f848faSopenharmony_ci		}
3678f9f848faSopenharmony_ci	}
3679f9f848faSopenharmony_ci	xfer->qh_start[xfer->flags_int.curr_dma_set] = last_obj;
3680f9f848faSopenharmony_ci
3681f9f848faSopenharmony_ci	if (!xfer->flags_int.curr_dma_set) {
3682f9f848faSopenharmony_ci		xfer->flags_int.curr_dma_set = 1;
3683f9f848faSopenharmony_ci		goto alloc_dma_set;
3684f9f848faSopenharmony_ci	}
3685f9f848faSopenharmony_ci}
3686f9f848faSopenharmony_ci
3687f9f848faSopenharmony_cistatic void
3688f9f848faSopenharmony_ciehci_xfer_unsetup(struct usb_xfer *xfer)
3689f9f848faSopenharmony_ci{
3690f9f848faSopenharmony_ci	return;
3691f9f848faSopenharmony_ci}
3692f9f848faSopenharmony_ci
3693f9f848faSopenharmony_cistatic void
3694f9f848faSopenharmony_ciehci_ep_init(struct usb_device *udev, struct usb_endpoint_descriptor *edesc,
3695f9f848faSopenharmony_ci    struct usb_endpoint *ep)
3696f9f848faSopenharmony_ci{
3697f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(udev->bus);
3698f9f848faSopenharmony_ci
3699f9f848faSopenharmony_ci	DPRINTFN(2, "endpoint=%p, addr=%d, endpt=%d, mode=%d (%d)\n",
3700f9f848faSopenharmony_ci	    ep, udev->address,
3701f9f848faSopenharmony_ci	    edesc->bEndpointAddress, udev->flags.usb_mode,
3702f9f848faSopenharmony_ci	    sc->sc_addr);
3703f9f848faSopenharmony_ci
3704f9f848faSopenharmony_ci	if (udev->device_index != sc->sc_addr) {
3705f9f848faSopenharmony_ci		if ((udev->speed != USB_SPEED_HIGH) &&
3706f9f848faSopenharmony_ci		    ((udev->hs_hub_addr == 0) ||
3707f9f848faSopenharmony_ci		    (udev->hs_port_no == 0) ||
3708f9f848faSopenharmony_ci		    (udev->parent_hs_hub == NULL) ||
3709f9f848faSopenharmony_ci		    (udev->parent_hs_hub->hub == NULL))) {
3710f9f848faSopenharmony_ci			/* We need a transaction translator */
3711f9f848faSopenharmony_ci			DPRINTFN(2, "no hub or no port\n");
3712f9f848faSopenharmony_ci			goto done;
3713f9f848faSopenharmony_ci		}
3714f9f848faSopenharmony_ci		switch (edesc->bmAttributes & UE_XFERTYPE) {
3715f9f848faSopenharmony_ci		case UE_CONTROL:
3716f9f848faSopenharmony_ci			ep->methods = &ehci_device_ctrl_methods;
3717f9f848faSopenharmony_ci			DPRINTFN(2, "UE_CONTROL\n");
3718f9f848faSopenharmony_ci			break;
3719f9f848faSopenharmony_ci		case UE_INTERRUPT:
3720f9f848faSopenharmony_ci			ep->methods = &ehci_device_intr_methods;
3721f9f848faSopenharmony_ci			DPRINTFN(2, "UE_INTERRUPT\n");
3722f9f848faSopenharmony_ci			break;
3723f9f848faSopenharmony_ci		case UE_ISOCHRONOUS:
3724f9f848faSopenharmony_ci			if (udev->speed == USB_SPEED_HIGH) {
3725f9f848faSopenharmony_ci				ep->methods = &ehci_device_isoc_hs_methods;
3726f9f848faSopenharmony_ci			} else if (udev->speed == USB_SPEED_FULL) {
3727f9f848faSopenharmony_ci				ep->methods = &ehci_device_isoc_fs_methods;
3728f9f848faSopenharmony_ci			}
3729f9f848faSopenharmony_ci			DPRINTFN(2, "UE_ISOCHRONOUS\n");
3730f9f848faSopenharmony_ci			break;
3731f9f848faSopenharmony_ci		case UE_BULK:
3732f9f848faSopenharmony_ci			ep->methods = &ehci_device_bulk_methods;
3733f9f848faSopenharmony_ci			DPRINTFN(2, "UE_BULK\n");
3734f9f848faSopenharmony_ci			break;
3735f9f848faSopenharmony_ci		default:
3736f9f848faSopenharmony_ci			/* do nothing */
3737f9f848faSopenharmony_ci			break;
3738f9f848faSopenharmony_ci		}
3739f9f848faSopenharmony_ci	}
3740f9f848faSopenharmony_cidone:
3741f9f848faSopenharmony_ci	return;
3742f9f848faSopenharmony_ci}
3743f9f848faSopenharmony_ci
3744f9f848faSopenharmony_cistatic void
3745f9f848faSopenharmony_ciehci_get_dma_delay(struct usb_device *udev, uint32_t *pus)
3746f9f848faSopenharmony_ci{
3747f9f848faSopenharmony_ci	/*
3748f9f848faSopenharmony_ci	 * Wait until the hardware has finished any possible use of
3749f9f848faSopenharmony_ci	 * the transfer descriptor(s) and QH
3750f9f848faSopenharmony_ci	 */
3751f9f848faSopenharmony_ci	*pus = (1125);			/* microseconds */
3752f9f848faSopenharmony_ci}
3753f9f848faSopenharmony_ci
3754f9f848faSopenharmony_cistatic void
3755f9f848faSopenharmony_ciehci_device_resume(struct usb_device *udev)
3756f9f848faSopenharmony_ci{
3757f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(udev->bus);
3758f9f848faSopenharmony_ci	struct usb_xfer *xfer;
3759f9f848faSopenharmony_ci	const struct usb_pipe_methods *methods;
3760f9f848faSopenharmony_ci
3761f9f848faSopenharmony_ci	DPRINTF("\n");
3762f9f848faSopenharmony_ci
3763f9f848faSopenharmony_ci	USB_BUS_LOCK(udev->bus);
3764f9f848faSopenharmony_ci
3765f9f848faSopenharmony_ci	TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
3766f9f848faSopenharmony_ci		if (xfer->xroot->udev == udev) {
3767f9f848faSopenharmony_ci			methods = xfer->endpoint->methods;
3768f9f848faSopenharmony_ci
3769f9f848faSopenharmony_ci			if ((methods == &ehci_device_bulk_methods) ||
3770f9f848faSopenharmony_ci			    (methods == &ehci_device_ctrl_methods)) {
3771f9f848faSopenharmony_ci				EHCI_APPEND_QH((ehci_qh_t *)xfer->qh_start[xfer->flags_int.curr_dma_set],
3772f9f848faSopenharmony_ci				    sc->sc_async_p_last);
3773f9f848faSopenharmony_ci			}
3774f9f848faSopenharmony_ci			if (methods == &ehci_device_intr_methods) {
3775f9f848faSopenharmony_ci				EHCI_APPEND_QH((ehci_qh_t *)xfer->qh_start[xfer->flags_int.curr_dma_set],
3776f9f848faSopenharmony_ci				    sc->sc_intr_p_last[xfer->qh_pos]);
3777f9f848faSopenharmony_ci			}
3778f9f848faSopenharmony_ci		}
3779f9f848faSopenharmony_ci	}
3780f9f848faSopenharmony_ci
3781f9f848faSopenharmony_ci	USB_BUS_UNLOCK(udev->bus);
3782f9f848faSopenharmony_ci
3783f9f848faSopenharmony_ci	return;
3784f9f848faSopenharmony_ci}
3785f9f848faSopenharmony_ci
3786f9f848faSopenharmony_cistatic void
3787f9f848faSopenharmony_ciehci_device_suspend(struct usb_device *udev)
3788f9f848faSopenharmony_ci{
3789f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(udev->bus);
3790f9f848faSopenharmony_ci	struct usb_xfer *xfer;
3791f9f848faSopenharmony_ci	const struct usb_pipe_methods *methods;
3792f9f848faSopenharmony_ci
3793f9f848faSopenharmony_ci	DPRINTF("\n");
3794f9f848faSopenharmony_ci
3795f9f848faSopenharmony_ci	USB_BUS_LOCK(udev->bus);
3796f9f848faSopenharmony_ci
3797f9f848faSopenharmony_ci	TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
3798f9f848faSopenharmony_ci
3799f9f848faSopenharmony_ci		if (xfer->xroot->udev == udev) {
3800f9f848faSopenharmony_ci
3801f9f848faSopenharmony_ci			methods = xfer->endpoint->methods;
3802f9f848faSopenharmony_ci
3803f9f848faSopenharmony_ci			if ((methods == &ehci_device_bulk_methods) ||
3804f9f848faSopenharmony_ci			    (methods == &ehci_device_ctrl_methods)) {
3805f9f848faSopenharmony_ci				EHCI_REMOVE_QH((ehci_qh_t *)xfer->qh_start[xfer->flags_int.curr_dma_set],
3806f9f848faSopenharmony_ci				    sc->sc_async_p_last);
3807f9f848faSopenharmony_ci			}
3808f9f848faSopenharmony_ci			if (methods == &ehci_device_intr_methods) {
3809f9f848faSopenharmony_ci				EHCI_REMOVE_QH((ehci_qh_t *)xfer->qh_start[xfer->flags_int.curr_dma_set],
3810f9f848faSopenharmony_ci				    sc->sc_intr_p_last[xfer->qh_pos]);
3811f9f848faSopenharmony_ci			}
3812f9f848faSopenharmony_ci		}
3813f9f848faSopenharmony_ci	}
3814f9f848faSopenharmony_ci
3815f9f848faSopenharmony_ci	USB_BUS_UNLOCK(udev->bus);
3816f9f848faSopenharmony_ci}
3817f9f848faSopenharmony_ci
3818f9f848faSopenharmony_cistatic void
3819f9f848faSopenharmony_ciehci_set_hw_power_sleep(struct usb_bus *bus, uint32_t state)
3820f9f848faSopenharmony_ci{
3821f9f848faSopenharmony_ci	struct ehci_softc *sc = EHCI_BUS2SC(bus);
3822f9f848faSopenharmony_ci
3823f9f848faSopenharmony_ci	switch (state) {
3824f9f848faSopenharmony_ci	case USB_HW_POWER_SUSPEND:
3825f9f848faSopenharmony_ci	case USB_HW_POWER_SHUTDOWN:
3826f9f848faSopenharmony_ci		ehci_suspend(sc);
3827f9f848faSopenharmony_ci		break;
3828f9f848faSopenharmony_ci	case USB_HW_POWER_RESUME:
3829f9f848faSopenharmony_ci		ehci_resume(sc);
3830f9f848faSopenharmony_ci		break;
3831f9f848faSopenharmony_ci	default:
3832f9f848faSopenharmony_ci		break;
3833f9f848faSopenharmony_ci	}
3834f9f848faSopenharmony_ci}
3835f9f848faSopenharmony_ci
3836f9f848faSopenharmony_cistatic void
3837f9f848faSopenharmony_ciehci_set_hw_power(struct usb_bus *bus)
3838f9f848faSopenharmony_ci{
3839f9f848faSopenharmony_ci	ehci_softc_t *sc = EHCI_BUS2SC(bus);
3840f9f848faSopenharmony_ci	uint32_t temp;
3841f9f848faSopenharmony_ci	uint32_t flags;
3842f9f848faSopenharmony_ci
3843f9f848faSopenharmony_ci	DPRINTF("\n");
3844f9f848faSopenharmony_ci
3845f9f848faSopenharmony_ci	USB_BUS_LOCK(bus);
3846f9f848faSopenharmony_ci
3847f9f848faSopenharmony_ci	flags = bus->hw_power_state;
3848f9f848faSopenharmony_ci
3849f9f848faSopenharmony_ci	temp = EOREAD4(sc, EHCI_USBCMD);
3850f9f848faSopenharmony_ci
3851f9f848faSopenharmony_ci	temp &= ~(EHCI_CMD_ASE | EHCI_CMD_PSE);
3852f9f848faSopenharmony_ci
3853f9f848faSopenharmony_ci	if (flags & (USB_HW_POWER_CONTROL |
3854f9f848faSopenharmony_ci	    USB_HW_POWER_BULK)) {
3855f9f848faSopenharmony_ci		DPRINTF("Async is active\n");
3856f9f848faSopenharmony_ci		temp |= EHCI_CMD_ASE;
3857f9f848faSopenharmony_ci	}
3858f9f848faSopenharmony_ci	if (flags & (USB_HW_POWER_INTERRUPT |
3859f9f848faSopenharmony_ci	    USB_HW_POWER_ISOC)) {
3860f9f848faSopenharmony_ci		DPRINTF("Periodic is active\n");
3861f9f848faSopenharmony_ci		temp |= EHCI_CMD_PSE;
3862f9f848faSopenharmony_ci	}
3863f9f848faSopenharmony_ci	EOWRITE4(sc, EHCI_USBCMD, temp);
3864f9f848faSopenharmony_ci
3865f9f848faSopenharmony_ci	USB_BUS_UNLOCK(bus);
3866f9f848faSopenharmony_ci
3867f9f848faSopenharmony_ci	return;
3868f9f848faSopenharmony_ci}
3869f9f848faSopenharmony_ci
3870f9f848faSopenharmony_cistatic void
3871f9f848faSopenharmony_ciehci_start_dma_delay_second(struct usb_xfer *xfer)
3872f9f848faSopenharmony_ci{
3873f9f848faSopenharmony_ci	struct ehci_softc *sc = EHCI_BUS2SC(xfer->xroot->bus);
3874f9f848faSopenharmony_ci
3875f9f848faSopenharmony_ci	DPRINTF("\n");
3876f9f848faSopenharmony_ci
3877f9f848faSopenharmony_ci	/* trigger doorbell */
3878f9f848faSopenharmony_ci	ehci_doorbell_async(sc);
3879f9f848faSopenharmony_ci
3880f9f848faSopenharmony_ci	/* give the doorbell 4ms */
3881f9f848faSopenharmony_ci	usbd_transfer_timeout_ms(xfer,
3882f9f848faSopenharmony_ci	    (void (*)(void *))&usb_dma_delay_done_cb, 4);
3883f9f848faSopenharmony_ci}
3884f9f848faSopenharmony_ci
3885f9f848faSopenharmony_ci/*
3886f9f848faSopenharmony_ci * Ring the doorbell twice before freeing any DMA descriptors. Some host
3887f9f848faSopenharmony_ci * controllers apparently cache the QH descriptors and need a message
3888f9f848faSopenharmony_ci * that the cache needs to be discarded.
3889f9f848faSopenharmony_ci */
3890f9f848faSopenharmony_cistatic void
3891f9f848faSopenharmony_ciehci_start_dma_delay(struct usb_xfer *xfer)
3892f9f848faSopenharmony_ci{
3893f9f848faSopenharmony_ci	struct ehci_softc *sc = EHCI_BUS2SC(xfer->xroot->bus);
3894f9f848faSopenharmony_ci
3895f9f848faSopenharmony_ci	DPRINTF("\n");
3896f9f848faSopenharmony_ci
3897f9f848faSopenharmony_ci	/* trigger doorbell */
3898f9f848faSopenharmony_ci	ehci_doorbell_async(sc);
3899f9f848faSopenharmony_ci
3900f9f848faSopenharmony_ci	/* give the doorbell 4ms */
3901f9f848faSopenharmony_ci	usbd_transfer_timeout_ms(xfer,
3902f9f848faSopenharmony_ci	    (void (*)(void *))&ehci_start_dma_delay_second, 4);
3903f9f848faSopenharmony_ci}
3904f9f848faSopenharmony_ci
3905f9f848faSopenharmony_ciconst struct usb_bus_methods ehci_bus_methods = {
3906f9f848faSopenharmony_ci	.endpoint_init = ehci_ep_init,
3907f9f848faSopenharmony_ci	.xfer_setup = ehci_xfer_setup,
3908f9f848faSopenharmony_ci	.xfer_unsetup = ehci_xfer_unsetup,
3909f9f848faSopenharmony_ci	.get_dma_delay = ehci_get_dma_delay,
3910f9f848faSopenharmony_ci	.device_resume = ehci_device_resume,
3911f9f848faSopenharmony_ci	.device_suspend = ehci_device_suspend,
3912f9f848faSopenharmony_ci	.set_hw_power = ehci_set_hw_power,
3913f9f848faSopenharmony_ci	.set_hw_power_sleep = ehci_set_hw_power_sleep,
3914f9f848faSopenharmony_ci	.roothub_exec = ehci_roothub_exec,
3915f9f848faSopenharmony_ci	.xfer_poll = ehci_do_poll,
3916f9f848faSopenharmony_ci	.start_dma_delay = ehci_start_dma_delay,
3917f9f848faSopenharmony_ci};
3918f9f848faSopenharmony_ci#undef USB_DEBUG_VAR
3919