xref: /plan9/sys/src/9/pc/usbehcipc.c (revision 5bdeb9a20ca533546de47a1bc407975caf1023f5)
184860c5dSDavid du Colombier /*
284860c5dSDavid du Colombier  * PC-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];
192a782499SDavid du Colombier static int maxehci = Nhcis;
2084860c5dSDavid du Colombier 
2184860c5dSDavid du Colombier /* Isn't this cap list search in a helper function? */
2284860c5dSDavid du Colombier static void
getehci(Ctlr * ctlr)2384860c5dSDavid du Colombier getehci(Ctlr* ctlr)
2484860c5dSDavid du Colombier {
2584860c5dSDavid du Colombier 	int i, ptr, cap, sem;
2684860c5dSDavid du Colombier 
2784860c5dSDavid du Colombier 	ptr = (ctlr->capio->capparms >> Ceecpshift) & Ceecpmask;
2884860c5dSDavid du Colombier 	for(; ptr != 0; ptr = pcicfgr8(ctlr->pcidev, ptr+1)){
2984860c5dSDavid du Colombier 		if(ptr < 0x40 || (ptr & ~0xFC))
3084860c5dSDavid du Colombier 			break;
3184860c5dSDavid du Colombier 		cap = pcicfgr8(ctlr->pcidev, ptr);
3284860c5dSDavid du Colombier 		if(cap != Clegacy)
3384860c5dSDavid du Colombier 			continue;
3484860c5dSDavid du Colombier 		sem = pcicfgr8(ctlr->pcidev, ptr+CLbiossem);
3584860c5dSDavid du Colombier 		if(sem == 0)
3684860c5dSDavid du Colombier 			continue;
3784860c5dSDavid du Colombier 		pcicfgw8(ctlr->pcidev, ptr+CLossem, 1);
3884860c5dSDavid du Colombier 		for(i = 0; i < 100; i++){
3984860c5dSDavid du Colombier 			if(pcicfgr8(ctlr->pcidev, ptr+CLbiossem) == 0)
4084860c5dSDavid du Colombier 				break;
4184860c5dSDavid du Colombier 			delay(10);
4284860c5dSDavid du Colombier 		}
4384860c5dSDavid du Colombier 		if(i == 100)
4484860c5dSDavid du Colombier 			dprint("ehci %#p: bios timed out\n", ctlr->capio);
4584860c5dSDavid du Colombier 		pcicfgw32(ctlr->pcidev, ptr+CLcontrol, 0);	/* no SMIs */
4684860c5dSDavid du Colombier 		ctlr->opio->config = 0;
4784860c5dSDavid du Colombier 		coherence();
4884860c5dSDavid du Colombier 		return;
4984860c5dSDavid du Colombier 	}
5084860c5dSDavid du Colombier }
5184860c5dSDavid du Colombier 
5284860c5dSDavid du Colombier static void
ehcireset(Ctlr * ctlr)5384860c5dSDavid du Colombier ehcireset(Ctlr *ctlr)
5484860c5dSDavid du Colombier {
5584860c5dSDavid du Colombier 	Eopio *opio;
5684860c5dSDavid du Colombier 	int i;
5784860c5dSDavid du Colombier 
5884860c5dSDavid du Colombier 	ilock(ctlr);
5984860c5dSDavid du Colombier 	dprint("ehci %#p reset\n", ctlr->capio);
6084860c5dSDavid du Colombier 	opio = ctlr->opio;
6184860c5dSDavid du Colombier 
6284860c5dSDavid du Colombier 	/*
6384860c5dSDavid du Colombier 	 * Turn off legacy mode. Some controllers won't
6484860c5dSDavid du Colombier 	 * interrupt us as expected otherwise.
6584860c5dSDavid du Colombier 	 */
6684860c5dSDavid du Colombier 	ehcirun(ctlr, 0);
6784860c5dSDavid du Colombier 	pcicfgw16(ctlr->pcidev, 0xc0, 0x2000);
6884860c5dSDavid du Colombier 
6984860c5dSDavid du Colombier 	/*
7084860c5dSDavid du Colombier 	 * reclaim from bios
7184860c5dSDavid du Colombier 	 */
7284860c5dSDavid du Colombier 	getehci(ctlr);
7384860c5dSDavid du Colombier 
7484860c5dSDavid du Colombier 	/* clear high 32 bits of address signals if it's 64 bits capable.
7584860c5dSDavid du Colombier 	 * This is probably not needed but it does not hurt and others do it.
7684860c5dSDavid du Colombier 	 */
7784860c5dSDavid du Colombier 	if((ctlr->capio->capparms & C64) != 0){
7884860c5dSDavid du Colombier 		dprint("ehci: 64 bits\n");
7984860c5dSDavid du Colombier 		opio->seg = 0;
8084860c5dSDavid du Colombier 		coherence();
8184860c5dSDavid du Colombier 	}
8284860c5dSDavid du Colombier 
8384860c5dSDavid du Colombier 	if(ehcidebugcapio != ctlr->capio){
8484860c5dSDavid du Colombier 		opio->cmd |= Chcreset;	/* controller reset */
8584860c5dSDavid du Colombier 		coherence();
8684860c5dSDavid du Colombier 		for(i = 0; i < 100; i++){
8784860c5dSDavid du Colombier 			if((opio->cmd & Chcreset) == 0)
8884860c5dSDavid du Colombier 				break;
8984860c5dSDavid du Colombier 			delay(1);
9084860c5dSDavid du Colombier 		}
9184860c5dSDavid du Colombier 		if(i == 100)
9284860c5dSDavid du Colombier 			print("ehci %#p controller reset timed out\n", ctlr->capio);
9384860c5dSDavid du Colombier 	}
9484860c5dSDavid du Colombier 
9584860c5dSDavid du Colombier 	/* requesting more interrupts per µframe may miss interrupts */
96*5bdeb9a2SDavid du Colombier 	opio->cmd &= ~Citcmask;
97*5bdeb9a2SDavid du Colombier 	opio->cmd |= 1 << Citcshift;		/* max of 1 intr. per 125 µs */
9884860c5dSDavid du Colombier 	coherence();
9984860c5dSDavid du Colombier 	switch(opio->cmd & Cflsmask){
10084860c5dSDavid du Colombier 	case Cfls1024:
10184860c5dSDavid du Colombier 		ctlr->nframes = 1024;
10284860c5dSDavid du Colombier 		break;
10384860c5dSDavid du Colombier 	case Cfls512:
10484860c5dSDavid du Colombier 		ctlr->nframes = 512;
10584860c5dSDavid du Colombier 		break;
10684860c5dSDavid du Colombier 	case Cfls256:
10784860c5dSDavid du Colombier 		ctlr->nframes = 256;
10884860c5dSDavid du Colombier 		break;
10984860c5dSDavid du Colombier 	default:
11084860c5dSDavid du Colombier 		panic("ehci: unknown fls %ld", opio->cmd & Cflsmask);
11184860c5dSDavid du Colombier 	}
11284860c5dSDavid du Colombier 	dprint("ehci: %d frames\n", ctlr->nframes);
11384860c5dSDavid du Colombier 	iunlock(ctlr);
11484860c5dSDavid du Colombier }
11584860c5dSDavid du Colombier 
11684860c5dSDavid du Colombier static void
setdebug(Hci *,int d)11784860c5dSDavid du Colombier setdebug(Hci*, int d)
11884860c5dSDavid du Colombier {
11984860c5dSDavid du Colombier 	ehcidebug = d;
12084860c5dSDavid du Colombier }
12184860c5dSDavid du Colombier 
12284860c5dSDavid du Colombier static void
shutdown(Hci * hp)12384860c5dSDavid du Colombier shutdown(Hci *hp)
12484860c5dSDavid du Colombier {
12584860c5dSDavid du Colombier 	int i;
12684860c5dSDavid du Colombier 	Ctlr *ctlr;
12784860c5dSDavid du Colombier 	Eopio *opio;
12884860c5dSDavid du Colombier 
12984860c5dSDavid du Colombier 	ctlr = hp->aux;
13084860c5dSDavid du Colombier 	ilock(ctlr);
13184860c5dSDavid du Colombier 	opio = ctlr->opio;
13284860c5dSDavid du Colombier 	opio->cmd |= Chcreset;		/* controller reset */
13384860c5dSDavid du Colombier 	coherence();
13484860c5dSDavid du Colombier 	for(i = 0; i < 100; i++){
13584860c5dSDavid du Colombier 		if((opio->cmd & Chcreset) == 0)
13684860c5dSDavid du Colombier 			break;
13784860c5dSDavid du Colombier 		delay(1);
13884860c5dSDavid du Colombier 	}
13984860c5dSDavid du Colombier 	if(i >= 100)
14084860c5dSDavid du Colombier 		print("ehci %#p controller reset timed out\n", ctlr->capio);
14184860c5dSDavid du Colombier 	delay(100);
14284860c5dSDavid du Colombier 	ehcirun(ctlr, 0);
14384860c5dSDavid du Colombier 	opio->frbase = 0;
14484860c5dSDavid du Colombier 	iunlock(ctlr);
14584860c5dSDavid du Colombier }
14684860c5dSDavid du Colombier 
14784860c5dSDavid du Colombier static void
scanpci(void)14884860c5dSDavid du Colombier scanpci(void)
14984860c5dSDavid du Colombier {
15084860c5dSDavid du Colombier 	static int already = 0;
15184860c5dSDavid du Colombier 	int i;
15284860c5dSDavid du Colombier 	ulong io;
15384860c5dSDavid du Colombier 	Ctlr *ctlr;
15484860c5dSDavid du Colombier 	Pcidev *p;
15584860c5dSDavid du Colombier 	Ecapio *capio;
15684860c5dSDavid du Colombier 
15784860c5dSDavid du Colombier 	if(already)
15884860c5dSDavid du Colombier 		return;
15984860c5dSDavid du Colombier 	already = 1;
16084860c5dSDavid du Colombier 	p = nil;
16184860c5dSDavid du Colombier 	while ((p = pcimatch(p, 0, 0)) != nil) {
16284860c5dSDavid du Colombier 		/*
16384860c5dSDavid du Colombier 		 * Find EHCI controllers (Programming Interface = 0x20).
16484860c5dSDavid du Colombier 		 */
16584860c5dSDavid du Colombier 		if(p->ccrb != Pcibcserial || p->ccru != Pciscusb)
16684860c5dSDavid du Colombier 			continue;
16784860c5dSDavid du Colombier 		switch(p->ccrp){
16884860c5dSDavid du Colombier 		case 0x20:
16984860c5dSDavid du Colombier 			io = p->mem[0].bar & ~0x0f;
17084860c5dSDavid du Colombier 			break;
17184860c5dSDavid du Colombier 		default:
17284860c5dSDavid du Colombier 			continue;
17384860c5dSDavid du Colombier 		}
1742a782499SDavid du Colombier 		if(0 && p->vid == Vintel && p->did == 0x3b34) {
1752a782499SDavid du Colombier 			print("usbehci: ignoring known bad ctlr %#ux/%#ux\n",
1762a782499SDavid du Colombier 				p->vid, p->did);
1772a782499SDavid du Colombier 			continue;
1782a782499SDavid du Colombier 		}
17984860c5dSDavid du Colombier 		if(io == 0){
18084860c5dSDavid du Colombier 			print("usbehci: %x %x: failed to map registers\n",
18184860c5dSDavid du Colombier 				p->vid, p->did);
18284860c5dSDavid du Colombier 			continue;
18384860c5dSDavid du Colombier 		}
18484860c5dSDavid du Colombier 		if(p->intl == 0xff || p->intl == 0) {
18584860c5dSDavid du Colombier 			print("usbehci: no irq assigned for port %#lux\n", io);
18684860c5dSDavid du Colombier 			continue;
18784860c5dSDavid du Colombier 		}
18884860c5dSDavid du Colombier 		dprint("usbehci: %#x %#x: port %#lux size %#x irq %d\n",
18984860c5dSDavid du Colombier 			p->vid, p->did, io, p->mem[0].size, p->intl);
19084860c5dSDavid du Colombier 
19106d03f7fSDavid du Colombier 		ctlr = malloc(sizeof(Ctlr));
19206d03f7fSDavid du Colombier 		if (ctlr == nil)
19306d03f7fSDavid du Colombier 			panic("usbehci: out of memory");
19484860c5dSDavid du Colombier 		ctlr->pcidev = p;
19584860c5dSDavid du Colombier 		capio = ctlr->capio = vmap(io, p->mem[0].size);
19684860c5dSDavid du Colombier 		ctlr->opio = (Eopio*)((uintptr)capio + (capio->cap & 0xff));
19784860c5dSDavid du Colombier 		pcisetbme(p);
19884860c5dSDavid du Colombier 		pcisetpms(p, 0);
19984860c5dSDavid du Colombier 		for(i = 0; i < Nhcis; i++)
20084860c5dSDavid du Colombier 			if(ctlrs[i] == nil){
20184860c5dSDavid du Colombier 				ctlrs[i] = ctlr;
20284860c5dSDavid du Colombier 				break;
20384860c5dSDavid du Colombier 			}
204b1c161c2SDavid du Colombier 		if(i >= Nhcis)
20584860c5dSDavid du Colombier 			print("ehci: bug: more than %d controllers\n", Nhcis);
20684860c5dSDavid du Colombier 
20784860c5dSDavid du Colombier 		/*
208cc499a30SDavid du Colombier 		 * currently, if we enable a second ehci controller on zt
209cc499a30SDavid du Colombier 		 * systems w x58m motherboard, we'll wedge solid after iunlock
210cc499a30SDavid du Colombier 		 * in init for the second one.
21184860c5dSDavid du Colombier 		 */
212cc499a30SDavid du Colombier 		if (i >= maxehci) {
213cc499a30SDavid du Colombier 			print("usbehci: ignoring controllers after first %d, "
214cc499a30SDavid du Colombier 				"at %#p\n", maxehci, io);
215cc499a30SDavid du Colombier 			ctlrs[i] = nil;
21684860c5dSDavid du Colombier 		}
21784860c5dSDavid du Colombier 	}
21884860c5dSDavid du Colombier }
21984860c5dSDavid du Colombier 
22084860c5dSDavid du Colombier static int
reset(Hci * hp)22184860c5dSDavid du Colombier reset(Hci *hp)
22284860c5dSDavid du Colombier {
22384860c5dSDavid du Colombier 	int i;
224cc499a30SDavid du Colombier 	char *s;
22584860c5dSDavid du Colombier 	Ctlr *ctlr;
22684860c5dSDavid du Colombier 	Ecapio *capio;
22784860c5dSDavid du Colombier 	Pcidev *p;
22884860c5dSDavid du Colombier 	static Lock resetlck;
22984860c5dSDavid du Colombier 
230cc499a30SDavid du Colombier 	s = getconf("*maxehci");
231cc499a30SDavid du Colombier 	if (s != nil && s[0] >= '0' && s[0] <= '9')
232cc499a30SDavid du Colombier 		maxehci = atoi(s);
233cc499a30SDavid du Colombier 	if(maxehci == 0 || getconf("*nousbehci"))
23484860c5dSDavid du Colombier 		return -1;
235cc499a30SDavid du Colombier 
23684860c5dSDavid du Colombier 	ilock(&resetlck);
23784860c5dSDavid du Colombier 	scanpci();
23884860c5dSDavid du Colombier 
23984860c5dSDavid du Colombier 	/*
24084860c5dSDavid du Colombier 	 * Any adapter matches if no hp->port is supplied,
24184860c5dSDavid du Colombier 	 * otherwise the ports must match.
24284860c5dSDavid du Colombier 	 */
24384860c5dSDavid du Colombier 	ctlr = nil;
24484860c5dSDavid du Colombier 	for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){
24584860c5dSDavid du Colombier 		ctlr = ctlrs[i];
24684860c5dSDavid du Colombier 		if(ctlr->active == 0)
24784860c5dSDavid du Colombier 		if(hp->port == 0 || hp->port == (uintptr)ctlr->capio){
24884860c5dSDavid du Colombier 			ctlr->active = 1;
24984860c5dSDavid du Colombier 			break;
25084860c5dSDavid du Colombier 		}
25184860c5dSDavid du Colombier 	}
25284860c5dSDavid du Colombier 	iunlock(&resetlck);
25384860c5dSDavid du Colombier 	if(i >= Nhcis || ctlrs[i] == nil)
25484860c5dSDavid du Colombier 		return -1;
25584860c5dSDavid du Colombier 
25684860c5dSDavid du Colombier 	p = ctlr->pcidev;
25784860c5dSDavid du Colombier 	hp->aux = ctlr;
25884860c5dSDavid du Colombier 	hp->port = (uintptr)ctlr->capio;
25984860c5dSDavid du Colombier 	hp->irq = p->intl;
26084860c5dSDavid du Colombier 	hp->tbdf = p->tbdf;
26184860c5dSDavid du Colombier 
26284860c5dSDavid du Colombier 	capio = ctlr->capio;
26384860c5dSDavid du Colombier 	hp->nports = capio->parms & Cnports;
26484860c5dSDavid du Colombier 
26584860c5dSDavid du Colombier 	ddprint("echi: %s, ncc %lud npcc %lud\n",
26684860c5dSDavid du Colombier 		capio->parms & 0x10000 ? "leds" : "no leds",
26784860c5dSDavid du Colombier 		(capio->parms >> 12) & 0xf, (capio->parms >> 8) & 0xf);
26884860c5dSDavid du Colombier 	ddprint("ehci: routing %s, %sport power ctl, %d ports\n",
26984860c5dSDavid du Colombier 		capio->parms & 0x40 ? "explicit" : "automatic",
27084860c5dSDavid du Colombier 		capio->parms & 0x10 ? "" : "no ", hp->nports);
27184860c5dSDavid du Colombier 
27284860c5dSDavid du Colombier 	ehcireset(ctlr);
27384860c5dSDavid du Colombier 	ehcimeminit(ctlr);
27484860c5dSDavid du Colombier 
27584860c5dSDavid du Colombier 	/*
27684860c5dSDavid du Colombier 	 * Linkage to the generic HCI driver.
27784860c5dSDavid du Colombier 	 */
27884860c5dSDavid du Colombier 	ehcilinkage(hp);
27984860c5dSDavid du Colombier 	hp->shutdown = shutdown;
28084860c5dSDavid du Colombier 	hp->debug = setdebug;
28184860c5dSDavid du Colombier 	return 0;
28284860c5dSDavid du Colombier }
28384860c5dSDavid du Colombier 
28484860c5dSDavid du Colombier void
usbehcilink(void)28584860c5dSDavid du Colombier usbehcilink(void)
28684860c5dSDavid du Colombier {
28784860c5dSDavid du Colombier 	addhcitype("ehci", reset);
28884860c5dSDavid du Colombier }
289