184860c5dSDavid du Colombier /*
284860c5dSDavid du Colombier * OMAP3-specific code for
384860c5dSDavid du Colombier * USB Enhanced Host Controller Interface (EHCI) driver
484860c5dSDavid du Colombier * High speed USB 2.0.
584860c5dSDavid du Colombier */
684860c5dSDavid du Colombier
784860c5dSDavid du Colombier #include "u.h"
884860c5dSDavid du Colombier #include "../port/lib.h"
984860c5dSDavid du Colombier #include "mem.h"
1084860c5dSDavid du Colombier #include "dat.h"
1184860c5dSDavid du Colombier #include "fns.h"
1284860c5dSDavid du Colombier #include "io.h"
1384860c5dSDavid du Colombier #include "../port/error.h"
1484860c5dSDavid du Colombier #include "../port/usb.h"
15*5bdeb9a2SDavid du Colombier #include "../port/portusbehci.h"
1684860c5dSDavid du Colombier #include "usbehci.h"
1784860c5dSDavid du Colombier
1884860c5dSDavid du Colombier static Ctlr* ctlrs[Nhcis];
1984860c5dSDavid du Colombier
2084860c5dSDavid du Colombier static void
ehcireset(Ctlr * ctlr)2184860c5dSDavid du Colombier ehcireset(Ctlr *ctlr)
2284860c5dSDavid du Colombier {
2384860c5dSDavid du Colombier Eopio *opio;
2484860c5dSDavid du Colombier int i;
2584860c5dSDavid du Colombier
2684860c5dSDavid du Colombier ilock(ctlr);
2784860c5dSDavid du Colombier dprint("ehci %#p reset\n", ctlr->capio);
2884860c5dSDavid du Colombier opio = ctlr->opio;
2984860c5dSDavid du Colombier
3084860c5dSDavid du Colombier /*
3184860c5dSDavid du Colombier * Turn off legacy mode. Some controllers won't
3284860c5dSDavid du Colombier * interrupt us as expected otherwise.
3384860c5dSDavid du Colombier */
3484860c5dSDavid du Colombier ehcirun(ctlr, 0);
3584860c5dSDavid du Colombier
3684860c5dSDavid du Colombier /* clear high 32 bits of address signals if it's 64 bits capable.
3784860c5dSDavid du Colombier * This is probably not needed but it does not hurt and others do it.
3884860c5dSDavid du Colombier */
3984860c5dSDavid du Colombier if((ctlr->capio->capparms & C64) != 0){
4084860c5dSDavid du Colombier dprint("ehci: 64 bits\n");
4184860c5dSDavid du Colombier opio->seg = 0;
4284860c5dSDavid du Colombier }
4384860c5dSDavid du Colombier
4484860c5dSDavid du Colombier if(ehcidebugcapio != ctlr->capio){
4584860c5dSDavid du Colombier opio->cmd |= Chcreset; /* controller reset */
4684860c5dSDavid du Colombier coherence();
4784860c5dSDavid du Colombier for(i = 0; i < 100; i++){
4884860c5dSDavid du Colombier if((opio->cmd & Chcreset) == 0)
4984860c5dSDavid du Colombier break;
5084860c5dSDavid du Colombier delay(1);
5184860c5dSDavid du Colombier }
5284860c5dSDavid du Colombier if(i == 100)
5384860c5dSDavid du Colombier print("ehci %#p controller reset timed out\n", ctlr->capio);
5484860c5dSDavid du Colombier }
5584860c5dSDavid du Colombier
5684860c5dSDavid du Colombier /* requesting more interrupts per µframe may miss interrupts */
57*5bdeb9a2SDavid du Colombier opio->cmd &= ~Citcmask;
58*5bdeb9a2SDavid du Colombier opio->cmd |= 1 << Citcshift; /* max of 1 intr. per 125 µs */
5984860c5dSDavid du Colombier coherence();
6084860c5dSDavid du Colombier switch(opio->cmd & Cflsmask){
6184860c5dSDavid du Colombier case Cfls1024:
6284860c5dSDavid du Colombier ctlr->nframes = 1024;
6384860c5dSDavid du Colombier break;
6484860c5dSDavid du Colombier case Cfls512:
6584860c5dSDavid du Colombier ctlr->nframes = 512;
6684860c5dSDavid du Colombier break;
6784860c5dSDavid du Colombier case Cfls256:
6884860c5dSDavid du Colombier ctlr->nframes = 256;
6984860c5dSDavid du Colombier break;
7084860c5dSDavid du Colombier default:
7184860c5dSDavid du Colombier panic("ehci: unknown fls %ld", opio->cmd & Cflsmask);
7284860c5dSDavid du Colombier }
7384860c5dSDavid du Colombier coherence();
7484860c5dSDavid du Colombier dprint("ehci: %d frames\n", ctlr->nframes);
7584860c5dSDavid du Colombier iunlock(ctlr);
7684860c5dSDavid du Colombier }
7784860c5dSDavid du Colombier
7884860c5dSDavid du Colombier static void
setdebug(Hci *,int d)7984860c5dSDavid du Colombier setdebug(Hci*, int d)
8084860c5dSDavid du Colombier {
8184860c5dSDavid du Colombier ehcidebug = d;
8284860c5dSDavid du Colombier }
8384860c5dSDavid du Colombier
8484860c5dSDavid du Colombier static void
shutdown(Hci * hp)8584860c5dSDavid du Colombier shutdown(Hci *hp)
8684860c5dSDavid du Colombier {
8784860c5dSDavid du Colombier int i;
8884860c5dSDavid du Colombier Ctlr *ctlr;
8984860c5dSDavid du Colombier Eopio *opio;
9084860c5dSDavid du Colombier
9184860c5dSDavid du Colombier ctlr = hp->aux;
9284860c5dSDavid du Colombier ilock(ctlr);
9384860c5dSDavid du Colombier opio = ctlr->opio;
9484860c5dSDavid du Colombier opio->cmd |= Chcreset; /* controller reset */
9584860c5dSDavid du Colombier coherence();
9684860c5dSDavid du Colombier for(i = 0; i < 100; i++){
9784860c5dSDavid du Colombier if((opio->cmd & Chcreset) == 0)
9884860c5dSDavid du Colombier break;
9984860c5dSDavid du Colombier delay(1);
10084860c5dSDavid du Colombier }
10184860c5dSDavid du Colombier if(i >= 100)
10284860c5dSDavid du Colombier print("ehci %#p controller reset timed out\n", ctlr->capio);
10384860c5dSDavid du Colombier delay(100);
10484860c5dSDavid du Colombier ehcirun(ctlr, 0);
10584860c5dSDavid du Colombier opio->frbase = 0;
10684860c5dSDavid du Colombier coherence();
10784860c5dSDavid du Colombier iunlock(ctlr);
10884860c5dSDavid du Colombier }
10984860c5dSDavid du Colombier
11084860c5dSDavid du Colombier /*
11184860c5dSDavid du Colombier * omap3-specific ehci code
11284860c5dSDavid du Colombier */
11384860c5dSDavid du Colombier
11484860c5dSDavid du Colombier enum {
11584860c5dSDavid du Colombier /* opio->insn[5] bits */
11684860c5dSDavid du Colombier Control = 1<<31, /* set to start access, cleared when done */
11784860c5dSDavid du Colombier Write = 2<<22,
11884860c5dSDavid du Colombier Read = 3<<22,
11984860c5dSDavid du Colombier Portsh = 24,
12084860c5dSDavid du Colombier Regaddrsh = 16, /* 0x2f means use extended reg addr */
12184860c5dSDavid du Colombier Eregaddrsh = 8,
12284860c5dSDavid du Colombier
12384860c5dSDavid du Colombier /* phy reg addresses */
12484860c5dSDavid du Colombier Funcctlreg = 4,
12584860c5dSDavid du Colombier Ifcctlreg = 7,
12684860c5dSDavid du Colombier
12784860c5dSDavid du Colombier Phystppullupoff = 0x90, /* on is 0x10 */
12884860c5dSDavid du Colombier
12984860c5dSDavid du Colombier Phyrstport2 = 147, /* gpio # */
13084860c5dSDavid du Colombier
13184860c5dSDavid du Colombier };
13284860c5dSDavid du Colombier
13384860c5dSDavid du Colombier static void
wrulpi(Eopio * opio,int port,int reg,uchar data)13484860c5dSDavid du Colombier wrulpi(Eopio *opio, int port, int reg, uchar data)
13584860c5dSDavid du Colombier {
13684860c5dSDavid du Colombier opio->insn[5] = Control | port << Portsh | Write | reg << Regaddrsh |
13784860c5dSDavid du Colombier data;
13884860c5dSDavid du Colombier coherence();
13984860c5dSDavid du Colombier /*
14084860c5dSDavid du Colombier * this seems contrary to the skimpy documentation in the manual
14184860c5dSDavid du Colombier * but inverting the test hangs forever.
14284860c5dSDavid du Colombier */
14384860c5dSDavid du Colombier while (!(opio->insn[5] & Control))
14484860c5dSDavid du Colombier ;
14584860c5dSDavid du Colombier }
14684860c5dSDavid du Colombier
14784860c5dSDavid du Colombier static int
reset(Hci * hp)14884860c5dSDavid du Colombier reset(Hci *hp)
14984860c5dSDavid du Colombier {
15084860c5dSDavid du Colombier Ctlr *ctlr;
15184860c5dSDavid du Colombier Ecapio *capio;
15284860c5dSDavid du Colombier Eopio *opio;
15384860c5dSDavid du Colombier Uhh *uhh;
15484860c5dSDavid du Colombier static int beenhere;
15584860c5dSDavid du Colombier
15684860c5dSDavid du Colombier if (beenhere)
15784860c5dSDavid du Colombier return -1;
15884860c5dSDavid du Colombier beenhere = 1;
15984860c5dSDavid du Colombier
16084860c5dSDavid du Colombier if(getconf("*nousbehci") != nil || probeaddr(PHYSEHCI) < 0)
16184860c5dSDavid du Colombier return -1;
16284860c5dSDavid du Colombier
163305b51b8SDavid du Colombier ctlr = smalloc(sizeof(Ctlr));
16484860c5dSDavid du Colombier /*
16584860c5dSDavid du Colombier * don't bother with vmap; i/o space is all mapped anyway,
16684860c5dSDavid du Colombier * and a size less than 1MB will blow an assertion in mmukmap.
16784860c5dSDavid du Colombier */
16884860c5dSDavid du Colombier ctlr->capio = capio = (Ecapio *)PHYSEHCI;
16984860c5dSDavid du Colombier ctlr->opio = opio = (Eopio*)((uintptr)capio + (capio->cap & 0xff));
17084860c5dSDavid du Colombier
17184860c5dSDavid du Colombier hp->aux = ctlr;
17284860c5dSDavid du Colombier hp->port = (uintptr)ctlr->capio;
17384860c5dSDavid du Colombier hp->irq = 77;
17484860c5dSDavid du Colombier hp->nports = capio->parms & Cnports;
17584860c5dSDavid du Colombier
17684860c5dSDavid du Colombier ddprint("echi: %s, ncc %lud npcc %lud\n",
17784860c5dSDavid du Colombier capio->parms & 0x10000 ? "leds" : "no leds",
17884860c5dSDavid du Colombier (capio->parms >> 12) & 0xf, (capio->parms >> 8) & 0xf);
17984860c5dSDavid du Colombier ddprint("ehci: routing %s, %sport power ctl, %d ports\n",
18084860c5dSDavid du Colombier capio->parms & 0x40 ? "explicit" : "automatic",
18184860c5dSDavid du Colombier capio->parms & 0x10 ? "" : "no ", hp->nports);
18284860c5dSDavid du Colombier
18384860c5dSDavid du Colombier ehcireset(ctlr);
18484860c5dSDavid du Colombier ehcimeminit(ctlr);
18584860c5dSDavid du Colombier
18684860c5dSDavid du Colombier /* omap35-specific set up */
18784860c5dSDavid du Colombier /* bit 5 `must be set to 1 for proper behavior', spruf98d §23.2.6.7.17 */
18884860c5dSDavid du Colombier opio->insn[4] |= 1<<5;
18984860c5dSDavid du Colombier coherence();
19084860c5dSDavid du Colombier
19184860c5dSDavid du Colombier /* insn[5] is for both utmi and ulpi, depending on hostconfig mode */
19284860c5dSDavid du Colombier uhh = (Uhh *)PHYSUHH;
19384860c5dSDavid du Colombier if (uhh->hostconfig & P1ulpi_bypass) { /* utmi port 1 active */
19484860c5dSDavid du Colombier /* not doing this */
19584860c5dSDavid du Colombier iprint("usbehci: bypassing ulpi on port 1!\n");
19684860c5dSDavid du Colombier opio->insn[5] &= ~(MASK(4) << 13);
19784860c5dSDavid du Colombier opio->insn[5] |= 1 << 13; /* select port 1 */
19884860c5dSDavid du Colombier coherence();
19984860c5dSDavid du Colombier } else { /* ulpi port 1 active */
20084860c5dSDavid du Colombier /* TODO may need to reset gpio port2 here */
20184860c5dSDavid du Colombier
20284860c5dSDavid du Colombier /* disable integrated stp pull-up resistor */
20384860c5dSDavid du Colombier wrulpi(opio, 1, Ifcctlreg, Phystppullupoff);
20484860c5dSDavid du Colombier
20584860c5dSDavid du Colombier /* force phy to `high-speed' */
20684860c5dSDavid du Colombier wrulpi(opio, 1, Funcctlreg, 0x40);
20784860c5dSDavid du Colombier }
20884860c5dSDavid du Colombier
20984860c5dSDavid du Colombier /*
21084860c5dSDavid du Colombier * Linkage to the generic HCI driver.
21184860c5dSDavid du Colombier */
21284860c5dSDavid du Colombier ehcilinkage(hp);
21384860c5dSDavid du Colombier hp->shutdown = shutdown;
21484860c5dSDavid du Colombier hp->debug = setdebug;
21584860c5dSDavid du Colombier
21684860c5dSDavid du Colombier intrenable(78, hp->interrupt, hp, UNKNOWN, "usbtll");
21784860c5dSDavid du Colombier intrenable(92, hp->interrupt, hp, UNKNOWN, "usb otg");
21884860c5dSDavid du Colombier intrenable(93, hp->interrupt, hp, UNKNOWN, "usb otg dma");
21984860c5dSDavid du Colombier return 0;
22084860c5dSDavid du Colombier }
22184860c5dSDavid du Colombier
22284860c5dSDavid du Colombier void
usbehcilink(void)22384860c5dSDavid du Colombier usbehcilink(void)
22484860c5dSDavid du Colombier {
22584860c5dSDavid du Colombier addhcitype("ehci", reset);
22684860c5dSDavid du Colombier }
227