xref: /plan9/sys/src/9/omap/usbehciomap.c (revision 5bdeb9a20ca533546de47a1bc407975caf1023f5)
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