230 lines
4.8 KiB
C
230 lines
4.8 KiB
C
/*
|
|
* OMAP3-specific code for
|
|
* USB Enhanced Host Controller Interface (EHCI) driver
|
|
* High speed USB 2.0.
|
|
*/
|
|
|
|
#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"
|
|
|
|
static Ctlr* ctlrs[Nhcis];
|
|
|
|
static void
|
|
ehcireset(Ctlr *ctlr)
|
|
{
|
|
Eopio *opio;
|
|
int i;
|
|
|
|
ilock(ctlr);
|
|
dprint("ehci %#p reset\n", ctlr->capio);
|
|
opio = ctlr->opio;
|
|
|
|
/*
|
|
* Turn off legacy mode. Some controllers won't
|
|
* interrupt us as expected otherwise.
|
|
*/
|
|
ehcirun(ctlr, 0);
|
|
|
|
/* clear high 32 bits of address signals if it's 64 bits capable.
|
|
* This is probably not needed but it does not hurt and others do it.
|
|
*/
|
|
if((ctlr->capio->capparms & C64) != 0){
|
|
dprint("ehci: 64 bits\n");
|
|
opio->seg = 0;
|
|
}
|
|
|
|
opio->cmd |= Chcreset; /* controller reset */
|
|
coherence();
|
|
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->capio);
|
|
|
|
/* requesting more interrupts per µframe may miss interrupts */
|
|
opio->cmd |= 0x10000; /* 1 intr. per ms */
|
|
coherence();
|
|
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);
|
|
}
|
|
coherence();
|
|
dprint("ehci: %d frames\n", ctlr->nframes);
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
static void
|
|
setdebug(Hci*, int d)
|
|
{
|
|
ehcidebug = d;
|
|
}
|
|
|
|
static void
|
|
shutdown(Hci *hp)
|
|
{
|
|
int i;
|
|
Ctlr *ctlr;
|
|
Eopio *opio;
|
|
|
|
ctlr = hp->aux;
|
|
ilock(ctlr);
|
|
opio = ctlr->opio;
|
|
opio->cmd |= Chcreset; /* controller reset */
|
|
coherence();
|
|
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->capio);
|
|
delay(100);
|
|
ehcirun(ctlr, 0);
|
|
opio->frbase = 0;
|
|
coherence();
|
|
iunlock(ctlr);
|
|
}
|
|
|
|
/*
|
|
* omap3-specific ehci code
|
|
*/
|
|
|
|
enum {
|
|
/* opio->insn[5] bits */
|
|
Control = 1<<31, /* set to start access, cleared when done */
|
|
Write = 2<<22,
|
|
Read = 3<<22,
|
|
Portsh = 24,
|
|
Regaddrsh = 16, /* 0x2f means use extended reg addr */
|
|
Eregaddrsh = 8,
|
|
|
|
/* phy reg addresses */
|
|
Funcctlreg = 4,
|
|
Ifcctlreg = 7,
|
|
|
|
Phystppullupoff = 0x90, /* on is 0x10 */
|
|
|
|
Phyrstport2 = 147, /* gpio # */
|
|
|
|
};
|
|
|
|
static void
|
|
wrulpi(Eopio *opio, int port, int reg, uchar data)
|
|
{
|
|
opio->insn[5] = Control | port << Portsh | Write | reg << Regaddrsh |
|
|
data;
|
|
coherence();
|
|
/*
|
|
* this seems contrary to the skimpy documentation in the manual
|
|
* but inverting the test hangs forever.
|
|
*/
|
|
while (!(opio->insn[5] & Control))
|
|
;
|
|
}
|
|
|
|
static int
|
|
reset(Hci *hp)
|
|
{
|
|
Ctlr *ctlr;
|
|
Ecapio *capio;
|
|
Eopio *opio;
|
|
Uhh *uhh;
|
|
static int beenhere;
|
|
|
|
if (beenhere)
|
|
return -1;
|
|
beenhere = 1;
|
|
|
|
if(getconf("*nousbehci") != nil || probeaddr(PHYSEHCI) < 0)
|
|
return -1;
|
|
|
|
ctlr = smalloc(sizeof(Ctlr));
|
|
/*
|
|
* don't bother with vmap; i/o space is all mapped anyway,
|
|
* and a size less than 1MB will blow an assertion in mmukmap.
|
|
*/
|
|
ctlr->capio = capio = (Ecapio *)PHYSEHCI;
|
|
ctlr->opio = opio = (Eopio*)((uintptr)capio + (capio->cap & 0xff));
|
|
|
|
hp->aux = ctlr;
|
|
hp->port = (uintptr)ctlr->capio;
|
|
hp->irq = 77;
|
|
hp->nports = capio->parms & Cnports;
|
|
|
|
ddprint("echi: %s, ncc %lud npcc %lud\n",
|
|
capio->parms & 0x10000 ? "leds" : "no leds",
|
|
(capio->parms >> 12) & 0xf, (capio->parms >> 8) & 0xf);
|
|
ddprint("ehci: routing %s, %sport power ctl, %d ports\n",
|
|
capio->parms & 0x40 ? "explicit" : "automatic",
|
|
capio->parms & 0x10 ? "" : "no ", hp->nports);
|
|
|
|
ctlr->tdalloc = ucallocalign;
|
|
ctlr->dmaalloc = ucalloc;
|
|
ctlr->dmafree = ucfree;
|
|
|
|
ehcireset(ctlr);
|
|
ehcimeminit(ctlr);
|
|
|
|
/* omap35-specific set up */
|
|
/* bit 5 `must be set to 1 for proper behavior', spruf98d §23.2.6.7.17 */
|
|
opio->insn[4] |= 1<<5;
|
|
coherence();
|
|
|
|
/* insn[5] is for both utmi and ulpi, depending on hostconfig mode */
|
|
uhh = (Uhh *)PHYSUHH;
|
|
if (uhh->hostconfig & P1ulpi_bypass) { /* utmi port 1 active */
|
|
/* not doing this */
|
|
iprint("usbehci: bypassing ulpi on port 1!\n");
|
|
opio->insn[5] &= ~(MASK(4) << 13);
|
|
opio->insn[5] |= 1 << 13; /* select port 1 */
|
|
coherence();
|
|
} else { /* ulpi port 1 active */
|
|
/* TODO may need to reset gpio port2 here */
|
|
|
|
/* disable integrated stp pull-up resistor */
|
|
wrulpi(opio, 1, Ifcctlreg, Phystppullupoff);
|
|
|
|
/* force phy to `high-speed' */
|
|
wrulpi(opio, 1, Funcctlreg, 0x40);
|
|
}
|
|
|
|
/*
|
|
* Linkage to the generic HCI driver.
|
|
*/
|
|
ehcilinkage(hp);
|
|
hp->shutdown = shutdown;
|
|
hp->debug = setdebug;
|
|
|
|
intrenable(78, hp->interrupt, hp, UNKNOWN, "usbtll");
|
|
intrenable(92, hp->interrupt, hp, UNKNOWN, "usb otg");
|
|
intrenable(93, hp->interrupt, hp, UNKNOWN, "usb otg dma");
|
|
|
|
intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, hp->type);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
usbehcilink(void)
|
|
{
|
|
addhcitype("ehci", reset);
|
|
}
|