896 lines · 20.0 KB · C
RawDownload
/* * Broadcom BCM57xx * Not implemented: *  proper fatal error handling *  multiple rings *  checksum offloading */ #include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "../port/netif.h" #include "etherif.h"#include "ethermii.h" #define dprint(...)	do{ if(debug)print(__VA_ARGS__); }while(0)#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4) typedef struct Ctlr Ctlr;struct Ctlr {	Lock	txlock, imlock;	Ether	*ether;	Ctlr	*next;	Pcidev	*pdev;	u32int	*nic, *status; 	u32int	*recvret, *recvprod, *sendr;	ulong	port;	uint	recvreti, recvprodi, sendri, sendcleani;	Block	**sends;	Block	**rxs;	int	active, duplex;	int	type; 	uint	nobuf;	uint	partial;	uint	rxerr;	uint	qfull;	uint	dmaerr;}; enum {	/* configurable constants */	RxRetRingLen 		= 0x200,	RxProdRingLen 		= 0x200,	TxRingLen 		= 0x200, 	Reset 			= 1<<0,	Enable 			= 1<<1,	Attn 			= 1<<2, 	Pwrctlstat 		= 0x4C, 	MiscHostCtl 		= 0x68,	TaggedStatus		= 1<<9,	IndirAccessEn		= 1<<7,	EnableClockCtl		= 1<<5,	PCIStateRegEn		= 1<<4,	WordSwap		= 1<<3,	ByteSwap		= 1<<2,	MaskPCIInt		= 1<<1,	ClearIntA		= 1<<0, 	Fwmbox			= 0x0b50,	/* magic value exchange */	Fwmagic			= 0x4b657654, 	Dmarwctl 		= 0x6C,	DMAWaterMask		= ~(7<<19),	DMAWaterValue		= 3<<19, 	Memwind		= 0x7C,	MemwindData		= 0x84, 	TxRCB 			= 0x100,	RxRetRCB 		= 0x200, 	InterruptMailbox 		= 0x204, 	RxProdBDRingIdx	= 0x26c,	RxBDRetRingIdx		= 0x284,	TxBDRingHostIdx	= 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,	RandomBackoff		= 0x438,	RxMTU			= 0x43C,	MIComm		= 0x44C,	MIStatus		= 0x450,	MIMode			= 0x454,	RxMACMode		= 0x468,	TxMACMode		= 0x45C,	TxMACLengths		= 0x464,	MACHash		= 0x470,	RxRules			= 0x480, 	RxRulesConf		= 0x500,	LowWaterMax		= 0x504,	LowWaterMaxMask	= ~0xFFFF,	LowWaterMaxValue	= 2, 	TxDataInitiatorMode	= 0xC00,	TxInitiatorConf		= 0x0C08,	TxStats			= 1<<0,	TxInitiatorMask		= 0x0C0C, 	TxDataCompletionMode	= 0x1000,	TxBDSelectorMode	= 0x1400,	TxBDInitiatorMode	= 0x1800,	TxBDCompletionMode	= 0x1C00, 	RxListPlacementMode	= 0x2000,	RxListPlacement		= 0x2010,	RxListPlacementConf	= 0x2014,	RxStats			= 1<<0,	RxListPlacementMask	= 0x2018, 	RxDataBDInitiatorMode	= 0x2400,	RxBDHostAddr		= 0x2450,	RxBDFlags		= 0x2458,	RxBDNIC		= 0x245C,	RxDataCompletionMode	= 0x2800,	RxBDInitiatorMode	= 0x2C00,	RxBDRepl		= 0x2C18, 	RxBDCompletionMode	= 0x3000,	HostCoalMode		= 0x3C00,	HostCoalRxTicks		= 0x3C08,	HostCoalTxTicks		= 0x3C0C,	RxMaxCoalFrames	= 0x3C10,	TxMaxCoalFrames	= 0x3C14,	RxMaxCoalFramesInt	= 0x3C20,	TxMaxCoalFramesInt	= 0x3C24,	StatusBlockHostAddr	= 0x3C38,	FlowAttention		= 0x3C48, 	MemArbiterMode		= 0x4000, 	BufferManMode		= 0x4400, 	MBUFLowWater		= 0x4414,	MBUFHighWater		= 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,	HostTxBDs		= 1<<17,	InterruptOnMAC		= 1<<26, 	MiscConf		= 0x6804,	CoreClockBlocksReset	= 1<<0,	GPHYPwrdnOverride	= 1<<26,	DisableGRCRstOnPpcie	= 1<<29,	TimerMask		= ~0xFF,	TimerValue		= 65<<1,	MiscLocalControl		= 0x6808,	InterruptOnAttn		= 1<<3,	AutoSEEPROM		= 1<<24, 	SwArbit			= 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,}; enum {	b5709,	b5722,	b5751,	b5754,	b5755,	b5756,	b5782,	b5787,	b5906,	Nctlrtype,}; typedef struct Ctlrtype Ctlrtype;struct Ctlrtype {	int	mtu;	int	flag;	char	*name;}; static Ctlrtype cttab[Nctlrtype] = {[b5709]	1514,	0,	"b5709",[b5722]	1514,	0,	"b5722",[b5751]	1514,	0,	"b5751",[b5754]	1514,	0,	"b5754",[b5755]	1514,	0,	"b5755",[b5756]	1514,	0,	"b5756",[b5782]	1514,	0,	"b5782",[b5787]	1514,	0,	"b5787",[b5906]	1514,	0,	"b5906",}; #define csr32(c, r)	((c)->nic[(r)/4]) static Ctlr *bcmhead;static int debug; static char*cname(Ctlr *c){	return cttab[c->type].name;} static longbcmifstat(Ether *edev, void *a, long n, ulong offset){	char *s, *p, *e;	Ctlr *c; 	c = edev->ctlr;	p = s = malloc(READSTR);	e = p + READSTR; 	p = seprint(p, e, "nobuf	%ud\n", c->nobuf);	p = seprint(p, e, "partial	%ud\n", c->partial);	p = seprint(p, e, "rxerr	%ud\n", c->rxerr);	p = seprint(p, e, "qfull	%ud\n", c->qfull);	p = seprint(p, e, "dmaerr	%ud\n", c->dmaerr);	p = seprint(p, e, "type: %s\n", cname(c)); 	USED(p);	n = readstr(offset, a, n, s);	free(s); 	return n;} 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 voidchecklink(Ether *edev){	uint i;	Ctlr *ctlr; 	ctlr = edev->ctlr;	miir(ctlr, Bmsr); /* read twice for current status as per 802.3 */	if(!(miir(ctlr, Bmsr) & BmsrLs)) {		edev->link = 0;		edev->mbps = 1000;		ctlr->duplex = 1;		dprint("bcm: no link\n");		goto out;	}	edev->link = 1;	while((miir(ctlr, Bmsr) & BmsrAnc) == 0)		;	i = miir(ctlr, Mssr);	if(i & (Mssr1000THD | Mssr1000TFD)) {		edev->mbps = 1000;		ctlr->duplex = (i & Mssr1000TFD) != 0;	} else if(i = miir(ctlr, Anlpar), i & (AnaTXHD | AnaTXFD)) {		edev->mbps = 100;		ctlr->duplex = (i & AnaTXFD) != 0;	} else if(i & (Ana10HD | Ana10FD)) {		edev->mbps = 10;		ctlr->duplex = (i & Ana10FD) != 0;	} else {		edev->link = 0;		edev->mbps = 1000;		ctlr->duplex = 1;		dprint("bcm: link partner supports neither 10/100/1000 Mbps\n");		goto out;	}	dprint("bcm: %d Mbps link, %s duplex\n", edev->mbps, ctlr->duplex ? "full" : "half");out:	if(ctlr->duplex)		csr32(ctlr, MACMode) &= ~MACHalfDuplex;	else		csr32(ctlr, MACMode) |= MACHalfDuplex;	if(edev->mbps >= 1000)		csr32(ctlr, MACMode) = (csr32(ctlr, MACMode) & MACPortMask) | MACPortGMII;	else		csr32(ctlr, MACMode) = (csr32(ctlr, MACMode) & MACPortMask) | MACPortMII;	csr32(ctlr, MACEventStatus) |= (1<<4) | (1<<3); /* undocumented bits (sync and config changed) */} static uint*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 & RxRetRingLen-1;	csr32(ctlr, RxBDRetRingIdx) = ctlr->recvreti;} static intreplenish(Ctlr *ctlr){	uint incr;	u32int *next;	Block *bp;		incr = (ctlr->recvprodi + 1) & (RxProdRingLen - 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, RxProdBDRingIdx) = ctlr->recvprodi = incr;	return 0;} static voidbcmreceive(Ether *edev){	uint len;	u32int *pkt;	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) & (TxRingLen - 1);	}	iunlock(&ctlr->txlock);} static voidbcmtransmit(Ether *edev){	uint incr;	u32int *next;	Ctlr *ctlr;	Block *bp;		ctlr = edev->ctlr;	ilock(&ctlr->txlock);	for(;;){		incr = (ctlr->sendri + 1) & (TxRingLen - 1);		if(incr == ctlr->sendcleani) {			dprint("bcm: send queue full\n");			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, TxBDRingHostIdx) = 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 %#.8ux", csr32(ctlr, FlowAttention));

Lines 1–500 of 896.