/* $OpenBSD: octhci.c,v 1.5 2014/07/10 21:50:42 jasper Exp $ */ /* * Copyright (c) 2014 Paul Irofti * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef OCTHCI_DEBUG #define DPRINTF(x) do { if (octhcidebug) printf x; } while(0) #define DPRINTFN(n,x) do { if (octhcidebug>(n)) printf x; } while (0) int octhcidebug = 3; #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define DEVNAME(sc) ((sc)->sc_bus.bdev.dv_xname) struct octhci_softc { struct usbd_bus sc_bus; /* base device */ void *sc_ih; /* interrupt handler */ bus_space_tag_t sc_bust; /* iobus space */ bus_space_handle_t sc_regn; /* usbn register space */ bus_space_handle_t sc_regc; /* usbc register space */ bus_space_handle_t sc_dma_reg; /* dma register space */ int sc_noport; /* Maximum number of ports */ u_int8_t sc_conf; /* Device configuration */ char sc_vendor[16]; /* Vendor string for root hub */ int sc_id_vendor; /* Vendor ID for root hub */ }; struct pool *octhcixfer; struct octhci_xfer { struct usbd_xfer xfer; }; int octhci_match(struct device *, void *, void *); void octhci_attach(struct device *, struct device *, void *); int octhci_init(struct octhci_softc *); void octhci_init_core(struct octhci_softc *); void octhci_init_host(struct octhci_softc *); int octhci_intr(void *); const struct cfattach octhci_ca = { sizeof(struct octhci_softc), octhci_match, octhci_attach, }; struct cfdriver octhci_cd = { NULL, "octhci", DV_DULL }; inline void octhci_regn_set(struct octhci_softc *, bus_size_t, uint64_t); inline void octhci_regn_clear(struct octhci_softc *, bus_size_t, uint64_t); inline void octhci_regc_set(struct octhci_softc *, bus_size_t, uint32_t); inline void octhci_regc_clear(struct octhci_softc *, bus_size_t, uint32_t); inline uint32_t octhci_regc_read(struct octhci_softc *, bus_size_t); inline void octhci_regc_write(struct octhci_softc *, bus_size_t, uint32_t); struct octhci_soft_td { struct octhci_soft_td *next; struct octhci_soft_td *prev; struct octhci_soft_td *std; octhci_physaddr_t physaddr; struct usb_dma dma; /* TD's DMA infos */ int offs; /* TD's offset in struct usb_dma */ int islot; }; usbd_status octhci_open(struct usbd_pipe *pipe); void octhci_softintr(void *); void octhci_poll(struct usbd_bus *); struct usbd_xfer * octhci_allocx(struct usbd_bus *); void octhci_freex(struct usbd_bus *, struct usbd_xfer *); struct usbd_bus_methods octhci_bus_methods = { .open_pipe = octhci_open, .soft_intr = octhci_softintr, .do_poll = octhci_poll, .allocx = octhci_allocx, .freex = octhci_freex, }; #define OCTHCI_INTR_ENDPT 1 usbd_status octhci_root_ctrl_transfer(struct usbd_xfer *xfer); usbd_status octhci_root_ctrl_start(struct usbd_xfer *xfer); void octhci_root_ctrl_abort(struct usbd_xfer *xfer); void octhci_root_ctrl_close(struct usbd_pipe *pipe); void octhci_root_ctrl_done(struct usbd_xfer *xfer); struct usbd_pipe_methods octhci_root_ctrl_methods = { .transfer = octhci_root_ctrl_transfer, .start = octhci_root_ctrl_start, .abort = octhci_root_ctrl_abort, .close = octhci_root_ctrl_close, .done = octhci_root_ctrl_done, }; usbd_status octhci_root_intr_transfer(struct usbd_xfer *xfer); usbd_status octhci_root_intr_start(struct usbd_xfer *xfer); void octhci_root_intr_abort(struct usbd_xfer *xfer); void octhci_root_intr_close(struct usbd_pipe *pipe); void octhci_root_intr_done(struct usbd_xfer *xfer); struct usbd_pipe_methods octhci_root_intr_methods = { .transfer = octhci_root_intr_transfer, .start = octhci_root_intr_start, .abort = octhci_root_intr_abort, .close = octhci_root_intr_close, .done = octhci_root_intr_done, }; int octhci_match(struct device *parent, void *match, void *aux) { struct iobus_attach_args *aa = aux; struct cfdata *cf = match; /* XXX: check for board type */ if (aa->aa_name == NULL || strcmp(aa->aa_name, cf->cf_driver->cd_name) != 0) return (0); return (1); } void octhci_attach(struct device *parent, struct device *self, void *aux) { struct octhci_softc *sc = (struct octhci_softc *)self; struct iobus_attach_args *aa = aux; int rc; sc->sc_bust = aa->aa_bust; rc = bus_space_map(aa->aa_bust, USBN_BASE, USBN_SIZE, 0, &sc->sc_regn); if (rc != 0) panic(": can't map registers"); /* dma registers */ rc = bus_space_map(aa->aa_bust, USBN_2_BASE, USBN_2_SIZE, 0, &sc->sc_dma_reg); if (rc != 0) panic(": can't map dma registers"); /* control registers */ rc = bus_space_map(aa->aa_bust, USBC_BASE, USBC_SIZE, 0, &sc->sc_regc); if (rc != 0) panic(": can't map control registers"); /* * XXX: assume only one USB port is available. * Should write a get_nports routine to get the actual number based * on the model. */ sc->sc_noport = 1; sc->sc_bus.usbrev = USBREV_2_0; if (octhci_init(sc)) return; octhci_init_core(sc); octhci_init_host(sc); #if 0 sc->sc_ih = octeon_intr_establish(CIU_INT_USB, IPL_USB, octhci_intr, (void *)sc, sc->sc_bus.bdev.dv_xname); if (sc->sc_ih == NULL) panic(": can't interrupt establish failed"); #endif /* * No usb methods yet. * sc->sc_bus.usbrev = USBREV_2_0; */ sc->sc_bus.methods = &octhci_bus_methods; sc->sc_bus.pipe_size = sizeof(struct usbd_pipe); sc->sc_bus.dmatag = aa->aa_dmat; config_found((void *)sc, &sc->sc_bus, usbctlprint); } int octhci_init(struct octhci_softc *sc) { uint64_t clk; if (octhcixfer == NULL) { octhcixfer = malloc(sizeof(struct pool), M_DEVBUF, M_NOWAIT); if (octhcixfer == NULL) { printf("%s: unable to allocate pool descriptor\n", DEVNAME(sc)); return (ENOMEM); } pool_init(octhcixfer, sizeof(struct octhci_xfer), 0, 0, 0, "octhcixfer", NULL); } /* * Clock setup. */ clk = bus_space_read_8(sc->sc_bust, sc->sc_regn, USBN_CLK_CTL_OFFSET); clk |= USBN_CLK_CTL_POR; clk &= ~(USBN_CLK_CTL_HRST | USBN_CLK_CTL_PRST | USBN_CLK_CTL_HCLK_RST | USBN_CLK_CTL_ENABLE | USBN_CLK_CTL_P_C_SEL | USBN_CLK_CTL_P_RTYPE); clk |= SET_USBN_CLK_CTL_DIVIDE(0x4ULL) | SET_USBN_CLK_CTL_DIVIDE2(0x0ULL); bus_space_write_8(sc->sc_bust, sc->sc_regn, USBN_CLK_CTL_OFFSET, clk); bus_space_read_8(sc->sc_bust, sc->sc_regn, USBN_CLK_CTL_OFFSET); /* * Reset HCLK and wait for it to stabilize. */ octhci_regn_set(sc, USBN_CLK_CTL_OFFSET, USBN_CLK_CTL_HCLK_RST); delay(64); octhci_regn_clear(sc, USBN_CLK_CTL_OFFSET, USBN_CLK_CTL_POR); /* * Wait for the PHY clock to start. */ delay(1000); octhci_regn_set(sc, USBN_USBP_CTL_STATUS_OFFSET, USBN_USBP_CTL_STATUS_ATE_RESET); delay(10); octhci_regn_clear(sc, USBN_USBP_CTL_STATUS_OFFSET, USBN_USBP_CTL_STATUS_ATE_RESET); octhci_regn_set(sc, USBN_CLK_CTL_OFFSET, USBN_CLK_CTL_PRST); /* * Select host mode. */ octhci_regn_clear(sc, USBN_USBP_CTL_STATUS_OFFSET, USBN_USBP_CTL_STATUS_HST_MODE); delay(1); octhci_regn_set(sc, USBN_CLK_CTL_OFFSET, USBN_CLK_CTL_HRST); /* * Enable clock. */ octhci_regn_set(sc, USBN_CLK_CTL_OFFSET, USBN_CLK_CTL_ENABLE); delay(1); return 0; } void octhci_init_core(struct octhci_softc *sc) { uint32_t value, version, minor, major; uint8_t chan; value = octhci_regc_read(sc, USBC_GSNPSID_OFFSET); /* 0x4F54XXXA */ minor = (value >> 4) & 0xf; major = ((value >> 8) & 0xf) / 2; version = (value >> 12) & 0xf; printf(": core version %d pass %d.%d\n", version, major, minor); value = 0; value |= (USBC_GAHBCFG_DMAEN | USBC_GAHBCFG_NPTXFEMPLVL | USBC_GAHBCFG_PTXFEMPLVL | USBC_GAHBCFG_GLBLINTRMSK); octhci_regc_write(sc, USBC_GAHBCFG_OFFSET, value); /* XXX: CN5XXX: usb->idle_hardware_channels = 0xf7 */ value = octhci_regc_read(sc, USBC_GUSBCFG_OFFSET); value &= ~USBC_GUSBCFG_TOUTCAL; value &= ~USBC_GUSBCFG_DDRSEL; value &= ~USBC_GUSBCFG_USBTRDTIM; value |= (5U << USBC_GUSBCFG_USBTRDTIM_OFFSET); value &= ~USBC_GUSBCFG_PHYLPWRCLKSEL; octhci_regc_write(sc, USBC_GUSBCFG_OFFSET, value); value = octhci_regc_read(sc, USBC_GINTMSK_OFFSET); value |= USBC_GINTMSK_OTGINTMSK | USBC_GINTMSK_MODEMISMSK | USBC_GINTMSK_HCHINTMSK; value &= ~USBC_GINTMSK_SOFMSK; octhci_regc_write(sc, USBC_GINTMSK_OFFSET, value); for (chan = 0; chan < 8; chan++) { octhci_regc_write(sc, USBC_HCINTMSK0_OFFSET + chan * 32, 0); } } void octhci_init_host(struct octhci_softc *sc) { uint32_t value; octhci_regc_set(sc, USBC_GINTMSK_OFFSET, USBC_GINTSTS_PRTINT); octhci_regc_set(sc, USBC_GINTMSK_OFFSET, USBC_GINTSTS_DISCONNINT); /* Clear USBC_HCFG_FSLSSUPP and USBC_HCFG_FSLSPCLKSEL */ value = octhci_regc_read(sc, USBC_HCFG_OFFSET); value &= USBC_HCFG_XXX_31_3; octhci_regc_write(sc, USBC_HCFG_OFFSET, value); octhci_regc_set(sc, USBC_HPRT_OFFSET, USBC_HPRT_PRTPWR); } int octhci_intr(void *arg) { struct octhci_softc *sc = (struct octhci_softc *)arg; uint32_t intsts; sc->sc_bus.intr_context++; sc->sc_bus.no_intrs++; intsts = octhci_regc_read(sc, USBC_GINTSTS_OFFSET); intsts &= octhci_regc_read(sc, USBC_GINTMSK_OFFSET) | USBC_GINTSTS_CURMOD; intsts |= octhci_regc_read(sc, USBC_GINTSTS_OFFSET); octhci_regc_write(sc, USBC_GINTSTS_OFFSET, intsts); if (intsts & USBC_GINTSTS_RXFLVL) /* Failed assumption: no DMA */ panic("octhci_intr: Packets pending to be read from RxFIFO"); if ((intsts & USBC_GINTSTS_PTXFEMP) || (intsts & USBC_GINTSTS_NPTXFEMP)) /* Failed assumption: no DMA */ panic("octhci_intr: Packets pending to be written on TxFIFO"); if ((intsts & USBC_GINTSTS_DISCONNINT) || (intsts & USBC_GINTSTS_PRTINT)) { /* Device disconnected */ uint32_t hprt; /* XXX: callback */ hprt = octhci_regc_read(sc, USBC_HPRT_OFFSET); hprt &= ~(USBC_HPRT_PRTENA); octhci_regc_write(sc, USBC_HPRT_OFFSET, hprt); } if (intsts & USBC_GINTSTS_HCHINT) { /* Host Channel Interrupt */ uint32_t haint; int chan; /* XXX: Assume single USB port */ for (haint = octhci_regc_read(sc, USBC_HAINT_OFFSET); haint != 0; haint ^= (1 << chan)) { chan = ffs32(haint) - 1; /* XXX: implement octhci_poll_chan(sc, chan); */ } } sc->sc_bus.intr_context--; return 1; } inline void octhci_regn_set(struct octhci_softc *sc, bus_size_t offset, uint64_t bits) { uint64_t value; value = bus_space_read_8(sc->sc_bust, sc->sc_regn, offset); value |= bits; bus_space_write_8(sc->sc_bust, sc->sc_regn, offset, value); /* guarantee completion of the store operation on RSL registers*/ bus_space_read_8(sc->sc_bust, sc->sc_regn, offset); } inline void octhci_regn_clear(struct octhci_softc *sc, bus_size_t offset, uint64_t bits) { uint64_t value; value = bus_space_read_8(sc->sc_bust, sc->sc_regn, offset); value &= ~bits; bus_space_write_8(sc->sc_bust, sc->sc_regn, offset, value); /* guarantee completion of the store operation on RSL registers*/ bus_space_read_8(sc->sc_bust, sc->sc_regn, offset); } inline void octhci_regc_set(struct octhci_softc *sc, bus_size_t offset, uint32_t bits) { uint32_t value; value = octhci_regc_read(sc, offset); value |= bits; octhci_regc_write(sc, offset, value); } inline void octhci_regc_clear(struct octhci_softc *sc, bus_size_t offset, uint32_t bits) { uint32_t value; value = octhci_regc_read(sc, offset); value &= ~bits; octhci_regc_write(sc, offset, value); } /* XXX: fix the endianess in the include and ditch these methods */ inline void octhci_regc_write(struct octhci_softc *sc, bus_size_t offset, uint32_t value) { /* * In the Cavium document, CSR addresses are written in little-endian * format. so the address should be swapped on the core running in * big-endian. */ bus_space_write_4(sc->sc_bust, sc->sc_regc, (offset^4), value); } inline uint32_t octhci_regc_read(struct octhci_softc *sc, bus_size_t offset) { /* * In the Cavium document, CSR addresses are written in little-endian * format. so the address should be swapped on the core running in * big-endian. */ return bus_space_read_4(sc->sc_bust, sc->sc_regc, (offset^4)); } /* * USBD Bus Methods. */ usbd_status octhci_open(struct usbd_pipe *pipe) { struct octhci_softc *sc = (struct octhci_softc *)pipe->device->bus; usb_endpoint_descriptor_t *ed = pipe->endpoint->edesc; if (sc->sc_bus.dying) return USBD_IOERROR; /* Root Hub */ if (pipe->device->depth == 0) { switch (ed->bEndpointAddress) { case USB_CONTROL_ENDPOINT: DPRINTF(("Root Hub Control\n")); pipe->methods = &octhci_root_ctrl_methods; break; case UE_DIR_IN | OCTHCI_INTR_ENDPT: DPRINTF(("Root Hub Interrupt\n")); pipe->methods = &octhci_root_intr_methods; break; default: pipe->methods = NULL; return USBD_INVAL; } return USBD_NORMAL_COMPLETION; } /* XXX: not supported yet */ return USBD_INVAL; } void octhci_softintr(void *aux) { } void octhci_poll(struct usbd_bus *bus) { } struct usbd_xfer * octhci_allocx(struct usbd_bus *bus) { struct octhci_xfer *xx; xx = pool_get(octhcixfer, PR_NOWAIT | PR_ZERO); #ifdef DIAGNOSTIC if (xx != NULL) xx->xfer.busy_free = XFER_BUSY; #endif return ((struct usbd_xfer *)xx); } void octhci_freex(struct usbd_bus *bus, struct usbd_xfer *xfer) { struct octhci_xfer *xx = (struct octhci_xfer*)xfer; #ifdef DIAGNOSTIC if (xfer->busy_free != XFER_BUSY) { printf("%s: xfer=%p not busy, 0x%08x\n", __func__, xfer, xfer->busy_free); return; } #endif pool_put(octhcixfer, xx); } /* Root hub descriptors. */ usb_device_descriptor_t octhci_devd = { USB_DEVICE_DESCRIPTOR_SIZE, UDESC_DEVICE, /* type */ {0x00, 0x02}, /* USB version */ UDCLASS_HUB, /* class */ UDSUBCLASS_HUB, /* subclass */ UDPROTO_HSHUBSTT, /* protocol */ 64, /* max packet */ {0},{0},{0x00,0x01}, /* device id */ 1,2,0, /* string indexes */ 1 /* # of configurations */ }; const usb_config_descriptor_t octhci_confd = { USB_CONFIG_DESCRIPTOR_SIZE, UDESC_CONFIG, {USB_CONFIG_DESCRIPTOR_SIZE + USB_INTERFACE_DESCRIPTOR_SIZE + USB_ENDPOINT_DESCRIPTOR_SIZE}, 1, 1, 0, UC_SELF_POWERED, 0 /* max power */ }; const usb_interface_descriptor_t octhci_ifcd = { USB_INTERFACE_DESCRIPTOR_SIZE, UDESC_INTERFACE, 0, 0, 1, UICLASS_HUB, UISUBCLASS_HUB, UIPROTO_HSHUBSTT, /* XXX */ 0 }; const usb_endpoint_descriptor_t octhci_endpd = { USB_ENDPOINT_DESCRIPTOR_SIZE, UDESC_ENDPOINT, UE_DIR_IN, UE_INTERRUPT, {2, 0}, /* max 15 ports */ 255 }; const usb_endpoint_ss_comp_descriptor_t octhci_endpcd = { USB_ENDPOINT_SS_COMP_DESCRIPTOR_SIZE, UDESC_ENDPOINT_SS_COMP, 0, 0, {0, 0} /* XXX */ }; const usb_hub_descriptor_t octhci_hubd = { USB_HUB_DESCRIPTOR_SIZE, UDESC_SS_HUB, 0, {0,0}, 0, 0, {0}, }; /* * USBD Root Control Pipe Methods. */ usbd_status octhci_root_ctrl_transfer(struct usbd_xfer *xfer) { usbd_status err; err = usb_insert_transfer(xfer); if (err) return (err); return (octhci_root_ctrl_start(SIMPLEQ_FIRST(&xfer->pipe->queue))); } usbd_status octhci_root_ctrl_start(struct usbd_xfer *xfer) { struct octhci_softc *sc = (struct octhci_softc *)xfer->device->bus; usb_port_status_t ps; usb_device_request_t *req; void *buf = NULL; usb_hub_descriptor_t hubd; usbd_status err; int s, len, value, index; int l, totlen = 0; int i; /* int port; */ /* uint32_t v; */ KASSERT(xfer->rqflags & URQ_REQUEST); if (sc->sc_bus.dying) return (USBD_IOERROR); req = &xfer->request; DPRINTFN(4,("%s: type=0x%02x request=%02x\n", __func__, req->bmRequestType, req->bRequest)); len = UGETW(req->wLength); value = UGETW(req->wValue); index = UGETW(req->wIndex); if (len != 0) buf = KERNADDR(&xfer->dmabuf, 0); #define C(x,y) ((x) | ((y) << 8)) switch(C(req->bRequest, req->bmRequestType)) { case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE): case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE): case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT): /* * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops * for the integrated root hub. */ break; case C(UR_GET_CONFIG, UT_READ_DEVICE): if (len > 0) { *(uint8_t *)buf = sc->sc_conf; totlen = 1; } break; case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE): DPRINTFN(8,("octhci_root_ctrl_start: wValue=0x%04x\n", value)); switch(value >> 8) { case UDESC_DEVICE: if ((value & 0xff) != 0) { err = USBD_IOERROR; goto ret; } totlen = l = min(len, USB_DEVICE_DESCRIPTOR_SIZE); USETW(octhci_devd.idVendor, sc->sc_id_vendor); memcpy(buf, &octhci_devd, l); break; /* * We can't really operate at another speed, but the spec says * we need this descriptor. */ case UDESC_OTHER_SPEED_CONFIGURATION: case UDESC_CONFIG: if ((value & 0xff) != 0) { err = USBD_IOERROR; goto ret; } totlen = l = min(len, USB_CONFIG_DESCRIPTOR_SIZE); memcpy(buf, &octhci_confd, l); ((usb_config_descriptor_t *)buf)->bDescriptorType = value >> 8; buf = (char *)buf + l; len -= l; l = min(len, USB_INTERFACE_DESCRIPTOR_SIZE); totlen += l; memcpy(buf, &octhci_ifcd, l); buf = (char *)buf + l; len -= l; l = min(len, USB_ENDPOINT_DESCRIPTOR_SIZE); totlen += l; memcpy(buf, &octhci_endpd, l); break; case UDESC_STRING: if (len == 0) break; *(u_int8_t *)buf = 0; totlen = 1; switch (value & 0xff) { case 0: /* Language table */ totlen = usbd_str(buf, len, "\001"); break; case 1: /* Vendor */ totlen = usbd_str(buf, len, sc->sc_vendor); break; case 2: /* Product */ totlen = usbd_str(buf, len, "octHCI root hub"); break; } break; default: err = USBD_IOERROR; goto ret; } break; case C(UR_GET_INTERFACE, UT_READ_INTERFACE): if (len > 0) { *(uint8_t *)buf = 0; totlen = 1; } break; case C(UR_GET_STATUS, UT_READ_DEVICE): if (len > 1) { USETW(((usb_status_t *)buf)->wStatus,UDS_SELF_POWERED); totlen = 2; } break; case C(UR_GET_STATUS, UT_READ_INTERFACE): case C(UR_GET_STATUS, UT_READ_ENDPOINT): if (len > 1) { USETW(((usb_status_t *)buf)->wStatus, 0); totlen = 2; } break; case C(UR_SET_ADDRESS, UT_WRITE_DEVICE): if (value >= USB_MAX_DEVICES) { err = USBD_IOERROR; goto ret; } break; case C(UR_SET_CONFIG, UT_WRITE_DEVICE): if (value != 0 && value != 1) { err = USBD_IOERROR; goto ret; } sc->sc_conf = value; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_DEVICE): case C(UR_SET_FEATURE, UT_WRITE_INTERFACE): case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT): err = USBD_IOERROR; goto ret; case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE): break; case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT): break; /* Hub requests */ case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_CLEAR_FEATURE, UT_WRITE_CLASS_OTHER): DPRINTFN(8, ("octhci_root_ctrl_start: UR_CLEAR_PORT_FEATURE " "port=%d feature=%d\n", index, value)); if (index < 1 || index > sc->sc_noport) { err = USBD_IOERROR; goto ret; } switch (value) { case UHF_PORT_ENABLE: break; case UHF_PORT_SUSPEND: /* TODO */ break; case UHF_PORT_POWER: break; case UHF_PORT_INDICATOR: break; case UHF_C_PORT_CONNECTION: break; case UHF_C_PORT_ENABLE: break; case UHF_C_PORT_SUSPEND: break; case UHF_C_PORT_OVER_CURRENT: break; case UHF_C_PORT_RESET: break; default: err = USBD_IOERROR; goto ret; } break; case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE): if (len == 0) break; if ((value & 0xff) != 0) { err = USBD_IOERROR; goto ret; } /* v = XREAD4(sc, XHCI_HCCPARAMS); */ hubd = octhci_hubd; hubd.bNbrPorts = sc->sc_noport; #if 0 USETW(hubd.wHubCharacteristics, (XHCI_HCC_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_GANGED) | (XHCI_HCC_PIND(v) ? UHD_PORT_IND : 0)); hubd.bPwrOn2PwrGood = 10; /* xHCI section 5.4.9 */ for (i = 1; i <= sc->sc_noport; i++) { v = XOREAD4(sc, XHCI_PORTSC(i)); if (v & XHCI_PS_DR) hubd.DeviceRemovable[i / 8] |= 1U << (i % 8); } #endif hubd.bDescLength = USB_HUB_DESCRIPTOR_SIZE + i; l = min(len, hubd.bDescLength); totlen = l; memcpy(buf, &hubd, l); break; case C(UR_GET_STATUS, UT_READ_CLASS_DEVICE): if (len != 16) { err = USBD_IOERROR; goto ret; } memset(buf, 0, len); totlen = len; break; case C(UR_GET_STATUS, UT_READ_CLASS_OTHER): DPRINTFN(8,("octhci_root_ctrl_start: get port status i=%d\n", index)); if (index < 1 || index > sc->sc_noport) { err = USBD_IOERROR; goto ret; } if (len != 4) { err = USBD_IOERROR; goto ret; } #if 0 v = XOREAD4(sc, XHCI_PORTSC(index)); DPRINTFN(8,("xhci_root_ctrl_start: port status=0x%04x\n", v)); switch (XHCI_PS_SPEED(v)) { case XHCI_SPEED_FULL: i = UPS_FULL_SPEED; break; case XHCI_SPEED_LOW: i = UPS_LOW_SPEED; break; case XHCI_SPEED_HIGH: i = UPS_HIGH_SPEED; break; case XHCI_SPEED_SUPER: default: i = UPS_SUPER_SPEED; break; } if (v & XHCI_PS_CCS) i |= UPS_CURRENT_CONNECT_STATUS; if (v & XHCI_PS_PED) i |= UPS_PORT_ENABLED; if (v & XHCI_PS_OCA) i |= UPS_OVERCURRENT_INDICATOR; if (v & XHCI_PS_PR) i |= UPS_RESET; if (v & XHCI_PS_PP) i |= UPS_PORT_POWER; USETW(ps.wPortStatus, i); i = 0; if (v & XHCI_PS_CSC) i |= UPS_C_CONNECT_STATUS; if (v & XHCI_PS_PEC) i |= UPS_C_PORT_ENABLED; if (v & XHCI_PS_OCC) i |= UPS_C_OVERCURRENT_INDICATOR; if (v & XHCI_PS_PRC) i |= UPS_C_PORT_RESET; #endif i = UPS_HIGH_SPEED; USETW(ps.wPortStatus, i); i = 0; i |= UPS_C_PORT_ENABLED; USETW(ps.wPortChange, i); l = min(len, sizeof ps); memcpy(buf, &ps, l); totlen = l; break; case C(UR_SET_DESCRIPTOR, UT_WRITE_CLASS_DEVICE): err = USBD_IOERROR; goto ret; case C(UR_SET_FEATURE, UT_WRITE_CLASS_DEVICE): break; case C(UR_SET_FEATURE, UT_WRITE_CLASS_OTHER): i = index >> 8; index &= 0x00ff; if (index < 1 || index > sc->sc_noport) { err = USBD_IOERROR; goto ret; } switch (value) { case UHF_PORT_ENABLE: break; case UHF_PORT_SUSPEND: DPRINTFN(6, ("suspend port %u (LPM=%u)\n", index, i)); break; case UHF_PORT_RESET: DPRINTFN(6, ("reset port %d\n", index)); break; case UHF_PORT_POWER: DPRINTFN(3, ("set port power %d\n", index)); break; case UHF_PORT_INDICATOR: DPRINTFN(3, ("set port indicator %d\n", index)); break; case UHF_C_PORT_RESET: break; default: err = USBD_IOERROR; goto ret; } break; case C(UR_CLEAR_TT_BUFFER, UT_WRITE_CLASS_OTHER): case C(UR_RESET_TT, UT_WRITE_CLASS_OTHER): case C(UR_GET_TT_STATE, UT_READ_CLASS_OTHER): case C(UR_STOP_TT, UT_WRITE_CLASS_OTHER): break; default: err = USBD_IOERROR; goto ret; } xfer->actlen = totlen; err = USBD_NORMAL_COMPLETION; ret: xfer->status = err; s = splusb(); usb_transfer_complete(xfer); splx(s); return (USBD_IN_PROGRESS); } void octhci_root_ctrl_abort(struct usbd_xfer *xfer) { } void octhci_root_ctrl_close(struct usbd_pipe *pipe) { } void octhci_root_ctrl_done(struct usbd_xfer *xfer) { } /* * USBD Root Interrupt Pipe Methods. */ usbd_status octhci_root_intr_transfer(struct usbd_xfer *xfer) { return USBD_NORMAL_COMPLETION; } usbd_status octhci_root_intr_start(struct usbd_xfer *xfer) { return USBD_NORMAL_COMPLETION; } void octhci_root_intr_abort(struct usbd_xfer *xfer) { } void octhci_root_intr_close(struct usbd_pipe *pipe) { } void octhci_root_intr_done(struct usbd_xfer *xfer) { }