// Code for handling EHCI USB controllers.
//
-// Copyright (C) 2010 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2010-2013 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "string.h" // memset
#include "usb.h" // struct usb_s
#include "usb-ehci.h" // struct ehci_qh
-#include "usb-ohci.h" // ohci_setup
-#include "usb-uhci.h" // uhci_setup
#include "util.h" // msleep
#include "x86.h" // readl
struct ehci_caps *caps;
struct ehci_regs *regs;
struct ehci_qh *async_qh;
- struct pci_device *companion[8];
int checkports;
- int legacycount;
};
struct ehci_pipe {
struct usb_pipe pipe;
};
+static int PendingEHCIPorts;
+
/****************************************************************
* Root hub
#define EHCI_TIME_POSTPOWER 20
#define EHCI_TIME_POSTRESET 2
-// Check if need companion controllers for full/low speed devices
-static void
-ehci_note_port(struct usb_ehci_s *cntl)
-{
- if (--cntl->checkports)
- // Ports still being detected.
- return;
- if (! cntl->legacycount)
- // No full/low speed devices found.
- return;
- // Start companion controllers.
- int i;
- for (i=0; i<ARRAY_SIZE(cntl->companion); i++) {
- struct pci_device *pci = cntl->companion[i];
- if (!pci)
- break;
-
- // ohci/uhci_setup call pci_config_X - don't run from irq handler.
- wait_preempt();
-
- if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_UHCI)
- uhci_setup(pci, cntl->usb.busid + i);
- else if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_OHCI)
- ohci_setup(pci, cntl->usb.busid + i);
- }
-}
-
// Check if device attached to port
static int
ehci_hub_detect(struct usbhub_s *hub, u32 port)
if ((portsc & PORT_LINESTATUS_MASK) == PORT_LINESTATUS_KSTATE) {
// low speed device
- cntl->legacycount++;
writel(portreg, portsc | PORT_OWNER);
goto doneearly;
}
return 0;
doneearly:
- ehci_note_port(cntl);
+ PendingEHCIPorts--;
return -1;
}
goto resetfail;
if (!(portsc & PORT_PE)) {
// full speed device
- cntl->legacycount++;
writel(portreg, portsc | PORT_OWNER);
goto resetfail;
}
rv = USB_HIGHSPEED;
resetfail:
- ehci_note_port(cntl);
+ PendingEHCIPorts--;
return rv;
}
// Set default of high speed for root hub.
writel(&cntl->regs->configflag, 1);
- cntl->checkports = readl(&cntl->caps->hcsparams) & HCS_N_PORTS_MASK;
// Find devices
int count = check_ehci_ports(cntl);
free(cntl);
}
-int
-ehci_setup(struct pci_device *pci, int busid, struct pci_device *comppci)
+static void
+ehci_controller_setup(struct pci_device *pci)
{
- if (! CONFIG_USB_EHCI)
- return -1;
-
+ wait_preempt(); // Avoid pci_config_readl when preempting
u16 bdf = pci->bdf;
u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0);
struct ehci_caps *caps = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK);
struct usb_ehci_s *cntl = malloc_tmphigh(sizeof(*cntl));
if (!cntl) {
warn_noalloc();
- return -1;
+ return;
}
memset(cntl, 0, sizeof(*cntl));
- cntl->usb.busid = busid;
cntl->usb.pci = pci;
cntl->usb.type = USB_TYPE_EHCI;
cntl->caps = caps;
+ cntl->checkports = readl(&cntl->caps->hcsparams) & HCS_N_PORTS_MASK;
cntl->regs = (void*)caps + readb(&caps->caplength);
if (hcc_params & HCC_64BIT_ADDR)
cntl->regs->ctrldssegment = 0;
+ PendingEHCIPorts += cntl->checkports;
dprintf(1, "EHCI init on dev %02x:%02x.%x (regs=%p)\n"
, pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)
// XXX - check for and disable SMM control?
- // Find companion controllers.
- int count = 0;
- for (;;) {
- if (!comppci || comppci == pci)
- break;
- if (pci_classprog(comppci) == PCI_CLASS_SERIAL_USB_UHCI)
- cntl->companion[count++] = comppci;
- else if (pci_classprog(comppci) == PCI_CLASS_SERIAL_USB_OHCI)
- cntl->companion[count++] = comppci;
- comppci = container_of(comppci->node.next, struct pci_device, node);
+ run_thread(configure_ehci, cntl);
+}
+
+void
+ehci_setup(void)
+{
+ if (! CONFIG_USB_EHCI)
+ return;
+ struct pci_device *pci;
+ foreachpci(pci) {
+ if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_EHCI)
+ ehci_controller_setup(pci);
}
- run_thread(configure_ehci, cntl);
- return 0;
+ // Wait for all EHCI ports to initialize. This forces OHCI/UHCI
+ // setup to always be after any EHCI ports are set to low speed.
+ while (PendingEHCIPorts)
+ yield();
}
#define __USB_EHCI_H
// usb-ehci.c
-int ehci_setup(struct pci_device *pci, int busid, struct pci_device *comppci);
+void ehci_setup(void);
struct usbdevice_s;
struct usb_endpoint_descriptor;
struct usb_pipe *ehci_alloc_pipe(struct usbdevice_s *usbdev
#include "malloc.h" // free
#include "output.h" // dprintf
#include "pci.h" // pci_bdf_to_bus
+#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_OHCI
#include "pci_regs.h" // PCI_BASE_ADDRESS_0
#include "string.h" // memset
#include "usb.h" // struct usb_s
free(intr_ed);
}
-void
-ohci_setup(struct pci_device *pci, int busid)
+static void
+ohci_controller_setup(struct pci_device *pci)
{
- if (! CONFIG_USB_OHCI)
- return;
struct usb_ohci_s *cntl = malloc_tmphigh(sizeof(*cntl));
if (!cntl) {
warn_noalloc();
return;
}
memset(cntl, 0, sizeof(*cntl));
- cntl->usb.busid = busid;
cntl->usb.pci = pci;
cntl->usb.type = USB_TYPE_OHCI;
+ wait_preempt(); // Avoid pci_config_readl when preempting
u16 bdf = pci->bdf;
u32 baseaddr = pci_config_readl(bdf, PCI_BASE_ADDRESS_0);
cntl->regs = (void*)(baseaddr & PCI_BASE_ADDRESS_MEM_MASK);
run_thread(configure_ohci, cntl);
}
+void
+ohci_setup(void)
+{
+ if (! CONFIG_USB_OHCI)
+ return;
+ struct pci_device *pci;
+ foreachpci(pci) {
+ if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_OHCI)
+ ohci_controller_setup(pci);
+ }
+}
+
/****************************************************************
* End point communication
#define __USB_OHCI_H
// usb-ohci.c
-void ohci_setup(struct pci_device *pci, int busid);
+void ohci_setup(void);
struct usbdevice_s;
struct usb_endpoint_descriptor;
struct usb_pipe *ohci_alloc_pipe(struct usbdevice_s *usbdev
#include "malloc.h" // free
#include "output.h" // dprintf
#include "pci.h" // pci_bdf_to_bus
+#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI
#include "pci_regs.h" // PCI_BASE_ADDRESS_4
#include "string.h" // memset
#include "usb.h" // struct usb_s
free(cntl);
}
-void
-uhci_setup(struct pci_device *pci, int busid)
+static void
+uhci_controller_setup(struct pci_device *pci)
{
- if (! CONFIG_USB_UHCI)
- return;
u16 bdf = pci->bdf;
struct usb_uhci_s *cntl = malloc_tmphigh(sizeof(*cntl));
if (!cntl) {
warn_noalloc();
return;
}
+ wait_preempt(); // Avoid pci_config_readl when preempting
memset(cntl, 0, sizeof(*cntl));
- cntl->usb.busid = busid;
cntl->usb.pci = pci;
cntl->usb.type = USB_TYPE_UHCI;
cntl->iobase = (pci_config_readl(bdf, PCI_BASE_ADDRESS_4)
run_thread(configure_uhci, cntl);
}
+void
+uhci_setup(void)
+{
+ if (! CONFIG_USB_UHCI)
+ return;
+ struct pci_device *pci;
+ foreachpci(pci) {
+ if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_UHCI)
+ uhci_controller_setup(pci);
+ }
+}
+
/****************************************************************
* End point communication
#define __USB_UHCI_H
// usb-uhci.c
-void uhci_setup(struct pci_device *pci, int busid);
+void uhci_setup(void);
struct usbdevice_s;
struct usb_endpoint_descriptor;
struct usb_pipe *uhci_alloc_pipe(struct usbdevice_s *usbdev
#include "x86.h" // readl
#include "malloc.h" // memalign_low
#include "pci.h" // pci_bdf_to_bus
+#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_XHCI
#include "pci_regs.h" // PCI_BASE_ADDRESS_0
#include "usb.h" // struct usb_s
#include "usb-xhci.h" // struct ehci_qh
return 0;
}
-int
-xhci_setup(struct pci_device *pci, int busid)
+static void
+xhci_controller_setup(struct pci_device *pci)
{
- ASSERT32FLAT();
- if (!CONFIG_USB_XHCI)
- return -1;
-
struct usb_xhci_s *xhci = malloc_low(sizeof(*xhci));
if (!xhci) {
warn_noalloc();
- return -1;
+ return;
}
memset(xhci, 0, sizeof(*xhci));
+ wait_preempt(); // Avoid pci_config_readl when preempting
xhci->baseaddr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0)
& PCI_BASE_ADDRESS_MEM_MASK;
xhci->caps = (void*)(xhci->baseaddr);
xhci->slots = hcs1 & 0xff;
xhci->xcap = ((hcc >> 16) & 0xffff) << 2;
- xhci->usb.busid = busid;
xhci->usb.pci = pci;
xhci->usb.type = USB_TYPE_XHCI;
xhci->hub.cntl = &xhci->usb;
pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER);
run_thread(configure_xhci, xhci);
- return 0;
+}
+
+void
+xhci_setup(void)
+{
+ if (! CONFIG_USB_XHCI)
+ return;
+ struct pci_device *pci;
+ foreachpci(pci) {
+ if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_XHCI)
+ xhci_controller_setup(pci);
+ }
}
// --------------------------------------------------------------
// usb-xhci.c
-int xhci_setup(struct pci_device *pci, int busid);
+void xhci_setup(void);
struct usb_pipe *xhci_alloc_pipe(struct usbdevice_s *usbdev
, struct usb_endpoint_descriptor *epdesc);
struct usb_pipe *xhci_update_pipe(struct usbdevice_s *usbdev
// Main code for handling USB controllers and devices.
//
-// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2009-2013 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "config.h" // CONFIG_*
#include "malloc.h" // free
#include "output.h" // dprintf
-#include "pci.h" // foreachpci
-#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI
-#include "pci_regs.h" // PCI_CLASS_REVISION
#include "string.h" // memset
#include "usb.h" // struct usb_s
#include "usb-ehci.h" // ehci_setup
yield();
}
+void
+__usb_setup(void *data)
+{
+ dprintf(3, "init usb\n");
+ xhci_setup();
+ ehci_setup();
+ uhci_setup();
+ ohci_setup();
+}
+
void
usb_setup(void)
{
ASSERT32FLAT();
if (! CONFIG_USB)
return;
-
- dprintf(3, "init usb\n");
-
- // Look for USB controllers
- int count = 0;
- struct pci_device *pci, *ehcipci = NULL;
- foreachpci(pci) {
- if (pci->class != PCI_CLASS_SERIAL_USB)
- continue;
-
- if (!ehcipci || pci->bdf >= ehcipci->bdf) {
- // Check to see if this device has an ehci controller
- int found = 0;
- ehcipci = pci;
- for (;;) {
- if (pci_classprog(ehcipci) == PCI_CLASS_SERIAL_USB_EHCI) {
- // Found an ehci controller.
- int ret = ehci_setup(ehcipci, count++, pci);
- if (ret)
- // Error
- break;
- count += found;
- pci = ehcipci;
- break;
- }
- if (ehcipci->class == PCI_CLASS_SERIAL_USB)
- found++;
- ehcipci = container_of_or_null(
- ehcipci->node.next, struct pci_device, node);
- if (!ehcipci || (pci_bdf_to_busdev(ehcipci->bdf)
- != pci_bdf_to_busdev(pci->bdf)))
- // No ehci controller found.
- break;
- }
- }
-
- if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_UHCI)
- uhci_setup(pci, count++);
- else if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_OHCI)
- ohci_setup(pci, count++);
- else if (pci_classprog(pci) == PCI_CLASS_SERIAL_USB_XHCI)
- xhci_setup(pci, count++);
- }
+ run_thread(__usb_setup, NULL);
}
struct usb_pipe *freelist;
struct mutex_s resetlock;
struct pci_device *pci;
- int busid;
u8 type;
u8 maxaddr;
};