723 lines · 17.4 KB · C
RawDownload
/* * broadcom bcm57xx bootstrap driver  */ #include "u.h"#include "lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "etherif.h"#include "ethermii.h" #define debug		bcmdebug#define iallocb(x)		allocb(x)#define	etheriq(e, b, frn)	toringbuf(e, (b)->rp, BLEN(b))#define	qget(blah)	fromringbuf(edev) #define dprint(...)	do{ if(debug)print(__VA_ARGS__); }while(0)#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)#define	Pciwaddrl(x)	PCIWADDR(x)#define Pciwaddrh(x)	(sizeof(uintptr)>4? (uvlong)PCIWADDR(x)>>32: 0) typedef struct Ctlr Ctlr;struct Ctlr {	Lock	txlock, imlock;	Ether	*ether;	Ctlr	*next;	Pcidev	*pdev;	ulong	*nic, *status; 	ulong	*recvret, *recvprod, *sendr;	ulong	port;	ulong	recvreti, recvprodi, sendri, sendcleani;	Block	**sends;	Block	**rxs;	int	active, duplex; 	uint	nobuf;	uint	partial;	uint	rxerr;	uint	qfull;	uint	dmaerr;}; enum {	RecvRetRingLen = 0x200,	RecvProdRingLen = 0x200,	SendRingLen = 0x200, 	Reset = 1<<0,	Enable = 1<<1,	Attn = 1<<2,		PowerControlStatus = 0x4C, 	MiscHostCtl = 0x68,	TaggedStatus		= 1<<9,	IndirectAccessEnable	= 1<<7,	EnableClockControl	= 1<<5,	EnablePCIStateRegister	= 1<<4,	WordSwap		= 1<<3,	ByteSwap		= 1<<2,	MaskPCIInt		= 1<<1,	ClearIntA		= 1<<0,		Fwmbox		= 0x0b50,	/* magic value exchange */	Fwmagic		= 0x4b657654, 	DMARWControl = 0x6C,	DMAWatermarkMask = ~(7<<19),	DMAWatermarkValue = 3<<19, 	MemoryWindow = 0x7C,	MemoryWindowData = 0x84,		SendRCB = 0x100,	RecvRetRCB = 0x200,		InterruptMailbox = 0x204,		RecvProdBDRingIndex = 0x26c,	RecvBDRetRingIndex = 0x284,	SendBDRingHostIndex = 0x304,		MACMode = 0x400,	MACPortMask = ~((1<<3)|(1<<2)),	MACPortGMII = 1<<3,	MACPortMII = 1<<2,	MACEnable = (1<<23) | (1<<22) | (1<<21) | (1 << 15) | (1 << 14) | (1<<12) | (1<<11),	MACHalfDuplex = 1<<1,		MACEventStatus = 0x404,	MACEventEnable = 0x408,	MACAddress = 0x410,	EthernetRandomBackoff = 0x438,	ReceiveMTU = 0x43C,	MIComm = 0x44C,	MIStatus = 0x450,	MIMode = 0x454,	ReceiveMACMode = 0x468,	TransmitMACMode = 0x45C,	TransmitMACLengths = 0x464,	MACHash = 0x470,	ReceiveRules = 0x480,		ReceiveRulesConfiguration = 0x500,	LowWatermarkMaximum = 0x504,	LowWatermarkMaxMask = ~0xFFFF,	LowWatermarkMaxValue = 2, 	SendDataInitiatorMode = 0xC00,	SendInitiatorConfiguration = 0x0C08,	SendStats = 1<<0,	SendInitiatorMask = 0x0C0C,		SendDataCompletionMode = 0x1000,	SendBDSelectorMode = 0x1400,	SendBDInitiatorMode = 0x1800,	SendBDCompletionMode = 0x1C00,		ReceiveListPlacementMode = 0x2000,	ReceiveListPlacement = 0x2010,	ReceiveListPlacementConfiguration = 0x2014,	ReceiveStats = 1<<0,	ReceiveListPlacementMask = 0x2018,		ReceiveDataBDInitiatorMode = 0x2400,	ReceiveBDHostAddr = 0x2450,	ReceiveBDFlags = 0x2458,	ReceiveBDNIC = 0x245C,	ReceiveDataCompletionMode = 0x2800,	ReceiveBDInitiatorMode = 0x2C00,	ReceiveBDRepl = 0x2C18,		ReceiveBDCompletionMode = 0x3000,	HostCoalescingMode = 0x3C00,	HostCoalescingRecvTicks = 0x3C08,	HostCoalescingSendTicks = 0x3C0C,	RecvMaxCoalescedFrames = 0x3C10,	SendMaxCoalescedFrames = 0x3C14,	RecvMaxCoalescedFramesInt = 0x3C20,	SendMaxCoalescedFramesInt = 0x3C24,	StatusBlockHostAddr = 0x3C38,	FlowAttention = 0x3C48, 	MemArbiterMode = 0x4000,		BufferManMode = 0x4400,		MBUFLowWatermark = 0x4414,	MBUFHighWatermark = 0x4418,		ReadDMAMode = 0x4800,	ReadDMAStatus = 0x4804,	WriteDMAMode = 0x4C00,	WriteDMAStatus = 0x4C04,		RISCState = 0x5004,	FTQReset = 0x5C00,	MSIMode = 0x6000,		ModeControl = 0x6800,	ByteWordSwap = (1<<4)|(1<<5)|(1<<2),//|(1<<1),	HostStackUp = 1<<16,	HostSendBDs = 1<<17,	InterruptOnMAC = 1<<26,		MiscConfiguration = 0x6804,	CoreClockBlocksReset = 1<<0,	GPHYPowerDownOverride = 1<<26,	DisableGRCResetOnPCIE = 1<<29,	TimerMask = ~0xFF,	TimerValue = 65<<1,	MiscLocalControl = 0x6808,	InterruptOnAttn = 1<<3,	AutoSEEPROM = 1<<24,		SwArbitration = 0x7020,	SwArbitSet1 = 1<<1,	SwArbitWon1 = 1<<9,	Pcitlplpl		= 0x7C00,	/* "lower 1k of the pcie pl regs" ?? */ 	PhyAuxControl = 0x18,	PhyIntStatus = 0x1A,	PhyIntMask = 0x1B,		Updated = 1<<0,	LinkStateChange = 1<<1,	Error = 1<<2,		PacketEnd = 1<<2,	FrameError = 1<<10,}; #define csr32(c, r)	((c)->nic[(r)/4]) static Ctlr *bcmhead;static int debug=1; enum {	Phybusy		= 1<<29,	Phyrdfail		= 1<<28,	Phyrd		= 1<<27,	Phywr		= 1<<26,};Lock miilock; static uintmiiwait(Ctlr *ctlr){	uint i, v; 	for(i = 0; i < 100; i += 5){		microdelay(10);		v = csr32(ctlr, MIComm);		if((v & Phybusy) == 0){			microdelay(5);			return csr32(ctlr, MIComm);		}		microdelay(5);	}	print("#l%d: bcm: miiwait: timeout\n", ctlr->ether->ctlrno);	return ~0;} static intmiir(Ctlr *ctlr, int r){	uint v, phyno; 	phyno = 1;	lock(&miilock);	csr32(ctlr, MIComm) = r<<16 | phyno<<21 | Phyrd | Phybusy;	v = miiwait(ctlr);	unlock(&miilock);	if(v == ~0)		return -1;	if(v & Phyrdfail){		print("#l%d: bcm: miir: fail\n", ctlr->ether->ctlrno);		return -1;	}	return v & 0xffff;} static intmiiw(Ctlr *ctlr, int r, int v){	uint phyno, w; 	phyno = 1;	lock(&miilock);	csr32(ctlr, MIComm) = r<<16 | v&0xffff | phyno<<21 | Phywr | Phybusy;	w = miiwait(ctlr);	unlock(&miilock);	if(w == ~0)		return -1;	return 0;} static ulong*currentrecvret(Ctlr *ctlr){	if(ctlr->recvreti == (ctlr->status[4] & 0xFFFF))		return 0;	return ctlr->recvret + ctlr->recvreti * 8;} static voidconsumerecvret(Ctlr *ctlr){	ctlr->recvreti = ctlr->recvreti+1 & RecvRetRingLen-1;	csr32(ctlr, RecvBDRetRingIndex) = ctlr->recvreti;} static intreplenish(Ctlr *ctlr){	ulong *next, incr;	Block *bp;		incr = (ctlr->recvprodi + 1) & (RecvProdRingLen - 1);	if(incr == (ctlr->status[2] >> 16))		return -1;	bp = iallocb(Rbsz);	if(bp == nil) {		/* iallocb never fails.  this code is unnecessary */		dprint("bcm: out of memory for receive buffers\n");		ctlr->nobuf++;		return -1;	}	next = ctlr->recvprod + ctlr->recvprodi * 8;	memset(next, 0, 32);	next[0] = Pciwaddrh(bp->rp);	next[1] = Pciwaddrl(bp->rp);	next[2] = Rbsz;	next[7] = ctlr->recvprodi;	ctlr->rxs[ctlr->recvprodi] = bp;	coherence();	csr32(ctlr, RecvProdBDRingIndex) = ctlr->recvprodi = incr;	return 0;} static voidbcmreceive(Ether *edev){	ulong *pkt, len;	Ctlr *ctlr;	Block *bp;		ctlr = edev->ctlr;	for(; pkt = currentrecvret(ctlr); replenish(ctlr), consumerecvret(ctlr)) {		bp = ctlr->rxs[pkt[7]];		len = pkt[2] & 0xFFFF;		bp->wp = bp->rp + len;		if((pkt[3] & PacketEnd) == 0){			dprint("bcm: partial frame received -- shouldn't happen\n");			ctlr->partial++;			freeb(bp);			continue;		}		if(pkt[3] & FrameError){			ctlr->rxerr++;			freeb(bp);			continue;		}		etheriq(edev, bp, 1);	}} static voidbcmtransclean(Ether *edev){	Ctlr *ctlr;		ctlr = edev->ctlr;	ilock(&ctlr->txlock);	while(ctlr->sendcleani != (ctlr->status[4] >> 16)) {		freeb(ctlr->sends[ctlr->sendcleani]);		ctlr->sends[ctlr->sendcleani] = nil;		ctlr->sendcleani = (ctlr->sendcleani + 1) & (SendRingLen - 1);	}	iunlock(&ctlr->txlock);} static voidbcmtransmit(Ether *edev){	ulong *next, incr;	Ctlr *ctlr;	Block *bp;		ctlr = edev->ctlr;	ilock(&ctlr->txlock);	for(;;){		incr = (ctlr->sendri + 1) & (SendRingLen - 1);		if(incr == ctlr->sendcleani) {			dprint("bcm: send queue full %ld %ld\n", incr, ctlr->sendcleani);			ctlr->qfull++;			break;		}		bp = qget(edev->oq);		if(bp == nil)			break;		next = ctlr->sendr + ctlr->sendri * 4;		next[0] = Pciwaddrh(bp->rp);		next[1] = Pciwaddrl(bp->rp);		next[2] = (BLEN(bp) << 16) | PacketEnd;		next[3] = 0;		ctlr->sends[ctlr->sendri] = bp;		coherence();		csr32(ctlr, SendBDRingHostIndex) = ctlr->sendri = incr;	}	iunlock(&ctlr->txlock);} static voidbcmerror(Ether *edev){	Ctlr *ctlr;		ctlr = edev->ctlr;	if(csr32(ctlr, FlowAttention)) {		if(csr32(ctlr, FlowAttention) & 0xf8ff8080)			print("bcm: fatal error %#.8lux", csr32(ctlr, FlowAttention));		csr32(ctlr, FlowAttention) = 0;	}	csr32(ctlr, MACEventStatus) = 0; /* worth ignoring */	if(csr32(ctlr, ReadDMAStatus) || csr32(ctlr, WriteDMAStatus)) {		dprint("bcm: DMA error\n");		ctlr->dmaerr++;		csr32(ctlr, ReadDMAStatus) = 0;		csr32(ctlr, WriteDMAStatus) = 0;	}	if(csr32(ctlr, RISCState)) {		if(csr32(ctlr, RISCState) & 0x78000403)			print("bcm: RISC halted %#.8lux", csr32(ctlr, RISCState));		csr32(ctlr, RISCState) = 0;	}} static voidbcminterrupt(Ureg*, void *arg){	ulong status, tag, dummy;	Ether *edev;	Ctlr *ctlr;		edev = arg;	ctlr = edev->ctlr;	ilock(&ctlr->imlock);	dummy = csr32(ctlr, InterruptMailbox);	USED(dummy);	csr32(ctlr, InterruptMailbox) = 1;	status = ctlr->status[0];	tag = ctlr->status[1];	ctlr->status[0] = 0;	if(status & Error)		bcmerror(edev);	if(status & LinkStateChange)		{}//checklink(edev);	bcmreceive(edev);	bcmtransclean(edev);	bcmtransmit(edev);	csr32(ctlr, InterruptMailbox) = tag << 24;	iunlock(&ctlr->imlock);} static voidmem32w(Ctlr *c, uint r, uint v){	pcicfgw32(c->pdev, MemoryWindow, r);	pcicfgw32(c->pdev, MemoryWindowData, v);} static ulongmem32r(Ctlr *c, uint r){	ulong v; 	pcicfgw32(c->pdev, MemoryWindow, r);	v = pcicfgr32(c->pdev, MemoryWindowData);	pcicfgw32(c->pdev, MemoryWindow, 0);	return v;} static intbcmµwait(Ctlr *ctlr, uint to, uint r, uint m, uint v){	int i; 	for(i = 0;; i += 100){		if((csr32(ctlr, r) & m) == v)			return 0;		if(i == to /* µs */)			return -1;		microdelay(100);	}} static intbcminit(Ether *edev){	ulong i, j;	Ctlr *ctlr;		ctlr = edev->ctlr;	dprint("bcm: reset\n");	/* initialization procedure according to the datasheet */	csr32(ctlr, MiscHostCtl) |= MaskPCIInt | ClearIntA;	csr32(ctlr, SwArbitration) |= SwArbitSet1;	if(bcmµwait(ctlr, 2000, SwArbitration, SwArbitWon1, SwArbitWon1) == -1){		print("bcm: arbiter failed to respond\n");		return -1;	}	csr32(ctlr, MemArbiterMode) |= Enable;	csr32(ctlr, MiscHostCtl) = WordSwap | IndirectAccessEnable | EnablePCIStateRegister | EnableClockControl		| MaskPCIInt | ClearIntA;	csr32(ctlr, MemoryWindow) = 0;	mem32w(ctlr, Fwmbox, Fwmagic);	csr32(ctlr, MiscConfiguration) |= GPHYPowerDownOverride | DisableGRCResetOnPCIE | CoreClockBlocksReset;	delay(100);	pcicfgw32(ctlr->pdev, PciPCR, ctlr->pdev->pcr);	/* restore pci bits lost */	csr32(ctlr, MiscHostCtl) |= MaskPCIInt | ClearIntA;	csr32(ctlr, MemArbiterMode) |= Enable;	csr32(ctlr, MiscHostCtl) |= WordSwap | IndirectAccessEnable | EnablePCIStateRegister | EnableClockControl | TaggedStatus;	csr32(ctlr, ModeControl) |= ByteWordSwap;	csr32(ctlr, MACMode) = (csr32(ctlr, MACMode) & MACPortMask) | MACPortGMII;	delay(40);	for(i = 0;; i += 100){		if(mem32r(ctlr, Fwmbox) == ~Fwmagic)			break;		if(i == 20*10000 /* µs */){			print("bcm: fw failed to respond %#.8lux\n", mem32r(ctlr, Fwmbox));			break; //return -1;		}		microdelay(100);	}	/*	 * there appears to be no justification for setting these bits in any driver

Lines 1–500 of 723.