blob: 7f8e7f066ac4d1e5c14512ea26e3d18cf4d463dd [file] [log] [blame]
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*
* Adapted from usbehcipc.c and usbuhci.c
*/
#define Clegacy 1
#define CLbiossem 2
#define CLossem 3
#define CLcontrol 4
#include <arch/x86.h>
#include <arch/pci.h>
#include <trap.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <kmalloc.h>
#include <time.h>
#include <mm.h>
static void ehci_disable_leg(struct pci_device *pcidev)
{
int i, ptr, cap, sem;
//ptr = (ctlr->capio->capparms >> Ceecpshift) & Ceecpmask;
uintptr_t bar0 = pci_get_membar(pcidev, 0);
assert(bar0);
uintptr_t ehci_hcc_regs = vmap_pmem_nocache(bar0,
pcidev->bar[0].mmio_sz);
uint32_t hccparams = read_mmreg32(ehci_hcc_regs + 0x08);
ptr = (hccparams >> 8) & ((1 << 8) - 1);
for (; ptr != 0; ptr = pcidev_read8(pcidev, ptr + 1)) {
if (ptr < 0x40 || (ptr & ~0xFC))
break;
cap = pcidev_read8(pcidev, ptr);
if (cap != Clegacy)
continue;
sem = pcidev_read8(pcidev, ptr + CLbiossem);
if (sem == 0)
continue;
pcidev_write8(pcidev, ptr + CLossem, 1);
for (i = 0; i < 100; i++) {
if (pcidev_read8(pcidev, ptr + CLbiossem) == 0)
break;
udelay(10);
}
if (i == 100)
printk("PCI EHCI %x:%x:%x: bios timed out\n",
pcidev->bus, pcidev->dev, pcidev->func);
/* bit 29 could be left on, in case we want to give it back */
pcidev_write32(pcidev, ptr + CLcontrol, 0); /* no SMIs */
//ctlr->opio->config = 0;
//coherence();
printk("PCI EHCI %x:%x:%x: disabled legacy USB\n",
pcidev->bus, pcidev->dev, pcidev->func);
return;
}
printk("PCI EHCI %x:%x:%x: couldn't find legacy capability\n",
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);
printk("PCI UHCI %x:%x:%x: disabled legacy USB\n",
pcidev->bus, pcidev->dev, pcidev->func);
}
void usb_disable_legacy(void)
{
struct pci_device *i;
STAILQ_FOREACH(i, &pci_devices, all_dev) {
if ((i->class == 0x0c) && (i->subclass == 0x03)) {
switch (i->progif) {
case 0x00:
uhci_disable_leg(i);
break;
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",
i->bus, i->dev, i->func, i->progif);
}
}
}
}