diff --git a/pci/xhci_pci.c b/pci/xhci_pci.c index e4cf9ec..fb2a9ab 100644 --- a/pci/xhci_pci.c +++ b/pci/xhci_pci.c @@ -1,4 +1,5 @@ /* $NetBSD: xhci_pci.c,v 1.3 2014/03/29 19:28:25 christos Exp $ */ +/* OpenBSD: xhci_pci.c,v 1.4 2014/07/12 17:38:51 yuo Exp */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -43,6 +44,7 @@ __KERNEL_RCSID(0, "$NetBSD: xhci_pci.c,v 1.3 2014/03/29 19:28:25 christos Exp $" #include #include +#include #include #include @@ -52,6 +54,17 @@ __KERNEL_RCSID(0, "$NetBSD: xhci_pci.c,v 1.3 2014/03/29 19:28:25 christos Exp $" #include #include +struct xhci_pci_quirk { + pci_vendor_id_t vendor; + pci_product_id_t product; + int quirks; +}; + +static const struct xhci_pci_quirk xhci_pci_quirks[] = { + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_CORE4G_M_XHCI, + XHCI_QUIRK_FORCE_INTR }, +}; + struct xhci_pci_softc { struct xhci_softc sc_xhci; pci_chipset_tag_t sc_pc; @@ -59,6 +72,18 @@ struct xhci_pci_softc { }; static int +xhci_pci_has_quirk(pci_vendor_id_t vendor, pci_product_id_t product) +{ + int i; + + for (i = 0; i < __arraycount(xhci_pci_quirks); i++) + if (vendor == xhci_pci_quirks[i].vendor && + product == xhci_pci_quirks[i].product) + return xhci_pci_quirks[i].quirks; + return 0; +} + +static int xhci_pci_match(device_t parent, cfdata_t match, void *aux) { struct pci_attach_args *pa = (struct pci_attach_args *) aux; @@ -71,6 +96,76 @@ xhci_pci_match(device_t parent, cfdata_t match, void *aux) return 0; } +static int +xhci_pci_port_route(struct xhci_pci_softc *psc) +{ + struct xhci_softc * const sc = &psc->sc_xhci; + + pcireg_t val; + + /* + * Check USB3 Port Routing Mask register that indicates the ports + * can be changed from OS, and turn on by USB3 Port SS Enable register. + */ + val = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_USB3PRM); + aprint_debug_dev(sc->sc_dev, + "USB3PRM / USB3.0 configurable ports: 0x%08x\n", val); + + pci_conf_write(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_USB3_PSSEN, val); + val = pci_conf_read(psc->sc_pc, psc->sc_tag,PCI_XHCI_INTEL_USB3_PSSEN); + aprint_debug_dev(sc->sc_dev, + "USB3_PSSEN / Enabled USB3.0 ports under xHCI: 0x%08x\n", val); + + /* + * Check USB2 Port Routing Mask register that indicates the USB2.0 + * ports to be controlled by xHCI HC, and switch them to xHCI HC. + */ + val = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_USB2PRM); + aprint_debug_dev(sc->sc_dev, + "XUSB2PRM / USB2.0 ports can switch from EHCI to xHCI:" + "0x%08x\n", val); + pci_conf_write(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_XUSB2PR, val); + val = pci_conf_read(psc->sc_pc, psc->sc_tag, PCI_XHCI_INTEL_XUSB2PR); + aprint_debug_dev(sc->sc_dev, + "XUSB2PR / USB2.0 ports under xHCI: 0x%08x\n", val); + + return 0; +} + +static void +xhci_pci_takecontroller(struct xhci_pci_softc *psc, int silent) +{ + uint32_t cparams, xecp, eec; + uint8_t bios_sem; + int i; + + cparams = xhci_read_4(&psc->sc_xhci, XHCI_HCCPARAMS); + eec = -1; + + /* Synchronise with the BIOS if it owns the controller. */ + for (xecp = XHCI_HCC_XECP(cparams) << 2; xecp != 0; + xecp = XHCI_XECP_NEXT(eec) << 2) { + eec = xhci_read_4(&psc->sc_xhci, xecp); + if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY) + continue; + bios_sem = xhci_read_1(&psc->sc_xhci, + xecp + XHCI_XECP_BIOS_SEM); + if (bios_sem) { + xhci_write_1(&psc->sc_xhci, xecp + XHCI_XECP_OS_SEM, 1); + aprint_debug("waiting for BIOS to give up control\n"); + for (i = 0; i < 5000; i++) { + bios_sem = xhci_read_1(&psc->sc_xhci, xecp + + XHCI_XECP_BIOS_SEM); + if (bios_sem == 0) + break; + DELAY(1000); + } + if (silent == 0 && bios_sem) + printf("timed out waiting for BIOS\n"); + } + } +} + static void xhci_pci_attach(device_t parent, device_t self, void *aux) { @@ -92,6 +187,10 @@ xhci_pci_attach(device_t parent, device_t self, void *aux) pci_aprint_devinfo(pa, "USB Controller"); + /* Check for quirks */ + sc->sc_xhci_quirks = xhci_pci_has_quirk(PCI_VENDOR(pa->pa_id), + PCI_PRODUCT(pa->pa_id)); + /* check if memory space access is enabled */ csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); #ifdef DEBUG @@ -165,12 +264,24 @@ xhci_pci_attach(device_t parent, device_t self, void *aux) "vendor 0x%04x", PCI_VENDOR(pa->pa_id)); #endif + /* Take host controller from BIOS */ + xhci_pci_takecontroller(psc, 0); + err = xhci_init(sc); if (err) { aprint_error_dev(self, "init failed, error=%d\n", err); goto fail; } + /* Intel chipset requires SuperSpeed enable and USB2 port routing */ + switch (PCI_VENDOR(pa->pa_id)) { + case PCI_VENDOR_INTEL: + xhci_pci_port_route(psc); + break; + default: + break; + } + if (!pmf_device_register1(self, xhci_suspend, xhci_resume, xhci_shutdown)) aprint_error_dev(self, "couldn't establish power handler\n"); diff --git a/usb/xhci.c b/usb/xhci.c index c13de65..c1cdc07 100644 --- a/usb/xhci.c +++ b/usb/xhci.c @@ -209,12 +209,25 @@ static const struct usbd_pipe_methods xhci_device_intr_methods = { .done = xhci_device_intr_done, }; -static inline uint32_t +inline uint32_t +xhci_read_1(const struct xhci_softc * const sc, bus_size_t offset) +{ + return bus_space_read_1(sc->sc_iot, sc->sc_ioh, offset); +} + +inline uint32_t xhci_read_4(const struct xhci_softc * const sc, bus_size_t offset) { return bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset); } +inline void +xhci_write_1(const struct xhci_softc * const sc, bus_size_t offset, + uint32_t value) +{ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, offset, value); +} + #if 0 /* unused */ static inline void xhci_write_4(const struct xhci_softc * const sc, bus_size_t offset, @@ -899,9 +912,13 @@ xhci_intr1(struct xhci_softc * const sc) iman = xhci_rt_read_4(sc, XHCI_IMAN(0)); //device_printf(sc->sc_dev, "%s IMAN0 %08x\n", __func__, iman); - if ((iman & XHCI_IMAN_INTR_PEND) == 0) { - return 0; + + if (!(sc->sc_xhci_quirks & XHCI_QUIRK_FORCE_INTR)) { + if ((iman & XHCI_IMAN_INTR_PEND) == 0) { + return 0; + } } + xhci_rt_write_4(sc, XHCI_IMAN(0), iman); iman = xhci_rt_read_4(sc, XHCI_IMAN(0)); //device_printf(sc->sc_dev, "%s IMAN0 %08x\n", __func__, iman); diff --git a/usb/xhcireg.h b/usb/xhcireg.h index 480b93f..af9f224 100644 --- a/usb/xhcireg.h +++ b/usb/xhcireg.h @@ -38,6 +38,11 @@ #define PCI_USBREV_3_0 0x30 /* USB 3.0 */ #define PCI_XHCI_FLADJ 0x61 /* RW frame length adjust */ +#define PCI_XHCI_INTEL_XUSB2PR 0xD0 /* Intel USB2 Port Routing */ +#define PCI_XHCI_INTEL_USB2PRM 0xD4 /* Intel USB2 Port Routing Mask */ +#define PCI_XHCI_INTEL_USB3_PSSEN 0xD8 /* Intel USB3 Port SuperSpeed Enable */ +#define PCI_XHCI_INTEL_USB3PRM 0xDC /* Intel USB3 Port Routing Mask */ + /* XHCI capability registers */ #define XHCI_CAPLENGTH 0x00 /* RO capability */ #define XHCI_CAP_CAPLENGTH(x) ((x) & 0xFF) @@ -188,10 +193,8 @@ /* XHCI legacy support */ #define XHCI_XECP_ID(x) ((x) & 0xFF) #define XHCI_XECP_NEXT(x) (((x) >> 8) & 0xFF) -#if 0 #define XHCI_XECP_BIOS_SEM 0x0002 #define XHCI_XECP_OS_SEM 0x0003 -#endif /* XHCI capability ID's */ #define XHCI_ID_USB_LEGACY 0x0001 diff --git a/usb/xhcivar.h b/usb/xhcivar.h index 7ddbaa2..8b5169f 100644 --- a/usb/xhcivar.h +++ b/usb/xhcivar.h @@ -114,8 +114,14 @@ struct xhci_softc { uint8_t sc_addr; uint8_t sc_conf; + + int sc_xhci_quirks; +#define XHCI_QUIRK_FORCE_INTR __BIT(0) /* force interrupt reading */ }; +uint32_t xhci_read_1(const struct xhci_softc * const, bus_size_t); +uint32_t xhci_read_4(const struct xhci_softc * const, bus_size_t); +void xhci_write_1(const struct xhci_softc * const, bus_size_t, uint32_t); int xhci_init(struct xhci_softc *); int xhci_intr(void *); int xhci_detach(struct xhci_softc *, int);