akaros/kern/arch/x86/usb.c
<<
>>
Prefs
   1/*
   2 * This file is part of the UCB release of Plan 9. It is subject to the license
   3 * terms in the LICENSE file found in the top-level directory of this
   4 * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
   5 * part of the UCB release of Plan 9, including this file, may be copied,
   6 * modified, propagated, or distributed except according to the terms contained
   7 * in the LICENSE file.
   8 *
   9 * Adapted from usbehcipc.c and usbuhci.c
  10 */
  11
  12#define Clegacy                                 1
  13#define CLbiossem                               2
  14#define CLossem                                 3
  15#define CLcontrol                               4
  16
  17#include <arch/x86.h>
  18#include <arch/pci.h>
  19#include <trap.h>
  20#include <stdio.h>
  21#include <string.h>
  22#include <assert.h>
  23#include <kmalloc.h>
  24#include <time.h>
  25#include <mm.h>
  26
  27static void ehci_disable_leg(struct pci_device *pcidev)
  28{
  29        int i, ptr, cap, sem;
  30
  31        //ptr = (ctlr->capio->capparms >> Ceecpshift) & Ceecpmask;
  32        uintptr_t bar0 = pci_get_membar(pcidev, 0);
  33        assert(bar0);
  34        uintptr_t ehci_hcc_regs = vmap_pmem_nocache(bar0,
  35                                                    pcidev->bar[0].mmio_sz);
  36        uint32_t hccparams = read_mmreg32(ehci_hcc_regs + 0x08);
  37
  38        ptr = (hccparams >> 8) & ((1 << 8) - 1);
  39
  40        for (; ptr != 0; ptr = pcidev_read8(pcidev, ptr + 1)) {
  41                if (ptr < 0x40 || (ptr & ~0xFC))
  42                        break;
  43                cap = pcidev_read8(pcidev, ptr);
  44                if (cap != Clegacy)
  45                        continue;
  46                sem = pcidev_read8(pcidev, ptr + CLbiossem);
  47                if (sem == 0)
  48                        continue;
  49                pcidev_write8(pcidev, ptr + CLossem, 1);
  50                for (i = 0; i < 100; i++) {
  51                        if (pcidev_read8(pcidev, ptr + CLbiossem) == 0)
  52                                break;
  53                        udelay(10);
  54                }
  55                if (i == 100)
  56                        printk("PCI EHCI %x:%x:%x: bios timed out\n",
  57                               pcidev->bus, pcidev->dev, pcidev->func);
  58                /* bit 29 could be left on, in case we want to give it back */
  59                pcidev_write32(pcidev, ptr + CLcontrol, 0);     /* no SMIs */
  60                //ctlr->opio->config = 0;
  61                //coherence();
  62                printk("PCI EHCI %x:%x:%x: disabled legacy USB\n",
  63                       pcidev->bus, pcidev->dev, pcidev->func);
  64                return;
  65        }
  66        printk("PCI EHCI %x:%x:%x: couldn't find legacy capability\n",
  67               pcidev->bus, pcidev->dev, pcidev->func);
  68}
  69
  70#define XHCI_USBLEGSUP 1
  71
  72static void xhci_disable_leg(struct pci_device *pcidev)
  73{
  74        uintptr_t bar0, xhci_hcc_regs, xecp;
  75        uint32_t hccparams, val;
  76        int i;
  77
  78        bar0 = pci_get_membar(pcidev, 0);
  79        assert(bar0);
  80        xhci_hcc_regs = vmap_pmem_nocache(bar0, pcidev->bar[0].mmio_sz);
  81        hccparams = read_mmreg32(xhci_hcc_regs + 0x10);
  82        xecp = (hccparams >> 16) & 0xffff;
  83
  84        /* xecp is the rel offset, in 32 bit words, from the base to the
  85         * extended capabilities pointer. */
  86        for (/* xecp set */; xecp; xecp = (read_mmreg32(xecp) >> 8) & 0xff) {
  87                xecp = xhci_hcc_regs + (xecp << 2);
  88                val = read_mmreg32(xecp);
  89
  90                if ((val & 0xff) != XHCI_USBLEGSUP)
  91                        continue;
  92                /* bios already does not own it */
  93                if (!(val & (1 << 16)))
  94                        return;
  95                /* take ownership.  Note we're allowed to do byte-width writes
  96                 * here. */
  97                write_mmreg8(xecp + 3, 1);
  98                /* book says to wait up to a second, though i regularly see it
  99                 * time out on my machines. */
 100                for (i = 0; i < 100000; i++) {
 101                        if (!(read_mmreg32(xecp) & (1 << 16)))
 102                                break;
 103                        udelay(10);
 104                }
 105                if (i == 100000) {
 106                        printk("PCI XHCI %x:%x:%x: bios timed out\n",
 107                               pcidev->bus, pcidev->dev, pcidev->func);
 108                        /* Force the bios's byte clear */
 109                        write_mmreg8(xecp + 2, 0);
 110                }
 111                /* Turn off settings in USBLEGCTLSTS.  Not sure if any of this
 112                 * is necessary. */
 113                val = read_mmreg32(xecp + 4);
 114                val &= ~((1 << 0) | (1 << 4) | (0x7 << 13));
 115                /* These are write-to-clear. */
 116                val |= 0x7 << 29;
 117                write_mmreg32(xecp + 4, val);
 118                printk("PCI XHCI %x:%x:%x: disabled legacy USB\n",
 119                       pcidev->bus, pcidev->dev, pcidev->func);
 120                return;
 121        }
 122        printk("PCI XHCI %x:%x:%x: couldn't find legacy capability\n",
 123               pcidev->bus, pcidev->dev, pcidev->func);
 124}
 125
 126static void uhci_disable_leg(struct pci_device *pcidev)
 127{
 128        pcidev_write16(pcidev, 0xc0, 0x2000);
 129        printk("PCI UHCI %x:%x:%x: disabled legacy USB\n",
 130               pcidev->bus, pcidev->dev, pcidev->func);
 131}
 132
 133void usb_disable_legacy(void)
 134{
 135        struct pci_device *i;
 136
 137        STAILQ_FOREACH(i, &pci_devices, all_dev) {
 138                if ((i->class == 0x0c) && (i->subclass == 0x03)) {
 139                        switch (i->progif) {
 140                        case 0x00:
 141                                uhci_disable_leg(i);
 142                                break;
 143                        case 0x20:
 144                                ehci_disable_leg(i);
 145                                break;
 146                        case 0x30:
 147                                xhci_disable_leg(i);
 148                                break;
 149                        default:
 150                                /* TODO: ohci */
 151                                printk("PCI USB %x:%x:%x unknown progif 0x%x\n",
 152                                       i->bus, i->dev, i->func, i->progif);
 153                        }
 154                }
 155        }
 156}
 157