184860c5dSDavid du Colombier /*
284860c5dSDavid du Colombier * PC-specific code for
384860c5dSDavid du Colombier * USB Enhanced Host Controller Interface (EHCI) driver
484860c5dSDavid du Colombier * High speed USB 2.0.
584860c5dSDavid du Colombier */
684860c5dSDavid du Colombier
784860c5dSDavid du Colombier #include "u.h"
884860c5dSDavid du Colombier #include "../port/lib.h"
984860c5dSDavid du Colombier #include "mem.h"
1084860c5dSDavid du Colombier #include "dat.h"
1184860c5dSDavid du Colombier #include "fns.h"
1284860c5dSDavid du Colombier #include "io.h"
1384860c5dSDavid du Colombier #include "../port/error.h"
1484860c5dSDavid du Colombier #include "../port/usb.h"
15*5bdeb9a2SDavid du Colombier #include "../port/portusbehci.h"
1684860c5dSDavid du Colombier #include "usbehci.h"
1784860c5dSDavid du Colombier
1884860c5dSDavid du Colombier static Ctlr* ctlrs[Nhcis];
192a782499SDavid du Colombier static int maxehci = Nhcis;
2084860c5dSDavid du Colombier
2184860c5dSDavid du Colombier /* Isn't this cap list search in a helper function? */
2284860c5dSDavid du Colombier static void
getehci(Ctlr * ctlr)2384860c5dSDavid du Colombier getehci(Ctlr* ctlr)
2484860c5dSDavid du Colombier {
2584860c5dSDavid du Colombier int i, ptr, cap, sem;
2684860c5dSDavid du Colombier
2784860c5dSDavid du Colombier ptr = (ctlr->capio->capparms >> Ceecpshift) & Ceecpmask;
2884860c5dSDavid du Colombier for(; ptr != 0; ptr = pcicfgr8(ctlr->pcidev, ptr+1)){
2984860c5dSDavid du Colombier if(ptr < 0x40 || (ptr & ~0xFC))
3084860c5dSDavid du Colombier break;
3184860c5dSDavid du Colombier cap = pcicfgr8(ctlr->pcidev, ptr);
3284860c5dSDavid du Colombier if(cap != Clegacy)
3384860c5dSDavid du Colombier continue;
3484860c5dSDavid du Colombier sem = pcicfgr8(ctlr->pcidev, ptr+CLbiossem);
3584860c5dSDavid du Colombier if(sem == 0)
3684860c5dSDavid du Colombier continue;
3784860c5dSDavid du Colombier pcicfgw8(ctlr->pcidev, ptr+CLossem, 1);
3884860c5dSDavid du Colombier for(i = 0; i < 100; i++){
3984860c5dSDavid du Colombier if(pcicfgr8(ctlr->pcidev, ptr+CLbiossem) == 0)
4084860c5dSDavid du Colombier break;
4184860c5dSDavid du Colombier delay(10);
4284860c5dSDavid du Colombier }
4384860c5dSDavid du Colombier if(i == 100)
4484860c5dSDavid du Colombier dprint("ehci %#p: bios timed out\n", ctlr->capio);
4584860c5dSDavid du Colombier pcicfgw32(ctlr->pcidev, ptr+CLcontrol, 0); /* no SMIs */
4684860c5dSDavid du Colombier ctlr->opio->config = 0;
4784860c5dSDavid du Colombier coherence();
4884860c5dSDavid du Colombier return;
4984860c5dSDavid du Colombier }
5084860c5dSDavid du Colombier }
5184860c5dSDavid du Colombier
5284860c5dSDavid du Colombier static void
ehcireset(Ctlr * ctlr)5384860c5dSDavid du Colombier ehcireset(Ctlr *ctlr)
5484860c5dSDavid du Colombier {
5584860c5dSDavid du Colombier Eopio *opio;
5684860c5dSDavid du Colombier int i;
5784860c5dSDavid du Colombier
5884860c5dSDavid du Colombier ilock(ctlr);
5984860c5dSDavid du Colombier dprint("ehci %#p reset\n", ctlr->capio);
6084860c5dSDavid du Colombier opio = ctlr->opio;
6184860c5dSDavid du Colombier
6284860c5dSDavid du Colombier /*
6384860c5dSDavid du Colombier * Turn off legacy mode. Some controllers won't
6484860c5dSDavid du Colombier * interrupt us as expected otherwise.
6584860c5dSDavid du Colombier */
6684860c5dSDavid du Colombier ehcirun(ctlr, 0);
6784860c5dSDavid du Colombier pcicfgw16(ctlr->pcidev, 0xc0, 0x2000);
6884860c5dSDavid du Colombier
6984860c5dSDavid du Colombier /*
7084860c5dSDavid du Colombier * reclaim from bios
7184860c5dSDavid du Colombier */
7284860c5dSDavid du Colombier getehci(ctlr);
7384860c5dSDavid du Colombier
7484860c5dSDavid du Colombier /* clear high 32 bits of address signals if it's 64 bits capable.
7584860c5dSDavid du Colombier * This is probably not needed but it does not hurt and others do it.
7684860c5dSDavid du Colombier */
7784860c5dSDavid du Colombier if((ctlr->capio->capparms & C64) != 0){
7884860c5dSDavid du Colombier dprint("ehci: 64 bits\n");
7984860c5dSDavid du Colombier opio->seg = 0;
8084860c5dSDavid du Colombier coherence();
8184860c5dSDavid du Colombier }
8284860c5dSDavid du Colombier
8384860c5dSDavid du Colombier if(ehcidebugcapio != ctlr->capio){
8484860c5dSDavid du Colombier opio->cmd |= Chcreset; /* controller reset */
8584860c5dSDavid du Colombier coherence();
8684860c5dSDavid du Colombier for(i = 0; i < 100; i++){
8784860c5dSDavid du Colombier if((opio->cmd & Chcreset) == 0)
8884860c5dSDavid du Colombier break;
8984860c5dSDavid du Colombier delay(1);
9084860c5dSDavid du Colombier }
9184860c5dSDavid du Colombier if(i == 100)
9284860c5dSDavid du Colombier print("ehci %#p controller reset timed out\n", ctlr->capio);
9384860c5dSDavid du Colombier }
9484860c5dSDavid du Colombier
9584860c5dSDavid du Colombier /* requesting more interrupts per µframe may miss interrupts */
96*5bdeb9a2SDavid du Colombier opio->cmd &= ~Citcmask;
97*5bdeb9a2SDavid du Colombier opio->cmd |= 1 << Citcshift; /* max of 1 intr. per 125 µs */
9884860c5dSDavid du Colombier coherence();
9984860c5dSDavid du Colombier switch(opio->cmd & Cflsmask){
10084860c5dSDavid du Colombier case Cfls1024:
10184860c5dSDavid du Colombier ctlr->nframes = 1024;
10284860c5dSDavid du Colombier break;
10384860c5dSDavid du Colombier case Cfls512:
10484860c5dSDavid du Colombier ctlr->nframes = 512;
10584860c5dSDavid du Colombier break;
10684860c5dSDavid du Colombier case Cfls256:
10784860c5dSDavid du Colombier ctlr->nframes = 256;
10884860c5dSDavid du Colombier break;
10984860c5dSDavid du Colombier default:
11084860c5dSDavid du Colombier panic("ehci: unknown fls %ld", opio->cmd & Cflsmask);
11184860c5dSDavid du Colombier }
11284860c5dSDavid du Colombier dprint("ehci: %d frames\n", ctlr->nframes);
11384860c5dSDavid du Colombier iunlock(ctlr);
11484860c5dSDavid du Colombier }
11584860c5dSDavid du Colombier
11684860c5dSDavid du Colombier static void
setdebug(Hci *,int d)11784860c5dSDavid du Colombier setdebug(Hci*, int d)
11884860c5dSDavid du Colombier {
11984860c5dSDavid du Colombier ehcidebug = d;
12084860c5dSDavid du Colombier }
12184860c5dSDavid du Colombier
12284860c5dSDavid du Colombier static void
shutdown(Hci * hp)12384860c5dSDavid du Colombier shutdown(Hci *hp)
12484860c5dSDavid du Colombier {
12584860c5dSDavid du Colombier int i;
12684860c5dSDavid du Colombier Ctlr *ctlr;
12784860c5dSDavid du Colombier Eopio *opio;
12884860c5dSDavid du Colombier
12984860c5dSDavid du Colombier ctlr = hp->aux;
13084860c5dSDavid du Colombier ilock(ctlr);
13184860c5dSDavid du Colombier opio = ctlr->opio;
13284860c5dSDavid du Colombier opio->cmd |= Chcreset; /* controller reset */
13384860c5dSDavid du Colombier coherence();
13484860c5dSDavid du Colombier for(i = 0; i < 100; i++){
13584860c5dSDavid du Colombier if((opio->cmd & Chcreset) == 0)
13684860c5dSDavid du Colombier break;
13784860c5dSDavid du Colombier delay(1);
13884860c5dSDavid du Colombier }
13984860c5dSDavid du Colombier if(i >= 100)
14084860c5dSDavid du Colombier print("ehci %#p controller reset timed out\n", ctlr->capio);
14184860c5dSDavid du Colombier delay(100);
14284860c5dSDavid du Colombier ehcirun(ctlr, 0);
14384860c5dSDavid du Colombier opio->frbase = 0;
14484860c5dSDavid du Colombier iunlock(ctlr);
14584860c5dSDavid du Colombier }
14684860c5dSDavid du Colombier
14784860c5dSDavid du Colombier static void
scanpci(void)14884860c5dSDavid du Colombier scanpci(void)
14984860c5dSDavid du Colombier {
15084860c5dSDavid du Colombier static int already = 0;
15184860c5dSDavid du Colombier int i;
15284860c5dSDavid du Colombier ulong io;
15384860c5dSDavid du Colombier Ctlr *ctlr;
15484860c5dSDavid du Colombier Pcidev *p;
15584860c5dSDavid du Colombier Ecapio *capio;
15684860c5dSDavid du Colombier
15784860c5dSDavid du Colombier if(already)
15884860c5dSDavid du Colombier return;
15984860c5dSDavid du Colombier already = 1;
16084860c5dSDavid du Colombier p = nil;
16184860c5dSDavid du Colombier while ((p = pcimatch(p, 0, 0)) != nil) {
16284860c5dSDavid du Colombier /*
16384860c5dSDavid du Colombier * Find EHCI controllers (Programming Interface = 0x20).
16484860c5dSDavid du Colombier */
16584860c5dSDavid du Colombier if(p->ccrb != Pcibcserial || p->ccru != Pciscusb)
16684860c5dSDavid du Colombier continue;
16784860c5dSDavid du Colombier switch(p->ccrp){
16884860c5dSDavid du Colombier case 0x20:
16984860c5dSDavid du Colombier io = p->mem[0].bar & ~0x0f;
17084860c5dSDavid du Colombier break;
17184860c5dSDavid du Colombier default:
17284860c5dSDavid du Colombier continue;
17384860c5dSDavid du Colombier }
1742a782499SDavid du Colombier if(0 && p->vid == Vintel && p->did == 0x3b34) {
1752a782499SDavid du Colombier print("usbehci: ignoring known bad ctlr %#ux/%#ux\n",
1762a782499SDavid du Colombier p->vid, p->did);
1772a782499SDavid du Colombier continue;
1782a782499SDavid du Colombier }
17984860c5dSDavid du Colombier if(io == 0){
18084860c5dSDavid du Colombier print("usbehci: %x %x: failed to map registers\n",
18184860c5dSDavid du Colombier p->vid, p->did);
18284860c5dSDavid du Colombier continue;
18384860c5dSDavid du Colombier }
18484860c5dSDavid du Colombier if(p->intl == 0xff || p->intl == 0) {
18584860c5dSDavid du Colombier print("usbehci: no irq assigned for port %#lux\n", io);
18684860c5dSDavid du Colombier continue;
18784860c5dSDavid du Colombier }
18884860c5dSDavid du Colombier dprint("usbehci: %#x %#x: port %#lux size %#x irq %d\n",
18984860c5dSDavid du Colombier p->vid, p->did, io, p->mem[0].size, p->intl);
19084860c5dSDavid du Colombier
19106d03f7fSDavid du Colombier ctlr = malloc(sizeof(Ctlr));
19206d03f7fSDavid du Colombier if (ctlr == nil)
19306d03f7fSDavid du Colombier panic("usbehci: out of memory");
19484860c5dSDavid du Colombier ctlr->pcidev = p;
19584860c5dSDavid du Colombier capio = ctlr->capio = vmap(io, p->mem[0].size);
19684860c5dSDavid du Colombier ctlr->opio = (Eopio*)((uintptr)capio + (capio->cap & 0xff));
19784860c5dSDavid du Colombier pcisetbme(p);
19884860c5dSDavid du Colombier pcisetpms(p, 0);
19984860c5dSDavid du Colombier for(i = 0; i < Nhcis; i++)
20084860c5dSDavid du Colombier if(ctlrs[i] == nil){
20184860c5dSDavid du Colombier ctlrs[i] = ctlr;
20284860c5dSDavid du Colombier break;
20384860c5dSDavid du Colombier }
204b1c161c2SDavid du Colombier if(i >= Nhcis)
20584860c5dSDavid du Colombier print("ehci: bug: more than %d controllers\n", Nhcis);
20684860c5dSDavid du Colombier
20784860c5dSDavid du Colombier /*
208cc499a30SDavid du Colombier * currently, if we enable a second ehci controller on zt
209cc499a30SDavid du Colombier * systems w x58m motherboard, we'll wedge solid after iunlock
210cc499a30SDavid du Colombier * in init for the second one.
21184860c5dSDavid du Colombier */
212cc499a30SDavid du Colombier if (i >= maxehci) {
213cc499a30SDavid du Colombier print("usbehci: ignoring controllers after first %d, "
214cc499a30SDavid du Colombier "at %#p\n", maxehci, io);
215cc499a30SDavid du Colombier ctlrs[i] = nil;
21684860c5dSDavid du Colombier }
21784860c5dSDavid du Colombier }
21884860c5dSDavid du Colombier }
21984860c5dSDavid du Colombier
22084860c5dSDavid du Colombier static int
reset(Hci * hp)22184860c5dSDavid du Colombier reset(Hci *hp)
22284860c5dSDavid du Colombier {
22384860c5dSDavid du Colombier int i;
224cc499a30SDavid du Colombier char *s;
22584860c5dSDavid du Colombier Ctlr *ctlr;
22684860c5dSDavid du Colombier Ecapio *capio;
22784860c5dSDavid du Colombier Pcidev *p;
22884860c5dSDavid du Colombier static Lock resetlck;
22984860c5dSDavid du Colombier
230cc499a30SDavid du Colombier s = getconf("*maxehci");
231cc499a30SDavid du Colombier if (s != nil && s[0] >= '0' && s[0] <= '9')
232cc499a30SDavid du Colombier maxehci = atoi(s);
233cc499a30SDavid du Colombier if(maxehci == 0 || getconf("*nousbehci"))
23484860c5dSDavid du Colombier return -1;
235cc499a30SDavid du Colombier
23684860c5dSDavid du Colombier ilock(&resetlck);
23784860c5dSDavid du Colombier scanpci();
23884860c5dSDavid du Colombier
23984860c5dSDavid du Colombier /*
24084860c5dSDavid du Colombier * Any adapter matches if no hp->port is supplied,
24184860c5dSDavid du Colombier * otherwise the ports must match.
24284860c5dSDavid du Colombier */
24384860c5dSDavid du Colombier ctlr = nil;
24484860c5dSDavid du Colombier for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){
24584860c5dSDavid du Colombier ctlr = ctlrs[i];
24684860c5dSDavid du Colombier if(ctlr->active == 0)
24784860c5dSDavid du Colombier if(hp->port == 0 || hp->port == (uintptr)ctlr->capio){
24884860c5dSDavid du Colombier ctlr->active = 1;
24984860c5dSDavid du Colombier break;
25084860c5dSDavid du Colombier }
25184860c5dSDavid du Colombier }
25284860c5dSDavid du Colombier iunlock(&resetlck);
25384860c5dSDavid du Colombier if(i >= Nhcis || ctlrs[i] == nil)
25484860c5dSDavid du Colombier return -1;
25584860c5dSDavid du Colombier
25684860c5dSDavid du Colombier p = ctlr->pcidev;
25784860c5dSDavid du Colombier hp->aux = ctlr;
25884860c5dSDavid du Colombier hp->port = (uintptr)ctlr->capio;
25984860c5dSDavid du Colombier hp->irq = p->intl;
26084860c5dSDavid du Colombier hp->tbdf = p->tbdf;
26184860c5dSDavid du Colombier
26284860c5dSDavid du Colombier capio = ctlr->capio;
26384860c5dSDavid du Colombier hp->nports = capio->parms & Cnports;
26484860c5dSDavid du Colombier
26584860c5dSDavid du Colombier ddprint("echi: %s, ncc %lud npcc %lud\n",
26684860c5dSDavid du Colombier capio->parms & 0x10000 ? "leds" : "no leds",
26784860c5dSDavid du Colombier (capio->parms >> 12) & 0xf, (capio->parms >> 8) & 0xf);
26884860c5dSDavid du Colombier ddprint("ehci: routing %s, %sport power ctl, %d ports\n",
26984860c5dSDavid du Colombier capio->parms & 0x40 ? "explicit" : "automatic",
27084860c5dSDavid du Colombier capio->parms & 0x10 ? "" : "no ", hp->nports);
27184860c5dSDavid du Colombier
27284860c5dSDavid du Colombier ehcireset(ctlr);
27384860c5dSDavid du Colombier ehcimeminit(ctlr);
27484860c5dSDavid du Colombier
27584860c5dSDavid du Colombier /*
27684860c5dSDavid du Colombier * Linkage to the generic HCI driver.
27784860c5dSDavid du Colombier */
27884860c5dSDavid du Colombier ehcilinkage(hp);
27984860c5dSDavid du Colombier hp->shutdown = shutdown;
28084860c5dSDavid du Colombier hp->debug = setdebug;
28184860c5dSDavid du Colombier return 0;
28284860c5dSDavid du Colombier }
28384860c5dSDavid du Colombier
28484860c5dSDavid du Colombier void
usbehcilink(void)28584860c5dSDavid du Colombier usbehcilink(void)
28684860c5dSDavid du Colombier {
28784860c5dSDavid du Colombier addhcitype("ehci", reset);
28884860c5dSDavid du Colombier }
289