|  | /************************************************************************** | 
|  | Etherboot -  BOOTP/TFTP Bootstrap Program | 
|  |  | 
|  | TIARA (Fujitsu Etherstar) NIC driver for Etherboot | 
|  | Copyright (c) Ken Yap 1998 | 
|  |  | 
|  | Information gleaned from: | 
|  |  | 
|  | TIARA.ASM Packet driver by Brian Fisher, Queens U, Kingston, Ontario | 
|  | Fujitsu MB86960 spec sheet (different chip but same family) | 
|  | ***************************************************************************/ | 
|  |  | 
|  | /* | 
|  | * This program is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU General Public License as | 
|  | * published by the Free Software Foundation; either version 2, or (at | 
|  | * your option) any later version. | 
|  | */ | 
|  |  | 
|  | /* to get some global routines like printf */ | 
|  | #include "etherboot.h" | 
|  | /* to get the interface to the body of the program */ | 
|  | #include "nic.h" | 
|  | #include "cards.h" | 
|  |  | 
|  | /* | 
|  | EtherStar I/O Register offsets | 
|  | */ | 
|  |  | 
|  | /* Offsets of registers */ | 
|  | #define	DLCR_XMIT_STAT	0x00 | 
|  | #define	DLCR_XMIT_MASK	0x01 | 
|  | #define	DLCR_RECV_STAT	0x02 | 
|  | #define	DLCR_RECV_MASK	0x03 | 
|  | #define	DLCR_XMIT_MODE	0x04 | 
|  | #define	DLCR_RECV_MODE	0x05 | 
|  | #define	DLCR_ENABLE	0x06 | 
|  | #define	DLCR_TDR_LOW	0x07 | 
|  | #define	DLCR_NODE_ID	0x08 | 
|  | #define	DLCR_TDR_HIGH	0x0F | 
|  | #define	BMPR_MEM_PORT	0x10 | 
|  | #define	BMPR_PKT_LEN	0x12 | 
|  | #define	BMPR_DMA_ENABLE	0x14 | 
|  | #define	PROM_ID		0x18 | 
|  |  | 
|  | #define	TMST		0x80 | 
|  | #define	TMT_OK		0x80 | 
|  | #define	TMT_16COLL	0x02 | 
|  | #define	BUF_EMPTY	0x40 | 
|  |  | 
|  | #define	CARD_DISABLE	0x80	/* written to DLCR_ENABLE to disable card */ | 
|  | #define	CARD_ENABLE	0	/* written to DLCR_ENABLE to enable card */ | 
|  |  | 
|  | #define	CLEAR_STATUS	0x0F	/* used to clear status info */ | 
|  | /* | 
|  | 00001111B | 
|  | !!!!!!!!-------- | 
|  | !!!!!!!+--------CLEAR BUS WRITE ERROR | 
|  | !!!!!!+---------CLEAR 16 COLLISION | 
|  | !!!!!+----------CLEAR COLLISION | 
|  | !!!!+-----------CLEAR UNDERFLOW | 
|  | !!!+------------NC | 
|  | !!+-------------NC | 
|  | !+--------------NC | 
|  | +---------------NC | 
|  | */ | 
|  |  | 
|  | #define	NO_TX_IRQS	0	/* written to clear transmit IRQs */ | 
|  |  | 
|  | #define	CLR_RCV_STATUS	0xCF	/* clears receive status */ | 
|  |  | 
|  | #define	EN_RCV_IRQS	0x80	/* enable receive interrupts */ | 
|  | /* | 
|  | 10000000B | 
|  | !!!!!!!!-------- | 
|  | !!!!!!!+--------ENABLE OVERFLOW | 
|  | !!!!!!+---------ENABLE CRC | 
|  | !!!!!+----------ENABLE ALIGN | 
|  | !!!!+-----------ENABLE SHORT PKT | 
|  | !!!+------------DISABLE REMOTE RESET | 
|  | !!+-------------RESERVED | 
|  | !+--------------RESERVED | 
|  | +---------------ENABLE PKT READY | 
|  | */ | 
|  |  | 
|  | #define	XMIT_MODE	0x02 | 
|  | /* | 
|  | 00000010B | 
|  | !!!!!!!!---------ENABLE CARRIER DETECT | 
|  | !!!!!!!+---------DISABLE LOOPBACK | 
|  | */ | 
|  |  | 
|  | #define	RECV_MODE	0x02 | 
|  | /* | 
|  | 00000010B | 
|  | !!!!!!!!---------ACCEPT ALL PACKETS | 
|  | !!!!!!!+---------ACCEPT PHYSICAL, MULTICAST, AND | 
|  | !!!!!!+----------BROADCAST PACKETS | 
|  | !!!!!+-----------DISABLE REMOTE RESET | 
|  | !!!!+------------DISABLE SHORT PACKETS | 
|  | !!!+-------------USE 6 BYTE ADDRESS | 
|  | !!+--------------NC | 
|  | !+---------------NC | 
|  | +----------------DISABLE CRC TEST MODE | 
|  | */ | 
|  |  | 
|  | /* NIC specific static variables go here */ | 
|  |  | 
|  | static unsigned short	ioaddr; | 
|  |  | 
|  | /************************************************************************** | 
|  | RESET - Reset adapter | 
|  | ***************************************************************************/ | 
|  | static void tiara_reset(struct nic *nic) | 
|  | { | 
|  | int		i; | 
|  |  | 
|  | outb(CARD_DISABLE, ioaddr + DLCR_ENABLE); | 
|  | outb(CLEAR_STATUS, ioaddr + DLCR_XMIT_STAT); | 
|  | outb(NO_TX_IRQS, ioaddr + DLCR_XMIT_MASK); | 
|  | outb(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT); | 
|  | outb(XMIT_MODE, ioaddr + DLCR_XMIT_MODE); | 
|  | outb(RECV_MODE, ioaddr + DLCR_RECV_MODE); | 
|  | /* Vacuum recv buffer */ | 
|  | while ((inb(ioaddr + DLCR_RECV_MODE) & BUF_EMPTY) == 0) | 
|  | inb(ioaddr + BMPR_MEM_PORT); | 
|  | /* Set node address */ | 
|  | for (i = 0; i < ETH_ALEN; ++i) | 
|  | outb(nic->node_addr[i], ioaddr + DLCR_NODE_ID + i); | 
|  | outb(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT); | 
|  | outb(CARD_ENABLE, ioaddr + DLCR_ENABLE); | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | POLL - Wait for a frame | 
|  | ***************************************************************************/ | 
|  | static int tiara_poll(struct nic *nic) | 
|  | { | 
|  | unsigned int		len; | 
|  |  | 
|  | if (inb(ioaddr + DLCR_RECV_MODE) & BUF_EMPTY) | 
|  | return (0); | 
|  | /* Ack packet */ | 
|  | outw(CLR_RCV_STATUS, ioaddr + DLCR_RECV_STAT); | 
|  | len = inw(ioaddr + BMPR_MEM_PORT);		/* throw away status */ | 
|  | len = inw(ioaddr + BMPR_MEM_PORT); | 
|  | /* Drop overlength packets */ | 
|  | if (len > ETH_FRAME_LEN) | 
|  | return (0);		/* should we drain the buffer? */ | 
|  | insw(ioaddr + BMPR_MEM_PORT, nic->packet, len / 2); | 
|  | /* If it's our own, drop it */ | 
|  | if (memcmp(nic->packet + ETH_ALEN, nic->node_addr, ETH_ALEN) == 0) | 
|  | return (0); | 
|  | nic->packetlen = len; | 
|  | return (1); | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | TRANSMIT - Transmit a frame | 
|  | ***************************************************************************/ | 
|  | static void tiara_transmit( | 
|  | struct nic *nic, | 
|  | const char *d,			/* Destination */ | 
|  | unsigned int t,			/* Type */ | 
|  | unsigned int s,			/* size */ | 
|  | const char *p)			/* Packet */ | 
|  | { | 
|  | unsigned int	len; | 
|  | unsigned long	time; | 
|  |  | 
|  | len = s + ETH_HLEN; | 
|  | if (len < ETH_ZLEN) | 
|  | len = ETH_ZLEN; | 
|  | t = htons(t); | 
|  | outsw(ioaddr + BMPR_MEM_PORT, d, ETH_ALEN / 2); | 
|  | outsw(ioaddr + BMPR_MEM_PORT, nic->node_addr, ETH_ALEN / 2); | 
|  | outw(t, ioaddr + BMPR_MEM_PORT); | 
|  | outsw(ioaddr + BMPR_MEM_PORT, p, s / 2); | 
|  | if (s & 1)					/* last byte */ | 
|  | outb(p[s-1], ioaddr + BMPR_MEM_PORT); | 
|  | while (s++ < ETH_ZLEN - ETH_HLEN)	/* pad */ | 
|  | outb(0, ioaddr + BMPR_MEM_PORT); | 
|  | outw(len | (TMST << 8), ioaddr + BMPR_PKT_LEN); | 
|  | /* wait for transmit complete */ | 
|  | time = currticks() + TICKS_PER_SEC;		/* wait one second */ | 
|  | while (currticks() < time && (inb(ioaddr) & (TMT_OK|TMT_16COLL)) == 0) | 
|  | ; | 
|  | if ((inb(ioaddr) & (TMT_OK|TMT_16COLL)) == 0) | 
|  | printf("Tiara timed out on transmit\n"); | 
|  | /* Do we need to ack the transmit? */ | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | DISABLE - Turn off ethernet interface | 
|  | ***************************************************************************/ | 
|  | static void tiara_disable(struct nic *nic) | 
|  | { | 
|  | /* Apparently only a power down can do this properly */ | 
|  | outb(CARD_DISABLE, ioaddr + DLCR_ENABLE); | 
|  | } | 
|  |  | 
|  | static int tiara_probe1(struct nic *nic) | 
|  | { | 
|  | /* Hope all the Tiara cards have this vendor prefix */ | 
|  | static char	vendor_prefix[] = { 0x08, 0x00, 0x1A }; | 
|  | static char	all_ones[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | 
|  | int		i; | 
|  |  | 
|  | for (i = 0; i < ETH_ALEN; ++i) | 
|  | nic->node_addr[i] = inb(ioaddr + PROM_ID + i); | 
|  | if (memcmp(nic->node_addr, vendor_prefix, sizeof(vendor_prefix)) != 0) | 
|  | return (0); | 
|  | if (memcmp(nic->node_addr, all_ones, sizeof(all_ones)) == 0) | 
|  | return (0); | 
|  | printf("\nTiara ioaddr %#hX, addr %!\n", ioaddr, nic->node_addr); | 
|  | return (1); | 
|  | } | 
|  |  | 
|  | /************************************************************************** | 
|  | PROBE - Look for an adapter, this routine's visible to the outside | 
|  | ***************************************************************************/ | 
|  | struct nic *tiara_probe(struct nic *nic, unsigned short *probe_addrs) | 
|  | { | 
|  | /* missing entries are addresses usually already used */ | 
|  | static unsigned short	io_addrs[] = { | 
|  | 0x100, 0x120, 0x140, 0x160, | 
|  | 0x180, 0x1A0, 0x1C0, 0x1E0, | 
|  | 0x200, 0x220, 0x240, /*Par*/ | 
|  | 0x280, 0x2A0, 0x2C0, /*Ser*/ | 
|  | 0x300, 0x320, 0x340, /*Par*/ | 
|  | 0x380, /*Vid,Par*/ 0x3C0, /*Ser*/ | 
|  | 0x0 | 
|  | }; | 
|  | unsigned short		*p; | 
|  |  | 
|  | /* if probe_addrs is 0, then routine can use a hardwired default */ | 
|  | if (probe_addrs == 0) | 
|  | probe_addrs = io_addrs; | 
|  | for (p = probe_addrs; (ioaddr = *p) != 0; ++p) | 
|  | if (tiara_probe1(nic)) | 
|  | break; | 
|  | /* if board found */ | 
|  | if (ioaddr != 0) | 
|  | { | 
|  | tiara_reset(nic); | 
|  | /* point to NIC specific routines */ | 
|  | nic->reset = tiara_reset; | 
|  | nic->poll = tiara_poll; | 
|  | nic->transmit = tiara_transmit; | 
|  | nic->disable = tiara_disable; | 
|  | return nic; | 
|  | } | 
|  | else | 
|  | return (0); | 
|  | } |