177 lines
3 KiB
C
177 lines
3 KiB
C
#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 "usbehci.h"
|
|
|
|
enum {
|
|
USBMODE = 0x1A8/4,
|
|
USBHOST = 3,
|
|
OTGSC = 0x1A4/4,
|
|
ULPI = 0x170/4,
|
|
};
|
|
|
|
static Ctlr ctlrs[3] = {
|
|
{
|
|
.base = USB0_BASE,
|
|
.irq = USB0IRQ,
|
|
},
|
|
{
|
|
.base = USB1_BASE,
|
|
.irq = USB1IRQ,
|
|
},
|
|
};
|
|
|
|
static void
|
|
ehcireset(Ctlr *ctlr)
|
|
{
|
|
int i;
|
|
Eopio *opio;
|
|
|
|
ilock(ctlr);
|
|
opio = ctlr->opio;
|
|
ehcirun(ctlr, 0);
|
|
opio->cmd |= Chcreset;
|
|
for(i = 0; i < 100; i++){
|
|
if((opio->cmd & Chcreset) == 0)
|
|
break;
|
|
delay(1);
|
|
}
|
|
if(i == 100)
|
|
print("ehci %#p controller reset timed out\n", ctlr->base);
|
|
opio->cmd |= Citc1;
|
|
switch(opio->cmd & Cflsmask){
|
|
case Cfls1024:
|
|
ctlr->nframes = 1024;
|
|
break;
|
|
case Cfls512:
|
|
ctlr->nframes = 512;
|
|
break;
|
|
case Cfls256:
|
|
ctlr->nframes = 256;
|
|
break;
|
|
default:
|
|
panic("ehci: unknown fls %ld", opio->cmd & Cflsmask);
|
|
}
|
|
dprint("ehci: %d frames\n", ctlr->nframes);
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
enum {
|
|
Cls = 64,
|
|
};
|
|
|
|
/* descriptors need to be allocated in uncached memory */
|
|
static void*
|
|
tdalloc(ulong size, int, ulong)
|
|
{
|
|
return ucalloc(size);
|
|
}
|
|
|
|
static void*
|
|
dmaalloc(ulong len)
|
|
{
|
|
return mallocalign(ROUND(len, Cls), Cls, 0, 0);
|
|
}
|
|
static void
|
|
dmafree(void *data)
|
|
{
|
|
free(data);
|
|
}
|
|
|
|
static void
|
|
dmaflush(int clean, void *data, ulong len)
|
|
{
|
|
uintptr va, pa;
|
|
|
|
va = (uintptr)data & ~(Cls-1);
|
|
pa = PADDR(va);
|
|
len = ROUND(len, Cls);
|
|
if(clean){
|
|
/* flush cache before write */
|
|
cleandse((uchar*)va, (uchar*)va+len);
|
|
clean2pa(pa, pa+len);
|
|
} else {
|
|
/* invalidate cache before read */
|
|
invaldse((uchar*)va, (uchar*)va+len);
|
|
inval2pa(pa, pa+len);
|
|
}
|
|
}
|
|
|
|
static int (*ehciportstatus)(Hci*,int);
|
|
|
|
static int
|
|
portstatus(Hci *hp, int port)
|
|
{
|
|
Ctlr *ctlr;
|
|
Eopio *opio;
|
|
int r, sts;
|
|
|
|
ctlr = hp->aux;
|
|
opio = ctlr->opio;
|
|
r = (*ehciportstatus)(hp, port);
|
|
if(r & HPpresent){
|
|
sts = opio->portsc[port-1];
|
|
r &= ~(HPhigh|HPslow);
|
|
if(sts & (1<<9))
|
|
r |= HPhigh;
|
|
else if(sts & 1<<26)
|
|
r |= HPslow;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static int
|
|
reset(Hci *hp)
|
|
{
|
|
static Lock resetlck;
|
|
Ctlr *ctlr;
|
|
|
|
ilock(&resetlck);
|
|
for(ctlr = ctlrs; ctlr->base != 0; ctlr++)
|
|
if(!ctlr->active && (hp->port == 0 || hp->port == ctlr->base)){
|
|
ctlr->active = 1;
|
|
break;
|
|
}
|
|
iunlock(&resetlck);
|
|
if(ctlr->base == 0)
|
|
return -1;
|
|
hp->port = ctlr->base;
|
|
hp->irq = ctlr->irq;
|
|
hp->aux = ctlr;
|
|
|
|
ctlr->r = vmap(ctlr->base, 0x1F0);
|
|
ctlr->opio = (Eopio *) ((uchar *) ctlr->r + 0x140);
|
|
ctlr->capio = (void *) ctlr->base;
|
|
hp->nports = 1;
|
|
|
|
ctlr->tdalloc = tdalloc;
|
|
ctlr->dmaalloc = dmaalloc;
|
|
ctlr->dmafree = dmafree;
|
|
ctlr->dmaflush = dmaflush;
|
|
|
|
ehcireset(ctlr);
|
|
ctlr->r[USBMODE] |= USBHOST;
|
|
ctlr->r[ULPI] = 1<<30 | 1<<29 | 0x0B << 16 | 3<<5;
|
|
ehcimeminit(ctlr);
|
|
ehcilinkage(hp);
|
|
|
|
/* hook portstatus */
|
|
ehciportstatus = hp->portstatus;
|
|
hp->portstatus = portstatus;
|
|
|
|
if(hp->interrupt != nil)
|
|
intrenable(hp->irq, hp->interrupt, hp, LEVEL, hp->type);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
usbehcilink(void)
|
|
{
|
|
// ehcidebug = 2;
|
|
addhcitype("ehci", reset);
|
|
}
|