Home | History | Annotate | Line # | Download | only in boot
dp8390.c revision 1.1.2.2
      1 /*	$NetBSD: dp8390.c,v 1.1.2.2 2012/10/30 17:20:31 yamt Exp $	*/
      2 /*	Id: dp8390.c,v 1.14 2011/10/05 13:16:20 isaki Exp 	*/
      3 
      4 /*
      5  * This file is derived from sys/arch/i386/stand/lib/netif/dp8390.c
      6  * NetBSD: dp8390.c,v 1.6 2008/12/14 18:46:33 christos Exp
      7  */
      8 
      9 /*
     10  * Polling driver for National Semiconductor DS8390/WD83C690 based
     11  * ethernet adapters.
     12  *
     13  * Copyright (c) 1998 Matthias Drochner.  All rights reserved.
     14  *
     15  * Copyright (c) 1994, 1995 Charles M. Hannum.  All rights reserved.
     16  *
     17  * Copyright (C) 1993, David Greenman.  This software may be used, modified,
     18  * copied, distributed, and sold, in both source and binary form provided that
     19  * the above copyright and these terms are retained.  Under no circumstances is
     20  * the author responsible for the proper functioning of this software, nor does
     21  * the author assume any responsibility for damages incurred with its use.
     22  */
     23 
     24 
     25 #include <sys/types.h>
     26 
     27 #include <lib/libsa/stand.h>
     28 #include <libx68k.h>
     29 
     30 #include <dev/ic/dp8390reg.h>
     31 #include "dp8390.h"
     32 #include "ne.h"
     33 
     34 int dp8390_iobase, dp8390_membase, dp8390_memsize;
     35 #if defined(SUPPORT_WD80X3) && defined(SUPPORT_SMC_ULTRA)
     36 int dp8390_is790;
     37 #endif
     38 uint8_t dp8390_cr_proto;
     39 uint8_t dp8390_dcr_reg;
     40 
     41 #define WE_IOBASE dp8390_iobase
     42 
     43 static u_short rec_page_start;
     44 static u_short rec_page_stop;
     45 static u_short next_packet;
     46 
     47 extern u_char eth_myaddr[6];
     48 
     49 static void dp8390_read(int, char *, u_short);
     50 
     51 #define NIC_GET(reg) inb(WE_IOBASE + (reg) * 2)
     52 #define NIC_PUT(reg, val) outb(WE_IOBASE + (reg) * 2, val)
     53 
     54 static void
     55 dp8390_init(void)
     56 {
     57 	int i;
     58 
     59 	/*
     60 	 * Initialize the NIC in the exact order outlined in the NS manual.
     61 	 * This init procedure is "mandatory"...don't change what or when
     62 	 * things happen.
     63 	 */
     64 
     65 	/* Set interface for page 0, remote DMA complete, stopped. */
     66 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STP);
     67 
     68 	if ((dp8390_dcr_reg & ED_DCR_LS)) {
     69 		NIC_PUT(ED_P0_DCR, dp8390_dcr_reg);
     70 	} else {
     71 		/*
     72 		 * Set FIFO threshold to 8, No auto-init Remote DMA, byte
     73 		 * order=80x86, byte-wide DMA xfers,
     74 		 */
     75 		NIC_PUT(ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS);
     76 	}
     77 
     78 	/* Clear remote byte count registers. */
     79 	NIC_PUT(ED_P0_RBCR0, 0);
     80 	NIC_PUT(ED_P0_RBCR1, 0);
     81 
     82 	/* Tell RCR to do nothing for now. */
     83 	NIC_PUT(ED_P0_RCR, ED_RCR_MON);
     84 
     85 	/* Place NIC in internal loopback mode. */
     86 	NIC_PUT(ED_P0_TCR, ED_TCR_LB0);
     87 
     88 	/* Set lower bits of byte addressable framing to 0. */
     89 	if (dp8390_is790)
     90 		NIC_PUT(0x09, 0);
     91 
     92 	/* Initialize receive buffer ring. */
     93 	NIC_PUT(ED_P0_BNRY, rec_page_start);
     94 	NIC_PUT(ED_P0_PSTART, rec_page_start);
     95 	NIC_PUT(ED_P0_PSTOP, rec_page_stop);
     96 
     97 	/*
     98 	 * Clear all interrupts.  A '1' in each bit position clears the
     99 	 * corresponding flag.
    100 	 */
    101 	NIC_PUT(ED_P0_ISR, 0xff);
    102 
    103 	/*
    104 	 * Disable all interrupts.
    105 	 */
    106 	NIC_PUT(ED_P0_IMR, 0);
    107 
    108 	/* Program command register for page 1. */
    109 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_1 | ED_CR_STP);
    110 
    111 	/* Copy out our station address. */
    112 	for (i = 0; i < 6; i++)
    113 		NIC_PUT(ED_P1_PAR0 + i, eth_myaddr[i]);
    114 
    115 	/*
    116 	 * Set current page pointer to one page after the boundary pointer, as
    117 	 * recommended in the National manual.
    118 	 */
    119 	next_packet = rec_page_start + 1;
    120 	NIC_PUT(ED_P1_CURR, next_packet);
    121 
    122 	/* Program command register for page 0. */
    123 	NIC_PUT(ED_P1_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STP);
    124 
    125 	/* directed and broadcast */
    126 	NIC_PUT(ED_P0_RCR, ED_RCR_AB);
    127 
    128 	/* Take interface out of loopback. */
    129 	NIC_PUT(ED_P0_TCR, 0);
    130 
    131 	/* Fire up the interface. */
    132 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STA);
    133 }
    134 
    135 int
    136 dp8390_config(void)
    137 {
    138 
    139 	rec_page_start = TX_PAGE_START + ED_TXBUF_SIZE;
    140 	rec_page_stop = TX_PAGE_START + (dp8390_memsize >> ED_PAGE_SHIFT);
    141 
    142 	dp8390_init();
    143 
    144 	return 0;
    145 }
    146 
    147 void
    148 dp8390_stop(void)
    149 {
    150 	int n = 5000;
    151 
    152 	/* Stop everything on the interface, and select page 0 registers. */
    153 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STP);
    154 
    155 	/*
    156 	 * Wait for interface to enter stopped state, but limit # of checks to
    157 	 * 'n' (about 5ms).  It shouldn't even take 5us on modern DS8390's, but
    158 	 * just in case it's an old one.
    159 	 */
    160 	while (((NIC_GET(ED_P0_ISR) & ED_ISR_RST) == 0) && --n)
    161 		continue;
    162 }
    163 
    164 int
    165 EtherSend(char *pkt, int len)
    166 {
    167 	ne2000_writemem(pkt, dp8390_membase, len);
    168 
    169 	/* Set TX buffer start page. */
    170 	NIC_PUT(ED_P0_TPSR, TX_PAGE_START);
    171 
    172 	/* Set TX length. */
    173 	NIC_PUT(ED_P0_TBCR0, len < 60 ? 60 : len);
    174 	NIC_PUT(ED_P0_TBCR1, len >> 8);
    175 
    176 	/* Set page 0, remote DMA complete, transmit packet, and *start*. */
    177 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_TXP | ED_CR_STA);
    178 
    179 	return len;
    180 }
    181 
    182 static void
    183 dp8390_read(int buf, char *dest, u_short len)
    184 {
    185 	u_short tmp_amount;
    186 
    187 	/* Does copy wrap to lower addr in ring buffer? */
    188 	if (buf + len > dp8390_membase + dp8390_memsize) {
    189 		tmp_amount = dp8390_membase + dp8390_memsize - buf;
    190 
    191 		/* Copy amount up to end of NIC memory. */
    192 		ne2000_readmem(buf, dest, tmp_amount);
    193 
    194 		len -= tmp_amount;
    195 		buf = RX_BUFBASE + (rec_page_start << ED_PAGE_SHIFT);
    196 		dest += tmp_amount;
    197 	}
    198 	ne2000_readmem(buf, dest, len);
    199 }
    200 
    201 int
    202 EtherReceive(char *pkt, int maxlen)
    203 {
    204 	struct dp8390_ring packet_hdr;
    205 	int packet_ptr;
    206 	u_short len;
    207 	u_char boundary, current;
    208 #ifdef DP8390_OLDCHIPS
    209 	u_char nlen;
    210 #endif
    211 
    212 	if (!(NIC_GET(ED_P0_RSR) & ED_RSR_PRX))
    213 		return 0; /* XXX error handling */
    214 
    215 	/* Set NIC to page 1 registers to get 'current' pointer. */
    216 	NIC_PUT(ED_P0_CR, dp8390_cr_proto | ED_CR_PAGE_1 | ED_CR_STA);
    217 
    218 	/*
    219 	 * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e.
    220 	 * it points to where new data has been buffered.  The 'CURR' (current)
    221 	 * register points to the logical end of the ring-buffer - i.e. it
    222 	 * points to where additional new data will be added.  We loop here
    223 	 * until the logical beginning equals the logical end (or in other
    224 	 * words, until the ring-buffer is empty).
    225 	 */
    226 	current = NIC_GET(ED_P1_CURR);
    227 
    228 	/* Set NIC to page 0 registers to update boundary register. */
    229 	NIC_PUT(ED_P1_CR, dp8390_cr_proto | ED_CR_PAGE_0 | ED_CR_STA);
    230 
    231 	if (next_packet == current)
    232 		return 0;
    233 
    234 	/* Get pointer to this buffer's header structure. */
    235 	packet_ptr = RX_BUFBASE + (next_packet << ED_PAGE_SHIFT);
    236 
    237 	/*
    238 	 * The byte count includes a 4 byte header that was added by
    239 	 * the NIC.
    240 	 */
    241 	ne2000_readmem(packet_ptr, (void *)&packet_hdr, 4);
    242 
    243 	len = le16toh(packet_hdr.count);
    244 
    245 #ifdef DP8390_OLDCHIPS
    246 	/*
    247 	 * Try do deal with old, buggy chips that sometimes duplicate
    248 	 * the low byte of the length into the high byte.  We do this
    249 	 * by simply ignoring the high byte of the length and always
    250 	 * recalculating it.
    251 	 *
    252 	 * NOTE: sc->next_packet is pointing at the current packet.
    253 	 */
    254 	if (packet_hdr.next_packet >= next_packet)
    255 		nlen = (packet_hdr.next_packet - next_packet);
    256 	else
    257 		nlen = ((packet_hdr.next_packet - rec_page_start) +
    258 			(rec_page_stop - next_packet));
    259 	--nlen;
    260 	if ((len & ED_PAGE_MASK) + sizeof(packet_hdr) > ED_PAGE_SIZE)
    261 		--nlen;
    262 	len = (len & ED_PAGE_MASK) | (nlen << ED_PAGE_SHIFT);
    263 #ifdef DIAGNOSTIC
    264 	if (len != packet_hdr.count) {
    265 		printf(IFNAME ": length does not match next packet pointer\n");
    266 		printf(IFNAME ": len %04x nlen %04x start %02x "
    267 		       "first %02x curr %02x next %02x stop %02x\n",
    268 		       packet_hdr.count, len,
    269 		       rec_page_start, next_packet, current,
    270 		       packet_hdr.next_packet, rec_page_stop);
    271 	}
    272 #endif
    273 #endif
    274 
    275 	if (packet_hdr.next_packet < rec_page_start ||
    276 	    packet_hdr.next_packet >= rec_page_stop)
    277 		panic(IFNAME ": RAM corrupt");
    278 
    279 	len -= sizeof(struct dp8390_ring);
    280 	if (len <= maxlen) {
    281 		/* Go get packet. */
    282 		dp8390_read(packet_ptr + sizeof(struct dp8390_ring),
    283 			    pkt, len);
    284 	} else
    285 		len = 0;
    286 
    287 	/* Update next packet pointer. */
    288 	next_packet = packet_hdr.next_packet;
    289 
    290 	/*
    291 	 * Update NIC boundary pointer - being careful to keep it one
    292 	 * buffer behind (as recommended by NS databook).
    293 	 */
    294 	boundary = next_packet - 1;
    295 	if (boundary < rec_page_start)
    296 		boundary = rec_page_stop - 1;
    297 	NIC_PUT(ED_P0_BNRY, boundary);
    298 
    299 	return len;
    300 }
    301