summaryrefslogtreecommitdiff
path: root/sys/src/9/bcm/usbdwc.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@gmx.de>2013-01-26 17:33:56 +0100
committercinap_lenrek <cinap_lenrek@gmx.de>2013-01-26 17:33:56 +0100
commitbc610a1b1c32f6e2e9b034217bb3ce9a7defa739 (patch)
tree98fa1e680867eaf19c1be5e818a570713fa7a286 /sys/src/9/bcm/usbdwc.c
parentea108c8ca6e726ac008f75775ab83775ec233171 (diff)
add raspberry pi kernel (from sources)
Diffstat (limited to 'sys/src/9/bcm/usbdwc.c')
-rw-r--r--sys/src/9/bcm/usbdwc.c965
1 files changed, 965 insertions, 0 deletions
diff --git a/sys/src/9/bcm/usbdwc.c b/sys/src/9/bcm/usbdwc.c
new file mode 100644
index 000000000..272405537
--- /dev/null
+++ b/sys/src/9/bcm/usbdwc.c
@@ -0,0 +1,965 @@
+/*
+ * USB host driver for BCM2835
+ * Synopsis DesignWare Core USB 2.0 OTG controller
+ *
+ * Copyright © 2012 Richard Miller <r.miller@acm.org>
+ *
+ * This is work in progress:
+ * - no isochronous pipes
+ * - no bandwidth budgeting
+ * - frame scheduling is crude
+ * - error handling is overly optimistic
+ * It should be just about adequate for a Plan 9 terminal with
+ * keyboard, mouse, ethernet adapter, and an external flash drive.
+ */
+
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/usb.h"
+
+#include "dwcotg.h"
+
+enum
+{
+ USBREGS = VIRTIO + 0x980000,
+ Enabledelay = 50,
+ Resetdelay = 10,
+ ResetdelayHS = 50,
+
+ Read = 0,
+ Write = 1,
+};
+
+typedef struct Ctlr Ctlr;
+typedef struct Epio Epio;
+
+struct Ctlr {
+ Dwcregs *regs; /* controller registers */
+ int nchan; /* number of host channels */
+ ulong chanbusy; /* bitmap of in-use channels */
+ QLock chanlock; /* serialise access to chanbusy */
+ QLock split; /* serialise split transactions */
+ int splitretry; /* count retries of Nyet */
+ int sofchan; /* bitmap of channels waiting for sof */
+ int wakechan; /* bitmap of channels to wakeup after fiq */
+ int debugchan; /* bitmap of channels for interrupt debug */
+ Rendez *chanintr; /* sleep till interrupt on channel N */
+};
+
+struct Epio {
+ QLock;
+ Block *cb;
+ ulong lastpoll;
+};
+
+static Ctlr dwc;
+static int debug;
+
+static char Ebadlen[] = "bad usb request length";
+static char Enotconfig[] = "usb endpoint not configured";
+
+static void clog(Ep *ep, Hostchan *hc);
+static void logdump(Ep *ep);
+
+static Hostchan*
+chanalloc(Ep *ep)
+{
+ Ctlr *ctlr;
+ int bitmap, i;
+
+ ctlr = ep->hp->aux;
+ qlock(&ctlr->chanlock);
+ bitmap = ctlr->chanbusy;
+ for(i = 0; i < ctlr->nchan; i++)
+ if((bitmap & (1<<i)) == 0){
+ ctlr->chanbusy = bitmap | 1 << i;
+ qunlock(&ctlr->chanlock);
+ return &ctlr->regs->hchan[i];
+ }
+ qunlock(&ctlr->chanlock);
+ panic("miller is a lazy git");
+ return nil;
+}
+
+static void
+chanrelease(Ep *ep, Hostchan *chan)
+{
+ Ctlr *ctlr;
+ int i;
+
+ ctlr = ep->hp->aux;
+ i = chan - ctlr->regs->hchan;
+ qlock(&ctlr->chanlock);
+ ctlr->chanbusy &= ~(1 << i);
+ qunlock(&ctlr->chanlock);
+}
+
+static void
+chansetup(Hostchan *hc, Ep *ep)
+{
+ int hcc;
+ Ctlr *ctlr = ep->hp->aux;
+
+ if(ep->debug)
+ ctlr->debugchan |= 1 << (hc - ctlr->regs->hchan);
+ else
+ ctlr->debugchan &= ~(1 << (hc - ctlr->regs->hchan));
+ switch(ep->dev->state){
+ case Dconfig:
+ case Dreset:
+ hcc = 0;
+ break;
+ default:
+ hcc = ep->dev->nb<<ODevaddr;
+ break;
+ }
+ hcc |= ep->maxpkt | 1<<OMulticnt | ep->nb<<OEpnum;
+ switch(ep->ttype){
+ case Tctl:
+ hcc |= Epctl;
+ break;
+ case Tiso:
+ hcc |= Episo;
+ break;
+ case Tbulk:
+ hcc |= Epbulk;
+ break;
+ case Tintr:
+ hcc |= Epintr;
+ break;
+ }
+ switch(ep->dev->speed){
+ case Lowspeed:
+ hcc |= Lspddev;
+ /* fall through */
+ case Fullspeed:
+ hc->hcsplt = Spltena | POS_ALL | ep->dev->hub << OHubaddr |
+ ep->dev->port;
+ break;
+ default:
+ hc->hcsplt = 0;
+ break;
+ }
+ hc->hcchar = hcc;
+ hc->hcint = ~0;
+}
+
+static int
+sofdone(void *a)
+{
+ Dwcregs *r;
+
+ r = a;
+ return r->gintsts & Sofintr;
+}
+
+static void
+sofwait(Ctlr *ctlr, int n)
+{
+ Dwcregs *r;
+ int x;
+
+ r = ctlr->regs;
+ do{
+ r->gintsts = Sofintr;
+ x = splfhi();
+ ctlr->sofchan |= 1 << n;
+ r->gintmsk |= Sofintr;
+ sleep(&ctlr->chanintr[n], sofdone, r);
+ splx(x);
+ }while((r->hfnum & 7) == 6);
+}
+
+static int
+chandone(void *a)
+{
+ Hostchan *hc;
+
+ hc = a;
+ return (hc->hcint & hc->hcintmsk) != 0;
+}
+
+static int
+chanwait(Ep *ep, Ctlr *ctlr, Hostchan *hc, int mask)
+{
+ int intr, n, x, ointr;
+ ulong start, now;
+ Dwcregs *r;
+
+ r = ctlr->regs;
+ n = hc - r->hchan;
+ for(;;){
+restart:
+ x = splfhi();
+ r->haintmsk |= 1 << n;
+ hc->hcintmsk = mask;
+ sleep(&ctlr->chanintr[n], chandone, hc);
+ hc->hcintmsk = 0;
+ splx(x);
+ intr = hc->hcint;
+ if(intr & Chhltd)
+ return intr;
+ start = fastticks(0);
+ ointr = intr;
+ now = start;
+ do{
+ intr = hc->hcint;
+ if(intr & Chhltd){
+ if((ointr != Ack && ointr != (Ack|Xfercomp)) ||
+ intr != (Ack|Chhltd|Xfercomp) ||
+ (now - start) > 60)
+ dprint("await %x after %ld %x -> %x\n",
+ mask, now - start, ointr, intr);
+ return intr;
+ }
+ if((intr & mask) == 0){
+ dprint("ep%d.%d await %x intr %x -> %x\n", ep->dev->nb, ep->nb, mask, ointr, intr);
+ goto restart;
+ }
+ now = fastticks(0);
+ }while(now - start < 100);
+ dprint("ep%d.%d halting channel %8.8ux hcchar %8.8ux "
+ "grxstsr %8.8ux gnptxsts %8.8ux hptxsts %8.8ux\n",
+ ep->dev->nb, ep->nb, intr, hc->hcchar, r->grxstsr,
+ r->gnptxsts, r->hptxsts);
+ mask = Chhltd;
+ hc->hcchar |= Chdis;
+ start = m->ticks;
+ while(hc->hcchar & Chen){
+ if(m->ticks - start >= 100){
+ print("ep%d.%d channel won't halt hcchar %8.8ux\n",
+ ep->dev->nb, ep->nb, hc->hcchar);
+ break;
+ }
+ }
+ logdump(ep);
+ }
+}
+
+static int
+chanintr(Ctlr *ctlr, int n)
+{
+ Hostchan *hc;
+ int i;
+
+ hc = &ctlr->regs->hchan[n];
+ if(ctlr->debugchan & (1 << n))
+ clog(nil, hc);
+ if((hc->hcsplt & Spltena) == 0)
+ return 0;
+ i = hc->hcint;
+ if(i == (Chhltd|Ack)){
+ hc->hcsplt |= Compsplt;
+ ctlr->splitretry = 0;
+ }else if(i == (Chhltd|Nyet)){
+ if(++ctlr->splitretry >= 3)
+ return 0;
+ }else
+ return 0;
+ if(hc->hcchar & Chen){
+ iprint("hcchar %8.8ux hcint %8.8ux", hc->hcchar, hc->hcint);
+ hc->hcchar |= Chen | Chdis;
+ while(hc->hcchar&Chen)
+ ;
+ iprint(" %8.8ux\n", hc->hcint);
+ }
+ hc->hcint = i;
+ if(ctlr->regs->hfnum & 1)
+ hc->hcchar &= ~Oddfrm;
+ else
+ hc->hcchar |= Oddfrm;
+ hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
+ return 1;
+}
+
+static Reg chanlog[32][5];
+static int nchanlog;
+
+static void
+logstart(Ep *ep)
+{
+ if(ep->debug)
+ nchanlog = 0;
+}
+
+static void
+clog(Ep *ep, Hostchan *hc)
+{
+ Reg *p;
+
+ if(ep != nil && !ep->debug)
+ return;
+ if(nchanlog == 32)
+ nchanlog--;
+ p = chanlog[nchanlog];
+ p[0] = dwc.regs->hfnum;
+ p[1] = hc->hcchar;
+ p[2] = hc->hcint;
+ p[3] = hc->hctsiz;
+ p[4] = hc->hcdma;
+ nchanlog++;
+}
+
+static void
+logdump(Ep *ep)
+{
+ Reg *p;
+ int i;
+
+ if(!ep->debug)
+ return;
+ p = chanlog[0];
+ for(i = 0; i < nchanlog; i++){
+ print("%5.5d.%5.5d %8.8ux %8.8ux %8.8ux %8.8ux\n",
+ p[0]&0xFFFF, p[0]>>16, p[1], p[2], p[3], p[4]);
+ p += 5;
+ }
+ nchanlog = 0;
+}
+
+static int
+chanio(Ep *ep, Hostchan *hc, int dir, int pid, void *a, int len)
+{
+ Ctlr *ctlr;
+ int nleft, n, nt, i, maxpkt, npkt;
+ uint hcdma, hctsiz;
+
+ ctlr = ep->hp->aux;
+ maxpkt = ep->maxpkt;
+ npkt = HOWMANY(len, ep->maxpkt);
+ if(npkt == 0)
+ npkt = 1;
+
+ hc->hcchar = (hc->hcchar & ~Epdir) | dir;
+ if(dir == Epin)
+ n = ROUND(len, ep->maxpkt);
+ else
+ n = len;
+ hc->hctsiz = n | npkt << OPktcnt | pid;
+ hc->hcdma = PADDR(a);
+
+ nleft = len;
+ logstart(ep);
+ for(;;){
+ hcdma = hc->hcdma;
+ hctsiz = hc->hctsiz;
+ hc->hctsiz = hctsiz & ~Dopng;
+ if(hc->hcchar&Chen){
+ dprint("ep%d.%d before chanio hcchar=%8.8ux\n",
+ ep->dev->nb, ep->nb, hc->hcchar);
+ hc->hcchar |= Chen | Chdis;
+ while(hc->hcchar&Chen)
+ ;
+ hc->hcint = Chhltd;
+ }
+ if((i = hc->hcint) != 0){
+ dprint("ep%d.%d before chanio hcint=%8.8ux\n",
+ ep->dev->nb, ep->nb, i);
+ hc->hcint = i;
+ }
+ if(hc->hcsplt & Spltena){
+ qlock(&ctlr->split);
+ sofwait(ctlr, hc - ctlr->regs->hchan);
+ if((dwc.regs->hfnum & 1) == 0)
+ hc->hcchar &= ~Oddfrm;
+ else
+ hc->hcchar |= Oddfrm;
+ }
+ hc->hcchar = (hc->hcchar &~ Chdis) | Chen;
+ clog(ep, hc);
+ if(ep->ttype == Tbulk && dir == Epin)
+ i = chanwait(ep, ctlr, hc, /* Ack| */ Chhltd);
+ else if(ep->ttype == Tintr && (hc->hcsplt & Spltena))
+ i = chanwait(ep, ctlr, hc, Chhltd);
+ else
+ i = chanwait(ep, ctlr, hc, Chhltd|Nak);
+ clog(ep, hc);
+ hc->hcint = i;
+
+ if(hc->hcsplt & Spltena){
+ hc->hcsplt &= ~Compsplt;
+ qunlock(&ctlr->split);
+ }
+
+ if((i & Xfercomp) == 0 && i != (Chhltd|Ack) && i != Chhltd){
+ if(i & Stall)
+ error(Estalled);
+ if(i & Nyet)
+ continue;
+ if(i & Nak){
+ if(ep->ttype == Tintr)
+ tsleep(&up->sleep, return0, 0, ep->pollival);
+ else
+ tsleep(&up->sleep, return0, 0, 1);
+ continue;
+ }
+ print("usbotg: ep%d.%d error intr %8.8ux\n",
+ ep->dev->nb, ep->nb, i);
+ if(i & ~(Chhltd|Ack))
+ error(Eio);
+ if(hc->hcdma != hcdma)
+ print("usbotg: weird hcdma %x->%x intr %x->%x\n",
+ hcdma, hc->hcdma, i, hc->hcint);
+ }
+ n = hc->hcdma - hcdma;
+ if(n == 0)
+ if((hc->hctsiz & Pktcnt) != (hctsiz & Pktcnt))
+ break;
+ else
+ continue;
+ if(dir == Epin && ep->ttype == Tbulk && n == nleft){
+ nt = (hctsiz & Xfersize) - (hc->hctsiz & Xfersize);
+ if(nt != n)
+ if(n == ((nt+3) & ~3))
+ n = nt;
+ else
+ print("usbotg: intr %8.8ux dma "
+ "%8.8ux-%8.8ux hctsiz "
+ "%8.8ux-%8.ux\n",
+ i, hcdma, hc->hcdma, hctsiz,
+ hc->hctsiz);
+ }
+ if(n > nleft){
+ if(n != ((nleft+3) & ~3))
+ dprint("too much: wanted %d got %d\n",
+ len, len - nleft + n);
+ n = nleft;
+ }
+ nleft -= n;
+ if(nleft == 0 || n % maxpkt != 0)
+ break;
+ if((i & Xfercomp) && ep->ttype != Tctl)
+ break;
+ if(dir == Epout)
+ dprint("too little: nleft %d hcdma %x->%x hctsiz %x->%x intr %x\n",
+ nleft, hcdma, hc->hcdma, hctsiz, hc->hctsiz, i);
+ }
+ logdump(ep);
+ return len - nleft;
+}
+
+static long
+eptrans(Ep *ep, int rw, void *a, long n)
+{
+ Hostchan *hc;
+
+ if(ep->clrhalt){
+ ep->clrhalt = 0;
+ if(ep->mode != OREAD)
+ ep->toggle[Write] = DATA0;
+ if(ep->mode != OWRITE)
+ ep->toggle[Read] = DATA0;
+ }
+ hc = chanalloc(ep);
+ if(waserror()){
+ ep->toggle[rw] = hc->hctsiz & Pid;
+ chanrelease(ep, hc);
+ if(strcmp(up->errstr, Estalled) == 0)
+ return 0;
+ nexterror();
+ }
+ chansetup(hc, ep);
+ if(rw == Read && ep->ttype == Tbulk){
+ long sofar, m;
+
+ sofar = 0;
+ do{
+ m = n - sofar;
+ if(m > ep->maxpkt)
+ m = ep->maxpkt;
+ m = chanio(ep, hc, Epin, ep->toggle[rw],
+ (char*)a + sofar, m);
+ ep->toggle[rw] = hc->hctsiz & Pid;
+ sofar += m;
+ }while(sofar < n && m == ep->maxpkt);
+ n = sofar;
+ }else{
+ n = chanio(ep, hc, rw == Read? Epin: Epout, ep->toggle[rw],
+ a, n);
+ ep->toggle[rw] = hc->hctsiz & Pid;
+ }
+ chanrelease(ep, hc);
+ poperror();
+ return n;
+}
+
+static long
+ctltrans(Ep *ep, uchar *req, long n)
+{
+ Hostchan *hc;
+ Epio *epio;
+ Block *b;
+ uchar *data;
+ int datalen;
+
+ epio = ep->aux;
+ if(epio->cb != nil){
+ freeb(epio->cb);
+ epio->cb = nil;
+ }
+ if(n < Rsetuplen)
+ error(Ebadlen);
+ if(req[Rtype] & Rd2h){
+ datalen = GET2(req+Rcount);
+ if(datalen <= 0 || datalen > Maxctllen)
+ error(Ebadlen);
+ /* XXX cache madness */
+ epio->cb = b = allocb(ROUND(datalen, ep->maxpkt) + CACHELINESZ);
+ b->wp = (uchar*)ROUND((uintptr)b->wp, CACHELINESZ);
+ memset(b->wp, 0x55, b->lim - b->wp);
+ cachedwbinvse(b->wp, b->lim - b->wp);
+ data = b->wp;
+ }else{
+ b = nil;
+ datalen = n - Rsetuplen;
+ data = req + Rsetuplen;
+ }
+ hc = chanalloc(ep);
+ if(waserror()){
+ chanrelease(ep, hc);
+ if(strcmp(up->errstr, Estalled) == 0)
+ return 0;
+ nexterror();
+ }
+ chansetup(hc, ep);
+ chanio(ep, hc, Epout, SETUP, req, Rsetuplen);
+ if(req[Rtype] & Rd2h){
+ b->wp += chanio(ep, hc, Epin, DATA1, data, datalen);
+ chanio(ep, hc, Epout, DATA1, nil, 0);
+ n = Rsetuplen;
+ }else{
+ if(datalen > 0)
+ chanio(ep, hc, Epout, DATA1, data, datalen);
+ chanio(ep, hc, Epin, DATA1, nil, 0);
+ n = Rsetuplen + datalen;
+ }
+ chanrelease(ep, hc);
+ poperror();
+ return n;
+}
+
+static long
+ctldata(Ep *ep, void *a, long n)
+{
+ Epio *epio;
+ Block *b;
+
+ epio = ep->aux;
+ b = epio->cb;
+ if(b == nil)
+ return 0;
+ if(n > BLEN(b))
+ n = BLEN(b);
+ memmove(a, b->rp, n);
+ b->rp += n;
+ if(BLEN(b) == 0){
+ freeb(b);
+ epio->cb = nil;
+ }
+ return n;
+}
+
+static void
+greset(Dwcregs *r, int bits)
+{
+ r->grstctl |= bits;
+ while(r->grstctl & bits)
+ ;
+ microdelay(10);
+}
+
+static void
+init(Hci *hp)
+{
+ Ctlr *ctlr;
+ Dwcregs *r;
+ uint n, rx, tx, ptx;
+
+ ctlr = hp->aux;
+ r = ctlr->regs;
+
+ ctlr->nchan = 1 + ((r->ghwcfg2 & Num_host_chan) >> ONum_host_chan);
+ ctlr->chanintr = malloc(ctlr->nchan * sizeof(Rendez));
+
+ r->gahbcfg = 0;
+ setpower(PowerUsb, 1);
+
+ while((r->grstctl&Ahbidle) == 0)
+ ;
+ greset(r, Csftrst);
+
+ r->gusbcfg |= Force_host_mode;
+ tsleep(&up->sleep, return0, 0, 25);
+ r->gahbcfg |= Dmaenable;
+
+ n = (r->ghwcfg3 & Dfifo_depth) >> ODfifo_depth;
+ rx = 0x306;
+ tx = 0x100;
+ ptx = 0x200;
+ r->grxfsiz = rx;
+ r->gnptxfsiz = rx | tx << ODepth;
+ tsleep(&up->sleep, return0, 0, 1);
+ r->hptxfsiz = (rx + tx) | ptx << ODepth;
+ greset(r, Rxfflsh);
+ r->grstctl = TXF_ALL;
+ greset(r, Txfflsh);
+ dprint("usbotg: FIFO depth %d sizes rx/nptx/ptx %8.8ux %8.8ux %8.8ux\n",
+ n, r->grxfsiz, r->gnptxfsiz, r->hptxfsiz);
+
+ r->hport0 = Prtpwr|Prtconndet|Prtenchng|Prtovrcurrchng;
+ r->gintsts = ~0;
+ r->gintmsk = Hcintr;
+ r->gahbcfg |= Glblintrmsk;
+}
+
+static void
+dump(Hci*)
+{
+}
+
+static void
+fiqintr(Ureg*, void *a)
+{
+ Hci *hp;
+ Ctlr *ctlr;
+ Dwcregs *r;
+ uint intr, haint, wakechan;
+ int i;
+
+ hp = a;
+ ctlr = hp->aux;
+ r = ctlr->regs;
+ wakechan = 0;
+ intr = r->gintsts;
+ if(intr & Hcintr){
+ haint = r->haint & r->haintmsk;
+ for(i = 0; haint; i++){
+ if(haint & 1 && chanintr(ctlr, i) == 0){
+ r->haintmsk &= ~(1 << i);
+ wakechan |= 1 << i;
+ }
+ haint >>= 1;
+ }
+ }
+ if(intr & Sofintr){
+ r->gintsts = Sofintr;
+ if((r->hfnum&7) != 6){
+ r->gintmsk &= ~Sofintr;
+ wakechan |= ctlr->sofchan;
+ ctlr->sofchan = 0;
+ }
+ }
+ if(wakechan){
+ ctlr->wakechan |= wakechan;
+ armtimerset(1);
+ }
+}
+
+static void
+irqintr(Ureg*, void *a)
+{
+ Ctlr *ctlr;
+ uint wakechan;
+ int i, x;
+
+ ctlr = a;
+ x = splfhi();
+ armtimerset(0);
+ wakechan = ctlr->wakechan;
+ ctlr->wakechan = 0;
+ splx(x);
+ for(i = 0; wakechan; i++){
+ if(wakechan & 1)
+ wakeup(&ctlr->chanintr[i]);
+ wakechan >>= 1;
+ }
+}
+
+static void
+epopen(Ep *ep)
+{
+ ddprint("usbotg: epopen ep%d.%d ttype %d\n",
+ ep->dev->nb, ep->nb, ep->ttype);
+ switch(ep->ttype){
+ case Tnone:
+ error(Enotconfig);
+ case Tintr:
+ assert(ep->pollival > 0);
+ /* fall through */
+ case Tbulk:
+ if(ep->toggle[Read] == 0)
+ ep->toggle[Read] = DATA0;
+ if(ep->toggle[Write] == 0)
+ ep->toggle[Write] = DATA0;
+ break;
+ }
+ ep->aux = malloc(sizeof(Epio));
+ if(ep->aux == nil)
+ error(Enomem);
+}
+
+static void
+epclose(Ep *ep)
+{
+ ddprint("usbotg: epclose ep%d.%d ttype %d\n",
+ ep->dev->nb, ep->nb, ep->ttype);
+ switch(ep->ttype){
+ case Tctl:
+ freeb(((Epio*)ep->aux)->cb);
+ /* fall through */
+ default:
+ free(ep->aux);
+ break;
+ }
+}
+
+static long
+epread(Ep *ep, void *a, long n)
+{
+ Epio *epio;
+ Block *b;
+ uchar *p;
+ ulong elapsed;
+ long nr;
+
+ ddprint("epread ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
+ epio = ep->aux;
+ b = nil;
+ qlock(epio);
+ if(waserror()){
+ qunlock(epio);
+ if(b)
+ freeb(b);
+ nexterror();
+ }
+ switch(ep->ttype){
+ default:
+ error(Egreg);
+ case Tctl:
+ nr = ctldata(ep, a, n);
+ qunlock(epio);
+ poperror();
+ return nr;
+ case Tintr:
+ elapsed = TK2MS(m->ticks) - epio->lastpoll;
+ if(elapsed < ep->pollival)
+ tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
+ /* fall through */
+ case Tbulk:
+ /* XXX cache madness */
+ b = allocb(ROUND(n, ep->maxpkt) + CACHELINESZ);
+ p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
+ cachedwbinvse(p, n);
+ nr = eptrans(ep, Read, p, n);
+ epio->lastpoll = TK2MS(m->ticks);
+ memmove(a, p, nr);
+ qunlock(epio);
+ freeb(b);
+ poperror();
+ return nr;
+ }
+}
+
+static long
+epwrite(Ep *ep, void *a, long n)
+{
+ Epio *epio;
+ Block *b;
+ uchar *p;
+ ulong elapsed;
+
+ ddprint("epwrite ep%d.%d %ld\n", ep->dev->nb, ep->nb, n);
+ epio = ep->aux;
+ b = nil;
+ qlock(epio);
+ if(waserror()){
+ qunlock(epio);
+ if(b)
+ freeb(b);
+ nexterror();
+ }
+ switch(ep->ttype){
+ default:
+ error(Egreg);
+ case Tintr:
+ elapsed = TK2MS(m->ticks) - epio->lastpoll;
+ if(elapsed < ep->pollival)
+ tsleep(&up->sleep, return0, 0, ep->pollival - elapsed);
+ /* fall through */
+ case Tctl:
+ case Tbulk:
+ /* XXX cache madness */
+ b = allocb(n + CACHELINESZ);
+ p = (uchar*)ROUND((uintptr)b->base, CACHELINESZ);
+ memmove(p, a, n);
+ cachedwbse(p, n);
+ if(ep->ttype == Tctl)
+ n = ctltrans(ep, p, n);
+ else{
+ n = eptrans(ep, Write, p, n);
+ epio->lastpoll = TK2MS(m->ticks);
+ }
+ qunlock(epio);
+ freeb(b);
+ poperror();
+ return n;
+ }
+}
+
+static char*
+seprintep(char *s, char*, Ep*)
+{
+ return s;
+}
+
+static int
+portenable(Hci *hp, int port, int on)
+{
+ Ctlr *ctlr;
+ Dwcregs *r;
+
+ assert(port == 1);
+ ctlr = hp->aux;
+ r = ctlr->regs;
+ dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
+ if(!on)
+ r->hport0 = Prtpwr | Prtena;
+ tsleep(&up->sleep, return0, 0, Enabledelay);
+ dprint("usbotg enable=%d; sts %#x\n", on, r->hport0);
+ return 0;
+}
+
+static int
+portreset(Hci *hp, int port, int on)
+{
+ Ctlr *ctlr;
+ Dwcregs *r;
+ int b, s;
+
+ assert(port == 1);
+ ctlr = hp->aux;
+ r = ctlr->regs;
+ dprint("usbotg reset=%d; sts %#x\n", on, r->hport0);
+ if(!on)
+ return 0;
+ r->hport0 = Prtpwr | Prtrst;
+ tsleep(&up->sleep, return0, 0, ResetdelayHS);
+ r->hport0 = Prtpwr;
+ tsleep(&up->sleep, return0, 0, Enabledelay);
+ s = r->hport0;
+ b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
+ if(b != 0)
+ r->hport0 = Prtpwr | b;
+ dprint("usbotg reset=%d; sts %#x\n", on, s);
+ if((s & Prtena) == 0)
+ print("usbotg: host port not enabled after reset");
+ return 0;
+}
+
+static int
+portstatus(Hci *hp, int port)
+{
+ Ctlr *ctlr;
+ Dwcregs *r;
+ int b, s;
+
+ assert(port == 1);
+ ctlr = hp->aux;
+ r = ctlr->regs;
+ s = r->hport0;
+ b = s & (Prtconndet|Prtenchng|Prtovrcurrchng);
+ if(b != 0)
+ r->hport0 = Prtpwr | b;
+ b = 0;
+ if(s & Prtconnsts)
+ b |= HPpresent;
+ if(s & Prtconndet)
+ b |= HPstatuschg;
+ if(s & Prtena)
+ b |= HPenable;
+ if(s & Prtenchng)
+ b |= HPchange;
+ if(s & Prtovrcurract)
+ b |= HPovercurrent;
+ if(s & Prtsusp)
+ b |= HPsuspend;
+ if(s & Prtrst)
+ b |= HPreset;
+ if(s & Prtpwr)
+ b |= HPpower;
+ switch(s & Prtspd){
+ case HIGHSPEED:
+ b |= HPhigh;
+ break;
+ case LOWSPEED:
+ b |= HPslow;
+ break;
+ }
+ return b;
+}
+
+static void
+shutdown(Hci*)
+{
+}
+
+static void
+setdebug(Hci*, int d)
+{
+ debug = d;
+}
+
+static int
+reset(Hci *hp)
+{
+ Ctlr *ctlr;
+ uint id;
+
+ ctlr = &dwc;
+ if(ctlr->regs != nil)
+ return -1;
+ ctlr->regs = (Dwcregs*)USBREGS;
+ id = ctlr->regs->gsnpsid;
+ if((id>>16) != ('O'<<8 | 'T'))
+ return -1;
+ dprint("usbotg: rev %d.%3.3x\n", (id>>12)&0xF, id&0xFFF);
+
+ intrenable(IRQtimerArm, irqintr, ctlr, 0, "dwc");
+
+ hp->aux = ctlr;
+ hp->port = 0;
+ hp->irq = IRQusb;
+ hp->tbdf = 0;
+ hp->nports = 1;
+ hp->highspeed = 1;
+
+ hp->init = init;
+ hp->dump = dump;
+ hp->interrupt = fiqintr;
+ hp->epopen = epopen;
+ hp->epclose = epclose;
+ hp->epread = epread;
+ hp->epwrite = epwrite;
+ hp->seprintep = seprintep;
+ hp->portenable = portenable;
+ hp->portreset = portreset;
+ hp->portstatus = portstatus;
+ hp->shutdown = shutdown;
+ hp->debug = setdebug;
+ hp->type = "dwcotg";
+
+ intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, "usbdwcotg");
+
+ return 0;
+}
+
+void
+usbdwclink(void)
+{
+ addhcitype("dwcotg", reset);
+}