xref: /plan9/sys/src/9/omap/ether9221.c (revision df2dbabf397493e64ca7440c63ccb06027111868)
18e32b400SDavid du Colombier /*
28e32b400SDavid du Colombier  * SMSC 9221 Ethernet driver
38e32b400SDavid du Colombier  * specifically for the ISEE IGEPv2 board,
48e32b400SDavid du Colombier  * where it is assigned to Chip Select 5,
58e32b400SDavid du Colombier  * its registers are at 0x2c000000 (inherited from u-boot),
68e32b400SDavid du Colombier  * and irq is 34 from gpio pin 176, thus gpio module 6.
78e32b400SDavid du Colombier  *
88e32b400SDavid du Colombier  * it's slow due to the use of fifos instead of buffer rings.
98e32b400SDavid du Colombier  * the slow system dma just makes it worse.
108e32b400SDavid du Colombier  *
118e32b400SDavid du Colombier  * igepv2 u-boot uses pin 64 on gpio 3 as an output pin to reset the 9221.
128e32b400SDavid du Colombier  */
138e32b400SDavid du Colombier #include "u.h"
148e32b400SDavid du Colombier #include "../port/lib.h"
158e32b400SDavid du Colombier #include "mem.h"
168e32b400SDavid du Colombier #include "dat.h"
178e32b400SDavid du Colombier #include "fns.h"
188e32b400SDavid du Colombier #include "io.h"
198e32b400SDavid du Colombier #include "../port/error.h"
208e32b400SDavid du Colombier #include "../port/netif.h"
218e32b400SDavid du Colombier 
228e32b400SDavid du Colombier #include "etherif.h"
238e32b400SDavid du Colombier 
248e32b400SDavid du Colombier /* currently using kprocs is a lot slower than not (87 s. to boot vs 60) */
258e32b400SDavid du Colombier #undef USE_KPROCS
268e32b400SDavid du Colombier 
278e32b400SDavid du Colombier enum {
288e32b400SDavid du Colombier 	Vid9221	= 0x9221,
298e32b400SDavid du Colombier 	Slop	= 4,			/* beyond ETHERMAXTU */
308e32b400SDavid du Colombier };
318e32b400SDavid du Colombier 
328e32b400SDavid du Colombier typedef struct Regs Regs;
338e32b400SDavid du Colombier struct Regs {
348e32b400SDavid du Colombier 	/* fifo ports */
358e32b400SDavid du Colombier 	ulong	rxdata;
368e32b400SDavid du Colombier 	uchar	_pad0[0x20 - 4];
378e32b400SDavid du Colombier 	ulong	txdata;
388e32b400SDavid du Colombier 	uchar	_pad1[0x40 - 0x24];
398e32b400SDavid du Colombier 	ulong	rxsts;
408e32b400SDavid du Colombier 	ulong	rxstspeek;
418e32b400SDavid du Colombier 	ulong	txsts;
428e32b400SDavid du Colombier 	ulong	txstspeek;
438e32b400SDavid du Colombier 
448e32b400SDavid du Colombier 	/* control & status */
458e32b400SDavid du Colombier 	ushort	rev;			/* chip revision */
468e32b400SDavid du Colombier 	ushort	id;			/* chip id, 0x9221 */
478e32b400SDavid du Colombier 	ulong	irqcfg;
488e32b400SDavid du Colombier 	ulong	intsts;
498e32b400SDavid du Colombier 	ulong	inten;
508e32b400SDavid du Colombier 	ulong	_pad2;
518e32b400SDavid du Colombier 	ulong	bytetest;
528e32b400SDavid du Colombier 	ulong	fifoint;		/* fifo level interrupts */
538e32b400SDavid du Colombier 	ulong	rxcfg;
548e32b400SDavid du Colombier 	ulong	txcfg;
558e32b400SDavid du Colombier 	ulong	hwcfg;
568e32b400SDavid du Colombier 	ulong	rxdpctl;		/* rx data path control */
578e32b400SDavid du Colombier 	ulong	rxfifoinf;
588e32b400SDavid du Colombier 	ulong	txfifoinf;
598e32b400SDavid du Colombier 	ulong	pmtctl;			/* power mgmt. control */
608e32b400SDavid du Colombier 	ulong	gpiocfg;
618e32b400SDavid du Colombier 	ulong	gptcfg;			/* timer */
628e32b400SDavid du Colombier 	ulong	gptcnt;
638e32b400SDavid du Colombier 	ulong	_pad3;
648e32b400SDavid du Colombier 	ulong	wordswap;
658e32b400SDavid du Colombier 	ulong	freerun; 		/* counters */
668e32b400SDavid du Colombier 	ulong	rxdrop;
678e32b400SDavid du Colombier 
688e32b400SDavid du Colombier 	/*
698e32b400SDavid du Colombier 	 * mac registers are accessed indirectly via the mac csr registers.
708e32b400SDavid du Colombier 	 * phy registers are doubly indirect, via the mac csr mii_acc &
718e32b400SDavid du Colombier 	 * mii_data mac csr registers.
728e32b400SDavid du Colombier 	 */
738e32b400SDavid du Colombier 	ulong	maccsrcmd;		/* mac csr synchronizer */
748e32b400SDavid du Colombier 	ulong	maccsrdata;
758e32b400SDavid du Colombier 	ulong	afccfg;			/* automatic flow control cfg. */
768e32b400SDavid du Colombier 	ulong	eepcmd;			/* eeprom */
778e32b400SDavid du Colombier 	ulong	eepdata;
788e32b400SDavid du Colombier 	/* 0xb8 */
798e32b400SDavid du Colombier };
808e32b400SDavid du Colombier 
818e32b400SDavid du Colombier enum {
828e32b400SDavid du Colombier 	Nstatistics	= 128,
838e32b400SDavid du Colombier };
848e32b400SDavid du Colombier 
858e32b400SDavid du Colombier enum {
868e32b400SDavid du Colombier 	/* txcmda bits */
878e32b400SDavid du Colombier 	Intcompl	= 1<<31,
888e32b400SDavid du Colombier 	Bufendalign	= 3<<24,	/* mask */
898e32b400SDavid du Colombier 	Datastoff	= 037<<16,	/* mask */
908e32b400SDavid du Colombier 	Firstseg	= 1<<13,
918e32b400SDavid du Colombier 	Lastseg		= 1<<12,
928e32b400SDavid du Colombier 	Bufsize		= MASK(11),
938e32b400SDavid du Colombier 
948e32b400SDavid du Colombier 	/* txcmdb bits */
958e32b400SDavid du Colombier 	Pkttag		= MASK(16) << 16,
968e32b400SDavid du Colombier 	Txcksumen	= 1<<14,
978e32b400SDavid du Colombier 	Addcrcdis	= 1<<13,
988e32b400SDavid du Colombier 	Framepaddis	= 1<<12,
998e32b400SDavid du Colombier 	Pktlen		= (1<<1) - 1,	/* mask */
1008e32b400SDavid du Colombier 
1018e32b400SDavid du Colombier 	/* txcfg bits */
1028e32b400SDavid du Colombier 	Txsdump		= 1<<15,	/* flush tx status fifo */
1038e32b400SDavid du Colombier 	Txddump		= 1<<14,	/* flush tx data fifo */
1048e32b400SDavid du Colombier 	Txon		= 1<<1,
1058e32b400SDavid du Colombier 	Stoptx		= 1<<0,
1068e32b400SDavid du Colombier 
1078e32b400SDavid du Colombier 	/* hwcfg bits */
1088e32b400SDavid du Colombier 	Mbo		= 1<<20,	/* must be one */
1098e32b400SDavid du Colombier 	Srstto		= 1<<1,		/* soft reset time-out */
1108e32b400SDavid du Colombier 	Srst		= 1<<0,
1118e32b400SDavid du Colombier 
1128e32b400SDavid du Colombier 	/* rxcfg bits */
1138e32b400SDavid du Colombier 	Rxdmacntshift	= 16,		/* ulong count, 12 bits wide */
1148e32b400SDavid du Colombier 	Rxdmacntmask	= MASK(12) << Rxdmacntshift,
1158e32b400SDavid du Colombier 	Rxdump		= 1<<15,	/* flush rx fifos */
1168e32b400SDavid du Colombier 
1178e32b400SDavid du Colombier 	/* rxsts bits */
1188e32b400SDavid du Colombier 	Rxpktlenshift	= 16,		/* byte count */
1198e32b400SDavid du Colombier 	Rxpktlenmask	= MASK(14) << Rxpktlenshift,
1208e32b400SDavid du Colombier 	Rxerr		= 1<<15,
1218e32b400SDavid du Colombier 
1228e32b400SDavid du Colombier 	/* rxfifoinf bits */
1238e32b400SDavid du Colombier 	Rxstsusedshift	= 16,		/* ulong count */
1248e32b400SDavid du Colombier 	Rxstsusedmask	= MASK(8) << Rxstsusedshift,
1258e32b400SDavid du Colombier 	Rxdatausedmask	= MASK(16),	/* byte count */
1268e32b400SDavid du Colombier 
1278e32b400SDavid du Colombier 	/* txfifoinf bits */
1288e32b400SDavid du Colombier 	Txstsusedshift	= 16,		/* ulong count */
1298e32b400SDavid du Colombier 	Txstsusedmask	= MASK(8) << Txstsusedshift,
1308e32b400SDavid du Colombier 	Txdatafreemask	= MASK(16),	/* byte count */
1318e32b400SDavid du Colombier 
1328e32b400SDavid du Colombier 	/* pmtctl bits */
1338e32b400SDavid du Colombier 	Dready		= 1<<0,
1348e32b400SDavid du Colombier 
1358e32b400SDavid du Colombier 	/* maccsrcmd bits */
1368e32b400SDavid du Colombier 	Csrbusy		= 1<<31,
1378e32b400SDavid du Colombier 	Csrread		= 1<<30,	/* not write */
1388e32b400SDavid du Colombier 	Csraddrshift	= 0,
1398e32b400SDavid du Colombier 	Csraddrmask	= MASK(8) - 1,
1408e32b400SDavid du Colombier 
1418e32b400SDavid du Colombier 	/* mac registers' indices */
1428e32b400SDavid du Colombier 	Maccr		= 1,
1438e32b400SDavid du Colombier 	Macaddrh,
1448e32b400SDavid du Colombier 	Macaddrl,
1458e32b400SDavid du Colombier 	Machashh,
1468e32b400SDavid du Colombier 	Machashl,
1478e32b400SDavid du Colombier 	Macmiiacc,			/* for doubly-indirect phy access */
1488e32b400SDavid du Colombier 	Macmiidata,
1498e32b400SDavid du Colombier 	Macflow,
1508e32b400SDavid du Colombier 	Macvlan1,
1518e32b400SDavid du Colombier 	Macvlan2,
1528e32b400SDavid du Colombier 	Macwuff,
1538e32b400SDavid du Colombier 	Macwucsr,
1548e32b400SDavid du Colombier 	Maccoe,
1558e32b400SDavid du Colombier 
1568e32b400SDavid du Colombier 	/* Maccr bits */
1578e32b400SDavid du Colombier 	Rxall		= 1<<31,
1588e32b400SDavid du Colombier 	Rcvown		= 1<<23,	/* don't receive own transmissions */
1598e32b400SDavid du Colombier 	Fdpx		= 1<<20,	/* full duplex */
1608e32b400SDavid du Colombier 	Mcpas		= 1<<19,	/* pass all multicast */
1618e32b400SDavid du Colombier 	Prms		= 1<<18,	/* promiscuous */
1628e32b400SDavid du Colombier 	Ho		= 1<<15,	/* hash-only filtering */
1638e32b400SDavid du Colombier 	Hpfilt		= 1<<13,	/* hash/perfect filtering */
1648e32b400SDavid du Colombier 	Padstr		= 1<<8,		/* strip padding & fcs (crc) */
1658e32b400SDavid du Colombier 	Txen		= 1<<3,
1668e32b400SDavid du Colombier 	Rxen		= 1<<2,
1678e32b400SDavid du Colombier 
1688e32b400SDavid du Colombier 	/* irqcfg bits */
1698e32b400SDavid du Colombier 	Irqdeasclr	= 1<<14,	/* deassertion intv'l clear */
1708e32b400SDavid du Colombier 	Irqdeassts	= 1<<13,	/* deassertion intv'l status */
1718e32b400SDavid du Colombier 	Irqint		= 1<<12,	/* intr being asserted? (ro) */
1728e32b400SDavid du Colombier 	Irqen		= 1<<8,
1738e32b400SDavid du Colombier 	Irqpol		= 1<<4,		/* irq output is active high */
1748e32b400SDavid du Colombier 	Irqpushpull	= 1<<0,		/* irq output is push/pull driver */
1758e32b400SDavid du Colombier 
1768e32b400SDavid du Colombier 	/* intsts/inten bits */
1778e32b400SDavid du Colombier 	Swint		= 1<<31,	/* generate an interrupt */
1788e32b400SDavid du Colombier 	Txstop		= 1<<25,
1798e32b400SDavid du Colombier 	Rxstop		= 1<<24,
1808e32b400SDavid du Colombier 	Txioc		= 1<<21,
1818e32b400SDavid du Colombier 	Rxdma		= 1<<20,
1828e32b400SDavid du Colombier 	Gptimer		= 1<<19,
1838e32b400SDavid du Colombier 	Phy		= 1<<18,
1848e32b400SDavid du Colombier 	Rxe		= 1<<14,	/* errors */
1858e32b400SDavid du Colombier 	Txe		= 1<<13,
1868e32b400SDavid du Colombier 	Tdfo		= 1<<10,	/* tx data fifo overrun */
1878e32b400SDavid du Colombier 	Tdfa		= 1<<9,		/* tx data fifo available */
1888e32b400SDavid du Colombier 	Tsff		= 1<<8,		/* tx status fifo full */
1898e32b400SDavid du Colombier 	Tsfl		= 1<<7,		/* tx status fifo level */
1908e32b400SDavid du Colombier 	Rsff		= 1<<4,		/* rx status fifo full */
1918e32b400SDavid du Colombier 	Rsfl		= 1<<3,		/* rx status fifo level */
1928e32b400SDavid du Colombier 
1938e32b400SDavid du Colombier 	/* eepcmd bits */
1948e32b400SDavid du Colombier 	Epcbusy		= 1<<31,
1958e32b400SDavid du Colombier 	Epccmdshift	= 28,		/* interesting one is Reload (7) */
1968e32b400SDavid du Colombier 	Epctimeout	= 1<<9,
1978e32b400SDavid du Colombier 	Epcmacloaded	= 1<<8,
1988e32b400SDavid du Colombier 	Epcaddrshift	= 0,
1998e32b400SDavid du Colombier };
2008e32b400SDavid du Colombier 
2018e32b400SDavid du Colombier enum {
2028e32b400SDavid du Colombier 	Rxintrs		= Rsff | Rsfl | Rxe,
2038e32b400SDavid du Colombier 	Txintrs		= Tsff | Tsfl | Txe | Txioc,
2048e32b400SDavid du Colombier };
2058e32b400SDavid du Colombier 
2068e32b400SDavid du Colombier /* wake-up frame filter */
2078e32b400SDavid du Colombier struct Wakeup {
2088e32b400SDavid du Colombier 	ulong	bytemask[4];		/* index is filter # */
2098e32b400SDavid du Colombier 	uchar	filt0cmd;		/* filter 0 command */
2108e32b400SDavid du Colombier 	uchar	_pad0;
2118e32b400SDavid du Colombier 	uchar	filt1cmd;
2128e32b400SDavid du Colombier 	uchar	_pad1;
2138e32b400SDavid du Colombier 	uchar	filt2cmd;
2148e32b400SDavid du Colombier 	uchar	_pad2;
2158e32b400SDavid du Colombier 	uchar	filt3cmd;
2168e32b400SDavid du Colombier 	uchar	_pad3;
2178e32b400SDavid du Colombier 	uchar	offset[4];		/* index is filter # */
2188e32b400SDavid du Colombier 	ushort	crc16[4];		/* " */
2198e32b400SDavid du Colombier };
2208e32b400SDavid du Colombier 
2218e32b400SDavid du Colombier typedef struct Ctlr Ctlr;
2228e32b400SDavid du Colombier struct Ctlr {
2238e32b400SDavid du Colombier 	int	port;
2248e32b400SDavid du Colombier 	Ctlr*	next;
2258e32b400SDavid du Colombier 	Ether*	edev;
2268e32b400SDavid du Colombier 	Regs*	regs;
2278e32b400SDavid du Colombier 	int	active;
2288e32b400SDavid du Colombier 	int	started;
2298e32b400SDavid du Colombier 	int	inited;
2308e32b400SDavid du Colombier 	int	id;
2318e32b400SDavid du Colombier 	int	cls;
2328e32b400SDavid du Colombier 	ushort	eeprom[0x40];
2338e32b400SDavid du Colombier 
2348e32b400SDavid du Colombier 	QLock	alock;			/* attach */
2358e32b400SDavid du Colombier 	int	nrb;			/* how many this Ctlr has in the pool */
2368e32b400SDavid du Colombier 
2378e32b400SDavid du Colombier 	int*	nic;
2388e32b400SDavid du Colombier 	Lock	imlock;
2398e32b400SDavid du Colombier 	int	im;			/* interrupt mask */
2408e32b400SDavid du Colombier 
2418e32b400SDavid du Colombier //	Mii*	mii;
2428e32b400SDavid du Colombier //	Rendez	lrendez;
2438e32b400SDavid du Colombier 	int	lim;
2448e32b400SDavid du Colombier 
2458e32b400SDavid du Colombier 	int	link;
2468e32b400SDavid du Colombier 
2478e32b400SDavid du Colombier 	QLock	slock;
2488e32b400SDavid du Colombier 	uint	statistics[Nstatistics];
2498e32b400SDavid du Colombier 	uint	lsleep;
2508e32b400SDavid du Colombier 	uint	lintr;
2518e32b400SDavid du Colombier 	uint	rsleep;
2528e32b400SDavid du Colombier 	uint	rintr;
2538e32b400SDavid du Colombier 	int	tsleep;
2548e32b400SDavid du Colombier 	uint	tintr;
2558e32b400SDavid du Colombier 
2568e32b400SDavid du Colombier 	uchar	ra[Eaddrlen];		/* receive address */
2578e32b400SDavid du Colombier 	ulong	mta[128];		/* multicast table array */
2588e32b400SDavid du Colombier 
2598e32b400SDavid du Colombier 	Rendez	rrendez;
2608e32b400SDavid du Colombier 	int	gotinput;
2618e32b400SDavid du Colombier 	int	rdcpydone;
2628e32b400SDavid du Colombier 
2638e32b400SDavid du Colombier 	Rendez	trendez;
2648e32b400SDavid du Colombier 	int	gotoutput;
2658e32b400SDavid du Colombier 	int	wrcpydone;
2668e32b400SDavid du Colombier 
2678e32b400SDavid du Colombier 	Lock	tlock;
2688e32b400SDavid du Colombier };
2698e32b400SDavid du Colombier 
2708e32b400SDavid du Colombier #define csr32r(c, r)	(*((c)->nic+((r)/4)))
2718e32b400SDavid du Colombier #define csr32w(c, r, v)	(*((c)->nic+((r)/4)) = (v))
2728e32b400SDavid du Colombier 
2738e32b400SDavid du Colombier static Ctlr *smcctlrhead, *smcctlrtail;
2748e32b400SDavid du Colombier 
2758e32b400SDavid du Colombier static char* statistics[Nstatistics] = { "dummy", };
2768e32b400SDavid du Colombier 
2778e32b400SDavid du Colombier static uchar mymac[] = { 0xb0, 0x0f, 0xba, 0xbe, 0x00, 0x00, };
2788e32b400SDavid du Colombier 
2798e32b400SDavid du Colombier static void etherclock(void);
2808e32b400SDavid du Colombier static void smcreceive(Ether *edev);
2818e32b400SDavid du Colombier static void smcinterrupt(Ureg*, void* arg);
2828e32b400SDavid du Colombier 
2838e32b400SDavid du Colombier static Ether *thisether;
2848e32b400SDavid du Colombier static int attached;
2858e32b400SDavid du Colombier 
2868e32b400SDavid du Colombier static void
smconce(Ether * edev)2878e32b400SDavid du Colombier smconce(Ether *edev)
2888e32b400SDavid du Colombier {
2898e32b400SDavid du Colombier 	static int beenhere;
2908e32b400SDavid du Colombier 	static Lock l;
2918e32b400SDavid du Colombier 
2928e32b400SDavid du Colombier 	ilock(&l);
2938e32b400SDavid du Colombier 	if (!beenhere && edev != nil) {
2948e32b400SDavid du Colombier 		beenhere = 1;
2958e32b400SDavid du Colombier 		/* simulate interrupts if we don't know the irq */
2968e32b400SDavid du Colombier 		if (edev->irq < 0) {		/* poll as backup */
2978e32b400SDavid du Colombier 			thisether = edev;
2988e32b400SDavid du Colombier 			addclock0link(etherclock, 1000/HZ);
2998e32b400SDavid du Colombier 			iprint(" polling");
3008e32b400SDavid du Colombier 		}
3018e32b400SDavid du Colombier 	}
3028e32b400SDavid du Colombier 	iunlock(&l);
3038e32b400SDavid du Colombier }
3048e32b400SDavid du Colombier 
3058e32b400SDavid du Colombier /*
3068e32b400SDavid du Colombier  * indirect (mac) register access
3078e32b400SDavid du Colombier  */
3088e32b400SDavid du Colombier 
3098e32b400SDavid du Colombier static void
macwait(Regs * regs)3108e32b400SDavid du Colombier macwait(Regs *regs)
3118e32b400SDavid du Colombier {
3128e32b400SDavid du Colombier 	long bound;
3138e32b400SDavid du Colombier 
314*df2dbabfSDavid du Colombier 	for (bound = 400*Mhz; regs->maccsrcmd & Csrbusy && bound > 0; bound--)
3158e32b400SDavid du Colombier 		;
3168e32b400SDavid du Colombier 	if (bound <= 0)
3178e32b400SDavid du Colombier 		iprint("smc: mac registers didn't come ready\n");
3188e32b400SDavid du Colombier }
3198e32b400SDavid du Colombier 
3208e32b400SDavid du Colombier static ulong
macrd(Regs * regs,uchar index)3218e32b400SDavid du Colombier macrd(Regs *regs, uchar index)
3228e32b400SDavid du Colombier {
3238e32b400SDavid du Colombier 	macwait(regs);
3248e32b400SDavid du Colombier 	regs->maccsrcmd = Csrbusy | Csrread | index;
3258e32b400SDavid du Colombier 	coherence();		/* back-to-back write/read delay per §6.2.1 */
3268e32b400SDavid du Colombier 	macwait(regs);
3278e32b400SDavid du Colombier 	return regs->maccsrdata;
3288e32b400SDavid du Colombier }
3298e32b400SDavid du Colombier 
3308e32b400SDavid du Colombier static void
macwr(Regs * regs,uchar index,ulong val)3318e32b400SDavid du Colombier macwr(Regs *regs, uchar index, ulong val)
3328e32b400SDavid du Colombier {
3338e32b400SDavid du Colombier 	macwait(regs);
3348e32b400SDavid du Colombier 	regs->maccsrdata = val;
3358e32b400SDavid du Colombier 	regs->maccsrcmd = Csrbusy | index;	/* fire */
3368e32b400SDavid du Colombier 	macwait(regs);
3378e32b400SDavid du Colombier }
3388e32b400SDavid du Colombier 
3398e32b400SDavid du Colombier 
3408e32b400SDavid du Colombier static long
smcifstat(Ether * edev,void * a,long n,ulong offset)3418e32b400SDavid du Colombier smcifstat(Ether* edev, void* a, long n, ulong offset)
3428e32b400SDavid du Colombier {
3438e32b400SDavid du Colombier 	Ctlr *ctlr;
3448e32b400SDavid du Colombier 	char *p, *s;
3458e32b400SDavid du Colombier 	int i, l, r;
3468e32b400SDavid du Colombier 
3478e32b400SDavid du Colombier 	ctlr = edev->ctlr;
3488e32b400SDavid du Colombier 	qlock(&ctlr->slock);
3498e32b400SDavid du Colombier 	p = malloc(READSTR);
3508e32b400SDavid du Colombier 	l = 0;
3518e32b400SDavid du Colombier 	for(i = 0; i < Nstatistics; i++){
3528e32b400SDavid du Colombier 		// read regs->rxdrop TODO
3538e32b400SDavid du Colombier 		r = 0;
3548e32b400SDavid du Colombier 		if((s = statistics[i]) == nil)
3558e32b400SDavid du Colombier 			continue;
3568e32b400SDavid du Colombier 		switch(i){
3578e32b400SDavid du Colombier 		default:
3588e32b400SDavid du Colombier 			ctlr->statistics[i] += r;
3598e32b400SDavid du Colombier 			if(ctlr->statistics[i] == 0)
3608e32b400SDavid du Colombier 				continue;
3618e32b400SDavid du Colombier 			l += snprint(p+l, READSTR-l, "%s: %ud %ud\n",
3628e32b400SDavid du Colombier 				s, ctlr->statistics[i], r);
3638e32b400SDavid du Colombier 			break;
3648e32b400SDavid du Colombier 		}
3658e32b400SDavid du Colombier 	}
3668e32b400SDavid du Colombier 
3678e32b400SDavid du Colombier 	l += snprint(p+l, READSTR-l, "lintr: %ud %ud\n",
3688e32b400SDavid du Colombier 		ctlr->lintr, ctlr->lsleep);
3698e32b400SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rintr: %ud %ud\n",
3708e32b400SDavid du Colombier 		ctlr->rintr, ctlr->rsleep);
3718e32b400SDavid du Colombier 	l += snprint(p+l, READSTR-l, "tintr: %ud %ud\n",
3728e32b400SDavid du Colombier 		ctlr->tintr, ctlr->tsleep);
3738e32b400SDavid du Colombier 
3748e32b400SDavid du Colombier 	l += snprint(p+l, READSTR-l, "eeprom:");
3758e32b400SDavid du Colombier 	for(i = 0; i < 0x40; i++){
3768e32b400SDavid du Colombier 		if(i && ((i & 0x07) == 0))
3778e32b400SDavid du Colombier 			l += snprint(p+l, READSTR-l, "\n       ");
3788e32b400SDavid du Colombier 		l += snprint(p+l, READSTR-l, " %4.4uX", ctlr->eeprom[i]);
3798e32b400SDavid du Colombier 	}
3808e32b400SDavid du Colombier 	l += snprint(p+l, READSTR-l, "\n");
3818e32b400SDavid du Colombier 	USED(l);
3828e32b400SDavid du Colombier 
3838e32b400SDavid du Colombier 	n = readstr(offset, a, n, p);
3848e32b400SDavid du Colombier 	free(p);
3858e32b400SDavid du Colombier 	qunlock(&ctlr->slock);
3868e32b400SDavid du Colombier 
3878e32b400SDavid du Colombier 	return n;
3888e32b400SDavid du Colombier }
3898e32b400SDavid du Colombier 
3908e32b400SDavid du Colombier static void
smcpromiscuous(void * arg,int on)3918e32b400SDavid du Colombier smcpromiscuous(void* arg, int on)
3928e32b400SDavid du Colombier {
3938e32b400SDavid du Colombier 	int rctl;
3948e32b400SDavid du Colombier 	Ctlr *ctlr;
3958e32b400SDavid du Colombier 	Ether *edev;
3968e32b400SDavid du Colombier 	Regs *regs;
3978e32b400SDavid du Colombier 
3988e32b400SDavid du Colombier 	edev = arg;
3998e32b400SDavid du Colombier 	ctlr = edev->ctlr;
4008e32b400SDavid du Colombier 	regs = ctlr->regs;
4018e32b400SDavid du Colombier 	rctl = macrd(regs, Maccr);
4028e32b400SDavid du Colombier 	if(on)
4038e32b400SDavid du Colombier 		rctl |= Prms;
4048e32b400SDavid du Colombier 	else
4058e32b400SDavid du Colombier 		rctl &= ~Prms;
4068e32b400SDavid du Colombier 	macwr(regs, Maccr, rctl);
4078e32b400SDavid du Colombier }
4088e32b400SDavid du Colombier 
4098e32b400SDavid du Colombier static void
smcmulticast(void *,uchar *,int)4108e32b400SDavid du Colombier smcmulticast(void*, uchar*, int)
4118e32b400SDavid du Colombier {
4128e32b400SDavid du Colombier 	/* nothing to do, we allow all multicast packets in */
4138e32b400SDavid du Colombier }
4148e32b400SDavid du Colombier 
4158e32b400SDavid du Colombier static int
iswrcpydone(void * arg)4168e32b400SDavid du Colombier iswrcpydone(void *arg)
4178e32b400SDavid du Colombier {
4188e32b400SDavid du Colombier 	return ((Ctlr *)arg)->wrcpydone;
4198e32b400SDavid du Colombier }
4208e32b400SDavid du Colombier 
4218e32b400SDavid du Colombier static int
smctxstart(Ctlr * ctlr,uchar * ubuf,uint len)4228e32b400SDavid du Colombier smctxstart(Ctlr *ctlr, uchar *ubuf, uint len)
4238e32b400SDavid du Colombier {
4248e32b400SDavid du Colombier 	uint wds, ruplen;
4258e32b400SDavid du Colombier 	ulong *wdp, *txdp;
4268e32b400SDavid du Colombier 	Regs *regs;
4278e32b400SDavid du Colombier 	static ulong buf[ROUNDUP(ETHERMAXTU, sizeof(ulong)) / sizeof(ulong)];
4288e32b400SDavid du Colombier 
4298e32b400SDavid du Colombier 	if (!ctlr->inited) {
4308e32b400SDavid du Colombier 		iprint("smctxstart: too soon to send\n");
4318e32b400SDavid du Colombier 		return -1;		/* toss it */
4328e32b400SDavid du Colombier 	}
4338e32b400SDavid du Colombier 	regs = ctlr->regs;
4348e32b400SDavid du Colombier 
4358e32b400SDavid du Colombier 	/* is there room for a packet in the tx data fifo? */
4368e32b400SDavid du Colombier 	if (len < ETHERMINTU)
4378e32b400SDavid du Colombier 		iprint("sending too-short (%d) pkt\n", len);
4388e32b400SDavid du Colombier 	else if (len > ETHERMAXTU)
4398e32b400SDavid du Colombier 		iprint("sending jumbo (%d) pkt\n", len);
4408e32b400SDavid du Colombier 
4418e32b400SDavid du Colombier 	ruplen = ROUNDUP(len, sizeof(ulong));
4428e32b400SDavid du Colombier 	coherence();	/* back-to-back read/read delay per §6.2.2 */
4438e32b400SDavid du Colombier 	if ((regs->txfifoinf & Txdatafreemask) < ruplen + 2*sizeof(ulong))
4448e32b400SDavid du Colombier 		return -1;	/* not enough room for data + command words */
4458e32b400SDavid du Colombier 
4468e32b400SDavid du Colombier 	if ((uintptr)ubuf & MASK(2)) {		/* ensure word alignment */
4478e32b400SDavid du Colombier 		memmove(buf, ubuf, len);
4488e32b400SDavid du Colombier 		ubuf = (uchar *)buf;
4498e32b400SDavid du Colombier 	}
4508e32b400SDavid du Colombier 
4518e32b400SDavid du Colombier 	/* tx cmd a: length is bytes in this buffer */
4528e32b400SDavid du Colombier 	txdp = &regs->txdata;
4538e32b400SDavid du Colombier 	*txdp = Intcompl | Firstseg | Lastseg | len;
4548e32b400SDavid du Colombier 	/* tx cmd b: length is bytes in this packet (could be multiple buf.s) */
4558e32b400SDavid du Colombier 	*txdp = len;
4568e32b400SDavid du Colombier 
4578e32b400SDavid du Colombier 	/* shovel pkt into tx fifo, which triggers transmission due to Txon */
4588e32b400SDavid du Colombier 	wdp = (ulong *)ubuf;
4598e32b400SDavid du Colombier 	for (wds = ruplen / sizeof(ulong) + 1; --wds > 0; )
4608e32b400SDavid du Colombier 		*txdp = *wdp++;
4618e32b400SDavid du Colombier 
4628e32b400SDavid du Colombier 	regs->intsts = Txintrs;		/* dismiss intr */
4638e32b400SDavid du Colombier 	coherence();
4648e32b400SDavid du Colombier 	regs->inten |= Txintrs;
4658e32b400SDavid du Colombier 	coherence();		/* back-to-back write/read delay per §6.2.1 */
4668e32b400SDavid du Colombier 	return 0;
4678e32b400SDavid du Colombier }
4688e32b400SDavid du Colombier 
4698e32b400SDavid du Colombier static void
smctransmit(Ether * edev)4708e32b400SDavid du Colombier smctransmit(Ether* edev)
4718e32b400SDavid du Colombier {
4728e32b400SDavid du Colombier 	Block *bp;
4738e32b400SDavid du Colombier 	Ctlr *ctlr;
4748e32b400SDavid du Colombier 
4758e32b400SDavid du Colombier 	ctlr = edev->ctlr;
4768e32b400SDavid du Colombier 	if (ctlr == nil)
4778e32b400SDavid du Colombier 		panic("smctransmit: nil ctlr");
4788e32b400SDavid du Colombier 	ilock(&ctlr->tlock);
4798e32b400SDavid du Colombier 	/*
4808e32b400SDavid du Colombier 	 * Try to fill the chip's buffers back up, via the tx fifo.
4818e32b400SDavid du Colombier 	 */
4828e32b400SDavid du Colombier 	while ((bp = qget(edev->oq)) != nil)
4838e32b400SDavid du Colombier 		if (smctxstart(ctlr, bp->rp, BLEN(bp)) < 0) {
4848e32b400SDavid du Colombier 			qputback(edev->oq, bp);	/* retry the block later */
4858e32b400SDavid du Colombier 			iprint("smctransmit: tx data fifo full\n");
4868e32b400SDavid du Colombier 			break;
4878e32b400SDavid du Colombier 		} else
4888e32b400SDavid du Colombier 			freeb(bp);
4898e32b400SDavid du Colombier 	iunlock(&ctlr->tlock);
4908e32b400SDavid du Colombier }
4918e32b400SDavid du Colombier 
4928e32b400SDavid du Colombier static void
smctransmitcall(Ether * edev)4938e32b400SDavid du Colombier smctransmitcall(Ether *edev)		/* called from devether.c */
4948e32b400SDavid du Colombier {
4958e32b400SDavid du Colombier 	Ctlr *ctlr;
4968e32b400SDavid du Colombier 
4978e32b400SDavid du Colombier 	ctlr = edev->ctlr;
4988e32b400SDavid du Colombier 	ctlr->gotoutput = 1;
4998e32b400SDavid du Colombier #ifdef USE_KPROCS
5008e32b400SDavid du Colombier 	wakeup(&ctlr->trendez);
5018e32b400SDavid du Colombier #else
5028e32b400SDavid du Colombier 	smctransmit(edev);
5038e32b400SDavid du Colombier #endif
5048e32b400SDavid du Colombier }
5058e32b400SDavid du Colombier 
5068e32b400SDavid du Colombier static int
smcrim(void * ctlr)5078e32b400SDavid du Colombier smcrim(void* ctlr)
5088e32b400SDavid du Colombier {
5098e32b400SDavid du Colombier 	return ((Ctlr*)ctlr)->gotinput;
5108e32b400SDavid du Colombier }
5118e32b400SDavid du Colombier 
5128e32b400SDavid du Colombier static void
smcrproc(void * arg)5138e32b400SDavid du Colombier smcrproc(void* arg)
5148e32b400SDavid du Colombier {
5158e32b400SDavid du Colombier 	Ctlr *ctlr;
5168e32b400SDavid du Colombier 	Ether *edev;
5178e32b400SDavid du Colombier 
5188e32b400SDavid du Colombier 	edev = arg;
5198e32b400SDavid du Colombier 	ctlr = edev->ctlr;
5208e32b400SDavid du Colombier 	for(;;){
5218e32b400SDavid du Colombier 		ctlr->rsleep++;
5228e32b400SDavid du Colombier 		sleep(&ctlr->rrendez, smcrim, ctlr);
5238e32b400SDavid du Colombier 
5248e32b400SDavid du Colombier 		/* process any newly-arrived packets and pass to etheriq */
5258e32b400SDavid du Colombier 		ctlr->gotinput = 0;
5268e32b400SDavid du Colombier 		smcreceive(edev);
5278e32b400SDavid du Colombier 	}
5288e32b400SDavid du Colombier }
5298e32b400SDavid du Colombier 
5308e32b400SDavid du Colombier static int
smcgotout(void * ctlr)5318e32b400SDavid du Colombier smcgotout(void* ctlr)
5328e32b400SDavid du Colombier {
5338e32b400SDavid du Colombier 	return ((Ctlr*)ctlr)->gotoutput;
5348e32b400SDavid du Colombier }
5358e32b400SDavid du Colombier 
5368e32b400SDavid du Colombier static void
smctproc(void * arg)5378e32b400SDavid du Colombier smctproc(void* arg)
5388e32b400SDavid du Colombier {
5398e32b400SDavid du Colombier 	Ctlr *ctlr;
5408e32b400SDavid du Colombier 	Ether *edev;
5418e32b400SDavid du Colombier 
5428e32b400SDavid du Colombier 	edev = arg;
5438e32b400SDavid du Colombier 	ctlr = edev->ctlr;
5448e32b400SDavid du Colombier 	for(;;){
5458e32b400SDavid du Colombier 		ctlr->tsleep++;
5468e32b400SDavid du Colombier 		sleep(&ctlr->trendez, smcgotout, ctlr);
5478e32b400SDavid du Colombier 
5488e32b400SDavid du Colombier 		/* process any newly-arrived packets and pass to etheriq */
5498e32b400SDavid du Colombier 		ctlr->gotoutput = 0;
5508e32b400SDavid du Colombier 		smctransmit(edev);
5518e32b400SDavid du Colombier 	}
5528e32b400SDavid du Colombier }
5538e32b400SDavid du Colombier 
5548e32b400SDavid du Colombier void	gpioirqclr(void);
5558e32b400SDavid du Colombier 
5568e32b400SDavid du Colombier static void
smcattach(Ether * edev)5578e32b400SDavid du Colombier smcattach(Ether* edev)
5588e32b400SDavid du Colombier {
5598e32b400SDavid du Colombier #ifdef USE_KPROCS
5608e32b400SDavid du Colombier 	char name[KNAMELEN];
5618e32b400SDavid du Colombier #endif
5628e32b400SDavid du Colombier 	Ctlr *ctlr;
5638e32b400SDavid du Colombier 
5648e32b400SDavid du Colombier 	ctlr = edev->ctlr;
5658e32b400SDavid du Colombier 	qlock(&ctlr->alock);
5668e32b400SDavid du Colombier 	if(waserror()){
5678e32b400SDavid du Colombier 		qunlock(&ctlr->alock);
5688e32b400SDavid du Colombier 		nexterror();
5698e32b400SDavid du Colombier 	}
5708e32b400SDavid du Colombier 	if (!ctlr->inited) {
5718e32b400SDavid du Colombier 		ctlr->inited = 1;
5728e32b400SDavid du Colombier #ifdef USE_KPROCS
5738e32b400SDavid du Colombier 		snprint(name, KNAMELEN, "#l%drproc", edev->ctlrno);
5748e32b400SDavid du Colombier 		kproc(name, smcrproc, edev);
5758e32b400SDavid du Colombier 
5768e32b400SDavid du Colombier 		snprint(name, KNAMELEN, "#l%dtproc", edev->ctlrno);
5778e32b400SDavid du Colombier 		kproc(name, smctproc, edev);
5788e32b400SDavid du Colombier #endif
5798e32b400SDavid du Colombier 
5808e32b400SDavid du Colombier iprint("smcattach:");
5818e32b400SDavid du Colombier #ifdef USE_KPROCS
5828e32b400SDavid du Colombier iprint(" with kprocs");
5838e32b400SDavid du Colombier #else
5848e32b400SDavid du Colombier iprint(" no kprocs");
5858e32b400SDavid du Colombier #endif
5868e32b400SDavid du Colombier iprint(", no dma");
5878e32b400SDavid du Colombier 		/* can now accept real or simulated interrupts */
5888e32b400SDavid du Colombier 
5898e32b400SDavid du Colombier 		smconce(edev);
5908e32b400SDavid du Colombier 		attached = 1;
5918e32b400SDavid du Colombier iprint("\n");
5928e32b400SDavid du Colombier 	}
5938e32b400SDavid du Colombier 	qunlock(&ctlr->alock);
5948e32b400SDavid du Colombier 	poperror();
5958e32b400SDavid du Colombier }
5968e32b400SDavid du Colombier 
5978e32b400SDavid du Colombier static int
isrdcpydone(void * arg)5988e32b400SDavid du Colombier isrdcpydone(void *arg)
5998e32b400SDavid du Colombier {
6008e32b400SDavid du Colombier 	return ((Ctlr *)arg)->rdcpydone;
6018e32b400SDavid du Colombier }
6028e32b400SDavid du Colombier 
6038e32b400SDavid du Colombier static void
smcreceive(Ether * edev)6048e32b400SDavid du Colombier smcreceive(Ether *edev)
6058e32b400SDavid du Colombier {
6068e32b400SDavid du Colombier 	uint wds, len, sts;
6078e32b400SDavid du Colombier 	ulong *wdp, *rxdp;
6088e32b400SDavid du Colombier 	Block *bp;
6098e32b400SDavid du Colombier 	Ctlr *ctlr;
6108e32b400SDavid du Colombier 	Regs *regs;
6118e32b400SDavid du Colombier 
6128e32b400SDavid du Colombier 	ctlr = edev->ctlr;
6138e32b400SDavid du Colombier 	regs = ctlr->regs;
6148e32b400SDavid du Colombier 	coherence();		/* back-to-back read/read delay per §6.2.2 */
6158e32b400SDavid du Colombier 	/*
6168e32b400SDavid du Colombier 	 * is there a full packet in the rx data fifo?
6178e32b400SDavid du Colombier 	 */
6188e32b400SDavid du Colombier 	while (((regs->rxfifoinf & Rxstsusedmask) >> Rxstsusedshift) != 0) {
6198e32b400SDavid du Colombier 		coherence();
6208e32b400SDavid du Colombier 		sts = regs->rxsts;		/* pop rx status */
6218e32b400SDavid du Colombier 		if(sts & Rxerr)
6228e32b400SDavid du Colombier 			iprint("smcreceive: rx error\n");
6238e32b400SDavid du Colombier 		len = (sts & Rxpktlenmask) >> Rxpktlenshift;
6248e32b400SDavid du Colombier 		if (len > ETHERMAXTU + Slop)
6258e32b400SDavid du Colombier 			iprint("smcreceive: oversized rx pkt (%d)\n", len);
6268e32b400SDavid du Colombier 		else if (len < ETHERMINTU)
6278e32b400SDavid du Colombier 			iprint("smcreceive: too-short (%d) pkt\n", len);
6288e32b400SDavid du Colombier 		wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong);
6298e32b400SDavid du Colombier 		if (wds > 0) {
6308e32b400SDavid du Colombier 			/* copy aligned words from rx fifo into a Block */
6318e32b400SDavid du Colombier 			bp = iallocb(len + sizeof(ulong) /* - 1 */);
6328e32b400SDavid du Colombier 			if (bp == nil)
6338e32b400SDavid du Colombier 				panic("smcreceive: nil Block*");
6348e32b400SDavid du Colombier 
6358e32b400SDavid du Colombier 			/* bp->rp should be 32-byte aligned, more than we need */
6368e32b400SDavid du Colombier 			assert(((uintptr)bp->rp & (sizeof(ulong) - 1)) == 0);
6378e32b400SDavid du Colombier 			wdp = (ulong *)bp->rp;
6388e32b400SDavid du Colombier 			rxdp = &regs->rxdata;
6398e32b400SDavid du Colombier 			wds = ROUNDUP(len, sizeof(ulong)) / sizeof(ulong) + 1;
6408e32b400SDavid du Colombier 			while (--wds > 0)
6418e32b400SDavid du Colombier 				*wdp++ = *rxdp;
6428e32b400SDavid du Colombier 			bp->wp = bp->rp + len;
6438e32b400SDavid du Colombier 
6448e32b400SDavid du Colombier 			/* and push the Block upstream */
6458e32b400SDavid du Colombier 			if (ctlr->inited)
6468e32b400SDavid du Colombier 				etheriq(edev, bp, 1);
6478e32b400SDavid du Colombier 			else
6488e32b400SDavid du Colombier 				freeb(bp);
6498e32b400SDavid du Colombier 
6508e32b400SDavid du Colombier 			regs->intsts = Rxintrs;		/* dismiss intr */
6518e32b400SDavid du Colombier 			coherence();
6528e32b400SDavid du Colombier 			regs->inten |= Rxintrs;
6538e32b400SDavid du Colombier 		}
6548e32b400SDavid du Colombier 		coherence();
6558e32b400SDavid du Colombier 	}
6568e32b400SDavid du Colombier 	regs->inten |= Rxintrs;
6578e32b400SDavid du Colombier 	coherence();
6588e32b400SDavid du Colombier }
6598e32b400SDavid du Colombier 
6608e32b400SDavid du Colombier /*
6618e32b400SDavid du Colombier  * disable the stsclr bits in inten and write them to intsts to ack and dismiss
6628e32b400SDavid du Colombier  * the interrupt source.
6638e32b400SDavid du Colombier  */
6648e32b400SDavid du Colombier void
ackintr(Regs * regs,ulong stsclr)6658e32b400SDavid du Colombier ackintr(Regs *regs, ulong stsclr)
6668e32b400SDavid du Colombier {
6678e32b400SDavid du Colombier 	if (stsclr == 0)
6688e32b400SDavid du Colombier 		return;
6698e32b400SDavid du Colombier 
6708e32b400SDavid du Colombier 	regs->inten &= ~stsclr;
6718e32b400SDavid du Colombier 	coherence();
6728e32b400SDavid du Colombier 
6738e32b400SDavid du Colombier //	regs->intsts = stsclr;		/* acknowledge & clear intr(s) */
6748e32b400SDavid du Colombier //	coherence();
6758e32b400SDavid du Colombier }
6768e32b400SDavid du Colombier 
6778e32b400SDavid du Colombier static void
smcinterrupt(Ureg *,void * arg)6788e32b400SDavid du Colombier smcinterrupt(Ureg*, void* arg)
6798e32b400SDavid du Colombier {
6808e32b400SDavid du Colombier 	int junk;
6818e32b400SDavid du Colombier 	unsigned intsts, intr;
6828e32b400SDavid du Colombier 	Ctlr *ctlr;
6838e32b400SDavid du Colombier 	Ether *edev;
6848e32b400SDavid du Colombier 	Regs *regs;
6858e32b400SDavid du Colombier 
6868e32b400SDavid du Colombier 	edev = arg;
6878e32b400SDavid du Colombier 	ctlr = edev->ctlr;
6888e32b400SDavid du Colombier 	ilock(&ctlr->imlock);
6898e32b400SDavid du Colombier 	regs = ctlr->regs;
6908e32b400SDavid du Colombier 
6918e32b400SDavid du Colombier 	gpioirqclr();
6928e32b400SDavid du Colombier 
6938e32b400SDavid du Colombier 	coherence();		/* back-to-back read/read delay per §6.2.2 */
6948e32b400SDavid du Colombier 	intsts = regs->intsts;
6958e32b400SDavid du Colombier 	coherence();
6968e32b400SDavid du Colombier 
6978e32b400SDavid du Colombier 	intsts &= ~MASK(3);		/* ignore gpio bits */
6988e32b400SDavid du Colombier 	if (0 && intsts == 0) {
6998e32b400SDavid du Colombier 		coherence();
7008e32b400SDavid du Colombier 		iprint("smc: interrupt without a cause; insts %#ux (vs inten %#lux)\n",
7018e32b400SDavid du Colombier 			intsts, regs->inten);
7028e32b400SDavid du Colombier 	}
7038e32b400SDavid du Colombier 
7048e32b400SDavid du Colombier 	intr = intsts & Rxintrs;
7058e32b400SDavid du Colombier 	if(intr) {
7068e32b400SDavid du Colombier 		/* disable interrupt sources; kproc/smcreceive will reenable */
7078e32b400SDavid du Colombier 		ackintr(regs, intr);
7088e32b400SDavid du Colombier 
7098e32b400SDavid du Colombier 		ctlr->rintr++;
7108e32b400SDavid du Colombier 		ctlr->gotinput = 1;
7118e32b400SDavid du Colombier #ifdef USE_KPROCS
7128e32b400SDavid du Colombier 		wakeup(&ctlr->rrendez);
7138e32b400SDavid du Colombier #else
7148e32b400SDavid du Colombier 		smcreceive(edev);
7158e32b400SDavid du Colombier #endif
7168e32b400SDavid du Colombier 	}
7178e32b400SDavid du Colombier 
7188e32b400SDavid du Colombier 	while(((regs->txfifoinf & Txstsusedmask) >> Txstsusedshift) != 0) {
7198e32b400SDavid du Colombier 		/* probably indicates tx completion, just toss it */
7208e32b400SDavid du Colombier 		junk = regs->txsts;		/* pop tx sts */
7218e32b400SDavid du Colombier 		USED(junk);
7228e32b400SDavid du Colombier 		coherence();
7238e32b400SDavid du Colombier 	}
7248e32b400SDavid du Colombier 
7258e32b400SDavid du Colombier 	intr = intsts & Txintrs;
7268e32b400SDavid du Colombier 	if (ctlr->gotoutput || intr) {
7278e32b400SDavid du Colombier 		/* disable interrupt sources; kproc/smctransmit will reenable */
7288e32b400SDavid du Colombier 		ackintr(regs, intr);
7298e32b400SDavid du Colombier 
7308e32b400SDavid du Colombier 		ctlr->tintr++;
7318e32b400SDavid du Colombier 		ctlr->gotoutput = 1;
7328e32b400SDavid du Colombier #ifdef USE_KPROCS
7338e32b400SDavid du Colombier 		wakeup(&ctlr->trendez);
7348e32b400SDavid du Colombier #else
7358e32b400SDavid du Colombier 		smctransmit(edev);
7368e32b400SDavid du Colombier #endif
7378e32b400SDavid du Colombier 	}
7388e32b400SDavid du Colombier 
7398e32b400SDavid du Colombier 	iunlock(&ctlr->imlock);
7408e32b400SDavid du Colombier }
7418e32b400SDavid du Colombier 
7428e32b400SDavid du Colombier static void
etherclock(void)7438e32b400SDavid du Colombier etherclock(void)
7448e32b400SDavid du Colombier {
7458e32b400SDavid du Colombier 	smcinterrupt(nil, thisether);
7468e32b400SDavid du Colombier }
7478e32b400SDavid du Colombier 
7488e32b400SDavid du Colombier static int
smcmii(Ctlr *)7498e32b400SDavid du Colombier smcmii(Ctlr *)
7508e32b400SDavid du Colombier {
7518e32b400SDavid du Colombier 	return 0;
7528e32b400SDavid du Colombier }
7538e32b400SDavid du Colombier 
7548e32b400SDavid du Colombier static int
smcdetach(Ctlr * ctlr)7558e32b400SDavid du Colombier smcdetach(Ctlr* ctlr)
7568e32b400SDavid du Colombier {
7578e32b400SDavid du Colombier 	Regs *regs;
7588e32b400SDavid du Colombier 
7598e32b400SDavid du Colombier 	if (ctlr == nil || ctlr->regs == nil)
7608e32b400SDavid du Colombier 		return -1;
7618e32b400SDavid du Colombier 	regs = ctlr->regs;
7628e32b400SDavid du Colombier 	/* verify that it's real by reading a few registers */
7638e32b400SDavid du Colombier 	switch (regs->id) {
7648e32b400SDavid du Colombier 	case Vid9221:
7658e32b400SDavid du Colombier 		break;
7668e32b400SDavid du Colombier 	default:
7678e32b400SDavid du Colombier 		print("smc: unknown chip id %#ux\n", regs->id);
7688e32b400SDavid du Colombier 		return -1;
7698e32b400SDavid du Colombier 	}
7708e32b400SDavid du Colombier 	regs->inten = 0;		/* no interrupts */
7718e32b400SDavid du Colombier 	regs->intsts = ~0;		/* clear any pending */
7728e32b400SDavid du Colombier 	regs->gptcfg = 0;
7738e32b400SDavid du Colombier 	coherence();
7748e32b400SDavid du Colombier 	regs->rxcfg = Rxdump;
7758e32b400SDavid du Colombier 	regs->txcfg = Txsdump | Txddump;
7768e32b400SDavid du Colombier 	regs->irqcfg &= ~Irqen;
7778e32b400SDavid du Colombier 	coherence();
7788e32b400SDavid du Colombier 	return 0;
7798e32b400SDavid du Colombier }
7808e32b400SDavid du Colombier 
7818e32b400SDavid du Colombier static void
smcshutdown(Ether * ether)7828e32b400SDavid du Colombier smcshutdown(Ether* ether)
7838e32b400SDavid du Colombier {
7848e32b400SDavid du Colombier 	smcdetach(ether->ctlr);
7858e32b400SDavid du Colombier }
7868e32b400SDavid du Colombier 
7878e32b400SDavid du Colombier static void
powerwait(Regs * regs)7888e32b400SDavid du Colombier powerwait(Regs *regs)
7898e32b400SDavid du Colombier {
7908e32b400SDavid du Colombier 	long bound;
7918e32b400SDavid du Colombier 
7928e32b400SDavid du Colombier 	regs->bytetest = 0;			/* bring power on */
793*df2dbabfSDavid du Colombier 	for (bound = 400*Mhz; !(regs->pmtctl & Dready) && bound > 0; bound--)
7948e32b400SDavid du Colombier 		;
7958e32b400SDavid du Colombier 	if (bound <= 0)
7968e32b400SDavid du Colombier 		iprint("smc: pmtctl didn't come ready\n");
7978e32b400SDavid du Colombier }
7988e32b400SDavid du Colombier 
7998e32b400SDavid du Colombier static int
smcreset(Ctlr * ctlr)8008e32b400SDavid du Colombier smcreset(Ctlr* ctlr)
8018e32b400SDavid du Colombier {
8028e32b400SDavid du Colombier 	int r;
8038e32b400SDavid du Colombier 	Regs *regs;
8048e32b400SDavid du Colombier 	static char zea[Eaddrlen];
8058e32b400SDavid du Colombier 
8068e32b400SDavid du Colombier 	regs = ctlr->regs;
8078e32b400SDavid du Colombier 	powerwait(regs);
8088e32b400SDavid du Colombier 
8098e32b400SDavid du Colombier 	if(smcdetach(ctlr))
8108e32b400SDavid du Colombier 		return -1;
8118e32b400SDavid du Colombier 
8128e32b400SDavid du Colombier 	/* verify that it's real by reading a few registers */
8138e32b400SDavid du Colombier 	switch (regs->id) {
8148e32b400SDavid du Colombier 	case Vid9221:
8158e32b400SDavid du Colombier 		break;
8168e32b400SDavid du Colombier 	default:
8178e32b400SDavid du Colombier 		print("smc: unknown chip id %#ux\n", regs->id);
8188e32b400SDavid du Colombier 		return -1;
8198e32b400SDavid du Colombier 	}
8208e32b400SDavid du Colombier 	if (regs->bytetest != 0x87654321) {
8218e32b400SDavid du Colombier 		print("smc: bytetest reg %#p (%#lux) != 0x87654321\n",
8228e32b400SDavid du Colombier 			&regs->bytetest, regs->bytetest);
8238e32b400SDavid du Colombier 		return -1;
8248e32b400SDavid du Colombier 	}
8258e32b400SDavid du Colombier 
8268e32b400SDavid du Colombier #ifdef TODO			/* read MAC from EEPROM */
8278e32b400SDavid du Colombier //	int ctrl, i, pause, swdpio, txcw;
8288e32b400SDavid du Colombier 	/*
8298e32b400SDavid du Colombier 	 * Snarf and set up the receive addresses.
8308e32b400SDavid du Colombier 	 * There are 16 addresses. The first should be the MAC address.
8318e32b400SDavid du Colombier 	 * The others are cleared and not marked valid (MS bit of Rah).
8328e32b400SDavid du Colombier 	 */
8338e32b400SDavid du Colombier 	for(i = Ea; i < Eaddrlen/2; i++){
8348e32b400SDavid du Colombier 		ctlr->ra[2*i] = ctlr->eeprom[i];
8358e32b400SDavid du Colombier 		ctlr->ra[2*i+1] = ctlr->eeprom[i]>>8;
8368e32b400SDavid du Colombier 	}
8378e32b400SDavid du Colombier 
8388e32b400SDavid du Colombier 	/*
8398e32b400SDavid du Colombier 	 * Clear the Multicast Table Array.
8408e32b400SDavid du Colombier 	 * It's a 4096 bit vector accessed as 128 32-bit registers.
8418e32b400SDavid du Colombier 	 */
8428e32b400SDavid du Colombier 	memset(ctlr->mta, 0, sizeof(ctlr->mta));
8438e32b400SDavid du Colombier 	for(i = 0; i < 128; i++)
8448e32b400SDavid du Colombier 		csr32w(ctlr, Mta+i*4, 0);
8458e32b400SDavid du Colombier #endif
8468e32b400SDavid du Colombier 	regs->hwcfg |= Mbo;
8478e32b400SDavid du Colombier 
8488e32b400SDavid du Colombier 	/* don't overwrite existing ea */
8498e32b400SDavid du Colombier //	if (memcmp(edev->ea, zea, Eaddrlen) == 0)
8508e32b400SDavid du Colombier //		memmove(edev->ea, ctlr->ra, Eaddrlen);
8518e32b400SDavid du Colombier 
8528e32b400SDavid du Colombier 	r = ctlr->ra[3]<<24 | ctlr->ra[2]<<16 | ctlr->ra[1]<<8 | ctlr->ra[0];
8538e32b400SDavid du Colombier 	macwr(regs, Macaddrl, r);
8548e32b400SDavid du Colombier 	macwr(regs, Macaddrh, ctlr->ra[5]<<8 | ctlr->ra[4]);
8558e32b400SDavid du Colombier 
8568e32b400SDavid du Colombier 	/* turn on the controller */
8578e32b400SDavid du Colombier 	macwr(regs, Maccoe, 0);
8588e32b400SDavid du Colombier 	regs->inten = 0;		/* no interrupts yet */
8598e32b400SDavid du Colombier 	regs->intsts = ~0;		/* clear any pending */
8608e32b400SDavid du Colombier 	regs->gptcfg = 0;
8618e32b400SDavid du Colombier 	coherence();
8628e32b400SDavid du Colombier 	regs->rxcfg = Rxdump;
8638e32b400SDavid du Colombier 	regs->txcfg = Txsdump | Txddump | Txon;
8648e32b400SDavid du Colombier 	regs->fifoint = 72<<24;		/* default values */
8658e32b400SDavid du Colombier 	macwr(regs, Maccr, Rxall | Rcvown | Fdpx | Mcpas | Txen | Rxen);
8668e32b400SDavid du Colombier 	coherence();		/* back-to-back write/read delay per §6.2.1 */
8678e32b400SDavid du Colombier 	regs->irqcfg = 1<<24 | Irqen | Irqpushpull;  /* deas for 10µs (linux) */
8688e32b400SDavid du Colombier 	coherence();		/* back-to-back write/read delay per §6.2.1 */
8698e32b400SDavid du Colombier 	regs->inten = Rxintrs | Txintrs;
8708e32b400SDavid du Colombier 	coherence();
8718e32b400SDavid du Colombier 
8728e32b400SDavid du Colombier 	if(smcmii(ctlr) < 0)
8738e32b400SDavid du Colombier 		return -1;
8748e32b400SDavid du Colombier 	return 0;
8758e32b400SDavid du Colombier }
8768e32b400SDavid du Colombier 
8778e32b400SDavid du Colombier static void
smcpci(void)8788e32b400SDavid du Colombier smcpci(void)
8798e32b400SDavid du Colombier {
8808e32b400SDavid du Colombier 	Ctlr *ctlr;
8818e32b400SDavid du Colombier 	static int beenhere;
8828e32b400SDavid du Colombier 
8838e32b400SDavid du Colombier 	if (beenhere)
8848e32b400SDavid du Colombier 		return;
8858e32b400SDavid du Colombier 	beenhere = 1;
8868e32b400SDavid du Colombier 
8878e32b400SDavid du Colombier 	if (probeaddr(PHYSETHER) < 0)
8888e32b400SDavid du Colombier 		return;
8898e32b400SDavid du Colombier 	ctlr = malloc(sizeof(Ctlr));
8908e32b400SDavid du Colombier 	ctlr->id = Vid9221<<16 | 0x0424;	/* smsc 9221 */
8918e32b400SDavid du Colombier 	ctlr->port = PHYSETHER;
8928e32b400SDavid du Colombier 	ctlr->nic = (int *)PHYSETHER;
8938e32b400SDavid du Colombier 	ctlr->regs = (Regs *)PHYSETHER;
8948e32b400SDavid du Colombier 
8958e32b400SDavid du Colombier 	if(smcreset(ctlr)){
8968e32b400SDavid du Colombier 		free(ctlr);
8978e32b400SDavid du Colombier 		return;
8988e32b400SDavid du Colombier 	}
8998e32b400SDavid du Colombier 	if(smcctlrhead != nil)
9008e32b400SDavid du Colombier 		smcctlrtail->next = ctlr;
9018e32b400SDavid du Colombier 	else
9028e32b400SDavid du Colombier 		smcctlrhead = ctlr;
9038e32b400SDavid du Colombier 	smcctlrtail = ctlr;
9048e32b400SDavid du Colombier }
9058e32b400SDavid du Colombier 
9068e32b400SDavid du Colombier static int
smcpnp(Ether * edev)9078e32b400SDavid du Colombier smcpnp(Ether* edev)
9088e32b400SDavid du Colombier {
9098e32b400SDavid du Colombier 	Ctlr *ctlr;
9108e32b400SDavid du Colombier 	static char zea[Eaddrlen];
9118e32b400SDavid du Colombier 
9128e32b400SDavid du Colombier 	if(smcctlrhead == nil)
9138e32b400SDavid du Colombier 		smcpci();
9148e32b400SDavid du Colombier 
9158e32b400SDavid du Colombier 	/*
9168e32b400SDavid du Colombier 	 * Any adapter matches if no edev->port is supplied,
9178e32b400SDavid du Colombier 	 * otherwise the ports must match.
9188e32b400SDavid du Colombier 	 */
9198e32b400SDavid du Colombier 	for(ctlr = smcctlrhead; ctlr != nil; ctlr = ctlr->next){
9208e32b400SDavid du Colombier 		if(ctlr->active)
9218e32b400SDavid du Colombier 			continue;
9228e32b400SDavid du Colombier 		if(edev->port == 0 || edev->port == ctlr->port){
9238e32b400SDavid du Colombier 			ctlr->active = 1;
9248e32b400SDavid du Colombier 			break;
9258e32b400SDavid du Colombier 		}
9268e32b400SDavid du Colombier 	}
9278e32b400SDavid du Colombier 	if(ctlr == nil)
9288e32b400SDavid du Colombier 		return -1;
9298e32b400SDavid du Colombier 
9308e32b400SDavid du Colombier 	edev->ctlr = ctlr;
9318e32b400SDavid du Colombier 	ctlr->edev = edev;			/* point back to Ether* */
9328e32b400SDavid du Colombier 	edev->port = ctlr->port;
9338e32b400SDavid du Colombier 	edev->irq = 34;
9348e32b400SDavid du Colombier // TODO: verify speed (100Mb/s) and duplicity (full-duplex)
9358e32b400SDavid du Colombier 	edev->mbps = 100;
9368e32b400SDavid du Colombier 
9378e32b400SDavid du Colombier 	/* don't overwrite existing ea */
9388e32b400SDavid du Colombier 	if (memcmp(edev->ea, zea, Eaddrlen) == 0)
9398e32b400SDavid du Colombier 		memmove(edev->ea, ctlr->ra, Eaddrlen);
9408e32b400SDavid du Colombier 
9418e32b400SDavid du Colombier 	/*
9428e32b400SDavid du Colombier 	 * Linkage to the generic ethernet driver.
9438e32b400SDavid du Colombier 	 */
9448e32b400SDavid du Colombier 	edev->attach = smcattach;
9458e32b400SDavid du Colombier 	edev->transmit = smctransmitcall;
9468e32b400SDavid du Colombier 	edev->interrupt = smcinterrupt;
9478e32b400SDavid du Colombier 	edev->ifstat = smcifstat;
9488e32b400SDavid du Colombier /*	edev->ctl = smcctl;			/* no ctl msgs supported */
9498e32b400SDavid du Colombier 
9508e32b400SDavid du Colombier 	edev->arg = edev;
9518e32b400SDavid du Colombier 	edev->promiscuous = smcpromiscuous;
9528e32b400SDavid du Colombier 	edev->multicast = smcmulticast;
9538e32b400SDavid du Colombier 	edev->shutdown = smcshutdown;
9548e32b400SDavid du Colombier 	return 0;
9558e32b400SDavid du Colombier }
9568e32b400SDavid du Colombier 
9578e32b400SDavid du Colombier void
ether9221link(void)9588e32b400SDavid du Colombier ether9221link(void)
9598e32b400SDavid du Colombier {
9608e32b400SDavid du Colombier 	addethercard("9221", smcpnp);
9618e32b400SDavid du Colombier }
962