15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier * USB host driver for BCM2835
35d9de2d3SDavid du Colombier * Synopsis DesignWare Core USB 2.0 OTG controller
45d9de2d3SDavid du Colombier *
55d9de2d3SDavid du Colombier * Copyright © 2012 Richard Miller <r.miller@acm.org>
65d9de2d3SDavid du Colombier *
75d9de2d3SDavid du Colombier * This is work in progress:
85d9de2d3SDavid du Colombier * - no isochronous pipes
95d9de2d3SDavid du Colombier * - no bandwidth budgeting
105d9de2d3SDavid du Colombier * - frame scheduling is crude
115d9de2d3SDavid du Colombier * - error handling is overly optimistic
125d9de2d3SDavid du Colombier * It should be just about adequate for a Plan 9 terminal with
135d9de2d3SDavid du Colombier * keyboard, mouse, ethernet adapter, and an external flash drive.
145d9de2d3SDavid du Colombier */
155d9de2d3SDavid du Colombier
165d9de2d3SDavid du Colombier #include "u.h"
175d9de2d3SDavid du Colombier #include "../port/lib.h"
185d9de2d3SDavid du Colombier #include "mem.h"
195d9de2d3SDavid du Colombier #include "dat.h"
205d9de2d3SDavid du Colombier #include "fns.h"
215d9de2d3SDavid du Colombier #include "io.h"
225d9de2d3SDavid du Colombier #include "../port/error.h"
23*5c47fe09SDavid du Colombier #include "usb.h"
245d9de2d3SDavid du Colombier
255d9de2d3SDavid du Colombier #include "dwcotg.h"
265d9de2d3SDavid du Colombier
275d9de2d3SDavid du Colombier enum
285d9de2d3SDavid du Colombier {
295d9de2d3SDavid du Colombier USBREGS = VIRTIO + 0x980000,
305d9de2d3SDavid du Colombier Enabledelay = 50,
315d9de2d3SDavid du Colombier Resetdelay = 10,
325d9de2d3SDavid du Colombier ResetdelayHS = 50,
335d9de2d3SDavid du Colombier
345d9de2d3SDavid du Colombier Read = 0,
355d9de2d3SDavid du Colombier Write = 1,
36*5c47fe09SDavid du Colombier
37*5c47fe09SDavid du Colombier /*
38*5c47fe09SDavid du Colombier * Workaround for an unexplained glitch where an Ack interrupt
39*5c47fe09SDavid du Colombier * is received without Chhltd, whereupon all channels remain
40*5c47fe09SDavid du Colombier * permanently busy and can't be halted. This was only seen
41*5c47fe09SDavid du Colombier * when the controller is reading a sequence of bulk input
42*5c47fe09SDavid du Colombier * packets in DMA mode. Setting Slowbulkin=1 will avoid the
43*5c47fe09SDavid du Colombier * lockup by reading packets individually with an interrupt
44*5c47fe09SDavid du Colombier * after each. More recent chips don't seem to exhibit the
45*5c47fe09SDavid du Colombier * problem, so it's probably safe to leave this off now.
46*5c47fe09SDavid du Colombier */
47*5c47fe09SDavid du Colombier Slowbulkin = 0,
485d9de2d3SDavid du Colombier };
495d9de2d3SDavid du Colombier
505d9de2d3SDavid du Colombier typedef struct Ctlr Ctlr;
515d9de2d3SDavid du Colombier typedef struct Epio Epio;
525d9de2d3SDavid du Colombier
535d9de2d3SDavid du Colombier struct Ctlr {
54*5c47fe09SDavid du Colombier Lock;
555d9de2d3SDavid du Colombier Dwcregs *regs; /* controller registers */
565d9de2d3SDavid du Colombier int nchan; /* number of host channels */
575d9de2d3SDavid du Colombier ulong chanbusy; /* bitmap of in-use channels */
585d9de2d3SDavid du Colombier QLock chanlock; /* serialise access to chanbusy */
595d9de2d3SDavid du Colombier QLock split; /* serialise split transactions */
605d9de2d3SDavid du Colombier int splitretry; /* count retries of Nyet */
615d9de2d3SDavid du Colombier int sofchan; /* bitmap of channels waiting for sof */
625d9de2d3SDavid du Colombier int wakechan; /* bitmap of channels to wakeup after fiq */
635d9de2d3SDavid du Colombier int debugchan; /* bitmap of channels for interrupt debug */
645d9de2d3SDavid du Colombier Rendez *chanintr; /* sleep till interrupt on channel N */
655d9de2d3SDavid du Colombier };
665d9de2d3SDavid du Colombier
675d9de2d3SDavid du Colombier struct Epio {
68*5c47fe09SDavid du Colombier union {
69*5c47fe09SDavid du Colombier QLock rlock;
70*5c47fe09SDavid du Colombier QLock ctllock;
71*5c47fe09SDavid du Colombier };
72*5c47fe09SDavid du Colombier QLock wlock;
735d9de2d3SDavid du Colombier Block *cb;
745d9de2d3SDavid du Colombier ulong lastpoll;
755d9de2d3SDavid du Colombier };
765d9de2d3SDavid du Colombier
775d9de2d3SDavid du Colombier static Ctlr dwc;
785d9de2d3SDavid du Colombier static int debug;
795d9de2d3SDavid du Colombier
805d9de2d3SDavid du Colombier static char Ebadlen[] = "bad usb request length";
815d9de2d3SDavid du Colombier
825d9de2d3SDavid du Colombier static void clog(Ep *ep, Hostchan *hc);
835d9de2d3SDavid du Colombier static void logdump(Ep *ep);
845d9de2d3SDavid du Colombier
85*5c47fe09SDavid du Colombier static void
filock(Lock * l)86*5c47fe09SDavid du Colombier filock(Lock *l)
87*5c47fe09SDavid du Colombier {
88*5c47fe09SDavid du Colombier int x;
89*5c47fe09SDavid du Colombier
90*5c47fe09SDavid du Colombier x = splfhi();
91*5c47fe09SDavid du Colombier ilock(l);
92*5c47fe09SDavid du Colombier l->sr = x;
93*5c47fe09SDavid du Colombier }
94*5c47fe09SDavid du Colombier
95*5c47fe09SDavid du Colombier static void
fiunlock(Lock * l)96*5c47fe09SDavid du Colombier fiunlock(Lock *l)
97*5c47fe09SDavid du Colombier {
98*5c47fe09SDavid du Colombier iunlock(l);
99*5c47fe09SDavid du Colombier }
100*5c47fe09SDavid du Colombier
1015d9de2d3SDavid du Colombier static Hostchan*
chanalloc(Ep * ep)1025d9de2d3SDavid du Colombier chanalloc(Ep *ep)
1035d9de2d3SDavid du Colombier {
1045d9de2d3SDavid du Colombier Ctlr *ctlr;
1055d9de2d3SDavid du Colombier int bitmap, i;
106*5c47fe09SDavid du Colombier static int first;
1075d9de2d3SDavid du Colombier
1085d9de2d3SDavid du Colombier ctlr = ep->hp->aux;
109*5c47fe09SDavid du Colombier retry:
1105d9de2d3SDavid du Colombier qlock(&ctlr->chanlock);
1115d9de2d3SDavid du Colombier bitmap = ctlr->chanbusy;
1125d9de2d3SDavid du Colombier for(i = 0; i < ctlr->nchan; i++)
1135d9de2d3SDavid du Colombier if((bitmap & (1<<i)) == 0){
1145d9de2d3SDavid du Colombier ctlr->chanbusy = bitmap | 1<<i;
1155d9de2d3SDavid du Colombier qunlock(&ctlr->chanlock);
1165d9de2d3SDavid du Colombier return &ctlr->regs->hchan[i];
1175d9de2d3SDavid du Colombier }
1185d9de2d3SDavid du Colombier qunlock(&ctlr->chanlock);
119*5c47fe09SDavid du Colombier if(!first++)
120*5c47fe09SDavid du Colombier print("usbdwc: all host channels busy - retrying\n");
121*5c47fe09SDavid du Colombier tsleep(&up->sleep, return0, 0, 1);
122*5c47fe09SDavid du Colombier goto retry;
1235d9de2d3SDavid du Colombier }
1245d9de2d3SDavid du Colombier
1255d9de2d3SDavid du Colombier static void
chanrelease(Ep * ep,Hostchan * chan)1265d9de2d3SDavid du Colombier chanrelease(Ep *ep, Hostchan *chan)
1275d9de2d3SDavid du Colombier {
1285d9de2d3SDavid du Colombier Ctlr *ctlr;
1295d9de2d3SDavid du Colombier int i;
1305d9de2d3SDavid du Colombier
1315d9de2d3SDavid du Colombier ctlr = ep->hp->aux;
1325d9de2d3SDavid du Colombier i = chan - ctlr->regs->hchan;
1335d9de2d3SDavid du Colombier qlock(&ctlr->chanlock);
1345d9de2d3SDavid du Colombier ctlr->chanbusy &= ~(1<<i);
1355d9de2d3SDavid du Colombier qunlock(&ctlr->chanlock);
1365d9de2d3SDavid du Colombier }
1375d9de2d3SDavid du Colombier
1385d9de2d3SDavid du Colombier static void
chansetup(Hostchan * hc,Ep * ep)1395d9de2d3SDavid du Colombier chansetup(Hostchan *hc, Ep *ep)
1405d9de2d3SDavid du Colombier {
1415d9de2d3SDavid du Colombier int hcc;
1425d9de2d3SDavid du Colombier Ctlr *ctlr = ep->hp->aux;
1435d9de2d3SDavid du Colombier
1445d9de2d3SDavid du Colombier if(ep->debug)
1455d9de2d3SDavid du Colombier ctlr->debugchan |= 1 << (hc - ctlr->regs->hchan);
1465d9de2d3SDavid du Colombier else
1475d9de2d3SDavid du Colombier ctlr->debugchan &= ~(1 << (hc - ctlr->regs->hchan));
1485d9de2d3SDavid du Colombier switch(ep->dev->state){
1495d9de2d3SDavid du Colombier case Dconfig:
1505d9de2d3SDavid du Colombier case Dreset:
1515d9de2d3SDavid du Colombier hcc = 0;
1525d9de2d3SDavid du Colombier break;
1535d9de2d3SDavid du Colombier default:
1545d9de2d3SDavid du Colombier hcc = ep->dev->nb<<ODevaddr;
1555d9de2d3SDavid du Colombier break;
1565d9de2d3SDavid du Colombier }
1575d9de2d3SDavid du Colombier hcc |= ep->maxpkt | 1<<OMulticnt | ep->nb<<OEpnum;
1585d9de2d3SDavid du Colombier switch(ep->ttype){
1595d9de2d3SDavid du Colombier case Tctl:
1605d9de2d3SDavid du Colombier hcc |= Epctl;
1615d9de2d3SDavid du Colombier break;
1625d9de2d3SDavid du Colombier case Tiso:
1635d9de2d3SDavid du Colombier hcc |= Episo;
1645d9de2d3SDavid du Colombier break;
1655d9de2d3SDavid du Colombier case Tbulk:
1665d9de2d3SDavid du Colombier hcc |= Epbulk;
1675d9de2d3SDavid du Colombier break;
1685d9de2d3SDavid du Colombier case Tintr:
1695d9de2d3SDavid du Colombier hcc |= Epintr;
1705d9de2d3SDavid du Colombier break;
1715d9de2d3SDavid du Colombier }
1725d9de2d3SDavid du Colombier switch(ep->dev->speed){
1735d9de2d3SDavid du Colombier case Lowspeed:
1745d9de2d3SDavid du Colombier hcc |= Lspddev;
1755d9de2d3SDavid du Colombier /* fall through */
1765d9de2d3SDavid du Colombier case Fullspeed:
177fe823997SDavid du Colombier if(ep->dev->hub > 1){
1785d9de2d3SDavid du Colombier hc->hcsplt = Spltena | POS_ALL | ep->dev->hub<<OHubaddr |
1795d9de2d3SDavid du Colombier ep->dev->port;
1805d9de2d3SDavid du Colombier break;
181fe823997SDavid du Colombier }
182fe823997SDavid du Colombier /* fall through */
1835d9de2d3SDavid du Colombier default:
1845d9de2d3SDavid du Colombier hc->hcsplt = 0;
1855d9de2d3SDavid du Colombier break;
1865d9de2d3SDavid du Colombier }
1875d9de2d3SDavid du Colombier hc->hcchar = hcc;
1885d9de2d3SDavid du Colombier hc->hcint = ~0;
1895d9de2d3SDavid du Colombier }
1905d9de2d3SDavid du Colombier
1915d9de2d3SDavid du Colombier static int
sofdone(void * a)1925d9de2d3SDavid du Colombier sofdone(void *a)
1935d9de2d3SDavid du Colombier {
1945d9de2d3SDavid du Colombier Dwcregs *r;
1955d9de2d3SDavid du Colombier
1965d9de2d3SDavid du Colombier r = a;
197*5c47fe09SDavid du Colombier return (r->gintmsk & Sofintr) == 0;
1985d9de2d3SDavid du Colombier }
1995d9de2d3SDavid du Colombier
2005d9de2d3SDavid du Colombier static void
sofwait(Ctlr * ctlr,int n)2015d9de2d3SDavid du Colombier sofwait(Ctlr *ctlr, int n)
2025d9de2d3SDavid du Colombier {
2035d9de2d3SDavid du Colombier Dwcregs *r;
2045d9de2d3SDavid du Colombier
2055d9de2d3SDavid du Colombier r = ctlr->regs;
2065d9de2d3SDavid du Colombier do{
207*5c47fe09SDavid du Colombier filock(ctlr);
2085d9de2d3SDavid du Colombier r->gintsts = Sofintr;
2095d9de2d3SDavid du Colombier ctlr->sofchan |= 1<<n;
2105d9de2d3SDavid du Colombier r->gintmsk |= Sofintr;
211*5c47fe09SDavid du Colombier fiunlock(ctlr);
2125d9de2d3SDavid du Colombier sleep(&ctlr->chanintr[n], sofdone, r);
2135d9de2d3SDavid du Colombier }while((r->hfnum & 7) == 6);
2145d9de2d3SDavid du Colombier }
2155d9de2d3SDavid du Colombier
2165d9de2d3SDavid du Colombier static int
chandone(void * a)2175d9de2d3SDavid du Colombier chandone(void *a)
2185d9de2d3SDavid du Colombier {
2195d9de2d3SDavid du Colombier Hostchan *hc;
2205d9de2d3SDavid du Colombier
2215d9de2d3SDavid du Colombier hc = a;
222fe823997SDavid du Colombier if(hc->hcint == (Chhltd|Ack))
223fe823997SDavid du Colombier return 0;
2245d9de2d3SDavid du Colombier return (hc->hcint & hc->hcintmsk) != 0;
2255d9de2d3SDavid du Colombier }
2265d9de2d3SDavid du Colombier
2275d9de2d3SDavid du Colombier static int
chanwait(Ep * ep,Ctlr * ctlr,Hostchan * hc,int mask)2285d9de2d3SDavid du Colombier chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
2295d9de2d3SDavid du Colombier {
230*5c47fe09SDavid du Colombier int intr, n, ointr;
2315d9de2d3SDavid du Colombier ulong start, now;
2325d9de2d3SDavid du Colombier Dwcregs *r;
2335d9de2d3SDavid du Colombier
2345d9de2d3SDavid du Colombier r = ctlr->regs;
2355d9de2d3SDavid du Colombier n = hc - r->hchan;
2365d9de2d3SDavid du Colombier for(;;){
2375d9de2d3SDavid du Colombier restart:
238*5c47fe09SDavid du Colombier filock(ctlr);
2395d9de2d3SDavid du Colombier r->haintmsk |= 1<<n;
2405d9de2d3SDavid du Colombier hc->hcintmsk = mask;
241*5c47fe09SDavid du Colombier fiunlock(ctlr);
242*5c47fe09SDavid du Colombier tsleep(&ctlr->chanintr[n], chandone, hc, 1000);
243*5c47fe09SDavid du Colombier if((intr = hc->hcint) == 0)
244*5c47fe09SDavid du Colombier goto restart;
2455d9de2d3SDavid du Colombier hc->hcintmsk = 0;
2465d9de2d3SDavid du Colombier if(intr & Chhltd)
2475d9de2d3SDavid du Colombier return intr;
2485d9de2d3SDavid du Colombier start = fastticks(0);
2495d9de2d3SDavid du Colombier ointr = intr;
2505d9de2d3SDavid du Colombier now = start;
2515d9de2d3SDavid du Colombier do{
2525d9de2d3SDavid du Colombier intr = hc->hcint;
2535d9de2d3SDavid du Colombier if(intr & Chhltd){
2545d9de2d3SDavid du Colombier if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
2555d9de2d3SDavid du Colombier intr != (Ack|Chhltd|Xfercomp) ||
2565d9de2d3SDavid du Colombier (now - start) > 60)
257*5c47fe09SDavid du Colombier dprint("await %x after %ldµs %x -> %x\n",
2585d9de2d3SDavid du Colombier mask, now - start, ointr, intr);
2595d9de2d3SDavid du Colombier return intr;
2605d9de2d3SDavid du Colombier }
2615d9de2d3SDavid du Colombier if((intr & mask) == 0){
262*5c47fe09SDavid du Colombier if(intr != Nak)
263*5c47fe09SDavid du Colombier dprint("ep%d.%d await %x after %ldµs intr %x -> %x\n",
264*5c47fe09SDavid du Colombier ep->dev->nb, ep->nb, mask, now - start, ointr, intr);
2655d9de2d3SDavid du Colombier goto restart;
2665d9de2d3SDavid du Colombier }
2675d9de2d3SDavid du Colombier now = fastticks(0);
2685d9de2d3SDavid du Colombier }while(now - start < 100);
2695d9de2d3SDavid du Colombier dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
2705d9de2d3SDavid du Colombier "grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
2715d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
2725d9de2d3SDavid du Colombier r->gnptxsts, r->hptxsts);
2735d9de2d3SDavid du Colombier mask = Chhltd;
2745d9de2d3SDavid du Colombier hc->hcchar |= Chdis;
2755d9de2d3SDavid du Colombier start = m->ticks;
2765d9de2d3SDavid du Colombier while(hc->hcchar & Chen){
2775d9de2d3SDavid du Colombier if(m->ticks - start >= 100){
2785d9de2d3SDavid du Colombier print("ep%d.%d channel won't halt hcchar %8.8ux\n",
2795d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, hc->hcchar);
2805d9de2d3SDavid du Colombier break;
2815d9de2d3SDavid du Colombier }
2825d9de2d3SDavid du Colombier }
2835d9de2d3SDavid du Colombier logdump(ep);
2845d9de2d3SDavid du Colombier }
2855d9de2d3SDavid du Colombier }
2865d9de2d3SDavid du Colombier
2875d9de2d3SDavid du Colombier static int
chanintr(Ctlr * ctlr,int n)2885d9de2d3SDavid du Colombier chanintr(Ctlr *ctlr, int n)
2895d9de2d3SDavid du Colombier {
2905d9de2d3SDavid du Colombier Hostchan *hc;
2915d9de2d3SDavid du Colombier int i;
2925d9de2d3SDavid du Colombier
2935d9de2d3SDavid du Colombier hc = &ctlr->regs->hchan[n];
294*5c47fe09SDavid du Colombier if((hc->hcint & hc->hcintmsk) == 0)
295*5c47fe09SDavid du Colombier return 1;
2965d9de2d3SDavid du Colombier if(ctlr->debugchan & (1<<n))
2975d9de2d3SDavid du Colombier clog(nil, hc);
2985d9de2d3SDavid du Colombier if((hc->hcsplt & Spltena) == 0)
2995d9de2d3SDavid du Colombier return 0;
3005d9de2d3SDavid du Colombier i = hc->hcint;
3015d9de2d3SDavid du Colombier if(i == (Chhltd|Ack)){
3025d9de2d3SDavid du Colombier hc->hcsplt |= Compsplt;
3035d9de2d3SDavid du Colombier ctlr->splitretry = 0;
3045d9de2d3SDavid du Colombier }else if(i == (Chhltd|Nyet)){
3055d9de2d3SDavid du Colombier if(++ctlr->splitretry >= 3)
3065d9de2d3SDavid du Colombier return 0;
3075d9de2d3SDavid du Colombier }else
3085d9de2d3SDavid du Colombier return 0;
3095d9de2d3SDavid du Colombier if(hc->hcchar & Chen){
3105d9de2d3SDavid du Colombier iprint("hcchar %8.8ux hcint %8.8ux", hc->hcchar, hc->hcint);
3115d9de2d3SDavid du Colombier hc->hcchar |= Chen | Chdis;
3125d9de2d3SDavid du Colombier while(hc->hcchar&Chen)
3135d9de2d3SDavid du Colombier ;
3145d9de2d3SDavid du Colombier iprint(" %8.8ux\n", hc->hcint);
3155d9de2d3SDavid du Colombier }
3165d9de2d3SDavid du Colombier hc->hcint = i;
3175d9de2d3SDavid du Colombier if(ctlr->regs->hfnum & 1)
3185d9de2d3SDavid du Colombier hc->hcchar &= ~Oddfrm;
3195d9de2d3SDavid du Colombier else
3205d9de2d3SDavid du Colombier hc->hcchar |= Oddfrm;
3215d9de2d3SDavid du Colombier hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
3225d9de2d3SDavid du Colombier return 1;
3235d9de2d3SDavid du Colombier }
3245d9de2d3SDavid du Colombier
3255d9de2d3SDavid du Colombier static Reg chanlog[32][5];
3265d9de2d3SDavid du Colombier static int nchanlog;
3275d9de2d3SDavid du Colombier
3285d9de2d3SDavid du Colombier static void
logstart(Ep * ep)3295d9de2d3SDavid du Colombier logstart(Ep *ep)
3305d9de2d3SDavid du Colombier {
3315d9de2d3SDavid du Colombier if(ep->debug)
3325d9de2d3SDavid du Colombier nchanlog = 0;
3335d9de2d3SDavid du Colombier }
3345d9de2d3SDavid du Colombier
3355d9de2d3SDavid du Colombier static void
clog(Ep * ep,Hostchan * hc)3365d9de2d3SDavid du Colombier clog(Ep *ep, Hostchan *hc)
3375d9de2d3SDavid du Colombier {
3385d9de2d3SDavid du Colombier Reg *p;
3395d9de2d3SDavid du Colombier
3405d9de2d3SDavid du Colombier if(ep != nil && !ep->debug)
3415d9de2d3SDavid du Colombier return;
3425d9de2d3SDavid du Colombier if(nchanlog == 32)
3435d9de2d3SDavid du Colombier nchanlog--;
3445d9de2d3SDavid du Colombier p = chanlog[nchanlog];
3455d9de2d3SDavid du Colombier p[0] = dwc.regs->hfnum;
3465d9de2d3SDavid du Colombier p[1] = hc->hcchar;
3475d9de2d3SDavid du Colombier p[2] = hc->hcint;
3485d9de2d3SDavid du Colombier p[3] = hc->hctsiz;
3495d9de2d3SDavid du Colombier p[4] = hc->hcdma;
3505d9de2d3SDavid du Colombier nchanlog++;
3515d9de2d3SDavid du Colombier }
3525d9de2d3SDavid du Colombier
3535d9de2d3SDavid du Colombier static void
logdump(Ep * ep)3545d9de2d3SDavid du Colombier logdump(Ep *ep)
3555d9de2d3SDavid du Colombier {
3565d9de2d3SDavid du Colombier Reg *p;
3575d9de2d3SDavid du Colombier int i;
3585d9de2d3SDavid du Colombier
3595d9de2d3SDavid du Colombier if(!ep->debug)
3605d9de2d3SDavid du Colombier return;
3615d9de2d3SDavid du Colombier p = chanlog[0];
3625d9de2d3SDavid du Colombier for(i = 0; i < nchanlog; i++){
3635d9de2d3SDavid du Colombier print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n",
3645d9de2d3SDavid du Colombier p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]);
3655d9de2d3SDavid du Colombier p += 5;
3665d9de2d3SDavid du Colombier }
3675d9de2d3SDavid du Colombier nchanlog = 0;
3685d9de2d3SDavid du Colombier }
3695d9de2d3SDavid du Colombier
3705d9de2d3SDavid du Colombier static int
chanio(Ep * ep,Hostchan * hc,int dir,int pid,void * a,int len)3715d9de2d3SDavid du Colombier chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
3725d9de2d3SDavid du Colombier {
3735d9de2d3SDavid du Colombier Ctlr *ctlr;
3745d9de2d3SDavid du Colombier int nleft, n, nt, i, maxpkt, npkt;
3755d9de2d3SDavid du Colombier uint hcdma, hctsiz;
3765d9de2d3SDavid du Colombier
3775d9de2d3SDavid du Colombier ctlr = ep->hp->aux;
3785d9de2d3SDavid du Colombier maxpkt = ep->maxpkt;
3795d9de2d3SDavid du Colombier npkt = HOWMANY(len, ep->maxpkt);
3805d9de2d3SDavid du Colombier if(npkt == 0)
3815d9de2d3SDavid du Colombier npkt = 1;
3825d9de2d3SDavid du Colombier
3835d9de2d3SDavid du Colombier hc->hcchar = (hc->hcchar & ~Epdir) | dir;
3845d9de2d3SDavid du Colombier if(dir == Epin)
3855d9de2d3SDavid du Colombier n = ROUND(len, ep->maxpkt);
3865d9de2d3SDavid du Colombier else
3875d9de2d3SDavid du Colombier n = len;
3885d9de2d3SDavid du Colombier hc->hctsiz = n | npkt<<OPktcnt | pid;
389*5c47fe09SDavid du Colombier hc->hcdma = dmaaddr(a);
3905d9de2d3SDavid du Colombier
3915d9de2d3SDavid du Colombier nleft = len;
3925d9de2d3SDavid du Colombier logstart(ep);
3935d9de2d3SDavid du Colombier for(;;){
3945d9de2d3SDavid du Colombier hcdma = hc->hcdma;
3955d9de2d3SDavid du Colombier hctsiz = hc->hctsiz;
3965d9de2d3SDavid du Colombier hc->hctsiz = hctsiz & ~Dopng;
3975d9de2d3SDavid du Colombier if(hc->hcchar&Chen){
3985d9de2d3SDavid du Colombier dprint("ep%d.%d before chanio hcchar=%8.8ux\n",
3995d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, hc->hcchar);
4005d9de2d3SDavid du Colombier hc->hcchar |= Chen | Chdis;
4015d9de2d3SDavid du Colombier while(hc->hcchar&Chen)
4025d9de2d3SDavid du Colombier ;
4035d9de2d3SDavid du Colombier hc->hcint = Chhltd;
4045d9de2d3SDavid du Colombier }
4055d9de2d3SDavid du Colombier if((i = hc->hcint) != 0){
4065d9de2d3SDavid du Colombier dprint("ep%d.%d before chanio hcint=%8.8ux\n",
4075d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, i);
4085d9de2d3SDavid du Colombier hc->hcint = i;
4095d9de2d3SDavid du Colombier }
4105d9de2d3SDavid du Colombier if(hc->hcsplt & Spltena){
4115d9de2d3SDavid du Colombier qlock(&ctlr->split);
4125d9de2d3SDavid du Colombier sofwait(ctlr, hc - ctlr->regs->hchan);
4135d9de2d3SDavid du Colombier if((dwc.regs->hfnum & 1) == 0)
4145d9de2d3SDavid du Colombier hc->hcchar &= ~Oddfrm;
4155d9de2d3SDavid du Colombier else
4165d9de2d3SDavid du Colombier hc->hcchar |= Oddfrm;
4175d9de2d3SDavid du Colombier }
4185d9de2d3SDavid du Colombier hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
4195d9de2d3SDavid du Colombier clog(ep, hc);
420*5c47fe09SDavid du Colombier wait:
4215d9de2d3SDavid du Colombier if(ep->ttype == Tbulk && dir == Epin)
422*5c47fe09SDavid du Colombier i = chanwait(ep, ctlr, hc, Chhltd);
4235d9de2d3SDavid du Colombier else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
4245d9de2d3SDavid du Colombier i = chanwait(ep, ctlr, hc, Chhltd);
4255d9de2d3SDavid du Colombier else
4265d9de2d3SDavid du Colombier i = chanwait(ep, ctlr, hc, Chhltd|Nak);
4275d9de2d3SDavid du Colombier clog(ep, hc);
428*5c47fe09SDavid du Colombier if(hc->hcint != i){
429*5c47fe09SDavid du Colombier dprint("chanwait intr %ux->%ux\n", i, hc->hcint);
430*5c47fe09SDavid du Colombier if((i = hc->hcint) == 0)
431*5c47fe09SDavid du Colombier goto wait;
432*5c47fe09SDavid du Colombier }
4335d9de2d3SDavid du Colombier hc->hcint = i;
4345d9de2d3SDavid du Colombier
4355d9de2d3SDavid du Colombier if(hc->hcsplt & Spltena){
4365d9de2d3SDavid du Colombier hc->hcsplt &= ~Compsplt;
4375d9de2d3SDavid du Colombier qunlock(&ctlr->split);
4385d9de2d3SDavid du Colombier }
4395d9de2d3SDavid du Colombier
4405d9de2d3SDavid du Colombier if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
4415d9de2d3SDavid du Colombier if(i & Stall)
4425d9de2d3SDavid du Colombier error(Estalled);
443fe823997SDavid du Colombier if(i & (Nyet|Frmovrun))
4445d9de2d3SDavid du Colombier continue;
4455d9de2d3SDavid du Colombier if(i & Nak){
4465d9de2d3SDavid du Colombier if(ep->ttype == Tintr)
4475d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival);
4485d9de2d3SDavid du Colombier else
4495d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, 1);
4505d9de2d3SDavid du Colombier continue;
4515d9de2d3SDavid du Colombier }
452fe823997SDavid du Colombier logdump(ep);
453*5c47fe09SDavid du Colombier print("usbdwc: ep%d.%d error intr %8.8ux\n",
4545d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, i);
4555d9de2d3SDavid du Colombier if(i & ~(Chhltd|Ack))
4565d9de2d3SDavid du Colombier error(Eio);
4575d9de2d3SDavid du Colombier if(hc->hcdma != hcdma)
458*5c47fe09SDavid du Colombier print("usbdwc: weird hcdma %ux->%ux intr %ux->%ux\n",
4595d9de2d3SDavid du Colombier hcdma, hc->hcdma, i, hc->hcint);
4605d9de2d3SDavid du Colombier }
4615d9de2d3SDavid du Colombier n = hc->hcdma - hcdma;
462b4d1cf41SDavid du Colombier if(n == 0){
4635d9de2d3SDavid du Colombier if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt))
4645d9de2d3SDavid du Colombier break;
4655d9de2d3SDavid du Colombier else
4665d9de2d3SDavid du Colombier continue;
467b4d1cf41SDavid du Colombier }
468*5c47fe09SDavid du Colombier if(dir == Epin && ep->ttype == Tbulk){
4695d9de2d3SDavid du Colombier nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
470b4d1cf41SDavid du Colombier if(nt != n){
471b4d1cf41SDavid du Colombier if(n == ROUND(nt, 4))
4725d9de2d3SDavid du Colombier n = nt;
4735d9de2d3SDavid du Colombier else
474*5c47fe09SDavid du Colombier print("usbdwc: intr %8.8ux "
475b4d1cf41SDavid du Colombier "dma %8.8ux-%8.8ux "
476b4d1cf41SDavid du Colombier "hctsiz %8.8ux-%8.ux\n",
4775d9de2d3SDavid du Colombier i, hcdma, hc->hcdma, hctsiz,
4785d9de2d3SDavid du Colombier hc->hctsiz);
4795d9de2d3SDavid du Colombier }
480b4d1cf41SDavid du Colombier }
4815d9de2d3SDavid du Colombier if(n > nleft){
482b4d1cf41SDavid du Colombier if(n != ROUND(nleft, 4))
4835d9de2d3SDavid du Colombier dprint("too much: wanted %d got %d\n",
4845d9de2d3SDavid du Colombier len, len - nleft + n);
4855d9de2d3SDavid du Colombier n = nleft;
4865d9de2d3SDavid du Colombier }
4875d9de2d3SDavid du Colombier nleft -= n;
488b4d1cf41SDavid du Colombier if(nleft == 0 || (n % maxpkt) != 0)
4895d9de2d3SDavid du Colombier break;
4905d9de2d3SDavid du Colombier if((i & Xfercomp) && ep->ttype != Tctl)
4915d9de2d3SDavid du Colombier break;
4925d9de2d3SDavid du Colombier if(dir == Epout)
4935d9de2d3SDavid du Colombier dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n",
4945d9de2d3SDavid du Colombier nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i);
4955d9de2d3SDavid du Colombier }
4965d9de2d3SDavid du Colombier logdump(ep);
4975d9de2d3SDavid du Colombier return len - nleft;
4985d9de2d3SDavid du Colombier }
4995d9de2d3SDavid du Colombier
5005d9de2d3SDavid du Colombier static long
multitrans(Ep * ep,Hostchan * hc,int rw,void * a,long n)501fe823997SDavid du Colombier multitrans(Ep *ep, Hostchan *hc, int rw, void *a, long n)
502fe823997SDavid du Colombier {
503fe823997SDavid du Colombier long sofar, m;
504fe823997SDavid du Colombier
505fe823997SDavid du Colombier sofar = 0;
506fe823997SDavid du Colombier do{
507fe823997SDavid du Colombier m = n - sofar;
508fe823997SDavid du Colombier if(m > ep->maxpkt)
509fe823997SDavid du Colombier m = ep->maxpkt;
510fe823997SDavid du Colombier m = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
511fe823997SDavid du Colombier (char*)a + sofar, m);
512fe823997SDavid du Colombier ep->toggle[rw] = hc->hctsiz & Pid;
513fe823997SDavid du Colombier sofar += m;
514fe823997SDavid du Colombier }while(sofar < n && m == ep->maxpkt);
515fe823997SDavid du Colombier return sofar;
516fe823997SDavid du Colombier }
517fe823997SDavid du Colombier
518fe823997SDavid du Colombier static long
eptrans(Ep * ep,int rw,void * a,long n)5195d9de2d3SDavid du Colombier eptrans(Ep *ep, int rw, void *a, long n)
5205d9de2d3SDavid du Colombier {
5215d9de2d3SDavid du Colombier Hostchan *hc;
5225d9de2d3SDavid du Colombier
5235d9de2d3SDavid du Colombier if(ep->clrhalt){
5245d9de2d3SDavid du Colombier ep->clrhalt = 0;
5255d9de2d3SDavid du Colombier if(ep->mode != OREAD)
5265d9de2d3SDavid du Colombier ep->toggle[Write] = DATA0;
5275d9de2d3SDavid du Colombier if(ep->mode != OWRITE)
5285d9de2d3SDavid du Colombier ep->toggle[Read] = DATA0;
5295d9de2d3SDavid du Colombier }
5305d9de2d3SDavid du Colombier hc = chanalloc(ep);
5315d9de2d3SDavid du Colombier if(waserror()){
5325d9de2d3SDavid du Colombier ep->toggle[rw] = hc->hctsiz & Pid;
5335d9de2d3SDavid du Colombier chanrelease(ep, hc);
5345d9de2d3SDavid du Colombier if(strcmp(up->errstr, Estalled) == 0)
5355d9de2d3SDavid du Colombier return 0;
5365d9de2d3SDavid du Colombier nexterror();
5375d9de2d3SDavid du Colombier }
5385d9de2d3SDavid du Colombier chansetup(hc, ep);
539*5c47fe09SDavid du Colombier if(Slowbulkin && rw == Read && ep->ttype == Tbulk)
540fe823997SDavid du Colombier n = multitrans(ep, hc, rw, a, n);
541fe823997SDavid du Colombier else{
5425d9de2d3SDavid du Colombier n = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
5435d9de2d3SDavid du Colombier a, n);
5445d9de2d3SDavid du Colombier ep->toggle[rw] = hc->hctsiz & Pid;
5455d9de2d3SDavid du Colombier }
5465d9de2d3SDavid du Colombier chanrelease(ep, hc);
5475d9de2d3SDavid du Colombier poperror();
5485d9de2d3SDavid du Colombier return n;
5495d9de2d3SDavid du Colombier }
5505d9de2d3SDavid du Colombier
5515d9de2d3SDavid du Colombier static long
ctltrans(Ep * ep,uchar * req,long n)5525d9de2d3SDavid du Colombier ctltrans(Ep *ep, uchar *req, long n)
5535d9de2d3SDavid du Colombier {
5545d9de2d3SDavid du Colombier Hostchan *hc;
5555d9de2d3SDavid du Colombier Epio *epio;
5565d9de2d3SDavid du Colombier Block *b;
5575d9de2d3SDavid du Colombier uchar *data;
5585d9de2d3SDavid du Colombier int datalen;
5595d9de2d3SDavid du Colombier
5605d9de2d3SDavid du Colombier epio = ep->aux;
5615d9de2d3SDavid du Colombier if(epio->cb != nil){
5625d9de2d3SDavid du Colombier freeb(epio->cb);
5635d9de2d3SDavid du Colombier epio->cb = nil;
5645d9de2d3SDavid du Colombier }
5655d9de2d3SDavid du Colombier if(n < Rsetuplen)
5665d9de2d3SDavid du Colombier error(Ebadlen);
5675d9de2d3SDavid du Colombier if(req[Rtype] & Rd2h){
5685d9de2d3SDavid du Colombier datalen = GET2(req+Rcount);
5695d9de2d3SDavid du Colombier if(datalen <= 0 || datalen > Maxctllen)
5705d9de2d3SDavid du Colombier error(Ebadlen);
5715d9de2d3SDavid du Colombier /* XXX cache madness */
572*5c47fe09SDavid du Colombier epio->cb = b = allocb(ROUND(datalen, ep->maxpkt));
573*5c47fe09SDavid du Colombier assert(((uintptr)b->wp & (BLOCKALIGN-1)) == 0);
5745d9de2d3SDavid du Colombier memset(b->wp, 0x55, b->lim - b->wp);
5755d9de2d3SDavid du Colombier cachedwbinvse(b->wp, b->lim - b->wp);
5765d9de2d3SDavid du Colombier data = b->wp;
5775d9de2d3SDavid du Colombier }else{
5785d9de2d3SDavid du Colombier b = nil;
5795d9de2d3SDavid du Colombier datalen = n - Rsetuplen;
5805d9de2d3SDavid du Colombier data = req + Rsetuplen;
5815d9de2d3SDavid du Colombier }
5825d9de2d3SDavid du Colombier hc = chanalloc(ep);
5835d9de2d3SDavid du Colombier if(waserror()){
5845d9de2d3SDavid du Colombier chanrelease(ep, hc);
5855d9de2d3SDavid du Colombier if(strcmp(up->errstr, Estalled) == 0)
5865d9de2d3SDavid du Colombier return 0;
5875d9de2d3SDavid du Colombier nexterror();
5885d9de2d3SDavid du Colombier }
5895d9de2d3SDavid du Colombier chansetup(hc, ep);
5905d9de2d3SDavid du Colombier chanio(ep, hc, Epout, SETUP, req, Rsetuplen);
5915d9de2d3SDavid du Colombier if(req[Rtype] & Rd2h){
592fe823997SDavid du Colombier if(ep->dev->hub <= 1){
593fe823997SDavid du Colombier ep->toggle[Read] = DATA1;
594fe823997SDavid du Colombier b->wp += multitrans(ep, hc, Read, data, datalen);
595fe823997SDavid du Colombier }else
5965d9de2d3SDavid du Colombier b->wp += chanio(ep, hc, Epin, DATA1, data, datalen);
5975d9de2d3SDavid du Colombier chanio(ep, hc, Epout, DATA1, nil, 0);
598*5c47fe09SDavid du Colombier cachedinvse(b->rp, BLEN(b));
5995d9de2d3SDavid du Colombier n = Rsetuplen;
6005d9de2d3SDavid du Colombier }else{
6015d9de2d3SDavid du Colombier if(datalen > 0)
6025d9de2d3SDavid du Colombier chanio(ep, hc, Epout, DATA1, data, datalen);
6035d9de2d3SDavid du Colombier chanio(ep, hc, Epin, DATA1, nil, 0);
6045d9de2d3SDavid du Colombier n = Rsetuplen + datalen;
6055d9de2d3SDavid du Colombier }
6065d9de2d3SDavid du Colombier chanrelease(ep, hc);
6075d9de2d3SDavid du Colombier poperror();
6085d9de2d3SDavid du Colombier return n;
6095d9de2d3SDavid du Colombier }
6105d9de2d3SDavid du Colombier
6115d9de2d3SDavid du Colombier static long
ctldata(Ep * ep,void * a,long n)6125d9de2d3SDavid du Colombier ctldata(Ep *ep, void *a, long n)
6135d9de2d3SDavid du Colombier {
6145d9de2d3SDavid du Colombier Epio *epio;
6155d9de2d3SDavid du Colombier Block *b;
6165d9de2d3SDavid du Colombier
6175d9de2d3SDavid du Colombier epio = ep->aux;
6185d9de2d3SDavid du Colombier b = epio->cb;
6195d9de2d3SDavid du Colombier if(b == nil)
6205d9de2d3SDavid du Colombier return 0;
6215d9de2d3SDavid du Colombier if(n > BLEN(b))
6225d9de2d3SDavid du Colombier n = BLEN(b);
6235d9de2d3SDavid du Colombier memmove(a, b->rp, n);
6245d9de2d3SDavid du Colombier b->rp += n;
6255d9de2d3SDavid du Colombier if(BLEN(b) == 0){
6265d9de2d3SDavid du Colombier freeb(b);
6275d9de2d3SDavid du Colombier epio->cb = nil;
6285d9de2d3SDavid du Colombier }
6295d9de2d3SDavid du Colombier return n;
6305d9de2d3SDavid du Colombier }
6315d9de2d3SDavid du Colombier
6325d9de2d3SDavid du Colombier static void
greset(Dwcregs * r,int bits)6335d9de2d3SDavid du Colombier greset(Dwcregs *r, int bits)
6345d9de2d3SDavid du Colombier {
6355d9de2d3SDavid du Colombier r->grstctl |= bits;
6365d9de2d3SDavid du Colombier while(r->grstctl & bits)
6375d9de2d3SDavid du Colombier ;
6385d9de2d3SDavid du Colombier microdelay(10);
6395d9de2d3SDavid du Colombier }
6405d9de2d3SDavid du Colombier
6415d9de2d3SDavid du Colombier static void
init(Hci * hp)6425d9de2d3SDavid du Colombier init(Hci *hp)
6435d9de2d3SDavid du Colombier {
6445d9de2d3SDavid du Colombier Ctlr *ctlr;
6455d9de2d3SDavid du Colombier Dwcregs *r;
6465d9de2d3SDavid du Colombier uint n, rx, tx, ptx;
6475d9de2d3SDavid du Colombier
6485d9de2d3SDavid du Colombier ctlr = hp->aux;
6495d9de2d3SDavid du Colombier r = ctlr->regs;
6505d9de2d3SDavid du Colombier
6515d9de2d3SDavid du Colombier ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan);
6525d9de2d3SDavid du Colombier ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez));
6535d9de2d3SDavid du Colombier
6545d9de2d3SDavid du Colombier r->gahbcfg = 0;
6555d9de2d3SDavid du Colombier setpower(PowerUsb, 1);
6565d9de2d3SDavid du Colombier
6575d9de2d3SDavid du Colombier while((r->grstctl&Ahbidle) == 0)
6585d9de2d3SDavid du Colombier ;
6595d9de2d3SDavid du Colombier greset(r, Csftrst);
6605d9de2d3SDavid du Colombier
6615d9de2d3SDavid du Colombier r->gusbcfg |= Force_host_mode;
6625d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, 25);
6635d9de2d3SDavid du Colombier r->gahbcfg |= Dmaenable;
6645d9de2d3SDavid du Colombier
6655d9de2d3SDavid du Colombier n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth;
6665d9de2d3SDavid du Colombier rx = 0x306;
6675d9de2d3SDavid du Colombier tx = 0x100;
6685d9de2d3SDavid du Colombier ptx = 0x200;
6695d9de2d3SDavid du Colombier r->grxfsiz = rx;
6705d9de2d3SDavid du Colombier r->gnptxfsiz = rx | tx<<ODepth;
6715d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, 1);
6725d9de2d3SDavid du Colombier r->hptxfsiz = (rx + tx) | ptx << ODepth;
6735d9de2d3SDavid du Colombier greset(r, Rxfflsh);
6745d9de2d3SDavid du Colombier r->grstctl = TXF_ALL;
6755d9de2d3SDavid du Colombier greset(r, Txfflsh);
676*5c47fe09SDavid du Colombier dprint("usbdwc: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
6775d9de2d3SDavid du Colombier n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz);
6785d9de2d3SDavid du Colombier
6795d9de2d3SDavid du Colombier r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
6805d9de2d3SDavid du Colombier r->gintsts = ~0;
6815d9de2d3SDavid du Colombier r->gintmsk = Hcintr;
6825d9de2d3SDavid du Colombier r->gahbcfg |= Glblintrmsk;
6835d9de2d3SDavid du Colombier }
6845d9de2d3SDavid du Colombier
6855d9de2d3SDavid du Colombier static void
dump(Hci *)6865d9de2d3SDavid du Colombier dump(Hci*)
6875d9de2d3SDavid du Colombier {
6885d9de2d3SDavid du Colombier }
6895d9de2d3SDavid du Colombier
6905d9de2d3SDavid du Colombier static void
fiqintr(Ureg *,void * a)6915d9de2d3SDavid du Colombier fiqintr(Ureg*, void *a)
6925d9de2d3SDavid du Colombier {
6935d9de2d3SDavid du Colombier Hci *hp;
6945d9de2d3SDavid du Colombier Ctlr *ctlr;
6955d9de2d3SDavid du Colombier Dwcregs *r;
6965d9de2d3SDavid du Colombier uint intr, haint, wakechan;
6975d9de2d3SDavid du Colombier int i;
6985d9de2d3SDavid du Colombier
6995d9de2d3SDavid du Colombier hp = a;
7005d9de2d3SDavid du Colombier ctlr = hp->aux;
7015d9de2d3SDavid du Colombier r = ctlr->regs;
7025d9de2d3SDavid du Colombier wakechan = 0;
703*5c47fe09SDavid du Colombier filock(ctlr);
7045d9de2d3SDavid du Colombier intr = r->gintsts;
7055d9de2d3SDavid du Colombier if(intr & Hcintr){
7065d9de2d3SDavid du Colombier haint = r->haint & r->haintmsk;
7075d9de2d3SDavid du Colombier for(i = 0; haint; i++){
708b4d1cf41SDavid du Colombier if(haint & 1){
709b4d1cf41SDavid du Colombier if(chanintr(ctlr, i) == 0){
7105d9de2d3SDavid du Colombier r->haintmsk &= ~(1<<i);
7115d9de2d3SDavid du Colombier wakechan |= 1<<i;
7125d9de2d3SDavid du Colombier }
713b4d1cf41SDavid du Colombier }
7145d9de2d3SDavid du Colombier haint >>= 1;
7155d9de2d3SDavid du Colombier }
7165d9de2d3SDavid du Colombier }
7175d9de2d3SDavid du Colombier if(intr & Sofintr){
7185d9de2d3SDavid du Colombier r->gintsts = Sofintr;
7195d9de2d3SDavid du Colombier if((r->hfnum&7) != 6){
7205d9de2d3SDavid du Colombier r->gintmsk &= ~Sofintr;
7215d9de2d3SDavid du Colombier wakechan |= ctlr->sofchan;
7225d9de2d3SDavid du Colombier ctlr->sofchan = 0;
7235d9de2d3SDavid du Colombier }
7245d9de2d3SDavid du Colombier }
7255d9de2d3SDavid du Colombier if(wakechan){
7265d9de2d3SDavid du Colombier ctlr->wakechan |= wakechan;
7275d9de2d3SDavid du Colombier armtimerset(1);
7285d9de2d3SDavid du Colombier }
729*5c47fe09SDavid du Colombier fiunlock(ctlr);
7305d9de2d3SDavid du Colombier }
7315d9de2d3SDavid du Colombier
7325d9de2d3SDavid du Colombier static void
irqintr(Ureg *,void * a)7335d9de2d3SDavid du Colombier irqintr(Ureg*, void *a)
7345d9de2d3SDavid du Colombier {
7355d9de2d3SDavid du Colombier Ctlr *ctlr;
7365d9de2d3SDavid du Colombier uint wakechan;
737*5c47fe09SDavid du Colombier int i;
7385d9de2d3SDavid du Colombier
7395d9de2d3SDavid du Colombier ctlr = a;
740*5c47fe09SDavid du Colombier filock(ctlr);
7415d9de2d3SDavid du Colombier armtimerset(0);
7425d9de2d3SDavid du Colombier wakechan = ctlr->wakechan;
7435d9de2d3SDavid du Colombier ctlr->wakechan = 0;
744*5c47fe09SDavid du Colombier fiunlock(ctlr);
7455d9de2d3SDavid du Colombier for(i = 0; wakechan; i++){
7465d9de2d3SDavid du Colombier if(wakechan & 1)
7475d9de2d3SDavid du Colombier wakeup(&ctlr->chanintr[i]);
7485d9de2d3SDavid du Colombier wakechan >>= 1;
7495d9de2d3SDavid du Colombier }
7505d9de2d3SDavid du Colombier }
7515d9de2d3SDavid du Colombier
7525d9de2d3SDavid du Colombier static void
epopen(Ep * ep)7535d9de2d3SDavid du Colombier epopen(Ep *ep)
7545d9de2d3SDavid du Colombier {
755*5c47fe09SDavid du Colombier ddprint("usbdwc: epopen ep%d.%d ttype %d\n",
7565d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, ep->ttype);
7575d9de2d3SDavid du Colombier switch(ep->ttype){
7585d9de2d3SDavid du Colombier case Tnone:
759a587111cSDavid du Colombier error(Enotconf);
7605d9de2d3SDavid du Colombier case Tintr:
7615d9de2d3SDavid du Colombier assert(ep->pollival > 0);
7625d9de2d3SDavid du Colombier /* fall through */
7635d9de2d3SDavid du Colombier case Tbulk:
7645d9de2d3SDavid du Colombier if(ep->toggle[Read] == 0)
7655d9de2d3SDavid du Colombier ep->toggle[Read] = DATA0;
7665d9de2d3SDavid du Colombier if(ep->toggle[Write] == 0)
7675d9de2d3SDavid du Colombier ep->toggle[Write] = DATA0;
7685d9de2d3SDavid du Colombier break;
7695d9de2d3SDavid du Colombier }
7705d9de2d3SDavid du Colombier ep->aux = malloc(sizeof(Epio));
7715d9de2d3SDavid du Colombier if(ep->aux == nil)
7725d9de2d3SDavid du Colombier error(Enomem);
7735d9de2d3SDavid du Colombier }
7745d9de2d3SDavid du Colombier
7755d9de2d3SDavid du Colombier static void
epclose(Ep * ep)7765d9de2d3SDavid du Colombier epclose(Ep *ep)
7775d9de2d3SDavid du Colombier {
778*5c47fe09SDavid du Colombier ddprint("usbdwc: epclose ep%d.%d ttype %d\n",
7795d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, ep->ttype);
7805d9de2d3SDavid du Colombier switch(ep->ttype){
7815d9de2d3SDavid du Colombier case Tctl:
7825d9de2d3SDavid du Colombier freeb(((Epio*)ep->aux)->cb);
7835d9de2d3SDavid du Colombier /* fall through */
7845d9de2d3SDavid du Colombier default:
7855d9de2d3SDavid du Colombier free(ep->aux);
7865d9de2d3SDavid du Colombier break;
7875d9de2d3SDavid du Colombier }
7885d9de2d3SDavid du Colombier }
7895d9de2d3SDavid du Colombier
7905d9de2d3SDavid du Colombier static long
epread(Ep * ep,void * a,long n)7915d9de2d3SDavid du Colombier epread(Ep *ep, void *a, long n)
7925d9de2d3SDavid du Colombier {
7935d9de2d3SDavid du Colombier Epio *epio;
794*5c47fe09SDavid du Colombier QLock *q;
7955d9de2d3SDavid du Colombier Block *b;
7965d9de2d3SDavid du Colombier uchar *p;
7975d9de2d3SDavid du Colombier ulong elapsed;
7985d9de2d3SDavid du Colombier long nr;
7995d9de2d3SDavid du Colombier
8005d9de2d3SDavid du Colombier ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
8015d9de2d3SDavid du Colombier epio = ep->aux;
802*5c47fe09SDavid du Colombier q = ep->ttype == Tctl? &epio->ctllock : &epio->rlock;
8035d9de2d3SDavid du Colombier b = nil;
804*5c47fe09SDavid du Colombier qlock(q);
8055d9de2d3SDavid du Colombier if(waserror()){
806*5c47fe09SDavid du Colombier qunlock(q);
8075d9de2d3SDavid du Colombier if(b)
8085d9de2d3SDavid du Colombier freeb(b);
8095d9de2d3SDavid du Colombier nexterror();
8105d9de2d3SDavid du Colombier }
8115d9de2d3SDavid du Colombier switch(ep->ttype){
8125d9de2d3SDavid du Colombier default:
8135d9de2d3SDavid du Colombier error(Egreg);
8145d9de2d3SDavid du Colombier case Tctl:
8155d9de2d3SDavid du Colombier nr = ctldata(ep, a, n);
816*5c47fe09SDavid du Colombier qunlock(q);
8175d9de2d3SDavid du Colombier poperror();
8185d9de2d3SDavid du Colombier return nr;
8195d9de2d3SDavid du Colombier case Tintr:
8205d9de2d3SDavid du Colombier elapsed = TK2MS(m->ticks) - epio->lastpoll;
8215d9de2d3SDavid du Colombier if(elapsed < ep->pollival)
8225d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
8235d9de2d3SDavid du Colombier /* fall through */
8245d9de2d3SDavid du Colombier case Tbulk:
8255d9de2d3SDavid du Colombier /* XXX cache madness */
826*5c47fe09SDavid du Colombier b = allocb(ROUND(n, ep->maxpkt));
827*5c47fe09SDavid du Colombier p = b->rp;
828*5c47fe09SDavid du Colombier assert(((uintptr)p & (BLOCKALIGN-1)) == 0);
829*5c47fe09SDavid du Colombier cachedinvse(p, n);
8305d9de2d3SDavid du Colombier nr = eptrans(ep, Read, p, n);
831*5c47fe09SDavid du Colombier cachedinvse(p, nr);
8325d9de2d3SDavid du Colombier epio->lastpoll = TK2MS(m->ticks);
8335d9de2d3SDavid du Colombier memmove(a, p, nr);
834*5c47fe09SDavid du Colombier qunlock(q);
8355d9de2d3SDavid du Colombier freeb(b);
8365d9de2d3SDavid du Colombier poperror();
8375d9de2d3SDavid du Colombier return nr;
8385d9de2d3SDavid du Colombier }
8395d9de2d3SDavid du Colombier }
8405d9de2d3SDavid du Colombier
8415d9de2d3SDavid du Colombier static long
epwrite(Ep * ep,void * a,long n)8425d9de2d3SDavid du Colombier epwrite(Ep *ep, void *a, long n)
8435d9de2d3SDavid du Colombier {
8445d9de2d3SDavid du Colombier Epio *epio;
845*5c47fe09SDavid du Colombier QLock *q;
8465d9de2d3SDavid du Colombier Block *b;
8475d9de2d3SDavid du Colombier uchar *p;
8485d9de2d3SDavid du Colombier ulong elapsed;
8495d9de2d3SDavid du Colombier
8505d9de2d3SDavid du Colombier ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
8515d9de2d3SDavid du Colombier epio = ep->aux;
852*5c47fe09SDavid du Colombier q = ep->ttype == Tctl? &epio->ctllock : &epio->wlock;
8535d9de2d3SDavid du Colombier b = nil;
854*5c47fe09SDavid du Colombier qlock(q);
8555d9de2d3SDavid du Colombier if(waserror()){
856*5c47fe09SDavid du Colombier qunlock(q);
8575d9de2d3SDavid du Colombier if(b)
8585d9de2d3SDavid du Colombier freeb(b);
8595d9de2d3SDavid du Colombier nexterror();
8605d9de2d3SDavid du Colombier }
8615d9de2d3SDavid du Colombier switch(ep->ttype){
8625d9de2d3SDavid du Colombier default:
8635d9de2d3SDavid du Colombier error(Egreg);
8645d9de2d3SDavid du Colombier case Tintr:
8655d9de2d3SDavid du Colombier elapsed = TK2MS(m->ticks) - epio->lastpoll;
8665d9de2d3SDavid du Colombier if(elapsed < ep->pollival)
8675d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
8685d9de2d3SDavid du Colombier /* fall through */
8695d9de2d3SDavid du Colombier case Tctl:
8705d9de2d3SDavid du Colombier case Tbulk:
8715d9de2d3SDavid du Colombier /* XXX cache madness */
872*5c47fe09SDavid du Colombier b = allocb(n);
873*5c47fe09SDavid du Colombier p = b->wp;
874*5c47fe09SDavid du Colombier assert(((uintptr)p & (BLOCKALIGN-1)) == 0);
8755d9de2d3SDavid du Colombier memmove(p, a, n);
8765d9de2d3SDavid du Colombier cachedwbse(p, n);
8775d9de2d3SDavid du Colombier if(ep->ttype == Tctl)
8785d9de2d3SDavid du Colombier n = ctltrans(ep, p, n);
8795d9de2d3SDavid du Colombier else{
8805d9de2d3SDavid du Colombier n = eptrans(ep, Write, p, n);
8815d9de2d3SDavid du Colombier epio->lastpoll = TK2MS(m->ticks);
8825d9de2d3SDavid du Colombier }
883*5c47fe09SDavid du Colombier qunlock(q);
8845d9de2d3SDavid du Colombier freeb(b);
8855d9de2d3SDavid du Colombier poperror();
8865d9de2d3SDavid du Colombier return n;
8875d9de2d3SDavid du Colombier }
8885d9de2d3SDavid du Colombier }
8895d9de2d3SDavid du Colombier
8905d9de2d3SDavid du Colombier static char*
seprintep(char * s,char *,Ep *)8915d9de2d3SDavid du Colombier seprintep(char *s, char*, Ep*)
8925d9de2d3SDavid du Colombier {
8935d9de2d3SDavid du Colombier return s;
8945d9de2d3SDavid du Colombier }
8955d9de2d3SDavid du Colombier
8965d9de2d3SDavid du Colombier static int
portenable(Hci * hp,int port,int on)8975d9de2d3SDavid du Colombier portenable(Hci *hp, int port, int on)
8985d9de2d3SDavid du Colombier {
8995d9de2d3SDavid du Colombier Ctlr *ctlr;
9005d9de2d3SDavid du Colombier Dwcregs *r;
9015d9de2d3SDavid du Colombier
9025d9de2d3SDavid du Colombier assert(port == 1);
9035d9de2d3SDavid du Colombier ctlr = hp->aux;
9045d9de2d3SDavid du Colombier r = ctlr->regs;
905*5c47fe09SDavid du Colombier dprint("usbdwc enable=%d; sts %#x\n", on, r->hport0);
9065d9de2d3SDavid du Colombier if(!on)
9075d9de2d3SDavid du Colombier r->hport0 = Prtpwr | Prtena;
9085d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, Enabledelay);
909*5c47fe09SDavid du Colombier dprint("usbdwc enable=%d; sts %#x\n", on, r->hport0);
9105d9de2d3SDavid du Colombier return 0;
9115d9de2d3SDavid du Colombier }
9125d9de2d3SDavid du Colombier
9135d9de2d3SDavid du Colombier static int
portreset(Hci * hp,int port,int on)9145d9de2d3SDavid du Colombier portreset(Hci *hp, int port, int on)
9155d9de2d3SDavid du Colombier {
9165d9de2d3SDavid du Colombier Ctlr *ctlr;
9175d9de2d3SDavid du Colombier Dwcregs *r;
9185d9de2d3SDavid du Colombier int b, s;
9195d9de2d3SDavid du Colombier
9205d9de2d3SDavid du Colombier assert(port == 1);
9215d9de2d3SDavid du Colombier ctlr = hp->aux;
9225d9de2d3SDavid du Colombier r = ctlr->regs;
923*5c47fe09SDavid du Colombier dprint("usbdwc reset=%d; sts %#x\n", on, r->hport0);
9245d9de2d3SDavid du Colombier if(!on)
9255d9de2d3SDavid du Colombier return 0;
9265d9de2d3SDavid du Colombier r->hport0 = Prtpwr | Prtrst;
9275d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, ResetdelayHS);
9285d9de2d3SDavid du Colombier r->hport0 = Prtpwr;
9295d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, Enabledelay);
9305d9de2d3SDavid du Colombier s = r->hport0;
9315d9de2d3SDavid du Colombier b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
9325d9de2d3SDavid du Colombier if(b != 0)
9335d9de2d3SDavid du Colombier r->hport0 = Prtpwr | b;
934*5c47fe09SDavid du Colombier dprint("usbdwc reset=%d; sts %#x\n", on, s);
9355d9de2d3SDavid du Colombier if((s & Prtena) == 0)
936*5c47fe09SDavid du Colombier print("usbdwc: host port not enabled after reset");
9375d9de2d3SDavid du Colombier return 0;
9385d9de2d3SDavid du Colombier }
9395d9de2d3SDavid du Colombier
9405d9de2d3SDavid du Colombier static int
portstatus(Hci * hp,int port)9415d9de2d3SDavid du Colombier portstatus(Hci *hp, int port)
9425d9de2d3SDavid du Colombier {
9435d9de2d3SDavid du Colombier Ctlr *ctlr;
9445d9de2d3SDavid du Colombier Dwcregs *r;
9455d9de2d3SDavid du Colombier int b, s;
9465d9de2d3SDavid du Colombier
9475d9de2d3SDavid du Colombier assert(port == 1);
9485d9de2d3SDavid du Colombier ctlr = hp->aux;
9495d9de2d3SDavid du Colombier r = ctlr->regs;
9505d9de2d3SDavid du Colombier s = r->hport0;
9515d9de2d3SDavid du Colombier b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
9525d9de2d3SDavid du Colombier if(b != 0)
9535d9de2d3SDavid du Colombier r->hport0 = Prtpwr | b;
9545d9de2d3SDavid du Colombier b = 0;
9555d9de2d3SDavid du Colombier if(s & Prtconnsts)
9565d9de2d3SDavid du Colombier b |= HPpresent;
9575d9de2d3SDavid du Colombier if(s & Prtconndet)
9585d9de2d3SDavid du Colombier b |= HPstatuschg;
9595d9de2d3SDavid du Colombier if(s & Prtena)
9605d9de2d3SDavid du Colombier b |= HPenable;
9615d9de2d3SDavid du Colombier if(s & Prtenchng)
9625d9de2d3SDavid du Colombier b |= HPchange;
9635d9de2d3SDavid du Colombier if(s & Prtovrcurract)
9645d9de2d3SDavid du Colombier b |= HPovercurrent;
9655d9de2d3SDavid du Colombier if(s & Prtsusp)
9665d9de2d3SDavid du Colombier b |= HPsuspend;
9675d9de2d3SDavid du Colombier if(s & Prtrst)
9685d9de2d3SDavid du Colombier b |= HPreset;
9695d9de2d3SDavid du Colombier if(s & Prtpwr)
9705d9de2d3SDavid du Colombier b |= HPpower;
9715d9de2d3SDavid du Colombier switch(s & Prtspd){
9725d9de2d3SDavid du Colombier case HIGHSPEED:
9735d9de2d3SDavid du Colombier b |= HPhigh;
9745d9de2d3SDavid du Colombier break;
9755d9de2d3SDavid du Colombier case LOWSPEED:
9765d9de2d3SDavid du Colombier b |= HPslow;
9775d9de2d3SDavid du Colombier break;
9785d9de2d3SDavid du Colombier }
9795d9de2d3SDavid du Colombier return b;
9805d9de2d3SDavid du Colombier }
9815d9de2d3SDavid du Colombier
9825d9de2d3SDavid du Colombier static void
shutdown(Hci *)9835d9de2d3SDavid du Colombier shutdown(Hci*)
9845d9de2d3SDavid du Colombier {
9855d9de2d3SDavid du Colombier }
9865d9de2d3SDavid du Colombier
9875d9de2d3SDavid du Colombier static void
setdebug(Hci *,int d)9885d9de2d3SDavid du Colombier setdebug(Hci*, int d)
9895d9de2d3SDavid du Colombier {
9905d9de2d3SDavid du Colombier debug = d;
9915d9de2d3SDavid du Colombier }
9925d9de2d3SDavid du Colombier
9935d9de2d3SDavid du Colombier static int
reset(Hci * hp)9945d9de2d3SDavid du Colombier reset(Hci *hp)
9955d9de2d3SDavid du Colombier {
9965d9de2d3SDavid du Colombier Ctlr *ctlr;
9975d9de2d3SDavid du Colombier uint id;
9985d9de2d3SDavid du Colombier
9995d9de2d3SDavid du Colombier ctlr = &dwc;
10005d9de2d3SDavid du Colombier if(ctlr->regs != nil)
10015d9de2d3SDavid du Colombier return -1;
10025d9de2d3SDavid du Colombier ctlr->regs = (Dwcregs*)USBREGS;
10035d9de2d3SDavid du Colombier id = ctlr->regs->gsnpsid;
10045d9de2d3SDavid du Colombier if((id>>16) != ('O'<<8 | 'T'))
10055d9de2d3SDavid du Colombier return -1;
1006*5c47fe09SDavid du Colombier dprint("usbdwc: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
10075d9de2d3SDavid du Colombier
10085d9de2d3SDavid du Colombier intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");
10095d9de2d3SDavid du Colombier
10105d9de2d3SDavid du Colombier hp->aux = ctlr;
10115d9de2d3SDavid du Colombier hp->port = 0;
10125d9de2d3SDavid du Colombier hp->irq = IRQusb;
10135d9de2d3SDavid du Colombier hp->tbdf = 0;
10145d9de2d3SDavid du Colombier hp->nports = 1;
10155d9de2d3SDavid du Colombier hp->highspeed = 1;
10165d9de2d3SDavid du Colombier
10175d9de2d3SDavid du Colombier hp->init = init;
10185d9de2d3SDavid du Colombier hp->dump = dump;
10195d9de2d3SDavid du Colombier hp->interrupt = fiqintr;
10205d9de2d3SDavid du Colombier hp->epopen = epopen;
10215d9de2d3SDavid du Colombier hp->epclose = epclose;
10225d9de2d3SDavid du Colombier hp->epread = epread;
10235d9de2d3SDavid du Colombier hp->epwrite = epwrite;
10245d9de2d3SDavid du Colombier hp->seprintep = seprintep;
10255d9de2d3SDavid du Colombier hp->portenable = portenable;
10265d9de2d3SDavid du Colombier hp->portreset = portreset;
10275d9de2d3SDavid du Colombier hp->portstatus = portstatus;
10285d9de2d3SDavid du Colombier hp->shutdown = shutdown;
10295d9de2d3SDavid du Colombier hp->debug = setdebug;
10305d9de2d3SDavid du Colombier hp->type = "dwcotg";
10315d9de2d3SDavid du Colombier return 0;
10325d9de2d3SDavid du Colombier }
10335d9de2d3SDavid du Colombier
10345d9de2d3SDavid du Colombier void
usbdwclink(void)10355d9de2d3SDavid du Colombier usbdwclink(void)
10365d9de2d3SDavid du Colombier {
10375d9de2d3SDavid du Colombier addhcitype("dwcotg", reset);
10385d9de2d3SDavid du Colombier }
1039