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