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"
235d9de2d3SDavid du Colombier #include "../port/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,
365d9de2d3SDavid du Colombier };
375d9de2d3SDavid du Colombier
385d9de2d3SDavid du Colombier typedef struct Ctlr Ctlr;
395d9de2d3SDavid du Colombier typedef struct Epio Epio;
405d9de2d3SDavid du Colombier
415d9de2d3SDavid du Colombier struct Ctlr {
425d9de2d3SDavid du Colombier Dwcregs *regs; /* controller registers */
435d9de2d3SDavid du Colombier int nchan; /* number of host channels */
445d9de2d3SDavid du Colombier ulong chanbusy; /* bitmap of in-use channels */
455d9de2d3SDavid du Colombier QLock chanlock; /* serialise access to chanbusy */
465d9de2d3SDavid du Colombier QLock split; /* serialise split transactions */
475d9de2d3SDavid du Colombier int splitretry; /* count retries of Nyet */
485d9de2d3SDavid du Colombier int sofchan; /* bitmap of channels waiting for sof */
495d9de2d3SDavid du Colombier int wakechan; /* bitmap of channels to wakeup after fiq */
505d9de2d3SDavid du Colombier int debugchan; /* bitmap of channels for interrupt debug */
515d9de2d3SDavid du Colombier Rendez *chanintr; /* sleep till interrupt on channel N */
525d9de2d3SDavid du Colombier };
535d9de2d3SDavid du Colombier
545d9de2d3SDavid du Colombier struct Epio {
555d9de2d3SDavid du Colombier QLock;
565d9de2d3SDavid du Colombier Block *cb;
575d9de2d3SDavid du Colombier ulong lastpoll;
585d9de2d3SDavid du Colombier };
595d9de2d3SDavid du Colombier
605d9de2d3SDavid du Colombier static Ctlr dwc;
615d9de2d3SDavid du Colombier static int debug;
625d9de2d3SDavid du Colombier
635d9de2d3SDavid du Colombier static char Ebadlen[] = "bad usb request length";
645d9de2d3SDavid du Colombier
655d9de2d3SDavid du Colombier static void clog(Ep *ep, Hostchan *hc);
665d9de2d3SDavid du Colombier static void logdump(Ep *ep);
675d9de2d3SDavid du Colombier
685d9de2d3SDavid du Colombier static Hostchan*
chanalloc(Ep * ep)695d9de2d3SDavid du Colombier chanalloc(Ep *ep)
705d9de2d3SDavid du Colombier {
715d9de2d3SDavid du Colombier Ctlr *ctlr;
725d9de2d3SDavid du Colombier int bitmap, i;
735d9de2d3SDavid du Colombier
745d9de2d3SDavid du Colombier ctlr = ep->hp->aux;
755d9de2d3SDavid du Colombier qlock(&ctlr->chanlock);
765d9de2d3SDavid du Colombier bitmap = ctlr->chanbusy;
775d9de2d3SDavid du Colombier for(i = 0; i < ctlr->nchan; i++)
785d9de2d3SDavid du Colombier if((bitmap & (1<<i)) == 0){
795d9de2d3SDavid du Colombier ctlr->chanbusy = bitmap | 1<<i;
805d9de2d3SDavid du Colombier qunlock(&ctlr->chanlock);
815d9de2d3SDavid du Colombier return &ctlr->regs->hchan[i];
825d9de2d3SDavid du Colombier }
835d9de2d3SDavid du Colombier qunlock(&ctlr->chanlock);
845d9de2d3SDavid du Colombier panic("miller is a lazy git");
855d9de2d3SDavid du Colombier return nil;
865d9de2d3SDavid du Colombier }
875d9de2d3SDavid du Colombier
885d9de2d3SDavid du Colombier static void
chanrelease(Ep * ep,Hostchan * chan)895d9de2d3SDavid du Colombier chanrelease(Ep *ep, Hostchan *chan)
905d9de2d3SDavid du Colombier {
915d9de2d3SDavid du Colombier Ctlr *ctlr;
925d9de2d3SDavid du Colombier int i;
935d9de2d3SDavid du Colombier
945d9de2d3SDavid du Colombier ctlr = ep->hp->aux;
955d9de2d3SDavid du Colombier i = chan - ctlr->regs->hchan;
965d9de2d3SDavid du Colombier qlock(&ctlr->chanlock);
975d9de2d3SDavid du Colombier ctlr->chanbusy &= ~(1<<i);
985d9de2d3SDavid du Colombier qunlock(&ctlr->chanlock);
995d9de2d3SDavid du Colombier }
1005d9de2d3SDavid du Colombier
1015d9de2d3SDavid du Colombier static void
chansetup(Hostchan * hc,Ep * ep)1025d9de2d3SDavid du Colombier chansetup(Hostchan *hc, Ep *ep)
1035d9de2d3SDavid du Colombier {
1045d9de2d3SDavid du Colombier int hcc;
1055d9de2d3SDavid du Colombier Ctlr *ctlr = ep->hp->aux;
1065d9de2d3SDavid du Colombier
1075d9de2d3SDavid du Colombier if(ep->debug)
1085d9de2d3SDavid du Colombier ctlr->debugchan |= 1 << (hc - ctlr->regs->hchan);
1095d9de2d3SDavid du Colombier else
1105d9de2d3SDavid du Colombier ctlr->debugchan &= ~(1 << (hc - ctlr->regs->hchan));
1115d9de2d3SDavid du Colombier switch(ep->dev->state){
1125d9de2d3SDavid du Colombier case Dconfig:
1135d9de2d3SDavid du Colombier case Dreset:
1145d9de2d3SDavid du Colombier hcc = 0;
1155d9de2d3SDavid du Colombier break;
1165d9de2d3SDavid du Colombier default:
1175d9de2d3SDavid du Colombier hcc = ep->dev->nb<<ODevaddr;
1185d9de2d3SDavid du Colombier break;
1195d9de2d3SDavid du Colombier }
1205d9de2d3SDavid du Colombier hcc |= ep->maxpkt | 1<<OMulticnt | ep->nb<<OEpnum;
1215d9de2d3SDavid du Colombier switch(ep->ttype){
1225d9de2d3SDavid du Colombier case Tctl:
1235d9de2d3SDavid du Colombier hcc |= Epctl;
1245d9de2d3SDavid du Colombier break;
1255d9de2d3SDavid du Colombier case Tiso:
1265d9de2d3SDavid du Colombier hcc |= Episo;
1275d9de2d3SDavid du Colombier break;
1285d9de2d3SDavid du Colombier case Tbulk:
1295d9de2d3SDavid du Colombier hcc |= Epbulk;
1305d9de2d3SDavid du Colombier break;
1315d9de2d3SDavid du Colombier case Tintr:
1325d9de2d3SDavid du Colombier hcc |= Epintr;
1335d9de2d3SDavid du Colombier break;
1345d9de2d3SDavid du Colombier }
1355d9de2d3SDavid du Colombier switch(ep->dev->speed){
1365d9de2d3SDavid du Colombier case Lowspeed:
1375d9de2d3SDavid du Colombier hcc |= Lspddev;
1385d9de2d3SDavid du Colombier /* fall through */
1395d9de2d3SDavid du Colombier case Fullspeed:
140fe823997SDavid du Colombier if(ep->dev->hub > 1){
1415d9de2d3SDavid du Colombier hc->hcsplt = Spltena | POS_ALL | ep->dev->hub<<OHubaddr |
1425d9de2d3SDavid du Colombier ep->dev->port;
1435d9de2d3SDavid du Colombier break;
144fe823997SDavid du Colombier }
145fe823997SDavid du Colombier /* fall through */
1465d9de2d3SDavid du Colombier default:
1475d9de2d3SDavid du Colombier hc->hcsplt = 0;
1485d9de2d3SDavid du Colombier break;
1495d9de2d3SDavid du Colombier }
1505d9de2d3SDavid du Colombier hc->hcchar = hcc;
1515d9de2d3SDavid du Colombier hc->hcint = ~0;
1525d9de2d3SDavid du Colombier }
1535d9de2d3SDavid du Colombier
1545d9de2d3SDavid du Colombier static int
sofdone(void * a)1555d9de2d3SDavid du Colombier sofdone(void *a)
1565d9de2d3SDavid du Colombier {
1575d9de2d3SDavid du Colombier Dwcregs *r;
1585d9de2d3SDavid du Colombier
1595d9de2d3SDavid du Colombier r = a;
1605d9de2d3SDavid du Colombier return r->gintsts & Sofintr;
1615d9de2d3SDavid du Colombier }
1625d9de2d3SDavid du Colombier
1635d9de2d3SDavid du Colombier static void
sofwait(Ctlr * ctlr,int n)1645d9de2d3SDavid du Colombier sofwait(Ctlr *ctlr, int n)
1655d9de2d3SDavid du Colombier {
1665d9de2d3SDavid du Colombier Dwcregs *r;
1675d9de2d3SDavid du Colombier int x;
1685d9de2d3SDavid du Colombier
1695d9de2d3SDavid du Colombier r = ctlr->regs;
1705d9de2d3SDavid du Colombier do{
1715d9de2d3SDavid du Colombier r->gintsts = Sofintr;
1725d9de2d3SDavid du Colombier x = splfhi();
1735d9de2d3SDavid du Colombier ctlr->sofchan |= 1<<n;
1745d9de2d3SDavid du Colombier r->gintmsk |= Sofintr;
1755d9de2d3SDavid du Colombier sleep(&ctlr->chanintr[n], sofdone, r);
1765d9de2d3SDavid du Colombier splx(x);
1775d9de2d3SDavid du Colombier }while((r->hfnum & 7) == 6);
1785d9de2d3SDavid du Colombier }
1795d9de2d3SDavid du Colombier
1805d9de2d3SDavid du Colombier static int
chandone(void * a)1815d9de2d3SDavid du Colombier chandone(void *a)
1825d9de2d3SDavid du Colombier {
1835d9de2d3SDavid du Colombier Hostchan *hc;
1845d9de2d3SDavid du Colombier
1855d9de2d3SDavid du Colombier hc = a;
186fe823997SDavid du Colombier if(hc->hcint == (Chhltd|Ack))
187fe823997SDavid du Colombier return 0;
1885d9de2d3SDavid du Colombier return (hc->hcint & hc->hcintmsk) != 0;
1895d9de2d3SDavid du Colombier }
1905d9de2d3SDavid du Colombier
1915d9de2d3SDavid du Colombier static int
chanwait(Ep * ep,Ctlr * ctlr,Hostchan * hc,int mask)1925d9de2d3SDavid du Colombier chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
1935d9de2d3SDavid du Colombier {
1945d9de2d3SDavid du Colombier int intr, n, x, ointr;
1955d9de2d3SDavid du Colombier ulong start, now;
1965d9de2d3SDavid du Colombier Dwcregs *r;
1975d9de2d3SDavid du Colombier
1985d9de2d3SDavid du Colombier r = ctlr->regs;
1995d9de2d3SDavid du Colombier n = hc - r->hchan;
2005d9de2d3SDavid du Colombier for(;;){
2015d9de2d3SDavid du Colombier restart:
2025d9de2d3SDavid du Colombier x = splfhi();
2035d9de2d3SDavid du Colombier r->haintmsk |= 1<<n;
2045d9de2d3SDavid du Colombier hc->hcintmsk = mask;
2055d9de2d3SDavid du Colombier sleep(&ctlr->chanintr[n], chandone, hc);
2065d9de2d3SDavid du Colombier hc->hcintmsk = 0;
2075d9de2d3SDavid du Colombier splx(x);
2085d9de2d3SDavid du Colombier intr = hc->hcint;
2095d9de2d3SDavid du Colombier if(intr & Chhltd)
2105d9de2d3SDavid du Colombier return intr;
2115d9de2d3SDavid du Colombier start = fastticks(0);
2125d9de2d3SDavid du Colombier ointr = intr;
2135d9de2d3SDavid du Colombier now = start;
2145d9de2d3SDavid du Colombier do{
2155d9de2d3SDavid du Colombier intr = hc->hcint;
2165d9de2d3SDavid du Colombier if(intr & Chhltd){
2175d9de2d3SDavid du Colombier if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
2185d9de2d3SDavid du Colombier intr != (Ack|Chhltd|Xfercomp) ||
2195d9de2d3SDavid du Colombier (now - start) > 60)
2205d9de2d3SDavid du Colombier dprint("await %x after %ld %x -> %x\n",
2215d9de2d3SDavid du Colombier mask, now - start, ointr, intr);
2225d9de2d3SDavid du Colombier return intr;
2235d9de2d3SDavid du Colombier }
2245d9de2d3SDavid du Colombier if((intr & mask) == 0){
225b4d1cf41SDavid du Colombier dprint("ep%d.%d await %x intr %x -> %x\n",
226b4d1cf41SDavid du Colombier ep->dev->nb, ep->nb, mask, ointr, intr);
2275d9de2d3SDavid du Colombier goto restart;
2285d9de2d3SDavid du Colombier }
2295d9de2d3SDavid du Colombier now = fastticks(0);
2305d9de2d3SDavid du Colombier }while(now - start < 100);
2315d9de2d3SDavid du Colombier dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
2325d9de2d3SDavid du Colombier "grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
2335d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
2345d9de2d3SDavid du Colombier r->gnptxsts, r->hptxsts);
2355d9de2d3SDavid du Colombier mask = Chhltd;
2365d9de2d3SDavid du Colombier hc->hcchar |= Chdis;
2375d9de2d3SDavid du Colombier start = m->ticks;
2385d9de2d3SDavid du Colombier while(hc->hcchar & Chen){
2395d9de2d3SDavid du Colombier if(m->ticks - start >= 100){
2405d9de2d3SDavid du Colombier print("ep%d.%d channel won't halt hcchar %8.8ux\n",
2415d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, hc->hcchar);
2425d9de2d3SDavid du Colombier break;
2435d9de2d3SDavid du Colombier }
2445d9de2d3SDavid du Colombier }
2455d9de2d3SDavid du Colombier logdump(ep);
2465d9de2d3SDavid du Colombier }
2475d9de2d3SDavid du Colombier }
2485d9de2d3SDavid du Colombier
2495d9de2d3SDavid du Colombier static int
chanintr(Ctlr * ctlr,int n)2505d9de2d3SDavid du Colombier chanintr(Ctlr *ctlr, int n)
2515d9de2d3SDavid du Colombier {
2525d9de2d3SDavid du Colombier Hostchan *hc;
2535d9de2d3SDavid du Colombier int i;
2545d9de2d3SDavid du Colombier
2555d9de2d3SDavid du Colombier hc = &ctlr->regs->hchan[n];
2565d9de2d3SDavid du Colombier if(ctlr->debugchan & (1<<n))
2575d9de2d3SDavid du Colombier clog(nil, hc);
2585d9de2d3SDavid du Colombier if((hc->hcsplt & Spltena) == 0)
2595d9de2d3SDavid du Colombier return 0;
2605d9de2d3SDavid du Colombier i = hc->hcint;
2615d9de2d3SDavid du Colombier if(i == (Chhltd|Ack)){
2625d9de2d3SDavid du Colombier hc->hcsplt |= Compsplt;
2635d9de2d3SDavid du Colombier ctlr->splitretry = 0;
2645d9de2d3SDavid du Colombier }else if(i == (Chhltd|Nyet)){
2655d9de2d3SDavid du Colombier if(++ctlr->splitretry >= 3)
2665d9de2d3SDavid du Colombier return 0;
2675d9de2d3SDavid du Colombier }else
2685d9de2d3SDavid du Colombier return 0;
2695d9de2d3SDavid du Colombier if(hc->hcchar & Chen){
2705d9de2d3SDavid du Colombier iprint("hcchar %8.8ux hcint %8.8ux", hc->hcchar, hc->hcint);
2715d9de2d3SDavid du Colombier hc->hcchar |= Chen | Chdis;
2725d9de2d3SDavid du Colombier while(hc->hcchar&Chen)
2735d9de2d3SDavid du Colombier ;
2745d9de2d3SDavid du Colombier iprint(" %8.8ux\n", hc->hcint);
2755d9de2d3SDavid du Colombier }
2765d9de2d3SDavid du Colombier hc->hcint = i;
2775d9de2d3SDavid du Colombier if(ctlr->regs->hfnum & 1)
2785d9de2d3SDavid du Colombier hc->hcchar &= ~Oddfrm;
2795d9de2d3SDavid du Colombier else
2805d9de2d3SDavid du Colombier hc->hcchar |= Oddfrm;
2815d9de2d3SDavid du Colombier hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
2825d9de2d3SDavid du Colombier return 1;
2835d9de2d3SDavid du Colombier }
2845d9de2d3SDavid du Colombier
2855d9de2d3SDavid du Colombier static Reg chanlog[32][5];
2865d9de2d3SDavid du Colombier static int nchanlog;
2875d9de2d3SDavid du Colombier
2885d9de2d3SDavid du Colombier static void
logstart(Ep * ep)2895d9de2d3SDavid du Colombier logstart(Ep *ep)
2905d9de2d3SDavid du Colombier {
2915d9de2d3SDavid du Colombier if(ep->debug)
2925d9de2d3SDavid du Colombier nchanlog = 0;
2935d9de2d3SDavid du Colombier }
2945d9de2d3SDavid du Colombier
2955d9de2d3SDavid du Colombier static void
clog(Ep * ep,Hostchan * hc)2965d9de2d3SDavid du Colombier clog(Ep *ep, Hostchan *hc)
2975d9de2d3SDavid du Colombier {
2985d9de2d3SDavid du Colombier Reg *p;
2995d9de2d3SDavid du Colombier
3005d9de2d3SDavid du Colombier if(ep != nil && !ep->debug)
3015d9de2d3SDavid du Colombier return;
3025d9de2d3SDavid du Colombier if(nchanlog == 32)
3035d9de2d3SDavid du Colombier nchanlog--;
3045d9de2d3SDavid du Colombier p = chanlog[nchanlog];
3055d9de2d3SDavid du Colombier p[0] = dwc.regs->hfnum;
3065d9de2d3SDavid du Colombier p[1] = hc->hcchar;
3075d9de2d3SDavid du Colombier p[2] = hc->hcint;
3085d9de2d3SDavid du Colombier p[3] = hc->hctsiz;
3095d9de2d3SDavid du Colombier p[4] = hc->hcdma;
3105d9de2d3SDavid du Colombier nchanlog++;
3115d9de2d3SDavid du Colombier }
3125d9de2d3SDavid du Colombier
3135d9de2d3SDavid du Colombier static void
logdump(Ep * ep)3145d9de2d3SDavid du Colombier logdump(Ep *ep)
3155d9de2d3SDavid du Colombier {
3165d9de2d3SDavid du Colombier Reg *p;
3175d9de2d3SDavid du Colombier int i;
3185d9de2d3SDavid du Colombier
3195d9de2d3SDavid du Colombier if(!ep->debug)
3205d9de2d3SDavid du Colombier return;
3215d9de2d3SDavid du Colombier p = chanlog[0];
3225d9de2d3SDavid du Colombier for(i = 0; i < nchanlog; i++){
3235d9de2d3SDavid du Colombier print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n",
3245d9de2d3SDavid du Colombier p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]);
3255d9de2d3SDavid du Colombier p += 5;
3265d9de2d3SDavid du Colombier }
3275d9de2d3SDavid du Colombier nchanlog = 0;
3285d9de2d3SDavid du Colombier }
3295d9de2d3SDavid du Colombier
3305d9de2d3SDavid du Colombier static int
chanio(Ep * ep,Hostchan * hc,int dir,int pid,void * a,int len)3315d9de2d3SDavid du Colombier chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
3325d9de2d3SDavid du Colombier {
3335d9de2d3SDavid du Colombier Ctlr *ctlr;
3345d9de2d3SDavid du Colombier int nleft, n, nt, i, maxpkt, npkt;
3355d9de2d3SDavid du Colombier uint hcdma, hctsiz;
3365d9de2d3SDavid du Colombier
3375d9de2d3SDavid du Colombier ctlr = ep->hp->aux;
3385d9de2d3SDavid du Colombier maxpkt = ep->maxpkt;
3395d9de2d3SDavid du Colombier npkt = HOWMANY(len, ep->maxpkt);
3405d9de2d3SDavid du Colombier if(npkt == 0)
3415d9de2d3SDavid du Colombier npkt = 1;
3425d9de2d3SDavid du Colombier
3435d9de2d3SDavid du Colombier hc->hcchar = (hc->hcchar & ~Epdir) | dir;
3445d9de2d3SDavid du Colombier if(dir == Epin)
3455d9de2d3SDavid du Colombier n = ROUND(len, ep->maxpkt);
3465d9de2d3SDavid du Colombier else
3475d9de2d3SDavid du Colombier n = len;
3485d9de2d3SDavid du Colombier hc->hctsiz = n | npkt<<OPktcnt | pid;
3495d9de2d3SDavid du Colombier hc->hcdma = PADDR(a);
3505d9de2d3SDavid du Colombier
3515d9de2d3SDavid du Colombier nleft = len;
3525d9de2d3SDavid du Colombier logstart(ep);
3535d9de2d3SDavid du Colombier for(;;){
3545d9de2d3SDavid du Colombier hcdma = hc->hcdma;
3555d9de2d3SDavid du Colombier hctsiz = hc->hctsiz;
3565d9de2d3SDavid du Colombier hc->hctsiz = hctsiz & ~Dopng;
3575d9de2d3SDavid du Colombier if(hc->hcchar&Chen){
3585d9de2d3SDavid du Colombier dprint("ep%d.%d before chanio hcchar=%8.8ux\n",
3595d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, hc->hcchar);
3605d9de2d3SDavid du Colombier hc->hcchar |= Chen | Chdis;
3615d9de2d3SDavid du Colombier while(hc->hcchar&Chen)
3625d9de2d3SDavid du Colombier ;
3635d9de2d3SDavid du Colombier hc->hcint = Chhltd;
3645d9de2d3SDavid du Colombier }
3655d9de2d3SDavid du Colombier if((i = hc->hcint) != 0){
3665d9de2d3SDavid du Colombier dprint("ep%d.%d before chanio hcint=%8.8ux\n",
3675d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, i);
3685d9de2d3SDavid du Colombier hc->hcint = i;
3695d9de2d3SDavid du Colombier }
3705d9de2d3SDavid du Colombier if(hc->hcsplt & Spltena){
3715d9de2d3SDavid du Colombier qlock(&ctlr->split);
3725d9de2d3SDavid du Colombier sofwait(ctlr, hc - ctlr->regs->hchan);
3735d9de2d3SDavid du Colombier if((dwc.regs->hfnum & 1) == 0)
3745d9de2d3SDavid du Colombier hc->hcchar &= ~Oddfrm;
3755d9de2d3SDavid du Colombier else
3765d9de2d3SDavid du Colombier hc->hcchar |= Oddfrm;
3775d9de2d3SDavid du Colombier }
3785d9de2d3SDavid du Colombier hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
3795d9de2d3SDavid du Colombier clog(ep, hc);
3805d9de2d3SDavid du Colombier if(ep->ttype == Tbulk && dir == Epin)
3815d9de2d3SDavid du Colombier i = chanwait(ep, ctlr, hc, /* Ack| */ Chhltd);
3825d9de2d3SDavid du Colombier else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
3835d9de2d3SDavid du Colombier i = chanwait(ep, ctlr, hc, Chhltd);
3845d9de2d3SDavid du Colombier else
3855d9de2d3SDavid du Colombier i = chanwait(ep, ctlr, hc, Chhltd|Nak);
3865d9de2d3SDavid du Colombier clog(ep, hc);
3875d9de2d3SDavid du Colombier hc->hcint = i;
3885d9de2d3SDavid du Colombier
3895d9de2d3SDavid du Colombier if(hc->hcsplt & Spltena){
3905d9de2d3SDavid du Colombier hc->hcsplt &= ~Compsplt;
3915d9de2d3SDavid du Colombier qunlock(&ctlr->split);
3925d9de2d3SDavid du Colombier }
3935d9de2d3SDavid du Colombier
3945d9de2d3SDavid du Colombier if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
3955d9de2d3SDavid du Colombier if(i & Stall)
3965d9de2d3SDavid du Colombier error(Estalled);
397fe823997SDavid du Colombier if(i & (Nyet|Frmovrun))
3985d9de2d3SDavid du Colombier continue;
3995d9de2d3SDavid du Colombier if(i & Nak){
4005d9de2d3SDavid du Colombier if(ep->ttype == Tintr)
4015d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival);
4025d9de2d3SDavid du Colombier else
4035d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, 1);
4045d9de2d3SDavid du Colombier continue;
4055d9de2d3SDavid du Colombier }
406fe823997SDavid du Colombier logdump(ep);
4075d9de2d3SDavid du Colombier print("usbotg: ep%d.%d error intr %8.8ux\n",
4085d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, i);
4095d9de2d3SDavid du Colombier if(i & ~(Chhltd|Ack))
4105d9de2d3SDavid du Colombier error(Eio);
4115d9de2d3SDavid du Colombier if(hc->hcdma != hcdma)
4125d9de2d3SDavid du Colombier print("usbotg: weird hcdma %x->%x intr %x->%x\n",
4135d9de2d3SDavid du Colombier hcdma, hc->hcdma, i, hc->hcint);
4145d9de2d3SDavid du Colombier }
4155d9de2d3SDavid du Colombier n = hc->hcdma - hcdma;
416b4d1cf41SDavid du Colombier if(n == 0){
4175d9de2d3SDavid du Colombier if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt))
4185d9de2d3SDavid du Colombier break;
4195d9de2d3SDavid du Colombier else
4205d9de2d3SDavid du Colombier continue;
421b4d1cf41SDavid du Colombier }
4225d9de2d3SDavid du Colombier if(dir == Epin && ep->ttype == Tbulk && n == nleft){
4235d9de2d3SDavid du Colombier nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
424b4d1cf41SDavid du Colombier if(nt != n){
425b4d1cf41SDavid du Colombier if(n == ROUND(nt, 4))
4265d9de2d3SDavid du Colombier n = nt;
4275d9de2d3SDavid du Colombier else
428b4d1cf41SDavid du Colombier print("usbotg: intr %8.8ux "
429b4d1cf41SDavid du Colombier "dma %8.8ux-%8.8ux "
430b4d1cf41SDavid du Colombier "hctsiz %8.8ux-%8.ux\n",
4315d9de2d3SDavid du Colombier i, hcdma, hc->hcdma, hctsiz,
4325d9de2d3SDavid du Colombier hc->hctsiz);
4335d9de2d3SDavid du Colombier }
434b4d1cf41SDavid du Colombier }
4355d9de2d3SDavid du Colombier if(n > nleft){
436b4d1cf41SDavid du Colombier if(n != ROUND(nleft, 4))
4375d9de2d3SDavid du Colombier dprint("too much: wanted %d got %d\n",
4385d9de2d3SDavid du Colombier len, len - nleft + n);
4395d9de2d3SDavid du Colombier n = nleft;
4405d9de2d3SDavid du Colombier }
4415d9de2d3SDavid du Colombier nleft -= n;
442b4d1cf41SDavid du Colombier if(nleft == 0 || (n % maxpkt) != 0)
4435d9de2d3SDavid du Colombier break;
4445d9de2d3SDavid du Colombier if((i & Xfercomp) && ep->ttype != Tctl)
4455d9de2d3SDavid du Colombier break;
4465d9de2d3SDavid du Colombier if(dir == Epout)
4475d9de2d3SDavid du Colombier dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n",
4485d9de2d3SDavid du Colombier nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i);
4495d9de2d3SDavid du Colombier }
4505d9de2d3SDavid du Colombier logdump(ep);
4515d9de2d3SDavid du Colombier return len - nleft;
4525d9de2d3SDavid du Colombier }
4535d9de2d3SDavid du Colombier
4545d9de2d3SDavid du Colombier static long
multitrans(Ep * ep,Hostchan * hc,int rw,void * a,long n)455fe823997SDavid du Colombier multitrans(Ep *ep, Hostchan *hc, int rw, void *a, long n)
456fe823997SDavid du Colombier {
457fe823997SDavid du Colombier long sofar, m;
458fe823997SDavid du Colombier
459fe823997SDavid du Colombier sofar = 0;
460fe823997SDavid du Colombier do{
461fe823997SDavid du Colombier m = n - sofar;
462fe823997SDavid du Colombier if(m > ep->maxpkt)
463fe823997SDavid du Colombier m = ep->maxpkt;
464fe823997SDavid du Colombier m = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
465fe823997SDavid du Colombier (char*)a + sofar, m);
466fe823997SDavid du Colombier ep->toggle[rw] = hc->hctsiz & Pid;
467fe823997SDavid du Colombier sofar += m;
468fe823997SDavid du Colombier }while(sofar < n && m == ep->maxpkt);
469fe823997SDavid du Colombier return sofar;
470fe823997SDavid du Colombier }
471fe823997SDavid du Colombier
472fe823997SDavid du Colombier static long
eptrans(Ep * ep,int rw,void * a,long n)4735d9de2d3SDavid du Colombier eptrans(Ep *ep, int rw, void *a, long n)
4745d9de2d3SDavid du Colombier {
4755d9de2d3SDavid du Colombier Hostchan *hc;
4765d9de2d3SDavid du Colombier
4775d9de2d3SDavid du Colombier if(ep->clrhalt){
4785d9de2d3SDavid du Colombier ep->clrhalt = 0;
4795d9de2d3SDavid du Colombier if(ep->mode != OREAD)
4805d9de2d3SDavid du Colombier ep->toggle[Write] = DATA0;
4815d9de2d3SDavid du Colombier if(ep->mode != OWRITE)
4825d9de2d3SDavid du Colombier ep->toggle[Read] = DATA0;
4835d9de2d3SDavid du Colombier }
4845d9de2d3SDavid du Colombier hc = chanalloc(ep);
4855d9de2d3SDavid du Colombier if(waserror()){
4865d9de2d3SDavid du Colombier ep->toggle[rw] = hc->hctsiz & Pid;
4875d9de2d3SDavid du Colombier chanrelease(ep, hc);
4885d9de2d3SDavid du Colombier if(strcmp(up->errstr, Estalled) == 0)
4895d9de2d3SDavid du Colombier return 0;
4905d9de2d3SDavid du Colombier nexterror();
4915d9de2d3SDavid du Colombier }
4925d9de2d3SDavid du Colombier chansetup(hc, ep);
493fe823997SDavid du Colombier if(rw == Read && ep->ttype == Tbulk)
494fe823997SDavid du Colombier n = multitrans(ep, hc, rw, a, n);
495fe823997SDavid du Colombier else{
4965d9de2d3SDavid du Colombier n = chanio(ep, hc, rw == Read? Epin : Epout, ep->toggle[rw],
4975d9de2d3SDavid du Colombier a, n);
4985d9de2d3SDavid du Colombier ep->toggle[rw] = hc->hctsiz & Pid;
4995d9de2d3SDavid du Colombier }
5005d9de2d3SDavid du Colombier chanrelease(ep, hc);
5015d9de2d3SDavid du Colombier poperror();
5025d9de2d3SDavid du Colombier return n;
5035d9de2d3SDavid du Colombier }
5045d9de2d3SDavid du Colombier
5055d9de2d3SDavid du Colombier static long
ctltrans(Ep * ep,uchar * req,long n)5065d9de2d3SDavid du Colombier ctltrans(Ep *ep, uchar *req, long n)
5075d9de2d3SDavid du Colombier {
5085d9de2d3SDavid du Colombier Hostchan *hc;
5095d9de2d3SDavid du Colombier Epio *epio;
5105d9de2d3SDavid du Colombier Block *b;
5115d9de2d3SDavid du Colombier uchar *data;
5125d9de2d3SDavid du Colombier int datalen;
5135d9de2d3SDavid du Colombier
5145d9de2d3SDavid du Colombier epio = ep->aux;
5155d9de2d3SDavid du Colombier if(epio->cb != nil){
5165d9de2d3SDavid du Colombier freeb(epio->cb);
5175d9de2d3SDavid du Colombier epio->cb = nil;
5185d9de2d3SDavid du Colombier }
5195d9de2d3SDavid du Colombier if(n < Rsetuplen)
5205d9de2d3SDavid du Colombier error(Ebadlen);
5215d9de2d3SDavid du Colombier if(req[Rtype] & Rd2h){
5225d9de2d3SDavid du Colombier datalen = GET2(req+Rcount);
5235d9de2d3SDavid du Colombier if(datalen <= 0 || datalen > Maxctllen)
5245d9de2d3SDavid du Colombier error(Ebadlen);
5255d9de2d3SDavid du Colombier /* XXX cache madness */
5265d9de2d3SDavid du Colombier epio->cb = b = allocb(ROUND(datalen, ep->maxpkt) + CACHELINESZ);
5275d9de2d3SDavid du Colombier b->wp = (uchar*)ROUND((uintptr)b->wp, CACHELINESZ);
5285d9de2d3SDavid du Colombier memset(b->wp, 0x55, b->lim - b->wp);
5295d9de2d3SDavid du Colombier cachedwbinvse(b->wp, b->lim - b->wp);
5305d9de2d3SDavid du Colombier data = b->wp;
5315d9de2d3SDavid du Colombier }else{
5325d9de2d3SDavid du Colombier b = nil;
5335d9de2d3SDavid du Colombier datalen = n - Rsetuplen;
5345d9de2d3SDavid du Colombier data = req + Rsetuplen;
5355d9de2d3SDavid du Colombier }
5365d9de2d3SDavid du Colombier hc = chanalloc(ep);
5375d9de2d3SDavid du Colombier if(waserror()){
5385d9de2d3SDavid du Colombier chanrelease(ep, hc);
5395d9de2d3SDavid du Colombier if(strcmp(up->errstr, Estalled) == 0)
5405d9de2d3SDavid du Colombier return 0;
5415d9de2d3SDavid du Colombier nexterror();
5425d9de2d3SDavid du Colombier }
5435d9de2d3SDavid du Colombier chansetup(hc, ep);
5445d9de2d3SDavid du Colombier chanio(ep, hc, Epout, SETUP, req, Rsetuplen);
5455d9de2d3SDavid du Colombier if(req[Rtype] & Rd2h){
546fe823997SDavid du Colombier if(ep->dev->hub <= 1){
547fe823997SDavid du Colombier ep->toggle[Read] = DATA1;
548fe823997SDavid du Colombier b->wp += multitrans(ep, hc, Read, data, datalen);
549fe823997SDavid du Colombier }else
5505d9de2d3SDavid du Colombier b->wp += chanio(ep, hc, Epin, DATA1, data, datalen);
5515d9de2d3SDavid du Colombier chanio(ep, hc, Epout, DATA1, nil, 0);
5525d9de2d3SDavid du Colombier n = Rsetuplen;
5535d9de2d3SDavid du Colombier }else{
5545d9de2d3SDavid du Colombier if(datalen > 0)
5555d9de2d3SDavid du Colombier chanio(ep, hc, Epout, DATA1, data, datalen);
5565d9de2d3SDavid du Colombier chanio(ep, hc, Epin, DATA1, nil, 0);
5575d9de2d3SDavid du Colombier n = Rsetuplen + datalen;
5585d9de2d3SDavid du Colombier }
5595d9de2d3SDavid du Colombier chanrelease(ep, hc);
5605d9de2d3SDavid du Colombier poperror();
5615d9de2d3SDavid du Colombier return n;
5625d9de2d3SDavid du Colombier }
5635d9de2d3SDavid du Colombier
5645d9de2d3SDavid du Colombier static long
ctldata(Ep * ep,void * a,long n)5655d9de2d3SDavid du Colombier ctldata(Ep *ep, void *a, long n)
5665d9de2d3SDavid du Colombier {
5675d9de2d3SDavid du Colombier Epio *epio;
5685d9de2d3SDavid du Colombier Block *b;
5695d9de2d3SDavid du Colombier
5705d9de2d3SDavid du Colombier epio = ep->aux;
5715d9de2d3SDavid du Colombier b = epio->cb;
5725d9de2d3SDavid du Colombier if(b == nil)
5735d9de2d3SDavid du Colombier return 0;
5745d9de2d3SDavid du Colombier if(n > BLEN(b))
5755d9de2d3SDavid du Colombier n = BLEN(b);
5765d9de2d3SDavid du Colombier memmove(a, b->rp, n);
5775d9de2d3SDavid du Colombier b->rp += n;
5785d9de2d3SDavid du Colombier if(BLEN(b) == 0){
5795d9de2d3SDavid du Colombier freeb(b);
5805d9de2d3SDavid du Colombier epio->cb = nil;
5815d9de2d3SDavid du Colombier }
5825d9de2d3SDavid du Colombier return n;
5835d9de2d3SDavid du Colombier }
5845d9de2d3SDavid du Colombier
5855d9de2d3SDavid du Colombier static void
greset(Dwcregs * r,int bits)5865d9de2d3SDavid du Colombier greset(Dwcregs *r, int bits)
5875d9de2d3SDavid du Colombier {
5885d9de2d3SDavid du Colombier r->grstctl |= bits;
5895d9de2d3SDavid du Colombier while(r->grstctl & bits)
5905d9de2d3SDavid du Colombier ;
5915d9de2d3SDavid du Colombier microdelay(10);
5925d9de2d3SDavid du Colombier }
5935d9de2d3SDavid du Colombier
5945d9de2d3SDavid du Colombier static void
init(Hci * hp)5955d9de2d3SDavid du Colombier init(Hci *hp)
5965d9de2d3SDavid du Colombier {
5975d9de2d3SDavid du Colombier Ctlr *ctlr;
5985d9de2d3SDavid du Colombier Dwcregs *r;
5995d9de2d3SDavid du Colombier uint n, rx, tx, ptx;
6005d9de2d3SDavid du Colombier
6015d9de2d3SDavid du Colombier ctlr = hp->aux;
6025d9de2d3SDavid du Colombier r = ctlr->regs;
6035d9de2d3SDavid du Colombier
6045d9de2d3SDavid du Colombier ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan);
6055d9de2d3SDavid du Colombier ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez));
6065d9de2d3SDavid du Colombier
6075d9de2d3SDavid du Colombier r->gahbcfg = 0;
6085d9de2d3SDavid du Colombier setpower(PowerUsb, 1);
6095d9de2d3SDavid du Colombier
6105d9de2d3SDavid du Colombier while((r->grstctl&Ahbidle) == 0)
6115d9de2d3SDavid du Colombier ;
6125d9de2d3SDavid du Colombier greset(r, Csftrst);
6135d9de2d3SDavid du Colombier
6145d9de2d3SDavid du Colombier r->gusbcfg |= Force_host_mode;
6155d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, 25);
6165d9de2d3SDavid du Colombier r->gahbcfg |= Dmaenable;
6175d9de2d3SDavid du Colombier
6185d9de2d3SDavid du Colombier n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth;
6195d9de2d3SDavid du Colombier rx = 0x306;
6205d9de2d3SDavid du Colombier tx = 0x100;
6215d9de2d3SDavid du Colombier ptx = 0x200;
6225d9de2d3SDavid du Colombier r->grxfsiz = rx;
6235d9de2d3SDavid du Colombier r->gnptxfsiz = rx | tx<<ODepth;
6245d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, 1);
6255d9de2d3SDavid du Colombier r->hptxfsiz = (rx + tx) | ptx << ODepth;
6265d9de2d3SDavid du Colombier greset(r, Rxfflsh);
6275d9de2d3SDavid du Colombier r->grstctl = TXF_ALL;
6285d9de2d3SDavid du Colombier greset(r, Txfflsh);
6295d9de2d3SDavid du Colombier dprint("usbotg: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
6305d9de2d3SDavid du Colombier n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz);
6315d9de2d3SDavid du Colombier
6325d9de2d3SDavid du Colombier r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
6335d9de2d3SDavid du Colombier r->gintsts = ~0;
6345d9de2d3SDavid du Colombier r->gintmsk = Hcintr;
6355d9de2d3SDavid du Colombier r->gahbcfg |= Glblintrmsk;
6365d9de2d3SDavid du Colombier }
6375d9de2d3SDavid du Colombier
6385d9de2d3SDavid du Colombier static void
dump(Hci *)6395d9de2d3SDavid du Colombier dump(Hci*)
6405d9de2d3SDavid du Colombier {
6415d9de2d3SDavid du Colombier }
6425d9de2d3SDavid du Colombier
6435d9de2d3SDavid du Colombier static void
fiqintr(Ureg *,void * a)6445d9de2d3SDavid du Colombier fiqintr(Ureg*, void *a)
6455d9de2d3SDavid du Colombier {
6465d9de2d3SDavid du Colombier Hci *hp;
6475d9de2d3SDavid du Colombier Ctlr *ctlr;
6485d9de2d3SDavid du Colombier Dwcregs *r;
6495d9de2d3SDavid du Colombier uint intr, haint, wakechan;
6505d9de2d3SDavid du Colombier int i;
6515d9de2d3SDavid du Colombier
6525d9de2d3SDavid du Colombier hp = a;
6535d9de2d3SDavid du Colombier ctlr = hp->aux;
6545d9de2d3SDavid du Colombier r = ctlr->regs;
6555d9de2d3SDavid du Colombier wakechan = 0;
6565d9de2d3SDavid du Colombier intr = r->gintsts;
6575d9de2d3SDavid du Colombier if(intr & Hcintr){
6585d9de2d3SDavid du Colombier haint = r->haint & r->haintmsk;
6595d9de2d3SDavid du Colombier for(i = 0; haint; i++){
660b4d1cf41SDavid du Colombier if(haint & 1){
661b4d1cf41SDavid du Colombier if(chanintr(ctlr, i) == 0){
6625d9de2d3SDavid du Colombier r->haintmsk &= ~(1<<i);
6635d9de2d3SDavid du Colombier wakechan |= 1<<i;
6645d9de2d3SDavid du Colombier }
665b4d1cf41SDavid du Colombier }
6665d9de2d3SDavid du Colombier haint >>= 1;
6675d9de2d3SDavid du Colombier }
6685d9de2d3SDavid du Colombier }
6695d9de2d3SDavid du Colombier if(intr & Sofintr){
6705d9de2d3SDavid du Colombier r->gintsts = Sofintr;
6715d9de2d3SDavid du Colombier if((r->hfnum&7) != 6){
6725d9de2d3SDavid du Colombier r->gintmsk &= ~Sofintr;
6735d9de2d3SDavid du Colombier wakechan |= ctlr->sofchan;
6745d9de2d3SDavid du Colombier ctlr->sofchan = 0;
6755d9de2d3SDavid du Colombier }
6765d9de2d3SDavid du Colombier }
6775d9de2d3SDavid du Colombier if(wakechan){
6785d9de2d3SDavid du Colombier ctlr->wakechan |= wakechan;
6795d9de2d3SDavid du Colombier armtimerset(1);
6805d9de2d3SDavid du Colombier }
6815d9de2d3SDavid du Colombier }
6825d9de2d3SDavid du Colombier
6835d9de2d3SDavid du Colombier static void
irqintr(Ureg *,void * a)6845d9de2d3SDavid du Colombier irqintr(Ureg*, void *a)
6855d9de2d3SDavid du Colombier {
6865d9de2d3SDavid du Colombier Ctlr *ctlr;
6875d9de2d3SDavid du Colombier uint wakechan;
6885d9de2d3SDavid du Colombier int i, x;
6895d9de2d3SDavid du Colombier
6905d9de2d3SDavid du Colombier ctlr = a;
6915d9de2d3SDavid du Colombier x = splfhi();
6925d9de2d3SDavid du Colombier armtimerset(0);
6935d9de2d3SDavid du Colombier wakechan = ctlr->wakechan;
6945d9de2d3SDavid du Colombier ctlr->wakechan = 0;
6955d9de2d3SDavid du Colombier splx(x);
6965d9de2d3SDavid du Colombier for(i = 0; wakechan; i++){
6975d9de2d3SDavid du Colombier if(wakechan & 1)
6985d9de2d3SDavid du Colombier wakeup(&ctlr->chanintr[i]);
6995d9de2d3SDavid du Colombier wakechan >>= 1;
7005d9de2d3SDavid du Colombier }
7015d9de2d3SDavid du Colombier }
7025d9de2d3SDavid du Colombier
7035d9de2d3SDavid du Colombier static void
epopen(Ep * ep)7045d9de2d3SDavid du Colombier epopen(Ep *ep)
7055d9de2d3SDavid du Colombier {
7065d9de2d3SDavid du Colombier ddprint("usbotg: epopen ep%d.%d ttype %d\n",
7075d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, ep->ttype);
7085d9de2d3SDavid du Colombier switch(ep->ttype){
7095d9de2d3SDavid du Colombier case Tnone:
710*a587111cSDavid du Colombier error(Enotconf);
7115d9de2d3SDavid du Colombier case Tintr:
7125d9de2d3SDavid du Colombier assert(ep->pollival > 0);
7135d9de2d3SDavid du Colombier /* fall through */
7145d9de2d3SDavid du Colombier case Tbulk:
7155d9de2d3SDavid du Colombier if(ep->toggle[Read] == 0)
7165d9de2d3SDavid du Colombier ep->toggle[Read] = DATA0;
7175d9de2d3SDavid du Colombier if(ep->toggle[Write] == 0)
7185d9de2d3SDavid du Colombier ep->toggle[Write] = DATA0;
7195d9de2d3SDavid du Colombier break;
7205d9de2d3SDavid du Colombier }
7215d9de2d3SDavid du Colombier ep->aux = malloc(sizeof(Epio));
7225d9de2d3SDavid du Colombier if(ep->aux == nil)
7235d9de2d3SDavid du Colombier error(Enomem);
7245d9de2d3SDavid du Colombier }
7255d9de2d3SDavid du Colombier
7265d9de2d3SDavid du Colombier static void
epclose(Ep * ep)7275d9de2d3SDavid du Colombier epclose(Ep *ep)
7285d9de2d3SDavid du Colombier {
7295d9de2d3SDavid du Colombier ddprint("usbotg: epclose ep%d.%d ttype %d\n",
7305d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, ep->ttype);
7315d9de2d3SDavid du Colombier switch(ep->ttype){
7325d9de2d3SDavid du Colombier case Tctl:
7335d9de2d3SDavid du Colombier freeb(((Epio*)ep->aux)->cb);
7345d9de2d3SDavid du Colombier /* fall through */
7355d9de2d3SDavid du Colombier default:
7365d9de2d3SDavid du Colombier free(ep->aux);
7375d9de2d3SDavid du Colombier break;
7385d9de2d3SDavid du Colombier }
7395d9de2d3SDavid du Colombier }
7405d9de2d3SDavid du Colombier
7415d9de2d3SDavid du Colombier static long
epread(Ep * ep,void * a,long n)7425d9de2d3SDavid du Colombier epread(Ep *ep, void *a, long n)
7435d9de2d3SDavid du Colombier {
7445d9de2d3SDavid du Colombier Epio *epio;
7455d9de2d3SDavid du Colombier Block *b;
7465d9de2d3SDavid du Colombier uchar *p;
7475d9de2d3SDavid du Colombier ulong elapsed;
7485d9de2d3SDavid du Colombier long nr;
7495d9de2d3SDavid du Colombier
7505d9de2d3SDavid du Colombier ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
7515d9de2d3SDavid du Colombier epio = ep->aux;
7525d9de2d3SDavid du Colombier b = nil;
7535d9de2d3SDavid du Colombier qlock(epio);
7545d9de2d3SDavid du Colombier if(waserror()){
7555d9de2d3SDavid du Colombier qunlock(epio);
7565d9de2d3SDavid du Colombier if(b)
7575d9de2d3SDavid du Colombier freeb(b);
7585d9de2d3SDavid du Colombier nexterror();
7595d9de2d3SDavid du Colombier }
7605d9de2d3SDavid du Colombier switch(ep->ttype){
7615d9de2d3SDavid du Colombier default:
7625d9de2d3SDavid du Colombier error(Egreg);
7635d9de2d3SDavid du Colombier case Tctl:
7645d9de2d3SDavid du Colombier nr = ctldata(ep, a, n);
7655d9de2d3SDavid du Colombier qunlock(epio);
7665d9de2d3SDavid du Colombier poperror();
7675d9de2d3SDavid du Colombier return nr;
7685d9de2d3SDavid du Colombier case Tintr:
7695d9de2d3SDavid du Colombier elapsed = TK2MS(m->ticks) - epio->lastpoll;
7705d9de2d3SDavid du Colombier if(elapsed < ep->pollival)
7715d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
7725d9de2d3SDavid du Colombier /* fall through */
7735d9de2d3SDavid du Colombier case Tbulk:
7745d9de2d3SDavid du Colombier /* XXX cache madness */
7755d9de2d3SDavid du Colombier b = allocb(ROUND(n, ep->maxpkt) + CACHELINESZ);
7765d9de2d3SDavid du Colombier p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
7775d9de2d3SDavid du Colombier cachedwbinvse(p, n);
7785d9de2d3SDavid du Colombier nr = eptrans(ep, Read, p, n);
7795d9de2d3SDavid du Colombier epio->lastpoll = TK2MS(m->ticks);
7805d9de2d3SDavid du Colombier memmove(a, p, nr);
7815d9de2d3SDavid du Colombier qunlock(epio);
7825d9de2d3SDavid du Colombier freeb(b);
7835d9de2d3SDavid du Colombier poperror();
7845d9de2d3SDavid du Colombier return nr;
7855d9de2d3SDavid du Colombier }
7865d9de2d3SDavid du Colombier }
7875d9de2d3SDavid du Colombier
7885d9de2d3SDavid du Colombier static long
epwrite(Ep * ep,void * a,long n)7895d9de2d3SDavid du Colombier epwrite(Ep *ep, void *a, long n)
7905d9de2d3SDavid du Colombier {
7915d9de2d3SDavid du Colombier Epio *epio;
7925d9de2d3SDavid du Colombier Block *b;
7935d9de2d3SDavid du Colombier uchar *p;
7945d9de2d3SDavid du Colombier ulong elapsed;
7955d9de2d3SDavid du Colombier
7965d9de2d3SDavid du Colombier ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
7975d9de2d3SDavid du Colombier epio = ep->aux;
7985d9de2d3SDavid du Colombier b = nil;
7995d9de2d3SDavid du Colombier qlock(epio);
8005d9de2d3SDavid du Colombier if(waserror()){
8015d9de2d3SDavid du Colombier qunlock(epio);
8025d9de2d3SDavid du Colombier if(b)
8035d9de2d3SDavid du Colombier freeb(b);
8045d9de2d3SDavid du Colombier nexterror();
8055d9de2d3SDavid du Colombier }
8065d9de2d3SDavid du Colombier switch(ep->ttype){
8075d9de2d3SDavid du Colombier default:
8085d9de2d3SDavid du Colombier error(Egreg);
8095d9de2d3SDavid du Colombier case Tintr:
8105d9de2d3SDavid du Colombier elapsed = TK2MS(m->ticks) - epio->lastpoll;
8115d9de2d3SDavid du Colombier if(elapsed < ep->pollival)
8125d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
8135d9de2d3SDavid du Colombier /* fall through */
8145d9de2d3SDavid du Colombier case Tctl:
8155d9de2d3SDavid du Colombier case Tbulk:
8165d9de2d3SDavid du Colombier /* XXX cache madness */
8175d9de2d3SDavid du Colombier b = allocb(n + CACHELINESZ);
8185d9de2d3SDavid du Colombier p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
8195d9de2d3SDavid du Colombier memmove(p, a, n);
8205d9de2d3SDavid du Colombier cachedwbse(p, n);
8215d9de2d3SDavid du Colombier if(ep->ttype == Tctl)
8225d9de2d3SDavid du Colombier n = ctltrans(ep, p, n);
8235d9de2d3SDavid du Colombier else{
8245d9de2d3SDavid du Colombier n = eptrans(ep, Write, p, n);
8255d9de2d3SDavid du Colombier epio->lastpoll = TK2MS(m->ticks);
8265d9de2d3SDavid du Colombier }
8275d9de2d3SDavid du Colombier qunlock(epio);
8285d9de2d3SDavid du Colombier freeb(b);
8295d9de2d3SDavid du Colombier poperror();
8305d9de2d3SDavid du Colombier return n;
8315d9de2d3SDavid du Colombier }
8325d9de2d3SDavid du Colombier }
8335d9de2d3SDavid du Colombier
8345d9de2d3SDavid du Colombier static char*
seprintep(char * s,char *,Ep *)8355d9de2d3SDavid du Colombier seprintep(char *s, char*, Ep*)
8365d9de2d3SDavid du Colombier {
8375d9de2d3SDavid du Colombier return s;
8385d9de2d3SDavid du Colombier }
8395d9de2d3SDavid du Colombier
8405d9de2d3SDavid du Colombier static int
portenable(Hci * hp,int port,int on)8415d9de2d3SDavid du Colombier portenable(Hci *hp, int port, int on)
8425d9de2d3SDavid du Colombier {
8435d9de2d3SDavid du Colombier Ctlr *ctlr;
8445d9de2d3SDavid du Colombier Dwcregs *r;
8455d9de2d3SDavid du Colombier
8465d9de2d3SDavid du Colombier assert(port == 1);
8475d9de2d3SDavid du Colombier ctlr = hp->aux;
8485d9de2d3SDavid du Colombier r = ctlr->regs;
8495d9de2d3SDavid du Colombier dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
8505d9de2d3SDavid du Colombier if(!on)
8515d9de2d3SDavid du Colombier r->hport0 = Prtpwr | Prtena;
8525d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, Enabledelay);
8535d9de2d3SDavid du Colombier dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
8545d9de2d3SDavid du Colombier return 0;
8555d9de2d3SDavid du Colombier }
8565d9de2d3SDavid du Colombier
8575d9de2d3SDavid du Colombier static int
portreset(Hci * hp,int port,int on)8585d9de2d3SDavid du Colombier portreset(Hci *hp, int port, int on)
8595d9de2d3SDavid du Colombier {
8605d9de2d3SDavid du Colombier Ctlr *ctlr;
8615d9de2d3SDavid du Colombier Dwcregs *r;
8625d9de2d3SDavid du Colombier int b, s;
8635d9de2d3SDavid du Colombier
8645d9de2d3SDavid du Colombier assert(port == 1);
8655d9de2d3SDavid du Colombier ctlr = hp->aux;
8665d9de2d3SDavid du Colombier r = ctlr->regs;
8675d9de2d3SDavid du Colombier dprint("usbotg reset=%d; sts %#x\n", on, r->hport0);
8685d9de2d3SDavid du Colombier if(!on)
8695d9de2d3SDavid du Colombier return 0;
8705d9de2d3SDavid du Colombier r->hport0 = Prtpwr | Prtrst;
8715d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, ResetdelayHS);
8725d9de2d3SDavid du Colombier r->hport0 = Prtpwr;
8735d9de2d3SDavid du Colombier tsleep(&up->sleep, return0, 0, Enabledelay);
8745d9de2d3SDavid du Colombier s = r->hport0;
8755d9de2d3SDavid du Colombier b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
8765d9de2d3SDavid du Colombier if(b != 0)
8775d9de2d3SDavid du Colombier r->hport0 = Prtpwr | b;
8785d9de2d3SDavid du Colombier dprint("usbotg reset=%d; sts %#x\n", on, s);
8795d9de2d3SDavid du Colombier if((s & Prtena) == 0)
8805d9de2d3SDavid du Colombier print("usbotg: host port not enabled after reset");
8815d9de2d3SDavid du Colombier return 0;
8825d9de2d3SDavid du Colombier }
8835d9de2d3SDavid du Colombier
8845d9de2d3SDavid du Colombier static int
portstatus(Hci * hp,int port)8855d9de2d3SDavid du Colombier portstatus(Hci *hp, int port)
8865d9de2d3SDavid du Colombier {
8875d9de2d3SDavid du Colombier Ctlr *ctlr;
8885d9de2d3SDavid du Colombier Dwcregs *r;
8895d9de2d3SDavid du Colombier int b, s;
8905d9de2d3SDavid du Colombier
8915d9de2d3SDavid du Colombier assert(port == 1);
8925d9de2d3SDavid du Colombier ctlr = hp->aux;
8935d9de2d3SDavid du Colombier r = ctlr->regs;
8945d9de2d3SDavid du Colombier s = r->hport0;
8955d9de2d3SDavid du Colombier b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
8965d9de2d3SDavid du Colombier if(b != 0)
8975d9de2d3SDavid du Colombier r->hport0 = Prtpwr | b;
8985d9de2d3SDavid du Colombier b = 0;
8995d9de2d3SDavid du Colombier if(s & Prtconnsts)
9005d9de2d3SDavid du Colombier b |= HPpresent;
9015d9de2d3SDavid du Colombier if(s & Prtconndet)
9025d9de2d3SDavid du Colombier b |= HPstatuschg;
9035d9de2d3SDavid du Colombier if(s & Prtena)
9045d9de2d3SDavid du Colombier b |= HPenable;
9055d9de2d3SDavid du Colombier if(s & Prtenchng)
9065d9de2d3SDavid du Colombier b |= HPchange;
9075d9de2d3SDavid du Colombier if(s & Prtovrcurract)
9085d9de2d3SDavid du Colombier b |= HPovercurrent;
9095d9de2d3SDavid du Colombier if(s & Prtsusp)
9105d9de2d3SDavid du Colombier b |= HPsuspend;
9115d9de2d3SDavid du Colombier if(s & Prtrst)
9125d9de2d3SDavid du Colombier b |= HPreset;
9135d9de2d3SDavid du Colombier if(s & Prtpwr)
9145d9de2d3SDavid du Colombier b |= HPpower;
9155d9de2d3SDavid du Colombier switch(s & Prtspd){
9165d9de2d3SDavid du Colombier case HIGHSPEED:
9175d9de2d3SDavid du Colombier b |= HPhigh;
9185d9de2d3SDavid du Colombier break;
9195d9de2d3SDavid du Colombier case LOWSPEED:
9205d9de2d3SDavid du Colombier b |= HPslow;
9215d9de2d3SDavid du Colombier break;
9225d9de2d3SDavid du Colombier }
9235d9de2d3SDavid du Colombier return b;
9245d9de2d3SDavid du Colombier }
9255d9de2d3SDavid du Colombier
9265d9de2d3SDavid du Colombier static void
shutdown(Hci *)9275d9de2d3SDavid du Colombier shutdown(Hci*)
9285d9de2d3SDavid du Colombier {
9295d9de2d3SDavid du Colombier }
9305d9de2d3SDavid du Colombier
9315d9de2d3SDavid du Colombier static void
setdebug(Hci *,int d)9325d9de2d3SDavid du Colombier setdebug(Hci*, int d)
9335d9de2d3SDavid du Colombier {
9345d9de2d3SDavid du Colombier debug = d;
9355d9de2d3SDavid du Colombier }
9365d9de2d3SDavid du Colombier
9375d9de2d3SDavid du Colombier static int
reset(Hci * hp)9385d9de2d3SDavid du Colombier reset(Hci *hp)
9395d9de2d3SDavid du Colombier {
9405d9de2d3SDavid du Colombier Ctlr *ctlr;
9415d9de2d3SDavid du Colombier uint id;
9425d9de2d3SDavid du Colombier
9435d9de2d3SDavid du Colombier ctlr = &dwc;
9445d9de2d3SDavid du Colombier if(ctlr->regs != nil)
9455d9de2d3SDavid du Colombier return -1;
9465d9de2d3SDavid du Colombier ctlr->regs = (Dwcregs*)USBREGS;
9475d9de2d3SDavid du Colombier id = ctlr->regs->gsnpsid;
9485d9de2d3SDavid du Colombier if((id>>16) != ('O'<<8 | 'T'))
9495d9de2d3SDavid du Colombier return -1;
9505d9de2d3SDavid du Colombier dprint("usbotg: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
9515d9de2d3SDavid du Colombier
9525d9de2d3SDavid du Colombier intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");
9535d9de2d3SDavid du Colombier
9545d9de2d3SDavid du Colombier hp->aux = ctlr;
9555d9de2d3SDavid du Colombier hp->port = 0;
9565d9de2d3SDavid du Colombier hp->irq = IRQusb;
9575d9de2d3SDavid du Colombier hp->tbdf = 0;
9585d9de2d3SDavid du Colombier hp->nports = 1;
9595d9de2d3SDavid du Colombier hp->highspeed = 1;
9605d9de2d3SDavid du Colombier
9615d9de2d3SDavid du Colombier hp->init = init;
9625d9de2d3SDavid du Colombier hp->dump = dump;
9635d9de2d3SDavid du Colombier hp->interrupt = fiqintr;
9645d9de2d3SDavid du Colombier hp->epopen = epopen;
9655d9de2d3SDavid du Colombier hp->epclose = epclose;
9665d9de2d3SDavid du Colombier hp->epread = epread;
9675d9de2d3SDavid du Colombier hp->epwrite = epwrite;
9685d9de2d3SDavid du Colombier hp->seprintep = seprintep;
9695d9de2d3SDavid du Colombier hp->portenable = portenable;
9705d9de2d3SDavid du Colombier hp->portreset = portreset;
9715d9de2d3SDavid du Colombier hp->portstatus = portstatus;
9725d9de2d3SDavid du Colombier hp->shutdown = shutdown;
9735d9de2d3SDavid du Colombier hp->debug = setdebug;
9745d9de2d3SDavid du Colombier hp->type = "dwcotg";
9755d9de2d3SDavid du Colombier return 0;
9765d9de2d3SDavid du Colombier }
9775d9de2d3SDavid du Colombier
9785d9de2d3SDavid du Colombier void
usbdwclink(void)9795d9de2d3SDavid du Colombier usbdwclink(void)
9805d9de2d3SDavid du Colombier {
9815d9de2d3SDavid du Colombier addhcitype("dwcotg", reset);
9825d9de2d3SDavid du Colombier }
983