x86: Disable legacy USB for xhci
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 10 Nov 2016 19:51:32 +0000 (14:51 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 7 Dec 2016 22:46:48 +0000 (14:46 -0800)
I'm not 100% that this works.  My machine always times out.  Perhaps there
are more magic steps to take?

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/x86/usb.c

index 83ad56d..1541a5e 100644 (file)
@@ -65,6 +65,61 @@ static void ehci_disable_leg(struct pci_device *pcidev)
               pcidev->bus, pcidev->dev, pcidev->func);
 }
 
+#define XHCI_USBLEGSUP 1
+
+static void xhci_disable_leg(struct pci_device *pcidev)
+{
+       uintptr_t bar0, xhci_hcc_regs, xecp;
+       uint32_t hccparams, val;
+       int i;
+
+       bar0 = pci_get_membar(pcidev, 0);
+       assert(bar0);
+       xhci_hcc_regs = vmap_pmem_nocache(bar0, pcidev->bar[0].mmio_sz);
+       hccparams = read_mmreg32(xhci_hcc_regs + 0x10);
+       xecp = (hccparams >> 16) & 0xffff;
+
+       /* xecp is the rel offset, in 32 bit words, from the base to the extended
+        * capabilities pointer. */
+       for (/* xecp set */; xecp; xecp = (read_mmreg32(xecp) >> 8) & 0xff) {
+               xecp = xhci_hcc_regs + (xecp << 2);
+               val = read_mmreg32(xecp);
+
+               if ((val & 0xff) != XHCI_USBLEGSUP)
+                       continue;
+               /* bios already does not own it */
+               if (!(val & (1 << 16)))
+                       return;
+               /* take ownership.  Note we're allowed to do byte-width writes here. */
+               write_mmreg8(xecp + 3, 1);
+               /* book says to wait up to a second, though i regularly see it time out
+                * on my machines. */
+               for (i = 0; i < 100000; i++) {
+                       if (!(read_mmreg32(xecp) & (1 << 16)))
+                               break;
+                       udelay(10);
+               }
+               if (i == 100000) {
+                       printk("PCI XHCI %x:%x:%x: bios timed out\n",
+                              pcidev->bus, pcidev->dev, pcidev->func);
+                       /* Force the bios's byte clear */
+                       write_mmreg8(xecp + 2, 0);
+               }
+               /* Turn off settings in USBLEGCTLSTS.  Not sure if any of this is
+                * necessary. */
+               val = read_mmreg32(xecp + 4);
+               val &= ~((1 << 0) | (1 << 4) | (0x7 << 13));
+               /* These are write-to-clear. */
+               val |= 0x7 << 29;
+               write_mmreg32(xecp + 4, val);
+               printk("PCI XHCI %x:%x:%x: disabled legacy USB\n",
+                      pcidev->bus, pcidev->dev, pcidev->func);
+               return;
+       }
+       printk("PCI XHCI %x:%x:%x: couldn't find legacy capability\n",
+              pcidev->bus, pcidev->dev, pcidev->func);
+}
+
 static void uhci_disable_leg(struct pci_device *pcidev)
 {
        pcidev_write16(pcidev, 0xc0, 0x2000);
@@ -85,6 +140,9 @@ void usb_disable_legacy()
                                case 0x20:
                                        ehci_disable_leg(i);
                                        break;
+                               case 0x30:
+                                       xhci_disable_leg(i);
+                                       break;
                                default:
                                        /* TODO: ohci */
                                        printk("PCI USB %x:%x:%x, unknown progif 0x%x\n",