shithub: riscv

Download patch

ref: 2aff96f17c4127617a48f75032c13e19cd3eb4a9
parent: fa1d6ffd83a6f9de19632c43c46412c8a41b4cc5
author: cinap_lenrek <[email protected]>
date: Sun Oct 4 17:10:53 EDT 2020

etheriwl: add for Intel Wireless-AC 9260

the 9000 series uses a new receive descriptor format
wich appears to reqire 4k aligned buffers. the old
format "halfworks" and just makes the firmware not
respond to any commands after the enable paging command.

the smartfifo command appears to causes problems.
but apparently not issuing it at all seems to work
fine on both the 8265 and 9260. so removing the code
for now.

issuing the bindingquota command before associated
makes association impossible. but enabling afterwards
works fine. (tested in 8265 and 9260).

the prph access functions now mask the address with
0xfffff. it is unclear why linux and openbsd drivers
specify addresses beyond that in ther register constants.

the timeevent change is interesting. the timeevent
needs to be restarted when it has stoped to make sure
probing/association packets are sent during the evnet.

--- a/sys/src/9/pc/etheriwl.c
+++ b/sys/src/9/pc/etheriwl.c
@@ -29,10 +29,10 @@
 	Nrx		= 1<<Nrxlog,
 
 	Rstatsize	= 16,
+
 	Rbufsize	= 4*1024,
 	Rdscsize	= 8,
 
-	Tbufsize	= 4*1024,
 	Tdscsize	= 128,
 	Tcmdsize	= 140,
 
@@ -185,6 +185,8 @@
 
 	FhRxStatus	= 0x1c44,
 
+	FhRxQ0Wptr	= 0x1c80,	// +q*4 (9000 mqrx)
+
 	FhTxConfig	= 0x1d00,	// +q*32
 		FhTxConfigDmaCreditEna	= 1<<3,
 		FhTxConfigDmaEna	= 1<<31,
@@ -237,9 +239,48 @@
 
 	LmpmChick	= 0xa01ff8,
 		ExtAddr = 1,
+
+	SbCpu1Status	= 0xa01e30,
+	SbCpu2Status	= 0xa01e34,
+	UregChick	= 0xa05c00,
+		UregChickMsiEnable	= 1<<24,
+
+	FhUcodeLoadStatus=0xa05c40,
 };
 
 /*
+ * RX ring for mqrx 9000
+*/
+enum {
+	RfhQ0FreeBase	= 0xa08000,	// +q*8
+	RfhQ0FreeWptr	= 0xa08080,	// +q*4
+	RfhQ0FreeRptr	= 0xa080c0,	// +q*4
+
+	RfhQ0UsedBase	= 0xa08100,	// +q*8
+	RfhQ0UsedWptr	= 0xa08180,	// +q*4
+
+	RfhQ0SttsBase	= 0xa08200,	// +q*8
+
+	RfhGenCfg	= 0xa09800,
+		RfhGenServiceDmaSnoop	= 1<<0,
+		RfhGenRfhDmaSnoop	= 1<<1,
+		RfhGenRbChunkSize64	= 0<<4,
+		RfhGenRbChunkSize128	= 1<<4,
+
+	RfhGenStatus	= 0xa09808,
+		RfhGenStatusDmaIdle	= 1<<31,
+
+	RfhRxqActive	= 0xa0980c,
+
+	RfhDmaCfg	= 0xa09820,
+		RfhDma1KSizeShift	= 16,
+		RfhDmaNrbdShift		= 20,
+		RfhDmaMinRbSizeShift	= 24,
+		RfhDmaDropTooLarge	= 1<<26,
+		RfhDmaEnable		= 1<<31,
+};
+
+/*
  * TX scheduler registers.
  */
 enum {
@@ -386,6 +427,11 @@
 
 struct FWInfo
 {
+	int	valid;
+
+	u16int	status;
+	u16int	flags;
+
 	u32int	major;
 	u32int	minor;
 	uchar	type;
@@ -439,8 +485,9 @@
 {
 	uint	i;
 	Block	**b;
-	u32int	*p;
 	uchar	*s;
+	uchar	*p;
+	uchar	*u;
 };
 
 struct Station
@@ -459,6 +506,7 @@
 	Wifi *wifi;
 
 	char *fwname;
+	int mqrx;
 	int family;
 	int type;
 	uint step;
@@ -483,7 +531,6 @@
 	int phyid;
 	int macid;
 	int bindid;
-	int timeid;
 
 	/* current receiver settings */
 	uchar bssid[Eaddrlen];
@@ -491,6 +538,12 @@
 	int prom;
 	int aid;
 
+	struct {
+		Rendez;
+		int id;
+		int active;
+	} te;
+
 	uvlong systime;
 
 	RXQ rx;
@@ -687,6 +740,7 @@
 static u32int
 prphread(Ctlr *ctlr, uint off)
 {
+	off &= 0xfffff;
 	csr32w(ctlr, PrphRaddr, ((sizeof(u32int)-1)<<24) | off);
 	coherence();
 	return csr32r(ctlr, PrphRdata);
@@ -694,11 +748,19 @@
 static void
 prphwrite(Ctlr *ctlr, uint off, u32int data)
 {
+	off &= 0xfffff;
 	csr32w(ctlr, PrphWaddr, ((sizeof(u32int)-1)<<24) | off);
 	coherence();
 	csr32w(ctlr, PrphWdata, data);
 }
 
+static void
+prphwrite64(Ctlr *ctlr, uint off, u64int data)
+{
+	prphwrite(ctlr, off, data & 0xFFFFFFFF);
+	prphwrite(ctlr, off+4, data >> 32);
+}
+
 static u32int
 memread(Ctlr *ctlr, uint off)
 {
@@ -720,10 +782,12 @@
 	FWInfo *i;
 
 	i = &ctlr->fwinfo;
-
 	switch(len){
-	case 1+1+2+1+1 + 1+1 + 1+1 + 2 + 4+4+4+4+4+4 + 4:
-	case 1+1+2+1+1 + 1+1 + 1+1 + 2 + 4+4+4+4+4+4 + 4 + 4+4 + 1+1 + 2 + 4+4:
+	case 2+2 + 1+1+2+1+1 + 1+1 + 1+1 + 2 + 4+4+4+4+4+4 + 4:
+	case 2+2 + 1+1+2+1+1 + 1+1 + 1+1 + 2 + 4+4+4+4+4+4 + 4 + 4+4 + 1+1 + 2 + 4+4:
+		i->status = get16(d); d += 2;
+		i->flags = get16(d); d += 2;
+
 		i->minor = *d++;
 		i->major = *d++;
 		d += 2;					// id
@@ -756,9 +820,14 @@
 		d += 2;
 		i->umac.errptr = get32(d); d += 4;
 		i->umac.logptr = get32(d); d += 4;
+
+		i->valid = (i->status == 0xcafe);
 		break;
 
-	case 4+4 + 1+1 + 1+1 + 4+4+4+4+4+4 + 4 + 4+4 + 4+4+4+4:
+	case 2+2 + 4+4 + 1+1 + 1+1 + 4+4+4+4+4+4 + 4 + 4+4 + 4+4+4+4:
+		i->status = get16(d); d += 2;
+		i->flags = get16(d); d += 2;
+
 		i->minor = get32(d);
 		d += 4;
 		i->major = get32(d);
@@ -785,6 +854,8 @@
 		i->umac.major = get32(d); d += 4;
 		i->umac.errptr = get32(d); d += 4;
 		i->umac.logptr = get32(d); d += 4;
+
+		i->valid = (i->status == 0xcafe);
 		break;
 	
 	default:
@@ -799,23 +870,31 @@
 		i->logptr = get32(d); d += 4;
 		i->errptr = get32(d); d += 4;
 		i->tstamp = get32(d); d += 4;
+		i->valid = 1;
 	}
 	USED(d);
+}
 
-	if(0){
-		iprint("fwinfo: ver %ud.%ud type %ud.%ud\n",
-			i->major, i->minor, i->type, i->subtype);
-		iprint("fwinfo: scdptr=%.8ux\n", i->scdptr);
-		iprint("fwinfo: regptr=%.8ux\n", i->regptr);
-		iprint("fwinfo: logptr=%.8ux\n", i->logptr);
-		iprint("fwinfo: errptr=%.8ux\n", i->errptr);
+static void
+printfwinfo(Ctlr *ctlr)
+{
+	FWInfo *i = &ctlr->fwinfo;
 
-		iprint("fwinfo: ts=%.8ux\n", i->tstamp);
+	print("fwinfo: status=%.4ux flags=%.4ux\n",
+		i->status, i->flags);
 
-		iprint("fwinfo: umac ver %ud.%ud\n", i->umac.major, i->umac.minor);
-		iprint("fwinfo: umac errptr %.8ux\n", i->umac.errptr);
-		iprint("fwinfo: umac logptr %.8ux\n", i->umac.logptr);
-	}
+	print("fwinfo: ver %ud.%ud type %ud.%ud\n",
+		i->major, i->minor, i->type, i->subtype);
+	print("fwinfo: scdptr=%.8ux\n", i->scdptr);
+	print("fwinfo: regptr=%.8ux\n", i->regptr);
+	print("fwinfo: logptr=%.8ux\n", i->logptr);
+	print("fwinfo: errptr=%.8ux\n", i->errptr);
+
+	print("fwinfo: ts=%.8ux\n", i->tstamp);
+
+	print("fwinfo: umac ver %ud.%ud\n", i->umac.major, i->umac.minor);
+	print("fwinfo: umac errptr %.8ux\n", i->umac.errptr);
+	print("fwinfo: umac logptr %.8ux\n", i->umac.logptr);
 }
 
 static void
@@ -826,7 +905,7 @@
 
 	print("lastcmd: %ud (0x%ux)\n", ctlr->tx[4].lastcmd,  ctlr->tx[4].lastcmd);
 
-	if(ctlr->fwinfo.errptr == 0){
+	if(!ctlr->fwinfo.valid || ctlr->fwinfo.errptr == 0){
 		print("no error pointer\n");
 		return;
 	}
@@ -914,7 +993,6 @@
 			goto Ready;
 		delay(10);
 	}
-
 	if(ctlr->family >= 7000){
 		csr32w(ctlr, Dbglinkpwrmgmt, csr32r(ctlr, Dbglinkpwrmgmt) | (1<<31));
 		delay(1);
@@ -928,6 +1006,7 @@
 	}
 	if(i >= 15000)
 		return "handover: timeout";
+
 	csr32w(ctlr, Cfg, csr32r(ctlr, Cfg) | NicReady);
 	for(i=0; i<5; i++){
 		if(csr32r(ctlr, Cfg) & NicReady)
@@ -1001,6 +1080,14 @@
 	if((err = clockwait(ctlr)) != nil)
 		return err;
 
+	if(ctlr->mqrx){
+		/* Newer cards default to MSIX? */
+		if((err = niclock(ctlr)) != nil)
+			return err;
+		prphwrite(ctlr, UregChick, UregChickMsiEnable);
+		nicunlock(ctlr);
+	}
+
 	if(ctlr->family < 8000){
 		if((err = niclock(ctlr)) != nil)
 			return err;
@@ -1057,11 +1144,20 @@
 
 	/* Stop RX ring */
 	if(niclock(ctlr) == nil){
-		csr32w(ctlr, FhRxConfig, 0);
-		for(j = 0; j < 200; j++){
-			if(csr32r(ctlr, FhRxStatus) & 0x1000000)
-				break;
-			delay(10);
+		if(ctlr->mqrx){
+			prphwrite(ctlr, RfhDmaCfg, 0);
+			for(j = 0; j < 200; j++){
+				if(prphread(ctlr, RfhGenStatus) & RfhGenStatusDmaIdle)
+					break;
+				delay(10);
+			}
+		} else {
+			csr32w(ctlr, FhRxConfig, 0);
+			for(j = 0; j < 200; j++){
+				if(csr32r(ctlr, FhRxStatus) & 0x1000000)
+					break;
+				delay(10);
+			}
 		}
 		nicunlock(ctlr);
 	}
@@ -1337,29 +1433,29 @@
 				if(i->main.nsect < 1)
 					i->main.nsect = 1;
 				s->addr = 0x00000000;
-				break;
+				goto Sect;
 			case 2:
 				s = &i->main.data;
 				if(i->main.nsect < 2)
 					i->main.nsect = 2;
 				s->addr = 0x00800000;
-				break;
+				goto Sect;
 			case 3:
 				s = &i->init.text;
 				if(i->init.nsect < 1)
 					i->init.nsect = 1;
 				s->addr = 0x00000000;
-				break;
+				goto Sect;
 			case 4:
 				s = &i->init.data;
 				if(i->init.nsect < 2)
 					i->init.nsect = 2;
 				s->addr = 0x00800000;
-				break;
+				goto Sect;
 			case 5:
 				s = &i->boot.text;
 				s->addr = 0x00000000;
-				break;
+				goto Sect;
 			case 19:
 				if(i->main.nsect >= nelem(i->main.sect))
 					return "too many main sections";
@@ -1374,8 +1470,10 @@
 					goto Tooshort;
 				s->addr = get32(p);
 				p += 4, l -= 4;
+			Sect:
+				s->size = l;
+				s->data = p;
 				break;
-
 			case 22:
 				if(l < 12)
 					goto Tooshort;
@@ -1389,14 +1487,12 @@
 					i->init.defcalib.eventmask = get32(p+8);
 					break;
 				}
-				continue;
-
+				break;
 			case 23:
 				if(l < 4)
 					goto Tooshort;
 				i->physku = get32(p);
-				continue;
-
+				break;
 			case 29:
 				if(l < 8)
 					goto Tooshort;
@@ -1404,7 +1500,7 @@
 				if(t >= nelem(i->api))
 					goto Tooshort;
 				i->api[t] = get32(p+4);
-				continue;
+				break;
 			case 30:
 				if(l < 8)
 					goto Tooshort;
@@ -1412,19 +1508,13 @@
 				if(t >= nelem(i->capa))
 					goto Tooshort;
 				i->capa[t] = get32(p+4);
-				continue;
-
+				break;
 			case 32:
 				if(l < 4)
 					goto Tooshort;
 				i->pagedmemsize = get32(p) & -FWPagesize;
-				continue;
-
-			default:
-				continue;
+				break;
 			}
-			s->size = l;
-			s->data = p;
 		}
 	} else {
 		if(((i->rev>>8) & 0xFF) < 2)
@@ -1540,17 +1630,25 @@
 }
 
 static int
-rbplant(Ctlr *ctlr, int i)
+rbplant(Ctlr *ctlr, uint i)
 {
 	Block *b;
 
-	b = iallocb(Rbufsize + 256);
+	assert(i < Nrx);
+
+	b = iallocb(Rbufsize*2);
 	if(b == nil)
 		return -1;
-	b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, 256);
+	b->rp = b->wp = (uchar*)ROUND((uintptr)b->base, Rbufsize);
 	memset(b->rp, 0, Rdscsize);
+
 	ctlr->rx.b[i] = b;
-	ctlr->rx.p[i] = PCIWADDR(b->rp) >> 8;
+
+	if(ctlr->mqrx)
+		put64(ctlr->rx.p + (i<<3), PCIWADDR(b->rp));
+	else
+		put32(ctlr->rx.p + (i<<2), PCIWADDR(b->rp) >> 8);
+
 	return 0;
 }
 
@@ -1585,17 +1683,31 @@
 	}
 
 	rx = &ctlr->rx;
+	if(ctlr->mqrx){
+		if(rx->u == nil)
+			rx->u = mallocalign(4 * Nrx, 4096, 0, 0);
+		if(rx->p == nil)
+			rx->p = mallocalign(8 * Nrx, 4096, 0, 0);
+		if(rx->u == nil || rx->p == nil)
+			return "no memory for rx rings";
+		memset(rx->u, 0, 4 * Nrx);
+		memset(rx->p, 0, 8 * Nrx);
+	} else {
+		rx->u = nil;
+		if(rx->p == nil)
+			rx->p = mallocalign(4 * Nrx, 256, 0, 0);
+		if(rx->p == nil)
+			return "no memory for rx rings";
+		memset(rx->p, 0, 4 * Nrx);
+	}
+	if(rx->s == nil)
+		rx->s = mallocalign(Rstatsize, 4096, 0, 0);
 	if(rx->b == nil)
 		rx->b = malloc(sizeof(Block*) * Nrx);
-	if(rx->p == nil)
-		rx->p = mallocalign(sizeof(u32int) * Nrx, 256, 0, 0);
-	if(rx->s == nil)
-		rx->s = mallocalign(Rstatsize, 16, 0, 0);
-	if(rx->b == nil || rx->p == nil || rx->s == nil)
+	if(rx->b == nil || rx->s == nil)
 		return "no memory for rx ring";
-	memset(ctlr->rx.s, 0, Rstatsize);
+	memset(rx->s, 0, Rstatsize);
 	for(i=0; i<Nrx; i++){
-		rx->p[i] = 0;
 		if(rx->b[i] != nil){
 			freeb(rx->b[i]);
 			rx->b[i] = nil;
@@ -1617,10 +1729,10 @@
 	}
 
 	if(ctlr->sched.s == nil)
-		ctlr->sched.s = mallocalign(512 * ctlr->ntxq * 2, 1024, 0, 0);
+		ctlr->sched.s = mallocalign((256+64)*2 * ctlr->ntxq, 4096, 0, 0);
 	if(ctlr->sched.s == nil)
 		return "no memory for sched buffer";
-	memset(ctlr->sched.s, 0, 512 * ctlr->ntxq);
+	memset(ctlr->sched.s, 0, (256+64)*2 * ctlr->ntxq);
 
 	for(q=0; q < nelem(ctlr->tx); q++){
 		tx = &ctlr->tx[q];
@@ -1627,9 +1739,9 @@
 		if(tx->b == nil)
 			tx->b = malloc(sizeof(Block*) * Ntx);
 		if(tx->d == nil)
-			tx->d = mallocalign(Tdscsize * Ntx, 256, 0, 0);
+			tx->d = mallocalign(Tdscsize * Ntx, 4096, 0, 0);
 		if(tx->c == nil)
-			tx->c = mallocalign(Tcmdsize * Ntx, 4, 0, 0);
+			tx->c = mallocalign(Tcmdsize * Ntx, 4096, 0, 0);
 		if(tx->b == nil || tx->d == nil || tx->c == nil)
 			return "no memory for tx ring";
 		memset(tx->d, 0, Tdscsize * Ntx);
@@ -1724,18 +1836,59 @@
 
 	if((err = niclock(ctlr)) != nil)
 		return err;
-	csr32w(ctlr, FhRxConfig, 0);
-	csr32w(ctlr, FhRxWptr, 0);
-	csr32w(ctlr, FhRxBase, PCIWADDR(ctlr->rx.p) >> 8);
-	csr32w(ctlr, FhStatusWptr, PCIWADDR(ctlr->rx.s) >> 4);
-	csr32w(ctlr, FhRxConfig,
-		FhRxConfigEna | 
-		FhRxConfigIgnRxfEmpty |
-		FhRxConfigIrqDstHost | 
-		FhRxConfigSingleFrame |
-		(Nrxlog << FhRxConfigNrbdShift));
-	csr32w(ctlr, FhRxWptr, (Nrx-1) & ~7);
 
+	if(ctlr->mqrx){
+		/* Stop RX DMA. */
+		prphwrite(ctlr, RfhDmaCfg, 0);
+		/* Disable RX used and free queue operation. */
+		prphwrite(ctlr, RfhRxqActive, 0);
+
+		prphwrite64(ctlr, RfhQ0SttsBase, PCIWADDR(ctlr->rx.s));
+		prphwrite64(ctlr, RfhQ0FreeBase, PCIWADDR(ctlr->rx.p));
+		prphwrite64(ctlr, RfhQ0UsedBase, PCIWADDR(ctlr->rx.u));
+
+		prphwrite(ctlr, RfhQ0FreeWptr, 0);
+		prphwrite(ctlr, RfhQ0FreeRptr, 0);
+		prphwrite(ctlr, RfhQ0UsedWptr, 0);
+
+		/* Enable RX DMA */
+		prphwrite(ctlr, RfhDmaCfg,
+			RfhDmaEnable |
+			RfhDmaDropTooLarge |
+			((Rbufsize/1024) << RfhDma1KSizeShift) |
+			(3 << RfhDmaMinRbSizeShift) |
+			(Nrxlog << RfhDmaNrbdShift));
+
+		/* Enable RX DMA snooping. */
+		prphwrite(ctlr, RfhGenCfg,
+			RfhGenServiceDmaSnoop |
+			RfhGenRfhDmaSnoop |
+			RfhGenRbChunkSize128);
+
+		/* Enable Q0 */
+		prphwrite(ctlr, RfhRxqActive, (1 << 16) | 1);
+		delay(1);
+
+		csr32w(ctlr, FhRxQ0Wptr, (Nrx-1) & ~7);
+		delay(1);
+	} else {
+		csr32w(ctlr, FhRxConfig, 0);
+		csr32w(ctlr, FhRxWptr, 0);
+		csr32w(ctlr, FhRxBase, PCIWADDR(ctlr->rx.p) >> 8);
+		csr32w(ctlr, FhStatusWptr, PCIWADDR(ctlr->rx.s) >> 4);
+		csr32w(ctlr, FhRxConfig,
+			FhRxConfigEna | 
+			FhRxConfigIgnRxfEmpty |
+			FhRxConfigIrqDstHost | 
+			FhRxConfigSingleFrame |
+			(Nrxlog << FhRxConfigNrbdShift));
+
+		csr32w(ctlr, FhRxWptr, (Nrx-1) & ~7);
+	}
+
+	for(i = 0; i < ctlr->ndma; i++)
+		csr32w(ctlr, FhTxConfig + i*32, 0);
+
 	if(ctlr->family >= 7000 || ctlr->type != Type4965)
 		prphwrite(ctlr, SchedTxFact, 0);
 	else
@@ -1781,9 +1934,13 @@
 	ctlr->phyid = -1;
 	ctlr->macid = -1;
 	ctlr->bindid = -1;
-	ctlr->timeid = -1;
+	ctlr->te.id = -1;
+	ctlr->te.active = 0;
 	ctlr->aid = 0;
 
+	if(ctlr->family >= 9000)
+		csr32w(ctlr, Gpc, csr32r(ctlr, Gpc) | 0x4000000);
+
 	ctlr->ie = Idefmask;
 	csr32w(ctlr, Imr, ctlr->ie);
 	csr32w(ctlr, Isr, ~0);
@@ -1958,6 +2115,7 @@
 			break;
 		}
 	}
+
 	if(i+1 >= nsect)
 		return "firmware misses CSS+paging sections";
 
@@ -2120,72 +2278,6 @@
 }
 
 static char*
-setsmartfifo(Ctlr *ctlr, u32int state, int fullon)
-{
-	uchar c[4*(1 + 2 + 5*2 + 5*2)], *p;
-	int i;
-
-	memset(p = c, 0, sizeof(c));
-	put32(p, state);
-	p += 4;
-
-	/* watermark long delay on */
-	put32(p, 4096);
-	p += 4;
-
-	/* watermark full on */
-	put32(p, 4096);
-	p += 4;
-
-	/* long delay timeouts */
-	for(i = 0; i < 5*2; i++){
-		put32(p, 1000000);
-		p += 4;
-	}
-
-	/* full on timeouts */
-	if(fullon){
-		/* single unicast */
-		put32(p, 320);
-		p += 4;
-		put32(p, 2016);
-		p += 4;
-
-		/* agg unicast */
-		put32(p, 320);
-		p += 4;
-		put32(p, 2016);
-		p += 4;
-
-		/* mcast */
-		put32(p, 2016);
-		p += 4;
-		put32(p, 10016);
-		p += 4;
-
-		/* ba */
-		put32(p, 320);
-		p += 4;
-		put32(p, 2016);
-		p += 4;
-
-		/* tx re */
-		put32(p, 320);
-		p += 4;
-		put32(p, 2016);
-		p += 4;
-	} else {
-		for(i = 0; i < 5; i++){
-			put32(p, 160);
-			p += 4;
-			put32(p, 400);
-			p += 4;
-		}
-	}
-	return cmd(ctlr, 209, c, p - c);
-}
-
-static char*
 delstation(Ctlr *ctlr, Station *sta)
 {
 	uchar c[4], *p;
@@ -2292,9 +2384,7 @@
 
 	if((err = cmd(ctlr, 24, c, p - c)) != nil)
 		return err;
-
 	sta->id = id;
-
 	return nil;
 }
 
@@ -2518,13 +2608,38 @@
 	return nil;
 }
 
+static int
+timeeventdone(void *arg)
+{
+	Ctlr *ctlr = arg;
+	return ctlr->te.id == -1 || ctlr->te.active != 0;
+}
+
 static char*
 settimeevent(Ctlr *ctlr, int amr, int ival)
 {
-	int timeid, duration, delay;
+	int duration, delay, timeid;
 	uchar c[9*4], *p;
 	char *err;
 
+	switch(amr){
+	case CmdAdd:
+		timeid = ctlr->te.id;
+		if(timeid == -1)
+			timeid = 0;
+		else {
+			if(ctlr->te.active)
+				return nil;
+			amr = CmdModify;
+		}
+		break;
+	default:
+		timeid = ctlr->te.id;
+		if(timeid == -1)
+			return nil;
+		break;
+	}
+
 	if(ival){
 		duration = ival*2;
 		delay = ival/2;
@@ -2533,19 +2648,6 @@
 		delay = 0;
 	}
 
-	timeid = ctlr->timeid;
-	if(timeid < 0){
-		if(amr == CmdRemove)
-			return nil;
-		amr = CmdAdd;
-		timeid = 0;
-	} else {
-		if(amr == CmdAdd)
-			amr = CmdModify;
-	}
-	if(ctlr->macid < 0)
-		return "no mac id set";
-
 	memset(p = c, 0, sizeof(c));
 	put32(p, ctlr->macid);
 	p += 4;
@@ -2569,13 +2671,17 @@
 	put16(p, 1<<0 | 1<<1 | 1<<11);	// policy
 	p += 2;
 
+	ctlr->te.active = 0;
 	if((err =  cmd(ctlr, 41, c, p - c)) != nil)
 		return err;
 
-	if(amr == CmdRemove)
-		ctlr->timeid = -1;
-
-	return nil;
+	if(amr == CmdRemove){
+		ctlr->te.active = 0;
+		ctlr->te.id = -1;
+		return nil;
+	}
+	tsleep(&ctlr->te, timeeventdone, ctlr, 100);
+	return ctlr->te.active? nil: "timeevent did not start";
 }
 
 
@@ -2713,9 +2819,6 @@
 	if(ctlr->calib.done == 0){
 		if((err = readnvmconfig(ctlr)) != nil)
 			return err;
-
-		if((err = setsmartfifo(ctlr, 3, 0)) != nil)
-			return err;
 	}
 
 	if((err = sendtxantconfig(ctlr, ctlr->rfcfg.txantmask)) != nil)
@@ -3047,13 +3150,16 @@
 	memmove(dma, data, size);
 	coherence();
 	
-	if(ctlr->family >= 7000 && dst >= 0x40000 && dst < 0x57fff)
-		prphwrite(ctlr, LmpmChick, prphread(ctlr, LmpmChick) | ExtAddr);
-
 	if((err = niclock(ctlr)) != nil){
 		free(dma);
 		return err;
 	}
+
+	if(ctlr->family >= 7000 && dst >= 0x40000 && dst < 0x57fff)
+		prphwrite(ctlr, LmpmChick, prphread(ctlr, LmpmChick) | ExtAddr);
+	else
+		prphwrite(ctlr, LmpmChick, prphread(ctlr, LmpmChick) & ~ExtAddr);
+
 	csr32w(ctlr, FhTxConfig + 9*32, 0);
 	csr32w(ctlr, FhSramAddr + 9*4, dst);
 	csr32w(ctlr, FhTfbdCtrl0 + 9*8, PCIWADDR(dma));
@@ -3069,8 +3175,10 @@
 	if(irqwait(ctlr, Ifhtx|Ierr, 5000) != Ifhtx)
 		err = "dma error / timeout";
 
-	if(ctlr->family >= 7000 && dst >= 0x40000 && dst < 0x57fff)
+	if(niclock(ctlr) == nil){
 		prphwrite(ctlr, LmpmChick, prphread(ctlr, LmpmChick) & ~ExtAddr);
+		nicunlock(ctlr);
+	}
 
 	free(dma);
 
@@ -3125,6 +3233,7 @@
 static char*
 ucodestart(Ctlr *ctlr)
 {
+	memset(&ctlr->fwinfo, 0, sizeof(ctlr->fwinfo));
 	if(ctlr->family >= 8000)
 		return setloadstatus(ctlr, -1);
 	csr32w(ctlr, Reset, 0);
@@ -3147,19 +3256,28 @@
 				return err;
 			if((err = ucodestart(ctlr)) != nil)
 				return err;
-			if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
+
+			tsleep(&up->sleep, return0, 0, 100);
+
+			if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive|| !ctlr->fwinfo.valid){
 				return "init firmware boot failed";
+			}
 			if((err = postboot(ctlr)) != nil)
 				return err;
 			if((err = reset(ctlr)) != nil)
 				return err;
 		}
+
 		if((err = loadsections(ctlr, fw->main.sect, fw->main.nsect)) != nil)
 			return err;
 		if((err= ucodestart(ctlr)) != nil)
 			return err;
-		if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive)
+
+		tsleep(&up->sleep, return0, 0, 100);
+
+		if(irqwait(ctlr, Ierr|Ialive, 5000) != Ialive || !ctlr->fwinfo.valid)
 			return "main firmware boot failed";
+
 		return postboot(ctlr);
 	}
 
@@ -3270,7 +3388,7 @@
 
 	assert(qid < ctlr->ntxq);
 
-	if(code & 0xFF00)
+	if((code & 0xFF00) != 0)
 		hdrlen = 8;
 	else
 		hdrlen = 4;
@@ -3340,8 +3458,9 @@
 	if(block != nil){
 		size = BLEN(block);
 		put32(d, PCIWADDR(block->rp)); d += 4;
-		put16(d, size << 4);
+		put16(d, size << 4); d += 2;
 	}
+	USED(d);
 
 	coherence();
 
@@ -3390,9 +3509,14 @@
 	char *err;
 
 	if(0) print("cmd %ud\n", code);
-	if((err = qcmd(ctlr, 4, code, data, size, nil)) != nil)
+
+	if((err = qcmd(ctlr, 4, code, data, size, nil)) != nil
+	|| (err = flushq(ctlr, 4)) != nil){
+		print("#l%d: cmd %ud: %s\n", ctlr->edev->ctlrno, code, err);
+		ctlr->broken = 1;
 		return err;
-	return flushq(ctlr, 4);
+	}
+	return nil;
 }
 
 static void
@@ -3404,12 +3528,11 @@
 		return;	// TODO
 
 	csr32w(ctlr, Led, csr32r(ctlr, Led) & ~LedBsmCtrl);
-
-	memset(c, 0, sizeof(c));
 	put32(c, 10000);
 	c[4] = which;
 	c[5] = on;
 	c[6] = off;
+	c[7] = 0;
 	cmd(ctlr, 72, c, sizeof(c));
 }
 
@@ -3418,14 +3541,6 @@
 {
 	char *err;
 
-	if((err = settimeevent(ctlr, CmdRemove, 0)) != nil){
-		print("can't remove timeevent: %s\n", err);
-		return err;
-	}
-	if((err = setsmartfifo(ctlr, 2, 0)) != nil){
-		print("setsmartfifo: %s\n", err);
-		return err;
-	}
 	if((err = setbindingquotas(ctlr, -1)) != nil){
 		print("can't disable quotas: %s\n", err);
 		return err;
@@ -3462,10 +3577,6 @@
 		print("removing bindingcontext: %s\n", err);
 		return err;
 	}
-	if((err = setsmartfifo(ctlr, 1, ctlr->aid != 0)) != nil){
-		print("setsmartfifo: %s\n", err);
-		return err;
-	}
 	if((err = setmcastfilter(ctlr)) != nil){
 		print("can't set mcast filter: %s\n", err);
 		return err;
@@ -3474,7 +3585,7 @@
 		print("can't set mac power: %s\n", err);
 		return err;
 	}
-	if((err = setbindingquotas(ctlr, ctlr->bindid)) != nil){
+	if((err = setbindingquotas(ctlr, ctlr->aid != 0 ? ctlr->bindid : -1)) != nil){
 		print("can't set binding quotas: %s\n", err);
 		return err;
 	}
@@ -3528,8 +3639,10 @@
 	char *err;
 
 	if(ctlr->family >= 7000){
+		flushq(ctlr, 0);
 		delstation(ctlr, &ctlr->bss);
 		delstation(ctlr, &ctlr->bcast);
+		settimeevent(ctlr, CmdRemove, 0);
 		if((err = rxoff7000(edev, ctlr)) != nil)
 			goto Out;
 	}
@@ -3600,10 +3713,6 @@
 		if(ctlr->family >= 7000)
 			if((err = setmaccontext(edev, ctlr, CmdModify, bss)) != nil)
 				goto Out;
-	} else {
-		if(ctlr->family >= 7000)
-			if((err = settimeevent(ctlr, CmdAdd, (bss != nil)? bss->ival: 0)) != nil)
-				goto Out;
 	}
 Out:
 	return err;
@@ -3637,12 +3746,20 @@
 			goto Broken;
 	}
 
+	/*
+	 * unless authenticated, the firmware will hop
+	 * channels unless we force it onto a channel using
+	 * a timeevent.
+	 */
+	if(ctlr->aid == 0 && ctlr->family >= 7000)
+		if(settimeevent(ctlr, CmdAdd, wn->ival) != nil)
+			goto Broken;
+
 	if(b == nil){
 		/* association note has no data to transmit */
 		qunlock(ctlr);
 		return;
 	}
-
 	flags = 0;
 	sta = &ctlr->bcast;
 	p = wn->minrate;
@@ -3895,6 +4012,13 @@
 }
 
 static void
+updatesystime(Ctlr *ctlr, u32int ts)
+{
+	u32int dt = ts - (u32int)ctlr->systime;
+	ctlr->systime += dt;
+}
+
+static void
 receive(Ctlr *ctlr)
 {
 	Block *b, *bb;
@@ -3955,9 +4079,29 @@
 				wifitxfail(ctlr->wifi, bb);
 			break;
 		case 41:
-			if(len >= 16 && get32(d) == 0 && ctlr->timeid == -1)
-				ctlr->timeid = get32(d+8);
+			if(len < 2*4)
+				break;
+			if(get32(d) != 0)
+				break;
+			if(ctlr->te.id == -1)
+				ctlr->te.id = get32(d+8);
 			break;
+		case 42:
+			if(len < 6*4)
+				break;
+			if(ctlr->te.id == -1 || get32(d+8) != ctlr->te.id)
+				break;
+			switch(get32(d+16)){
+			case 1:
+				ctlr->te.active = 1;
+				wakeup(&ctlr->te);
+				break;
+			case 2:
+				ctlr->te.active = 0;
+				ctlr->te.id = -1;
+				wakeup(&ctlr->te);
+			}
+			break;
 		case 102:	/* calibration result (Type5000 only) */
 			if(ctlr->family >= 7000)
 				break;
@@ -3978,6 +4122,7 @@
 		case 4:		/* init complete (>= 7000 family) */
 			if(ctlr->family < 7000)
 				break;
+			/* wet floor */
 		case 103:	/* calibration done (Type5000 only) */
 			ctlr->calib.done = 1;
 			break;
@@ -4041,10 +4186,8 @@
 		case 177:	/* mduart load notification */
 			break;
 		case 192:	/* rx phy */
-			if(len >= 8){
-				u32int dt = get32(d+4) - (u32int)ctlr->systime;
-				ctlr->systime += dt;
-			}
+			if(len >= 8)
+				updatesystime(ctlr, get32(d+4));
 			break;
 		case 195:	/* rx done */
 			if(d + 2 > b->lim)
@@ -4051,14 +4194,25 @@
 				break;
 			d += d[1];
 			d += 56;
+			/* wet floor */
 		case 193:	/* mpdu rx done */
 			if(d + 4 > b->lim)
 				break;
-			len = get16(d); d += 4;
-			if(d + len + 4 > b->lim)
-				break;
-			if((d[len] & 3) != 3)
-				break;
+			len = get16(d);
+			if(ctlr->mqrx){
+				if(d + 48 + len > b->lim)
+					break;
+				updatesystime(ctlr, get32(d+36));
+				if((d[12] & 3) != 3)
+					break;
+				d += 48;
+			} else {
+				d += 4;
+				if(d + len + 4 > b->lim)
+					break;
+				if((d[len] & 3) != 3)
+					break;
+			}
 			if(ctlr->wifi == nil)
 				break;
 			if(rbplant(ctlr, rx->i) < 0)
@@ -4080,7 +4234,11 @@
 			wakeup(tx);
 		}
 	}
-	csr32w(ctlr, FhRxWptr, ((hw+Nrx-1) % Nrx) & ~7);
+
+	if(ctlr->mqrx){
+		csr32w(ctlr, FhRxQ0Wptr, ((hw+Nrx-1) % Nrx) & ~7);
+	}else
+		csr32w(ctlr, FhRxWptr, ((hw+Nrx-1) % Nrx) & ~7);
 }
 
 static void
@@ -4104,7 +4262,8 @@
 		goto done;
 	csr32w(ctlr, Isr, isr);
 	csr32w(ctlr, FhIsr, fhisr);
-	if((isr & (Iswrx | Ifhrx | Irxperiodic)) || (fhisr & Ifhrx))
+
+	if((isr & (Iswrx | Ifhrx | Irxperiodic | Ialive)) || (fhisr & Ifhrx))
 		receive(ctlr);
 	if(isr & Ierr){
 		ctlr->broken = 1;
@@ -4185,6 +4344,10 @@
 			family = 8000;
 			fwname = "iwm-8265-34";
 			break;
+		case 0x2526:	/* Wireless AC 9260 */
+			family = 9000;
+			fwname = "iwm-9260-34";
+			break;
 		}
 
 		ctlr = malloc(sizeof(Ctlr));
@@ -4203,6 +4366,7 @@
 		ctlr->pdev = pdev;
 		ctlr->fwname = fwname;
 		ctlr->family = family;
+		ctlr->mqrx = family >= 9000;
 
 		if(iwlhead != nil)
 			iwltail->link = ctlr;