diff options
| -rw-r--r-- | sys/src/9/pc/usbohci.c | 134 |
1 files changed, 84 insertions, 50 deletions
diff --git a/sys/src/9/pc/usbohci.c b/sys/src/9/pc/usbohci.c index 522a8df39..93428c3d2 100644 --- a/sys/src/9/pc/usbohci.c +++ b/sys/src/9/pc/usbohci.c @@ -108,6 +108,7 @@ enum Cie = 0x08, /* iso. list enable */ Ccle = 0x10, /* ctl list enable */ Cble = 0x20, /* bulk list enable */ + Cir = 0x100, /* interrupt routing (smm active) */ Cfsmask = 3 << 6, /* functional state... */ Cfsreset = 0 << 6, Cfsresume = 1 << 6, @@ -115,6 +116,7 @@ enum Cfssuspend = 3 << 6, /* command status */ + Socr = 1 << 3, /* ownership change request */ Sblf = 1 << 2, /* bulk list (load) flag */ Sclf = 1 << 1, /* control list (load) flag */ Shcr = 1 << 0, /* host controller reset */ @@ -588,7 +590,7 @@ tdalloc(void) ddprint("ohci: tdalloc %d Tds\n", Incr); pool = xspanalloc(Incr*sizeof(Td), Align, 0); if(pool == nil) - panic("tdalloc"); + panic("ohci: tdalloc"); for(i=Incr; --i>=0;){ pool[i].next = tdpool.free; tdpool.free = &pool[i]; @@ -636,7 +638,7 @@ edalloc(void) ddprint("ohci: edalloc %d Eds\n", Incr); pool = xspanalloc(Incr*sizeof(Ed), Align, 0); if(pool == nil) - panic("edalloc"); + panic("ohci: edalloc"); for(i=Incr; --i>=0;){ pool[i].next = edpool.free; edpool.free = &pool[i]; @@ -1145,6 +1147,7 @@ qhinterrupt(Ctlr *, Ep *ep, Qio *io, Td *td, int) switch(err){ case Tddataovr: /* Overrun is not an error */ + break; case Tdok: /* virtualbox doesn't always report underflow on short packets */ if(td->cbp == 0) @@ -1263,57 +1266,66 @@ isointerrupt(Ctlr *ctlr, Ep *ep, Qio *io, Td *td, int) static void interrupt(Ureg *, void *arg) { - Td *td, *ntd, *td0; + Td *td, *ntd; Hci *hp; Ctlr *ctlr; - ulong status, curred; + ulong status, curred, done; int i, frno; hp = arg; ctlr = hp->aux; ilock(ctlr); + done = ctlr->hcca->donehead; status = ctlr->ohci->intrsts; + if(status == ~0){ + iunlock(ctlr); + return; + } + if(done & ~0xF){ + ctlr->hcca->donehead = 0; + status |= Wdh; + } + else if(status & Wdh){ + done = ctlr->hcca->donehead; + ctlr->hcca->donehead = 0; + } + status &= ~Mie; + if(status == 0){ + iunlock(ctlr); + return; + } + ctlr->ohci->intrsts = status; status &= ctlr->ohci->intrenable; status &= Oc|Rhsc|Fno|Ue|Rd|Sf|Wdh|So; - frno = TRUNC(ctlr->ohci->fmnumber, Ntdframes); - if((status & Wdh) != 0){ - /* lsb of donehead has bit to flag other intrs. */ - td = pa2ptr(ctlr->hcca->donehead & ~0xF); - }else - td = nil; - td0 = td; - - for(i = 0; td != nil && i < 1024; i++){ - if(0)ddprint("ohci tdinterrupt: td %#p\n", td); - ntd = pa2ptr(td->nexttd & ~0xF); - td->nexttd = 0; - if(td->ep == nil || td->io == nil) - panic("ohci: interrupt: ep %#p io %#p", td->ep, td->io); - ohciinterrupts[td->ep->ttype]++; - if(td->ep->ttype == Tiso) - isointerrupt(ctlr, td->ep, td->io, td, frno); - else - qhinterrupt(ctlr, td->ep, td->io, td, frno); - td = ntd; + if(status & Wdh){ + frno = TRUNC(ctlr->ohci->fmnumber, Ntdframes); + td = pa2ptr(done & ~0xF); + for(i = 0; td != nil && i < 1024; i++){ + if(0)ddprint("ohci tdinterrupt: td %#p\n", td); + ntd = pa2ptr(td->nexttd & ~0xF); + td->nexttd = 0; + if(td->ep == nil || td->io == nil) + panic("ohci: interrupt: ep %#p io %#p", td->ep, td->io); + ohciinterrupts[td->ep->ttype]++; + if(td->ep->ttype == Tiso) + isointerrupt(ctlr, td->ep, td->io, td, frno); + else + qhinterrupt(ctlr, td->ep, td->io, td, frno); + td = ntd; + } + if(i == 1024) + iprint("ohci: bug: more than 1024 done Tds?\n"); + status &= ~Wdh; } - if(i == 1024) - print("ohci: bug: more than 1024 done Tds?\n"); - - if(pa2ptr(ctlr->hcca->donehead & ~0xF) != td0) - print("ohci: bug: donehead changed before ack\n"); - ctlr->hcca->donehead = 0; - - ctlr->ohci->intrsts = status; - status &= ~Wdh; status &= ~Sf; if(status & So){ - print("ohci: sched overrun: too much load\n"); + iprint("ohci: sched overrun: too much load\n"); ctlr->overrun++; status &= ~So; } - if((status & Ue) != 0){ + if(status & Ue){ curred = ctlr->ohci->periodcurred; - print("ohci: unrecoverable error frame 0x%.8lux ed 0x%.8lux, " + iprint("ohci: unrecoverable error frame 0x%.8lux ed 0x%.8lux, " "ints %d %d %d %d\n", ctlr->ohci->fmnumber, curred, ohciinterrupts[Tctl], ohciinterrupts[Tintr], @@ -1322,8 +1334,10 @@ interrupt(Ureg *, void *arg) dumped(pa2ptr(curred)); status &= ~Ue; } - if(status != 0) - print("ohci interrupt: unhandled sts 0x%.8lux\n", status); + if(status != 0){ + iprint("ohci interrupt: unhandled sts 0x%.8lux\n", status); + ctlr->ohci->intrdisable = status; + } iunlock(ctlr); } @@ -2301,11 +2315,16 @@ init(Hci *hp) dprint("ohci %#p init\n", ctlr->ohci); ohci = ctlr->ohci; - fmi = ctlr->ohci->fminterval; - ctlr->ohci->cmdsts = Shcr; /* reset the block */ - while(ctlr->ohci->cmdsts & Shcr) - delay(1); /* wait till reset complete, Ohci says 10us max. */ - ctlr->ohci->fminterval = fmi; + fmi = ohci->fminterval; + ohci->cmdsts = Shcr; /* reset the block */ + for(i = 0; i<100; i++){ + if((ohci->cmdsts & Shcr) == 0) + break; + delay(1); /* wait till reset complete, Ohci says 10us max. */ + } + if(i == 100) + print("ohci: reset timed out\n"); + ohci->fminterval = fmi; /* * now that soft reset is done we are in suspend state. @@ -2379,13 +2398,13 @@ scanpci(void) dprint("ohci: %x/%x port 0x%lux size 0x%x irq %d\n", p->vid, p->did, mem, p->mem[0].size, p->intl); if(mem == 0){ - print("usbohci: failed to map registers\n"); + print("ohci: failed to map registers\n"); continue; } ctlr = malloc(sizeof(Ctlr)); if(ctlr == nil){ - print("usbohci: no memory\n"); + print("ohci: no memory\n"); continue; } ctlr->pcidev = p; @@ -2399,7 +2418,7 @@ scanpci(void) break; } if(i == Nhcis) - print("usbohci: bug: no more controllers\n"); + print("ohci: bug: no more controllers\n"); } } @@ -2424,16 +2443,16 @@ mkqhtree(Ctlr *ctlr) n = (1 << (depth+1)) - 1; qt = mallocz(sizeof(*qt), 1); if(qt == nil) - panic("usb: can't allocate scheduling tree"); + panic("ohci: can't allocate scheduling tree"); qt->nel = n; qt->depth = depth; qt->bw = mallocz(n * sizeof(qt->bw), 1); qt->root = tree = mallocz(n * sizeof(Ed *), 1); if(qt->bw == nil || qt->root == nil) - panic("usb: can't allocate scheduling tree"); + panic("ohci: can't allocate scheduling tree"); for(i = 0; i < n; i++){ if((tree[i] = edalloc()) == nil) - panic("mkqhtree"); + panic("ohci: mkqhtree"); tree[i]->ctrl = (8 << Edmpsshift); /* not needed */ tree[i]->ctrl |= Edskip; @@ -2473,7 +2492,7 @@ ohcimeminit(Ctlr *ctlr) hcca = xspanalloc(sizeof(Hcca), 256, 0); if(hcca == nil) - panic("usbhreset: no memory for Hcca"); + panic("ohci: no memory for Hcca"); memset(hcca, 0, sizeof(*hcca)); ctlr->hcca = hcca; @@ -2483,9 +2502,23 @@ ohcimeminit(Ctlr *ctlr) static void ohcireset(Ctlr *ctlr) { + int i; + ilock(ctlr); dprint("ohci %#p reset\n", ctlr->ohci); + if(ctlr->ohci->control & Cir){ + dprint("ohci: smm active, taking over\n"); + ctlr->ohci->cmdsts |= Socr; /* take ownership */ + for(i = 0; i<100; i++){ + if((ctlr->ohci->control & Cir) == 0) + break; + delay(1); + } + if(i == 100) + print("ohci: smm takeover timed out\n"); + } + /* * usually enter here in reset, wait till its through, * then do our own so we are on known timing conditions. @@ -2509,6 +2542,7 @@ shutdown(Hci *hp) ctlr = hp->aux; ilock(ctlr); + ctlr->ohci->intrdisable = Mie; ctlr->ohci->intrenable = 0; ctlr->ohci->control = 0; delay(100); |
