net: Add network_offset to blocks
[akaros.git] / kern / arch / x86 / usb.c
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
27 static 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, pcidev->bar[0].mmio_sz);
35         uint32_t hccparams = read_mmreg32(ehci_hcc_regs + 0x08);
36         ptr = (hccparams >> 8) & ((1 << 8) - 1);
37
38         for(; ptr != 0; ptr = pcidev_read8(pcidev, ptr + 1)) {
39                 if (ptr < 0x40 || (ptr & ~0xFC))
40                         break;
41                 cap = pcidev_read8(pcidev, ptr);
42                 if (cap != Clegacy)
43                         continue;
44                 sem = pcidev_read8(pcidev, ptr + CLbiossem);
45                 if (sem == 0)
46                         continue;
47                 pcidev_write8(pcidev, ptr + CLossem, 1);
48                 for (i = 0; i < 100; i++) {
49                         if (pcidev_read8(pcidev, ptr + CLbiossem) == 0)
50                                 break;
51                         udelay(10);
52                 }
53                 if (i == 100)
54                         printk("PCI EHCI %x:%x:%x: bios timed out\n",
55                                pcidev->bus, pcidev->dev, pcidev->func);
56                 /* bit 29 could be left on, in case we want to give it back */
57                 pcidev_write32(pcidev, ptr + CLcontrol, 0);     /* no SMIs */
58                 //ctlr->opio->config = 0;
59                 //coherence();
60                 printk("PCI EHCI %x:%x:%x: disabled legacy USB\n",
61                        pcidev->bus, pcidev->dev, pcidev->func);
62                 return;
63         }
64         printk("PCI EHCI %x:%x:%x: couldn't find legacy capability\n",
65                pcidev->bus, pcidev->dev, pcidev->func);
66 }
67
68 #define XHCI_USBLEGSUP 1
69
70 static void xhci_disable_leg(struct pci_device *pcidev)
71 {
72         uintptr_t bar0, xhci_hcc_regs, xecp;
73         uint32_t hccparams, val;
74         int i;
75
76         bar0 = pci_get_membar(pcidev, 0);
77         assert(bar0);
78         xhci_hcc_regs = vmap_pmem_nocache(bar0, pcidev->bar[0].mmio_sz);
79         hccparams = read_mmreg32(xhci_hcc_regs + 0x10);
80         xecp = (hccparams >> 16) & 0xffff;
81
82         /* xecp is the rel offset, in 32 bit words, from the base to the extended
83          * capabilities pointer. */
84         for (/* xecp set */; xecp; xecp = (read_mmreg32(xecp) >> 8) & 0xff) {
85                 xecp = xhci_hcc_regs + (xecp << 2);
86                 val = read_mmreg32(xecp);
87
88                 if ((val & 0xff) != XHCI_USBLEGSUP)
89                         continue;
90                 /* bios already does not own it */
91                 if (!(val & (1 << 16)))
92                         return;
93                 /* take ownership.  Note we're allowed to do byte-width writes here. */
94                 write_mmreg8(xecp + 3, 1);
95                 /* book says to wait up to a second, though i regularly see it time out
96                  * on my machines. */
97                 for (i = 0; i < 100000; i++) {
98                         if (!(read_mmreg32(xecp) & (1 << 16)))
99                                 break;
100                         udelay(10);
101                 }
102                 if (i == 100000) {
103                         printk("PCI XHCI %x:%x:%x: bios timed out\n",
104                                pcidev->bus, pcidev->dev, pcidev->func);
105                         /* Force the bios's byte clear */
106                         write_mmreg8(xecp + 2, 0);
107                 }
108                 /* Turn off settings in USBLEGCTLSTS.  Not sure if any of this is
109                  * necessary. */
110                 val = read_mmreg32(xecp + 4);
111                 val &= ~((1 << 0) | (1 << 4) | (0x7 << 13));
112                 /* These are write-to-clear. */
113                 val |= 0x7 << 29;
114                 write_mmreg32(xecp + 4, val);
115                 printk("PCI XHCI %x:%x:%x: disabled legacy USB\n",
116                        pcidev->bus, pcidev->dev, pcidev->func);
117                 return;
118         }
119         printk("PCI XHCI %x:%x:%x: couldn't find legacy capability\n",
120                pcidev->bus, pcidev->dev, pcidev->func);
121 }
122
123 static void uhci_disable_leg(struct pci_device *pcidev)
124 {
125         pcidev_write16(pcidev, 0xc0, 0x2000);
126         printk("PCI UHCI %x:%x:%x: disabled legacy USB\n",
127                pcidev->bus, pcidev->dev, pcidev->func);
128 }
129
130 void usb_disable_legacy()
131 {
132         struct pci_device *i;
133
134         STAILQ_FOREACH(i, &pci_devices, all_dev) {
135                 if ((i->class == 0x0c) && (i->subclass == 0x03)) {
136                         switch (i->progif) {
137                                 case 0x00:
138                                         uhci_disable_leg(i);
139                                         break;
140                                 case 0x20:
141                                         ehci_disable_leg(i);
142                                         break;
143                                 case 0x30:
144                                         xhci_disable_leg(i);
145                                         break;
146                                 default:
147                                         /* TODO: ohci */
148                                         printk("PCI USB %x:%x:%x, unknown progif 0x%x\n",
149                                                i->bus, i->dev, i->func, i->progif);
150                         }
151                 }
152         }
153 }